!$Id: geocape_ch4_mod.f,v 1.1 2012/03/01 22:00:27 daven Exp $ MODULE GEOCAPE_CH4_MOD ! !****************************************************************************** ! Module GEOCAPE_CH4_MOD for GEO-CAPE CH4 observations. ! By kjw, added adj32_023 (dkh, 02/12/12) ! !****************************************************************************** ! IMPLICIT NONE !================================================================= ! MODULE VARIABLES !================================================================= ! Parameters INTEGER, PARAMETER :: LLGEOCAPE = 13 ! 卫星观测的垂直层数 INTEGER, PARAMETER :: MAXGEOCAPE = 639059 ! Record to store information about the new instrument REAL*8 :: AVGKERNEL( LLGEOCAPE, LLGEOCAPE ) REAL*8 :: OBSERROR( LLGEOCAPE, LLGEOCAPE ) REAL*8 :: OBSERROR_INV( LLGEOCAPE, LLGEOCAPE ) REAL*8 :: TOTERROR_INV( LLGEOCAPE, LLGEOCAPE ) REAL*8 :: PRESSURE( LLGEOCAPE ) REAL*8 :: PRESSURE_EDGE( LLGEOCAPE ) REAL*8 :: RANDNUM( MAXGEOCAPE ) ! 保存了随机数的数组 CONTAINS !------------------------------------------------------------------------------ SUBROUTINE READ_GEOCAPE_INFO ! !****************************************************************************** ! Subroutine READ_GEOCAPE_INFO reads and stores information about the new ! instrument, specifically AK, pressure levels and error covariance matrices. ! (kjw, 07/24/11) ! 用于读取卫星观测的数据,保存 AK、气压以及误差协方差矩阵(没有观测值么),应该有观测值 ! Arguments as Input: ! ============================================================================ ! (1 ) FILENAME (CHAR) : GEOCAPE filename to read ! ! ! NOTES: ! (1 ) !****************************************************************************** ! ! Reference to f90 modules USE FILE_MOD, ONLY : IOERROR USE TIME_MOD, ONLY : GET_NYMD ! Arguments CHARACTER(LEN=255) :: FILENAME ! Local variables CHARACTER(LEN=255) :: READ_FILENAME ! netCDF id's INTEGER :: NCID, LG, LN INTEGER :: nobs_id, yyyymmdd_id, hhmmss_id INTEGER :: qflag_id, xch4_id, ch4ak_id INTEGER :: ch4pres_id, ch4prior_id INTEGER :: gcii_id, gcjj_id, gcfrac_id ! Loop indexes, and error handling. INTEGER :: IOS, IU_IN !================================================================= ! READ_GEOCAPE_CH4_OBS begins here! !================================================================= ! Initialize module variabl AVGKERNEL(:,:) = 0d0 OBSERROR(:,:) = 0d0 OBSERROR_INV(:,:) = 0d0 TOTERROR_INV(:,:) = 0d0 PRESSURE(:) = 0d0 PRESSURE_EDGE(:) = 0d0 RANDNUM(:) = 0d0 ! Read and store one variable at a time ! ------ Averaging Kernel Matrix ------ 读取平均核函数,并保存在模块变量中 ! Filename to read READ_FILENAME = TRIM( '/home/kjw/new_satellites/geocape/' ) // & 'data/' // TRIM( 'geocape_AK.txt' ) WRITE(6,*) ' - READ_GEOCAPE_AK: reading file: ', & TRIM(READ_FILENAME) ! Open file OPEN( IU_IN, FILE=TRIM( READ_FILENAME ), & STATUS='OLD', IOSTAT=IOS ) IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_IN, 'read_avg_kernel:1' ) ! Read File and save info into module variable AVGKERNEL(:,:) DO LN=1,LLGEOCAPE READ( IU_IN, '(13F12.6)', IOSTAT=IOS ) AVGKERNEL(LN,:) ! IO status IF ( IOS < 0 ) THEN WRITE( 6, '(a)' ) 'Unexpected end of file encountered!' WRITE( 6, '(a)' ) 'STOP in READ_GEOCAPE_CH4' ENDIF IF ( IOS > 0 ) THEN CALL IOERROR(IOS, IU_IN, 'read_avg_kernel:2') ENDIF ENDDO ! Close file CLOSE( IU_IN ) ! ------ Observation Error Covariance Matrix ------ 读取误差协方差矩阵 ! Filename to read READ_FILENAME = TRIM( '/home/kjw/new_satellites/geocape/' ) // & 'data/' // TRIM( 'geocape_obs_error.txt' ) WRITE(6,*) ' - READ_GEOCAPE_OBSERROR: reading file: ', & TRIM(READ_FILENAME) ! Open file OPEN( IU_IN, FILE=TRIM( READ_FILENAME ), & STATUS='OLD', IOSTAT=IOS ) IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_IN, 'read_obs_error:1' ) ! Read File and save info into module variable OBSERROR(:,:) 读取观测误差 DO LN=1,LLGEOCAPE READ( IU_IN, '(13F18.12)', IOSTAT=IOS ) OBSERROR(LN,:) ! IO status IF ( IOS < 0 ) THEN WRITE( 6, '(a)' ) 'Unexpected end of file encountered!' WRITE( 6, '(a)' ) 'STOP in READ_GEOCAPE_CH4' ENDIF IF ( IOS > 0 ) THEN CALL IOERROR(IOS, IU_IN, 'read_obs_error:2') ENDIF ENDDO ! Close file CLOSE( IU_IN ) ! ------ Inverse of Observation Error Covariance Matrix ------ 对观测误差协方差矩阵求逆 ! Filename to read READ_FILENAME = TRIM( '/home/kjw/new_satellites/geocape/' ) // & 'data/' // TRIM( 'geocape_obs_error_inv.txt' ) WRITE(6,*) ' - READ_GEOCAPE_OBSERROR_INV: reading file: ', & TRIM(READ_FILENAME) ! Open file OPEN( IU_IN, FILE=TRIM( READ_FILENAME ), & STATUS='OLD', IOSTAT=IOS ) IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_IN, 'read_obs_error:1' ) ! Read File and save info into module variable OBSERROR_INV(:,:) DO LN=1,LLGEOCAPE READ( IU_IN, '(13F18.6)', IOSTAT=IOS ) OBSERROR_INV(LN,:) ! IO status IF ( IOS < 0 ) THEN WRITE( 6, '(a)' ) 'Unexpected end of file encountered!' WRITE( 6, '(a)' ) 'STOP in READ_GEOCAPE_CH4' ENDIF IF ( IOS > 0 ) THEN CALL IOERROR(IOS, IU_IN, 'read_obs_error:2') ENDIF ENDDO ! Close file CLOSE( IU_IN ) ! ! ------ Total Error Covariance Matrix ------ ! ! Filename to read ! READ_FILENAME = TRIM( '/home/kjw/new_satellites/geocape/' ) // ! & 'data/' // TRIM( 'geocape_total_error_inv.txt' ) ! WRITE(6,*) ' - READ_GEOCAPE_TOTERROR: reading file: ', ! & TRIM(READ_FILENAME) ! ! ! ! Open file ! OPEN( IU_IN, FILE=TRIM( READ_FILENAME ), ! & STATUS='OLD', IOSTAT=IOS ) ! IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_IN, 'read_tot_error:1' ) ! ! ! Read File and save info into module variable OBSERROR(:,:) ! DO LN=1,LLGEOCAPE ! READ( IU_IN, '(13F18.12)', IOSTAT=IOS ) TOTERROR_INV(LN,:) ! ! ! IO status ! IF ( IOS < 0 ) THEN ! WRITE( 6, '(a)' ) 'Unexpected end of file encountered!' ! WRITE( 6, '(a)' ) 'STOP in READ_GEOCAPE_CH4' ! ENDIF ! IF ( IOS > 0 ) THEN ! CALL IOERROR(IOS, IU_IN, 'read_tot_error:2') ! ENDIF ! ENDDO ! ! ! Close file ! CLOSE( IU_IN ) ! ------ Pressure Levels ------ 读取气压数据 ! Filename to read READ_FILENAME = TRIM( '/home/kjw/new_satellites/geocape/' ) // & 'data/' // TRIM( 'geocape_pressure.txt' ) WRITE(6,*) ' - READ_GEOCAPE_PRESSURE: reading file: ', & TRIM(READ_FILENAME) ! Open file OPEN( IU_IN, FILE=TRIM( READ_FILENAME ), & STATUS='OLD', IOSTAT=IOS ) IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_IN, 'read_pressure:1' ) ! Read File and save info into module variable PRESSURE(:) READ( IU_IN, '(13F12.6)', IOSTAT=IOS ) PRESSURE(:) ! IO status IF ( IOS < 0 ) THEN WRITE( 6, '(a)' ) 'Unexpected end of file encountered!' WRITE( 6, '(a)' ) 'STOP in READ_GEOCAPE_CH4' ENDIF IF ( IOS > 0 ) THEN CALL IOERROR(IOS, IU_IN, 'read_pressure:2') ENDIF ! Close file CLOSE( IU_IN ) ! ------ Pressure Edges ------ 获取气压的边界值 ! By finite difference on log(pressure) grid PRESSURE_EDGE(1) = PRESSURE(1) PRESSURE_EDGE(LLGEOCAPE) = 0. DO LN=2,LLGEOCAPE-1 PRESSURE_EDGE(LN) = exp( log(pressure(LN+1)) + & ( log(PRESSURE(LN)) - log(PRESSURE(LN+1)) ) / 2. ) ENDDO ! Return to calling program 还真没有直接读取观测值 END SUBROUTINE READ_GEOCAPE_INFO !------------------------------------------------------------------------------ SUBROUTINE CALC_GEOCAPE_CH4_FORCE( COST_FUNC ) ! !****************************************************************************** ! Subroutine CALC_GEOCAPE_CH4_FORCE calculates the adjoint forcing from the GEOCAPE ! CH4 observations and updates the cost function. (kjw, 07/20/11) ! 该程序计算伴随强迫,并且更新代价函数 ! ! Arguments as Input/Output: ! ============================================================================ ! (1 ) COST_FUNC (REAL*8) : Cost funciton [unitless] ! ! ! NOTES: ! (1 ) !****************************************************************************** ! ! Reference to f90 modules USE BPCH2_MOD, ONLY : GET_RES_EXT, GET_TAU0 USE BPCH2_MOD, ONLY : READ_BPCH2 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, CLDFRC USE DIRECTORY_ADJ_MOD, ONLY : DIAGADJ_DIR, ADJTMP_DIR USE GRID_MOD, ONLY : GET_YEDGE, GET_AREA_M2 USE PRESSURE_MOD, ONLY : GET_PCENTER, GET_PEDGE USE TIME_MOD, ONLY : GET_NYMD, GET_NHMS USE TIME_MOD, ONLY : GET_TAU USE TIME_MOD, ONLY : GET_LOCALTIME, EXPAND_DATE USE TRACER_MOD, ONLY : STT USE TRACER_MOD, ONLY : XNUMOL, XNUMOLAIR USE TRACER_MOD, ONLY : TCVV USE ERROR_MOD, ONLY : ERROR_STOP # include "CMN_SIZE" ! Size params ! Arguments REAL*8, INTENT(INOUT) :: COST_FUNC ! Local variables INTEGER, SAVE :: NT ! # observations processed this day INTEGER :: LG, LN, LLN, II, JJ, NB, JMIN, OB INTEGER :: nlev, lind, IU_IN INTEGER :: nboxes, nobs INTEGER :: NTSTART, NTSTOP, NTh INTEGER, SAVE :: NTT REAL*8 :: GC_CH4_TRUE_ARRAY(IIPAR,JJPAR,LLPAR) REAL*8 :: CH4_PRIOR(IIPAR,JJPAR,LLGEOCAPE) REAL*4 :: DUMMY_PRIOR(IIPAR,JJPAR,LLGEOCAPE) REAL*4 :: DUMMY_TRUE(IIPAR,JJPAR,LLPAR) REAL*8 :: GC_PCENTER(LLPAR) REAL*8 :: GC_PEDGE(LLPAR) REAL*8 :: GC_AD(LLPAR) REAL*8 :: GC_CH4_NATIVE(LLPAR) REAL*8 :: GC_CH4_NATIVE_OB(LLPAR) REAL*8 :: thispcen(LLPAR) REAL*8 :: thispedg(LLPAR) REAL*8 :: thisad(LLPAR) REAL*8 :: thisch4(LLPAR) REAL*8 :: GC_CH4_onGEOCAPE(LLGEOCAPE) REAL*8 :: GC_CH4_onGEOCAPE_OB(LLGEOCAPE) REAL*8 :: GRIDMAP(LLPAR,LLGEOCAPE) REAL*8 :: CH4_HAT(LLGEOCAPE) REAL*8 :: CH4_HAT_OB(LLGEOCAPE) REAL*8 :: CH4_HAT_ADJ(LLGEOCAPE) REAL*8 :: CH4_HAT_werr(LLGEOCAPE) REAL*8 :: CH4_HAT_werr_ADJ(LLGEOCAPE) REAL*8 :: CH4_PERT(LLGEOCAPE) REAL*8 :: CH4_PERT_OB(LLGEOCAPE) REAL*8 :: CH4_PERT_ADJ(LLGEOCAPE) REAL*8 :: frac, frac_total REAL*8 :: latmin, Jfrac_min, Jfrac REAL*8 :: box_area, cloud_frac REAL*8 :: mass_air, mole_air, mole_ch4 REAL*8 :: LHS, RHS, GC_XCH4, XTAU REAL*8 :: DIFF(LLGEOCAPE) REAL*8 :: FORCE(LLGEOCAPE) REAL*8 :: DIFF_ADJ(LLGEOCAPE) REAL*8 :: thisforce(LLPAR) REAL*8 :: GC_CH4_onGEOCAPE_ADJ(LLGEOCAPE) REAL*8 :: GC_CH4_NATIVE_ADJ(LLPAR) REAL*8 :: NEW_COST(MAXGEOCAPE) REAL*8 :: OLD_COST LOGICAL, SAVE :: FIRST = .TRUE. LOGICAL, SAVE :: DO_FDTEST = .TRUE. INTEGER :: IOS CHARACTER(LEN=255) :: FILENAME CHARACTER(LEN=255) :: FILENAME_OBS ! Variables for FD testing REAL*8 :: cost_func_pos, cost_func_neg REAL*8 :: cost_func_0 REAL*8 :: PERT(LLPAR) REAL*8 :: ADJ_SAVE(LLPAR) REAL*8 :: ADJ(LLPAR) REAL*8 :: FD_CEN(LLPAR) REAL*8 :: FD_POS(LLPAR) REAL*8 :: FD_NEG(LLPAR) REAL*8 :: DOFS !================================================================= ! CALC_GEOCAPE_CH4_FORCE begins here! !================================================================= NEW_COST(:) = 0d0 ! Open files for output 在第一次运行时会打开相关文件用于输出(?) IF ( FIRST ) THEN FILENAME = 'pres.NN.m' CALL EXPAND_NAME( FILENAME, N_CALC ) ! 替换文件名中的 NN 为迭代次数 FILENAME = TRIM( DIAGADJ_DIR ) // TRIM( FILENAME ) OPEN( 101, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', & IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) FILENAME = 'gc_nh3.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_nh3.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 = 'adj_nh3_pert.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 = 'adj_gc_nh3.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_nh3_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 = 'exp_nh3_hat_dbl.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' ) !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' ) ! Read CH4 data CALL READ_GEOCAPE_INFO ! Initialize counter for total number of observations processed NTT = 0 FIRST = .FALSE. ! only open files on first call to ENDIF ! ! 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' ) ! Save a value of the cost function first OLD_COST = COST_FUNC ! Read "TRUE" state for this time step [kg/box] GC_CH4_TRUE_ARRAY(:,:,:) = 0d0 FILENAME_OBS = '/home/kjw/GEOS-Chem/gcadj_std/runs/v8-02-01/' // & 'ch4/geocape/' // GET_RES_EXT() // '/adjtmp/' // & 'gctm.obs.YYYYMMDD.hhmm' CALL EXPAND_DATE( FILENAME_OBS, GET_NYMD(), GET_NHMS() ) !FILENAME_OBS = TRIM( ADJTMP_DIR ) // TRIM( FILENAME_OBS ) XTAU = GET_TAU() CALL READ_BPCH2( TRIM(FILENAME_OBS), 'IJ-OBS-$', 1, & XTAU, IIPAR, JJPAR, ! II, JJ, LL 都是维度大小而非坐标 & LLPAR, DUMMY_TRUE , QUIET=.TRUE.) ! DUMMY_TRUE 为读取的观测数据? GC_CH4_TRUE_ARRAY(:,:,:) = DUMMY_TRUE(:,:,:) ! Convert from [kg] --> [v/v] 单位转换 DO II=1,IIPAR DO JJ=1,JJPAR DO LG=1,LLPAR GC_CH4_TRUE_ARRAY(II,JJ,LG) = GC_CH4_TRUE_ARRAY(II,JJ,LG) & * ( 1d3 / 16d0 ) / ( AD(II,JJ,LG) * 1d3 / 28.96 ) ENDDO ENDDO ENDDO ! Read a priori vertical profiles from file FILENAME = '/home/kjw/new_satellites/geocape/data/' // & 'geocape_prior.' // GET_RES_EXT() // '.bpch' XTAU = GET_TAU0( 1, 1, 1985 ) CALL READ_BPCH2( FILENAME, 'IJ-AVG-$', 1, & XTAU, IIPAR, JJPAR, & LLGEOCAPE, DUMMY_PRIOR, QUIET=.TRUE. ) CH4_PRIOR(:,:,:) = DUMMY_PRIOR(:,:,:) ! 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, LLNT, IIJJ, I, J, L, LL ) !!$OMP+PRIVATE( GC_PRES, GC_PSURF, GC_CH4, DIFF ) !!$OMP+PRIVATE( GC_CH4_NATIVE, CH4_PERT, CH4_HAT, FORCE ) !!$OMP+PRIVATE( ADJ_GC_CH4_NATIVE, ADJ_GC_CH4 ) !!$OMP+PRIVATE( ADJ_CH4_PERT, ADJ_CH4_HAT ) !!$OMP+PRIVATE( ADJ_DIFF ) ! If new day of observations initialize count IF ( GET_NHMS() .EQ. 230000 ) THEN NT = 0 ! ------ Random Numbers ------ ! Open and read random number file. mean = 0, stddev = 1 FILENAME = '/home/kjw/new_satellites/geocape/data/' // & 'randnums/random.YYYYMMDD.txt' CALL EXPAND_DATE( FILENAME, GET_NYMD(), 0 ) OPEN( IU_IN, FILE=TRIM( FILENAME ), STATUS='UNKNOWN', & IOSTAT=IOS, FORM='FORMATTED', ACCESS='SEQUENTIAL' ) DO LG=1,MAXGEOCAPE READ(IU_IN,'(F13.6)') RANDNUM(LG) ENDDO CLOSE(IU_IN) ENDIF ! Begin counter for number of observations processed this hour NTh = 0 ! Information for spatial criteria for observations latmin = 40.0 ! Determine minimum JJ index over which to look for observations DO JJ=1, JJPAR-1 IF ( ( GET_YEDGE(JJ) .LE. latmin ) .AND. & ( GET_YEDGE(JJ+1) .GT. latmin ) ) THEN JMIN = JJ Jfrac_min = ( GET_YEDGE(JJ+1) - latmin ) / & ( GET_YEDGE(JJ+1) - GET_YEDGE(JJ) ) ENDIF ENDDO print*, ' - CALC_GEOCAPE_CH4_FORCE ', GET_NYMD(), GET_NHMS() ! Loop over each grid box north of the minimum latitude 对于每个网格进行循环 ! 1. Determine number of observations in the current grid box ! 2. Make obseravations DO II = 1, IIPAR ! If not 1400 <= local time < 1500, cycle to next II value IF ( ( GET_LOCALTIME( II ) .LT. 14.00 ) .OR. & ( GET_LOCALTIME( II ) .GE. 15.00 ) ) CYCLE ! It is 1400-1500 local time, so let's make observations! DO JJ = JMIN, JJPAR ! For safety, initilize these variables nobs = 0 cloud_frac = 0. box_area = 0. GC_PCENTER(:) = 0d0 GC_PEDGE(:) = 0d0 GC_AD(:) = 0d0 GC_CH4_NATIVE(:) = 0d0 GC_CH4_onGEOCAPE(:) = 0d0 GC_CH4_onGEOCAPE_OB(:) = 0d0 ! Fraction of grid box above minimum latitude Jfrac = 1. IF ( JJ .EQ. JMIN ) Jfrac = Jfrac_min ! Determine number of observations in this grid box ! # obs = box_area * (1-cloud_fraction) * Jfrac / 100 ! divide by 100 because each observation takes up 100 km2 box_area = GET_AREA_M2( JJ ) * 1d-6 ! [m2] --> [km2] cloud_frac = CLDFRC( II, JJ ) nobs = NINT( ( (1-cloud_frac) * box_area * Jfrac ) / 100. ) ! Get GEOS-Chem pressure and CH4 column corresponding to this grid box. ! CH4 in [kg/box] and pressure in [hPa] ! Get column of pressure centers and CH4 values DO LG=1,LLPAR ! 对垂直上进行循环,计算柱浓度 ! Pressure centers [hPa] GC_PCENTER(LG) = GET_PCENTER(II,JJ,LG) ! Pressure edges [hPa] GC_PEDGE(LG) = GET_PEDGE(II,JJ,LG) ! mass per box [kg] GC_AD(LG) = AD(II,JJ,LG) ! CH4 values [kg/box] --> [v/v] GC_CH4_NATIVE(LG) = ( CHK_STT(II,JJ,LG,1 ) & * XNUMOL(1) ) / ( GC_AD(LG) * XNUMOLAIR ) ENDDO ! Number of vertical levels to use in these observations ! Chop off lowermost levels if ! GEOS-Chem surface pressure < GEOCAPE pressure levels nlev = count( PRESSURE_EDGE .LT. GC_PEDGE(1) ) IF ( nlev .LT. 13 ) nlev = nlev + 1 lind = LLGEOCAPE + 1 - nlev ! minimum vertical index on GEOCAPE grid 最小气压层的索引 ! Get interpolation matrix that maps GEOS-Chem to GEOCAPE grid GRIDMAP(1:LLPAR, 1:LLGEOCAPE) = & GET_INTMAP( GC_PEDGE, PRESSURE_EDGE, nlev ) ! Get GEOS-Chem column from "truth" run to make pseudo-observations GC_CH4_NATIVE_OB(:) = 0d0 GC_CH4_NATIVE_OB(:) = GC_CH4_TRUE_ARRAY(II,JJ,:) ! Interpolate GEOS-Chem CH4 column and observation to GEOCAPE grid ! Column in [v/v] DO LN = lind, LLGEOCAPE GC_CH4_onGEOCAPE(LN) = 0d0 GC_CH4_onGEOCAPE_OB(LN) = 0d0 DO LG = 1, LLPAR GC_CH4_onGEOCAPE(LN) = GC_CH4_onGEOCAPE(LN) & + GRIDMAP(LG,LN) * GC_CH4_NATIVE(LG) GC_CH4_onGEOCAPE_OB(LN) = GC_CH4_onGEOCAPE_OB(LN) & + GRIDMAP(LG,LN) * GC_CH4_NATIVE_OB(LG) ENDDO ENDDO !-------------------------------------------------------------- ! Apply GEOCAPE observation operator ! ! x_hat = x_a + A_k ( x_m - x_a ) ! ! where ! x_hat = GC modeled column as seen by GEOCAPE [molec/cm2] ! x_a = GEOCAPE apriori column [molec/cm2] ! x_m = GC modeled column on GEOCAPE grid [molec/cm2] ! A = GEOCAPE averaging kernel !-------------------------------------------------------------- ! x_m - x_a for model and "observation" ! [v/v] --> ln( v/v ) happens here DO LN = lind, LLGEOCAPE GC_CH4_onGEOCAPE(LN) =MAX(GC_CH4_onGEOCAPE(LN), 1d-10) GC_CH4_onGEOCAPE_OB(LN)=MAX(GC_CH4_onGEOCAPE_OB(LN),1d-10) CH4_PERT(LN) =LOG( GC_CH4_onGEOCAPE(LN) ) - & LOG( CH4_PRIOR(II,JJ,LN) ) CH4_PERT_OB(LN) =LOG( GC_CH4_onGEOCAPE_OB(LN) ) - & LOG( CH4_PRIOR(II,JJ,LN) ) ENDDO ! x_a + A_k * ( x_m - x_a ) for model and "observation" DO LN = lind, LLGEOCAPE CH4_HAT(LN) = 0d0 CH4_HAT_OB(LN) = 0d0 DO LLN = lind, LLGEOCAPE CH4_HAT(LN) = CH4_HAT(LN) & + AVGKERNEL(LN,LLN) * CH4_PERT(LLN) CH4_HAT_OB(LN) = CH4_HAT_OB(LN) & + AVGKERNEL(LN,LLN) * CH4_PERT_OB(LLN) ENDDO CH4_HAT(LN) = CH4_HAT(LN) +LOG( CH4_PRIOR(II,JJ,LN) ) CH4_HAT_OB(LN)= CH4_HAT_OB(LN)+LOG( CH4_PRIOR(II,JJ,LN) ) ENDDO ! Loop over number of observations in this grid box DO OB=1,NOBS ! Increment number of observations NTh = NTh + 1 ! processed this hour NT = NT + 1 ! processed today NTT = NTT + 1 ! processed total !print*, ' - CALC_GEOCAPE_CH4_FORCE ', OB, ' of ',NOBS ! For safety, initialize these up to LLGEOCAPE CH4_HAT_werr(:) = 0d0 DIFF(:) = 0d0 FORCE(:) = 0d0 NEW_COST(:) = 0d0 ! Add random error to this observation DO LN = lind, LLGEOCAPE CH4_HAT_werr(LN) = CH4_HAT(LN) DO LLN = lind, LLGEOCAPE CH4_HAT_werr(LN) = CH4_HAT_werr(LN) + & CH4_HAT(LN) * RANDNUM(NT) * OBSERROR(LN,LLN) ENDDO ENDDO !-------------------------------------------------------------- ! Calculate cost function, given S is observation error covariance matrix ! Sobs = 1x1 array [ (molec/cm2) ^2 ] ! J = [ model - obs ]^T S_{obs}^{-1} [ model - obs ] !-------------------------------------------------------------- ! Calculate difference between modeled and observed profile DO LN = lind, LLGEOCAPE DIFF(LN) = CH4_HAT_werr(LN) - CH4_HAT_OB(LN) ENDDO ! Calculate adjoint forcing: 2 * DIFF^T * S_{obs}^{-1} ! and cost function: DIFF^T * S_{obs}^{-1} * DIFF DO LN = lind, LLGEOCAPE DO LLN = lind, LLGEOCAPE FORCE(LN) = FORCE(LN) + & 2d0 * OBSERROR_INV(LN,LLN) * DIFF(LLN) ENDDO NEW_COST(LN) = NEW_COST(LN) + 0.5*DIFF(LN)*FORCE(LN) ENDDO !-------------------------------------------------------------- ! Begin adjoint calculations !-------------------------------------------------------------- ! dkh debug ! print*, 'DIFF , FORCE, Sobs ' ! WRITE(6,102) (DIFF, FORCE, Sobs) ! 102 FORMAT(1X,d14.6,1X,d14.6) ! The adjoint forcing is 2 * S_{obs}^{-1} * DIFF = FORCE DIFF_ADJ(:) = 2. * FORCE(:) ! Adjoint of GEOS-Chem - Observation difference CH4_HAT_werr_ADJ(:) = DIFF_ADJ(:) ! Adjoint of adding random error to observation DO LN=lind,LLGEOCAPE CH4_HAT_ADJ(LN) = 0d0 DO LLN=lind,LLGEOCAPE CH4_HAT_ADJ(LN) = CH4_HAT_ADJ(LN) + & CH4_HAT_ADJ(LLN) * RANDNUM(NT) * OBSERROR(LLN,LN) ENDDO ENDDO ! Adjoint of GEOCAPE observation operator DO LN=lind,LLGEOCAPE CH4_PERT_ADJ(LN) = 0D0 DO LLN=lind,LLGEOCAPE CH4_PERT_ADJ(LN) = CH4_PERT_ADJ(LN) + & AVGKERNEL(LLN,LN) * CH4_HAT_ADJ(LLN) ENDDO ENDDO ! Adjoint of x_m - x_a DO LN = lind, LLGEOCAPE ! fwd code: !GC_CH4(LN) = MAX(GC_CH4(LN), 1d-10) !CH4_PERT(LN) = LOG(GC_CH4(LN)) - LOG(PRIOR(LN)) ! adj code: IF ( GC_CH4_onGEOCAPE(LN) > 1d-10 ) THEN GC_CH4_onGEOCAPE_ADJ(LN) = 1d0 / GC_CH4_onGEOCAPE(LN) * & CH4_PERT_ADJ(LN) ELSE GC_CH4_onGEOCAPE_ADJ(LN) = 1d0 / 1d-10 * CH4_PERT_ADJ(LN) ENDIF ENDDO ! Adjoint of interpolation DO LN=lind,LLGEOCAPE DO LG=1,LLPAR GC_CH4_NATIVE_ADJ(LG) = GC_CH4_NATIVE_ADJ(LG) + & GRIDMAP(LG,LN) * GC_CH4_onGEOCAPE_ADJ(LN) ENDDO ENDDO ! Adjoint of unit conversion DO LG=1,LLPAR GC_CH4_NATIVE_ADJ(LG) = GC_CH4_NATIVE_ADJ(LG) & * XNUMOL(1) / ( XNUMOLAIR * GC_AD(LG) ) ENDDO ! Pass adjoing forcing back to adjoint tracer array DO LG=1,LLPAR STT_ADJ(II,JJ,LG,1) = STT_ADJ(II,JJ,LG,1) + & GC_CH4_NATIVE_ADJ(LG) ENDDO ! Update cost function COST_FUNC = COST_FUNC + SUM(NEW_COST(:)) ENDDO ! End looping over each observation in this grid box ENDDO ! End looping over each grid box JJ ENDDO ! End looping over each grid box II !!$OMP END PARALLEL DO ! ----------------------------------------------------------------------- ! Use this section to test the adjoint of the GEOCAPE_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 ( DO_FDTEST ) THEN WRITE(116,210) ' LG' , ' TROP', ' GC_PRES', & ' FD_POS', ' FD_NEG', ' FD_CEN', & ' ADJ', ' COST_POS', ' COST_NEG', & ' FD_POS/ADJ', ' FD_NEG/ADJ', ' FD_CEN/ADJ' PERT(:) = 0D0 COST_FUNC_0 = 0d0 CALL CALC_GEOCAPE_CH4_FORCE_FD( COST_FUNC_0, PERT, ADJ ) ADJ_SAVE(:) = ADJ(:) DO LN=lind,LLGEOCAPE DOFS = DOFS + AVGKERNEL(LN,LN) ENDDO ! Write identifying information to top of satellite diagnostic file WRITE(116,212) 'COST_FUNC_0:',( COST_FUNC_0 ) WRITE(116,212) 'RANDOM ERROR',RANDNUM(NT) WRITE(116,212) 'DOFS ',DOFS !WRITE(116,*) (AVGKERNEL(1,LN),LN=1,13) !WRITE(116,*) (OBSERROR(1,LN),LN=1,13) ! Perform finite difference testing at each vertical level DO LG = 1, 47 ! Positive perturbation to GEOS-Chem CH4 columns PERT(:) = 0.0 PERT(LG) = 0.001 COST_FUNC_pos = 0D0 CALL CALC_GEOCAPE_CH4_FORCE_FD( COST_FUNC_pos, PERT, ADJ ) ! Negative perturbation to GEOS-Chem CH4 columns PERT(:) = 0.0 PERT(LG) = -0.001 COST_FUNC_neg = 0D0 CALL CALC_GEOCAPE_CH4_FORCE_FD( COST_FUNC_neg, PERT, ADJ ) ! Calculate dJ/dCH4 from perturbations FD_CEN(LG) = ( COST_FUNC_pos - COST_FUNC_neg ) / 0.2d0 FD_POS(LG) = ( COST_FUNC_pos - COST_FUNC_0 ) / 0.1d0 FD_NEG(LG) = ( COST_FUNC_0 - COST_FUNC_neg ) / 0.1d0 ! Write information to satellite diagnostic file WRITE(116, 211) LG, GC_PCENTER(LG), & FD_POS(LG), FD_NEG(LG), & FD_CEN(LG), ADJ_SAVE(LG), & COST_FUNC_pos, COST_FUNC_neg, & FD_POS(LG)/ADJ_SAVE(LG), & FD_NEG(LG)/ADJ_SAVE(LG), & FD_CEN(LG)/ADJ_SAVE(LG) 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,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) ! ----------------------------------------------------------------------- DO_FDTEST = .FALSE. ENDIF ! IF ( DO_FDTEST ) ! Update cost function !COST_FUNC = COST_FUNC + SUM(NEW_COST(:)) print*, ' Updated value of COST_FUNC = ', COST_FUNC print*, ' GEOCAPE contribution this hour = ', COST_FUNC - OLD_COST print*, ' # Obs analyzed this hour = ', NTh print*, ' # Obs analyzed today = ', NT print*, ' # Obs analyzed total = ', NTT ! Return to calling program END SUBROUTINE CALC_GEOCAPE_CH4_FORCE !------------------------------------------------------------------------------ SUBROUTINE CALC_GEOCAPE_CH4_FORCE_FD( COST_FUNC_A, PERT, ADJ ) ! !****************************************************************************** ! Subroutine CALC_GEOCAPE_CH4_FORCE calculates the adjoint forcing from the GEOCAPE ! CH4 observations and updates the cost function. (kjw, 07/20/11) ! 似乎是伴随强迫使用的程序,输出不仅有代价函数,还有伴随强迫(不过需要输入扰动) ! ! Arguments as Input/Output: ! ============================================================================ ! (1 ) COST_FUNC_A (REAL*8) : Cost funciton (INOUT) [unitless] ! (2 ) PERT (Real*8) : Array of perturbations to CH4 column (+/- 0.1, for ex.) 扰动的柱浓度 ! (5 ) ADJ (REAL*8) : Array of adjoint forcings (OUT) 强迫伴随 ! ! NOTES: ! (1 ) !****************************************************************************** ! ! Reference to f90 modules USE BPCH2_MOD, ONLY : GET_RES_EXT, GET_TAU0 USE BPCH2_MOD, ONLY : READ_BPCH2 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, CLDFRC USE DIRECTORY_ADJ_MOD, ONLY : DIAGADJ_DIR, ADJTMP_DIR USE GRID_MOD, ONLY : GET_YEDGE, GET_AREA_M2 USE PRESSURE_MOD, ONLY : GET_PCENTER, GET_PEDGE USE TIME_MOD, ONLY : GET_NYMD, GET_NHMS USE TIME_MOD, ONLY : GET_TAU USE TIME_MOD, ONLY : GET_LOCALTIME, EXPAND_DATE USE TRACER_MOD, ONLY : STT USE TRACER_MOD, ONLY : XNUMOL, XNUMOLAIR USE TRACER_MOD, ONLY : TCVV USE ERROR_MOD, ONLY : ERROR_STOP # include "CMN_SIZE" ! Size params ! Arguments REAL*8, INTENT(INOUT) :: COST_FUNC_A REAL*8, INTENT(OUT) :: ADJ(LLPAR) REAL*8, INTENT(IN) :: PERT(LLPAR) ! Local variables INTEGER :: NT INTEGER :: LG, LN, LLN, II, JJ, NB, JMIN, OB INTEGER :: nlev, lind, IU_IN INTEGER :: nboxes, nobs INTEGER :: NTSTART, NTSTOP REAL*8 :: GC_CH4_TRUE_ARRAY(IIPAR,JJPAR,LLPAR) REAL*8 :: CH4_PRIOR(IIPAR,JJPAR,LLGEOCAPE) REAL*4 :: DUMMY_PRIOR(IIPAR,JJPAR,LLGEOCAPE) REAL*4 :: DUMMY_TRUE(IIPAR,JJPAR,LLPAR) REAL*8 :: GC_PCENTER(LLPAR) REAL*8 :: GC_PEDGE(LLPAR) REAL*8 :: GC_AD(LLPAR) REAL*8 :: GC_CH4_NATIVE(LLPAR) REAL*8 :: GC_CH4_NATIVE_OB(LLPAR) REAL*8 :: thispcen(LLPAR) REAL*8 :: thispedg(LLPAR) REAL*8 :: thisad(LLPAR) REAL*8 :: thisch4(LLPAR) REAL*8 :: GC_CH4_onGEOCAPE(LLGEOCAPE) REAL*8 :: GC_CH4_onGEOCAPE_OB(LLGEOCAPE) REAL*8 :: GRIDMAP(LLPAR,LLGEOCAPE) REAL*8 :: CH4_HAT(LLGEOCAPE) REAL*8 :: CH4_HAT_OB(LLGEOCAPE) REAL*8 :: CH4_HAT_ADJ(LLGEOCAPE) REAL*8 :: CH4_HAT_werr(LLGEOCAPE) REAL*8 :: CH4_HAT_werr_ADJ(LLGEOCAPE) REAL*8 :: CH4_PERT(LLGEOCAPE) REAL*8 :: CH4_PERT_OB(LLGEOCAPE) REAL*8 :: CH4_PERT_ADJ(LLGEOCAPE) REAL*8 :: frac, frac_total REAL*8 :: latmin, Jfrac_min, Jfrac REAL*8 :: box_area, cloud_frac REAL*8 :: mass_air, mole_air, mole_ch4 REAL*8 :: LHS, RHS, GC_XCH4, XTAU REAL*8 :: DIFF(LLGEOCAPE) REAL*8 :: FORCE(LLGEOCAPE) REAL*8 :: DIFF_ADJ(LLGEOCAPE) REAL*8 :: thisforce(LLPAR) REAL*8 :: GC_CH4_onGEOCAPE_ADJ(LLGEOCAPE) REAL*8 :: GC_CH4_NATIVE_ADJ(LLPAR) REAL*8 :: NEW_COST(MAXGEOCAPE) REAL*8 :: OLD_COST LOGICAL, SAVE :: FIRST = .TRUE. LOGICAL, SAVE :: DO_FDTEST = .TRUE. INTEGER :: IOS CHARACTER(LEN=255) :: FILENAME CHARACTER(LEN=255) :: FILENAME_OBS !================================================================= ! CALC_GEOCAPE_CH4_FORCE_FD begins here! !================================================================= print*, ' - CALC_GEOCAPE_CH4_FORCE_FD ' NEW_COST(:) = 0d0 ! Read "TRUE" state for this time step GC_CH4_TRUE_ARRAY(:,:,:) = 0d0 ! FILENAME_OBS = '/home/kjw/GEOS-Chem/gcadj_std/runs/v8-02-01/' // ! & 'ch4/geocape/' // GET_RES_EXT() // '/adjtmp/' // ! & 'gctm.obs.YYYYMMDD.hhmm' FILENAME_OBS = 'gctm.obs.YYYYMMDD.hhmm' ! 观测文件, 居然是直接指定的么 CALL EXPAND_DATE( FILENAME_OBS, GET_NYMD(), GET_NHMS() ) FILENAME_OBS = TRIM( ADJTMP_DIR ) // TRIM( FILENAME_OBS ) XTAU = GET_TAU() CALL READ_BPCH2( TRIM(FILENAME_OBS), 'IJ-OBS-$', 1, & XTAU, IIPAR, JJPAR, & LLPAR, DUMMY_TRUE, QUIET=.TRUE.) ! 读取观测数据 GC_CH4_TRUE_ARRAY(:,:,:) = DUMMY_TRUE(:,:,:) ! 将观测数据赋值 ! Convert from [kg] --> [v/v] 逐个网格转换单位 DO II=1,IIPAR DO JJ=1,JJPAR DO LG=1,LLPAR GC_CH4_TRUE_ARRAY(II,JJ,LG) = GC_CH4_TRUE_ARRAY(II,JJ,LG) & * ( 1d3 / 16d0 ) / ( AD(II,JJ,LG) * 1d3 / 28.96 ) ENDDO ENDDO ENDDO ! Read a priori vertical profiles from file 读取先验廓线 FILENAME = '/home/kjw/new_satellites/geocape/data/' // & 'geocape_prior.' // GET_RES_EXT() // '.bpch' XTAU = GET_TAU0( 1, 1, 1985 ) CALL READ_BPCH2( FILENAME, 'IJ-AVG-$', 1, & XTAU, IGLOB, JGLOB, & LLGEOCAPE, DUMMY_PRIOR, QUIET=.TRUE. ) CH4_PRIOR(:,:,:) = DUMMY_PRIOR(:,:,:) ! 给先验廓线赋值 ! Select arbitrary II, JJ and NT value 为什么这里的数值都直接给定了 II=40 JJ=JJPAR-10 NT=100 ! Initialize variables 初始化相关的数组 GC_PCENTER(:) = 0d0 GC_PEDGE(:) = 0d0 GC_AD(:) = 0d0 GC_CH4_NATIVE(:) = 0d0 GC_CH4_onGEOCAPE(:) = 0d0 GC_CH4_onGEOCAPE_OB(:) = 0d0 CH4_HAT_werr(:) = 0d0 DIFF(:) = 0d0 FORCE(:) = 0d0 ! Get GEOS-Chem pressure and CH4 column corresponding to this grid box. ! CH4 in [kg/box] and pressure in [hPa] ! Get column of pressure centers and CH4 values DO LG=1,LLPAR ! Pressure centers [hPa] GC_PCENTER(LG) = GET_PCENTER(II,JJ,LG) ! Pressure edges [hPa] GC_PEDGE(LG) = GET_PEDGE(II,JJ,LG) ! mass per box [kg] GC_AD(LG) = AD(II,JJ,LG) ! CH4 values [kg/box] --> [v/v] GC_CH4_NATIVE(LG) = ( CHK_STT(II,JJ,LG,1) & * (1+PERT(LG)) * XNUMOL(1) ) / ( GC_AD(LG) * XNUMOLAIR ) ENDDO ! Number of vertical levels to use in these observations 处理观测和模式垂直层上的问题 ! Chop off lowermost levels if ! GEOS-Chem surface pressure < GEOCAPE pressure levels nlev = count( PRESSURE_EDGE .LT. GC_PEDGE(1) ) IF ( nlev .LT. 13 ) nlev = nlev + 1 lind = LLGEOCAPE + 1 - nlev ! minimum vertical index on GEOCAPE grid 垂直层相关 ! Get interpolation matrix that maps GEOS-Chem to GEOCAPE grid GRIDMAP(1:LLPAR, 1:LLGEOCAPE) = & GET_INTMAP( GC_PEDGE, PRESSURE_EDGE, nlev ) ! Get GEOS-Chem column from "truth" run to make pseudo-observations GC_CH4_NATIVE_OB(:) = 0d0 GC_CH4_NATIVE_OB(:) = GC_CH4_TRUE_ARRAY(II,JJ,:) ! 给观测数据赋值 ! Interpolate GEOS-Chem CH4 column and observation to GEOCAPE grid ! Column in [v/v] 在垂直上进行循环, 进行插值 DO LN = lind, LLGEOCAPE GC_CH4_onGEOCAPE(LN) = 0d0 GC_CH4_onGEOCAPE_OB(LN) = 0d0 DO LG = 1, LLPAR GC_CH4_onGEOCAPE(LN) = GC_CH4_onGEOCAPE(LN) & + GRIDMAP(LG,LN) * GC_CH4_NATIVE(LG) GC_CH4_onGEOCAPE_OB(LN) = GC_CH4_onGEOCAPE_OB(LN) & + GRIDMAP(LG,LN) * GC_CH4_NATIVE_OB(LG) ENDDO ENDDO !-------------------------------------------------------------- ! Apply GEOCAPE observation operator ! ! x_hat = x_a + A_k ( x_m - x_a ) ! ! where ! x_hat = GC modeled column as seen by GEOCAPE [molec/cm2] ! x_a = GEOCAPE apriori column [molec/cm2] ! x_m = GC modeled column on GEOCAPE grid [molec/cm2] ! A = GEOCAPE averaging kernel !-------------------------------------------------------------- ! x_m - x_a for model and "observation" 以下部分似乎是在计算模式柱浓度 ! [v/v] --> ln( v/v ) happens here 这部分是在观测垂直层上计算的 DO LN = lind, LLGEOCAPE GC_CH4_onGEOCAPE(LN) =MAX(GC_CH4_onGEOCAPE(LN), 1d-10) GC_CH4_onGEOCAPE_OB(LN)=MAX(GC_CH4_onGEOCAPE_OB(LN),1d-10) CH4_PERT(LN) =LOG( GC_CH4_onGEOCAPE(LN) ) - & LOG( CH4_PRIOR(II,JJ,LN) ) CH4_PERT_OB(LN) =LOG( GC_CH4_onGEOCAPE_OB(LN) ) - & LOG( CH4_PRIOR(II,JJ,LN) ) ENDDO ! x_a + A_k * ( x_m - x_a ) for model and "observation" CH4_HAT(:)=CH4_PERT(:) DO LN = lind, LLGEOCAPE CH4_HAT(LN) = 0d0 CH4_HAT_OB(LN) = 0d0 DO LLN = lind, LLGEOCAPE CH4_HAT(LN) = CH4_HAT(LN) & + AVGKERNEL(LN,LLN) * CH4_PERT(LLN) CH4_HAT_OB(LN) = CH4_HAT_OB(LN) & + AVGKERNEL(LN,LLN) * CH4_PERT_OB(LLN) ENDDO CH4_HAT(LN) = CH4_HAT(LN) + LOG( CH4_PRIOR(II,JJ,LN) ) CH4_HAT_OB(LN) = CH4_HAT_OB(LN) + LOG( CH4_PRIOR(II,JJ,LN) ) ENDDO ! For safety, initialize these up to LLGEOCAPE ! Add random error to this observation 给观测增加随机误差 CH4_HAT_werr(:) = CH4_HAT(:) DO LN = lind, LLGEOCAPE CH4_HAT_werr(LN) = CH4_HAT(LN) DO LLN = lind, LLGEOCAPE CH4_HAT_werr(LN) = CH4_HAT_werr(LN) + & CH4_HAT(LN) * RANDNUM(NT) * OBSERROR(LN,LLN) ENDDO ENDDO !------------------------------------------------------------- ! Calculate cost function, given S is observation error covariance matrix ! Sobs = 1x1 array [ (molec/cm2) ^2 ] ! J = [ model - obs ]^T S_{obs}^{-1} [ model - obs ] !-------------------------------------------------------------- ! Calculate difference between modeled and observed profile 计算差分部分 DO LN = lind, LLGEOCAPE DIFF(LN) = CH4_HAT_werr(LN) - CH4_HAT_OB(LN) ENDDO ! 因为卫星垂直上各层的误差存在相关性, 所以最终每层都要处理与其他各层之间的关联 ! Calculate adjoint forcing: 2 * DIFF^T * S_{obs}^{-1} 计算伴随强迫 ! and cost function: DIFF^T * S_{obs}^{-1} * DIFF 代价函数就是距离 DO LN = lind, LLGEOCAPE ! 在垂直上进行循环 DO LLN = lind, LLGEOCAPE ! 继续在垂直上进行循环? FORCE(LN) = FORCE(LN) + ! 伴随强迫的每一层都加上了该层与其他层之间的协方差 & 2d0 * OBSERROR_INV(LN,LLN) * DIFF(LLN) ENDDO NEW_COST(LN) = NEW_COST(LN) + 0.5*DIFF(LN)*FORCE(LN) ENDDO ! 也就是说, 这部分是将有限差分作为切线性算子的替代, 计算相关参数 !-------------------------------------------------------------- ! Begin adjoint calculations 开始计算伴随部分 !-------------------------------------------------------------- ! 整理以下这部分就是: FORCE = DIFF = CH4_HAT_werr_ADJ = CH4_HAT_ADJ ! The adjoint forcing is 2 * S_{obs}^{-1} * DIFF = FORCE DIFF_ADJ(:) = FORCE(:) ! 差分的伴随就是伴随强迫 ! Adjoint of GEOS-Chem - Observation difference CH4_HAT_werr_ADJ(:) = DIFF_ADJ(:) ! 观测模拟差的伴随 ! Adjoint of adding random error to observation 随机观测误差的伴随 DO LN=lind,LLGEOCAPE CH4_HAT_ADJ(LN) = 0d0 ! 初始化为 0 DO LLN=lind,LLGEOCAPE ! 考虑协方差, 计算最终误差 CH4_HAT_ADJ(LN) = CH4_HAT_ADJ(LN) + & CH4_HAT_werr_ADJ(LLN) * RANDNUM(NT) * OBSERROR(LLN,LN) ENDDO ENDDO CH4_HAT_ADJ(:) = CH4_HAT_werr_ADJ(:) ! 那你这上面计算的有什么意义 ! Adjoint of GEOCAPE observation operator 观测算子的伴随 CH4_PERT_ADJ(:) = CH4_HAT_ADJ(:) DO LN=lind,LLGEOCAPE CH4_PERT_ADJ(LN) = 0D0 ! 又给初始化了, 那上面的赋值有啥意义 DO LLN=lind,LLGEOCAPE ! 反正和协方差计算相关, 累积 CH4_PERT_ADJ(LN) = CH4_PERT_ADJ(LN) + & AVGKERNEL(LLN,LN) * CH4_HAT_ADJ(LLN) ENDDO ENDDO ! Adjoint of x_m - x_a 状态变量的伴随? DO LN = lind, LLGEOCAPE ! 对于观测垂直层进行循环 ! fwd code: 看上去这边是针对正向模拟写的, 因为正向模拟这边存在范围限制 !GC_CH4(LN) = MAX(GC_CH4(LN), 1d-10) 限制了最小值为 1e-10 !CH4_PERT(LN) = LOG(GC_CH4(LN)) - LOG(PRIOR(LN)) ! adj code: IF ( GC_CH4_onGEOCAPE(LN) > 1d-10 ) THEN GC_CH4_onGEOCAPE_ADJ(LN) = 1d0 / GC_CH4_onGEOCAPE(LN) * & CH4_PERT_ADJ(LN) ELSE GC_CH4_onGEOCAPE_ADJ(LN) = 1d0 / 1d-10 * CH4_PERT_ADJ(LN) ENDIF ENDDO ! Adjoint of interpolation 插值还需要伴随的么 DO LN=lind,LLGEOCAPE ! LN 是观测的垂直层 DO LG=1,LLPAR ! LG 是模式的垂直层 GC_CH4_NATIVE_ADJ(LG) = GC_CH4_NATIVE_ADJ(LG) + & GRIDMAP(LG,LN) * GC_CH4_onGEOCAPE_ADJ(LN) ! 从卫星垂直层插值到模式垂直层 ENDDO ENDDO ! Adjoint of unit conversion 单位转换的伴随, 大概就是从原始值到体积比的换算 DO LG=1,LLPAR GC_CH4_NATIVE_ADJ(LG) = GC_CH4_NATIVE_ADJ(LG) & * XNUMOL(1) / ( XNUMOLAIR * GC_AD(LG) ) ENDDO ! Pass adjoing forcing back to adjoint tracer array 将伴随强迫返回到大数组上 DO LG=1,LLPAR ! 最终的伴随强迫是在模式的垂直层上 ADJ(LG) = GC_CH4_NATIVE_ADJ(LG) * CHK_STT(II,JJ,LG,1) ENDDO ! Update cost function 更新代价函数 COST_FUNC_A = COST_FUNC_A + SUM(NEW_COST(:)) ! Return to calling program END SUBROUTINE CALC_GEOCAPE_CH4_FORCE_FD !------------------------------------------------------------------------------ FUNCTION GET_INTMAP( GC_PEDGE, GEOCAPE_PEDGE, nlev ) & RESULT ( M ) ! !****************************************************************************** ! Function GET_INTMAP creates the matrix that places GEOS-Chem column methane ! [molec/cm2] onto the 13-level pressure grid used by theoretical instrument, M. ! GC[1x47] * M[47x13] = GEOCAPE[1x13] (kjw, 7/21/11) ! ! Arguments as Input: ! ============================================================================ ! (1 ) GC_PEDGE (REAL*8) : LLPAR bottom pressure edges of GEOS-Chem column ! (2 ) SCIA_PEDGE (REAL*8) : LLGEOCAPE pressure edges of GEOCAPE column ! (3 ) nlev (REAL*8) : Number of GEOCAPE pressure levels to use ! ! Arguments as Output: ! ============================================================================ ! (1 ) M (REAL*8) : Interpolation matrix that maps GEOS-Chem to GEOCAPE grid ! ! NOTES: ! (1 ) Based on GET_INTMAP in scia_ch4_mod.f ! !****************************************************************************** ! ! Reference to f90 modules USE ERROR_MOD, ONLY : ERROR_STOP USE PRESSURE_MOD, ONLY : GET_BP # include "CMN_SIZE" ! Size params ! Arguments REAL*8 :: GC_PEDGE(LLPAR) REAL*8 :: GEOCAPE_PEDGE(LLGEOCAPE) INTEGER :: nlev ! Return value REAL*8 :: M(LLPAR,LLGEOCAPE) ! Local variables INTEGER :: LGC, LTM, LS, LG, LN, LIND REAL*8 :: DIFF, DELTA_SURFP REAL*8 :: GUP, GLO, NUP, NLO REAL*8 :: column_total(LLGEOCAPE) !================================================================= ! GET_INTMAP begins here! !================================================================= ! Initialize output M(:,:) = 0D0 ! Minimum GEOCAPE vertical level to use lind = LLGEOCAPE + 1 - nlev ! Loop over each pressure level of GEOS-Chem and GEOCAPE grids DO LG=1,LLPAR-1 ! Get upper and lower pressure edges of GEOS-Chem box GUP = GC_PEDGE( LG+1 ) GLO = GC_PEDGE( LG ) DO LN=lind,LLGEOCAPE-1 ! Get top and bottom pressures of GEOCAPE box NUP = GEOCAPE_PEDGE( LN+1 ) NLO = GEOCAPE_PEDGE( LN ) ! If both GEOS-Chem edges are within the GEOCAPE box, map value = 1 IF ( ( GUP .gt. NUP ) .AND. ( GLO .lt. NLO ) ) THEN M(LG,LN) = 1 ENDIF ! If both GEOS-Chem stradles a GEOCAPE pressure level, interpolate IF ( ( GUP .lt. NUP ) .AND. ( GLO .gt. NUP ) ) THEN DIFF = GLO - GUP M(LG,LN+1) = ( NUP - GUP ) / DIFF M(LG,LN ) = ( GLO - NUP ) / DIFF ENDIF ENDDO ENDDO ! Add value for uppermost GEOS-Chem grid box M(LLPAR,LLGEOCAPE) = 1 ! Correct for case in which GEOS-Chem pressure is higher than GEOCAPE IF ( GC_PEDGE(1) .GT. GEOCAPE_PEDGE(1) ) THEN ! If any part of GEOS-Chem box are under GEOCAPE_PEDGE(1), let ! this GEOS-Chem grid box contribute to the observation because ! GEOCAPE and GEOS-Chem should have same surface pressure. map value = 1 DO LG=1,LLPAR-1 ! If GEOS-Chem box entirely below GEOCAPE surface pressure IF ( ( GC_PEDGE(LG) .GT. GEOCAPE_PEDGE(1) ) .AND. & ( GC_PEDGE(LG+1) .GT. GEOCAPE_PEDGE(1) ) ) THEN M(LG,1) = 1 ENDIF ! If GEOS-Chem box straddles GEOCAPE surface pressure IF ( ( GC_PEDGE(LG) .GT. GEOCAPE_PEDGE(1) ) .AND. & ( GC_PEDGE(LG+1) .LT. GEOCAPE_PEDGE(1) ) ) THEN DIFF = GC_PEDGE(LG) - GC_PEDGE( LG+1 ) M(LG,1) = ( GEOCAPE_PEDGE(1) - GC_PEDGE(LG+1) ) / DIFF ENDIF ENDDO ENDIF ! Correct for case in which GEOS-Chem surface pressure is within 2nd GEOCAPE ! pressure level. IF ( GC_PEDGE(1) .LT. GEOCAPE_PEDGE(2) ) THEN M(1,1) = 0. ENDIF ! Correct for case in which GEOS-Chem surface pressure is within 3rd GEOCAPE ! pressure level. IF ( GC_PEDGE(1) .LT. GEOCAPE_PEDGE(3) ) THEN M(1,2) = 0. ENDIF ! Correct for case in which GEOS-Chem surface pressure is within 4th GEOCAPE ! pressure level. IF ( GC_PEDGE(1) .LT. GEOCAPE_PEDGE(4) ) THEN M(1,3) = 0. ENDIF ! Correct for case in which GEOS-Chem surface pressure is within 5th GEOCAPE ! pressure level. IF ( GC_PEDGE(1) .LT. GEOCAPE_PEDGE(5) ) THEN M(1,4) = 0. ENDIF ! Correct for case in which GEOS-Chem surface pressure is within 6th GEOCAPE ! pressure level. IF ( GC_PEDGE(1) .LT. GEOCAPE_PEDGE(6) ) THEN M(1,5) = 0. ENDIF ! Normalize each column of M to 1 so that we are not creating any molecules ! when mapping from GEOS-Chem to GEOCAPE grids. ! DO NOT do this since we are mapping molc/cm2, not ! Initialize to be safe and calculate column total column_total(:) = 0d0 column_total(:) = SUM( M, DIM=1 ) ! Normalize columns to column_total DO LN=1,LLGEOCAPE IF ( column_total(LN) .EQ. 0. ) CYCLE M(:,LN) = M(:,LN) / column_total(LN) ENDDO ! Return to calling program END FUNCTION GET_INTMAP !----------------------------------------------------------------------------- END MODULE GEOCAPE_CH4_MOD