MODULE MLS_O3_OBS_MOD ! ! ! Module MLS_O3_OBS contains all subroutines and variables needed for OMI O3 column data ! ! ! Module Routines: ! (1) READ_MLS_O3_FILE : Read MLS hdf file IMPLICIT NONE #include "CMN_SIZE" PRIVATE !PUBLIC READ_OMI_O3_FILE PUBLIC READ_MLS_O3_FILE PUBLIC CALC_MLS_O3_FORCE ! MLS data REAL*8, ALLOCATABLE :: MLS_LON(:) REAL*8, ALLOCATABLE :: MLS_LAT(:) REAL*8, ALLOCATABLE :: MLS_TIME(:) REAL*8, ALLOCATABLE :: MLS_O3(:,:) REAL*8, ALLOCATABLE :: MLS_O3_STD(:,:) REAL*8, ALLOCATABLE :: MLS_CN(:) REAL*8, ALLOCATABLE :: MLS_CON_QUAL(:) REAL*8, ALLOCATABLE :: MLS_STA_QUAL(:) REAL*8, ALLOCATABLE :: MLS_MAIN_QUAL(:) REAL*8, ALLOCATABLE :: MLS_VIEW_ZENITH(:) REAL*8, ALLOCATABLE :: MLS_SOLAR_ZENITH(:) REAL*8, ALLOCATABLE :: MLS_PRESSURE(:) ! MLS grid specification INTEGER :: N_MLS_OBS INTEGER :: N_MLS_ALT CONTAINS !--------------------------------------------------------------------------! SUBROUTINE READ_MLS_O3_FILE ( YYYYMMDD, HHMMSS ) USE ERROR_MOD, ONLY : ALLOC_ERR USE TIME_MOD, ONLY : EXPAND_DATE, GET_MONTH, GET_YEAR USE HDF5 ! Arguments INTEGER, INTENT(IN) :: YYYYMMDD, HHMMSS CHARACTER(LEN=255) :: DIR_MLS CHARACTER(LEN=255) :: DIR_MONTH_MLS CHARACTER(LEN=255) :: FILENAME_MLS CHARACTER(LEN=255) :: FILENAME_FULL CHARACTER(LEN=255) :: DSET_NAME INTEGER(HID_T) :: file_id, dset_id, dspace_id INTEGER(HSIZE_T) :: dims(2), maxdims(2), data_dims(2) INTEGER :: error CALL CLEANUP_MLS DIR_MLS = '/users/jk/16/xzhang/MLS_O3/' DIR_MONTH_MLS = 'YYYY/MM/' FILENAME_MLS = 'MLS-Aura_L2GP-O3_v04-20-c01_YYYYdMMDD.he5' CALL EXPAND_DATE(DIR_MLS, YYYYMMDD, 0) CALL EXPAND_DATE(DIR_MONTH_MLS, YYYYMMDD, 0) CALL EXPAND_DATE(FILENAME_MLS, YYYYMMDD, 0) FILENAME_FULL = TRIM(DIR_MLS) // TRIM(DIR_MONTH_MLS) // TRIM(FILENAME_MLS) PRINT *,"READING MLS File: ", FILENAME_FULL ! Initialize HDF5 Interface PRINT *,"INITIALIZE INTERFACE" CALL h5open_f(error) ! Open HDF5 file PRINT *,"OPEN FILE" CALL h5fopen_f (FILENAME_FULL, H5F_ACC_RDONLY_F, file_id, error) ! Read Time array PRINT *,"READING TIME ARRAY" DSET_NAME = '/HDFEOS/SWATHS/O3/Data Fields/L2gpValue' PRINT *,"OPENING DATA SET" CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) ! open dataspace CALL h5dget_space_f(dset_id, dspace_id, error) ! read in length of data arrays CALL h5sget_simple_extent_dims_f(dspace_id, dims, maxdims, error) ! close dataspace CALL h5sclose_f(dspace_id, error) N_MLS_ALT = dims(1) N_MLS_OBS = dims(2) ALLOCATE(MLS_TIME(N_MLS_OBS)) ALLOCATE(MLS_LON(N_MLS_OBS)) ALLOCATE(MLS_LAT(N_MLS_OBS)) !ALLOCATE(MLS_O3_COL(N_MLS_OBS)) ALLOCATE(MLS_O3(N_MLS_ALT,N_MLS_OBS)) ALLOCATE(MLS_CON_QUAL(N_MLS_OBS)) ALLOCATE(MLS_O3_STD(N_MLS_ALT,N_MLS_OBS)) ALLOCATE(MLS_MAIN_QUAL(N_MLS_OBS)) ALLOCATE(MLS_STA_QUAL(N_MLS_OBS)) ALLOCATE(MLS_CN(N_MLS_OBS)) ALLOCATE(MLS_VIEW_ZENITH(N_MLS_OBS)) ALLOCATE(MLS_SOLAR_ZENITH(N_MLS_OBS)) ALLOCATE(MLS_PRESSURE(N_MLS_ALT)) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_O3, data_dims, error) CALL h5dclose_f(dset_id,error) ! Read Time array DSET_NAME= '/HDFEOS/SWATHS/O3/Geolocation Fields/Time' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_TIME, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read Longitude array DSET_NAME= '/HDFEOS/SWATHS/O3/Geolocation Fields/Longitude' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_LON, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read Latitude array DSET_NAME= '/HDFEOS/SWATHS/O3/Geolocation Fields/Latitude' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_LAT, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read MLS O3 array DSET_NAME= '/HDFEOS/SWATHS/O3/Data Fields/L2gpValue' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_O3, data_dims, error) CALL h5dclose_f(dset_id,error) !PRINT *, "L2gpValue", MLS_O3(:,N_MLS_OBS) ! Read MLS O3 Precision array DSET_NAME= '/HDFEOS/SWATHS/O3/Data Fields/L2gpPrecision' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_O3_STD, data_dims, error) CALL h5dclose_f(dset_id,error) ! Read Quality array DSET_NAME= '/HDFEOS/SWATHS/O3/Data Fields/Quality' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_MAIN_QUAL, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read Status array DSET_NAME= '/HDFEOS/SWATHS/O3/Data Fields/Status' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_STA_QUAL, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read ChunkNumber array DSET_NAME= '/HDFEOS/SWATHS/O3/Geolocation Fields/ChunkNumber' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_CN, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read LineofSightAngle array DSET_NAME= '/HDFEOS/SWATHS/O3/Geolocation Fields/LineOfSightAngle' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_VIEW_ZENITH, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read Solar zenith Angle array DSET_NAME= '/HDFEOS/SWATHS/O3/Geolocation Fields/SolarZenithAngle' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_SOLAR_ZENITH, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) ! Read Convergence quality array DSET_NAME= '/HDFEOS/SWATHS/O3/Data Fields/Convergence' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_CON_QUAL, (/data_dims(2),0/), error) CALL h5dclose_f(dset_id,error) DSET_NAME= '/HDFEOS/SWATHS/O3/Geolocation Fields/Pressure' CALL h5dopen_f(file_id, DSET_NAME, dset_id, error) CALL h5dread_f(dset_id, H5T_NATIVE_DOUBLE, MLS_PRESSURE, (/data_dims(1),0/), error) CALL h5dclose_f(dset_id,error) ! close HDF5 file CALL h5fclose_f(file_id,error) ! close HDF5 interface CALL h5close_f(error) END SUBROUTINE READ_MLS_O3_FILE !-----------------------------------------------------------------------------! SUBROUTINE CALC_MLS_O3_FORCE !! !! Subroutine CALC_OMI_O3_FORCE computes the O3 adjoint forcing from OMI column data !! USE ADJ_ARRAYS_MOD, ONLY : STT_ADJ USE ADJ_ARRAYS_MOD, ONLY : N_CALC USE ADJ_ARRAYS_MOD, ONLY : EXPAND_NAME USE CHECKPT_MOD, ONLY : CHK_STT USE COMODE_MOD, ONLY : JLOP USE COMODE_MOD, ONLY : CSPEC_AFTER_CHEM USE COMODE_MOD, ONLY : CSPEC_AFTER_CHEM_ADJ USE DAO_MOD, ONLY : AD USE DAO_MOD, ONLY : AIRDEN USE DAO_MOD, ONLY : BXHEIGHT USE GRID_MOD, ONLY : GET_IJ USE TIME_MOD, ONLY : GET_HOUR USE TROPOPAUSE_MOD, ONLY : ITS_IN_THE_TROP USE ADJ_ARRAYS_MOD, ONLY : ID2C USE TRACERID_MOD, ONLY : IDO3, IDTOX USE ADJ_ARRAYS_MOD, ONLY : COST_FUNC USE TRACER_MOD, ONLY : XNUMOLAIR USE TRACER_MOD, ONLY : TCVV USE PRESSURE_MOD, ONLY : GET_PCENTER, GET_PEDGE USE DIRECTORY_ADJ_MOD, ONLY : DIAGADJ_DIR #include "CMN_SIZE" ! size parameters INTEGER :: I,J,K,L INTEGER :: I_MLS, J_MLS, JLOOP INTEGER :: IIJJ(2) ! variables for time unit conversion REAL*8 :: tai93 INTEGER :: iy,im,id,ih,imin REAL*8 :: sec INTEGER :: GC_HOUR ! variables for observation operator and adjoint thereof REAL*8 :: GC_O3(LLPAR) REAL*8 :: GC_O3_ADJ(IIPAR,JJPAR,LLPAR) REAL*8 :: GC_O3_COL REAL*8 :: CM22DU REAL*8 :: DIFF REAL*8 :: OBS_ERROR REAL*8 :: GC_PRES(LLPAR) REAL*8 :: MLS_O3_GC(LLPAR) REAL*8 :: MLS_O3_GC_STD(LLPAR) REAL*8 :: NCP(LLPAR) REAL*8 :: COUNT_GRID(IIPAR,JJPAR) REAL*8 :: COST_CONTRIB(LLPAR) ! arrays needed for superobservations LOGICAL :: SUPER_OBS = .TRUE. ! do super observations? REAL*8 :: SOBS_COUNT(IIPAR,JJPAR) ! super observation count REAL*8 :: SOBS_DIFF(IIPAR,JJPAR) REAL*8 :: SOBS_ADJ_FORCE(IIPAR,JJPAR,LLPAR) ! super observation adjoint forcing REAL*8 :: GC_ADJ_COUNT(IIPAR,JJPAR,LLPAR) REAL*8 :: NEW_COST(IIPAR,JJPAR) ! super observation cost function contribution REAL*8 :: SOBS_GC(IIPAR,JJPAR) REAL*8 :: SOBS_BIAS(IIPAR,JJPAR) REAL*8 :: SOBS_CHISQUARED(IIPAR,JJPAR) LOGICAL, SAVE :: FIRST = .TRUE. INTEGER :: IOS CHARACTER(LEN=255) :: FILENAME IF ( FIRST ) THEN FILENAME = 'gc_press_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 761, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'diff_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 762, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'gc_o3_stt_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 763, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'gc_o3_stt_adj_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 764, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'lat_orb_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 765, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'lon_orb_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 766, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'obs_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 769, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'err_mls.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 762, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) ENDIF ! Loop through data to find observations CM22DU = 2.69E16 ! conversion factor for DU -> #/cm2 GC_HOUR = GET_HOUR() ! initialize needed arrays and variables COUNT_GRID = 0d0 COST_CONTRIB = 0d0 GC_O3 = 0d0 GC_O3_ADJ = 0d0 GC_O3_COL = 0d0 MLS_O3_GC = 0d0 MLS_O3_GC_STD = 0d0 NCP = 0d0 SOBS_COUNT = 0d0 SOBS_DIFF = 0d0 SOBS_ADJ_FORCE = 0d0 NEW_COST = 0d0 GC_ADJ_COUNT = 0d0 DO I_MLS=1, N_MLS_OBS IF(MLS_TIME(I_MLS)>0) THEN ! There is an observation in the MLS grid box. ! Check if it was made within the current hour. tai93 = MLS_TIME(I_MLS) ! Convert TAI93 to UTC CALL TAI2UTC(tai93,iy,im,id,ih,imin,sec) IF ( ( GC_HOUR .EQ. ih ) .AND. & ( MLS_STA_QUAL(I_MLS) < 250 ) .AND. & ( ABS(MLS_LAT(I_MLS)) < 75d0 ) .AND. & !( MLS_SOLAR_ZENITH(I_MLS) < 75d0 ) .AND. & ( MLS_MAIN_QUAL(I_MLS) > 1.0 ) .AND. & ( MLS_CON_QUAL(I_MLS) < 1.03 ) ) THEN ! Get model grid coordinate indices that correspond to the observation IIJJ = GET_IJ(REAL(MLS_LON(I_MLS),4),REAL(MLS_LAT(I_MLS),4)) I = IIJJ(1) J = IIJJ(2) ! Get GEOS-CHEM O3 values GC_O3 = 0d0 GC_O3_COL = 0d0 MLS_O3_GC = 0d0 MLS_O3_GC_STD = 0d0 NCP = 0d0 COST_CONTRIB = 0d0 DO L = 1, LLPAR GC_PRES(L) = GET_PCENTER(I,J,L) ! CHK_STT is in units of [kg/box] here, convert to ppb IF( ( GET_PCENTER(I,J,L) < 215 ) .AND. ( GET_PCENTER(I,J,L) > 10 ) ) THEN GC_O3(L) = CHK_STT(I,J,L,IDTOX) * TCVV(IDTOX) / AD(I,J,L) DO J_MLS = 1, N_MLS_ALT IF( ( MLS_PRESSURE(J_MLS) >= GET_PEDGE(I,J,L+1) ) .AND. & ( MLS_PRESSURE(J_MLS) < GET_PEDGE(I,J,L) ) .AND. & ( MLS_PRESSURE(J_MLS) < 215 ) .AND. & ( MLS_PRESSURE(J_MLS) > 10 ) .AND. & ( MLS_O3(J_MLS,I_MLS) > 0 ) ) THEN MLS_O3_GC(L) = MLS_O3_GC(L) + MLS_O3(J_MLS,I_MLS) MLS_O3_GC_STD(L) = MLS_O3_GC_STD(L) + MLS_O3_STD(J_MLS,I_MLS)**2 NCP(L) = NCP(L) + 1 ENDIF ENDDO IF (NCP(L)>0) THEN MLS_O3_GC(L) = MLS_O3_GC(L)/NCP(L) !PRINT *, "MLS_O3_GC", MLS_O3_GC(L) MLS_O3_GC_STD(L) = (MLS_O3_GC_STD(L)**(0.5))/NCP(L) OBS_ERROR = MLS_O3_GC_STD(L) !OBS_ERROR(L) = 0.2 * MLS_O3_GC(L) DIFF = GC_O3(L) - MLS_O3_GC(L) PRINT *, "GC_O3", GC_O3(L) PRINT *, "MLS_O3_GC", MLS_O3_GC(L) !PRINT *, "DIFF(L)", DIFF(L) !PRINT *, "OBS_ERROR(L)", OBS_ERROR(L) COST_CONTRIB(L) = 0.5 * (DIFF/OBS_ERROR)**2 IF ( ( COST_CONTRIB(L) > 0d0) .AND. & ( COST_CONTRIB(L) < 200d0) .AND. & ( GET_PCENTER(I,J,L) < 215 ) .AND. & ( GET_PCENTER(I,J,L) > 10 ) ) THEN IF (SUPER_OBS) THEN SOBS_DIFF(I,J) = SOBS_DIFF(I,J) + DIFF NEW_COST(I,J) = NEW_COST(I,J) + COST_CONTRIB(L) SOBS_COUNT(I,J) = SOBS_COUNT(I,J) + 1 SOBS_ADJ_FORCE(I,J,L) = SOBS_ADJ_FORCE(I,J,L) + DIFF/(OBS_ERROR**2) * TCVV(IDTOX) / AD(I,J,L) GC_ADJ_COUNT(I,J,L) = GC_ADJ_COUNT(I,J,L) + 1 ENDIF ENDIF ENDIF ENDIF ENDDO ENDIF ENDIF ENDDO 110 FORMAT(F18.6,1X) IF (SUPER_OBS) THEN DO J = 1,JJPAR DO I = 1, IIPAR IF(SOBS_COUNT(I,J) > 0d0) THEN DO L = 1,LLPAR IF( ( GET_PCENTER(I,J,L) < 215 ) .AND. & ( GET_PCENTER(I,J,L) > 10 ) .AND. & ( GC_ADJ_COUNT(I,J,L) > 0 ) ) THEN STT_ADJ(I,J,L,IDTOX) = STT_ADJ(I,J,L,IDTOX) + SOBS_ADJ_FORCE(I,J,L)/SOBS_COUNT(I,J) ENDIF ENDDO COST_FUNC = COST_FUNC + NEW_COST(I,J)/SOBS_COUNT(I,J) ENDIF ENDDO ENDDO ENDIF !PRINT *, "STT_ADJ AFTER MLS O3", SOBS_ADJ_FORCE PRINT *, "COST FUNCTION OF MLS O3", COST_FUNC END SUBROUTINE CALC_MLS_O3_FORCE !----------------------------------------------------------------------------! SUBROUTINE CLEANUP_MLS IF (ALLOCATED( MLS_LON )) DEALLOCATE( MLS_LON ) IF (ALLOCATED( MLS_LAT )) DEALLOCATE( MLS_LAT ) IF (ALLOCATED( MLS_TIME )) DEALLOCATE( MLS_TIME ) IF (ALLOCATED( MLS_O3 )) DEALLOCATE( MLS_O3 ) IF (ALLOCATED( MLS_O3_STD )) DEALLOCATE( MLS_O3_STD ) IF (ALLOCATED( MLS_CON_QUAL )) DEALLOCATE( MLS_CON_QUAL ) IF (ALLOCATED( MLS_MAIN_QUAL )) DEALLOCATE( MLS_MAIN_QUAL ) IF (ALLOCATED( MLS_STA_QUAL )) DEALLOCATE( MLS_STA_QUAL ) IF (ALLOCATED( MLS_CN )) DEALLOCATE( MLS_CN ) IF (ALLOCATED( MLS_SOLAR_ZENITH )) DEALLOCATE( MLS_SOLAR_ZENITH ) IF (ALLOCATED( MLS_VIEW_ZENITH )) DEALLOCATE( MLS_VIEW_ZENITH ) IF (ALLOCATED( MLS_PRESSURE )) DEALLOCATE( MLS_PRESSURE ) END SUBROUTINE CLEANUP_MLS !-----------------------------------------------------------------------------! SUBROUTINE TAI2UTC(tai93,iy,im,id,ih,imin,sec) !! !! SUBROUTINE TAI2UTC converts TAI93 time (seconds since 1.1.1993) to UTC !! !! adapted from !! code.google.com/p/miyoshi/source/browse/trunk/common/common.f90 IMPLICIT NONE INTEGER,PARAMETER :: n=7 ! number of leap seconds after Jan. 1, 1993 INTEGER,PARAMETER :: leapsec(n) = (/ 15638399, 47174400, 94608001, 141868802, 189302403, 410227204, 504921605/) REAL*8,INTENT(IN) :: tai93 INTEGER,INTENT(OUT) :: iy,im,id,ih,imin REAL*8,INTENT(OUT) :: sec REAL*8,PARAMETER :: mins = 60.0d0 REAL*8,PARAMETER :: hour = 60.0d0*mins REAL*8,PARAMETER :: day = 24.0d0*hour REAL*8,PARAMETER :: year = 365.0d0*day INTEGER,PARAMETER :: mdays(12) = (/31,28,31,30,31,30,31,31,30,31,30,31/) REAL*8 :: wk,tai INTEGER :: days,i,leap tai = tai93 sec = 0.0d0 DO i=1,n IF(FLOOR(tai93) == leapsec(i)+1) THEN sec = 60.0d0 + tai93-FLOOR(tai93) ENDIF IF(FLOOR(tai93) > leapsec(i)) tai = tai -1.0d0 END DO iy = 1993 + FLOOR(tai /year) wk = tai - REAL(iy-1993)*year - FLOOR(REAL(iy-1993)/4.0)*day IF(wk < 0.0d0) THEN iy = iy -1 wk = tai - REAL(iy-1993)*year - FLOOR(REAL(iy-1993)/4.0)*day END IF days = FLOOR(wk/day) wk = wk - REAL(days)*day im = 1 DO i=1,12 leap = 0 IF(im == 2 .AND. MOD(iy,4)==0) leap=1 IF(im == i .AND. days >= mdays(i)+leap) THEN im = im + 1 days = days - mdays(i)-leap END IF END DO id = days +1 ih = FLOOR(wk/hour) wk = wk - REAL(ih)*hour imin = FLOOR(wk/mins) IF(sec < 60.0d0) sec = wk - REAL(imin)*mins RETURN END SUBROUTINE TAI2UTC END MODULE MLS_O3_OBS_MOD