Files
GEOS-Chem-adjoint-v35-note/code/obs_operators/tes_ch4_mod.f
2018-08-28 00:40:44 -04:00

4509 lines
161 KiB
Fortran

!$Id: tes_ch4_mod.f,v 1.2 2012/03/01 23:27:52 daven Exp $
MODULE TES_CH4_MOD
!
!******************************************************************************
! Module TES_CH4_MOD contains variables and routines which are used to
! assimilate real or simulated TES CH4 observations. The module is based on
! TES_NH3_MOD (kjw, 7/06/11)
! Added to adj32_023 (dkh, 02/12/12)
!******************************************************************************
!
IMPLICIT NONE
!=================================================================
! MODULE VARIABLES
!=================================================================
! Parameters
INTEGER, PARAMETER :: MAXLEV = 67
INTEGER, PARAMETER :: MAXTES = 2000
REAL*4, PARAMETER :: ERR_PPB = 40.0 !Stddev in TES obs
!LOGICAL :: LTES_PSO = .TRUE.
! Module Variables
REAL*4 :: BIAS_PPB
! Record to store data from each TES obs
TYPE TES_CH4_OBS
INTEGER :: LTES(1)
REAL*8 :: LAT(1)
REAL*8 :: LON(1)
REAL*8 :: TIME(1)
REAL*8 :: ERR(1)
REAL*8 :: CH4(MAXLEV)
REAL*8 :: GC_CH4(MAXLEV)
REAL*8 :: PRES(MAXLEV)
REAL*8 :: PRIOR(MAXLEV)
REAL*8 :: AVG_KERNEL(MAXLEV,MAXLEV)
REAL*8 :: S_OER(MAXLEV,MAXLEV)
REAL*8 :: S_OER_INV(MAXLEV,MAXLEV)
ENDTYPE TES_CH4_OBS
TYPE(TES_CH4_OBS) :: TES(MAXTES)
CONTAINS
!------------------------------------------------------------------------------
SUBROUTINE READ_TES_CH4_OBS( YYYYMMDD, NTES )
!
!******************************************************************************
! Subroutine READ_TES_CH4_OBS reads the file and passes back info contained
! therein. (dkh, 02/19/09)
!
! Based on READ_TES_NH3 OBS (dkh, 04/26/10)
!
! Arguments as Input:
! ============================================================================
! (1 ) YYYYMMDD INTEGER : Current year-month-day
!
! Arguments as Output:
! ============================================================================
! (1 ) NTES (INTEGER) : Number of TES retrievals for current day
!
! Module variable as Output:
! ============================================================================
! (1 ) TES (TES_CH4_OBS) : TES retrieval for current day
!
! NOTES:
! (1 ) Add calculation of S_OER_INV, though eventually we probably want to
! do this offline. (dkh, 05/04/10)
! (2 ) Now read data files in BPCH format for better compatibility with
! the standard GEOS-Chem distribution. (kjw, 06/05/10)
!******************************************************************************
!
! Reference to f90 modules
USE DIRECTORY_MOD, ONLY : DATA_DIR
USE DIRECTORY_ADJ_MOD, ONLY : DIAGADJ_DIR
USE TIME_MOD, ONLY : EXPAND_DATE
USE BPCH2_MOD, ONLY : READ_BPCH2, GET_TAU0
USE BPCH2_MOD, ONLY : OPEN_BPCH2_FOR_READ
USE TIME_MOD, ONLY : GET_YEAR, GET_MONTH, GET_DAY
USE FILE_MOD, ONLY : IU_FILE
USE ADJ_ARRAYS_MOD, ONLY : N_CALC, EXPAND_NAME
USE LOGICAL_ADJ_MOD, ONLY : LTES_PSO
! From READ_BPCH2
USE FILE_MOD, ONLY : IU_FILE, IOERROR
! Arguments
INTEGER, INTENT(IN) :: YYYYMMDD
INTEGER, INTENT(OUT) :: NTES
! local variables
INTEGER :: FID
INTEGER :: LTES
INTEGER :: NT
INTEGER :: YYYY, MM, DD
INTEGER :: START
CHARACTER(LEN=5) :: TMP
CHARACTER(LEN=255) :: READ_FILENAME
CHARACTER(LEN=255) :: FILENAME
LOGICAL :: file_exist
REAL*8, PARAMETER :: FILL = -999.0D0
REAL*8, PARAMETER :: TOL = 1d-04
REAL*8 :: U(MAXLEV,MAXLEV)
REAL*8 :: VT(MAXLEV,MAXLEV)
REAL*8 :: S(MAXLEV)
REAL*8 :: TMP1
REAL*8 :: XTAU
REAL*8 :: TEST(MAXLEV,MAXLEV)
! From READ_BPCH2
INTEGER :: I, J, L, LL, IOS
INTEGER :: NTRACER, NSKIP
INTEGER :: HALFPOLAR, CENTER180
INTEGER :: NI, NJ, NL
INTEGER :: IFIRST, JFIRST, LFIRST
REAL*4 :: LONRES, LATRES
REAL*8 :: ZTAU0, ZTAU1
CHARACTER(LEN=20) :: MODELNAME
CHARACTER(LEN=40) :: CATEGORY
CHARACTER(LEN=40) :: UNIT
CHARACTER(LEN=40) :: RESERVED
!Arrays in which to read BPCH files
REAL*4 :: DUMMY_NTES(1)
REAL*4 :: DUMMY_0D(MAXTES)
REAL*4 :: DUMMY_1D(MAXTES,MAXLEV,1)
REAL*4 :: DUMMY_2D(MAXTES,MAXLEV,MAXLEV)
!=================================================================
! READ_TES_CH4_OBS begins here!
!=================================================================
! filename root
READ_FILENAME = TRIM( 'tes_ch4_YYYYMMDD.bpch' )
!READ_FILENAME = TRIM( 'temp_test.bpch' )
! Expand date tokens in filename
CALL EXPAND_DATE( READ_FILENAME, YYYYMMDD, 9999 )
! Construct complete filename
READ_FILENAME = TRIM( '/home/kjw/TES/data/V004/bpch/' ) //
& TRIM( READ_FILENAME )
INQUIRE( FILE=READ_FILENAME, exist=file_exist )
! If there is no observation file for this day,
! Return to calling program
IF ( .not. file_exist ) THEN
WRITE(6,*) ' - READ_TES_CH4_OBS: file does not exist: ',
& TRIM( READ_FILENAME )
WRITE(6,*) ' no observations today.'
! Set NTES = 0 and Return to calling program
NTES = 0
RETURN
ENDIF
! Start Reading Data from BPCH
WRITE(6,*) ' - READ_TES_CH4_OBS: reading file: ',
& TRIM( READ_FILENAME )
! Read variables from bpch instead of netCDF (kjw, 06/05/10)
! 1. Open BPCH file for today if it exists. If it doesn't,
! return NTES = 0.
! 2. Read nTES (tracer=1) from bpch
! a. read LTES from bpch, store in TES struct
! b. read remaining 0-d data, store in struct
! c. read 1-d data, store in TES struct
! d. read 2-d data, store in TES struct
! READ nTES from BPCH. Tracer numbers correspond to the following
! variables in the TES BPCH files:
! Tracer # Variable
! 1 targets (# TES obs in file)
! 2 LTES (# good vertical levels in each obs)
! 3 Longitude
! 4 Latitude
! 5 YYYYMMDD
! 6 Species
! 7 Pressure
! 8 Constraint Vector
! 9 GEOS-Chem_obs
! 10 Averaging Kernel
! 11 Inverse of Observation Error Covar Matrix
!---------------------------------------------------------------
! Tau for the bpch file
YYYY = INT( floor( YYYYMMDD / 1d4 ) )
MM = INT( floor( YYYYMMDD - 1d4*YYYY ) / 1d2 )
DD = NINT( YYYYMMDD - 1d4*YYYY - 1d2*MM )
XTAU = GET_TAU0( MM, DD, YYYY )
! Number of TES observations in the file
WRITE(6,*) ' - Reading: NTES ... '
print*,'XTAU = ',XTAU
CALL READ_BPCH2( TRIM(READ_FILENAME), 'IJ-AVG-$', 1,
& XTAU, 1, 1,
& 1, DUMMY_NTES(1), QUIET=.TRUE. )
NTES = INT( DUMMY_NTES(1) )
print*, ' - Found # obs today: NTES = ,', NTES
!==================================================================
! Read data for each TES observation in the current day.
! Do NOT use READ_BPCH2 because output dimensions limited size
! of global 1x1 grid.
! The following lines are modified from READ_BPCH2 (kjw, 07/22/10)
!
! 0-D Data
! 2. # good vertical levels for each obs.
! 3. longitude
! 4. latitude
! 5. mmdd.frac-of-day
! 1-D Data
! 6. Species (CH4)
! 7. Pressure
! 8. Constraint Vector
! 9. GEOS-Chem_obs
! 2-D Data
! 10. Averaging Kernel
! 11. Inverse of Observation Error Covariance Matrix
!==================================================================
!=================================================================
! Open binary punch file and read top-of-file header.
! Do some error checking to make sure the file is the right format.
!=================================================================
CALL OPEN_BPCH2_FOR_READ( IU_FILE, READ_FILENAME )
!=================================================================
! Read data from the binary punch file
!
! NOTE: IOS < 0 is end-of-file, IOS > 0 is error condition
!=================================================================
DO
READ( IU_FILE, IOSTAT=IOS )
& MODELNAME, LONRES, LATRES, HALFPOLAR, CENTER180
IF ( IOS < 0 ) EXIT
IF ( IOS > 0 ) CALL IOERROR( IOS, IU_FILE, 'tes_ch4_mod:1')
READ( IU_FILE, IOSTAT=IOS )
& CATEGORY, NTRACER, UNIT, ZTAU0, ZTAU1, RESERVED,
& NI, NJ, NL, IFIRST, JFIRST, LFIRST,
& NSKIP
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'tes_ch4_mod:2' )
! Place array into DUMMY_2D
DUMMY_2D(:,:,:) = 0d0
READ( IU_FILE, IOSTAT=IOS )
& ( ( ( DUMMY_2D(I,J,L), I=1,NI ), J=1,NJ ), L=1,NL )
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'tes_ch4_mod:3' )
! Test for a match
IF ( 'IJ-AVG-$' == TRIM( CATEGORY ) .and. XTAU == ZTAU0 ) THEN
! LTES
IF ( NTRACER == 2 ) THEN
WRITE(6,*) ' - Reading: LTES ... '
TES(1:NTES)%LTES(1) = DUMMY_2D(1:NTES,1,1)
! Longitude
ELSEIF ( NTRACER == 3 ) THEN
WRITE(6,*) ' - Reading: Longitude ... '
TES(1:NTES)%LON(1) = DUMMY_2D(1:NTES,1,1)
! Latitude
ELSEIF ( NTRACER == 4 ) THEN
WRITE(6,*) ' - Reading: Latitude ... '
TES(1:NTES)%LAT(1) = DUMMY_2D(1:NTES,1,1)
! MMDD.frac-of-day
ELSEIF ( NTRACER == 5 ) THEN
WRITE(6,*) ' - Reading: Frac-of-day ... '
TES(1:NTES)%TIME(1) = DUMMY_2D(1:NTES,1,1) +
& GET_YEAR()*1d4
! Species (CH4)
ELSEIF ( NTRACER == 6 ) THEN
WRITE(6,*) ' - Reading: CH4 ... '
DO NT=1,NTES
LTES = TES(NT)%LTES(1)
START = MAXLEV - LTES + 1
TES(NT)%CH4(1:LTES) = DUMMY_2D(NT,START:MAXLEV,1)
ENDDO
! Pressure
ELSEIF ( NTRACER == 7 ) THEN
WRITE(6,*) ' - Reading: Pressure ... '
DO NT=1,NTES
LTES = TES(NT)%LTES(1)
START = MAXLEV - LTES + 1
TES(NT)%PRES(1:LTES) = DUMMY_2D(NT,START:MAXLEV,1)
ENDDO
! Constraint Vector
ELSEIF ( NTRACER == 8 ) THEN
WRITE(6,*) ' - Reading: Constraint Vector ... '
DO NT=1,NTES
LTES = TES(NT)%LTES(1)
START = MAXLEV - LTES + 1
TES(NT)%PRIOR(1:LTES) = DUMMY_2D(NT,START:MAXLEV,1)
ENDDO
! ! Kind of Useless now that LTES_PSO created, kjw 07/25/10
! ! GEOS-Chem Obs
ELSEIF ( NTRACER == 9 ) THEN
WRITE(6,*) ' - Reading: GEOS-Chem Obs ... '
DO NT=1,NTES
LTES = TES(NT)%LTES(1)
START = MAXLEV - LTES + 1
TES(NT)%GC_CH4(1:LTES) = DUMMY_2D(NT,START:MAXLEV,1)
ENDDO
! Averaging Kernel
ELSEIF ( NTRACER == 10) THEN
WRITE(6,*) ' - Reading: Averaging Kernel ... '
DO NT=1,NTES
LTES = TES(NT)%LTES(1)
START = MAXLEV - LTES + 1
TES(NT)%AVG_KERNEL(1:LTES,1:LTES) =
& DUMMY_2D(NT,START:MAXLEV,START:MAXLEV)
ENDDO
! Inverse of Observation Error Covariance Matrix
ELSEIF ( NTRACER == 11) THEN
WRITE(6,*) ' - Reading: S_OER_INV ... '
DO NT=1,NTES
LTES = TES(NT)%LTES(1)
START = MAXLEV - LTES + 1
TES(NT)%S_OER_INV(1:LTES,1:LTES) =
& DUMMY_2D(NT,START:MAXLEV,START:MAXLEV)
ENDDO
ENDIF ! If tracer == #
ENDIF ! If Category and Tau match
ENDDO
! Close today's BPCH file of TES observations
CLOSE( IU_FILE )
! Read Errors and populate TES%GC_CH4 if using pseudo-obs
IF ( LTES_PSO ) THEN
! Make pseudo observations and save in TES%GC_CH4
CALL MAKE_PSEUDO_OBS( YYYYMMDD, NTES )
ENDIF
! ! Save AK and S_OER_INV for one observation.
! ! The plot to make sure they are correct order
! WRITE(6,'(a)') ' Writing AK and S_OER_INV files'
! FILENAME = 'test_ak.NN.m'
! CALL EXPAND_NAME( FILENAME, N_CALC )
! print*,'FILENAME1 = ',FILENAME
! FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
! OPEN( IU_FILE, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
! & IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
!
! ! Save observation # 600
! LTES = TES(600)%LTES(1)
! print*,'LTES of obs # 600 = ',LTES
! DO L=1,LTES
! WRITE(IU_FILE,'(65F16.12)') (TES(600)%AVG_KERNEL(L,LL),
! & LL=1,LTES)
! ENDDO
! CLOSE(IU_FILE)
!
! FILENAME = 'test_s_obs.NN.m'
! CALL EXPAND_NAME( FILENAME, N_CALC )
! print*,'FILENAME2 = ',FILENAME
! FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
! OPEN( 189, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
! & IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
!
!
! ! Save observation # 600
! LTES = TES(600)%LTES(1)
! print*,'LTES of obs # 600 = ',LTES
! DO L=1,LTES
! WRITE(IU_FILE,'(65F16.12)') (TES(600)%S_OER_INV(L,LL),
! & LL=1,LTES)
! ENDDO
! WRITE(6,'(a)') ' Done writing AK and S_OER_INV files'
!
! CLOSE(IU_FILE)
! Check reading against values read from BPCH in IDL
!print*,'TES(600)%LTES = ',TES(600)%LTES(1)
!print*,'TES(600)%LON = ',TES(600)%LON(1)
!print*,'TES(600)%LAT = ',TES(600)%LAT(1)
!print*,'TES(600)%TIME = ',TES(600)%TIME(1)
!print*,'TES(600)%PRES = ',TES(600)%PRES
!print*,'TES(600)%CH4 = ',TES(600)%CH4
!print*,'TES(600)%AK = ',TES(600)%AVG_KERNEL(1:4,1)
!print*,'TES(600)%ERR = ',TES(600)%ERR(1)
! Success as of kjw, 07/24/10
! Return to calling program
END SUBROUTINE READ_TES_CH4_OBS
!------------------------------------------------------------------------------
SUBROUTINE CALC_TES_CH4_FORCE( COST_FUNC )
!
!******************************************************************************
! Subroutine CALC_TES_CH4_FORCE calculates the adjoint forcing from the TES
! CH4 observations and updates the cost function. (dkh, 02/15/09)
!
!
! Arguments as Input/Output:
! ============================================================================
! (1 ) COST_FUNC (REAL*8) : Cost funciton [unitless]
!
!
! NOTES:
! (1 ) Updated to GCv8 (dkh, 10/07/09)
! (1 ) Add more diagnostics. Now read and write doubled CH4 (dkh, 11/08/09)
!******************************************************************************
!
! Reference to f90 modules
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 DAO_MOD, ONLY : AD
USE DAO_MOD, ONLY : AIRDEN
USE DAO_MOD, ONLY : BXHEIGHT
USE DAO_MOD, ONLY : TROPP
USE DIRECTORY_ADJ_MOD, ONLY : DIAGADJ_DIR
USE GRID_MOD, ONLY : GET_IJ
USE PRESSURE_MOD, ONLY : GET_PCENTER, GET_PEDGE
USE TIME_MOD, ONLY : GET_NYMD, GET_NHMS, GET_YEAR
USE TIME_MOD, ONLY : GET_MONTH, GET_DAY, GET_HOUR
USE TIME_MOD, ONLY : GET_TS_CHEM, EXPAND_DATE
USE TRACER_MOD, ONLY : XNUMOLAIR, XNUMOL
USE TROPOPAUSE_MOD, ONLY : ITS_IN_THE_TROP
!kjw
USE TRACER_MOD, ONLY : STT
USE FILE_MOD, ONLY : IU_FILE
USE TIME_MOD, ONLY : GET_TAUe, GET_TAU
USE LOGICAL_ADJ_MOD, ONLY : LTES_PSO
!kjw
# include "CMN_SIZE" ! Size params
! Arguments
REAL*8, INTENT(INOUT) :: COST_FUNC
! Local variables
INTEGER :: NTSTART, NTSTOP, NT
INTEGER :: IIJJ(2), I, J
INTEGER :: L, LL, LTES
INTEGER :: JLOOP
REAL*8 :: GC_PRES(LLPAR)
REAL*8 :: GC_CH4_NATIVE(LLPAR)
REAL*8 :: GC_CH4(MAXLEV)
REAL*8 :: GC_PSURF
REAL*8 :: MAP(LLPAR,MAXLEV)
REAL*8 :: CH4_HAT(MAXLEV)
REAL*8 :: CH4_HAT_EXP(MAXLEV)
REAL*8 :: CH4_PERT(MAXLEV)
REAL*8 :: FORCE
REAL*8 :: DIFF
REAL*8 :: NEW_COST(MAXTES)
REAL*8 :: OLD_COST
REAL*8, SAVE :: TIME_FRAC(MAXTES)
INTEGER,SAVE :: NTES
REAL*8 :: DOFS
CHARACTER :: F117_STATUS
!kjw for testing adjoint of tes obs operator
REAL*8 :: ADJ(LLPAR)
REAL*8 :: ADJ_SAVE(LLPAR)
REAL*8 :: PERT(LLPAR)
REAL*8 :: FD_CEN(LLPAR)
REAL*8 :: FD_POS(LLPAR)
REAL*8 :: FD_NEG(LLPAR)
REAL*8 :: COST_FUNC_0
REAL*8 :: COST_FUNC_1
REAL*8 :: COST_FUNC_2
LOGICAL :: ori
!kjw for testing adjoint of tes obs operator
REAL*8 :: GC_CH4_NATIVE_ADJ(LLPAR)
REAL*8 :: CH4_HAT_ADJ(MAXLEV)
REAL*8 :: CH4_HAT_EXP_ADJ(MAXLEV)
REAL*8 :: CH4_PERT_ADJ(MAXLEV)
REAL*8 :: GC_CH4_ADJ(MAXLEV)
REAL*8 :: DIFF_ADJ
REAL*4 :: S_obs_inv
REAL*8 :: GC_avg
REAL*8 :: OBS_avg
REAL*8 :: GC_avg_ADJ
! REAL*8 :: Pres_ln(MAXLEV)
! REAL*8 :: Pedges_ln(MAXLEV)
! REAL*8 :: Pedges(MAXLEV)
! REAL*8 :: Pdiff(MAXLEV)
! REAL*8 :: Nmolec(MAXLEV)
! REAL*8 :: DIFF_onTES(MAXLEV)
! REAL*8 :: Totmolec
REAL*8 :: OBS_RTVMR, GC_RTVMR
REAL*8 :: OBS_RTVMR_ADJ, GC_RTVMR_ADJ
REAL*8 :: M_STAR(4,MAXLEV)
INTEGER :: reg
REAL*8 :: JJ_this(9)
REAL*8 :: Jforce_this(9)
REAL*8 :: Jdiff_this(9)
LOGICAL, SAVE :: FIRST = .TRUE.
LOGICAL, SAVE :: VERYFIRST = .TRUE.
LOGICAL, SAVE :: GOD = .FALSE.
INTEGER :: IOS
CHARACTER(LEN=255) :: FILENAME
!=================================================================
! CALC_TES_CH4_FORCE begins here!
!=================================================================
print*, ' - CALC_TES_CH4_FORCE '
!------------------GOD RETRIEVAL-------------------
! The god retrieval assumes perfect observations in every grid box
! at all time steps. The retrieval should perfectly reproduce
! the "true" emission field in identical twin tests. If it does
! not, then there is a bug in the code. (kjw, 10/07/10)
!
! OUTLINE
! A. Get STT in [ppb]
! B. Get pseudo-obs in [ppb]
! C. Calculate forcing and adjoint variable
!
! If GOD == .TRUE. THEN do GOD retrieval
IF ( GOD == .TRUE. ) THEN
! Save a value of the cost function
OLD_COST = COST_FUNC
! A. Get STT in [ppb]
! B. Get pseudo-obs in [ppb]
! C. Calculate forcing and adjoint variable
! Return to calling program
RETURN
ENDIF ! End if GOD == .TRUE.
!--------------------------------------------------
!kjw for testing
ori=.TRUE.
!kjw for testing
! Reset
NEW_COST = 0D0
JJ_this(:) = 0d0
Jforce_this(:) = 0d0
Jdiff_this(:) = 0d0
! Calculate TES vs. GEOS-Chem bais
IF ( VERYFIRST .AND. (LTES_PSO == .FALSE.) ) CALL CALC_TES_GC_BIAS
VERYFIRST = .FALSE.
! Open files for diagnostic output
IF ( FIRST ) THEN
! Open files for diagnostic output
FILENAME = 'pres.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 101, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'gc_ch4.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 102, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'tes_ch4.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 103, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'apriori.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 104, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'diff.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 105, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'force.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 106, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'nt_ll.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 107, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'ch4_pert_adj.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 108, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'gc_ch4_adj.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 109, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'exp_ch4_hat.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 110, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'gc_press.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 111, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'gc_ch4_native.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 112, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'gc_ch4_on_tes.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 113, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'gc_ch4_on_tes_woStrat.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 115, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
FILENAME = 'gc_ch4_native_adj.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 114, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
!kjw for testing adjoint of obs operator
FILENAME = 'test_adjoint_obs.NN.m'
CALL EXPAND_NAME( FILENAME, N_CALC )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( 116, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
ENDIF
! Save a value of the cost function first
OLD_COST = COST_FUNC
! Check if it is the last hour of a day
!kjw. Change this for methane or else we'll never read a file.
!kjw. For forcing every 1 hour, the following line should work:
! IF ( GET_NHMS() == 230000 ) THEN
IF ( GET_NHMS() == 230000 ) THEN
! Read the TES CH4 file for this day
CALL READ_TES_CH4_OBS( GET_NYMD(), NTES )
! If NTES = 0, it means there are no observations today.
! Return to calling procedure
IF ( NTES == 0 ) THEN
WRITE(6,*) ' No TES CH4 obs today. Returning 01 ... '
RETURN
ENDIF
! TIME is YYYYMMDD.frac-of-day. Subtract date and save just time frac
TIME_FRAC(1:NTES) = TES(1:NTES)%TIME(1) - GET_NYMD()
ENDIF
! If NTES = 0, it means there are no more observations today.
! Return to calling procedure
IF ( NTES == 0 ) THEN
WRITE(6,*) ' No TES CH4 obs today. Returning 02 ... '
RETURN
ENDIF
! Get the range of TES retrievals for the current hour
CALL GET_NT_RANGE( NTES, GET_NHMS(), TIME_FRAC, NTSTART, NTSTOP )
IF ( NTSTART == 0 .and. NTSTOP == 0 ) THEN
print*, ' No matching TES CH4 obs for this hour'
RETURN
ENDIF
print*, ' for hour range: ', GET_NHMS(), TIME_FRAC(NTSTART),
& TIME_FRAC(NTSTOP)
print*, ' found record range: ', NTSTART, NTSTOP
! Calculate S_obs_inv for stddev of ERR_PPB [ppb]
! Expected difference = ln( 1800 +/- ERR_PPB ) - ln( 1800 )
! = ln( ( 1800 +/- ERR_PPB ) / 1800 )
!S_obs_inv = 1. / ( LOG( ( 1800. + ERR_PPB ) / 1800. ) )**2
S_obs_inv = 1. / ( (ERR_PPB*1d-9) ** 2 )
print*,'kjw debug: calculate S_obs_inv.'
print*, 'ERR_PPB = ', ERR_PPB
!print*,'S_obs_inv (should be ~2070) = ',S_obs_inv
print*,'S_obs_inv (should be ~6.25e14) = ',S_obs_inv
! Open file for this hour's satellite diagnostics
FILENAME = 'diag_sat.YYYYMMDD.hhmm.NN'
CALL EXPAND_NAME( FILENAME, N_CALC )
CALL EXPAND_DATE( FILENAME, GET_NYMD(), GET_NHMS() )
FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
OPEN( IU_FILE, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
& IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
! need to update this in order to do i/o with this loop parallel
!! ! Now do a parallel loop for analyzing data
!!$OMP PARALLEL DO
!!$OMP+DEFAULT( SHARED )
!!$OMP+PRIVATE( NT, MAP, LTES, IIJJ, I, J, L, LL, JLOOP )
!!$OMP+PRIVATE( GC_PRES, GC_PSURF, GC_CH4, DIFF )
!!$OMP+PRIVATE( GC_CH4_NATIVE, CH4_PERT, CH4_HAT, FORCE )
!!$OMP+PRIVATE( GC_CH4_NATIVE_ADJ, GC_CH4_ADJ )
!!$OMP+PRIVATE( CH4_PERT_ADJ, CH4_HAT_ADJ )
!!$OMP+PRIVATE( DIFF_ADJ )
DO NT = NTSTART, NTSTOP, -1
!IF ( NT .EQ. 600 ) THEN
print*, ' - CALC_TES_CH4_FORCE: analyzing record ', NT
! For safety, initialize these up to LLTES
GC_CH4(:) = 0d0
MAP(:,:) = 0d0
CH4_HAT_ADJ(:) = 0d0
FORCE = 0d0
! Copy LTES to make coding a bit cleaner
LTES = TES(NT)%LTES(1)
! Get grid box of current record
IIJJ = GET_IJ( REAL(TES(NT)%LON(1),4), REAL(TES(NT)%LAT(1),4))
I = IIJJ(1)
J = IIJJ(2)
! ! dkh debug
! print*, 'I,J = ', I, J
! print*,TES(NT)%TIME(1)
! print*,TES(NT)%LAT(1)
! print*,TES(NT)%LON(1)
! Get GC pressure levels (mbar)
DO L = 1, LLPAR
GC_PRES(L) = GET_PCENTER(I,J,L)
ENDDO
! Get GC surface pressure (mbar)
GC_PSURF = GET_PEDGE(I,J,1)
! Calculate the interpolation weight matrix
MAP(1:LLPAR,1:LTES)
& = GET_INTMAP( LLPAR, GC_PRES(:), GC_PSURF,
& LTES, TES(NT)%PRES(1:LTES), TES(NT)%PRES(1) )
! Get CH4 values at native model resolution
!DO L = 1, LLPAR
!kjw. JLOP and CSPEC are variables in comode_mod.f, associated
! with smvgear. For getting CH4 values at native model
! resolution, write my own thing, it'll probably have to do
! with using CH4 from STT when below tropopause, and using
! the TES retrieval above the tropopause. Therefore, there
! will be no adjoint forcing above the tropopause.
!kjw.
!kjw
! Get CH4 from restored STT array
! Find out units of STT (I think it's [kg/box]).
! If so, convert to [v/v] before next step
GC_CH4_NATIVE(:) = CHK_STT(I,J,:,1)
! print*,'CHK_STT(14,14,14,1) = ',CHK_STT(14,14,14,1)
! print*,' STT(14,14,14,1) = ',STT(14,14,14,1)
! Unit Conversion
DO L=1,LLPAR
! Convert from [kg/box] --> [v/v]
! Numerator = moles CH4/box
! Denominator = moles air/box
GC_CH4_NATIVE(L) = (GC_CH4_NATIVE(L)*XNUMOL(1)/6.022d23 ) /
& ( AD(I,J,L) * XNUMOLAIR / 6.022d23 )
!!!! Bypass unit conversion
!!!GC_CH4_NATIVE(:) = GC_CH4_NATIVE(:)
ENDDO
! Interpolate GC CH4 column to TES grid
DO LL = 1, LTES
GC_CH4(LL) = 0d0
DO L = 1, LLPAR
GC_CH4(LL) = GC_CH4(LL)
& + MAP(L,LL) * GC_CH4_NATIVE(L)
ENDDO
ENDDO
IF ( NT == 600 ) THEN
!print*,'LTES = ',LTES
!print*,'LLPAR = ',LLPAR
!print*,'GC_PSURF = ',GC_PSURF
!WRITE(6,'(a)') 'GEOS-Chem pressure grid'
!WRITE(6,'(F10.3)') ( GC_PRES(L), L=1,47 )
!WRITE(6,'(a)') 'TES pressure grid'
!WRITE(6,'(F10.3)') ( TES(NT)%PRES(L), L=1,65 )
!WRITE(6,'(a)') '20th row of MAP matrix in CALC_FORCE'
!WRITE(6,'(5F8.5)') ( MAP(20:24,L) , L=1,65 )
!print*,'GC_CH4 (observation) = ', TES(NT)%GC_CH4(10)
!print*,'CH4 (model) = ', GC_CH4(10)
ENDIF
! IF ( NT == 600 ) THEN
! ! dkh debug: compare profiles:
! print*, ' GC_PRES, GC_native_CH4 [ppb] '
! WRITE(6,100) (GC_PRES(L), GC_CH4_NATIVE(L)*1d9,
! & L = LLPAR, 1, -1 )
! print*, ' TES_PRES, GC_CH4 '
! WRITE(6,100) (TES(NT)%PRES(LL),
! & GC_CH4(LL)*1d9, LL = LTES, 1, -1 )
! ENDIF
! 100 FORMAT(1X,F16.8,1X,F16.8)
!--------------------------------------------------------------
! Apply TES observation operator
!
! x_hat = x_a + A_k ( x_m - x_a )
!
! where
! x_hat = GC modeled column as seen by TES [lnvmr]
! x_a = TES apriori column [lnvmr]
! x_m = GC modeled column [lnvmr]
! A_k = TES averaging kernel
!--------------------------------------------------------------
! x_m - x_a
DO L = 1, LTES
GC_CH4(L) = MAX(GC_CH4(L), 1d-10)
CH4_PERT(L) = LOG(GC_CH4(L)) - LOG(TES(NT)%PRIOR(L))
ENDDO
! !kjw testing
! IF ( ori .EQ. .TRUE. ) THEN
! print*,'CH4_PERT(13) = ',CH4_PERT(13)
! ENDIF
! !kjw testing
!!!!!! ! Bypass !x_m - x_a
!!!!!! CH4_PERT(:) = GC_CH4(:)
!!!!!!
! x_a + A_k * ( x_m - x_a )
! AVG_KERNEL indexing may look backwards because BPCH files storing AK
! values use IDL column major indexing.
DO L = 1, LTES
CH4_HAT(L) = 0d0
DO LL = 1, LTES
CH4_HAT(L) = CH4_HAT(L)
& + TES(NT)%AVG_KERNEL(LL,L) * CH4_PERT(LL)
ENDDO
CH4_HAT(L) = CH4_HAT(L) + LOG(TES(NT)%PRIOR(L))
ENDDO
! Indexing of Averaging Kernel is seemingly backwards because
! TES observation files processed in IDL, which is column major
!!! ! Bypass !x_a + A_k * ( x_m - x_a )
!!! CH4_HAT(:) = CH4_PERT(:)
!!!
! !kjw testing
! IF ( ori .EQ. .TRUE. ) THEN
! print*,'CH4_HAT(13) = ',CH4_HAT(13)
! ENDIF
! !kjw testing
! !--------------------------------------------------------------
! ! Calculate column average ln(vmr) weighted by # density of TES grid
! ! This operation is self adjoint.
! ! To get # density [molec / m^2], get pressure differences in grid.
! ! Get kg/m^2 of air in each box. (F=ma) dP=kg*g
! ! Get molec/m^2 air molec/m2 = kg/m2 * XNUMOLAIR
! ! TES pressure grid linear in ln(pres). Get pressure at edge of boxes
! !--------------------------------------------------------------
! pres_ln(1:LTES)=LOG(TES(NT)%pres(1:LTES)) ! [hPa] --> ln([hPa])
! Pedges_ln(1)=pres_ln(1) ! Bottom edge is surface pressure
! DO L=2,LTES-1
! Pedges_ln(L) = ( pres_ln(L) + pres_ln(L+1) ) / 2.
! ENDDO
! Pedges=EXP(pedges_ln) ! ln([hPa]) --> [hPa]
! Pedges(LTES)=0 ! Top of atmosphere
! !print*,' kjw debug: TES pressure edges'
! !print*, Pedges
!
! ! Calculate pressure difference of each LTES-1 boxes
! DO L=1,LTES-1
! Pdiff(L) = Pedges(L) - Pedges(L+1)
! ENDDO
!
! ! Calculate # molecules air in each LTES-1 obxes
! Pdiff(:) = 100 * Pdiff(:) ! [hPa] --> [Pa]
! Pdiff(:) = Pdiff(:) / 9.8 ! [hPa] --> [kg]
! Nmolec(:)= Pdiff(:) * XNUMOLAIR ! [kg] --> [molec]
! Totmolec = 0d0
! DO L=1,LTES-1
! IF ( TROPP(I,J) < Pedges(L) ) THEN
! Totmolec = Totmolec + Nmolec(L)
! ENDIF
! ENDDO
!
! IF ( NT .EQ. 600 ) THEN
! print*,' kjw debug:# molecules in column (should be ~2e29)'
! print*, Totmolec
! print*,' kjw debug: Nmolec'
! DO L=1,LTES
! IF ( TROPP(I,J) < Pedges(L) ) THEN
! print*, Nmolec(L)/Totmolec
! ENDIF
! ENDDO
! print*,'SUM(Nmolec(1:22)/Totmolec)',
! & SUM(Nmolec(1:22)/Totmolec)
! print*,'SUM(NMOLEC) = ',SUM(Nmolec)
! ENDIF
!
! ! Calculate column average ln(vmr) weighted by # density of TES levels
! ! Only include levels with tropospheric air
! OBS_avg = 0d0
! GC_avg = 0d0
! DIFF_onTES(:) = 0d0
! DIFF_onTES(1:LTES) = LOG(TES(NT)%GC_CH4(1:LTES)) -
! & CH4_HAT(1:LTES)
! DO L=1,LTES-1
! IF ( TROPP(I,J) < Pedges(L) ) THEN
! OBS_avg = OBS_avg +
! & LOG(TES(NT)%GC_CH4(L+1)) * Nmolec(L)/Totmolec
! GC_avg = GC_avg +
! & CH4_HAT(L+1) * Nmolec(L)/Totmolec
! ENDIF
! ENDDO
! Transform from [ln(vmr)] --> [vmr]
CH4_HAT_EXP = EXP(CH4_HAT)
! Calculate RTVMR for profiles.
CALL GET_RTVMR( NT, TES(NT)%CH4, OBS_RTVMR, M_STAR )
CALL GET_RTVMR( NT, CH4_HAT_EXP, GC_RTVMR, M_STAR )
! kjw debug. Check RTVMR stuff
!IF ( NT == 600 ) THEN
! print*,'Check RTVMR stuff'
! print*,'Lat, Lon, PSURF of observation #600 '
! print*,TES(NT)%LAT(1),TES(NT)%LON(1),TES(NT)%PRES(1)
! print*,'GC_RTVMR = ',GC_RTVMR
! print*,'CH4_HAT = ',CH4_HAT
!ENDIF
! Retrieve RTVMR from TES structure for pseudo-observations
! TES(NT)%GC_CH4(67) set during SUBROUTINE MAKE_PSEUDO_OBS
IF ( LTES_PSO ) THEN
OBS_RTVMR = 0d0
OBS_RTVMR = TES(NT)%GC_CH4(67)
ENDIF
! DIFF = model - obs. units: [ln(vmr)] / m^2
!IF ( NT == 600 ) THEN
! print*,'Error difference [ppb]: ',TES(NT)%ERR(1)*1d9
! print*,'GC_RTVMR = ',GC_RTVMR*1d9
! print*,'OBS_RTVMR = ',OBS_RTVMR*1d9
! ! WRITE(6, '(a)') 'Pseudo-obs GEOS-Chem '
! ! WRITE(6, 545) ( TES(NT)%GC_CH4(L), EXP(CH4_HAT(L)),
! & ! L=1,LTES )
!ENDIF
545 FORMAT(F16.14,2x,F16.14)
! Calculate DOFS for satellite diagnostic file
DOFS = 0d0
DO L=1,LTES
DOFS = DOFS + TES(NT)%AVG_KERNEL(L,L)
ENDDO
!--------------------------------------------------------------
! Calculate cost function, given S is error on ln(vmr)
! J = [ model - obs ]^T S_{obs}^{-1} [ model - obs ]
!--------------------------------------------------------------
! If using pseudo-obs, do not apply bias
DIFF = GC_RTVMR - OBS_RTVMR! + BIAS_PPB * 1d-9
! Calculate DIFF^T * S_{obs}^{-1} * DIFF
FORCE = 0d0
FORCE = 2 * DIFF * S_obs_inv
NEW_COST(NT) = 0.5d0 * DIFF * FORCE
! Write satellite information to file
! I,J,LAT,LON,TIME,HOUR,model RTVMR,obs RTVMR,DOFS
IF ( NT == NTSTART ) THEN
WRITE(IU_FILE,301) 'I','J','LAT','LON','MONTH','DAY','HOUR',
& 'TIME_FRAC','MODEL_RTVMR','OBS_RTVMR','DOFS',
& 'ERROR'
ENDIF
WRITE(IU_FILE,302) I,J,TES(NT)%LAT(1),TES(NT)%LON(1),
& GET_MONTH(), GET_DAY(), GET_HOUR(),
& TIME_FRAC(NT),
& 1e9*GC_RTVMR, 1e9*OBS_RTVMR, DOFS,
& 1e9*TES(NT)%ERR(1)
301 FORMAT(A4,2x,A4,2x,A8, 2x,A8, 2x,A6,2x,A4,2x,A4,2x,A16, 2x,
& A12, 2x,A12, 2x,A7,2x,A8)
302 FORMAT(I4,2x,I4,2x,F8.3,2x,F8.3,2x,I4,2x,I4,2x,I4,2x,F16.13,2x,
& F12.4,2x,F12.4,2x,F7.3,2x,F8.3)
! ! Calculate difference between modeled and observed profile
! ! Eliminate stratospheric forcing in this
! DO L = 1, LTES
! IF ( TROPP(I,J) < TES(NT)%PRES(L) ) THEN
! DIFF(L) = CH4_HAT(L) - LOG( TES(NT)%GC_CH4(L) )
! ELSE
! DIFF(L) = 0d0
! ENDIF
! ENDDO
!
!
! ! Calculate 1/2 * DIFF^T * S_{obs}^{-1} * DIFF
! DO L = 1, LTES
! FORCE(L) = 0d0
! !FORCE(L) = FORCE(L) + TES(NT)%S_OER_INV(L,L) * DIFF(L)
! DO LL = 1, LTES
! FORCE(L) = FORCE(L) + TES(NT)%S_OER_INV(L,LL) * DIFF(LL)
! ENDDO
! NEW_COST(NT) = NEW_COST(NT) + 0.5d0 * DIFF(L) * FORCE(L)
! ENDDO
!IF ( NT == 600 ) THEN
! print*,'DIFF = ', DIFF
! print*,'FORCE = ',FORCE
! print*,'NEW_COST = ',NEW_COST(NT)
!ENDIF
! ! dkh debug: compare profiles:
! print*, ' CH4_HAT, CH4_TES, CH4_GC [ppb]'
! WRITE(6,090) ( 1d9 * EXP(CH4_HAT(L)),
! & 1d9 * TES(NT)%CH4(L),
! & 1d9 * TES(NT)%GC_CH4(L),
! & L, L = LTES, 1, -1 )
!
! print*, ' TES_PRIOR, CH4_HAT, CH4_GC [lnvmr], diag(S^-1)'
! WRITE(6,101) ( LOG(TES(NT)%PRIOR(L)), CH4_HAT(L),
! & LOG(TES(NT)%GC_CH4(L)), TES(NT)%S_OER_INV(L,L),
! & L, L = LTES, 1, -1 )
! ENDIF
! 090 FORMAT(1X,F16.8,1X,F16.8,1X,F16.8,1X,i3)
! 101 FORMAT(1X,F16.8,1X,F16.8,1X,F16.8,1X,d14.6,1x,i3)
!--------------------------------------------------------------
! Begin adjoint calculations
!--------------------------------------------------------------
!kjw. We've now calculated:
! 1) forcing ( 2 * S_{obs}^{-1} * DIFF = FORCE ) units of [lnvmr]^{-1}
! 2) New contribution to cost function do to diff
! DIFF * S_{obs}^{-1} * DIFF
! This has all been done on the TES pressure grid
!
! At this point, we need to initialize the adjoint variable: STT_ADJ
! Do so by applying the adjoint of all operators used to get
! STT --> ln(vmr) for calculating ( F(x)-y )
! IF ( NT == 600 ) THEN
! ! dkh debug
! print*, 'DIFF , FORCE '
! WRITE(6,102) (DIFF(L), FORCE(L),
! & L = LTES, 1, -1 )
! ENDIF
! 102 FORMAT(1X,d14.6,1X,d14.6)
! The adjoint forcing is 2 * S_{obs}^{-1} * DIFF = FORCE
DIFF_ADJ = FORCE
! Adjoint of difference
GC_RTVMR_ADJ = DIFF_ADJ
!print*, ' FORCE with 1 for sensitivity '
!print*, ' FORCE with 1 for sensitivity '
!print*, ' FORCE with 1 for sensitivity '
!print*, ' FORCE with 1 for sensitivity '
!ADJ_DIFF(:) = 1d0
!NEW_COST(NT) = ?? SUM(ABS(LOG(CH4_HAT(1:LTES))))
!print*, ' sumlog =', SUM(ABS(LOG(CH4_HAT(:))))
!print*, ' sumlog =', ABS(LOG(CH4_HAT(:)))
! Adjoint of RTVMR Averaging
DO L=1,LTES
CH4_HAT_ADJ(L) = M_STAR(2,L) * GC_RTVMR_ADJ
ENDDO
! kjw debug
!IF ( NT == 600 ) THEN
! print*,'CH4_HAT_ADJ = ',CH4_HAT_ADJ
!ENDIF
! Adjoint of ln(vmr) --> vmr
DO L=1,LTES
IF ( CH4_HAT_ADJ(L) /= 0.0 ) THEN
CH4_HAT_EXP_ADJ(L) = CH4_HAT_ADJ(L) * CH4_HAT_EXP(L)
ELSE
CH4_HAT_EXP_ADJ(L) = 0d0
ENDIF
ENDDO
! kjw debug
!IF ( NT == 600 ) THEN
! print*,'CH4_HAT_EXP_ADJ = ',CH4_HAT_EXP_ADJ
!ENDIF
! DO L = 1, LTES-1
! IF ( TROPP(I,J) < Pedges(L) ) THEN
! CH4_HAT_ADJ(L+1) = GC_RTVMR_ADJ * Nmolec(L)/Totmolec
! ELSE
! CH4_HAT_ADJ(L+1) = 0d0
! ENDIF
! ENDDO
! ! Adjoint of difference
! DO L = 1, LTES
! IF ( TROPP(I,J) < TES(NT)%PRES(L) ) THEN
! CH4_HAT_ADJ(L) = DIFF_ADJ(L)
! ELSE
! CH4_HAT_ADJ(L) = 0d0
! ENDIF
! ENDDO
! adjoint of TES operator
DO L = 1, LTES
CH4_PERT_ADJ(L) = 0d0
DO LL = 1, LTES
CH4_PERT_ADJ(L) = CH4_PERT_ADJ(L)
& + TES(NT)%AVG_KERNEL(L,LL)
& * CH4_HAT_EXP_ADJ(LL)
ENDDO
ENDDO
! Adjoint of x_m - x_a (adjoint of natural log transform)
DO L = 1, LTES
! fwd code:
!GC_CH4(L) = MAX(GC_CH4(L), 1d-10)
!CH4_PERT(L) = LOG(GC_CH4(L)) - LOG(TES(NT)%PRIOR(L))
! adj code:
IF ( GC_CH4(L) > 1d-10 ) THEN
GC_CH4_ADJ(L) = 1d0 / GC_CH4(L) * CH4_PERT_ADJ(L)
ELSE
GC_CH4_ADJ(L) = 1d0 / 1d-10 * CH4_PERT_ADJ(L)
ENDIF
ENDDO
! ! dkh debug
! print*, 'CH4_HAT_ADJ, CH4_PERT_ADJ, GC_CH4_ADJ'
! WRITE(6,103) (CH4_HAT_ADJ(L), CH4_PERT_ADJ(L), GC_CH4_ADJ(L),
! & L = LTES, 1, -1 )
! 103 FORMAT(1X,d14.6,1X,d14.6,1X,d14.6)
! adjoint of interpolation
DO L = 1, LLPAR
GC_CH4_NATIVE_ADJ(L) = 0d0
DO LL = 1, LTES
GC_CH4_NATIVE_ADJ(L) = GC_CH4_NATIVE_ADJ(L)
& + MAP(L,LL) * GC_CH4_ADJ(LL)
ENDDO
ENDDO
! kjw
! Adjoint of interpolation leaves GC_CH4_NATIVE_ADJ with some zeros
! in the lower troposphere. This occurs because the GC pres grid is
! finer in lower troposphere than the TES grid. So, when interpolating
! from GC --> TES, the contribution from some GC grid boxes to any TES
! grid box is zero. Unfortunately, when we go back from TES to GC grid,
! this means that some
! WRITE(114,112) ( GC_CH4_NATIVE_ADJ(L), L=LLPAR,1,-1)
!
! ! dkh debug
! print*, 'GC_CH4_NATIVE_ADJ 1 '
! WRITE(6,104) (GC_CH4_NATIVE_ADJ(L), L = LLPAR, 1, -1 )
DO L = 1, LLPAR
! Adjoint of unit conversion
GC_CH4_NATIVE_ADJ(L) = ( GC_CH4_NATIVE_ADJ(L) *
& XNUMOL(1) / 6.022d23 ) /
& ( AD(I,J,L) * XNUMOLAIR / 6.022d23 )
! Just to make sure we're only forcing the troposphere
IF ( ITS_IN_THE_TROP(I,J,L) ) THEN
! Pass adjoint back to adjoint tracer array
STT_ADJ(I,J,L,1) =
& STT_ADJ(I,J,L,1) + GC_CH4_NATIVE_ADJ(L)
ENDIF
ENDDO
!kjw debug
!IF ( NT == 600 ) THEN
! print*,'GC_CH4_NATIVE_ADJ = ',GC_CH4_NATIVE_ADJ
!ENDIF
!kjw debug
!! ! dkh debug
! print*, 'GC_CH4_NATIVE_ADJ conv '
! WRITE(6,104) (GC_CH4_NATIVE_ADJ(L), L = LLPAR, 1, -1 )
! 104 FORMAT(1X,d14.6)
!
!
! WRITE(101,110) ( TES(NT)%PRES(LL), LL=LTES,1,-1)
! WRITE(102,110) ( 1d9 * GC_CH4(LL), LL=LTES,1,-1)
! WRITE(103,110) ( 1d9 * TES(NT)%CH4(LL), LL=LTES,1,-1)
! WRITE(104,110) ( 1d9 * TES(NT)%PRIOR(LL), LL=LTES,1,-1)
! WRITE(105,110) ( DIFF(LL), LL=LTES,1,-1)
! WRITE(106,112) ( FORCE(LL), LL=LTES,1,-1)
! WRITE(107,111) NT, LTES
! WRITE(108,112) ( CH4_PERT_ADJ(LL), LL=LTES,1,-1)
! WRITE(109,112) ( GC_CH4_ADJ(LL), LL=LTES,1,-1)
! WRITE(110,110) ( 1d9 * EXP(CH4_HAT(LL)), LL=LTES,1,-1)
! WRITE(111,110) ( GC_PRES(L), L=LLPAR,1,-1)
! WRITE(112,110) ( 1d9 * GC_CH4_NATIVE(L), L=LLPAR,1,-1)
! WRITE(113,110) ( 1d9 * CH4_HAT(LL), LL=LTES,1,-1)
! WRITE(115,110) ( 1d9 * TES(NT)%GC_CH4(LL), LL=LTES,1,-1)
110 FORMAT(F18.6,1X)
111 FORMAT(i4,1X,i4,1x)
112 FORMAT(D14.6,1X)
! -----------------------------------------------------------------------
! Use this section to test the adjoint of the TES_CH4 operator by
! slightly perturbing model [CH4] and recording resultant change
! in calculated contribution to the cost function.
!
! This routine will write the following information for each observation
! to rundir/diagadj/test_adjoint_obs.NN.m
!
! The adjoint of the observation operator has been tested and validated
! as of 7/20/10, kjw.
!
! IF ( NT .EQ. 600 ) THEN
! WRITE(116,210) ' L' , ' TROP', ' GC_PRES',
! & ' FD_POS', ' FD_NEG', ' FD_CEN',
! & ' ADJ', ' COST_POS', ' COST_NEG',
! & ' FD_POS/ADJ', ' FD_NEG/ADJ', ' FD_CEN/ADJ'
! PERT(:) = 1D0
! CALL CALC_TES_CH4_FORCE_FD( COST_FUNC_0, PERT, ADJ, NT )
! ori=.FALSE.
! ADJ_SAVE(:) = ADJ(:)
! print*, 'dch4: COST_FUNC_0 = ', COST_FUNC_0
! WRITE(116,213) 'I ', I
! WRITE(116,213) 'J ', J
! WRITE(116,213) 'LTES ',TES(NT)%LTES(1)
! WRITE(116,212) 'GC_PSURF ', GC_PSURF
! WRITE(116,212) 'TES PSURF ',TES(NT)%PRES(1)
! WRITE(116,212) 'NEW_COST: ',NEW_COST(NT)
! WRITE(116,213) 'NT ', NT
! WRITE(116,212) 'COST_FUNC_0:',( COST_FUNC_0 )
! WRITE(116,212) 'TES(NT).TIME',TES(NT)%TIME(1)
! DO L = 1, 47
! PERT(:) = 1D0
! PERT(L) = 1.1
! COST_FUNC_1 = 0D0
! CALL CALC_TES_CH4_FORCE_FD( COST_FUNC_1, PERT, ADJ, NT )
! PERT(L) = 0.9
! COST_FUNC_2 = 0D0
! CALL CALC_TES_CH4_FORCE_FD( COST_FUNC_2, PERT, ADJ, NT )
! FD_CEN(L) = ( COST_FUNC_1 - COST_FUNC_2 ) / 0.2d0
! FD_POS(L) = ( COST_FUNC_1 - COST_FUNC_0 ) / 0.1d0
! FD_NEG(L) = ( COST_FUNC_0 - COST_FUNC_2 ) / 0.1d0
! WRITE(116, 211) L, ITS_IN_THE_TROP(I,J,L), GC_PRES(L),
! & FD_POS(L), FD_NEG(L),
! & FD_CEN(L), ADJ_SAVE(L),
! & COST_FUNC_1, COST_FUNC_2,
! & FD_POS(L)/ADJ_SAVE(L),
! & FD_NEG(L)/ADJ_SAVE(L),
! & FD_CEN(L)/ADJ_SAVE(L)
! ENDDO
! WRITE(116,'(a)') '----------------------------------------------'
!
! 210 FORMAT(A4,2x,A6,2x,A12,2x,A12,2x,A12,2x,A12,2x,A12,2x,A12,2x,
! & A12,2x,A12,2x,A12,2x,A12,2x)
! 211 FORMAT(I4,2x,L6,2x,D12.6,2x,D12.6,2x,D12.6,2x,D12.6,2x,D12.6,
! & 2x,D12.6,2x,D12.6,2x,D12.6,2x,D12.6,2x,D12.6)
! 212 FORMAT(A12,F22.6)
! 213 FORMAT(A12,I4)
! 214 FORMAT(I4,2x,F18.6,2x,F18.6)
!! -----------------------------------------------------------------------
! ENDIF ! IF ( NT .EQ. 600 )
print*, ' - CALC_TES_CH4_FORCE: NEW_COST(NT) = ',NEW_COST(NT)
! ! kjw
! ! Calculate contribution to cost function for regions in my 9 box grid
! ! Here, we define regions
! reg = 0
! IF ( LTES_PSO == .TRUE. ) THEN
! IF (( J-1 > JJPAR*2./3. ) .AND. ( I+1 < IIPAR/3. )) reg = 1
! IF (( J-1 > JJPAR*2./3. ) .AND. ( I-1 > IIPAR*2./3.)) reg = 3
! IF (( J-1 > JJPAR*2./3. ) .AND. ( reg == 0 ) ) reg = 2
! IF (( J+1 < JJPAR/3. ) .AND. ( I+1 < IIPAR/3. )) reg = 7
! IF (( J+1 < JJPAR/3. ) .AND. ( I-1 > IIPAR*2./3.)) reg = 9
! IF (( J+1 < JJPAR/3. ) .AND. ( reg == 0 ) ) reg = 8
! IF (( I+1 < IIPAR/3. ) .AND. ( reg == 0 ) ) reg = 4
! IF (( I-1 > IIPAR*2./3. ) .AND. ( reg == 0 ) ) reg = 6
! IF (( reg == 0 ) ) reg = 5
! ENDIF ! ENDIF LTES_PSO == .TRUE.
!
! ! Assign value to proper region
! JJ_this(reg) = JJ_this(reg) + NEW_COST(NT)
! Jforce_this(reg) = Jforce_this(reg) + FORCE
! Jdiff_this(reg) = Jdiff_this(reg) + DIFF
!
! JJ(reg) = JJ(reg) + NEW_COST(NT)
! Jforce(reg) = Jforce(reg) + FORCE
! Jdiff(reg) = Jdiff(reg) + DIFF
ENDDO ! NT
!!$OMP END PARALLEL DO
print*, ' - CALC_TES_CH4_FORCE: finished assimilating ' //
& 'data this hour.'
print*,'NEW_COST(NTSTOP:NTSTART) = ', NEW_COST(NTSTOP:NTSTART)
print*,'SUM(NEW_COST(NTSTOP:NTSTART)) = ',
& SUM(NEW_COST(NTSTOP:NTSTART))
print*,'NTSTART, NTSTOP = ', NTSTART, NTSTOP
! Update cost function
COST_FUNC = COST_FUNC + SUM(NEW_COST(NTSTOP:NTSTART))
IF ( FIRST ) FIRST = .FALSE.
! ! Print information about JJ_this, Jforce_this, Jdiff_this
! ! Cost function info by region
! print*,'EYOEYOEYO, J info below'
! print*,'Year, month, day, hour, hours before end of simulation'
! print*,GET_YEAR(), GET_MONTH(), GET_DAY(), GET_HOUR(),
! & GET_TAUe() - GET_TAU()
! WRITE(6,820) 'JJ_this= ', JJ
! WRITE(6,820) 'Jforce_this= ', Jforce
! WRITE(6,820) 'Jdiff*1d9_this= ', Jdiff*1d9
! 820 FORMAT(A18, 2x, 9F28.6 )
print*, ' Updated value of COST_FUNC = ', COST_FUNC
print*, ' TES contribution this hour = ', COST_FUNC - OLD_COST
! Close Satellite diagnostic file
CLOSE( IU_FILE )
! kjw
! Print Information about cost function and forcing according
! to region defined by apriori. Print information added during
! this call to CALC_TES_CH4_FORCE
! kjw
! Return to calling program
END SUBROUTINE CALC_TES_CH4_FORCE
!------------------------------------------------------------------------------
SUBROUTINE CALC_TES_CH4_FORCE_FD( COST_FUNC_A, PERT, ADJ, NT )
!
!******************************************************************************
! Subroutine CALC_TES_CH4_FORCE_FD tests the adjoint of CALC_TES_CH4_FORCE
! (dkh, 05/05/10)
!
! Can be driven with:
! PERT(:) = 1D0
! CALL CALC_TES_CH4_FORCE_FD( COST_FUNC_0, PERT, ADJ )
! ADJ_SAVE(:) = ADJ(:)
! print*, 'do3: COST_FUNC_0 = ', COST_FUNC_0
! DO L = 1, 30
! PERT(:) = 1D0
! PERT(L) = 1.1
! COST_FUNC = 0D0
! CALL CALC_TES_CH4_FORCE_FD( COST_FUNC_1, PERT, ADJ )
! PERT(L) = 0.9
! COST_FUNC = 0D0
! CALL CALC_TES_CH4_FORCE_FD( COST_FUNC_2, PERT, ADJ )
! FD(L) = ( COST_FUNC_1 - COST_FUNC_2 ) / 0.2d0
! print*, 'do3: FD = ', FD(L), L
! print*, 'do3: ADJ = ', ADJ_SAVE(L), L
! print*, 'do3: COST = ', COST_FUNC, L
! print*, 'do3: FD / ADJ ', FD(L) / ADJ_SAVE(L) , L
! ENDDO
!
!
!
!
! Arguments as Input/Output:
! ===========================================================================
! (1 ) COST_FUNC_A (REAL*8) : Cost funciton [unitless]
!
!
! NOTES:
! (1 ) Updated to GCv8 (dkh, 10/07/09)
! (1 ) Add more diagnostics. Now read and write doubled CH4 (dkh, 11/08/09)
!******************************************************************************
!
! Reference to f90 modules
USE ADJ_ARRAYS_MOD, ONLY : STT_ADJ
USE ADJ_ARRAYS_MOD, ONLY : N_CALC
USE ADJ_ARRAYS_MOD, ONLY : EXPAND_NAME
! USE ADJ_ARRAYS_MOD, ONLY : CH4_PROF_SAV
USE CHECKPT_MOD, ONLY : CHK_STT
USE COMODE_MOD, ONLY : CSPEC, JLOP
USE DAO_MOD, ONLY : AD
USE DAO_MOD, ONLY : AIRDEN
USE DAO_MOD, ONLY : BXHEIGHT
USE DAO_MOD, ONLY : TROPP
USE DIRECTORY_ADJ_MOD, ONLY : DIAGADJ_DIR
USE GRID_MOD, ONLY : GET_IJ
USE PRESSURE_MOD, ONLY : GET_PCENTER, GET_PEDGE
USE TIME_MOD, ONLY : GET_NYMD, GET_NHMS
USE TIME_MOD, ONLY : GET_TS_CHEM
USE TRACER_MOD, ONLY : XNUMOLAIR, XNUMOL
USE TROPOPAUSE_MOD, ONLY : ITS_IN_THE_TROP
USE TRACER_MOD, ONLY : STT
USE LOGICAL_ADJ_MOD, ONLY : LTES_PSO
# include "CMN_SIZE" ! Size params
! Arguments
REAL*8, INTENT(INOUT) :: COST_FUNC_A
INTEGER, INTENT(IN) :: NT
REAL*8, INTENT(IN) :: PERT(LLPAR)
REAL*8, INTENT(OUT) :: ADJ(LLPAR)
! Local variables
INTEGER :: NTSTART, NTSTOP
INTEGER :: IIJJ(2), I, J
INTEGER :: L, LL, LTES
INTEGER :: JLOOP
REAL*8 :: GC_PRES(LLPAR)
REAL*8 :: GC_CH4_NATIVE(LLPAR)
REAL*8 :: GC_CH4(MAXLEV)
REAL*8 :: GC_PSURF
REAL*8 :: MAP(LLPAR,MAXLEV)
REAL*8 :: CH4_HAT(MAXLEV)
REAL*8 :: CH4_HAT_EXP(MAXLEV)
REAL*8 :: CH4_PERT(MAXLEV)
REAL*8 :: FORCE
REAL*8 :: DIFF
REAL*8 :: NEW_COST!(MAXTES)
REAL*8 :: OLD_COST
!REAL*8, SAVE :: TIME_FRAC!(MAXTES)
!INTEGER,SAVE :: NTES
REAL*8 :: GC_CH4_NATIVE_ADJ(LLPAR)
REAL*8 :: CH4_HAT_ADJ(MAXLEV)
REAL*8 :: CH4_HAT_EXP_ADJ(MAXLEV)
REAL*8 :: CH4_PERT_ADJ(MAXLEV)
REAL*8 :: GC_CH4_ADJ(MAXLEV)
REAL*8 :: DIFF_ADJ
REAL*4 :: S_obs_inv
REAL*8 :: GC_RTVMR
REAL*8 :: OBS_RTVMR
REAL*8 :: GC_RTVMR_ADJ
REAL*8 :: M_STAR(4,MAXLEV)
REAL*8 :: Pres_ln(MAXLEV)
REAL*8 :: Pedges_ln(MAXLEV)
REAL*8 :: Pedges(MAXLEV)
REAL*8 :: Pdiff(MAXLEV)
REAL*8 :: Nmolec(MAXLEV)
REAL*8 :: Totmolec
REAL*8 :: DIFF_onTES(MAXLEV)
!LOGICAL, SAVE :: FIRST = .TRUE.
INTEGER :: IOS
CHARACTER(LEN=255) :: FILENAME
!=================================================================
! CALC_TES_CH4_FORCE_FD begins here!
!=================================================================
!print*, ' - CALC_TES_CH4_FORCE_FD '
NEW_COST = 0D0
! Calculate S_obs_inv
!S_obs_inv = 1. / ( LOG( ( 1800. + ERR_PPB ) / 1800. ) )**2
S_obs_inv = 1. / ( 40.0d-9 ** 2 )
! ! Open files for output
! IF ( FIRST ) THEN
! FILENAME = 'force_adj_stuff.NN.m'
! CALL EXPAND_NAME( FILENAME, N_CALC )
! FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
! OPEN( 101, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
! & IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
!
!
! ENDIF
!
! ! Save a value of the cost function first
! OLD_COST = COST_FUNC_A
!
! ! Check if it is the last hour of a day
! IF ( GET_NHMS() == 230000 ) THEN
!
! ! Read the TES CH4 file for this day
! CALL READ_TES_CH4_OBS( GET_NYMD(), NTES )
!
! ! If NTES = 0, it means there are no observations today.
! ! Return to calling procedure
! IF ( NTES == 0 ) THEN
! WRITE(6,*) ' No TES CH4 obs today. Returning 01 ... '
! RETURN
! ENDIF
!
! ! TIME is YYYYMMDD.frac-of-day. Subtract date and save just time fraction
! TIME_FRAC(1:NTES) = TES(1:NTES)%TIME(1) - GET_NYMD()
!
!! ENDIF
!
! ! If NTES = 0, it means there are no observations today.
! ! Return to calling procedure
! IF ( NTES == 0 ) THEN
! WRITE(6,*) ' No TES CH4 obs today. Returning 02 ... '
! RETURN
! ENDIF
!
!
! ! Get the range of TES retrievals for the current hour
! CALL GET_NT_RANGE( NTES, GET_NHMS(), TIME_FRAC, NTSTART, NTSTOP )
!
! IF ( NTSTART == 0 .and. NTSTOP == 0 ) THEN
!
! print*, ' No matching TES CH4 obs for this hour'
! RETURN
! ENDIF
!
! print*, ' for hour range: ', GET_NHMS(), TIME_FRAC(NTSTART),
! & TIME_FRAC(NTSTOP)
! print*, ' found record range: ', NTSTART, NTSTOP
!! need to update this in order to do i/o with this loop parallel
!!! ! Now do a parallel loop for analyzing data
!!!$OMP PARALLEL DO
!!!$OMP+DEFAULT( SHARED )
!!!$OMP+PRIVATE( NT, MAP, LTES, IIJJ, I, J, L, LL, JLOOP )
!!!$OMP+PRIVATE( GC_PRES, GC_PSURF, GC_CH4, DIFF )
!!!$OMP+PRIVATE( GC_CH4_NATIVE, CH4_PERT, CH4_HAT, FORCE )
!!!$OMP+PRIVATE( GC_CH4_NATIVE_ADJ, GC_CH4_ADJ )
!!!$OMP+PRIVATE( CH4_PERT_ADJ, CH4_HAT_ADJ )
!!!$OMP+PRIVATE( DIFF_ADJ )
! DO NT = NTSTART, NTSTOP, -1
!print*, ' - CALC_TES_CH4_FORCE_FD: analyzing record ', NT
! For safety, initialize these up to LLTES
GC_CH4(:) = 0d0
MAP(:,:) = 0d0
CH4_HAT_ADJ(:) = 0d0
FORCE = 0d0
! Copy LTES to make coding a bit cleaner
LTES = TES(NT)%LTES(1)
! Get grid box of current record
IIJJ = GET_IJ( REAL(TES(NT)%LON(1),4), REAL(TES(NT)%LAT(1),4))
I = IIJJ(1)
J = IIJJ(2)
!print*, 'I,J = ', I, J
! Get GC pressure levels (mbar)
DO L = 1, LLPAR
GC_PRES(L) = GET_PCENTER(I,J,L)
ENDDO
! Get GC surface pressure (mbar)
GC_PSURF = GET_PEDGE(I,J,1)
! Calculate the interpolation weight matrix
MAP(1:LLPAR,1:LTES)
& = GET_INTMAP( LLPAR, GC_PRES(:), GC_PSURF,
& LTES, TES(NT)%PRES(1:LTES), TES(NT)%PRES(1) )
! Get CH4 values at native model resolution
!DO L = 1, LLPAR
!kjw. JLOP and CSPEC are variables in comode_mod.f, associated
! with smvgear. For getting CH4 values at native model
! resolution, write my own thing, it'll probably have to do
! with using CH4 from STT when below tropopause, and using
! the TES retrieval above the tropopause. Therefore, there
! will be no adjoint forcing above the tropopause.
!kjw.
!kjw
! Get CH4 from restored STT array
! Find out units of STT (I think it's [kg/box]).
! If so, convert to [v/v] before next step
DO L=1,LLPAR
GC_CH4_NATIVE(L) = CHK_STT(I,J,L,1) * PERT(L)
ENDDO
! Unit Conversion
DO L=1,LLPAR
! Convert from [kg/box] --> [v/v]
! Numerator = moles CH4/box
! Denominator = moles air/box
GC_CH4_NATIVE(L) = (GC_CH4_NATIVE(L)*XNUMOL(1)/6.022d23 ) /
& ( AD(I,J,L) * XNUMOLAIR / 6.022d23 )
ENDDO
!!!! Bypass unit conversion
!!! GC_CH4_NATIVE(:) = GC_CH4_NATIVE(:)
! Interpolate GC CH4 column to TES grid
DO LL = 1, LTES
GC_CH4(LL) = 0d0
DO L = 1, LLPAR
GC_CH4(LL) = GC_CH4(LL)
& + MAP(L,LL) * GC_CH4_NATIVE(L)
ENDDO
ENDDO
!!! ! Bypass interpolation
!!! GC_CH4(1:47) = GC_CH4_NATIVE(:)
!!!
! !kjw testing
! IF ( ori .EQ. .TRUE. ) THEN
! print*,'-------------------------'
! print*,'GC_CH4(13) = ',GC_CH4(13)
! ENDIF
! !kjw testing
! ! dkh debug: compare profiles:
! print*, ' GC_PRES, GC_native_CH4 [ppb] '
! WRITE(6,100) (GC_PRES(L), GC_CH4_NATIVE(L)*1d9,
! & L = LLPAR, 1, -1 )
! print*, ' TES_PRES, GC_CH4 '
! WRITE(6,100) (TES(NT)%PRES(LL),
! & GC_CH4(LL)*1d9, LL = LTES, 1, -1 )
! 100 FORMAT(1X,F16.8,1X,F16.8)
!--------------------------------------------------------------
! Apply TES observation operator
!
! x_hat = x_a + A_k ( x_m - x_a )
!
! where
! x_hat = GC modeled column as seen by TES [lnvmr]
! x_a = TES apriori column [lnvmr]
! x_m = GC modeled column [lnvmr]
! A_k = TES averaging kernel
!--------------------------------------------------------------
! x_m - x_a
DO L = 1, LTES
GC_CH4(L) = MAX(GC_CH4(L), 1d-10)
CH4_PERT(L) = LOG(GC_CH4(L)) - LOG(TES(NT)%PRIOR(L))
ENDDO
! !kjw testing
! IF ( ori .EQ. .TRUE. ) THEN
! print*,'CH4_PERT(13) = ',CH4_PERT(13)
! ENDIF
! !kjw testing
!!! ! Bypass !x_m - x_a
!!! CH4_PERT(:) = GC_CH4(:)
!!!
! x_a + A_k * ( x_m - x_a )
! AVG_KERNEL indexing may look backwards because BPCH files storing AK
! values use IDL column major indexing.
DO L = 1, LTES
CH4_HAT(L) = 0d0
DO LL = 1, LTES
CH4_HAT(L) = CH4_HAT(L)
& + TES(NT)%AVG_KERNEL(LL,L) * CH4_PERT(LL)
ENDDO
CH4_HAT(L) = CH4_HAT(L) + LOG(TES(NT)%PRIOR(L))
ENDDO
!!! ! Bypass !x_a + A_k * ( x_m - x_a )
!!! CH4_HAT(:) = CH4_PERT(:)
!!!
! !--------------------------------------------------------------
! ! Calculate column average ln(vmr) weighted by # density of TES grid
! ! This operation is self adjoint.
! ! To get # density [molec / m^2], get pressure differences in grid.
! ! Get kg/m^2 of air in each box. (F=ma) dP=kg*g
! ! Get molec/m^2 air molec/m2 = kg/m2 * XNUMOLAIR
! ! TES pressure grid linear in ln(pres). Get pressure at edge of boxes
! !--------------------------------------------------------------
! pres_ln(1:LTES)=LOG(TES(NT)%pres(1:LTES)) ! [hPa] --> ln([hPa])
! Pedges_ln(1)=pres_ln(1) ! Bottom edge is surface pressure
! DO L=2,LTES-1
! Pedges_ln(L) = ( pres_ln(L) + pres_ln(L+1) ) / 2.
! ENDDO
! Pedges=EXP(pedges_ln) ! ln([hPa]) --> [hPa]
! Pedges(LTES)=0 ! Top of atmosphere
! !print*,' kjw debug: TES pressure edges'
! !print*, Pedges
!
! ! Calculate pressure difference of each LTES-1 boxes
! DO L=1,LTES-1
! Pdiff(L) = Pedges(L) - Pedges(L+1)
! ENDDO
!
! ! Calculate # molecules air in each LTES-1 obxes
! Pdiff(:) = 100 * Pdiff(:) ! [hPa] --> [Pa]
! Pdiff(:) = Pdiff(:) / 9.8 ! [hPa] --> [kg]
! Nmolec(:)= Pdiff(:) * XNUMOLAIR ! [kg] --> [molec]
! Totmolec = 0d0
! DO L=1,LTES-1
! IF ( TROPP(I,J) < Pedges(L) ) THEN
! Totmolec = Totmolec + Nmolec(L)
! ENDIF
! ENDDO
!
! IF ( NT .EQ. 600 ) THEN
! !print*,' kjw debug:# molecules in column (should be ~2e29)'
! !print*, Totmolec
! ENDIF
!
! ! Calculate column average ln(vmr) weighted by # density of TES levels
! ! Only include levels with tropospheric air
! OBS_avg = 0d0
! GC_avg = 0d0
! DIFF_onTES(:) = 0d0
! DIFF_onTES(1:LTES) = LOG(TES(NT)%GC_CH4(1:LTES)) -
! & CH4_HAT(1:LTES)
! DO L=1,LTES-1
! IF ( TROPP(I,J) < Pedges(L) ) THEN
! OBS_avg = OBS_avg +
! & LOG(TES(NT)%GC_CH4(L+1)) * Nmolec(L)/Totmolec
! GC_avg = GC_avg +
! & CH4_HAT(L+1) * Nmolec(L)/Totmolec
! ENDIF
! ENDDO
! Transform from [ln(vmr)] --> [vmr]
CH4_HAT_EXP = EXP(CH4_HAT)
! Calculate RTln(VMR) for profiles.
CALL GET_RTVMR( NT, TES(NT)%GC_CH4, OBS_RTVMR, M_STAR )
CALL GET_RTVMR( NT, CH4_HAT_EXP, GC_RTVMR, M_STAR )
! Retrieve RTVMR from TES structure for pseudo-observations
IF ( LTES_PSO ) THEN
OBS_RTVMR = 0d0
OBS_RTVMR = TES(NT)%GC_CH4(67)
ENDIF
!--------------------------------------------------------------
! Calculate cost function, given S is error on ln(vmr)
! J = [ model - obs ]^T S_{obs}^{-1} [ ln(model - obs ]
!--------------------------------------------------------------
! DIFF = model - obs. units: [ln(vmr)] / m^2
DIFF = GC_RTVMR - OBS_RTVMR
! Calculate DIFF^T * S_{obs}^{-1} * DIFF
FORCE = 0d0
FORCE = 2 * DIFF * S_obs_inv
NEW_COST = 0.5d0 * DIFF * FORCE
! ! Calculate difference between modeled and observed profile
! ! Eliminate stratospheric forcing in this
! DO L = 1, LTES
! IF ( TROPP(I,J) < TES(NT)%PRES(L) ) THEN
! DIFF(L) = CH4_HAT(L) - LOG( TES(NT)%GC_CH4(L) )
! ELSE
! DIFF(L) = 0d0
! ENDIF
! ENDDO
! ! Bypass DIFF
!!!! DIFF(:) = CH4_HAT(:)
!
!
! ! Calculate 1/2 * DIFF^T * S_{obs}^{-1} * DIFF
! DO L = 1, LTES
! FORCE(L) = 0d0
! DO LL = 1, LTES
! FORCE(L) = FORCE(L) + TES(NT)%S_OER_INV(L,LL) *DIFF(LL)
! ENDDO
! NEW_COST = NEW_COST + 0.5d0 * DIFF(L) * FORCE(L)
! !NEW_COST(NT) = NEW_COST(NT) + 0.5d0 * DIFF(L) * FORCE(L)
! ENDDO
!!! !Bypass this part of adjoint
!!! DO L=1,LTES
!!! FORCE(L) = 0.5d0
!!! NEW_COST=NEW_COST+ 0.5d0 * DIFF(L)
!!! ENDDO
! ! dkh debug: compare profiles:
! print*, ' CH4_HAT, CH4_TES, CH4_GC [ppb]'
! WRITE(6,090) ( 1d9 * EXP(CH4_HAT(L)),
! & 1d9 * TES(NT)%CH4(L),
! & 1d9 * TES(NT)%GC_CH4(L),
! & L, L = LTES, 1, -1 )
!
! print*, ' TES_PRIOR, CH4_HAT, CH4_GC [lnvmr], diag(S^-1)'
! WRITE(6,101) ( LOG(TES(NT)%PRIOR(L)), CH4_HAT(L),
! & LOG(TES(NT)%GC_CH4(L)), TES(NT)%S_OER_INV(L,L),
! & L, L = LTES, 1, -1 )
! 090 FORMAT(1X,F16.8,1X,F16.8,1X,F16.8,1X,i3)
! 101 FORMAT(1X,F16.8,1X,F16.8,1X,F16.8,1X,d14.6,1x,i3)
! !--------------------------------------------------------------
! ! Begin adjoint calculations
! !--------------------------------------------------------------
!kjw. We've now calculated:
! 1) forcing ( 2 * S_{obs}^{-1} * DIFF = FORCE ) units of [lnvmr]^{-1}
! 2) New contribution to cost function do to diff
! DIFF * S_{obs}^{-1} * DIFF
! This has all been done on the TES pressure grid
!
! At this point, we need to initialize the adjoint variable: STT_ADJ
! Do so by applying the adjoint of all operators used to get
! STT --> ln(vmr) for calculating ( F(x)-y )
! dkh debug
! print*, 'DIFF , FORCE '
! WRITE(6,102) (DIFF(L), FORCE(L),
! & L = LTES, 1, -1 )
! 102 FORMAT(1X,d14.6,1X,d14.6)
! The adjoint forcing is 2 * S_{obs}^{-1} * DIFF = FORCE
DIFF_ADJ = FORCE
! Adjoint of difference
GC_RTVMR_ADJ = DIFF_ADJ
!print*, ' FORCE with 1 for sensitivity '
!print*, ' FORCE with 1 for sensitivity '
!print*, ' FORCE with 1 for sensitivity '
!print*, ' FORCE with 1 for sensitivity '
!ADJ_DIFF(:) = 1d0
!NEW_COST(NT) = ?? SUM(ABS(LOG(CH4_HAT(1:LTES))))
!print*, ' sumlog =', SUM(ABS(LOG(CH4_HAT(:))))
!print*, ' sumlog =', ABS(LOG(CH4_HAT(:)))
! ! Adjoint of Column Averaging
! DO L = 1, LTES-1
! IF ( TROPP(I,J) < Pedges(L) ) THEN
! CH4_HAT_ADJ(L+1) = GC_avg_ADJ * Nmolec(L)/Totmolec
! ELSE
! CH4_HAT_ADJ(L+1) = 0d0
! ENDIF
! ENDDO
! Adjoint of RTVMR Averaging
DO L=1,LTES
CH4_HAT_ADJ(L) = M_STAR(2,L) * GC_RTVMR_ADJ
ENDDO
! Adjoint of ln(vmr) --> vmr
DO L=1,LTES
IF ( CH4_HAT_ADJ(L) /= 0.0 ) THEN
CH4_HAT_EXP_ADJ(L) = CH4_HAT_ADJ(L) * CH4_HAT_EXP(L)
ELSE
CH4_HAT_EXP_ADJ(L) = 0d0
ENDIF
ENDDO
! ! Adjoint of difference
! DO L = 1, LTES
! IF ( TROPP(I,J) < TES(NT)%PRES(L) ) THEN
! CH4_HAT_ADJ(L) = DIFF_ADJ(L)
! ELSE
! CH4_HAT_ADJ(L) = 0d0
! ENDIF
! ENDDO
!!! ! Bypass adjoint of difference
!!! CH4_HAT_ADJ(:) = DIFF_ADJ(:)
!!! ! adjoint of TES operator
DO L = 1, LTES
CH4_PERT_ADJ(L) = 0d0
DO LL = 1, LTES
CH4_PERT_ADJ(L) = CH4_PERT_ADJ(L)
& + TES(NT)%AVG_KERNEL(L,LL)
& * CH4_HAT_EXP_ADJ(LL)
ENDDO
ENDDO
!!! ! Bypass adjoint of TES operator
!!! CH4_PERT_ADJ(:) = CH4_HAT_ADJ(:)
!!!
! Adjoint of x_m - x_a
DO L = 1, LTES
! fwd code:
!GC_CH4(L) = MAX(GC_CH4(L), 1d-10)
!CH4_PERT(L) = LOG(GC_CH4(L)) - LOG(TES(NT)%PRIOR(L))
! adj code:
IF ( GC_CH4(L) > 1d-10 ) THEN
GC_CH4_ADJ(L) = 1d0 / GC_CH4(L) * CH4_PERT_ADJ(L)
ELSE
GC_CH4_ADJ(L) = 1d0 / 1d-10 * CH4_PERT_ADJ(L)
ENDIF
ENDDO
!!! ! Bypass adjoint of x_m - x_a
!!! GC_CH4_ADJ(:) = CH4_PERT_ADJ(:)
! dkh debug
! print*, 'CH4_HAT_ADJ, CH4_PERT_ADJ, GC_CH4_ADJ'
! WRITE(6,103) (CH4_HAT_ADJ(L), CH4_PERT_ADJ(L), GC_CH4_ADJ(L),
! & L = LTES, 1, -1 )
! 103 FORMAT(1X,d14.6,1X,d14.6,1X,d14.6)
! ! adjoint of interpolation
DO L = 1, LLPAR
GC_CH4_NATIVE_ADJ(L) = 0d0
DO LL = 1, LTES
GC_CH4_NATIVE_ADJ(L) = GC_CH4_NATIVE_ADJ(L)
& + MAP(L,LL) * GC_CH4_ADJ(LL)
ENDDO
ENDDO
!!! ! Bypass adjoint of interpolation
!!! GC_CH4_NATIVE_ADJ(:) = GC_CH4_ADJ(1:47)
!!!
! kjw
! Adjoint of interpolation leaves GC_CH4_NATIVE_ADJ with some zeros
! in the lower troposphere. This occurs because the GC pres grid is
! finer in lower troposphere than the TES grid. So, when interpolating
! from GC --> TES, the contribution from some GC grid boxes to any TES
! grid box is zero. Unfortunately, when we go back from TES to GC grid,
! this means that some
! WRITE(114,112) ( GC_CH4_NATIVE_ADJ(L), L=LLPAR,1,-1)
! dkh debug
!print*, 'GC_CH4_NATIVE_ADJ 1 '
! WRITE(6,104) (GC_CH4_NATIVE_ADJ(L), L = LLPAR, 1, -1 )
DO L = 1, LLPAR
! Adjoint of unit conversion
GC_CH4_NATIVE_ADJ(L) = ( GC_CH4_NATIVE_ADJ(L) *
& XNUMOL(1) / 6.022d23 ) /
& ( AD(I,J,L) * XNUMOLAIR / 6.022d23 )
!!!! Bypass adjoint of unit conversion
!!!GC_CH4_NATIVE_ADJ(:) = GC_CH4_NATIVE_ADJ(:)
! Just to make sure we're only forcing the troposphere
IF ( ITS_IN_THE_TROP(I,J,L) ) THEN
! Pass adjoint back to adjoint tracer array
STT_ADJ(I,J,L,1) =
& STT_ADJ(I,J,L,1) + GC_CH4_NATIVE_ADJ(L)
ADJ(L) = GC_CH4_NATIVE_ADJ(L) * CHK_STT(I,J,L,1)
ELSE
ADJ(L) = 0
ENDIF
ENDDO
! ! dkh debug
! print*, 'GC_CH4_NATIVE_ADJ conv '
! WRITE(6,104) (GC_CH4_NATIVE_ADJ(L), L = LLPAR, 1, -1 )
! 104 FORMAT(1X,d14.6)
!
!
! WRITE(101,110) ( TES(NT)%PRES(LL), LL=LTES,1,-1)
! WRITE(102,110) ( 1d9 * GC_CH4(LL), LL=LTES,1,-1)
! WRITE(103,110) ( 1d9 * TES(NT)%CH4(LL), LL=LTES,1,-1)
! WRITE(104,110) ( 1d9 * TES(NT)%PRIOR(LL), LL=LTES,1,-1)
! WRITE(105,110) ( DIFF(LL), LL=LTES,1,-1)
! WRITE(106,112) ( FORCE(LL), LL=LTES,1,-1)
! WRITE(107,111) NT, LTES
! WRITE(108,112) ( CH4_PERT_ADJ(LL), LL=LTES,1,-1)
! WRITE(109,112) ( GC_CH4_ADJ(LL), LL=LTES,1,-1)
! WRITE(110,110) ( 1d9 * EXP(CH4_HAT(LL)), LL=LTES,1,-1)
! WRITE(111,110) ( GC_PRES(L), L=LLPAR,1,-1)
! WRITE(112,110) ( 1d9 * GC_CH4_NATIVE(L), L=LLPAR,1,-1)
! WRITE(113,110) ( 1d9 * GC_CH4(LL), LL=LTES,1,-1)
! 110 FORMAT(F18.6,1X)
! 111 FORMAT(i4,1X,i4,1x)
! 112 FORMAT(D14.6,1X)
!
!
! ENDDO ! NT
!!!$OMP END PARALLEL DO
!
! WRITE(116,212) TES(NT)%TIME(1)
! 212 FORMAT(F22.6)
! Update cost function
! COST_FUNC = SUM( NEW_COST(NTSTART:NTSTOP))
COST_FUNC_A = NEW_COST
! Return to calling program
END SUBROUTINE CALC_TES_CH4_FORCE_FD
!!------------------------------------------------------------------------------
SUBROUTINE MAKE_PSEUDO_OBS( YYYYMMDD, NTES )
!
!******************************************************************************
! Subroutine MAKE_PSEUDO_OBS populates TES%GC_CH4 with processed GC columns
! Processing consists of adding error and applying TES observation operator
!
! Arguments as Input:
! ============================================================================
! (1 ) NTES (INTEGER) : Number of TES retrievals in this day
! (2 ) YYYYMMDD (INTEGER) : Current model date
!
! Arguments as Output:
! ============================================================================
! (0 )
!
! NOTES:
!
!******************************************************************************
!
! Reference to f90 modules
USE TIME_MOD, ONLY : EXPAND_DATE
USE FILE_MOD, ONLY : IU_FILE, IOERROR
USE DIRECTORY_MOD, ONLY : RUN_DIR
USE DIRECTORY_ADJ_MOD, ONLY : ADJTMP_DIR, DIAGADJ_DIR
USE GRID_MOD, ONLY : GET_IJ
USE TIME_MOD, ONLY : GET_YEAR, GET_MONTH, GET_DAY
USE BPCH2_MOD, ONLY : GET_TAU0, READ_BPCH2
USE TRANSFER_MOD, ONLY : TRANSFER_2D, TRANSFER_3D
USE ADJ_ARRAYS_MOD, ONLY : N_CALC, EXPAND_NAME
# include "CMN_SIZE" ! Size params
! Arguments
INTEGER, INTENT(IN) :: NTES
INTEGER, INTENT(IN) :: YYYYMMDD
! Local variables
CHARACTER(LEN=255) :: FILENAME
CHARACTER(LEN=255) :: FILENAME_ROOT
CHARACTER(LEN=255) :: FILENAME_OBS
INTEGER :: IOS, NT, I, J, L
INTEGER :: IIJJ(2), LL, LTES
INTEGER :: DD, MM, YYYY
INTEGER :: HH, HH_last, HHMMSS
REAL*4 :: ARRAY2D(IIPAR,JJPAR,1)
REAL*4 :: ARRAY3D(IIPAR,JJPAR,LLPAR)
REAL*8 :: MAP(LLPAR,MAXLEV)
REAL*8 :: GC_CH4(IIPAR,JJPAR,LLPAR)
REAL*8 :: GC_PRES(IIPAR,JJPAR,LLPAR)
REAL*8 :: GC_PSURF(IIPAR,JJPAR)
REAL*8 :: GC_PRES_this(LLPAR)
REAL*8 :: GC_CH4_NATIVE_this(LLPAR)
REAL*8 :: GC_CH4_this(MAXLEV)
REAL*8 :: CH4_PERT(MAXLEV)
REAL*8 :: CH4_HAT(MAXLEV)
REAL*8 :: GC_PSURF_this
REAL*8 :: XTAU
REAL*8 :: day_frac
REAL*8 :: GC_PSO_RTVMR
REAL*8 :: GC_PSO_RTVMR_werr
REAL*8 :: M_STAR(4,MAXLEV)
!=================================================================
! MAKE_PSEUDO_OBS begins here!
!=================================================================
! ----------------------------------------------------------------
! Get Today's Error values
WRITE(6,'(a)') ' MAKE_PSEUDO_OBS - reading random errors'
! filename
FILENAME = TRIM( 'tes_ch4_random_YYYYMMDD.txt' )
! Expand date tokens in filename
CALL EXPAND_DATE( FILENAME, YYYYMMDD, 9999 )
! Construct complete filename
FILENAME = TRIM('/home/kjw/TES/data/V004/bpch/randoms/') //
& TRIM( FILENAME )
! Open file
print*,'Opening: ', TRIM(FILENAME)
OPEN( IU_FILE, FILE=TRIM( FILENAME ),
& STATUS='OLD', IOSTAT=IOS )
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'read_rand_file:1')
! Read each line
DO NT=1,NTES+1
READ( IU_FILE, '(F16.12)', IOSTAT=IOS ) TES(NT)%ERR(1)
IF ( NT .EQ. NTES+1 ) THEN
IF ( IOS < 0 ) THEN
WRITE(6,'(a)') 'Done reading random errors'
ELSE
WRITE(6,'(a)') 'Unexpected end. read_rand_file:2'
ENDIF
ENDIF
ENDDO
! Close file
CLOSE( IU_FILE )
print*,'1TES(300)%ERR(1) = ',TES(300)%ERR(1)
! Errors have mean=0, stddev=1. Multiply by ERR_PPB (20-40) / 1d9
print*,ERR_PPB
DO NT=1,NTES
TES(NT)%ERR(1) = TES(NT)%ERR(1) * ERR_PPB / 1.0d9
ENDDO
print*,'2TES(300)%ERR(1) = ',TES(300)%ERR(1)
! ----------------------------------------------------------------
! Get Today's GEOS-Chem columns
WRITE(6,'(a)') ' MAKE_PSEUDO_OBS - Reading GEOS-Chem observations'
! Set HH_last to -1 to guarantee opening file for 1st observation
HH_last = -1
! Loop over each TES observation
DO NT=1,NTES
print*, ' - MAKE_PSEUDO_OBS: for record ', NT
! Get date and hour of observation (round time)
YYYY = floor( ( YYYYMMDD * 1d-4 ) )
MM = floor( ( YYYYMMDD - 1d4 * YYYY ) * 1d-2 )
DD = floor( ( YYYYMMDD - 1d4 * YYYY - 1d2 * MM ) )
day_frac = TES(NT)%TIME(1) - 1d4 * YYYY -
& 1d2 * MM - DD
HH = nint( 24. * day_frac )
IF ( HH == 24 ) HH = 23 ! If last 1/2 hour of day, use HH=23
HHMMSS = 1d4 * HH
! Open new obs file if necessary
IF ( HH /= HH_last ) THEN
FILENAME_ROOT = TRIM( RUN_DIR )
FILENAME_OBS = 'gctm.obs.YYYYMMDD.hhmm'
print*,'TES(NT)%TIME(1) = ',TES(NT)%TIME(1)
print*,'GET_YEAR = ', GET_YEAR()
print*,'GET_MONTH = ', GET_MONTH()
print*,'GET_DAY = ', GET_DAY()
print*,'YYYY = ',YYYY
print*,'MM = ',MM
print*,'HH = ',HH
print*,'HHMMSS = ',HHMMSS
print*,'FILENAME_OBS = ',FILENAME_OBS
CALL EXPAND_DATE( FILENAME_OBS, YYYYMMDD, HHMMSS )
FILENAME_OBS = TRIM( ADJTMP_DIR ) //
& TRIM( FILENAME_OBS )
print*,'FILENAME_OBS = ', FILENAME_OBS
! Get Tau value for BPCH read
XTAU = GET_TAU0( MM, DD, YYYY, HH )
! Get 3D array of GEOS-Chem values
print*,'Read observations'
GC_CH4(:,:,:) = 0d0
ARRAY3D(:,:,:) = 0d0
CALL READ_BPCH2( TRIM(FILENAME_OBS), 'IJ-OBS-$', 1,
& XTAU, IIPAR, JJPAR,
& LLPAR, ARRAY3D, QUIET=.TRUE. )
!CALL TRANSFER_3D( ARRAY3D(:,:,:), GC_CH4(:,:,:) )
GC_CH4(:,:,:) = ARRAY3D(:,:,:)
! Get 3D array of GEOS-Chem pressure centers
print*,'Read pressure centers'
ARRAY3D(:,:,:) = 0d0
GC_PRES(:,:,:) = 0d0
CALL READ_BPCH2( TRIM(FILENAME_OBS), 'IJ-OBS-$', 2,
& XTAU, IIPAR, JJPAR,
& LLPAR, ARRAY3D, QUIET=.TRUE. )
!CALL TRANSFER_3D( ARRAY3D(:,:,:), GC_PRES(:,:,:) )
GC_PRES(:,:,:) = ARRAY3D(:,:,:)
! Get 2D array of GEOS-Chem surface pressure
print*,'Read surface pressure'
GC_PSURF(:,:) = 0d0
ARRAY2D(:,:,1) = 0d0
CALL READ_BPCH2( TRIM(FILENAME_OBS), 'IJ-OBS-$', 3,
& XTAU, IIPAR, JJPAR,
& 1, ARRAY2D, QUIET=.TRUE. )
!CALL TRANSFER_2D( ARRAY2D(:,:,1), GC_PSURF(:,:) )
GC_PSURF(:,:) = ARRAY2D(:,:,1)
ENDIF
! RESET a few variables to be safe
I = 0
J = 0
GC_PRES_this(:) = 0d0
GC_PSURF_this = 0d0
CH4_HAT(:) = 0d0
MAP(:,:) = 0d0
GC_PSO_RTVMR = 0d0
! Copy LTES to make coding a bit cleaner
LTES = TES(NT)%LTES(1)
! Get grid box of current record
IIJJ = GET_IJ( REAL(TES(NT)%LON(1),4), REAL(TES(NT)%LAT(1),4))
I = IIJJ(1)
J = IIJJ(2)
! Get GC pressure levels (mbar)
DO L = 1, LLPAR
GC_PRES_this(L) = GC_PRES(I,J,L)
GC_CH4_NATIVE_this(L) = GC_CH4(I,J,L)! + TES(NT)%ERR(1)
ENDDO
! Get GC surface pressure (mbar)
GC_PSURF_this = GC_PSURF(I,J)
! Calculate the interpolation weight matrix
MAP(1:LLPAR,1:LTES)
& = GET_INTMAP( LLPAR, GC_PRES_this(:), GC_PSURF_this,
& LTES, TES(NT)%PRES(1:LTES), TES(NT)%PRES(1) )
IF ( NT == 600 ) THEN
!print*,'LTES = ',LTES
!print*,'LLPAR = ',LLPAR
!print*,'GC_PSURF = ',GC_PSURF_this
!WRITE(6,'(a)') 'GEOS-Chem pressure grid'
!WRITE(6,'(F10.3)') ( GC_PRES_this(L), L=1,47 )
!WRITE(6,'(a)') 'TES pressure grid'
!WRITE(6,'(F10.3)') ( TES(NT)%PRES(L), L=1,65 )
WRITE(6,'(a)') '20th row of MAP matrix in MAKE_PSEUDO_OBS'
WRITE(6,'(5F8.5)') ( MAP(20:24,L) , L=65,1,-1)
ENDIF
! Interpolate GC CH4 column to TES grid
DO LL = 1, LTES
GC_CH4_this(LL) = 0d0
DO L = 1, LLPAR
GC_CH4_this(LL) = GC_CH4_this(LL)
& + MAP(L,LL) * GC_CH4_NATIVE_this(L)
ENDDO
ENDDO
!--------------------------------------------------------------
! Apply TES observation operator
!
! x_hat = x_a + A_k ( x_m - x_a )
!
! where
! x_hat = GC modeled column as seen by TES [lnvmr]
! x_a = TES apriori column [lnvmr]
! x_m = GC modeled column [lnvmr]
! A_k = TES averaging kernel
!--------------------------------------------------------------
! x_m - x_a
DO L = 1, LTES
GC_CH4_this(L) = MAX(GC_CH4_this(L), 1d-10)
CH4_PERT(L) = LOG(GC_CH4_this(L)) - LOG(TES(NT)%PRIOR(L))
ENDDO
! x_a + A_k * ( x_m - x_a )
DO L = 1, LTES
CH4_HAT(L) = 0d0
DO LL = 1, LTES
CH4_HAT(L) = CH4_HAT(L)
& + TES(NT)%AVG_KERNEL(LL,L) * CH4_PERT(LL)
ENDDO
CH4_HAT(L) = CH4_HAT(L) + LOG(TES(NT)%PRIOR(L))
ENDDO
! Indexing of Averaging Kernel is seemingly backwards because
! TES observation files processed in IDL, which is column major
! Get RTVMR of GEOS-Chem column w/ TES obs operator applied
CALL GET_RTVMR( NT, EXP(CH4_HAT), GC_PSO_RTVMR, M_STAR )
! Add random error w/ standard deviation = ERR_PPB to RTVMR
GC_PSO_RTVMR_werr = GC_PSO_RTVMR + TES(NT)%ERR(1)
! Place pseudo-observation RTVMR [v/v] in TES structure
TES(NT)%GC_CH4(67) = GC_PSO_RTVMR_werr
IF ( NT == 600 ) THEN
print*,'Error [v/v] = ', TES(NT)%ERR(1)
print*,'GC_PSO_RTVMR = ',GC_PSO_RTVMR
print*,'GC_PSO_RTVMR_werr = ',GC_PSO_RTVMR_werr
ENDIF
! Make this hour equal to last hour
HH_last = HH
HH = 0
! ! Check GEOS-Chem, Error, and CH4_HAT for a given observation
! ! Success! kjw, 07/25/10
! IF ( NT == 600 ) THEN
! ! Write values for one observation to check that it's right
! FILENAME = 'test_pseudo_obs.NN.m'
! CALL EXPAND_NAME( FILENAME, N_CALC )
! FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME )
! OPEN( IU_FILE, FILE=TRIM( FILENAME ), STATUS='UNKNOWN',
! & IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' )
!
! WRITE(IU_FILE,'(a4,i4)') 'I = ', I
! WRITE(IU_FILE,'(a4,i4)') 'J = ', J
! WRITE(IU_FILE,'(a12,F8.3)') 'TES PSURF = ', TES(NT)%PRES(1)
! WRITE(IU_FILE,'(a12,F8.3)') 'GC PSURF = ', GC_PSURF_this
! WRITE(IU_FILE,'(a10,F16.12)') 'ERR_PPB =',1d9*TES(NT)%ERR(1)
! WRITE(IU_FILE,'(a)') '-------------------------------------'
! WRITE(IU_FILE,'(a)') 'GEOS-Chem CH4 Native'
! WRITE(IU_FILE,'(F16.8)') (1d9*GC_CH4(I,J,L), L=1,47)
! WRITE(IU_FILE,'(a)') '-------------------------------------'
! WRITE(IU_FILE,'(a)') 'GEOS-Chem CH4 Native w Error'
! WRITE(IU_FILE,'(F16.8)') (1d9*GC_CH4_native_this(L),L=1,47)
! WRITE(IU_FILE,'(a)') '-------------------------------------'
! WRITE(IU_FILE,'(a)') 'GEOS-Chem CH4 on TES'
! WRITE(IU_FILE,'(F16.8)') (1d9*GC_CH4_this(L), L=1,65)
! WRITE(IU_FILE,'(a)') '-------------------------------------'
! WRITE(IU_FILE,'(a)') 'TES a priori'
! WRITE(IU_FILE,'(F16.8)') ( 1d9*TES(NT)%PRIOR(L), L=1,65 )
! WRITE(IU_FILE,'(a)') '-------------------------------------'
! WRITE(IU_FILE,'(a)') 'CH4 HAT'
! WRITE(IU_FILE,'(F24.12)') ( CH4_HAT(L), L=1,65 )
! WRITE(IU_FILE,'(a)') '-------------------------------------'
! WRITE(IU_FILE,'(a)') 'EXP( CH4 HAT )'
! WRITE(IU_FILE,'(F16.8)') ( 1d9*EXP(CH4_HAT(L)), L=1,65 )
! WRITE(IU_FILE,'(a)') '-------------------------------------'
! WRITE(IU_FILE,'(a)') 'GC_CH4 HAT'
! WRITE(IU_FILE,'(F16.8)') ( 1d9*TES(NT)%GC_CH4(L), L=1,65 )
!
! CLOSE(IU_FILE)
! ENDIF
ENDDO ! End looping over each observation
! Return to calling program
END SUBROUTINE MAKE_PSEUDO_OBS
!------------------------------------------------------------------------------
SUBROUTINE CALC_TES_GC_BIAS
!
!******************************************************************************
! Subroutine CALC_TES_GC_BIAS calculates mean TES bias w.r.t. GEOS-Chem during
! the entire simulation period. Bias is then stored in module variable BIAS
!
! Arguments as Input:
! ============================================================================
! (1 )
!
! Arguments as Output:
! ============================================================================
! (1 )
!
! NOTES:
!
!******************************************************************************
!
! Reference to f90 modules
USE TIME_MOD, ONLY : GET_TAUb, GET_TAUe
USE TIME_MOD, ONLY : GET_NYMD, GET_NHMS
USE TIME_MOD, ONLY : GET_TIME_BEHIND_ADJ
USE TIME_MOD, ONLY : EXPAND_DATE
USE DIRECTORY_ADJ_MOD, ONLY : ADJTMP_DIR
USE BPCH2_MOD, ONLY : READ_BPCH2, GET_RES_EXT
USE BPCH2_MOD, ONLY : OPEN_BPCH2_FOR_READ
USE GRID_MOD, ONLY : GET_IJ
USE FILE_MOD, ONLY : IU_FILE, IOERROR
USE GRID_MOD, ONLY : GET_AREA_M2
# include "CMN_SIZE"
! Arguments
! Local Variables
LOGICAL :: file_exist
CHARACTER(LEN=255) :: TES_dir, READ_FILENAME
CHARACTER(LEN=255) :: PRS_ROOTNAME, PRS_FILENAME, CHK_FILENAME
INTEGER :: NTES, I, J, LTES, NHITS, hh
INTEGER :: NT, nday, N, L, ND49_NT, IOS
INTEGER :: nymd0, ND49_NTES, LL
INTEGER :: IIJJ(2)
INTEGER :: MATCHES(MAXTES)
INTEGER :: NTSTART, NTSTOP
REAL*8 :: GC_RTVMRt, OBS_RTVMRt
REAL*4 :: OBS_RTVMR_today(MAXTES)
REAL*4 :: GC_RTVMR_today(MAXTES)
REAL*4 :: OBS_RTVMR_tot(1000)
REAL*4 :: GC_RTVMR_tot(1000)
REAL*4 :: NOBS_tot(1000)
REAL*4 :: ARRAY0(1)
REAL*4 :: ARRAY1(MAXTES)
REAL*4 :: ND49_lat(MAXTES)
REAL*4 :: ND49_lon(MAXTES)
REAL*4 :: ND49_PSURF(MAXTES)
REAL*4 :: ARRAY2(MAXTES,LLPAR,1)
REAL*4 :: ND49_PCEN(MAXTES,LLPAR)
REAL*4 :: ND49_PEDGE(MAXTES,LLPAR)
REAL*4 :: ND49_kg_box(MAXTES,LLPAR)
REAL*8 :: tau0
REAL*8 :: date0(2)
REAL*8 :: TIME_FRAC(MAXTES)
REAL*4 :: TRACER(IIPAR,JJPAR,LLPAR)
REAL*4 :: GC_chk(IIPAR,JJPAR,LLPAR)
REAL*4 :: GC_ch4_kg(LLPAR)
REAL*8 :: GC_PRES(LLPAR)
REAL*8 :: GC_CH4_NATIVE(LLPAR)
REAL*8 :: CH4_PERT(MAXLEV)
REAL*8 :: GC_CH4(MAXLEV)
REAL*8 :: GC_PSURF
REAL*8 :: MAP(LLPAR,MAXLEV)
REAL*8 :: M_STAR(4,MAXLEV)
REAL*8 :: CH4_HAT(MAXLEV)
REAL*8 :: CH4_HAT_EXP(MAXLEV)
REAL*4 :: TAUb, TAUe
REAL*8 :: BIAS_tot, N_tot
! For binary punch file, version 2.0
INTEGER :: NI, NJ, NL, NV
INTEGER :: IJLOOP
INTEGER :: IFIRST, JFIRST, LFIRST
INTEGER :: NTRACER, NSKIP
INTEGER :: HALFPOLAR, CENTER180
REAL*4 :: LONRES, LATRES
REAL*8 :: ZTAU0, ZTAU1
CHARACTER(LEN=20) :: MODELNAME
CHARACTER(LEN=40) :: CATEGORY
CHARACTER(LEN=40) :: UNIT
CHARACTER(LEN=40) :: RESERVED
!=================================================================
! CALC_TES_GC_BIAS begins here!
!=================================================================
! TES observation root directory
TES_dir = '/home/kjw/TES/data/V004/bpch/'
print*,' CALC_TES_GC_BIAS'
print*,' current NYMD, NHMS = ',GET_NYMD(), GET_NHMS()
! Set TAUb and TAUe
TAUb = GET_TAUb()
TAUe = GET_TAUe()
! Set some variables for first iteration of loop
tau0 = TAUe - 24d0
nday = 1
! Loop over every day in assimilation period
DO WHILE ( tau0 > TAUb )
! Zero arrays to be safe
GC_RTVMR_today(:) = 0d0
OBS_RTVMR_today(:) = 0d0
! Get NYMD of the day
date0 = GET_TIME_BEHIND_ADJ( 1380 + (nday-1)*1440 )
nymd0 = date0(1)
print*,'nymd0 = ',nymd0
print*,'tau0 = ',tau0
! Get filename of TES observations
READ_FILENAME = TRIM( 'tes_ch4_YYYYMMDD.bpch' )
CALL EXPAND_DATE( READ_FILENAME, nymd0, 9999 )
READ_FILENAME = TRIM( TES_dir ) // TRIM( READ_FILENAME )
! Find whether observations exists on this day
INQUIRE( FILE=READ_FILENAME, exist=file_exist )
! If the file exists, proceed.
IF ( file_exist ) THEN
! Read TES_CH4_OBS during the day
CALL READ_TES_CH4_OBS( nymd0, NTES )
! TIME is YYYYMMDD.frac-of-day.
! Subtract date and save just time frac
TIME_FRAC(1:NTES) = TES(1:NTES)%TIME(1) - nymd0
! Open ND49 file and find the entries we want
PRS_FILENAME = 'ND49_trim_' // GET_RES_EXT() //
& '_YYYYMMDD.bpch'
CALL EXPAND_DATE( PRS_FILENAME, nymd0, 9999 )
PRS_ROOTNAME = '/home/kjw/GEOS-Chem/runs/ch4/TES/ND49_trim/'
PRS_FILENAME = TRIM( PRS_ROOTNAME) // TRIM( PRS_FILENAME )
! Get # of observations in this BPCH file
! Read NTES from ND49 file
CALL READ_BPCH2( PRS_FILENAME, 'IJ-AVG-$', 1,
& tau0, 1, 1,
& 1, ARRAY0(1), QUIET=.TRUE. )
ND49_NTES = INT( ARRAY0(1) )
!=================================================================
! Open binary punch file and read top-of-file header.
! Do some error checking to make sure the file is the right format.
!=================================================================
CALL OPEN_BPCH2_FOR_READ( IU_FILE, PRS_FILENAME )
!=================================================================
! Read data from the binary punch file
!
! NOTE: IOS < 0 is end-of-file, IOS > 0 is error condition
!=================================================================
DO
READ( IU_FILE, IOSTAT=IOS )
& MODELNAME, LONRES, LATRES, HALFPOLAR, CENTER180
IF ( IOS < 0 ) EXIT
IF ( IOS > 0 ) CALL IOERROR(IOS,IU_FILE, 'tes_ch4_mod:1')
READ( IU_FILE, IOSTAT=IOS )
& CATEGORY, NTRACER, UNIT, ZTAU0, ZTAU1, RESERVED,
& NI, NJ, NL, IFIRST, JFIRST, LFIRST,
& NSKIP
IF ( IOS /= 0 ) CALL IOERROR(IOS,IU_FILE,'tes_ch4_mod:2')
! Zero Dummy array
ARRAY2(:,:,:) = 0d0
READ( IU_FILE, IOSTAT=IOS )
& ( ( ( ARRAY2(I,J,L), I=1,NI ), J=1,NJ ), L=1,NL )
IF ( IOS /= 0 ) CALL IOERROR(IOS,IU_FILE,'tes_ch4_mod:3')
! Test for a match
IF ( 'IJ-AVG-$' == TRIM( CATEGORY ) ) THEN
! Longitude
IF ( NTRACER == 3 ) THEN
WRITE(6,*) ' - Reading: Latitude ... '
ND49_lat = ARRAY2(1:ND49_NTES,1,1)
! Latitude
ELSEIF ( NTRACER == 4 ) THEN
WRITE(6,*) ' - Reading: Longitude ... '
ND49_lon = ARRAY2(1:ND49_NTES,1,1)
! Surface Pressure
ELSEIF ( NTRACER == 7 ) THEN
WRITE(6,*) ' - Reading: PSURF ... '
ND49_PSURF = ARRAY2(1:ND49_NTES,1,1)
! Pressure Centers
ELSEIF ( NTRACER == 10 ) THEN
WRITE(6,*) ' - Reading: Pressure ... '
ND49_PCEN(:,:) = ARRAY2(1:ND49_NTES,1:LLPAR,1)
ENDIF ! If tracer == #
ENDIF ! If Category match
ENDDO
CLOSE( IU_FILE )
! Calculate Pressure edges from Pressure centers (approximate)
DO N=1,ND49_NTES
ND49_PEDGE(N,1) = ND49_PSURF(N)
DO L=2,LLPAR
ND49_PEDGE(N,L) = 0.5d0 * ( ND49_PCEN(N,L) +
& ND49_PCEN(N,(L-1)) )
ENDDO
ENDDO
! Calculate kg air/box from Pressure edges
ND49_PEDGE(:,:) = ND49_PEDGE(:,:) * 1d2 ! [hPa] --> [Pa]
DO N=1,ND49_NTES
DO L=1,LLPAR-1
ND49_kg_box(N,L) = (ND49_PEDGE(N,L) -ND49_PEDGE(N,(L+1)))
& / 9.81
ENDDO
!ND49_kg_box(N,47) = ND49_PEDGE(N,47) / 9.81
ND49_kg_box(N,LLPAR) = ND49_PEDGE(N,LLPAR) / 9.81
ENDDO
! Convert
! Associate ND49_NTES information with TES(NT) information
! Create array of indices of length = NTES.
! It should have values ex. [1, 2, 5, 7, ... ], associating each
! NTES with the matching index of ND49_NTES
nhits=1 ! nhits counts the # of matches we have.
! nhits should = NTES when these loops finish
DO NT=1,NTES
DO ND49_NT=1,ND49_NTES
IF ( TES(NT)%LAT(1) == ND49_lat(ND49_NT) .AND.
& TES(NT)%LON(1) == ND49_lon(ND49_NT) ) THEN
MATCHES(NT) = ND49_NT
nhits=nhits + 1
ENDIF
ENDDO
ENDDO
! Loop over every hour in the day
DO hh=23,0,-1
! Get NT range for this hour
CALL GET_NT_RANGE( NTES, hh*10000, TIME_FRAC,
& NTSTART, NTSTOP )
! If we have observations during this hour, proceed
IF ( NTSTART /= 0 .OR. NTSTOP /= 0 ) THEN
! Get GEOS-Chem CH4 values during this hour
!------------------------------------------------------
CHK_FILENAME = 'gctm.chk.YYYYMMDD.hhmm'
CALL EXPAND_DATE( CHK_FILENAME, nymd0, hh*10000 )
CHK_FILENAME = TRIM( ADJTMP_DIR ) //
& TRIM( CHK_FILENAME )
! Open the binary punch file for input
CALL OPEN_BPCH2_FOR_READ( IU_FILE, CHK_FILENAME )
READ( IU_FILE, IOSTAT=IOS )
& MODELNAME, LONRES, LATRES, HALFPOLAR, CENTER180
! IOS < 0 is end-of-file, so exit
IF ( IOS < 0 ) EXIT
! IOS > 0 is a real I/O error -- print error message
IF ( IOS > 0 )
& CALL IOERROR( IOS,IU_FILE,'read_checkpt_file:7' )
READ( IU_FILE, IOSTAT=IOS )
& CATEGORY, NTRACER, UNIT, ZTAU0, ZTAU1, RESERVED,
& NI, NJ, NL, IFIRST,JFIRST, LFIRST,
& NSKIP
IF ( IOS /= 0 )
& CALL IOERROR(IOS,IU_FILE,'read_checkpt_file:8' )
READ( IU_FILE, IOSTAT=IOS )
& ( ( ( TRACER(I,J,L), I=1,NI ), J=1,NJ ), L=1,NL )
IF ( IOS /= 0 )
& CALL IOERROR( IOS,IU_FILE,'read_checkpt_file:9' )
! Convert from kg/box to [v/v]
DO J=1,JJPAR
DO I=1,IIPAR
DO L=1,LLPAR
GC_chk(I,J,L) = TRACER(I,J,L)
ENDDO
ENDDO
ENDDO
! Close file
CLOSE( IU_FILE )
!------------------------------------------------------
!!!!!! GCvmr = GCkg / 16d-3 / ( kg_air(I,J,L) / 29d-3 )
! Loop over observations during this hour
DO NT = NTSTART, NTSTOP, -1
! For safety, initialize these up to LLTES
GC_CH4_NATIVE(:) = 0d0
GC_CH4(:) = 0d0
MAP(:,:) = 0d0
! Copy LTES to make coding a bit cleaner
LTES = TES(NT)%LTES(1)
! Get grid box of current record
IIJJ = GET_IJ( REAL(TES(NT)%LON(1),4),
& REAL(TES(NT)%LAT(1),4) )
I = IIJJ(1)
J = IIJJ(2)
! Get GEOS-Chem CH4 in [v/v] from [kg/box]
GC_CH4_kg(:) = GC_chk(I,J,:)
DO L=1,LLPAR
GC_CH4_NATIVE(L) = GC_CH4_kg(L) * 29d-3 / 16d-3 /
& ( ND49_kg_box(MATCHES(NT),L) * GET_AREA_M2(J) )
ENDDO
! IF (NT == 600) THEN
! print*,'GC_CH4_NATIVE(14) = ', GC_CH4_NATIVE(14)
! print*,'GC_CH4_kg(14) = ', GC_CH4_kg(14)
! print*,'ND49_kg_box(14) = ',
! & ND49_kg_box(MATCHES(NT),14)
! print*,'ND49_PEDGE(14) = ',
! & ND49_PEDGE(MATCHES(NT),14)
! ENDIF
! Get GEOS-Chem pressure levels
GC_PRES(:) = ND49_PCEN(MATCHES(NT),:)
GC_PSURF = ND49_PSURF(MATCHES(NT))
! Calculate the interpolation weight matrix
MAP(1:LLPAR,1:LTES)
& = GET_INTMAP( LLPAR, GC_PRES(:), GC_PSURF,
& LTES, TES(NT)%PRES(1:LTES), TES(NT)%PRES(1))
! Interpolate GC CH4 column to TES grid
DO LL = 1, LTES
GC_CH4(LL) = 0d0
DO L = 1, LLPAR
GC_CH4(LL) = GC_CH4(LL)
& + MAP(L,LL) * GC_CH4_NATIVE(L)
ENDDO
ENDDO
!--------------------------------------------------------------
! Apply TES observation operator
!
! x_hat = x_a + A_k ( x_m - x_a )
!
! where
! x_hat = GC modeled column as seen by TES [lnvmr]
! x_a = TES apriori column [lnvmr]
! x_m = GC modeled column [lnvmr]
! A_k = TES averaging kernel
!--------------------------------------------------------------
! x_m - x_a
DO L = 1, LTES
GC_CH4(L) =MAX(GC_CH4(L), 1d-10)
CH4_PERT(L) =LOG(GC_CH4(L))-LOG(TES(NT)%PRIOR(L))
ENDDO
! x_a + A_k * ( x_m - x_a )
DO L = 1, LTES
CH4_HAT(L) = 0d0
DO LL = 1, LTES
CH4_HAT(L) = CH4_HAT(L)
& + TES(NT)%AVG_KERNEL(LL,L) * CH4_PERT(LL)
ENDDO
CH4_HAT(L) = CH4_HAT(L) + LOG(TES(NT)%PRIOR(L))
ENDDO
! Transform from [ln(vmr)] --> [ppb]
CH4_HAT_EXP = EXP(CH4_HAT)
! Calculate RTVMR for profiles.
CALL GET_RTVMR(NT,TES(NT)%CH4, OBS_RTVMRt, M_STAR)
CALL GET_RTVMR(NT,CH4_HAT_EXP, GC_RTVMRt, M_STAR)
! Save RTVMR values
GC_RTVMR_today(NT) = GC_RTVMRt * 1d9
OBS_RTVMR_today(NT) = OBS_RTVMRt * 1d9
ENDDO! End looping over each obs during this hour
ENDIF ! End if we have obs during this hour
ENDDO ! End looping over each hour during the day
! If the file does not exist, say so and move to next day
ELSE
WRITE(6,*) ' - CALC_TES_GC_BIAS: no files today: ',
& TRIM( READ_FILENAME )
ENDIF
! Average RTVMRs from the day
OBS_RTVMR_tot(nday) = SUM( OBS_RTVMR_today )
GC_RTVMR_tot(nday) = SUM( GC_RTVMR_today )
NOBS_tot(nday) = NTES
! Increment Time counters
tau0 = tau0 - 24
nday = nday + 1
ENDDO
! Calculate mean bias from OBS_RTVMR_tot and GC_RTVMR_tot
BIAS_tot = SUM( OBS_RTVMR_tot - GC_RTVMR_tot )
N_tot = SUM( NOBS_tot )
BIAS_PPB = BIAS_tot / N_tot
print*,' - CALC_TES_GC_BIAS: '
print*,' GC_RTVMR_tot = ', SUM( GC_RTVMR_tot )/N_tot
print*,' OBS_RTVMR_tot = ', SUM( OBS_RTVMR_tot )/N_tot
print*,' Total # observations = ', N_tot
print*,' Mean Bias [ppb] = ', BIAS_PPB
print*,' We hope mean bias ~ 110 ppb'
! Return to calling program
END SUBROUTINE CALC_TES_GC_BIAS
!------------------------------------------------------------------------------
SUBROUTINE GET_RTVMR( NT, VMR_IN, RTVMR_OUT, M_STAR )
!
!******************************************************************************
! Subroutine GET_RTVMR returns Representative Tropospheric Volume Mixing Ratio
! for a given column of ln(vmr). RTVMR is described in Payne et. al. 2009
!
! Arguments as Input:
! ============================================================================
! (1 ) NT (INTEGER) : TES observation #
! (2 ) VMR_IN (REAL) : CH4 column [ln(vmr)] from which to calculate RTVMR
!
! Arguments as Output:
! ============================================================================
! (1 ) RTVMR (REAL) : RTVMR calculated from CH4 column
! (2 ) M_STAR (REAL) : Normalized Mapping Matrix
!
! NOTES:
!
!******************************************************************************
!
! Reference to f90 modules
! Arguments
INTEGER, INTENT(IN) :: NT
REAL*8, INTENT(IN) :: VMR_IN(MAXLEV)
REAL*8, INTENT(OUT) :: RTVMR_OUT
REAL*8, INTENT(OUT) :: M_STAR(4,MAXLEV)
!INTEGER :: NT
!REAL*8 :: ln_VMR_IN(MAXLEV)
!REAL*8 :: RTVMR_OUT
! Local Variables
INTEGER :: L, LC, LTES
REAL*8 :: MAX_AK
REAL*8 :: FINE_GRID(MAXLEV)
REAL*8 :: COARSE_GRID(4)
REAL*8 :: VMR_COARSE(4)
REAL*8 :: AK_ROW(MAXLEV)
REAL*8 :: temp
LOGICAL :: FOUND_2nd, FOUND_3rd
!=================================================================
! GET_RTVMR begins here!
!=================================================================
! Initialize and make necessary variables from arguments
! If we've found 2nd and 3rd elements of coarse grid
FOUND_2nd = .FALSE.
FOUND_3rd = .FALSE.
! To make coding cleaner
LTES = TES(NT)%LTES(1)
! Fine pressure grid
FINE_GRID(1:LTES) = TES(NT)%PRES(1:LTES)
! Construct Coarse Pressure grid
COARSE_GRID(1) = TES(NT)%PRES(1) ! Bottom level
COARSE_GRID(4) = TES(NT)%PRES(LTES) ! Top level
AK_ROW(1:LTES) = SUM( TES(NT)%AVG_KERNEL(1:LTES,1:LTES), 2 )
! Find max of rows of AK below ~50hPa
MAX_AK = MAXVAL( AK_ROW(1:35) )
IF (NT == 600) THEN
print*,'--------------------------------------------------'
print*,'NT = ', NT
print*,'MAX_AK',MAX_AK
print*,'AK_ROW',AK_ROW
print*,'--------------------------------------------------'
ENDIF
DO L=LTES,1,-1
! First pressure level at which sum of rows of AK > 0.4
IF ( AK_ROW(L) > 0.4 .AND. TES(NT)%PRES(L) > 30.0 .AND.
& FOUND_3rd == .FALSE. ) THEN
COARSE_GRID(3) = TES(NT)%PRES(L)
FOUND_3rd = .TRUE.
ENDIF
! Pressure level at which rows of AK are maximum
IF ( AK_ROW(L) == MAX_AK .AND. FOUND_2nd == .FALSE. ) THEN
COARSE_GRID(2) = TES(NT)%PRES(L)
FOUND_2nd = .TRUE.
ENDIF
ENDDO
! Now that we have fine and coarse grids, make mapping matrix
M_STAR = MAKE_RTVMR_MAP( NT, LTES, FINE_GRID, COARSE_GRID )
! !kjw debug
! IF ( NT == 600 ) THEN
! print*,'Checking AK_ROW'
! print*,AK_ROW
! print*,'Checking COARSE_GRID'
! print*,COARSE_GRID
! print*,'Checking M_STAR ... '
! print*,'SUM of rows of M_STAR(4,LTES)'
! print*,SUM(M_STAR,2)
! print*,'Writing Out M_STAR'
! WRITE(6,546) (L,M_STAR(1,L),M_STAR(2,L),M_STAR(3,L),
! & M_STAR(4,L), L=1,MAXLEV)
! 546 FORMAT(i4, 2x, F10.8, 2x, F10.8, 2x, F10.8, 2x, F10.8)
! ENDIF
! Apply mapping matrix to CH4 column
DO LC=1,4
temp = 0d0
DO L=1,LTES
temp = temp + M_STAR(LC,L) * VMR_IN(L)
ENDDO
VMR_COARSE(LC) = temp
ENDDO
! RTVMR value is 2nd element of the coarse array
RTVMR_OUT = VMR_COARSE(2)
! Return to calling program
END SUBROUTINE GET_RTVMR
!------------------------------------------------------------------------------
FUNCTION MAKE_RTVMR_MAP( NT, LTES, FINE_GRID, COARSE_GRID )
& RESULT( M_STAR )
!
!******************************************************************************
! Subroutine MAKE_RTVMR_MAP makes matrix to map 67 element TES grid to 4
! element RTVMR grid. Adapted from from Mark Shephard and Vivienne
! Payne's retv_make_map_vhp.pro (acquired by kjw from Vivienne).
!
! Arguments as Input:
! ============================================================================
! (1 ) NT (INTEGER) : # of TES observation
! (2 ) FINE_GRID (REAL) : Fine pressure grid from which to map VMR
! (3 ) COARSE_GRID (REAL) : Coarse pressure grid onto which we will map VMR
!
! Arguments as Output:
! ============================================================================
! (1 ) M_STAR (REAL) : Normalized mapping matrix x_coarse = M* x_fine
! M* is pseudo-inverse of M, which maps coarse to fine grid
! NOTES:
!
!******************************************************************************
!
! Reference to f90 modules
! Arguments
!INTEGER, INTENT(IN) :: LTES
!REAL*8, INTENT(IN) :: FINE_GRID(MAXLEV)
!REAL*8, INTENT(IN) :: COARSE_GRID(4)
!REAL*8, INTENT(OUT) :: M_STAR(4,MAXLEV)
INTEGER :: LTES, NT
REAL*8 :: FINE_GRID(MAXLEV)
REAL*8 :: COARSE_GRID(4)
REAL*8 :: M_STAR(4,MAXLEV)
! Local Variables
INTEGER :: L, LC, IND, K
INTEGER :: FINE_INDS(4), FINE_INDS_SIX(6)
REAL*8 :: MAP_TEMP(4,MAXLEV)
REAL*8 :: MAP_NORM(4,MAXLEV)
REAL*8 :: sum_map(4)
REAL*8 :: xdelta_p, xcoeff
!=================================================================
! MAKE_RTVMR_MAP begins here!
!=================================================================
! Initialize and get required values
MAP_TEMP(:,:) = 0d0
MAP_NORM(:,:) = 0d0
! Find indices of fine grid which match coarse grid
FINE_INDS(:) = 0d0
IND = 1
DO L=1,LTES
IF ( FINE_GRID(L) == COARSE_GRID(IND) ) THEN
FINE_INDS(IND) = L
IND = IND + 1
ENDIF
ENDDO
! Make 6-element array of indices
FINE_INDS_SIX(:) = 0d0
FINE_INDS_SIX(1) = FINE_INDS(1)
FINE_INDS_SIX(2:5) = FINE_INDS(:)
FINE_INDS_SIX(6) = FINE_INDS(4)
!kjw debug
! IF ( NT == 600 ) THEN
! print*,'Checking FINE_GRID'
! print*,FINE_GRID
! print*,'Checking FINE_INDS'
! print*,FINE_INDS
! print*,'Checking FINE_INDS_SIX'
! print*,FINE_INDS_SIX
! ENDIF
DO L=1,6
IF ( FINE_INDS_SIX(L) == 0.0 ) THEN
print*,'kjw debug: indices of fine grid matches to coarse'
print*,FINE_INDS_SIX
print*,' doh, this is f***ed up. FINE_INDS(L) = 0. L = ',L
print*,COARSE_GRID
ENDIF
IF ( FINE_INDS_SIX(L) > 67.0 ) THEN
print*,'kjw debug: indices of fine grid matches to coarse'
print*,' doh, this is f***ed up. FINE_INDS(L) >67. L = ',L
print*,COARSE_GRID
ENDIF
ENDDO
! Populate mapping matrix
K = 1
DO LC=1,4
DO L=FINE_INDS_SIX(K),FINE_INDS_SIX(K+2)
! Bottom of profile is set a constant perturbation
IF ( FINE_GRID(L) > COARSE_GRID(LC) .AND. LC == 1 ) THEN
MAP_TEMP(LC,L) = 1.0d0
ENDIF
! Bottom side of profile
IF ( LC /= 1 ) THEN
IF ( FINE_GRID(L) >= COARSE_GRID(LC) .AND.
& FINE_GRID(L) <= COARSE_GRID(LC-1) ) THEN
xdelta_p = LOG(COARSE_GRID(LC-1))-LOG(COARSE_GRID(LC))
xcoeff = 1d0 - ( LOG(FINE_GRID(L)) -
& LOG(COARSE_GRID(LC)) ) / xdelta_p
MAP_TEMP(LC,L) = xcoeff
ENDIF
ENDIF
! Top side of profile
IF ( LC /= 4 ) THEN
IF ( FINE_GRID(L) <= COARSE_GRID(LC) .AND.
& FINE_GRID(L) >= COARSE_GRID(LC+1) ) THEN
xdelta_p = LOG(COARSE_GRID(LC))-LOG(COARSE_GRID(LC+1))
xcoeff = 1d0 - ( -LOG(FINE_GRID(L)) +
& LOG(COARSE_GRID(LC)) ) / xdelta_p
MAP_TEMP(LC,L) = xcoeff
ENDIF
ENDIF
! Top of profile is set a constant perturbation
IF ( FINE_GRID(L) < COARSE_GRID(LC) .AND. LC == 4 ) THEN
MAP_TEMP(LC,L) = 1.0d0
ENDIF
ENDDO
! Increment Indices between which to fill
K = K + 1
ENDDO
! !kjw debug
! IF ( NT == 600 ) THEN
! print*,'Checking M_STAR ... '
! print*,'SUM of rows of MAP_TEMP(4,LTES)'
! print*,SUM(MAP_TEMP,2)
! print*,'Writing Out MAP_TEMP'
! WRITE(6,547) (L,MAP_TEMP(1,L),MAP_TEMP(2,L),MAP_TEMP(3,L),
! & MAP_TEMP(4,L), L=1,MAXLEV)
! 547 FORMAT(i4, 2x, F10.8, 2x, F10.8, 2x, F10.8, 2x, F10.8)
! ENDIF
! Normalize Mapping Matrix
sum_map(:) = 0d0
sum_map(:) = SUM( MAP_TEMP, 2 )
sum_map(:) = 0d0
DO LC=1,4
sum_map(LC) = SUM( MAP_TEMP(LC,:) )
IF (NT .EQ. 600) THEN
!print*,'Sum map',LC
!print*,sum_map(LC)
ENDIF
MAP_NORM(LC,:) = MAP_TEMP(LC,:) / sum_map(LC)
ENDDO
! !kjw debug
! IF ( NT == 600 ) THEN
! print*,'Checking M_STAR ... '
! print*,'SUM of rows of MAP_NORM(4,LTES)'
! print*,SUM(MAP_NORM,2)
! print*,'Writing Out MAP_NORM'
! WRITE(6,547) (L,MAP_NORM(1,L),MAP_NORM(2,L),MAP_NORM(3,L),
! & MAP_NORM(4,L), L=1,MAXLEV)
! ENDIF
! Assign Map to output variable
M_STAR = MAP_NORM
! Return to calling program
END FUNCTION MAKE_RTVMR_MAP
!------------------------------------------------------------------------------
SUBROUTINE GET_NT_RANGE( NTES, HHMMSS, TIME_FRAC, NTSTART, NTSTOP)
!
!******************************************************************************
! Subroutine GET_NT_RANGE retuns the range of retrieval records for the
! current model hour
!
!
! Arguments as Input:
! ============================================================================
! (1 ) NTES (INTEGER) : Number of TES retrievals in this day
! (2 ) HHMMSS (INTEGER) : Current model time
! (3 ) TIME_FRAC (REAL) : Vector of times (frac-of-day) for the TES retrievals
!
! Arguments as Output:
! ============================================================================
! (1 ) NTSTART (INTEGER) : TES record number at which to start
! (1 ) NTSTOP (INTEGER) : TES record number at which to stop
!
! NOTES:
!
!******************************************************************************
!
! Reference to f90 modules
USE ERROR_MOD, ONLY : ERROR_STOP
USE TIME_MOD, ONLY : YMD_EXTRACT
! Arguments
INTEGER, INTENT(IN) :: NTES
INTEGER, INTENT(IN) :: HHMMSS
REAL*8, INTENT(IN) :: TIME_FRAC(NTES)
INTEGER, INTENT(OUT) :: NTSTART
INTEGER, INTENT(OUT) :: NTSTOP
! Local variables
INTEGER, SAVE :: NTSAVE
LOGICAL :: FOUND_ALL_RECORDS
INTEGER :: NTEST
INTEGER :: HH, MM, SS
REAL*8 :: GC_HH_FRAC
REAL*8 :: H1_FRAC
!=================================================================
! GET_NT_RANGE begins here!
!=================================================================
! Initialize
FOUND_ALL_RECORDS = .FALSE.
NTSTART = 0
NTSTOP = 0
! set NTSAVE to NTES every time we start with a new file
IF ( HHMMSS == 230000 ) NTSAVE = NTES
print*, ' GET_NT_RANGE for ', HHMMSS
print*, ' NTSAVE ', NTSAVE
print*, ' NTES ', NTES
CALL YMD_EXTRACT( HHMMSS, HH, MM, SS )
! Convert HH from hour to fraction of day
GC_HH_FRAC = REAL(HH,8) / 24d0
! one hour as a fraction of day
H1_FRAC = 1d0 / 24d0
! All records have been read already
IF ( NTSAVE == 0 ) THEN
print*, 'All records have been read already '
RETURN
! No records reached yet
ELSEIF ( TIME_FRAC(NTSAVE) + H1_FRAC < GC_HH_FRAC ) THEN
print*, 'No records reached yet'
RETURN
!
ELSEIF ( TIME_FRAC(NTSAVE) + H1_FRAC >= GC_HH_FRAC ) THEN
! Starting record found
NTSTART = NTSAVE
print*, ' Starting : TIME_FRAC(NTSTART) ',
& TIME_FRAC(NTSTART), NTSTART
! Now search forward to find stopping record
NTEST = NTSTART
DO WHILE ( FOUND_ALL_RECORDS == .FALSE. )
! Advance to the next record
NTEST = NTEST - 1
! Stop if we reach the earliest available record
IF ( NTEST == 0 ) THEN
NTSTOP = NTEST + 1
FOUND_ALL_RECORDS = .TRUE.
print*, ' Records found '
print*, ' NTSTART, NTSTOP = ', NTSTART, NTSTOP
! Reset NTSAVE
NTSAVE = NTEST
! When the combined test date rounded up to the nearest
! half hour is smaller than the current model date, the
! stopping record has been passed.
!kjw
! shouldn't the line below be:
! ELSEIF ( TIME_FRAC(NTEST) + H1_FRAC/2d0 < GC_HH_FRAC ) THEN
! (difference is dividing H1_FRAC by 2)
! necessary to round to nearest half hour instead of full hour
!kjw
ELSEIF ( TIME_FRAC(NTEST) + H1_FRAC < GC_HH_FRAC ) THEN
print*, ' Testing : TIME_FRAC ',
& TIME_FRAC(NTEST), NTEST
NTSTOP = NTEST + 1
FOUND_ALL_RECORDS = .TRUE.
print*, ' Records found '
print*, ' NTSTART, NTSTOP = ', NTSTART, NTSTOP
! Reset NTSAVE
NTSAVE = NTEST
ELSE
print*, ' still looking ', NTEST
ENDIF
ENDDO
ELSE
CALL ERROR_STOP('problem', 'GET_NT_RANGE' )
ENDIF
! Return to calling program
END SUBROUTINE GET_NT_RANGE
!------------------------------------------------------------------------------
FUNCTION GET_INTMAP( LGC_TOP, GC_PRESC, GC_SURFP,
& LTM_TOP, TM_PRESC, TM_SURFP )
* RESULT ( HINTERPZ )
!
!******************************************************************************
! Function GET_INTMAP linearly interpolates column quatities
! based upon the centered (average) pressue levels.
!
! Arguments as Input:
! ============================================================================
! (1 ) LGC_TOP (TYPE) : Description [unit]
! (2 ) GC_PRES (TYPE) : Description [unit]
! (3 ) GC_SURFP(TYPE) : Description [unit]
! (4 ) LTM_TOP (TYPE) : Description [unit]
! (5 ) TM_PRES (TYPE) : Description [unit]
! (6 ) TM_SURFP(TYPE) : Description [unit]
!
! Arguments as Output:
! ============================================================================
! (1 ) HINTERPZ (TYPE) : Description [unit]
!
! NOTES:
! (1 ) Based on the GET_HINTERPZ_2 routine I wrote for read_sciano2_mod.
!
!******************************************************************************
!
! Reference to f90 modules
USE ERROR_MOD, ONLY : ERROR_STOP
USE PRESSURE_MOD, ONLY : GET_BP
! Arguments
INTEGER :: LGC_TOP, LTM_TOP
REAL*8 :: GC_PRESC(LGC_TOP)
REAL*8 :: TM_PRESC(LTM_TOP)
REAL*8 :: GC_SURFP
REAL*8 :: TM_SURFP
! Return value
REAL*8 :: HINTERPZ(LGC_TOP, LTM_TOP)
! Local variables
INTEGER :: LGC, LTM
REAL*8 :: DIFF, DELTA_SURFP
REAL*8 :: LOW, HI
!=================================================================
! GET_HINTERPZ_2 begins here!
!=================================================================
HINTERPZ(:,:) = 0D0
! ! Rescale GC grid according to TM surface pressure
!! p1_A = (a1 + b1 (ps_A - PTOP))
!! p2_A = (a2 + b2 (ps_A - PTOP))
!! p1_B = (a + b (ps_B - PTOP))
!! p2_B = *(a + b (ps_B - PTOP))
!! pc_A = 0.5(a1+a2 +(b1+b2)*(ps_A - PTOP))
!! pc_B = 0.5(a1+a2 +(b1+b2)*(ps_B - PTOP))
!! pc_B - pc_A = 0.5(b1_b2)(ps_B-ps_A)
!! pc_B = 0.5(b1_b2)(ps_B-ps_A) + pc_A
! DELTA_SURFP = 0.5d0 * ( TM_SURFP -GC_SURFP )
!
! DO LGC = 1, LGC_TOP
! GC_PRESC(LGC) = ( GET_BP(LGC) + GET_BP(LGC+1))
! & * DELTA_SURFP + GC_PRESC(LGC)
! IF (GC_PRESC(LGC) < 0) THEN
! CALL ERROR_STOP( 'highly unlikey',
! & 'read_sciano2_mod.f')
! ENDIF
!
! ENDDO
! Loop over each pressure level of TM grid
DO LTM = 1, LTM_TOP
! Find the levels from GC that bracket level LTM
DO LGC = 1, LGC_TOP - 1
LOW = GC_PRESC(LGC+1)
HI = GC_PRESC(LGC)
IF (LGC == 0) HI = TM_SURFP !kjw. this line is useless
! Linearly interpolate value on the LTM grid
IF ( TM_PRESC(LTM) <= HI .and.
& TM_PRESC(LTM) > LOW) THEN
DIFF = HI - LOW
HINTERPZ(LGC+1,LTM) = ( HI - TM_PRESC(LTM) ) / DIFF
HINTERPZ(LGC ,LTM) = ( TM_PRESC(LTM) - LOW ) / DIFF
ENDIF
! dkh debug
!print*, 'LGC,LTM,HINT', LGC, LTM, HINTERPZ(LGC,LTM)
ENDDO
ENDDO
! Correct for case where TES pressure is higher than the
! highest GC pressure. In this case, just 1:1 map.
DO LTM = 1, LTM_TOP
IF ( TM_PRESC(LTM) > GC_PRESC(1) ) THEN
HINTERPZ(:,LTM) = 0D0
HINTERPZ(LTM,LTM) = 1D0
ENDIF
ENDDO
! Return to calling program
END FUNCTION GET_INTMAP
!!------------------------------------------------------------------------------
!!------------------------------------------------------------------------------
FUNCTION GET_IJ_2x25( LON, LAT ) RESULT ( IIJJ )
!
!******************************************************************************
! Subroutine GET_IJ_2x25 returns I and J index from the 2 x 2.5 grid for a
! LON, LAT coord. (dkh, 11/08/09)
!
!
! Arguments as Input:
! ============================================================================
! (1 ) LON (REAL*8) : Longitude [degrees]
! (2 ) LAT (REAL*8) : Latitude [degrees]
!
! Function result
! ============================================================================
! (1 ) IIJJ(1) (INTEGER) : Long index [none]
! (2 ) IIJJ(2) (INTEGER) : Lati index [none]
!
! NOTES:
!
!******************************************************************************
!
! Reference to f90 modules
USE ERROR_MOD, ONLY : ERROR_STOP
! Arguments
REAL*4 :: LAT, LON
! Return
INTEGER :: I, J, IIJJ(2)
! Local variables
REAL*8 :: TLON, TLAT, DLON, DLAT
REAL*8, PARAMETER :: DISIZE = 2.5d0
REAL*8, PARAMETER :: DJSIZE = 2.0d0
INTEGER, PARAMETER :: IIMAX = 144
INTEGER, PARAMETER :: JJMAX = 91
!=================================================================
! GET_IJ_2x25 begins here!
!=================================================================
TLON = 180d0 + LON + DISIZE
TLAT = 90d0 + LAT + DJSIZE
I = TLON / DISIZE
J = TLAT / DJSIZE
IF ( TLON / DISIZE - REAL(I) >= 0.5d0 ) THEN
I = I + 1
ENDIF
IF ( TLAT / DJSIZE - REAL(J) >= 0.5d0 ) THEN
J = J + 1
ENDIF
! Longitude wraps around
!IF ( I == 73 ) I = 1
IF ( I == ( IIMAX + 1 ) ) I = 1
! Check for impossible values
IF ( I > IIMAX .or. J > JJMAX .or.
& I < 1 .or. J < 1 ) THEN
CALL ERROR_STOP('Error finding grid box', 'GET_IJ_2x25')
ENDIF
IIJJ(1) = I
IIJJ(2) = J
! Return to calling program
END FUNCTION GET_IJ_2x25
!!-----------------------------------------------------------------------------
! SUBROUTINE INIT_TES_CH4
!!
!!*****************************************************************************
!! Subroutine INIT_TES_CH4 deallocates all module arrays. (dkh, 02/15/09)
!!
!! NOTES:
!!
!!******************************************************************************
!!
! USE ERROR_MOD, ONLY : ALLOC_ERR
!
!# include "CMN_SIZE" ! IIPAR, JJPAR
!
! ! Local variables
! INTEGER :: AS
!
! !=================================================================
! ! INIT_TES_CH4 begins here
! !=================================================================
!
! ! dkh debug
! print*, ' INIT_TES_CH4'
!
! ALLOCATE( CH4_SAVE( LLPAR, MAXTES ), STAT=AS )
! IF ( AS /= 0 ) CALL ALLOC_ERR( 'CH4_SAVE' )
! CH4_SAVE = 0d0
!
!
! TES( 1 )%NYMD = 20050704
! TES( 2 )%NYMD = 20050704
! TES( 3 )%NYMD = 20050704
! TES( 4 )%NYMD = 20050704
! TES( 5 )%NYMD = 20050704
! TES( 6 )%NYMD = 20050704
! TES( 7 )%NYMD = 20050704
! TES( 8 )%NYMD = 20050704
! TES( 9 )%NYMD = 20050705
! TES( 10 )%NYMD = 20050705
! TES( 11 )%NYMD = 20050705
! TES( 12 )%NYMD = 20050705
! TES( 13 )%NYMD = 20050705
! TES( 14 )%NYMD = 20050705
! TES( 15 )%NYMD = 20050705
! TES( 16 )%NYMD = 20050705
! TES( 17 )%NYMD = 20050705
! TES( 18 )%NYMD = 20050710
! TES( 19 )%NYMD = 20050710
! TES( 20 )%NYMD = 20050710
! TES( 21 )%NYMD = 20050710
! TES( 22 )%NYMD = 20050710
! TES( 23 )%NYMD = 20050710
! TES( 24 )%NYMD = 20050710
! TES( 25 )%NYMD = 20050710
! TES( 26 )%NYMD = 20050710
! TES( 27 )%NYMD = 20050711
! TES( 28 )%NYMD = 20050711
! TES( 29 )%NYMD = 20050711
! TES( 30 )%NYMD = 20050711
! TES( 31 )%NYMD = 20050712
! TES( 32 )%NYMD = 20050712
! TES( 33 )%NYMD = 20050712
! TES( 34 )%NYMD = 20050712
! TES( 35 )%NYMD = 20050712
! TES( 36 )%NYMD = 20050712
! TES( 37 )%NYMD = 20050712
! TES( 38 )%NYMD = 20050712
! TES( 39 )%NYMD = 20050713
! TES( 40 )%NYMD = 20050713
! TES( 41 )%NYMD = 20050713
! TES( 42 )%NYMD = 20050713
! TES( 43 )%NYMD = 20050713
! TES( 44 )%NYMD = 20050713
! TES( 45 )%NYMD = 20050713
! TES( 46 )%NYMD = 20050713
! TES( 47 )%NYMD = 20050713
! TES( 48 )%NYMD = 20050714
! TES( 49 )%NYMD = 20050714
! TES( 50 )%NYMD = 20050714
! TES( 51 )%NYMD = 20050714
! TES( 52 )%NYMD = 20050714
! TES( 53 )%NYMD = 20050714
! TES( 54 )%NYMD = 20050714
! TES( 55 )%NYMD = 20050714
! TES( 56 )%NYMD = 20050715
! TES( 57 )%NYMD = 20050715
! TES( 58 )%NYMD = 20050715
! TES( 59 )%NYMD = 20050715
! TES( 60 )%NYMD = 20050715
! TES( 61 )%NYMD = 20050715
! TES( 62 )%NYMD = 20050715
! TES( 63 )%NYMD = 20050715
! TES( 64 )%NYMD = 20050715
! TES( 65 )%NYMD = 20050716
! TES( 66 )%NYMD = 20050717
! TES( 67 )%NYMD = 20050717
! TES( 68 )%NYMD = 20050717
! TES( 69 )%NYMD = 20050717
! TES( 70 )%NYMD = 20050717
! TES( 71 )%NYMD = 20050717
! TES( 72 )%NYMD = 20050717
! TES( 73 )%NYMD = 20050717
! TES( 74 )%NYMD = 20050717
! TES( 75 )%NYMD = 20050718
! TES( 76 )%NYMD = 20050718
! TES( 77 )%NYMD = 20050718
! TES( 78 )%NYMD = 20050718
! TES( 79 )%NYMD = 20050719
! TES( 80 )%NYMD = 20050719
! TES( 81 )%NYMD = 20050719
! TES( 82 )%NYMD = 20050719
! TES( 83 )%NYMD = 20050719
! TES( 84 )%NYMD = 20050719
! TES( 85 )%NYMD = 20050719
! TES( 86 )%NYMD = 20050719
! TES( 87 )%NYMD = 20050719
!
! TES( 1 )%NHMS = 202000
! TES( 2 )%NHMS = 202100
! TES( 3 )%NHMS = 202100
! TES( 4 )%NHMS = 202100
! TES( 5 )%NHMS = 202200
! TES( 6 )%NHMS = 202300
! TES( 7 )%NHMS = 202300
! TES( 8 )%NHMS = 202400
! TES( 9 )%NHMS = 082100
! TES( 10 )%NHMS = 082100
! TES( 11 )%NHMS = 082200
! TES( 12 )%NHMS = 082200
! TES( 13 )%NHMS = 082300
! TES( 14 )%NHMS = 082300
! TES( 15 )%NHMS = 082400
! TES( 16 )%NHMS = 082400
! TES( 17 )%NHMS = 082500
! TES( 18 )%NHMS = 194300
! TES( 19 )%NHMS = 194300
! TES( 20 )%NHMS = 194400
! TES( 21 )%NHMS = 194400
! TES( 22 )%NHMS = 194500
! TES( 23 )%NHMS = 194500
! TES( 24 )%NHMS = 194600
! TES( 25 )%NHMS = 194600
! TES( 26 )%NHMS = 194700
! TES( 27 )%NHMS = 092300
! TES( 28 )%NHMS = 092300
! TES( 29 )%NHMS = 092400
! TES( 30 )%NHMS = 092400
! TES( 31 )%NHMS = 193000
! TES( 32 )%NHMS = 193100
! TES( 33 )%NHMS = 193100
! TES( 34 )%NHMS = 193200
! TES( 35 )%NHMS = 193300
! TES( 36 )%NHMS = 193300
! TES( 37 )%NHMS = 193400
! TES( 38 )CH4%NHMS = 193400
! TES( 39 )%NHMS = 091000
! TES( 40 )%NHMS = 091100
! TES( 41 )%NHMS = 091100
! TES( 42 )%NHMS = 091200
! TES( 43 )%NHMS = 091200
! TES( 44 )%NHMS = 091200
! TES( 45 )%NHMS = 091300
! TES( 46 )%NHMS = 091300
! TES( 47 )%NHMS = 091400
! TES( 48 )%NHMS = 191900
! TES( 49 )%NHMS = 191900
! TES( 50 )%NHMS = 191900
! TES( 51 )%NHMS = 192000
! TES( 52 )%NHMS = 192000
! TES( 53 )%NHMS = 192100
! TES( 54 )%NHMS = 192100
! TES( 55 )%NHMS = 192200
! TES( 56 )%NHMS = 085800
! TES( 57 )%NHMS = 085800
! TES( 58 )%NHMS = 085900
! TES( 59 )%NHMS = 085900
! TES( 60 )%NHMS = 090000
! TES( 61 )%NHMS = 090000
! TES( 62 )%NHMS = 090100
! TES( 63 )%NHMS = 090100
! TES( 64 )%NHMS = 090100
! TES( 65 )%NHMS = 190900
! TES( 66 )%NHMS = 084500
! TES( 67 )%NHMS = 084600
! TES( 68 )%NHMS = 084600
! TES( 69 )%NHMS = 084700
! TES( 70 )%NHMS = 084700
! TES( 71 )%NHMS = 084800
! TES( 72 )%NHMS = 084800
! TES( 73 )%NHMS = 084900
! TES( 74 )%NHMS = 084900
! TES( 75 )%NHMS = 203200
! TES( 76 )%NHMS = 203300
! TES( 77 )%NHMS = 203300
! TES( 78 )%NHMS = 203400
! TES( 79 )%NHMS = 083300
! TES( 80 )%NHMS = 083400
! TES( 81 )%NHMS = 083400
! TES( 82 )%NHMS = 083500
! TES( 83 )%NHMS = 083500
! TES( 84 )%NHMS = 083500
! TES( 85 )%NHMS = 083600
! TES( 86 )%NHMS = 083600
! TES( 87 )%NHMS = 083700
!
! TES( 1 )%LAT = 31.29
! TES( 2 )%LAT = 33
! TES( 3 )%LAT = 34.64
! TES( 4 )%LAT = 36.2
! TES( 5 )%LAT = 37.91
! TES( 6 )%LAT = 41.1
! TES( 7 )%LAT = 42.8
! TES( 8 )%LAT = 44.43
! TES( 9 )%LAT = 43.54
! TES( 10 )%LAT = 41.84
! TES( 11 )%LAT = 40.2
! TES( 12 )%LAT = 38.65
! TES( 13 )%LAT = 36.94
! TES( 14 )%LAT = 35.3
! TES( 15 )%LAT = 33.74
! TES( 16 )%LAT = 32.03
! TES( 17 )%LAT = 30.39
! TES( 18 )%LAT = 31.28
! TES( 19 )%LAT = 32.99
! TES( 20 )%LAT = 34.63
! TES( 21 )%LAT = 36.19
! TES( 22 )%LAT = 37.9
! TES( 23 )%LAT = 39.53
! TES( 24 )%LAT = 41.09
! TES( 25 )%LAT = 42.8
! TES( 26 )%LAT = 44.42
! TES( 27 )%LAT = 43.55
! TES( 28 )%LAT = 41.85
! TES( 29 )%LAT = 40.22
! TES( 30 )%LAT = 38.66
! TES( 31 )%LAT = 31.28
! TES( 32 )%LAT = 32.99
! TES( 33 )%LAT = 34.63
! TES( 34 )%LAT = 36.19
! TES( 35 )%LAT = 39.53
! TES( 36 )%LAT = 41.09
! TES( 37 )%LAT = 42.79
! TES( 38 )%LAT = 44.42
! TES( 39 )%LAT = 43.55
! TES( 40 )%LAT = 41.85
! TES( 41 )%LAT = 40.22
! TES( 42 )%LAT = 38.66
! TES( 43 )%LAT = 36.96
! TES( 44 )%LAT = 35.32
! TES( 45 )%LAT = 33.76
! TES( 46 )%LAT = 32.04
! TES( 47 )%LAT = 30.4
! TES( 48 )%LAT = 32.99
! TES( 49 )%LAT = 34.63
! TES( 50 )%LAT = 36.2
! TES( 51 )%LAT = 37.9
! TES( 52 )%LAT = 39.54
! TES( 53 )%LAT = 41.1
! TES( 54 )%LAT = 42.8
! TES( 55 )%LAT = 44.42
! TES( 56 )%LAT = 43.55
! TES( 57 )%LAT = 41.85
! TES( 58 )%LAT = 40.22
! TES( 59 )%LAT = 38.66
! TES( 60 )%LAT = 36.95
! TES( 61 )%LAT = 35.31
! TES( 62 )%LAT = 33.75
! TES( 63 )%LAT = 32.04
! TES( 64 )%LAT = 30.4
! TES( 65 )%LAT = 44.4
! TES( 66 )%LAT = 43.59
! TES( 67 )%LAT = 41.89
! TES( 68 )%LAT = 40.26
! TES( 69 )%LAT = 38.7
! TES( 70 )%LAT = 37
! TES( 71 )%LAT = 35.36
! TES( 72 )%LAT = 33.8
! TES( 73 )%LAT = 32.09
! TES( 74 )%LAT = 30.45
! TES( 75 )%LAT = 31.27
! TES( 76 )%LAT = 32.98
! TES( 77 )%LAT = 34.62
! TES( 78 )%LAT = 36.18
! TES( 79 )%LAT = 43.58
! TES( 80 )%LAT = 41.88
! TES( 81 )%LAT = 40.25
! TES( 82 )%LAT = 38.69
! TES( 83 )%LAT = 36.98
! TES( 84 )%LAT = 35.34
! TES( 85 )%LAT = 33.78
! TES( 86 )%LAT = 32.07
! TES( 87 )%LAT = 30.43
!
! TES( 1 )%LON = -105.13
! TES( 2 )%LON = -105.6
! TES( 3 )%LON = -106.05
! TES( 4 )%LON = -106.5
! TES( 5 )%LON = -107
! TES( 6 )%LON = -108
! TES( 7 )%LON = -108.57
! TES( 8 )%LON = -109.13
! TES( 9 )%LON = -92.52
! TES( 10 )%LON = -93.09
! TES( 11 )%LON = -93.62
! TES( 12 )%LON = -94.11
! TES( 13 )%LON = -94.62
! TES( 14 )%LON = -95.09
! TES( 15 )%LON = -95.53
! TES( 16 )%LON = -96
! TES( 17 )%LON = -96.44
! TES( 18 )%LON = -95.84
! TES( 19 )%LON = -96.3
! TES( 20 )%LON = -96.76
! TES( 21 )%LON = -97.2
! TES( 22 )%LON = -97.71
! TES( 23 )%LON = -98.21
! TES( 24 )%LON = -98.71
! TES( 25 )%LON = -99.27
! TES( 26 )%LON = -99.83
! TES( 27 )%LON = -107.94
! TES( 28 )%LON = -108.51
! TES( 29 )%LON = -109.04
! TES( 30 )%LON = -109.53
! TES( 31 )%LON = -92.74
! TES( 32 )%LON = -93.2
! TES( 33 )%LON = -93.66
! TES( 34 )%LON = -94.11
! TES( 35 )%LON = -95.11
! TES( 36 )%LON = -95.61
! TES( 37 )%LON = -96.17
! TES( 38 )%LON = -96.73
! TES( 39 )%LON = -104.84
! TES( 40 )%LON = -105.41
! TES( 41 )%LON = -105.94
! TES( 42 )%LON = -106.43
! TES( 43 )%LON = -106.94
! TES( 44 )%LON = -107.42
! TES( 45 )%LON = -107.86
! TES( 46 )%LON = -108.33
! TES( 47 )%LON = -108.76
! TES( 48 )%LON = -90.1
! TES( 49 )%LON = -90.56
! TES( 50 )%LON = -91.01
! TES( 51 )%LON = -91.51
! TES( 52 )%LON = -92.01
! TES( 53 )%LON = -92.51
! TES( 54 )%LON = -93.07
! TES( 55 )%LON = -93.64
! TES( 56 )%LON = -101.74
! TES( 57 )%LON = -102.32
! TES( 58 )%LON = -102.84
! TES( 59 )%LON = -103.33
! TES( 60 )%LON = -103.84
! TES( 61 )%LON = -104.32
! TES( 62 )%LON = -104.76
! TES( 63 )%LON = -105.23
! TES( 64 )%LON = -105.67
! TES( 65 )%LON = -90.54
! TES( 66 )%LON = -98.64
! TES( 67 )%LON = -99.22
! TES( 68 )%LON = -99.75
! TES( 69 )%LON = -100.23
! TES( 70 )%LON = -100.75
! TES( 71 )%LON = -101.22
! TES( 72 )%LON = -101.67
! TES( 73 )%LON = -102.13
! TES( 74 )%LON = -102.57
! TES( 75 )%LON = -108.19
! TES( 76 )%LON = -108.65
! TES( 77 )%LON = -109.11
! TES( 78 )%LON = -109.55
! TES( 79 )%LON = -95.57
! TES( 80 )%LON = -96.14
! TES( 81 )%LON = -96.67
! TES( 82 )%LON = -97.16
! TES( 83 )%LON = -97.67
! TES( 84 )%LON = -98.15
! TES( 85 )%LON = -98.59
! TES( 86 )%LON = -99.06
! TES( 87 )%LON = -99.49
!
! TES( 1 )%FILENAME = TRIM('retv_vars.02945_0457_002.cdf')
! TES( 2 )%FILENAME = TRIM('retv_vars.02945_0457_003.cdf')
! TES( 3 )%FILENAME = TRIM('retv_vars.02945_0457_004.cdf')
! TES( 4 )%FILENAME = TRIM('retv_vars.02945_0458_002.cdf')
! TES( 5 )%FILENAME = TRIM('retv_vars.02945_0458_003.cdf')
! TES( 6 )%FILENAME = TRIM('retv_vars.02945_0459_002.cdf')
! TES( 7 )%FILENAME = TRIM('retv_vars.02945_0459_003.cdf')
! TES( 8 )%FILENAME = TRIM('retv_vars.02945_0459_004.cdf')
! TES( 9 )%FILENAME = TRIM('retv_vars.02945_0982_002.cdf')
! TES( 10 )%FILENAME = TRIM('retv_vars.02945_0982_003.cdf')
! TES( 11 )%FILENAME = TRIM('retv_vars.02945_0982_004.cdf')
! TES( 12 )%FILENAME = TRIM('retv_vars.02945_0983_002.cdf')
! TES( 13 )%FILENAME = TRIM('retv_vars.02945_0983_003.cdf')
! TES( 14 )%FILENAME = TRIM('retv_vars.02945_0983_004.cdf')
! TES( 15 )%FILENAME = TRIM('retv_vars.02945_0984_002.cdf')
! TES( 16 )%FILENAME = TRIM('retv_vars.02945_0984_003.cdf')
! TES( 17 )%FILENAME = TRIM('retv_vars.02945_0984_004.cdf')
! TES( 18 )%FILENAME = TRIM('retv_vars.02956_0457_002.cdf')
! TES( 19 )%FILENAME = TRIM('retv_vars.02956_0457_003.cdf')
! TES( 20 )%FILENAME = TRIM('retv_vars.02956_0457_004.cdf')
! TES( 21 )%FILENAME = TRIM('retv_vars.02956_0458_002.cdf')
! TES( 22 )%FILENAME = TRIM('retv_vars.02956_0458_003.cdf')
! TES( 23 )%FILENAME = TRIM('retv_vars.02956_0458_004.cdf')
! TES( 24 )%FILENAME = TRIM('retv_vars.02956_0459_002.cdf')
! TES( 25 )%FILENAME = TRIM('retv_vars.02956_0459_003.cdf')
! TES( 26 )%FILENAME = TRIM('retv_vars.02956_0459_004.cdf')
! TES( 27 )%FILENAME = TRIM('retv_vars.02956_1054_002.cdf')
! TES( 28 )%FILENAME = TRIM('retv_vars.02956_1054_003.cdf')
! TES( 29 )%FILENAME = TRIM('retv_vars.02956_1054_004.cdf')
! TES( 30 )%FILENAME = TRIM('retv_vars.02956_1055_002.cdf')
! TES( 31 )%FILENAME = TRIM('retv_vars.02960_0457_002.cdf')
! TES( 32 )%FILENAME = TRIM('retv_vars.02960_0457_003.cdf')
! TES( 33 )%FILENAME = TRIM('retv_vars.02960_0457_004.cdf')
! TES( 34 )%FILENAME = TRIM('retv_vars.02960_0458_002.cdf')
! TES( 35 )%FILENAME = TRIM('retv_vars.02960_0458_004.cdf')
! TES( 36 )%FILENAME = TRIM('retv_vars.02960_0459_002.cdf')
! TES( 37 )%FILENAME = TRIM('retv_vars.02960_0459_003.cdf')
! TES( 38 )%FILENAME = TRIM('retv_vars.02960_0459_004.cdf')
! TES( 39 )%FILENAME = TRIM('retv_vars.02960_1054_002.cdf')
! TES( 40 )%FILENAME = TRIM('retv_vars.02960_1054_003.cdf')
! TES( 41 )%FILENAME = TRIM('retv_vars.02960_1054_004.cdf')
! TES( 42 )%FILENAME = TRIM('retv_vars.02960_1055_002.cdf')
! TES( 43 )%FILENAME = TRIM('retv_vars.02960_1055_003.cdf')
! TES( 44 )%FILENAME = TRIM('retv_vars.02960_1055_004.cdf')
! TES( 45 )%FILENAME = TRIM('retv_vars.02960_1056_002.cdf')
! TES( 46 )%FILENAME = TRIM('retv_vars.02960_1056_003.cdf')
! TES( 47 )%FILENAME = TRIM('retv_vars.02960_1056_004.cdf')
! TES( 48 )%FILENAME = TRIM('retv_vars.02963_0457_003.cdf')
! TES( 49 )%FILENAME = TRIM('retv_vars.02963_0457_004.cdf')
! TES( 50 )%FILENAME = TRIM('retv_vars.02963_0458_002.cdf')
! TES( 51 )%FILENAME = TRIM('retv_vars.02963_0458_003.cdf')
! TES( 52 )%FILENAME = TRIM('retv_vars.02963_0458_004.cdf')
! TES( 53 )%FILENAME = TRIM('retv_vars.02963_0459_002.cdf')
! TES( 54 )%FILENAME = TRIM('retv_vars.02963_0459_003.cdf')
! TES( 55 )%FILENAME = TRIM('retv_vars.02963_0459_004.cdf')
! TES( 56 )%FILENAME = TRIM('retv_vars.02963_1054_002.cdf')
! TES( 57 )%FILENAME = TRIM('retv_vars.02963_1054_003.cdf')
! TES( 58 )%FILENAME = TRIM('retv_vars.02963_1054_004.cdf')
! TES( 59 )%FILENAME = TRIM('retv_vars.02963_1055_002.cdf')
! TES( 60 )%FILENAME = TRIM('retv_vars.02963_1055_003.cdf')
! TES( 61 )%FILENAME = TRIM('retv_vars.02963_1055_004.cdf')
! TES( 62 )%FILENAME = TRIM('retv_vars.02963_1056_002.cdf')
! TES( 63 )%FILENAME = TRIM('retv_vars.02963_1056_003.cdf')
! TES( 64 )%FILENAME = TRIM('retv_vars.02963_1056_004.cdf')
! TES( 65 )%FILENAME = TRIM('retv_vars.02967_0459_004.cdf')
! TES( 66 )%FILENAME = TRIM('retv_vars.02967_1054_002.cdf')
! TES( 67 )%FILENAME = TRIM('retv_vars.02967_1054_003.cdf')
! TES( 68 )%FILENAME = TRIM('retv_vars.02967_1054_004.cdf')
! TES( 69 )%FILENAME = TRIM('retv_vars.02967_1055_002.cdf')
! TES( 70 )%FILENAME = TRIM('retv_vars.02967_1055_003.cdf')
! TES( 71 )%FILENAME = TRIM('retv_vars.02967_1055_004.cdf')
! TES( 72 )%FILENAME = TRIM('retv_vars.02967_1056_002.cdf')
! TES( 73 )%FILENAME = TRIM('retv_vars.02967_1056_003.cdf')
! TES( 74 )%FILENAME = TRIM('retv_vars.02967_1056_004.cdf')
! TES( 75 )%FILENAME = TRIM('retv_vars.02971_0457_002.cdf')
! TES( 76 )%FILENAME = TRIM('retv_vars.02971_0457_003.cdf')
! TES( 77 )%FILENAME = TRIM('retv_vars.02971_0457_004.cdf')
! TES( 78 )%FILENAME = TRIM('retv_vars.02971_0458_002.cdf')
! TES( 79 )%FILENAME = TRIM('retv_vars.02971_0982_002.cdf')
! TES( 80 )%FILENAME = TRIM('retv_vars.02971_0982_003.cdf')
! TES( 81 )%FILENAME = TRIM('retv_vars.02971_0982_004.cdf')
! TES( 82 )%FILENAME = TRIM('retv_vars.02971_0983_002.cdf')
! TES( 83 )%FILENAME = TRIM('retv_vars.02971_0983_003.cdf')
! TES( 84 )%FILENAME = TRIM('retv_vars.02971_0983_004.cdf')
! TES( 85 )%FILENAME = TRIM('retv_vars.02971_0984_002.cdf')
! TES( 86 )%FILENAME = TRIM('retv_vars.02971_0984_003.cdf')
! TES( 87 )%FILENAME = TRIM('retv_vars.02971_0984_004.cdf')
!
! ! Return to calling program
! END SUBROUTINE INIT_TES_CH4
!!------------------------------------------------------------------------------
!
! SUBROUTINE CLEANUP_TES_CH4
!!
!!*****************************************************************************
!! Subroutine CLEANUP_TES_CH4 deallocates all module arrays. (dkh, 02/15/09)
!!
!! NOTES:
!!
!!******************************************************************************
!!
!
! IF ( ALLOCATED( CH4_SAVE ) ) DEALLOCATE( CH4_SAVE )
!
!
! ! Return to calling program
! END SUBROUTINE CLEANUP_TES_CH4
!!------------------------------------------------------------------------------
!------------------------------------------------------------------------------
! SUBROUTINE GET_GC_PSEUDO_OBS( NTES )
!!
!!******************************************************************************
!! Subroutine GET_GC_PSEUDO_OBS replaces TES observatins in TES%CH4 with
!! pseudo-observations from a GEOS-Chem run with scaling factors = 1.
!! The GEOS-Chem profile is mapped to the TES pressure grid and processed with
!! the TES averaging kernel before being saved in TES%CH4
!!
!! Arguments as Input:
!! ============================================================================
!! (1 ) LGC_TOP (TYPE) : Description [unit]
!! (2 ) GC_PRES (TYPE) : Description [unit]
!! (3 ) GC_SURFP(TYPE) : Description [unit]
!! (4 ) LTM_TOP (TYPE) : Description [unit]
!! (5 ) TM_PRES (TYPE) : Description [unit]
!! (6 ) TM_SURFP(TYPE) : Description [unit]
!!
!! Arguments as Output:
!! ============================================================================
!! (1 ) HINTERPZ (TYPE) : Description [unit]
!!
!! NOTES:
!! (1 ) Based on the GET_HINTERPZ_2 routine I wrote for read_sciano2_mod.
!!
!!******************************************************************************
!!
! ! Reference to f90 modules
! USE ERROR_MOD, ONLY : ERROR_STOP
! USE TIME_MOD, ONLY : GET_NYMD, GET_TIME_BEHIND_ADJ
! USE PRESSURE_MOD, ONLY : GET_BP
!
! ! Arguments
! INTEGER :: NTES
!
! ! Local variables
! INTEGER :: YYYYMMDD
! CHARACTER(LEN=255) :: ROOT_FILENAME
! CHARACTER(LEN=255) :: READ_FILENAME
!
! !=================================================================
! ! GET_GC_PSEUDO_OBS begins here!
! !=================================================================
!
!
! ! Filename root
! res_str =
! ROOT_FILENAME = TRIM( '/home/kjw/GEOS-Chem/runs' //
! & '/ch4/TES/ND49_' // GET_RES_EXT() )
! READ_FILENAME = TRIM( 'tsYYYYMMDD.bpch' )
!
!
! ! Initialize tau_round_old
! tau_round_old = -1
!
! ! Loop over all observations
! DO NT = NTES, 1, -1
!
! ! Copy LTES to cleanup code
! LTES = TES(NT)%LTES(1)
!
! ! Get Tau value for this observation
! tau_this = GET_TAU() - 23 + 24d0 * TES(NT)%TIME(1)
!
! ! Round Tau value to nearest 3 hours to access ND49 files
! tau_round = 3*NINT( tau_this/3d0 )
!
! ! If the rounded tau value is different than previous rounded tau,
! ! we need to read new datablock from ND49 file
! IF tau_round /= tau_round_old THEN
!
! ! If observation occurs after 01:30:00 AM (UTC)
! IF ( tau_this >= GET_TAU()-23+1.5 ) THEN
! YYYYMMDD = GET_NYMD()
! ! If observation occurs in the early morning (UTC)
! ENDIF ELSE
! DATE = GET_TIME_BEHIND_ADJ( 60*24 )
! YYYYMMDD = DATE(1)
! ENDIF
!
! ! Expand date tokens in filename
! CALL EXPAND_DATE( READ_FILENAME, YYYYMMDD, 9999 )
!
! ! Get Filename of GEOS-Chem output to read
! READ_FILENAME = TRIM( ROOT_FILENAME ) // TRIM( READ_FILENAME )
!
! WRITE(6,*) ' - READ_GEOS-Chem_CH4_OBS: reading file: ',
! & READ_FILENAME
!
! ! Read data from BPCH file
! CALL READ_BPCH2( READ_FILENAME, 'IJ-AVG-$', 1,
! & tau_this, IGLOB, JGLOB,
! & LLPAR, ARRAY, QUIET=.TRUE.)
! CALL TRANSFER_3D( ARRAY(:,:,:), GC_CH4_NATIVE_3D(:,:,:) )
!
! ENDIF
!
! ! Get GC column [ppb] --> [v/v]
! GC_CH4_NATIVE(:) = GC_CH4_NATIVE_3D(II,JJ,:) / 1d9
!
!
! ! Get I,J indices of grid box corresponding to current TES scan
! IIJJ = GET_IJ( TES(NT)%LON(1), TES(NT)%LAT(1) )
! II = IIJJ(1)
! JJ = IIJJ(2)
!
!
! ! Map GEOS-Chem column to TES pressure grid
!
! ! Reset variables to be safe
! MAP(:,:) = 0d0
! GC_PRES(:) = 0d0
!
!
! ! Get GC pressure levels (mbar)
! DO L = 1, LLPAR
! GC_PRES(L) = GET_PCENTER(I,J,L)
! ENDDO
!
! ! Get GC surface pressure (mbar)
! GC_PSURF = GET_PEDGE(I,J,1)
!
! ! Calculate the interpolation weight matrix
! MAP(1:LLPAR,1:LTES)
! & = GET_INTMAP( LLPAR, GC_PRES(:), GC_PSURF,
! & LTES, TES(NT)%PRES(1:LTES), GC_PSURF )
!
! ! Interpolate GC O3 column to TES grid
! DO LL = 1, LTES
! GC_CH4_onTES(LL) = 0d0
! DO L = 1, LLPAR
! GC_CH4_onTES(LL) = GC_CH4_onTES(LL)
! & + MAP(L,LL) * GC_CH4_NATIVE(L)
! ENDDO
! ENDDO
!
! !--------------------------------------------------------------
! ! Apply TES observation operator
! !
! ! x_hat = x_a + A_k ( x_m - x_a )
! !
! ! where
! ! x_hat = GC modeled column as seen by TES [lnvmr]
! ! x_a = TES apriori column [lnvmr]
! ! x_m = GC modeled column [lnvmr]
! ! A_k = TES averaging kernel
! !--------------------------------------------------------------
!
! ! x_m - x_a
! DO L = 1, LTES
! GC_CH4_onTES(L) = MAX(GC_CH4_onTES(L), 1d-10)
! CH4_PERT_onTES(L) = LOG(GC_CH4_onTES(L)) -
! & LOG(TES(NT)%PRIOR(L))
! ENDDO
!
! ! x_a + A_k * ( x_m - x_a )
! DO L = 1, LTES
! CH4_HAT_onTES(L) = 0d0
! DO LL = 1, LTES
! CH4_HAT_onTES(L) = CH4_HAT_onTES(L)
! & + TES(NT)%AVG_KERNEL(L,LL) * CH4_PERT_onTES(LL)
! ENDDO
! CH4_HAT_onTES(L) = CH4_HAT_onTES(L)
! & + LOG(TES(NT)%PRIOR(L))
! ENDDO
!
!
! ! Replace stratospheric values with real TES observations
! ! to prevent adjoint forcing of stratosphere
! DO L = 1, LTES
! IF TES(NT)%PRES(L) < TROPP(II,JJ) THEN
! CH4_HAT_onTES(L) = LOG( TES(NT)%CH4(L) )
! ENDIF
! ENDDO
!
!
! ! Place GEOS-Chem column into the TES_CH4 structure
! TES(NT)%GC_CH4(:) = CH4_HAT_onTES(:)
!
!
! ENDDO ! End looping over each observation
!
!
!
! END SUBROUTINE GET_GC_PSEUDO_OBS
!
!!--------------------------------------------------------------------------
! SUBROUTINE SVD(A,N,U,S,VT)
!!
!!******************************************************************************
!! Subroutine SVD is a driver for the LAPACK SVD routine DGESVD. (dkh, 05/04/10)
!!
!!
!! Arguments as Input:
!! ============================================================================
!! (1 ) A (REAL*8) : N x N matrix to decompose
!! (2 ) N (INTEGER) : N is dimension of A
!!
!! Arguments as Output:
!! ============================================================================
!! (1 ) U (REAL*8) : Array of left singular vectors
!! (2 ) S (REAL*8) : Vector of singular values
!! (3 ) VT (REAL*8) : Array of right singular vectors, TRANSPOSED
!!
!!
!! NOTES:
!!
!! Copyright (C) 2009-2010 Intel Corporation. All Rights Reserved.
!! The information and material ("Material") provided below is owned by Intel
!! Corporation or its suppliers or licensors, and title to such Material remains
!! with Intel Corporation or its suppliers or licensors. The Material contains
!! proprietary information of Intel or its suppliers and licensors. The Material
!! is protected by worldwide copyright laws and treaty provisions. No part of
!! the Material may be copied, reproduced, published, uploaded, posted,
!! transmitted, or distributed in any way without Intel's prior express written
!! permission. No license under any patent, copyright or other intellectual
!! property rights in the Material is granted to or conferred upon you, either
!! expressly, by implication, inducement, estoppel or otherwise. Any license
!! under such intellectual property rights must be express and approved by Intel
!! in writing.
!! =============================================================================
!!
!! DGESVD Example.
!! ==============
!!
!! Program computes the singular value decomposition of a general
!! rectangular matrix A:
!!
!! 8.79 9.93 9.83 5.45 3.16
!! 6.11 6.91 5.04 -0.27 7.98
!! -9.15 -7.93 4.86 4.85 3.01
!! 9.57 1.64 8.83 0.74 5.80
!! -3.49 4.02 9.80 10.00 4.27
!! 9.84 0.15 -8.99 -6.02 -5.31
!!
!! Description.
!! ============
!!
!! The routine computes the singular value decomposition (SVD) of a real
!! m-by-n matrix A, optionally computing the left and/or right singular
!! vectors. The SVD is written as
!!
!! A = U*SIGMA*VT
!!
!! where SIGMA is an m-by-n matrix which is zero except for its min(m,n)
!! diagonal elements, U is an m-by-m orthogonal matrix and VT (V transposed)
!! is an n-by-n orthogonal matrix. The diagonal elements of SIGMA
!! are the singular values of A; they are real and non-negative, and are
!! returned in descending order. The first min(m, n) columns of U and V are
!! the left and right singular vectors of A.
!!
!! Note that the routine returns VT, not V.
!!
!! Example Program Results.
!! ========================
!!
!! DGESVD Example Program Results
!!
!! Singular values
!! 27.47 22.64 8.56 5.99 2.01
!!
!! Left singular vectors (stored columnwise)
!! -0.59 0.26 0.36 0.31 0.23
!! -0.40 0.24 -0.22 -0.75 -0.36
!! -0.03 -0.60 -0.45 0.23 -0.31
!! -0.43 0.24 -0.69 0.33 0.16
!! -0.47 -0.35 0.39 0.16 -0.52
!! 0.29 0.58 -0.02 0.38 -0.65
!!
!! Right singular vectors (stored rowwise)
!! -0.25 -0.40 -0.69 -0.37 -0.41
!! 0.81 0.36 -0.25 -0.37 -0.10
!! -0.26 0.70 -0.22 0.39 -0.49
!! 0.40 -0.45 0.25 0.43 -0.62
!! -0.22 0.14 0.59 -0.63 -0.44
!! =============================================================================
!!******************************************************************************
!!
! ! Arguements
! INTEGER,INTENT(IN) :: N
! REAL*8, INTENT(IN) :: A(N,N)
! REAL*8, INTENT(OUT) :: U(N,N)
! REAL*8, INTENT(OUT) :: S(N)
! REAL*8, INTENT(OUT) :: VT(N,N)
!
! ! Local variables
! INTEGER, PARAMETER :: LWMAX = MAXLEV * 35
! INTEGER :: INFO, LWORK
! DOUBLE PRECISION :: WORK( LWMAX )
!
!! .. External Subroutines ..
! EXTERNAL :: DGESVD
!
!! .. Intrinsic Functions ..
! INTRINSIC :: INT, MIN
!
! !=================================================================
! ! SVD begins here!
! !=================================================================
!
!! .. Executable Statements ..
! !WRITE(*,*)'DGESVD Example Program Results'
!!
!! Query the optimal workspace.
!!
! LWORK = -1
! CALL DGESVD( 'All', 'All', N, N, A, N, S, U, N, VT, N,
! $ WORK, LWORK, INFO )
! LWORK = MIN( LWMAX, INT( WORK( 1 ) ) )
!!
!! Compute SVD.
!!
! CALL DGESVD( 'All', 'All', N, N, A, N, S, U, N, VT, N,
! $ WORK, LWORK, INFO )
!!
!! Check for convergence.
!!
! IF( INFO.GT.0 ) THEN
! WRITE(*,*)'The algorithm computing SVD failed to converge.'
! STOP
! END IF
!
!! Uncomment the following to print out singlular values, vectors (dkh, 05/04/10)
!!!
!!! Print singular values.
!!!
!! CALL PRINT_MATRIX( 'Singular values', 1, N, S, 1 )
!!!
!!! Print left singular vectors.
!!!
!! CALL PRINT_MATRIX( 'Left singular vectors (stored columnwise)',
!! $ N, N, U, N )
!!!
!!! Print right singular vectors.
!!!
!! CALL PRINT_MATRIX( 'Right singular vectors (stored rowwise)',
!! $ N, N, VT, N )
!
! ! Return to calling program
! END SUBROUTINE SVD
!!------------------------------------------------------------------------------
! SUBROUTINE DGESVD_EXAMPLE
!
!! .. Parameters ..
! INTEGER M, N
! PARAMETER ( M = 6, N = 5 )
! INTEGER LDA, LDU, LDVT
! PARAMETER ( LDA = M, LDU = M, LDVT = N )
! INTEGER LWMAX
! PARAMETER ( LWMAX = 1000 )
!!
!! .. Local Scalars ..
! INTEGER INFO, LWORK
!!
!! .. Local Arrays ..
! DOUBLE PRECISION A( LDA, N ), U( LDU, M ), VT( LDVT, N ), S( N ),
! $ WORK( LWMAX )
! DATA A/
! $ 8.79, 6.11,-9.15, 9.57,-3.49, 9.84,
! $ 9.93, 6.91,-7.93, 1.64, 4.02, 0.15,
! $ 9.83, 5.04, 4.86, 8.83, 9.80,-8.99,
! $ 5.45,-0.27, 4.85, 0.74,10.00,-6.02,
! $ 3.16, 7.98, 3.01, 5.80, 4.27,-5.31
! $ /
!!
!! .. External Subroutines ..
! EXTERNAL DGESVD
! !EXTERNAL PRINT_MATRIX
!!
!! .. Intrinsic Functions ..
! INTRINSIC INT, MIN
!!
!! .. Executable Statements ..
! WRITE(*,*)'DGESVD Example Program Results'
!!
!! Query the optimal workspace.
!!
! LWORK = -1
! CALL DGESVD( 'All', 'All', M, N, A, LDA, S, U, LDU, VT, LDVT,
! $ WORK, LWORK, INFO )
! LWORK = MIN( LWMAX, INT( WORK( 1 ) ) )
!!
!! Compute SVD.
!!
! CALL DGESVD( 'All', 'All', M, N, A, LDA, S, U, LDU, VT, LDVT,
! $ WORK, LWORK, INFO )
!!
!! Check for convergence.
!!
! IF( INFO.GT.0 ) THEN
! WRITE(*,*)'The algorithm computing SVD failed to converge.'
! STOP
! END IF
!!
!! Print singular values.
!!
!! CALL PRINT_MATRIX( 'Singular values', 1, N, S, 1 )
!!
!! Print left singular vectors.
!!
!! CALL PRINT_MATRIX( 'Left singular vectors (stored columnwise)',
!! $ M, N, U, LDU )
!!
!! Print right singular vectors.
!!
!! CALL PRINT_MATRIX( 'Right singular vectors (stored rowwise)',
!! $ N, N, VT, LDVT )
!!
!!
!! End of DGESVD Example.
! END SUBROUTINE DGESVD_EXAMPLE
!------------------------------------------------------------------------------
!
! Auxiliary routine: printing a matrix.
!
! SUBROUTINE PRINT_MATRIX( DESC, M, N, A, LDA )
! CHARACTER*(*) DESC
! INTEGER M, N, LDA
! DOUBLE PRECISION A( LDA, * )
!
! INTEGER I, J
!
! WRITE(*,*)
! WRITE(*,*) DESC
! DO I = 1, M
! WRITE(*,9998) ( A( I, J ), J = 1, N )
! END DO
!
! Change format of output (dkh, 05/04/10)
! 9998 FORMAT( 11(:,1X,F6.2) )
! 9998 FORMAT( 11(:,1X,E14.8) )
! RETURN
!
! END SUBROUTINE PRINT_MATRIX
!------------------------------------------------------------------------------
END MODULE TES_CH4_MOD