Files
GEOS-Chem-adjoint-v35-note/code/obs_operators/geocape_ch4_mod.f

1429 lines
56 KiB
Fortran
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

!$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