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

1794 lines
64 KiB
Fortran

!$Id: sciabr_co_obs_mod.f,v 1.3 2012/03/01 22:00:27 daven Exp $
MODULE SCIAbr_CO_OBS_MOD
!******************************************************************************
! Module SCIAbr_CO_OBS_MOD contains subroutines necessary to
! 1. Read SCIA Bremen (ASCII) file with CO observations (monthly data files)
! 2. Determine when SCIA CO obs are available
! 3. Transform CHK_STT into SCIA levels and then columns
! 4. Compute adjoint forcing, including transforming the difference between
! model and MOPITT back to model space using the adjoint of averaging
! kernel and interpolation code.
!
! Module Routines:
! ============================================================================
! (1 ) READ_SCIA_CO_FILE : Read SCIA ASCII file
! (2 ) ITS_TIME_FOR_SCIA_CO_OBS: function checks model time vs. OBS_HOUR array
! (3 ) CALC_OBS_HOUR : calculates OBS_HOUR_SCIA_CO
! (4 ) COMPUTE_COLUMN : vertical gridding of CHK_STT, bin data
! (5 ) READ_ERROR_VARIANCE : Reads error variance file
! (6 ) CALC_SCIA_CO_FORCE : Calculates cost function and ADJ_STT increments
! (7 ) CLEANUP_SCIA : Deallocates memory of arrays
!
! ============================================================================
! Module Variables:
! ============================================================================
! (1 ) SCIACOcol(nobss) : columns read from AIRS file
! (2 ) SCIACOcol_err(nobss) :
! (3 ) COpressure(nlevs, nobss) :
! (4 ) Longitude(nobss) : vector of longitudes read from AIRS file
! (5 ) Latitude(nobss) : vector of latitudes read from AIRS file
! (6 ) COUNT_GRID(:,:,:) : array of # obs/gridsquare (in 1 day)
! (7 ) SCIA_COL_GRID(:,:,:) : gridded AIRS CO columns (computed and from file)
! (8 ) iday(:) : is a fraction of the day since beginning
! of the year
! (9 ) NObss : number of observation in each SCIA file
! (10 ) GRID_SCIA
! (11) SZA(nobs) : vector of solar zenith angle read from file
! (12) mday(nobs) : vector with day of month of obs
! (13) time_h(nobs) : vector with hour of day of obs
! (13) local_t(nobs) : vector with local time of obs
! (14) Cloud(nobs) : vector with cloud-free(0) or contam (1)
!
! NOTES:
! (1 ) Filter on MOPITT data: morning over pass only (in mop_mod.f) and
! only use obs that are greater than 5e17 (mak, 11/18/05)
! (2 ) Now READ(read data, calculate OBS_HOUR_SCIA_CO), then
! CALC_SCIA_CO_FORCE(GRID_SCIA, compute adj forcing): (mak, 8/1/07)
! (3 ) Fixed conflict with NOBS in CMN_ADJ, now we have NObss for SCIA.
! (4 ) Move NLev to obs modules from CMN_ADJ. this variable controls the
! vertical levels on which cost function is computed; NLev=1 indicates
! we're comparing model-satellite columns (mak, 8/14/07)
!
!******************************************************************************
IMPLICIT NONE
!=============================================================
! MODULE VARIABLES
!=============================================================
PRIVATE
PUBLIC :: READ_SCIAbr_CO_FILE
PUBLIC :: ITS_TIME_FOR_SCIABR_CO_OBS
PUBLIC :: CALC_SCIABr_CO_FORCE
REAL*8, ALLOCATABLE :: SCIACOcol_err(:)
REAL*8, ALLOCATABLE :: SCIACOcol(:)
REAL*8, ALLOCATABLE :: Longitude(:)
REAL*8, ALLOCATABLE :: Latitude(:)
REAL*8, ALLOCATABLE :: SZA(:)
INTEGER, ALLOCATABLE :: COUNT_GRID(:,:,:)
INTEGER, ALLOCATABLE :: iday(:)
INTEGER, ALLOCATABLE :: mday(:)
REAL*8, ALLOCATABLE :: time_h(:)
INTEGER, ALLOCATABLE :: Cloud(:)
REAL*8, ALLOCATABLE :: SCIA_COL_GRID(:,:,:,:)
REAL*8, ALLOCATABLE :: ERR_COL_GRID(:,:,:)
REAL*8, ALLOCATABLE :: CHK_STT_SCIA(:,:)
INTEGER :: NObss
!INTEGER, PARAMETER :: NLev = 1
INTEGER, PARAMETER :: NLevs = 60
INTEGER :: NDays
INTEGER :: fId
INTEGER :: TRCNUM
INTEGER, ALLOCATABLE :: OBS_HOUR_SCIA_CO(:,:,:)
REAL*4, ALLOCATABLE :: FRACTION(:,:,:,:)
REAL*8, ALLOCATABLE :: AirMass(:,:,:)
REAL*8, ALLOCATABLE :: ADJ_SCIA_ALL(:,:,:)
REAL*4, ALLOCATABLE :: ERR_PERCENT(:,:)
INTEGER, ALLOCATABLE :: DOMAIN_OBS(:,:)
CONTAINS
SUBROUTINE READ_SCIAbr_CO_FILE( YYYYMMDD, HHMMSS )
!******************************************************************************
! Subroutine READ_SCIA_FILE reads the SCIA ASCII file and assigns OBS_HOUR
! array based on available data. SCIA CO data are stored in a 1 month/file
! frequency. (mak, 7/12/07)
!
! Arguments as input:
! ============================================================================
! (1 ) YYYYMMDD : Year-Month-Day
! (2 ) HHMMSS : and Hour-Min-Sec for which to read restart file
!
!
USE ErrorModule, ONLY : ReplaceNanAndInf
USE ERROR_MOD, ONLY : GEOS_CHEM_STOP
USE FILE_MOD, ONLY : IOERROR
USE TIME_MOD, ONLY : EXPAND_DATE, YMD_EXTRACT, GET_MONTH
# include "CMN_SIZE" ! Size parameters
! Arguments
INTEGER, INTENT(IN) :: YYYYMMDD, HHMMSS
! Local Variables
INTEGER :: I, L, N
CHARACTER(LEN=255) DIR_SCIA
CHARACTER(LEN=255) FILENAME_SCIA
CHARACTER(LEN=255) FILENAME
integer ipx,ist,iread,it
real dsr_time,t_int,lat_c,lon_c,lat_1
real lon_1, lat_2,lon_2,lat_3,lon_3,lat_4,lon_4
real sza_in,los,azi
integer cld,lnd
real rms,snrad,alt,h20,h20_err,ch4,ch4_err,co,co_err
real co_corr, co_corr_err
integer coq
integer counter, count_day
INTEGER :: IU_FILE, IOS, IOS1
CHARACTER(LEN=255) :: header
INTEGER :: LINECOUNT
INTEGER :: YEAR, MONTH, DAY
INTEGER :: DAYOM_year(12), days_so_far
LOGICAL, SAVE :: FIRST = .TRUE.
INTEGER, SAVE :: LASTMONTH = -999
!=============================
! FIRST CLEANUP IF NECESSARY:
!=============================
CALL CLEANUP_SCIA
!========================
! FILENAME
!=========================
DIR_SCIA = '/as/data/scia/monthly_good/'
!DIR_SCIA = '/as/home/ctm/mak/sciamachy/data_scia/monthly_good/'
!DIR_SCIA = '/as/home/ctm/jaf/SCIA/data/'
FILENAME_SCIA = 'SCI_WFMD_L2_w8003_YYYYMM_v0.6.was'
IU_FILE = 15
days_so_far = -1
DAYOM_year(:) = -1
MONTH = YYYYMMDD/100 - (YYYYMMDD/10000)*100
YEAR = YYYYMMDD/10000
! Determine Ndays, number of days in the given month
SELECT CASE (MONTH)
CASE(4,6,9,11)
Ndays = 30
CASE(2)
IF (YEAR .eq. 2004) Ndays = 29
IF (YEAR .ne. 2004) Ndays = 28
CASE DEFAULT
Ndays = 31
END SELECT
IF( YEAR ==2004) THEN
DAYOM_year = (/31,29,31,30,31,30,31,31,30,31,30,31/)
ELSEIF (YEAR ==2005) THEN
DAYOM_year = (/31,28,31,30,31,30,31,31,30,31,30,31/)
ENDIF
days_so_far = sum(DAYOM_year(1:MONTH-1))
CALL EXPAND_DATE( FILENAME_SCIA, YYYYMMDD, 0 )
FILENAME = trim(DIR_SCIA)//FILENAME_SCIA
!print*, 'filename:', FILENAME
! zero counters
counter = 0
count_day = 0
LINECOUNT = 0
! Figure out how many observations to read (#lines in the file):
CALL SYSTEM('wc -l '//trim(fileName)//' > tmp.txt')
OPEN( 5, FILE='tmp.txt', IOSTAT=IOS1 )
IF ( IOS1 /= 0 ) CALL IOERROR( IOS1, 5, 'tmp:1' )
! Read #lines
READ( 5, *, IOSTAT=IOS1 ) linecount
IF ( IOS1 /= 0 ) CALL IOERROR( IOS1, 5, 'tmp:2' )
! Close file
CLOSE( 5 )
!PRINT*, 'There are:', LINECOUNT-39, 'good observations'
IF ( LINECOUNT == 0 ) THEN
PRINT*, 'There are no obs available, exit'
CALL GEOS_CHEM_STOP
ENDIF
OPEN( IU_FILE, FILE=fileName, IOSTAT=IOS )
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'scia:1' )
NObss = linecount-39
! Read SCIA header (first 41 lines, 39 lines for monthly files)
READ(IU_FILE,'(a)') HEADER
WRITE(*,*) TRIM( HEADER )
! rest of header
do i=2,39
read(IU_FILE, *)
enddo
!=========================================================
! ALLOCATE ARRAYS: (now that we read in their dimensions!)
!=========================================================
ALLOCATE( iday( NObss ) )
iday(:) = 0d0
ALLOCATE( mday( NObss ) )
mday(:) = 0d0
ALLOCATE( SCIACOcol( NObss ) )
SCIACOcol(:) = 0d0
ALLOCATE( SCIACOcol_err( NObss ) )
SCIACOcol_err(:) = 0d0
ALLOCATE( Longitude( NObss ) )
Longitude(:) = 0d0
ALLOCATE( Latitude( NObss ) )
Latitude(:) = 0d0
ALLOCATE( SZA( NObss ) )
SZA(:) = 0d0
ALLOCATE( time_h(NObss ) )
time_h(:) = 0d0
ALLOCATE( Cloud( NObss ) )
Cloud(:) = 0d0
!compute iday based on the input file, meaning August 1, for july file
! then subtract iday_input - iday = day fraction within the given month
! multiply the fraction times the NDAYS (from above), get, e.g. 15.5
! meaning it's July 15 at noon. use the modulus command to get day and
! hour
DO i = 40, NObss+39
IF ( IOS < 0 ) EXIT
IF ( IOS > 0 ) CALL IOERROR( IOS, IU_FILE, 'scia:2' )
! Read SCIA data
read( IU_FILE,
& '(i6,i4,i6,i2,f14.9,f9.5,10f10.5,3f8.3,2i4,11e13.9,i3)',
& IOSTAT=IOS) ipx,ist,iread,it,dsr_time,t_int,lat_c,lon_c,lat_1,
& lon_1, lat_2,lon_2,lat_3,lon_3,lat_4,lon_4,sza_in,los,azi,cld,
& lnd, rms,snrad,alt,h20,h20_err,ch4,ch4_err,co,co_err,co_corr,
& co_corr_err,coq
! dsr_time is Starttime in frac.days since 1.1.2000
! time_h is obs hour for each obs
if(year == 2004) then
iday(i-39)=dsr_time-366-365*3+1
time_h(i-39)=(dsr_time-366-365*3-iday(i-39)+1)*24
Cloud(i-39) = cld
ELSEIF(year == 2005 ) then
iday(i-39)=dsr_time-366*2-365*3+1
time_h(i-39)=(dsr_time-366*2-365*3-iday(i-39)+1)*24
Cloud(i-39) = cld
else
print*, 'year is:', year
print*, 'only looking at 2004 and 2005 data'
CALL GEOS_CHEM_STOP
endif
mday(i-39)=iday(i-39)-days_so_far !gives day of the month
! if it's a good data (quality flag coq != 1)
SCIACOcol(i-39) = co
SCIACOcol_err(i-39) = co_err
Latitude(i-39) = lat_c
Longitude(i-39) = lon_c !-180 ! for -180..180 range
SZA(i-39) = sza_in
counter = counter+1
ENDDO
! Close file
CLOSE( IU_FILE )
! Echo info
!PRINT*, '### number of observations (total): ', counter
!PRINT*, 'NObss: ', NObss
! PRINT*, '### Longitude min, max: ', MINVAL( Longitude ),
! & MAXVAL( Longitude )
! PRINT*, '### Latitude min, max: ', MINVAL( Latitude ),
! & MAXVAL( Latitude )
!PRINT*, '### SZA min, max: ', MINVAL( SZA ), MAXVAL( SZA )
!PRINT*, '### COcol min, max:', MINVAL(SCIACOcol),MAXVAL(SCIACOcol)
! PRINT*, '### COcol_err min, max:', MINVAL(SCIACOcol_err),
! & MAXVAL(SCIACOcol_err)
!print*, 'min/max of day of the month:', minval(mday), maxval(mday)
!print*, 'min/max hour of day:', minval(time_h), maxval(time_h)
! Decide how many arrays to read/store, for now 2 CO col computed
! and CO col read from file
TRCNUM = 1
!=========================================================
! ALLOCATE ARRAYS: (now that we read in their dimensions!)
!=========================================================
! Allocate 2 CO column matrices for CO columns computed and read in
!from file
ALLOCATE( SCIA_COL_GRID(IIPAR, JJPAR, Ndays,TRCNUM))
SCIA_COL_GRID(:,:,:,:) = 0d0
ALLOCATE( ERR_COL_GRID(IIPAR, JJPAR, Ndays))
ERR_COL_GRID(:,:,:) = 0d0
ALLOCATE ( COUNT_GRID(IIPAR, JJPAR, Ndays))
COUNT_GRID(:,:,:) = 0
ALLOCATE( OBS_HOUR_SCIA_CO(IIPAR, JJPAR, NDAYS ))
OBS_HOUR_SCIA_CO(:,:,:) = 0d0
ALLOCATE( ERR_PERCENT(IIPAR,JJPAR) )
ERR_PERCENT(:,:) = 0d0
! only compute SCIA_OBS_HOUR; grid when computing forcing
CALL CALC_OBS_HOUR
CALL INIT_DOMAIN
! READ ERROR FILE
IF(GET_MONTH() .NE. LASTMONTH) THEN
CALL READ_ERROR_VARIANCE
LASTMONTH = GET_MONTH()
ENDIF
END SUBROUTINE READ_SCIAbr_CO_FILE
!----------------------------------------------------------------------
SUBROUTINE COMPUTE_COLUMN
!*************************************************************************
! This subroutine computes SCIA column, based on the averaging kernels
! COverticality from SCIA file. (jaf, 7/07) It now includes vertical
! regridding, previously done in IDL ( mak, 8/2/07)
!
! Notes:
! (1 ) The subroutine does vertical regridding of GC column.
! Then reads averaging kernels, a priori guess and pressure levels
! Then it does retrieval to get a GC column * SCIA AK.
! (2 ) CHK_STT(IIPAR,JJPAR,LLPAR,1) -> Model_CO_MR(IIPAR,JJPAR,LLPAR)->
! ->CHK_STT_SCIA_VGRID(IIPAR,JJPAR,NLevs)->CHK_STT_STT(IIPAR,JJPAR)
!*************************************************************************
USE FILE_MOD, ONLY : IOERROR
USE TIME_MOD, ONLY : GET_HOUR, GET_DAY
USE DAO_MOD, ONLY : AD
USE TRACER_MOD, ONLY : TCVV
# include "CMN_SIZE" ! Size parameters
REAL*8 :: T(NLevs)
REAL*8 :: delP(NLevs)
REAL*8 :: Pedge(NLevs)
REAL*8 :: Pa(NLevs+1)
REAL*8 :: sciapress(NLevs)
REAL*8 :: xa(NLevs)
REAL*8 :: ap(NLevs)
REAL*8 :: AK(NLevs)
REAL*8 :: A(NLevs)
REAL*8 :: alt(NLevs)
REAL*8 :: temp(NLevs)
REAL*8 :: sza20(NLevs), sza30(NLevs), sza40(NLevs)
REAL*8 :: sza50(NLevs), sza60(NLevs), sza65(NLevs)
REAL*8 :: sza70(NLevs), sza75(NLevs),sza80(NLevs),sza85(NLevs)
INTEGER :: L, N, I, J, K, D, w, LL
INTEGER :: IU_FILE, IOS
INTEGER :: ILON, ILAT
CHARACTER(LEN=255) :: HEADER
LOGICAL, SAVE :: FIRST = .TRUE.
REAL*8 :: Model_CO_MR(IIPAR, JJPAR, NLevs)
xa(:) = 0d0
ap(:) = 0d0
Pa(:) = 0d0
sciapress(:) = 0d0
T(:) = 0d0
delP(:) = 0d0
PEdge(:) = 0d0
IF ( FIRST ) THEN
ALLOCATE( CHK_STT_SCIA( IIPAR,JJPAR ) )
CHK_STT_SCIA(:,:) = 0d0
ALLOCATE( FRACTION(IIPAR,JJPAR,LLPAR,NLevs))
FRACTION(:,:,:,:) = 0e0
ALLOCATE( Airmass(IIPAR,JJPAR,NLevs))
Airmass(:,:,:) = 0d0
ALLOCATE( ADJ_SCIA_ALL(IIPAR,JJPAR,LLPAR))
ADJ_SCIA_ALL= 0d0
FIRST = .FALSE.
ELSE
CHK_STT_SCIA(:,:) = 0d0
FRACTION(:,:,:,:) = 0e0
Airmass(:,:,:) = 0d0
ADJ_SCIA_ALL(:,:,:)= 0d0
ENDIF
Model_CO_MR(:,:,:) = 0d0
! xcol = AK*geos_raw + (I - AK) *xapriori
! OR xcol = AK*COppbv + (I-COverticality) *xapriori
! OR model_col(1) = T.xa + A.(geos_raw - xa)
! where T is COverticality and xa is read from file
! READ AIRS FIRST GUESS (A PRIORI) FROM A TEXT FILE:
!--------------------------------------------------------
! Open file with SCIA a priori (xa) and averaging kernels
!--------------------------------------------------------
IU_FILE = 15
OPEN( IU_FILE, FILE='ak_co_wfmdscia_V2.dat', IOSTAT=IOS )
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'aveker:1' )
! Read SCIA AK file header (first 4 lines)
READ(IU_FILE,'(a)') HEADER
!WRITE(*,*) TRIM( HEADER )
! rest of header
DO I = 2, 4
READ(IU_FILE, *)
ENDDO
DO I = 5, NLevs+4
L = I - 4
! Read SCIA info
! Note: Pressure (Pa) is in hPa, a priori (xa) is ppmv!!!
READ( IU_FILE, 100, IOSTAT=IOS ) alt(L), sciapress(L),
& temp(L), ap(L), sza20(L), sza30(L), sza40(L),
& sza50(L), sza60(L), sza65(L), sza70(L), sza75(L),
& sza80(L), sza85(L)
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'aveker:2' )
100 FORMAT(f7.2,2f8.2,f14.5,2x,10f8.3)
ENDDO
! Close file
CLOSE( IU_FILE )
! ak file lists first row starting w/ highest altitude
! Need to reverse order for Pa, xa; AK reversal done below
Pa(1:NLevs) = sciapress(NLevs:1:-1)
xa(1:NLevs) = ap(NLevs:1:-1)
! Assign top pressure (not given in AK file).
! For this, I assume the first pressure given is halfway
! between the pressure below (given) and the pressure
! above (not given)
Pa(NLevs+1) = 2*Pa(NLevs) - Pa(NLevs-1)
!---------------------------------------------------------
! GEOS-Chem profile on LLPAR levels, convert to NLevs =60
!---------------------------------------------------------
! have: GC profile for particular day of the month and for particular
! hour in the day CHK_STT(I,J,L)
CALL REGRIDV_SCIA( Model_CO_MR )
print*, 'max/min of Model_CO_MR:', maxval(Model_CO_MR),
& minval(Model_CO_MR)
! print*, 'Model_CO_MR location of min',minloc(Model_CO_MR),'max ',
! & MAXLOC(Model_CO_MR)
! CHK_STT is kg, since stored that way in geos_chem_mod
! convert to v/v from kg
c$$$!$OMP PARALLEL DO
c$$$!$OMP+DEFAULT( SHARED )
c$$$!$OMP+PRIVATE( I, J, L)
c$$$!$OMP+SCHEDULE( DYNAMIC )
c$$$ DO L= 1,NLevs
c$$$ DO J= 1,JJPAR
c$$$ DO I= 1,IIPAR
c$$$ Model_CO_MR(I,J,L) = CHK_STT_SCIA_VGRID(I,J,L)
c$$$ & * ADJ_TCVV(1) / AD(I,J,L)
c$$$ ENDDO
c$$$ ENDDO
c$$$ ENDDO
c$$$!$OMP END PARALLEL DO
!--------------------------------------------------------
! transfer function [molec/cm2/ppmv]
!DO L = 1, NLevs
! t(L) = 2.12E+16*delP(L) ! FOR SCIA (since P is in hPa and have to
!ENDDO ! convert to 1/cm2 from 1/m2
! and a priori in ppmv)
! column version of the averaging kernel
! A in molec/cm2/ppmv (avgker: unitless)
! For each observation 1 to NObss:
DO w = 1, NObss ! loop over # of obs
IF(floor(time_h(w)) == GET_HOUR() .AND.
& mday(w) == GET_DAY() .and.
& Cloud(w) == 0 ) THEN
! Look for model grid box corresponding to the SCIA observation:
! Get I corresponding to PLON(IND)
ILON = INT( ( Longitude(w) + 180d0 ) / DISIZE + 1.5d0 )
! Handle date line correctly (bmy, 4/23/04)
IF (ILON > IIPAR ) ILON = ILON - IIPAR
! Get J corresponding to PLAT(IND)
ILAT = INT( ( Latitude(w) + 90d0 ) / DJSIZE + 1.5d0 )
! Initialize AK
AK(:) = 0d0
! Get correct averaging kernel for given SZA of observation
IF ( (SZA(w) .ge. 0 ) .AND. (SZA(w) .lt. 25 ) )
& AK = sza20(NLevs:1:-1)
IF ( (SZA(w) .ge. 25 ) .AND. (SZA(w) .lt. 35 ) )
& AK = sza30(NLevs:1:-1)
IF ( (SZA(w) .ge. 35 ) .AND. (SZA(w) .lt. 45 ) )
& AK = sza40(NLevs:1:-1)
IF ( (SZA(w) .ge. 45 ) .AND. (SZA(w) .lt. 55 ) )
& AK = sza50(NLevs:1:-1)
IF ( (SZA(w) .ge. 55 ) .AND. (SZA(w) .lt. 62.5) )
& AK = sza60(NLevs:1:-1)
IF ( (SZA(w) .ge. 62.5) .AND. (SZA(w) .lt. 67.5) )
& AK = sza65(NLevs:1:-1)
IF ( (SZA(w) .ge. 67.5) .AND. (SZA(w) .lt. 72.5) )
& AK = sza70(NLevs:1:-1)
IF ( (SZA(w) .ge. 72.5) .AND. (SZA(w) .lt. 77.5) )
& AK = sza75(NLevs:1:-1)
IF ( (SZA(w) .ge. 77.5) .AND. (SZA(w) .lt. 82.5) )
& AK = sza80(NLevs:1:-1)
IF ( (SZA(w) .ge. 82.5) .AND. (SZA(w) .le. 90 ) )
& AK = sza85(NLevs:1:-1)
! Check that averaging kernel has been assigned
IF(MAXVAL(AK) .eq. 0) PRINT*, 'No averaging kernel assigned'
!print*, 'scia col:', SCIACOcol(w)
DO L = 1, NLevs ! loop over # of levels
delP(L) = Pa(L) - Pa(L+1)
! Compute transfer function for particular level
T(L) = 2.12E+16*delP(L)
! If we have all info, compute the CO column
IF ( (T(L) .gt. 0) .AND. (AK(L) .gt. 0) ) THEN
A(L) = T(L) * AK(L)
! SCIA a priori info (xa) is in ppmv
! Model profile is in v/v
CHK_STT_SCIA(ILON,ILAT) =
& CHK_STT_SCIA(ILON,ILAT)
& + T(L) * xa(L) + A(L) *
& (Model_CO_MR(ILON,ILAT,L)*1e6 - xa(L) )
ENDIF ! If we have all info
ENDDO ! Loop over levels
! print*, 'min max airmass:', minval(airmass), maxval(airmass)
! print*,'min max fraction:',minval(fraction),maxval(fraction)
! print*, 'min max ADJ_SCIA_ALL:',
! & minval(ADJ_SCIA_ALL(ILON,ILAT,:)),
! & MAXVAL(ADJ_SCIA_ALL(ILON,ILAT,:))
! PRINT*, 'ADJ_TCVV:', ADJ_TCVV(1)
! print*, 'min max a:', minval(a), maxval(a)
DO L = 1,LLPAR
DO LL = 1,NLevs
! d(CHK_STT_SCIA)/d(Model_CO_MR) = A(LL)*1e6
IF (AirMass(ILON,ILAT,LL) .GT. 0) THEN
ADJ_SCIA_ALL(ILON,ILAT,L) = ADJ_SCIA_ALL(ILON,ILAT,L)+
& (A(LL)*1e6) *(TCVV(1)/AirMass(ILON,ILAT,LL))
& *FRACTION(ILON,ILAT,L,LL)
ENDIF
ENDDO
ENDDO
! PRINT*, 'ADJ_SCIA_ALL:', ADJ_SCIA_ALL(ILON,ILAT,:)
! SCIA column
SCIA_COL_GRID(ILON,ILAT,mday(w),1) =
& SCIA_COL_GRID(ILON,ILAT,mday(w),1) + SCIACOcol(W)/0.9
! SCIA column error
ERR_COL_GRID(ILON,ILAT,mday(w)) =
& ERR_COL_GRID(ILON,ILAT,mday(w))+ SCIACOcol_err(w)
!print*, 'model col:', CHK_STT_SCIA(ILON,ILAT)
ENDIF !time and day of the model is the same as this obs
ENDDO ! Loop over observations
!=======================================
! BIN OUTPUT INFO INTO MODEL GRID BOXES
!=======================================
! Getting a day of the month ok if one month at a time
D = GET_DAY()
! print*, 'before averaging:'
! PRINT*, '### SCIA_COL_GRID min, max: ',
! & MINVAL( SCIA_COL_GRID(:,:,D,1) ), MAXVAL( SCIA_COL_GRID(:,:,D,1))
! PRINT*, '### CHK_STT_SCIA min, max: ',
! & MINVAL( CHK_STT_SCIA ), MAXVAL( CHK_STT_SCIA )
! PRINT*, '### ERR_COL_GRID min, max: ',
! & MINVAL( ERR_COL_GRID(:,:,D) ), MAXVAL( ERR_COL_GRID(:,:,D))
DO I = 1, IIPAR
DO J = 1, JJPAR
IF ( COUNT_GRID(I,J,D) .gt. 0. ) then
! average SCIA
SCIA_COL_GRID(I,J,D,1) = SCIA_COL_GRID(I,J,D,1)/
& COUNT_GRID(I,J,D)
! average SCIA error
ERR_COL_GRID(I,J,D) = ERR_COL_GRID(I,J,D)/COUNT_GRID(I,J,D)
! average model
CHK_STT_SCIA(I,J) = CHK_STT_SCIA(I,J)/COUNT_GRID(I,J,D)
DO L = 1,LLPAR
! d(CHK_STT_SCIA)/d(Model_CO_MR) = A(LL)*1e6
ADJ_SCIA_ALL(I,J,L) = ADJ_SCIA_ALL(I,J,L)/COUNT_GRID(I,J,D)
ENDDO
ELSE
SCIA_COL_GRID(I,J,D,:) = -999.
ERR_COL_GRID(I,J,D) = -999.
CHK_STT_SCIA(I,J) = -999.
ADJ_SCIA_ALL(I,J,:) = 0d0
ENDIF
ENDDO
ENDDO
PRINT*, '### SCIA_COL_GRID min, max: ',
& MINVAL( SCIA_COL_GRID(:,:,D,1) ), MAXVAL( SCIA_COL_GRID(:,:,D,1))
PRINT*, '### CHK_STT_SCIA min, max: ',
& MINVAL( CHK_STT_SCIA ), MAXVAL( CHK_STT_SCIA )
! PRINT*, '### ERR_COL_GRID min, max: ',
! & MINVAL( ERR_COL_GRID(:,:,D) ), MAXVAL( ERR_COL_GRID(:,:,D))
! PRINT*, '### ADJ_SCIA_ALL min, max:',
! & MINVAL( ADJ_SCIA_ALL ) , MAXVAL(ADJ_SCIA_ALL)
! call flush(6)
! PRINT*, '### COUNT_GRID min, max: ',
! & MINVAL( COUNT_GRID(:,:,:) ), MAXVAL( COUNT_GRID(:,:,:))
! PRINT*, 'TODAY:'
! PRINT*, '### COUNT_GRID min, max: ',
! & MINVAL( COUNT_GRID(:,:,D) ), MAXVAL( COUNT_GRID(:,:,D) )
END SUBROUTINE COMPUTE_COLUMN
!----------------------------------------------------------------------
SUBROUTINE READ_ERROR_VARIANCE
USE ERROR_MOD, ONLY : ERROR_STOP, GEOS_CHEM_STOP
USE BPCH2_MOD
USE FILE_MOD, ONLY : IOERROR
USE TIME_MOD, ONLY : GET_TAU
IMPLICIT NONE
# include "CMN_SIZE" ! Size parameters
! Arguments
CHARACTER(LEN=255) :: INPUT_FILE
INTEGER :: I, IOS, J, L
CHARACTER(LEN=255) :: FILENAME
! For binary punch file, version 2.0
REAL*4 :: LONRES, LATRES
REAL*4 :: TRACER(IIPAR,JJPAR)
INTEGER :: HALFPOLAR
INTEGER :: CENTER180
INTEGER :: NI, NJ, NL, k
INTEGER :: IFIRST, JFIRST, LFIRST
INTEGER :: NTRACER, NSKIP
REAL*8 :: ZTAU0, ZTAU1, TAUTMP
CHARACTER(LEN=20) :: MODELNAME
CHARACTER(LEN=40) :: CATEGORY
CHARACTER(LEN=40) :: UNIT
CHARACTER(LEN=40) :: RESERVED = ''
CHARACTER(LEN=80) :: TITLE
INTEGER :: IU_FILE
LOGICAL :: IT_EXISTS
INPUT_FILE = 'RRE_seasonMay1sciabrGlobal.bpch'
IU_FILE = 66
PRINT*, 'SET ERROR TO 30% AS WE SAVE SCIA FOR RRE CALCULATION'
ERR_PERCENT(:,:) = 0.3
GOTO 121
!================================================================
! READ OLD RESTART FILE
!================================================================
FILENAME = TRIM( INPUT_FILE )
! Echo some input to the screen
WRITE( 6, '(a)' ) REPEAT( '=', 79 )
WRITE( 6, '(a,/)' ) 'O B S E R R O R F I L E'
WRITE( 6, 100 ) TRIM( FILENAME )
100 FORMAT( 'READ_FILE: Reading ', a )
ERR_PERCENT(:,:) = -999d0
!READ SEASONAL ERRORS:
IF (((GET_TAU() .ge. 169440.) .and.(GET_TAU().lt. 170184.))
&.or.((GET_TAU() .ge. 176736.) .and.(GET_TAU().lt. 178200.)))THEN
! if May 2004, March 2005 or April 2005: read spring error
TAUTMP = 169440.00
ELSEIF((GET_TAU() .ge. 170184.).and.(GET_TAU().lt. 172392.))THEN
! if June 2004 through August 2004, read summer error
TAUTMP = 170184.00
ELSEIF((GET_TAU() .ge. 172392.).and.(GET_TAU().lt. 174576.))THEN
! if September 2004 through November 2004, read fall error
TAUTMP = 172392.00
ELSEIF((GET_TAU().ge. 174576.).and.(GET_TAU() .lt. 176736.))THEN
TAUTMP = 174576.00
ELSE
PRINT*, 'missing obs error for tau:', GET_TAU()
CALL GEOS_CHEM_STOP
ENDIF
! Open the binary punch file for input
CALL OPEN_BPCH2_FOR_READ( IU_FILE, FILENAME )
! Echo more output
!=================================================================
! Read tracers -- store in the TRACER array
!=================================================================
DO
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_file:4' )
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_file:5')
READ( IU_FILE, IOSTAT=IOS )
& ( ( ( TRACER(I,J), I=1,NI ), J=1,NJ ), L=1,NL )
IF ( IOS /= 0 ) CALL IOERROR( IOS,IU_FILE,'read_file:6')
!==============================================================
! Assign data from the TRACER array to the ERR_PERCENT array.
!==============================================================
IF ( ZTAU0 == TAUTMP ) THEN
PRINT*, 'Reading error for tau:', ztau0
! Make sure array dimensions are of global size
! (NI=IIPAR; NJ=JJPAR, NL=LLPAR), or stop the run
!print*, 'inside the loop'
!print*, 'max value is:', maxval(TRACER(:,:,:))
!print*, 'min value is:', minval(TRACER)
! This OpenMP Pragma breaks compilation on ifort 12 (yd 10/22/2012)
!!yd!$OMP PARALLEL DO
!!yd!$OMP+DEFAULT( SHARED )
!!yd!$OMP+PRIVATE( I, J , L)
DO J = 1, JJPAR
DO I = 1, IIPAR
IF(TRACER(I,J) .ge. 0.05)THEN
ERR_PERCENT(I,J) = TRACER(I,J)
ELSEIF((TRACER(I,J) .lt. 0.05).and.(TRACER(I,J).gt.0)) THEN
ERR_PERCENT(I,J) = 0.05
ELSE
ERR_PERCENT(I,J) = TRACER(I,J)
ENDIF
ENDDO
ENDDO
!!yd!$OMP END PARALLEL DO
ENDIF
ENDDO
! Close file
CLOSE( IU_FILE )
121 CONTINUE
print*, 'max value is:', maxval(ERR_PERCENT(:,:))
print*, 'min value is:', minval(ERR_PERCENT)
CALL FLUSH(6)
END SUBROUTINE READ_ERROR_VARIANCE
!---------------------------------------------------------------------------
SUBROUTINE GRID_SCIA
!*********************************************************************
! GRIDS SCIA ARRAYS ONTO GEOS-CHEM GRID OF CHOICE (e.g GEOS3 2x2.5)
!*********************************************************************
# include "CMN_SIZE"
INTEGER :: W, ILON, ILAT, I, J, M, D, count_day
!=====================================================
! GRID_SCIA begins here
!=====================================================
! at time point while looping over all obs we need to figure out
! what day is the obs from to assign it to the correct 3rd dimension.
count_day = 0
DO W = 1, NOBSS
! COMPUTE LONGITUDE AND LATITUDE
!=================================================================
! Look for model grid box corresponding to the MOPITT observation:
! Get I corresponding to PLON(IND)
ILON = INT( ( Longitude(W) + 180d0 ) / DISIZE + 1.5d0 )
! Handle date line correctly (bmy, 4/23/04)
IF (ILON > IIPAR ) ILON = ILON - IIPAR
! Get J corresponding to PLAT(IND)
ILAT = INT( ( Latitude(W) + 90d0 ) / DJSIZE + 1.5d0 )
! IF valid column obs, increment the counter for that box
IF(SCIACOcol(W) .gt. 0) THEN
D = mday(w)
DO I = 1, TRCNUM
IF (I .eq. 1 ) THEN
SCIA_COL_GRID(ILON,ILAT,D,I) =
& SCIA_COL_GRID(ILON,ILAT,D,I) + SCIACOcol(W)
ERR_COL_GRID(ILON,ILAT,D) =
& ERR_COL_GRID(ILON,ILAT,D) + SCIACOcol_err(w)
ELSEIF ( I .eq. 2 ) THEN
! CO_COL_GRID(ILON,ILAT,D,I) =
! & CO_COL_GRID(ILON,ILAT,D,I) +
! & (COcol(W) - COcol_pro(W))
ENDIF
ENDDO
ENDIF
count_day = count_day+1
ENDDO ! Loop over obs
print*,'number of obs this month',count_day
! print*,'Bin output model and scia data into array....'
!=======================================
! BIN OUTPUT INFO INTO MODEL GRID BOXES
!=======================================
DO D = 1, NDays
DO I = 1, IIPAR
DO J = 1, JJPAR
IF ( COUNT_GRID(I,J,D) .gt. 0. ) then
DO M = 1, TRCNUM
SCIA_COL_GRID(I,J,D,M) = SCIA_COL_GRID(I,J,D,M)/
& COUNT_GRID(I,J,D)
IF(M == 1) THEN
ERR_COL_GRID(I,J,D) = ERR_COL_GRID(I,J,D)/COUNT_GRID(I,J,D)
ENDIF
ENDDO
ELSE
SCIA_COL_GRID(I,J,D,:) = -999.
ERR_COL_GRID(I,J,D) = -999.
ENDIF
ENDDO
ENDDO
ENDDO ! Loop over days
PRINT*, '### SCIA_COL_GRID min, max: ',
& MINVAL( SCIA_COL_GRID(:,:,:,1) ), MAXVAL( SCIA_COL_GRID(:,:,:,1))
PRINT*, '### ERR_COL_GRID min, max: ',
& MINVAL( ERR_COL_GRID(:,:,:) ), MAXVAL( ERR_COL_GRID(:,:,:))
END SUBROUTINE GRID_SCIA
!-------------------------------------------------------------------------
FUNCTION ITS_TIME_FOR_SCIAbr_CO_OBS( ) RESULT( FLAG )
!
!******************************************************************************
! Function ITS_TIME_FOR_MOPITT_OBS returns TRUE if there are observations
! available for particular time (hour of a particular day) based on
! the OBS_HOUR array which holds the hour of obs in each gridbox (computed
! when file read in mop02_mod.f) (mak, 7/12/07)
!
! NOTES:
!
!******************************************************************************
!
USE TIME_MOD, ONLY : GET_HOUR, GET_MINUTE, GET_DAY
# include "CMN_SIZE" ! Size params
! Function value
LOGICAL :: FLAG
INTEGER :: I,J, D
!=================================================================
! ITS_TIME_FOR_SCIAbr_CO_OBS begins here!
!=================================================================
! Default to false
FLAG = .FALSE.
DO J = 1,JJPAR
DO I = 1,IIPAR
IF(GET_HOUR() == OBS_HOUR_SCIA_CO(I,J,GET_DAY())
& .AND. GET_MINUTE() == 0 ) THEN
print*, 'obs_hour was', GET_HOUR(), 'in box', i, j
FLAG = .TRUE.
GOTO 11
ENDIF
ENDDO
ENDDO
11 CONTINUE
END FUNCTION ITS_TIME_FOR_SCIAbr_CO_OBS
!---------------------------------------------------------------------------
SUBROUTINE CALC_SCIAbr_CO_FORCE
! References to F90 modules
USE ERROR_MOD, ONLY : IT_IS_NAN, ERROR_STOP
USE GRID_MOD, ONLY : GET_AREA_CM2
USE TIME_MOD, ONLY : GET_HOUR, GET_NYMDe, GET_NHMSe,
& GET_DAY
USE ADJ_ARRAYS_MOD, ONLY : SET_OBS,SET_MODEL,SET_MODEL_BIAS,
& SET_FORCING,COST_ARRAY,OBS_COUNT,
& STT_ADJ, COST_FUNC, IFD, JFD,
& LFD, NFD, ADJ_FORCE,
& DAY_OF_SIM
! if no AK, need access to STT
USE TRACER_MOD, ONLY : STT
USE LOGICAL_ADJ_MOD, ONLY : LPRINTFD, LDCOSAT
# include "CMN_SIZE" ! Size parameters
! Internal variables
REAL*8 :: DIFF_COST
REAL*8 :: NEW_COST(IIPAR,JJPAR)
INTEGER :: I, J, L, N, LL
INTEGER :: ADJ_EXPLD_COUNT
INTEGER, PARAMETER :: MAX_ALLOWED_EXPLD = 10
REAL*8, PARAMETER :: MAX_ALLOWED_INCREASE = 10D15
REAL*8 :: MAX_ADJ_TMP
REAL*4 :: invSy(IIPAR,JJPAR) !error variance for column
INTEGER :: DAYOM
REAL*8 :: Sy
LOGICAL :: USING_AK = .TRUE.
!================================================================
! CALC_SCIAbr_CO_FORCE begins here!
!================================================================
!print*, 'in CALC_SCIA_CO_FORCE'
! Some error checking stuff
MAX_ADJ_TMP = MAXVAL( STT_ADJ )
ADJ_EXPLD_COUNT = 0
Sy = 0d0
!initialize:
NEW_COST(:,:) = 0d0
! reinitialize domain
CALL INIT_DOMAIN
IF ( USING_AK ) THEN
! grid scia and compute GC*AK (column value as CHK_STT_SCIA)
! COL obs from GC to compare to scia
print*, 'using averaging kernels'
CALL COMPUTE_COLUMN
ELSE
! NO AVERAGING KERNELS
print*, 'not using averaging kernels'
IF(.not. (ALLOCATED( CHK_STT_SCIA) )) THEN
ALLOCATE( CHK_STT_SCIA( IIPAR,JJPAR ) )
CHK_STT_SCIA(:,:) = 0d0
ENDIF
CALL GRID_SCIA
! compute straight column, no averaging kernels for now
!CHK_STT_SCIA(:,:) = SUM(CHK_STT,3)
! this gives us a GC column in kg
!$OMP PARALLEL DO
!$OMP+DEFAULT( SHARED )
!$OMP+PRIVATE( I, J, L, N)
DO N = 1, 1 !FOR NOW, UNTIL WE have more than just CO obs
DO J = 1, JJPAR
DO I = 1, IIPAR
DO L = 1, LLPAR
CHK_STT_SCIA(I,J) = CHK_STT_SCIA(I,J) + STT(I,J,L,N)
ENDDO
! kg -> molec/cm2 conversion
! molec/cm2 = kg * 1000g/kg / (28g/mole) * 6.02 *10^23 molec/mole
! * (1/GET_AREA_CM2)
CHK_STT_SCIA(I,J) = CHK_STT_SCIA(I,J) * 1000 / 28 * 6.02 * 1e23
& * (1/GET_AREA_CM2(J))
ENDDO
ENDDO
ENDDO
!$OMP END PARALLEL DO
print*, 'min/max GC/scia column:', minval(chk_stt_scia),
& maxval(chk_stt_scia)
ENDIF !using AK
! DAY of the month:
DAYOM = GET_DAY()
! Compute error for each day of the obs and store its inverse in invSy
invSy(:,:) = 0d0
!$OMP PARALLEL DO
!$OMP+DEFAULT( SHARED )
!$OMP+PRIVATE( I, J, Sy)
DO J = 1, JJPAR
DO I = 1, IIPAR
IF( (SCIA_COL_GRID(I,J,DAYOM,1) .GT. 1e15) .and.
& (GET_HOUR() .EQ. OBS_HOUR_SCIA_CO(I,J,DAYOM)) .and.
& (ERR_PERCENT(I,J) .gt. 0) .and.
& (DOMAIN_OBS(I,J) .eq. 1) )THEN
! invSy(I,J) = 1/((ERR_COL_GRID(I,J,DAYOM)/100.0)**2 *
! 50% error
! invSy(I,J) = 1/((50.0/100.0)**2 *
! & SCIA_COL_GRID(I,J,DAYOM,1)**2)
Sy= ERR_PERCENT(I,J)**2 *
& SCIA_COL_GRID(I,J,DAYOM,1)**2
invSy(I,J) = 1.0/Sy
IF ( invSy(i,j) .gt. 1 ) THEN
CALL ERROR_STOP('invSy is too big', 'scia_co_obs_mod.f')
ENDIF
ELSE
!DOMAIN_OBS(I,J)=0
ENDIF
ENDDO
ENDDO
!$OMP END PARALLEL DO
! print*, 'min/max of % error:',
! & minval(err_col_grid(:,:,DAYOM)), maxval(err_col_grid(:,:,DAYOM))
! print*, 'min/max of error:',
! & minval(invSy), maxval(invSy)
! PRINT*, 'min/max of STT_ADJ, before obs:'
! PRINT*, minval(STT_ADJ), maxval(STT_ADJ)
! print*, 'STT_ADJ location of min', minloc(STT_ADJ),'max ',
! & MAXLOC(STT_ADJ)
! print*, 'adj stt(6,39,:)',adj_stt(6,39,:,1)
!DO N = 1, NOBS
!DO L = 1, NLEV !for SCIA NLEV=1, since using column data
!$OMP PARALLEL DO
!$OMP+DEFAULT( SHARED )
!$OMP+PRIVATE( I, J, LL)
!$OMP+PRIVATE( DIFF_COST )
DO J = 1, JJPAR
DO I = 1, IIPAR
! Determine the contribution to the cost function in each grid cell
! from each species
! CO_COL_GRID is SCIA observations
IF ( (SCIA_COL_GRID(I,J,DAYOM,1) .GT. 1e15) .and.
& (GET_HOUR() .EQ. OBS_HOUR_SCIA_CO(I,J,DAYOM)) .and.
& (DOMAIN_OBS(I,J) .eq. 1) )THEN
DIFF_COST = (CHK_STT_SCIA(I,J) - SCIA_COL_GRID(I,J,DAYOM,1))
! Calculate new additions to cost function
! include all regions for which there are obs
! NOTE: a bit of a mismatch in weight_obs in vertical
NEW_COST(I,J) = DOMAIN_OBS(I,J) *
! Updated for consistency with merged CALC_APRIOR (dkh, 01/18/12, adj32_017)
! (DIFF_COST ** 2) * invSy(I,J)
& 0.5d0 * (DIFF_COST ** 2) * invSy(I,J)
! Check for errors
!$OMP CRITICAL
IF ( IT_IS_NAN( NEW_COST(I,J) ) ) THEN
WRITE(6,*) ' Bad NEW_COST in ', I, J,
& ' from OBS, CHK, DOMAIN_OBS = ',
! & OBS_STT(I,J,L,N), CHK_STT_MOP(I,J,L,N),
& DOMAIN_OBS(I,J), DIFF_COST, invSy(i,j)
CALL ERROR_STOP('NEW_COST is NaN', 'adjoint_mod.f')
ENDIF
!$OMP END CRITICAL
! update diagnostic arrays if we're saving these diagnostics
IF ( LDCOSAT ) THEN
CALL SET_MODEL(I,J,DAY_OF_SIM,2,CHK_STT_SCIA(I,J))
CALL SET_OBS(I,J,DAY_OF_SIM,2,SCIA_COL_GRID(I,J,DAYOM,1))
CALL SET_MODEL_BIAS(I,J,DAY_OF_SIM,2,
& DIFF_COST/SCIA_COL_GRID(I,J,DAYOM,1))
CALL SET_FORCING(I,J,DAY_OF_SIM,NEW_COST(I,J))
PRINT*, 'MODEL:', I,J,CHK_STT_SCIA(I,J)
PRINT*, 'SCIA:', SCIA_COL_GRID(I,J,DAYOM,1)
! Update cost array
COST_ARRAY(I,J,DAY_OF_SIM) = COST_ARRAY(I,J,DAY_OF_SIM) +
& NEW_COST(I,J)
ENDIF
OBS_COUNT(I,J) = OBS_COUNT(I,J) + 1
!Adjoint of obs operator, LOOP over all 30 levels
DO LL=1,LLPAR
! Force the adjoint variables x with dJ/dx
! NO AVERAGING KERNERLS
! ADJ_FORCE(I,J,LL,1) = 2.0D0 * DOMAIN_OBS(I,J)
! & * DIFF_COST * invSy(I,J) * 1000.0/28.0*6.02 * 1e23
! & * (1/GET_AREA_CM2(J))
! WITH AVERAGING KERNERLS
! Updated for consistency with merged CALC_APRIOR (dkh, 01/18/12, adj32_017)
!ADJ_FORCE(I,J,LL,1) = 2.0D0 * DOMAIN_OBS(I,J)
ADJ_FORCE(I,J,LL,1) = DOMAIN_OBS(I,J)
& * DIFF_COST * invSy(I,J)* ADJ_SCIA_ALL(I,J,LL)
! Update STT_ADJ, first tracer (CO)
STT_ADJ(I,J,LL,1) = STT_ADJ(I,J,LL,1) + ADJ_FORCE(I,J,LL,1)
ENDDO
ENDIF
ENDDO
ENDDO
!$OMP END PARALLEL DO
!ENDDO
!ENDDO
PRINT*, 'OBS this hour:', sum(domain_obs(:,:))
print*, 'OBS so far:', sum(obs_count)
! Error checking: warn of exploding adjoit values, except
! the first jump up from zero (MAX_ADJ_TMP = 0 first few times)
IF ( MAXVAL(ABS(STT_ADJ)) > (MAX_ADJ_TMP * MAX_ALLOWED_INCREASE)
& .AND. ( MAX_ADJ_TMP > 0d0 ) ) THEN
WRITE(6,*)' *** - WARNING: EXPLODING adjoints in ADJ'
WRITE(6,*)' *** - MAX(STT_ADJ) before = ',MAX_ADJ_TMP
WRITE(6,*)' *** - MAX(STT_ADJ) after = ',MAXVAL(ABS(STT_ADJ))
ADJ_EXPLD_COUNT = ADJ_EXPLD_COUNT + 1
IF (ADJ_EXPLD_COUNT > MAX_ALLOWED_EXPLD )
& CALL ERROR_STOP('Too many exploding adjoints',
& 'ADJ_AEROSOL, adjoint_mod.f')
ENDIF
! Update cost function
!PRINT*, 'min/max of ADJ_FORCE:'
!PRINT*, minval(ADJ_FORCE), maxval(ADJ_FORCE)
!print*, 'location of min/max of ADJ_FORCE'
!PRINT*, minloc(ADJ_FORCE), MAXLOC(ADJ_FORCE)
!print*, 'adj force(6,41,:)',adj_force(6,41,:,1)
!PRINT*, 'min/max of STT_ADJ:'
!PRINT*, minval(STT_ADJ), maxval(STT_ADJ)
!PRINT*, 'min/max of ADJ_EMS:'
!PRINT*, minval(ADJ_EMS), maxval(ADJ_EMS)
!print*, 'location of min', minloc(ADJ_EMS),'max of ADJ_EMS',
! & MAXLOC(ADJ_EMS)
! print*, 'adj stt(6,41,:)',adj_stt(6,41,:,1)
! print*, 'adj_stt(9,39,:)',adj_stt(9,39,:,1)
PRINT*, 'min/max of NEW_COST'
PRINT*, minval(NEW_COST), maxval(NEW_COST)
!PRINT*, 'NEW_COST(FD)=', NEW_COST(IFD,JFD,NFD)
PRINT*, 'TOTAL NEW_COST = ', SUM(NEW_COST)
PRINT*, 'COST_FUNC BEFORE ADDING NEW_COST=', COST_FUNC
COST_FUNC = COST_FUNC + SUM ( NEW_COST )
!COST_ARRAY(1,1,1) = COST_ARRAY(1,1,1) + SUM ( NEW_COST )
! Echo output to screen
IF ( LPRINTFD ) THEN
!WRITE(6,*) ' ADJ_FORCE(:) = ', ADJ_FORCE(IFD,JFD,:,NFD)
WRITE(6,*) ' Using predicted value (CHK_STT_SCIA) = ',
& CHK_STT_SCIA(IFD,JFD), '[molec/cm2]'
WRITE(6,*) ' Using observed value (SCIA_STT) = ',
& SCIA_COL_GRID(IFD,JFD,DAYOM,1), '[molec/cm2]'
WRITE(6,*) ' Using WEIGHT = ', DOMAIN_OBS (IFD,JFD)
WRITE(6,*) ' ADJ_FORCE = ',
& ADJ_FORCE(IFD,JFD,LFD,NFD)
WRITE(6,*) ' STT_ADJ = ',
& STT_ADJ(IFD,JFD,LFD,NFD)
WRITE(6,*) ' NEW_COST = ',
& NEW_COST(IFD,JFD)
ENDIF
END SUBROUTINE CALC_SCIAbr_CO_FORCE
!------------------------------------------------------------------------------
SUBROUTINE CALC_OBS_HOUR
!***************************************************************************
! Subroutine CALC_OBS_HOUR computes an array of hours for each day of obs.
! If there is an obs in a particular gridbox on that day, it assigns the
! hour (0..23). If there isn't, OBS_HOUR stays initialized to -1. Also,
! this subroutine computes COUNT_GRID array.
! (mak, 12/14/05)
!***************************************************************************
USE ERROR_MOD, ONLY : ALLOC_ERR
USE BPCH2_MOD, ONLY : GET_TAU0
USE TIME_MOD, ONLY : GET_MONTH, GET_DAY,
& GET_YEAR, GET_HOUR
# include "CMN_SIZE"
INTEGER :: W, I, J, D
INTEGER :: ilon, ilat
REAL*4 :: OBS_HOURr(IIPAR,JJPAR, NDAYS)
integer :: count, as
count_grid(:,:,:) = 0d0
OBS_HOUR_SCIA_CO(:,:,:) = -99
OBS_HOURr(:,:,:) = 0
count = 0
!print*, 'in calc_obs_hour'
DO W = 1, NOBSS
!=================================================================
! COMPUTE LONGITUDE AND LATITUDE
!=================================================================
! Look for model grid box corresponding to the MOPITT observation:
! Get I corresponding to PLON(IND)
ILON = INT( ( Longitude(W) + 180d0 ) / DISIZE + 1.5d0 )
! Handle date line correctly (bmy, 4/23/04)
IF ( ILON > IIPAR ) ILON = ILON - IIPAR
! Get J corresponding to PLAT(IND)
ILAT = INT( ( Latitude(W) + 90d0 ) / DJSIZE + 1.5d0 )
if ( (ilon .eq. -999) .or. (ilat .eq. -999) ) then
print*,'ilon,ilat=',ilon,ilat
print*,'STOP'
stop
endif
! If there's an obs, calculate the time
IF(SCIACOcol(W) .gt. 0) THEN
D = mday(w)
OBS_HOURr(ILON,ILAT,D) = OBS_HOURr(ILON,ILAT,D)
& + time_h(w)
count_grid(ILON,ILAT,D) = count_grid(ILON, ILAT,D) + 1
ENDIF
! print*, 'obs hour in:', ilon, ilat, 'is:', obs_hour(ilon,ilat)
ENDDO
! average obs_hour on the grid
!$OMP PARALLEL DO
!$OMP+DEFAULT( SHARED )
!$OMP+PRIVATE( I, J, D, count)
DO D = 1, NDAYS
DO J = 1, jjPAR
DO I = 1, IIPAR
IF ( COUNT_GRID(I,J,D) .gt. 0. ) then
OBS_HOUR_SCIA_CO(I,J,D) = FLOOR(OBS_HOURr(I,J,D)/
& COUNT_GRID(I,J,D))
count = count + 1
!IF( D == 2 ) THEN
! PRINT*, I,J, OBS_HOUR_SCIA_CO(I,J,D)
!ENDIF
ENDIF
ENDDO
ENDDO
ENDDO
!$OMP END PARALLEL DO
!print*, 'obs hour in 144,45 is:', obs_hour(144,45)
!print*, 'today we have',sum(COUNT_GRID(:,:,GET_DAY()),'obs.'
END SUBROUTINE CALC_OBS_HOUR
!---------------------------------------------------------------------------
SUBROUTINE REGRIDV_SCIA( Model_CO_MR )
!***************************************************************************
! Subroutine REGRIDV_SCIA regrids CHK_STT from LLPAR levels of the GC to
! NLevs of SCIA retrieval. This code is a direct Fortran translation
! of Jenny Fisher's IDL code, which was in turn constructed from
! gamap's regridv.pro It calls a subroutine REGRID_COLUMN, which is a
! Fortran translation of IDL code, which apparently was a translation of
! Fortran code that we can no longer locate, which is a shame. (mak, 8/8/07)
!
! NOTES:
! (1 ) Missing from the idl version (Not needed here):
! ! Airmass on input grid is AD(I,J,L)
! ! Convert data from [v/v] to mass
! ! CHK_STT is already in kg
! ! Regrid vertically -- preserve column mass (now in kg)
! !OutCol = Regrid_Column( InCol, InPEdge, OutPEdge, $! ! No_Check=No_Check, _EXTRA=e )
! ! OutCol is now CHK_STT_SCIA_VGRID
!***************************************************************************
USE ERROR_MOD, ONLY : ERROR_STOP
USE BPCH2_MOD, ONLY : GET_NAME_EXT, GET_RES_EXT
USE FILE_MOD, ONLY : IOERROR
USE TIME_MOD, ONLY : GET_DAY
USE PRESSURE_MOD, ONLY: GET_PEDGE
USE CHECKPT_MOD, ONLY : CHK_PSC
USE TRACER_MOD, ONLY : STT, TCVV
# include "CMN_SIZE" ! PTOP, LLPAR, JJPAR, IIPAR
! NLevs = 60 levels of SCIA AKs and pressure levels
REAL*8, INTENT(INOUT):: Model_CO_MR(IIPAR, JJPAR, NLevs)
REAL*8 :: CHK_STT_SCIA_VGRID(IIPAR,JJPAR,NLevs)
REAL*8 :: SCIAPress(NLevs)
REAL*8 :: InPEdge(LLPAR+1)
REAL*8 :: OutPEdge(NLevs+1)
REAL*8 :: SCIAEdge(NLevs+1)
REAL*8 :: SCIAEdgePressure(NLevs+1)
REAL*8 :: surfP
!REAL*8 :: FRACTION(LLPAR,NLevs)
!REAL*8 :: AirMass(NLevs)
INTEGER I, J, D, L, LL, IU_FILE, IOS, k
REAL*8 :: HI, LOW, DIFF
LOGICAL, SAVE :: FIRST = .TRUE.
LOGICAL, SAVE :: valid = .FALSE.
!================================================================
! Read SCIA Pressure info
!================================================================
IU_FILE=16
FRACTION(:,:,:,:) = 0d0
! Read SCIA pressures
OPEN( IU_FILE, FILE='SCIA_pressure.dat', IOSTAT=IOS )
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'sciaP:1' )
READ( IU_FILE, 100, IOSTAT=IOS) SCIAPress
IF ( IOS /= 0 ) CALL IOERROR( IOS, IU_FILE, 'sciaP:2' )
100 FORMAT(f7.2)
! Close file
CLOSE( IU_FILE )
! SURFACE PRESSURE: use checkpointed pressure
!-------------------
SCIAEdge(1:60) = SCIAPress(60:1:-1)
!print*, 'scia pressure, max and min:'
!print*, maxval(sciaedge(1:60)), minval(sciaedge(1:60))
!Assume first given edge is 0.01hPa
SCIAEdge(61) = 0.01
!Store pressure edges
SCIAEdgePressure = SciaEDGE
! Convert to sigma scale
surfP=SCIAEdge(1)
DO k = 1,61
SCIAEdge(k)=SCIAEdge(k)/surfP
ENDDO
!-------------------
! REGRID DATA
!-------------------
CHK_STT_SCIA_VGRID(:,:,:) = 0d0
D = GET_DAY()
! Loop over surface grid boxes
! WARNING: parallelization screws it up. (mak, 8/15/07)
!!$OMP PARALLEL DO
!!$OMP+DEFAULT( SHARED )
!!$OMP+PRIVATE( I, J, L, LL, valid, first)
DO J = 1, JJPAR
DO I = 1, IIPAR
IF ( COUNT_GRID(I,J,D) .gt. 0. ) then
!to be safe, remove junk values:
fraction(i,j,:,:) = 0d0
DO L = 1, LLPAR
! OutVertEdge = AIRSEdgePressure / PSurf[I,J]
! Pressure edges on INPUT and OUTPUT grids
! both in and out pressures in hPa
InPEdge(L) = ( GET_PEDGE(I,J,L) ) !* Psurf(I,J)) + PTOP)/100
ENDDO
InPEdge(LLPAR+1) = PTOP
! OutPEdge = ( OutVertEdge * PSurf[I,J] ) ;+ OutType.PTOP
OutPEdge(:) = SCIAEdgePressure(:)
!=====================================================
! Determine fraction of each INPUT box
! which contributes to each OUTPUT box
!=====================================================
! LM1 = LLPAR, L = L, LM2 = NLevs, K = LL
! Loop over INPUT layers
FIRST = .TRUE.
valid = .false.
DO L = 1, LLPAR
! Reset VALID flag
Valid = .false.
! If the thickness of this pressure level is zero, then this
! means that this pressure level lies below the surface
! pressure (due to topography), as set up in the calling
! program. Therefore, skip to the next INPUT level.
! This also helps avoid divide by zero errors. (bmy, 8/6/01)
IF ( ( InPEdge(L) - InPedge(L+1) ) .lt. 1e-5 ) then
!print*, '1. going to 12'
goto 12 !NextL
ENDIF
! Loop over OUTPUT layers
DO LL = 1, NLevs
if( OutPEdge(LL) .lt. InPEdge(L) .and.
& OutPEdge(LL) .lt. InPEdge(L+1) .and.
& (ll .eq. 1) .and. (l.eq.1) ) THEN
Fraction(i,j,L,LL) = 1d0
!print*, 'first GC layer lower than 1st SCIA layer'
! Go to next iteration
goto 12 !NextL
endif
!=================================================
! No contribution if:
! -------------------
! Bottom of OUTPUT layer above Top of INPUT layer OR
! Top of OUTPUT layer below Bottom of INPUT layer
! ..unless it's the first layer in GC (mak, 8/15/07)
!===================================================
if ( OutPEdge(LL) .lt. InPEdge(L+1) .OR.
& OutPEdge(LL+1) .gt. InPEdge(L) ) THEN
goto 13 !NextLL
ENDIF
!==================================================
! Contribution if:
! ----------------
! Entire INPUT layer in OUTPUT layer
!===================================================
if ( OutPEdge(LL) .ge. InPEdge(L) .AND.
& OutPEdge(LL+1) .le. InPEdge(L+1) ) then
Fraction(i,j,L,LL) = 1d0
!Indicate a valid contribution from L to LL
Valid = .true.
! Go to next iteration
goto 13 !NextLL
endif
!==================================================
! Contribution if:
! ----------------
! Top of OUTPUT layer in INPUT layer
!==================================================
if ( OutPEdge(LL+1) .le. InPEdge(L) .AND.
& OutPEdge(LL) .ge. InPEdge(L) ) THEN
Fraction(i,j,L,LL) =(InPEdge(L) - OutPEdge(LL+1)) /
& ( InPEdge(L) - InPEdge(L+1) )
! Indicate a valid contribution from L to LL
Valid = .true.
! Go to next iteration
goto 13 !NextLL
endif
!==================================================
! Contribution if:
! ----------------
! Entire OUTPUT layer in INPUT layer
!==================================================
if ( OutPEdge(LL) .le. InPEdge(L) .AND.
& OutPEdge(LL+1) .ge. InPEdge(L+1) ) then
Fraction(i,j,L,LL)=(OutPEdge(LL) - OutPEdge(LL+1))/
& ( InPEdge(L) - InPEdge(L+1) )
! Also add the to the first OUTPUT layer the fraction
! of the first INPUT layer that is below sigma = 1.0
! This is a condition that can be found in GEOS-3 data.
if ( ( First ) .AND.
& ( LL .eq. 1 ) .AND.
& ( InPEdge(L) .gt. OutPEdge(1) ) ) then
Fraction(i,j,L,LL) = Fraction(i,j,L,LL) +
& ( InPEdge(L) - OutPEdge(1) ) /
& ( InPEdge(L) - InPEdge(L+1) )
! We only need to do this once...
First = .false.
endif
! Indicate a valid contribution from L to LL
Valid = .true.
! Go to next iteration
goto 13 !NextLL
endif
!===================================================
! Contribution if:
! ----------------
! Bottom of OUTPUT layer in INPUT layer
!===================================================
if ( OutPEdge(LL) .ge. InPEdge(L+1) .AND.
& OutPEdge(LL+1) .le. InPEdge(L+1) ) then
Fraction(i,j,L,LL) = ( OutPEdge(LL) - InPEdge(L+1) ) /
& ( InPEdge(L) - InPEdge(L+1) )
! Also add the to the first OUTPUT layer the fraction
! of the first INPUT layer that is below sigma = 1.0
! This is a condition that can be found in GEOS-3 data.
if ( ( First ) .AND.
& ( LL .eq. 1 ) .AND.
& ( InPEdge(L) .gt. OutPEdge(1) ) ) then
Fraction(i,j,L,LL) = Fraction(i,j,L,LL) +
& ( InPEdge(L) - OutPEdge(1) ) /
& ( InPEdge(L) - InPEdge(L+1) )
! We only need to do this once...
First = .false.
endif
! Indicate a valid contribution from L to LL
Valid = .true.
! Go to next iteration
goto 13 !NextLL
endif
13 CONTINUE !NextLL
ENDDO ! LL
!======================================================
! Consistency Check:
! ------------------
! If SUM( FRACTION(L,:) ) does not = 1, there is a problem.
! Test those INPUT layers (L) which make a contribution to
! OUTPUT layers (LL) for this criterion.
!
!======================================================
if ( Valid ) then
if ( Abs( 1e0 - sum( Fraction(i,j,L,:))) .ge. 1e-4 ) THEN
print*, 'Fraction does not add to 1'
print*, L, LL,sum( Fraction(i,j,L,:) )
print*, 'frac(5,:):', fraction(i,j,L,:)
PRINT*, 'InPEdge:', InPEdge
print*, 'OutPEdge:', OutPEdge
CALL ERROR_STOP ('REGRIDV still sucks',
& 'scia_co_obs_mod.f' )
endif
endif
12 CONTINUE !NextL
ENDDO !L
!==========================================================
! Compute "new" data -- multiply "old" data by fraction of
! "old" data residing in the "new" layer
!==========================================================
! Map CO from GC to SCIA grid
DO LL = 1 , NLevs
DO L = 1 , LLPAR
CHK_STT_SCIA_VGRID(I,J,LL) =
& CHK_STT_SCIA_VGRID(I,J,LL)
& + STT(I,J,L,1)*FRACTION(i,j,L,LL)
ENDDO
ENDDO
!print*, 'columns before and after regridding:'
!PRINT*,I,J,SUM(CHK_STT(I,J,:,1)),SUM(CHK_STT_SCIA_VGRID(I,J,:))
IF(Abs( SUM(CHK_STT_SCIA_VGRID(I,J,:)) - SUM(STT(I,J,:,1)))
& /SUM(STT(I,J,:,1)) .gt. 1e-5 ) THEN
PRINT*, 'columns before and after regrid dont add up:'
print*, 'columns before and after regridding:'
PRINT*,I,J,SUM(STT(I,J,:,1)),SUM(CHK_STT_SCIA_VGRID(I,J,:))
PRINT*, 'InPEdge:', InPEdge
print*, 'OutPEdge:', OutPEdge
print*, 'chk_stt'
print*, 'chk_stt_scia_vgrid:'
print*, chk_stt_scia_vgrid(i,j,:)
CALL ERROR_STOP ('REGRIDV sucks',
& 'scia_co_obs_mod.f' )
ENDIF
! Airmass on output grid (in kg/box in each level)
AirMass(I,J,:) = RVR_GetAirMass( SCIAEdge, j, surfP )
!AirMass = RVR_GetAirMass( OutVertEdge, OutArea[I,J], surfP )
! Convert data from kg to [v/v]
! Model_CO_MR = kgCO * gair/gCO / kgair = [v/v]
DO LL = 1, NLevs
Model_CO_MR(I,J,LL) = CHK_STT_SCIA_VGRID(I,J,LL) *
& TCVV(1)/AirMass(I,J,LL)
ENDDO
! DO L = 1, LLPAR
! DO LL = 1, NLev
! ADJ_SCIA_ALL(I,J,L) = ADJ_SCIA_ALL(I,J,L) +
! & A(LL)*1e6*ADJ_TCVV(1)/AirMass(I,J,LL)*FRACTION(L,LL)
! !ADJ_SCIA_REGRID(I,J,L,LL) = FRACTION(L,LL)
! ENDDO
! ENDDO
! Model_CO_MR = [CHK_STT(L)*FRACTION(L,LL)]*ADJ_TCVV/Airmass
! d(Model_CO_MR)/d(CHK_STT) = (ADJ_TCVV/Airmass)*FRACTION(L,LL)
! DO LL = 1,NLevs
! ADJ_SCIA_CONVERT(I,J,LL) = ADJ_TCVV(1)/AirMass(LL)
! ENDDO
ENDIF
ENDDO
ENDDO
!!$OMP END PARALLEL DO
END SUBROUTINE REGRIDV_SCIA
!---------------------------------------------------------------------------
FUNCTION RVR_GetAirMass( SCIAEdge, J, SurfP) RESULT ( AirMassloc )
!====================================================================
! Internal function RVR_GETAIRMASS returns a column vector of air
! mass given the vertical coordinates, the surface area,
! and surface pressure. (bmy, 12/19/03)
!====================================================================
USE GRID_MOD, ONLY : GET_AREA_M2
# include "CMN_SIZE"
INTEGER, INTENT(IN) :: J
REAL*8, INTENT(IN) :: SurfP
REAL*8 :: AirMassloc(NLevs)
REAL*8, INTENT(IN) :: SCIAEdge(NLevs+1)
INTEGER :: L
REAL*8 :: g100
AirMassloc(:) = 0d0
! Constant 100/g
g100 = 100d0 / 9.8d0
! Loop over levels
! airmass(L) = hPa * m2 * 1 * 100Pa/hPa * 1/(m/s2) =
! = N * 1/(m/s2) = kg
DO L = 1, NLevs
AirMassloc(L) = SurfP * GET_AREA_M2(J) *
& ( SCIAEdge(L) - SCIAEdge(L+1) ) * g100
ENDDO
END FUNCTION RVR_GetAirMass
!---------------------------------------------------------------------------
SUBROUTINE INIT_DOMAIN
USE ERROR_MOD, ONLY : ALLOC_ERR
# include "CMN_SIZE"
INTEGER I, J, as
LOGICAL, SAVE :: FIRST = .TRUE.
IF ( FIRST ) THEN
ALLOCATE( DOMAIN_OBS( IIPAR,JJPAR ) ,stat=as )
IF ( as /= 0 ) CALL ALLOC_ERR( 'DOMAIN_OBS' )
FIRST = .FALSE.
ENDIF
DOMAIN_OBS(:,:) = 0d0
!$OMP PARALLEL DO
!$OMP+DEFAULT( SHARED )
!$OMP+PRIVATE( I, J )
DO J = 1, JJPAR
DO I = 1, IIPAR
IF (
! & .and. (I .ge. 93) .and. (I .le. 144) ! MOPITT TRACE-P..
! & .and. (J .ge. 41) .and. (J .le. 73) ! ..region 2x2.5
! & .and. (J .ge. 40) .and. (J .le. 72) ! ..region 2x2.5
!! & .and. (EMS_orig(I,J,NEMS) .NE. 0 )
!! & .and. L < LPAUSE(I,J) ! Only in the troposphere
!! & .and. IS_LAND(I,J) ! Only the land species
! & .and. ( MOD( I, 2 ) == 0 ) ! Only in every other cell
!! & .and. J >= 10 ! Not in antarctica
! & .and. L == 8 ! Only at ~500mb
! & .and. (J .ge. 24) ! only N.Hemisphere
& (J .le. 38) ! not poleward of 60N
& .and. (J .ge. 9) ! not poleward of 60S
& ) THEN
DOMAIN_OBS(I,J) = 1
ELSE
DOMAIN_OBS(I,J) = 0
ENDIF
ENDDO
ENDDO
!$OMP END PARALLEL DO
PRINT*, sum(DOMAIN_obs), 'MAX observations today'
END SUBROUTINE INIT_DOMAIN
!-----------------------------------------------------------------------------
SUBROUTINE CLEANUP_SCIA
! DEALLOCATE ALL MEMORY (DONE BEFORE READING REACH MONTHLY FILE)
! Deallocate
IF ( ALLOCATED( SCIACOcol ) ) DEALLOCATE( SCIACOcol )
IF ( ALLOCATED( SCIACOcol_err ) ) DEALLOCATE( SCIACOcol_err )
IF ( ALLOCATED( Longitude ) ) DEALLOCATE( Longitude )
IF ( ALLOCATED( Latitude ) ) DEALLOCATE( Latitude )
IF ( ALLOCATED( SZA ) ) DEALLOCATE( SZA )
IF ( ALLOCATED( SCIA_COL_GRID ) ) DEALLOCATE( SCIA_COL_GRID )
IF ( ALLOCATED( ERR_COL_GRID ) ) DEALLOCATE( ERR_COL_GRID )
IF ( ALLOCATED( COUNT_GRID ) ) DEALLOCATE( COUNT_GRID )
IF ( ALLOCATED( iday ) ) DEALLOCATE( iday )
IF ( ALLOCATED( mday ) ) DEALLOCATE( mday )
IF ( ALLOCATED( time_h ) ) DEALLOCATE( time_h )
IF ( ALLOCATED( OBS_HOUR_SCIA_CO ) ) DEALLOCATE( OBS_HOUR_SCIA_CO)
IF ( ALLOCATED( ERR_PERCENT ) ) DEALLOCATE( ERR_PERCENT )
IF ( ALLOCATED( Cloud ) ) DEALLOCATE( Cloud )
END SUBROUTINE CLEANUP_SCIA
!---------------------------------------------------------------------------
END MODULE SCIAbr_CO_OBS_MOD