From 65dfe7e277b0f4a88b3ad8da0dabb6d019bcd0be Mon Sep 17 00:00:00 2001 From: ronesy <rlt@nilu.no> Date: Fri, 10 May 2024 11:37:04 +0200 Subject: [PATCH] Included changes for using satellite observations and satellite data preprocessor: prep_satellite --- README.txt | 46 +- grid_to_ncdf/README_gridncdf.txt | 13 +- grid_to_ncdf/SETTINGS | 8 + grid_to_ncdf/factor_convert.f90 | 97 +++ grid_to_ncdf/grid_convert.f90 | 51 +- grid_to_ncdf/grid_to_ncdf.f90 | 25 +- grid_to_ncdf/init_convert.f90 | 146 +++++ grid_to_ncdf/initialize.f90 | 30 +- grid_to_ncdf/makefile | 2 + grid_to_ncdf/mod_flexpart.f90 | 96 ++- grid_to_ncdf/mod_settings.f90 | 8 + grid_to_ncdf/write_ncdf.f90 | 20 +- prep_flexpart/README_prepflex.txt | 2 +- prep_flexpart/{SETTINGS_co2 => SETTINGS} | 18 +- prep_flexpart/SETTINGS_co2_nest | 113 ---- prep_flexpart/SETTINGS_ghg | 113 ---- prep_flexpart/SETTINGS_ghg_nest | 113 ---- prep_flexpart/job_prep_flexpart.sh | 2 +- prep_flexpart/list_obsfiles.f90 | 8 - prep_flexpart/mod_settings.f90 | 4 + prep_flexpart/mod_var.f90 | 3 +- prep_flexpart/prep_outgrid.f90 | 11 + prep_flexpart/process_obs.f90 | 4 +- prep_flexpart/read_basic.f90 | 9 +- prep_flexpart/read_icos.f90 | 11 +- prep_flexpart/read_noaa.f90 | 9 +- prep_flexpart/read_obspack.f90 | 9 +- prep_flexpart/read_reclist.f90 | 5 +- prep_flexpart/read_wdcgg.f90 | 9 +- prep_flexpart/run_flexpart.sh | 15 +- prep_flexpart/sbatch_prep_flexpart.sh | 4 +- prep_fluxes/FLUXES_co2_bbg | 68 --- prep_fluxes/FLUXES_co2_ff | 68 --- prep_fluxes/FLUXES_co2_nee | 68 --- prep_fluxes/FLUXES_co2_ocn | 68 --- prep_fluxes/README_prepflux.txt | 3 +- prep_fluxes/{FLUXES_ghg => SETTINGS_FLUX} | 4 +- prep_fluxes/job_prep_flux.sh | 2 +- prep_fluxes/sbatch_prep_flux.sh | 2 +- prep_regions/initialize.f90 | 41 +- prep_regions/mod_settings.f90 | 28 + prep_regions/mod_strings.f90 | 92 ++- prep_regions/mod_var.f90 | 2 +- prep_regions/prep_regions.f90 | 13 +- prep_regions/read_obs.f90 | 237 +++++--- prep_regions/read_reclist.f90 | 5 +- prep_satellite/SETTINGS_bremen | 155 +++++ prep_satellite/SETTINGS_oco2 | 157 +++++ prep_satellite/SETTINGS_tropomi | 159 +++++ prep_satellite/average.f90 | 468 +++++++++++++++ prep_satellite/geodarea.f90 | 61 ++ prep_satellite/get_bremen.f90 | 431 +++++++++++++ prep_satellite/get_oco2.f90 | 465 ++++++++++++++ prep_satellite/get_tropomi.f90 | 552 +++++++++++++++++ prep_satellite/gridarea.f90 | 64 ++ prep_satellite/main.f90 | 86 +++ prep_satellite/makefile | 40 ++ prep_satellite/mod_dates.f90 | 176 ++++++ prep_satellite/mod_save.f90 | 225 +++++++ prep_satellite/mod_settings.f90 | 567 ++++++++++++++++++ prep_satellite/mod_var.f90 | 39 ++ prep_satellite/prep_ageclass.f90 | 69 +++ prep_satellite/prep_command.f90 | 171 ++++++ prep_satellite/prep_outgrid.f90 | 131 ++++ prep_satellite/prep_pathnames.f90 | 75 +++ prep_satellite/run_flexpart.sh | 97 +++ prep_satellite/sbatch_satellite.sh | 15 + prep_syndata/get_obs.f90 | 18 +- prep_syndata/initialize.f90 | 36 +- prep_syndata/job_prep_syndata.sh | 4 +- prep_syndata/mod_save.f90 | 123 +++- prep_syndata/mod_settings.f90 | 26 +- prep_syndata/mod_strings.f90 | 19 +- prep_syndata/sbatch.sh | 4 +- ...ghg_nest_config => SETTINGS_ch4sat_config} | 62 +- ...S_ghg_nest_files => SETTINGS_ch4sat_files} | 63 +- settings/SETTINGS_co2_config | 6 + settings/SETTINGS_co2_files | 24 +- settings/SETTINGS_co2_nest_config | 161 ----- settings/SETTINGS_co2_nest_files | 133 ---- settings/SETTINGS_ghg_config | 10 +- settings/SETTINGS_ghg_files | 20 +- source/README_source.txt | 4 +- source/average_fp.f90 | 12 +- source/calc_conc.f90 | 8 +- source/congrad.f90 | 10 +- source/get_initconc.f90 | 8 +- source/init_cini.f90 | 48 +- source/init_cini_month.f90 | 64 +- source/init_co2.f90 | 21 +- source/init_ghg.f90 | 26 +- source/initialize.f90 | 73 ++- source/m1qn3_interface.f90 | 4 + source/mod_flexpart.f90 | 12 +- source/mod_obs.f90 | 52 +- source/mod_save.f90 | 62 +- source/mod_settings.f90 | 34 +- source/mod_strings.f90 | 15 +- source/read_obs.f90 | 285 ++++++--- source/read_reclist.f90 | 21 +- source/sbatch_flexinvert.sh | 6 +- source/simulate.f90 | 153 +++-- 102 files changed, 6034 insertions(+), 1501 deletions(-) create mode 100644 grid_to_ncdf/factor_convert.f90 create mode 100644 grid_to_ncdf/init_convert.f90 rename prep_flexpart/{SETTINGS_co2 => SETTINGS} (89%) delete mode 100644 prep_flexpart/SETTINGS_co2_nest delete mode 100644 prep_flexpart/SETTINGS_ghg delete mode 100644 prep_flexpart/SETTINGS_ghg_nest delete mode 100644 prep_fluxes/FLUXES_co2_bbg delete mode 100644 prep_fluxes/FLUXES_co2_ff delete mode 100644 prep_fluxes/FLUXES_co2_nee delete mode 100644 prep_fluxes/FLUXES_co2_ocn rename prep_fluxes/{FLUXES_ghg => SETTINGS_FLUX} (92%) create mode 100644 prep_satellite/SETTINGS_bremen create mode 100644 prep_satellite/SETTINGS_oco2 create mode 100644 prep_satellite/SETTINGS_tropomi create mode 100644 prep_satellite/average.f90 create mode 100644 prep_satellite/geodarea.f90 create mode 100644 prep_satellite/get_bremen.f90 create mode 100644 prep_satellite/get_oco2.f90 create mode 100644 prep_satellite/get_tropomi.f90 create mode 100644 prep_satellite/gridarea.f90 create mode 100644 prep_satellite/main.f90 create mode 100644 prep_satellite/makefile create mode 100644 prep_satellite/mod_dates.f90 create mode 100644 prep_satellite/mod_save.f90 create mode 100644 prep_satellite/mod_settings.f90 create mode 100644 prep_satellite/mod_var.f90 create mode 100644 prep_satellite/prep_ageclass.f90 create mode 100644 prep_satellite/prep_command.f90 create mode 100644 prep_satellite/prep_outgrid.f90 create mode 100644 prep_satellite/prep_pathnames.f90 create mode 100755 prep_satellite/run_flexpart.sh create mode 100755 prep_satellite/sbatch_satellite.sh rename settings/{SETTINGS_ghg_nest_config => SETTINGS_ch4sat_config} (78%) rename settings/{SETTINGS_ghg_nest_files => SETTINGS_ch4sat_files} (71%) delete mode 100644 settings/SETTINGS_co2_nest_config delete mode 100644 settings/SETTINGS_co2_nest_files diff --git a/README.txt b/README.txt index fe9fe0a..83cfac9 100644 --- a/README.txt +++ b/README.txt @@ -1,40 +1,56 @@ ================================================================ - FLEXINVERT-Plus + FLEXINVERT ================================================================ Description: - FLEXINVERT-Plus is a Bayesian inversion framework + + FLEXINVERT is a Bayesian inversion framework for optimizing fluxes of different atmospheric species (e.g. CO2, CH4, BC). Contents: - 1) prep_flexpart: + + prep_flexpart: - selects (and optionally averages) observations and prepares flexpart runs with releases for each observation - - flexpart grid_time output files written per release + - flexpart grid_time output files are written per release - note need at least FLEXPART-v10.2 - 2) prep_fluxes: + + prep_satellite: + - selects (and optionally averages) satellite retrievals and + prepares flexpart releases for each retrieval and the + flexpart options files + - flexpart grid_time output files are written per retrieval + and represent the total column source receptor relationship + - note need at least FLEXPART-v10.4beta + + prep_fluxes: - regrids fluxes (fluxes need to be at the same spatial resolution as the flexpart grid_time files) - writes fluxes in a standard ncdf format required by - FLEXINVERT-Plus - 3) prep_regions (optional): + FLEXINVERT + + prep_regions (optional): - prepares variable resolution grid for the inversion based on flexpart flux sensitivities (output from prep_flexpart) (alternatively can provide own regions definition file) - 4) settings: + + settings: - contains example SETTINGS_config and SETTINGS_files files - for FLEXINVERT-Plus - 5) source: - - source code for FLEXINVERT-Plus - 6) prep_syndata: - - prepares synthetic data for testing and OSEs + for FLEXINVERT + + source: + - source code for FLEXINVERT (for the actual inversion) + + prep_syndata: + - prepares synthetic data for testing and OSSEs - uses output from a forward run and adds random perturbations to the prior fluxes and observations according to the error covariance matrices - 7) grid_to_ncdf: - - extra tool (not needed for FLEXINVERT-Plus) to convert + + grid_to_ncdf: + - extra tool (not needed for FLEXINVERT) to convert the binary grid_time files to NetCDF diff --git a/grid_to_ncdf/README_gridncdf.txt b/grid_to_ncdf/README_gridncdf.txt index 58691c1..2328418 100644 --- a/grid_to_ncdf/README_gridncdf.txt +++ b/grid_to_ncdf/README_gridncdf.txt @@ -1,14 +1,17 @@ ================================================================ - FLEXINVERT-Plus BINARY TO NETCDF CONVERTOR + FLEXINVERT binary to netcdf converter ================================================================ Description: - Converts FLEXPART grid_time files from sparse binary format - to netcdf. Note this is not needed to run FLEXINVERT-Plus but - is an extra tool that users may find helpful, e.g. for viewing - the footprints. + Converts one of the following flexpart output file types: + - grid_time + - grid_initial + - factor_drygrid + from sparse binary format to netcdf. Note this is not needed to + run FLEXINVERT but is an extra tool that users may find helpful, + e.g. for viewing the footprints. Usage: 1) compile with gfortran using: make diff --git a/grid_to_ncdf/SETTINGS b/grid_to_ncdf/SETTINGS index 0020b77..732a96f 100644 --- a/grid_to_ncdf/SETTINGS +++ b/grid_to_ncdf/SETTINGS @@ -22,9 +22,17 @@ file_recept: /myfile.txt datei: 20140701 datef: 20140831 +# Directory frequency: 'month' or 'day' +# note: use 'day' for satellite retrievals (otherwise use 'month') +filefreq: month + # Nested output (true/false) lnested: .true. # Inversion formatted flexpart output (true/false) linversionout: .true. +# Type of input (0 = grid_time, 1 = grid_initial, 2 = factor for dryair) +intype: 0 + + diff --git a/grid_to_ncdf/factor_convert.f90 b/grid_to_ncdf/factor_convert.f90 new file mode 100644 index 0000000..0c0d687 --- /dev/null +++ b/grid_to_ncdf/factor_convert.f90 @@ -0,0 +1,97 @@ +!--------------------------------------------------------------------------------------- +! GRID_TO_NCDF: factor_convert +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> factor_convert +!! +!! Purpose: +!! +!--------------------------------------------------------------------------------------- + +subroutine factor_convert(files, config, nr, timestamp) + + use mod_var + use mod_settings + use mod_flexpart + use mod_dates + + implicit none + + type (files_t), intent(in out) :: files + type (config_t), intent(in) :: config + integer, intent(in) :: nr + integer, intent(in) :: timestamp + + character(max_path_len) :: filename, filedates, path_flexpart, path_output + character(len=8) :: adate + character(len=6) :: atime + logical :: lexist + integer :: nfiles, nread, i, ierr, n + integer :: jjjjmmdd, hhmiss + real, dimension(:,:,:), allocatable :: grid + real(kind=8), dimension(:), allocatable :: ftime, dates + real(kind=8) :: jdate + + ! path names + if ( trim(config%filefreq).eq.'month' ) then + write(adate,'(I6.6)') timestamp/100 + print*, adate + else if ( trim(config%filefreq).eq.'day' ) then + write(adate,'(I8.8)') timestamp + endif + path_flexpart = trim(files%path_flexpart)//trim(recname(nr))//'/'//trim(adate)//'/' + path_output = trim(files%path_output)//trim(recname(nr))//'/' + filedates = trim(path_flexpart)//'dates' + + ! read binary file + call get_nread(filedates, nread) + allocate ( dates(nread) ) + allocate ( ftime(nread) ) + allocate ( grid(nxgrid,nygrid,nread) ) + call read_dates(filedates, nread, dates) + do n = 1, nread + ftime(n) = juldate(int(dates(n)/1d6),int(dates(n)-floor(dates(n)/1d6)*1d6)) + end do + grid(:,:,:) = 1. + if ( config%lnested ) then + filename = trim(path_flexpart)//'factor_drygrid_nest' + else + filename = trim(path_flexpart)//'factor_drygrid' + endif + inquire ( file=trim(filename),exist=lexist ) + if ( lexist ) then + write(*,*) 'Reading correction factor :'//trim(filename) + call read_factor(filename, nread, nxgrid, nygrid, nxshift, grid) + endif + + ! output file name + if ( config%lnested ) then + filename = trim(path_output)//'factor_drygrid_nest.nc' + else + filename = trim(path_output)//'factor_drygrid.nc' + endif + + ! write as ncdf + call write_ncdf(config, filename, ftime, grid) + + deallocate( grid ) + deallocate( ftime ) + deallocate( dates ) + +end subroutine factor_convert + diff --git a/grid_to_ncdf/grid_convert.f90 b/grid_to_ncdf/grid_convert.f90 index efaba9e..4c3adfd 100644 --- a/grid_to_ncdf/grid_convert.f90 +++ b/grid_to_ncdf/grid_convert.f90 @@ -23,7 +23,7 @@ !! !--------------------------------------------------------------------------------------- -subroutine grid_convert(files, config, nr, amonth) +subroutine grid_convert(files, config, nr, timestamp) use mod_var use mod_settings @@ -33,8 +33,9 @@ subroutine grid_convert(files, config, nr, amonth) implicit none type (files_t), intent(in out) :: files - type (config_t), intent(in) :: config + type (config_t), intent(in out) :: config integer, intent(in) :: nr + integer, intent(in) :: timestamp character(max_path_len) :: filename, filedates, path_flexpart, path_output character(len=max_path_len), dimension(:), allocatable :: filelist @@ -53,7 +54,13 @@ subroutine grid_convert(files, config, nr, amonth) integer :: numx, numy ! list all grid files in directory - path_flexpart = trim(files%path_flexpart)//trim(recname(nr))//'/'//trim(amonth)//'/' + if ( trim(config%filefreq).eq.'month' ) then + write(adate,'(I6.6)') timestamp/100 + print*, adate + else if ( trim(config%filefreq).eq.'day' ) then + write(adate,'(I8.8)') timestamp + endif + path_flexpart = trim(files%path_flexpart)//trim(recname(nr))//'/'//trim(adate)//'/' path_output = trim(files%path_output)//trim(recname(nr))//'/' print*, path_flexpart print*, path_output @@ -110,42 +117,56 @@ subroutine grid_convert(files, config, nr, amonth) ! read binary grid files if ( config%lnested ) then adate = filelist(i)(16:23) - atime = filelist(i)(24:29) + if ( trim(config%filefreq).eq.'month' ) then + atime = filelist(i)(24:29) + else if ( trim(config%filefreq).eq.'day' ) then + ! assume satellite -> get number of retrieval + atime = filelist(i)(25:30) + endif else adate = filelist(i)(11:18) - atime = filelist(i)(19:24) + if ( trim(config%filefreq).eq.'month' ) then + atime = filelist(i)(19:24) + else if ( trim(config%filefreq).eq.'day' ) then + ! assume satellite -> get number of retrieval + atime = filelist(i)(20:25) + endif endif -! print*, 'adate = ',adate -! print*, 'atime = ',atime read(adate,*) jjjjmmdd read(atime,*) hhmiss -! print*, 'jjjjmmdd = ',jjjjmmdd -! print*, 'hhmiss = ',hhmiss + print*, 'jjjjmmdd = ',jjjjmmdd + print*, 'hhmiss = ',hhmiss if ( config%linversionout ) then jdate = juldate(jjjjmmdd, hhmiss) else jdate = juldate(datei,timei) endif -! print*, 'jdate = ',jdate filename = trim(path_flexpart)//trim(filelist(i)) if ( config%linversionout ) then call read_grid(config, filename, filedates, jdate, nxgrid, nygrid, grid, gtime) else call read_grid_std(config, filename, jdate, nxgrid, nygrid, grid, gtime) endif -! print*, 'gtime = ',gtime ! write to ncdf do n = 1, ngrid call caldate(gtime(n), jjjjmmdd, hhmiss) time(n) = real(jjjjmmdd,kind=8)*100d0+real(hhmiss/10000,kind=8) end do - print*, 'time = ',time if ( config%lnested ) then - filename = trim(path_output)//'grid_time_nest_'//adate//atime//'_001.nc' + if ( trim(config%filefreq).eq.'month' ) then + filename = trim(path_output)//'grid_time_nest_'//adate//atime//'_001.nc' + else if ( trim(config%filefreq).eq.'day' ) then + ! assume satellite + filename = trim(path_output)//'grid_time_nest_'//adate//'_'//atime//'.nc' + endif else - filename = trim(path_output)//'grid_time_'//adate//atime//'_001.nc' + if ( trim(config%filefreq).eq.'month' ) then + filename = trim(path_output)//'grid_time_'//adate//atime//'_001.nc' + else if ( trim(config%filefreq).eq.'day' ) then + filename = trim(path_output)//'grid_time_'//adate//'_'//atime//'.nc' + endif endif - call write_ncdf(filename, time, grid) + call write_ncdf(config, filename, time, grid) end do deallocate( grid ) diff --git a/grid_to_ncdf/grid_to_ncdf.f90 b/grid_to_ncdf/grid_to_ncdf.f90 index 1de5465..dd7f9a8 100644 --- a/grid_to_ncdf/grid_to_ncdf.f90 +++ b/grid_to_ncdf/grid_to_ncdf.f90 @@ -40,7 +40,7 @@ program grid_to_ncdf character(len=6) :: adate integer :: nr integer :: yyyymmdd, hhmiss, eomday - real(kind=8) :: juldatei, juldatef, jd + real(kind=8) :: juldatei, juldatef, jd, dt call getarg(1,settings_files) if (settings_files == '') then @@ -60,14 +60,27 @@ program grid_to_ncdf jd = juldatei do while ( jd.le.juldatef ) call caldate(jd, yyyymmdd, hhmiss) - write(adate,'(I6.6)') yyyymmdd/100 - call grid_convert(files, config, nr, adate) - eomday = calceomday(yyyymmdd/100) - jd = jd + dble(eomday) + select case ( config%intype ) + case (0) + ! grid_time + call grid_convert(files, config, nr, yyyymmdd) + case (1) + ! grid_initial + call init_convert(files, config, nr, yyyymmdd) + case (2) + ! factor + call factor_convert(files, config, nr, yyyymmdd) + endselect + if ( trim(config%filefreq).eq.'month' ) then + eomday = calceomday(yyyymmdd/100) + dt = dble(eomday) + else if ( trim(config%filefreq).eq.'day' ) then + dt = 1d0 + endif + jd = jd + dt end do end do - end program diff --git a/grid_to_ncdf/init_convert.f90 b/grid_to_ncdf/init_convert.f90 new file mode 100644 index 0000000..a57bfeb --- /dev/null +++ b/grid_to_ncdf/init_convert.f90 @@ -0,0 +1,146 @@ +!--------------------------------------------------------------------------------------- +! GRID_TO_NCDF: init_convert +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> init_convert +!! +!! Purpose: +!! +!--------------------------------------------------------------------------------------- + +subroutine init_convert(files, config, nr, timestamp) + + use mod_var + use mod_settings + use mod_flexpart + use mod_dates + + implicit none + + type (files_t), intent(in out) :: files + type (config_t), intent(in) :: config + integer, intent(in) :: nr + integer, intent(in) :: timestamp + + character(max_path_len) :: filename, filedates, path_flexpart, path_output + character(len=max_path_len), dimension(:), allocatable :: filelist + character(len=8) :: adate + character(len=6) :: atime + character(len=3) :: anspec + logical :: lexist + integer :: nfiles, i, ierr, n + integer :: jjjjmmdd, hhmiss + real, dimension(:,:,:), allocatable :: grid + real(kind=8), dimension(:), allocatable :: levels + real(kind=8) :: jdate + integer :: numpoint + integer :: ibdate, ibtime + integer, dimension(maxpoint) :: releases + real :: bndx, bndy, delx, dely + integer :: numx, numy + + ! list all grid files in directory + if ( trim(config%filefreq).eq.'month' ) then + write(adate,'(I6.6)') timestamp/100 + print*, adate + else if ( trim(config%filefreq).eq.'day' ) then + write(adate,'(I8.8)') timestamp + endif + path_flexpart = trim(files%path_flexpart)//trim(recname(nr))//'/'//trim(adate)//'/' + path_output = trim(files%path_output)//trim(recname(nr))//'/' + print*, path_flexpart + print*, path_output + call system('mkdir '//trim(path_output)) + call system('ls '//trim(path_flexpart)//' | grep grid_initial_ | wc -l > '//trim(path_output)//'files.txt') + call system('ls '//trim(path_flexpart)//' | grep grid_initial_ >> '//trim(path_output)//'files.txt') + + ! read file list + open(100,file=trim(path_output)//'files.txt',status="old",action="read",iostat=ierr) + if ( ierr.ne.0 ) then + print*, 'ERROR: problem read files.txt' + stop + endif + read(100,*) nfiles + allocate( filelist(nfiles) ) + do i = 1, nfiles + read(100,fmt='(A)',iostat=ierr) filelist(i) + if (ierr.ne.0) exit + end do + close(100) + + ! read header for this month to get ngrid + filename = trim(path_flexpart)//'header' + inquire(file=trim(filename),exist=lexist) + if ( .not.lexist ) then + print*, 'ERROR: cannot find flexpart header' + stop + endif + print*, 'Reading flexpart header: '//trim(filename) + call read_header(filename, numpoint, ibdate, ibtime, releases, & + numx, numy, bndx, bndy, delx, dely) + print*, 'numx, numy, bndx, bndy, delx, dely = ',numx,numy,bndx,bndy,delx,dely + allocate( grid(nxgrid,nygrid,nzgrid) ) + allocate( levels(nzgrid) ) + ! trick to use write_ncdf also for grid_initial + ngrid = nzgrid + levels(:) = dble(outheight(1:nzgrid)) + + ! loop over files + filedates = trim(path_flexpart)//'dates' + do i = 1, nfiles + ! read binary grid files + adate = filelist(i)(14:21) + if ( trim(config%filefreq).eq.'month' ) then + atime = filelist(i)(22:27) + else if ( trim(config%filefreq).eq.'day' ) then + ! assume satellite -> get number of retrieval + atime = filelist(i)(23:28) + endif + print*, 'adate = ',adate + print*, 'atime = ',atime + read(adate,*) jjjjmmdd + read(atime,*) hhmiss + print*, 'jjjjmmdd = ',jjjjmmdd + print*, 'hhmiss = ',hhmiss + if ( config%linversionout ) then + jdate = juldate(jjjjmmdd, hhmiss) + else + jdate = juldate(datei,timei) + endif +! print*, 'jdate = ',jdate + filename = trim(path_flexpart)//trim(filelist(i)) + if ( config%linversionout ) then + call read_init(filename, grid) + else + call read_init_std(filename, grid) + endif + ! write to ncdf + if ( trim(config%filefreq).eq.'month' ) then + filename = trim(path_output)//'grid_initial_'//adate//atime//'_001.nc' + else if ( trim(config%filefreq).eq.'day' ) then + ! assume satellite + filename = trim(path_output)//'grid_initial_'//adate//'_'//atime//'.nc' + endif + call write_ncdf(config, filename, levels, grid) + end do + + deallocate( grid ) + deallocate( levels ) + +end subroutine init_convert + diff --git a/grid_to_ncdf/initialize.f90 b/grid_to_ncdf/initialize.f90 index d71b8e3..ab0df23 100644 --- a/grid_to_ncdf/initialize.f90 +++ b/grid_to_ncdf/initialize.f90 @@ -38,6 +38,7 @@ subroutine initialize(files, config) logical :: lexist integer :: yyyymm character(len=6) :: adate + character(len=8) :: areldate integer :: numpoint integer :: ibdate, ibtime integer, dimension(maxpoint) :: releases @@ -48,19 +49,39 @@ subroutine initialize(files, config) ! read list of receptors ! ---------------------- - filename = trim(files%file_recept) - call read_reclist(filename) + if ( trim(config%filefreq).eq.'month' ) then + filename = trim(files%file_recept) + call read_reclist(filename) + else + ! assume satellite retrievals -> no reclist + nrec = 1 + allocate ( recname(nrec) ) + recname(1) = '' + endif ! initialize flexpart variables ! ----------------------------- yyyymm=config%datei/100 write(adate,'(i6)') yyyymm + write(areldate,'(i8)') config%datei lexist = .false. i = 1 do while ( (.not.lexist).and.(i.le.nrec) ) - if ( .not.config%lnested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' - if ( config%lnested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' + if ( (.not.config%lnested).or.config%intype.eq.1 ) then + if ( trim(config%filefreq).eq.'month' ) then + filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' + else + filename = trim(files%path_flexpart)//trim(areldate)//'/header' + endif + else if ( config%lnested ) then + if ( trim(config%filefreq).eq.'month' ) then + filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' + else + filename = trim(files%path_flexpart)//trim(areldate)//'/header_nest' + endif + endif + print*, filename inquire(file=trim(filename),exist=lexist) i = i + 1 end do @@ -91,6 +112,7 @@ subroutine initialize(files, config) print*, 'trajdays = ',trajdays print*, 'ngrid = ',ngrid print*, 'nxshift = ',nxshift + print*, 'nxgrid, nygrid, nzgrid = ',nxgrid,nygrid,nzgrid ! initialize lon and lat dimensions if ( .not.allocated(glon) ) allocate( glon(nxgrid) ) diff --git a/grid_to_ncdf/makefile b/grid_to_ncdf/makefile index b47ea6a..75feccc 100644 --- a/grid_to_ncdf/makefile +++ b/grid_to_ncdf/makefile @@ -17,6 +17,8 @@ SRCS = mod_var.f90 \ mod_flexpart.f90 \ initialize.f90 \ grid_convert.f90 \ + init_convert.f90 \ + factor_convert.f90 \ write_ncdf.f90 \ read_reclist.f90 \ grid_to_ncdf.f90 diff --git a/grid_to_ncdf/mod_flexpart.f90 b/grid_to_ncdf/mod_flexpart.f90 index bd5acb8..0480e61 100644 --- a/grid_to_ncdf/mod_flexpart.f90 +++ b/grid_to_ncdf/mod_flexpart.f90 @@ -33,7 +33,7 @@ module mod_flexpart implicit none private - public :: read_header, read_grid, read_init, & + public :: read_header, read_grid, read_init, read_factor, & read_grid_std, read_init_std, get_nread, read_dates contains @@ -179,7 +179,7 @@ module mod_flexpart real(kind=8), dimension(ngrid), intent(in out) :: gtime real, parameter :: scaleconc=1.e12 - real, parameter :: smallnum=1.e-38 + real, parameter :: smallnum=tiny(0.) logical :: lexist integer :: ierr integer :: nread @@ -190,7 +190,7 @@ module mod_flexpart real, dimension(numx*numy) :: sparse_dump_r real, dimension(:,:,:), allocatable :: work integer(kind=4), dimension(:), allocatable :: times - real(kind=4), dimension(:), allocatable :: dates + real(kind=8), dimension(:), allocatable :: dates real(kind=8) :: jdrel real(kind=8), dimension(:), allocatable :: jdtime @@ -215,6 +215,7 @@ module mod_flexpart ! open grid_time file inquire ( file=trim(filename),exist=lexist ) + print*, filename if ( .not.lexist ) then write(*,*) 'WARNING: cannot find '//trim(filename) go to 10 @@ -226,6 +227,7 @@ module mod_flexpart endif ! read footprints + n = 0 do nt = 1, ngrid read(100,iostat=ierr,end=20) jjjjmmdd read(100,iostat=ierr,end=20) hhmiss @@ -311,7 +313,7 @@ module mod_flexpart real(kind=8), dimension(ngrid), intent(in out) :: gtime real, parameter :: scaleconc=1.e12 - real, parameter :: smallnum=1.e-38 + real, parameter :: smallnum=tiny(0.) logical :: lexist integer :: ierr integer :: sp_count_i, sp_count_r @@ -414,12 +416,13 @@ module mod_flexpart real, dimension(nxgrid,nygrid,nzgrid), intent(in out) :: gridinit real, parameter :: scaleconc=1.e12 - real, parameter :: smallnum=1.e-38 + real, parameter :: smallnum=tiny(0.) logical :: lexist integer :: ierr integer :: sp_count_i, sp_count_r integer :: jjjjmmdd, hhmiss - integer :: ii, ir, fact, n, nt, jy, ix, kz + integer :: ii, ir, n, nt, jy, ix, kz + real :: fact integer, dimension(nxgrid*nygrid*nzgrid) :: sparse_dump_i real, dimension(nxgrid*nygrid*nzgrid) :: sparse_dump_r @@ -439,7 +442,7 @@ module mod_flexpart read(100) jjjjmmdd read(100) hhmiss - fact = 1 + fact = 1. read(100) sp_count_i read(100) (sparse_dump_i(ix), ix=1, sp_count_i) read(100) sp_count_r @@ -459,6 +462,9 @@ module mod_flexpart gridinit(ix+1,jy+1,kz) = abs(sparse_dump_r(ir))*scaleconc end do + ! shift grid by xshift along longitudinal dimension + gridinit = cshift(gridinit, shift=-1*nxshift, dim=1) + close(100) 10 continue @@ -552,7 +558,7 @@ module mod_flexpart character(len=max_path_len), intent(in) :: filename integer, intent(in) :: nread - real(kind=4), dimension(nread), intent(in out) :: dates + real(kind=8), dimension(nread), intent(in out) :: dates character(len=14) :: tmp integer :: ierr, n @@ -574,6 +580,80 @@ module mod_flexpart end subroutine read_dates ! -------------------------------------------------- + ! read_factor + ! -------------------------------------------------- + !> read_factor + !! + !! Purpose: Reads the correction factor (i.e. ratio + !! of the wet to dry air density for + !! correcting mixing ratios for dry air). + !! + ! -------------------------------------------------- + + subroutine read_factor(filename, nread, numx, numy, xshift, factor) + + use mod_var + + implicit none + + character(len=max_path_len), intent(in) :: filename + integer, intent(in) :: nread + integer, intent(in) :: numx, numy, xshift + real, dimension(numx,numy,nread), intent(in out) :: factor + + real, parameter :: smallnum=tiny(0.) + logical :: lexist + integer :: ierr + integer :: sp_count_i, sp_count_r + integer :: ii, ir, n, nt, jy, ix + real :: fact + integer, dimension(numx*numy) :: sparse_dump_i + real, dimension(numx*numy) :: sparse_dump_r + + ! open factor file + inquire ( file=trim(filename),exist=lexist ) + if ( .not.lexist ) then + write(*,*) 'WARNING: cannot find '//trim(filename) + go to 10 + endif + open(100,file=trim(filename),form='unformatted',action='read',status='old',iostat=ierr) + if( ierr.ne.0 ) then + write(*,*) 'WARNING: cannot read '//trim(filename) + go to 10 + endif + + ! read footprints + do nt = 1, nread + fact = 1. + read(100) sp_count_i + read(100) (sparse_dump_i(ix), ix=1,sp_count_i) + read(100) sp_count_r + read(100) (sparse_dump_r(ix), ix=1,sp_count_r) + ii = 0 + do ir = 1, sp_count_r + if ((sparse_dump_r(ir)*fact).gt.smallnum) then + ii = ii + 1 + n = sparse_dump_i(ii) + fact = fact*(-1.) + else + n = n + 1 + endif + jy = (n - numx * numy)/numx + ix = n - numx * numy - numx * jy + factor(ix+1,jy+1,nt) = abs(sparse_dump_r(ir)) + end do + end do + + close(100) + + ! shift grid by xshift along longitudinal dimension + factor = cshift(factor, shift=-1*xshift, dim=1) + + 10 continue + + end subroutine read_factor + + ! -------------------------------------------------- end module mod_flexpart diff --git a/grid_to_ncdf/mod_settings.f90 b/grid_to_ncdf/mod_settings.f90 index 6a897d2..ef6bf34 100644 --- a/grid_to_ncdf/mod_settings.f90 +++ b/grid_to_ncdf/mod_settings.f90 @@ -48,6 +48,8 @@ module mod_settings integer :: datef ! end date (yyyymmdd) logical :: lnested ! use nested flexpart output (true or false) logical :: linversionout ! inversion formatted flexpart output (true or false) + integer :: intype ! 0 = grid_time, 1 = grid_initial, 2 = factor for dryair + character(len=6) :: filefreq ! frequency of file directories end type config_t @@ -288,12 +290,18 @@ module mod_settings identifier = "datef:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) config%datef = cn + identifier = "filefreq:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%filefreq = cc identifier = "lnested:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) config%lnested = cl identifier = "linversionout:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) config%linversionout = cl + identifier = "intype:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%intype = cn endif diff --git a/grid_to_ncdf/write_ncdf.f90 b/grid_to_ncdf/write_ncdf.f90 index 4bf72ca..8e4c72e 100644 --- a/grid_to_ncdf/write_ncdf.f90 +++ b/grid_to_ncdf/write_ncdf.f90 @@ -32,13 +32,15 @@ !! !--------------------------------------------------------------------------------------- -subroutine write_ncdf(filename, time, grid) +subroutine write_ncdf(config, filename, time, grid) use mod_var + use mod_settings use netcdf implicit none + type (config_t), intent(in) :: config character(len=max_path_len), intent(in) :: filename real(kind=8), dimension(ngrid), intent(in) :: time real, dimension(nxgrid,nygrid,ngrid), intent(in) :: grid @@ -51,9 +53,21 @@ subroutine write_ncdf(filename, time, grid) ! variable and dimension names lonname = 'longitude' latname = 'latitude' - timename = 'time' + select case ( config%intype ) + case(0) + ! grid time + timename = 'time' + varunit = 'sm3/kg' + case (1) + ! grid initial + timename = 'height' + varunit = 'kg/kg' + case (2) + ! factor + timename = 'time' + varunit = 'kg/kg' + endselect varname = 'grid' - varunit = 'sm3/kg' ! open netcdf file print*, 'Writing file: ',trim(filename) diff --git a/prep_flexpart/README_prepflex.txt b/prep_flexpart/README_prepflex.txt index 0292831..aa71b4c 100644 --- a/prep_flexpart/README_prepflex.txt +++ b/prep_flexpart/README_prepflex.txt @@ -1,6 +1,6 @@ ================================================================ - FLEXPART PRE-PROCESSOR + FLEXPART pre-processor ================================================================ diff --git a/prep_flexpart/SETTINGS_co2 b/prep_flexpart/SETTINGS similarity index 89% rename from prep_flexpart/SETTINGS_co2 rename to prep_flexpart/SETTINGS index 66f1501..ad393e6 100644 --- a/prep_flexpart/SETTINGS_co2 +++ b/prep_flexpart/SETTINGS @@ -9,26 +9,26 @@ # Path and file settings # flexpart source path -path_flexpart: /mypath/FLEXPART/ +path_flexpart: /mypath/flexpart/ # path where to write options folder (root only, recname and month are appended) -path_options: /mypath/TEST_INPUT/FLEXPART/CO2/NO_NEST/ +path_options: /mypath/flexpart_options/ # path where to write flexpart output (root only, recname and month are appended) -path_output: /mypath/TEST_OUTPUT/FLEXOUT/CO2/NO_NEST/ +path_output: /mypath/flexpart_output/ # path to OH fields (if use OH chemistry) -path_ohfield: /mypath_to_ohfields/ +path_ohfield: /mypath/ohfields/ # path to observations -path_obs: /mypath/TEST_INPUT/OBS/CO2/ +path_obs: /mypath/obs/ # path where to write observation output -path_obsout: /mypath/TEST_OUTPUT/OBS/CO2/ +path_obsout: /mypath/obs_out/ # observation file format (one of obspack, wdcgg, noaa, basic, icos) obsformat: obspack # observation file suffix suffix: .txt # receptor list file -file_recept: /mypath/TEST_INPUT/reclist_co2.txt +file_recept: /mypath/input/reclist.txt # AVAILABLE files -file_avail: /mypath_to_available_file/AVAILABLE -file_availnest: /mypath_to_available_file/AVAILABLE +file_avail: /mypath/available_files/AVAILABLE +file_availnest: /mypath/available_files/AVAILABLE # General settings # make flexpart releases only when there are observations (1) or at regular intervals (0) diff --git a/prep_flexpart/SETTINGS_co2_nest b/prep_flexpart/SETTINGS_co2_nest deleted file mode 100644 index 66b4251..0000000 --- a/prep_flexpart/SETTINGS_co2_nest +++ /dev/null @@ -1,113 +0,0 @@ -# ================================================== -# -# FLEXPART RUN SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Path and file settings -# flexpart source path -path_flexpart: /mypath/FLEXPART/ -# path where to write options folder (root only, recname and month are appended) -path_options: /mypath/TEST_INPUT/FLEXPART/CO2/NEST/ -# path where to write flexpart output (root only, recname and month are appended) -path_output: /mypath/TEST_OUTPUT/FLEXOUT/CO2/NEST/ -# path to OH fields (if use OH chemistry) -path_ohfield: /mypath_to_ohfields/ -# path to observations -path_obs: /mypath/TEST_INPUT/OBS/CO2/ -# path where to write observation output -path_obsout: /mypath/TEST_OUTPUT/OBS/CO2/ -# observation file format (one of obspack, wdcgg, noaa, basic, icos) -obsformat: obspack -# observation file suffix -suffix: .txt -# receptor list file -file_recept: /mypath/TEST_INPUT/reclist_co2.txt -# AVAILABLE files -file_avail: /mypath_to_available_file/AVAILABLE -file_availnest: /mypath_to_available_file/AVAILABLE - -# General settings -# make flexpart releases only when there are observations (1) or at regular intervals (0) -# important: if set lrelease = 0 observations have to be from fixed locations (not aircraft, ship, satellite) -lrelease: 0 -# if regular flexpart releases (lrelease = 0) set frequency (hours) -relfreq: 1 -# select afternoon observations (night for mountains) (select = 1, no selection = 0) -lselect: 1 -# average observations (average = 1, no average = 0) -laverage: 0 -# averaging interval if laverage = 1 (hours) -intaverage: 0 -# flexpart file format (namelist = 1, ascii = 0) -lnamelist: 1 - -# COMMAND settings -# start date of simulation (yyyymmdd) -datei: 20120101 -# end date of simulation (yyyymmdd) -datef: 20120201 -# output rate (secs) -outrate: 10800 -# time average of output (secs) -outaverage: 10800 -# sampling rate of output (secs) -outsample: 1800 -# (mass unit = 1, mixing ratio unit = 2) -ind_source: 1 -# (mass unit = 1, mixing ratio unit = 2) -ind_receptor: 2 -# use nested output (0 = no, 1 = yes) -lnested: 1 -# output sensitivity to initial conditions (for backward runs, 0 = none, 1 = mass unit, 2 = mass mixing ratio) -linit_cond: 2 - -# OUTGRID settings -# longitude of lower left corner of output grid -outlonleft: -179.00 -# latitude of lower left corner of output grid -outlatlower: -90.00 -# number of longitudinal grid cells in output grid -numxgrid: 180 -# number of latitudinal grid cells in output grid -numygrid: 90 -# longitudinal resolution in output grid -dxout: 2.0 -# latitudinal resolution in outout grid -dyout: 2.0 -# longitude of lower left corner of nested grid (used if nested_output = 1) -outlonnest: -15.0 -# latitude of lower left corner of nested grid (used if nested_output = 1) -outlatnest: 30.0 -# number of longitudinal grid cells in nested grid (used if nested_output = 1) -numxnest: 100.0 -# number of latitudinal grid cells in nested grid (used if nested_output = 1) -numynest: 90.0 -# longitudinal resolution in nested grid (used if nested_output = 1) -dxoutnest: 0.5 -# latitudinal resolution in nested grid (used if nested_output = 1) -dyoutnest: 0.5 -# comma-separated list of vertical levels in output grid (meters) -zlevel: 100,500,1000,2000,3000,5000,7000,9000,12000,15000,20000,50000 - -# RELEASES settings -# species (see: /options/SPECIES/spec_overview) -species: CO2 -# start date of releases (yyyymmdd) -reldatei: 20120101 -#end date of releases (yyyymmdd) -reldatef: 20120201 -# number of particles per grid cell -npart: 10000 -# release heights relative to (1 = ground, 2 = sea level) -zref: 2 -# orography inputfile (if zref = 1) -file_orog: none - -# AGECLASS settings -# ageclass (sec) -ageclass: 259200 - diff --git a/prep_flexpart/SETTINGS_ghg b/prep_flexpart/SETTINGS_ghg deleted file mode 100644 index 42d8263..0000000 --- a/prep_flexpart/SETTINGS_ghg +++ /dev/null @@ -1,113 +0,0 @@ -# ================================================== -# -# FLEXPART RUN SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Path and file settings -# flexpart source path -path_flexpart: /mypath/FLEXPART/ -# path where to write options folder (root only, recname and month are appended) -path_options: /mypath/TEST_INPUT/FLEXPART/GHG/NO_NEST/ -# path where to write flexpart output (root only, recname and month are appended) -path_output: /mypath/TEST_OUTPUT/FLEXOUT/GHG/NO_NEST/ -# path to OH fields (if use OH chemistry) -path_ohfield: /mypath_to_ohfields/ -# path to observations -path_obs: /mypath/TEST_INPUT/OBS/GHG/ -# path where to write observation output -path_obsout: /mypath/TEST_OUTPUT/OBS/GHG/ -# observation file format (one of obspack, wdcgg, noaa, basic, icos) -obsformat: wdcgg -# observation file suffix -suffix: .dat -# receptor list file -file_recept: /mypath/TEST_INPUT/reclist_ghg.txt -# AVAILABLE files -file_avail: /mypath_to_available_file/AVAILABLE -file_availnest: /mypath_to_available_file/AVAILABLE - -# General settings -# make flexpart releases only when there are observations (1) or at regular intervals (0) -# important: if set lrelease = 0 observations have to be from fixed locations (not aircraft, ship, satellite) -lrelease: 0 -# if regular flexpart releases (lrelease = 0) set frequency (hours) -relfreq: 1 -# select afternoon observations (night for mountains) (select = 1, no selection = 0) -lselect: 0 -# average observations (average = 1, no average = 0) -laverage: 0 -# averaging interval if laverage = 1 (hours) -intaverage: 1 -# flexpart file format (namelist = 1, ascii = 0) -lnamelist: 1 - -# COMMAND settings -# start date of simulation (yyyymmdd) -datei: 20120101 -# end date of simulation (yyyymmdd) -datef: 20120201 -# output rate (secs) -outrate: 86400 -# time average of output (secs) -outaverage: 86400 -# sampling rate of output (secs) -outsample: 1800 -# (mass unit = 1, mixing ratio unit = 2) -ind_source: 1 -# (mass unit = 1, mixing ratio unit = 2) -ind_receptor: 2 -# use nested output (0 = no, 1 = yes) -lnested: 0 -# output sensitivity to initial conditions (for backward runs, 0 = none, 1 = mass unit, 2 = mass mixing ratio) -linit_cond: 2 - -# OUTGRID settings -# longitude of lower left corner of output grid -outlonleft: -179.00 -# latitude of lower left corner of output grid -outlatlower: -90.00 -# number of longitudinal grid cells in output grid -numxgrid: 360 -# number of latitudinal grid cells in output grid -numygrid: 180 -# longitudinal resolution in output grid -dxout: 1.0 -# latitudinal resolution in outout grid -dyout: 1.0 -# longitude of lower left corner of nested grid (used if nested_output = 1) -outlonnest: -15.0 -# latitude of lower left corner of nested grid (used if nested_output = 1) -outlatnest: 30.0 -# number of longitudinal grid cells in nested grid (used if nested_output = 1) -numxnest: 100.0 -# number of latitudinal grid cells in nested grid (used if nested_output = 1) -numynest: 90.0 -# longitudinal resolution in nested grid (used if nested_output = 1) -dxoutnest: 0.5 -# latitudinal resolution in nested grid (used if nested_output = 1) -dyoutnest: 0.5 -# comma-separated list of vertical levels in output grid (meters) -zlevel: 100,500,1000,2000,3000,5000,7000,9000,12000,15000,20000,50000 - -# RELEASES settings -# species (see: /options/SPECIES/spec_overview) -species: CH4 -# start date of releases (yyyymmdd) -reldatei: 20120101 -#end date of releases (yyyymmdd) -reldatef: 20120201 -# number of particles per grid cell -npart: 10000 -# release heights relative to (1 = ground, 2 = sea level) -zref: 2 -# orography inputfile (if zref = 1) -file_orog: none - -# AGECLASS settings -# ageclass (sec) -ageclass: 432000 - diff --git a/prep_flexpart/SETTINGS_ghg_nest b/prep_flexpart/SETTINGS_ghg_nest deleted file mode 100644 index ae52bc0..0000000 --- a/prep_flexpart/SETTINGS_ghg_nest +++ /dev/null @@ -1,113 +0,0 @@ -# ================================================== -# -# FLEXPART RUN SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Path and file settings -# flexpart source path -path_flexpart: /mypath/FLEXPART/ -# path where to write options folder (root only, recname and month are appended) -path_options: /mypath/TEST_INPUT/FLEXPART/GHG/NEST/ -# path where to write flexpart output (root only, recname and month are appended) -path_output: /mypath/TEST_OUTPUT/FLEXOUT/GHG/NEST/ -# path to OH fields (if use OH chemistry) -path_ohfield: /mypath_to_ohfields/ -# path to observations -path_obs: /mypath/TEST_INPUT/OBS/GHG/ -# path where to write observation output -path_obsout: /mypath/TEST_OUTPUT/OBS/GHG/ -# observation file format (one of obspack, wdcgg, noaa, icos) -obsformat: noaa -# observation file suffix -suffix: event.txt -# receptor list file -file_recept: /mypath/TEST_INPUT/reclist_ghg.txt -# AVAILABLE files -file_avail: /mypath_to_available_file/AVAILABLE -file_availnest: /mypath_to_available_file/AVAILABLE - -# General settings -# make flexpart releases only when there are observations (1) or at regular intervals (0) -# important: if set lrelease = 0 observations have to be from fixed locations (not aircraft, ship, satellite) -lrelease: 0 -# if regular flexpart releases (lrelease = 0) set frequency (hours) -relfreq: 1 -# select afternoon observations (night for mountains) (select = 1, no selection = 0) -lselect: 0 -# average observations (average = 1, no average = 0) -laverage: 0 -# averaging interval if laverage = 1 (hours) -intaverage: 1 -# flexpart file format (namelist = 1, ascii = 0) -lnamelist: 1 - -# COMMAND settings -# start date of simulation (yyyymmdd) -datei: 20120101 -# end date of simulation (yyyymmdd) -datef: 20120201 -# output rate (secs) -outrate: 86400 -# time average of output (secs) -outaverage: 86400 -# sampling rate of output (secs) -outsample: 1800 -# (mass unit = 1, mixing ratio unit = 2) -ind_source: 1 -# (mass unit = 1, mixing ratio unit = 2) -ind_receptor: 2 -# use nested output (0 = no, 1 = yes) -lnested: 1 -# output sensitivity to initial conditions (for backward runs, 0 = none, 1 = mass unit, 2 = mass mixing ratio) -linit_cond: 2 - -# OUTGRID settings -# longitude of lower left corner of output grid -outlonleft: -179.00 -# latitude of lower left corner of output grid -outlatlower: -90.00 -# number of longitudinal grid cells in output grid -numxgrid: 180 -# number of latitudinal grid cells in output grid -numygrid: 90 -# longitudinal resolution in output grid -dxout: 2.0 -# latitudinal resolution in outout grid -dyout: 2.0 -# longitude of lower left corner of nested grid (used if nested_output = 1) -outlonnest: -15.0 -# latitude of lower left corner of nested grid (used if nested_output = 1) -outlatnest: 30.0 -# number of longitudinal grid cells in nested grid (used if nested_output = 1) -numxnest: 100.0 -# number of latitudinal grid cells in nested grid (used if nested_output = 1) -numynest: 90.0 -# longitudinal resolution in nested grid (used if nested_output = 1) -dxoutnest: 0.5 -# latitudinal resolution in nested grid (used if nested_output = 1) -dyoutnest: 0.5 -# comma-separated list of vertical levels in output grid (meters) -zlevel: 100,500,1000,2000,3000,5000,7000,9000,12000,15000,20000,50000 - -# RELEASES settings -# species (see: /options/SPECIES/spec_overview) -species: CH4 -# start date of releases (yyyymmdd) -reldatei: 20120101 -#end date of releases (yyyymmdd) -reldatef: 20120201 -# number of particles per grid cell -npart: 10000 -# release heights relative to (1 = ground, 2 = sea level) -zref: 2 -# orography inputfile (if zref = 1) -file_orog: none - -# AGECLASS settings -# ageclass (sec) -ageclass: 432000 - diff --git a/prep_flexpart/job_prep_flexpart.sh b/prep_flexpart/job_prep_flexpart.sh index a8c6d91..cd3a6bd 100755 --- a/prep_flexpart/job_prep_flexpart.sh +++ b/prep_flexpart/job_prep_flexpart.sh @@ -1,6 +1,6 @@ #!/bin/bash #--------------------------------------------------- -file='./SETTINGS_ghg_nest' +file='./SETTINGS' ./prep_flexpart ${file} diff --git a/prep_flexpart/list_obsfiles.f90 b/prep_flexpart/list_obsfiles.f90 index fa48a1b..ce62464 100644 --- a/prep_flexpart/list_obsfiles.f90 +++ b/prep_flexpart/list_obsfiles.f90 @@ -36,14 +36,6 @@ subroutine list_obsfiles(settings) ! list observation files ! ---------------------- - call system('rm -f '//trim(settings%path_obsout)//'obsfiles.txt') - - ! use find to descend into subdirectories (filelist then contains file path and requires editting read_xxx.f90) -! call system ('find ' //trim(settings%path_obs)// ' -not -path "*/\.*" -type f \( -iname "' //'*'//trim(settings%suffix)// & -! '" \) | wc -l > '//trim(settings%path_obsout)//'obsfiles.txt') -! call system ('find ' //trim(settings%path_obs)// ' -not -path "*/\.*" -type f \( -iname "' //'*'//trim(settings%suffix)// & -! '" \) >> '//trim(settings%path_obsout)//'obsfiles.txt') - call system('rm -f '//trim(settings%path_obsout)//'obsfiles.txt') call system('ls '//trim(settings%path_obs)//'*'//trim(settings%suffix)//' | wc -l > '//trim(settings%path_obsout)//'obsfiles.txt') call system('ls '//trim(settings%path_obs)//' | grep '//trim(settings%suffix)//' >> '//trim(settings%path_obsout)//'obsfiles.txt') diff --git a/prep_flexpart/mod_settings.f90 b/prep_flexpart/mod_settings.f90 index f6ea9a6..5b740c5 100644 --- a/prep_flexpart/mod_settings.f90 +++ b/prep_flexpart/mod_settings.f90 @@ -45,6 +45,7 @@ module mod_settings character(len=max_path_len) :: file_recept character(len=max_path_len) :: file_avail character(len=max_path_len) :: file_availnest + character(len=max_name_len) :: windfield character(len=max_name_len) :: obsformat character(len=max_name_len) :: suffix @@ -344,6 +345,9 @@ module mod_settings identifier = "file_availnest:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) settings%file_availnest = cc + identifier = "windfield:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%windfield = cc ! General settings identifier = "lselect:" diff --git a/prep_flexpart/mod_var.f90 b/prep_flexpart/mod_var.f90 index 8cd4fa0..05fbe5d 100644 --- a/prep_flexpart/mod_var.f90 +++ b/prep_flexpart/mod_var.f90 @@ -31,11 +31,12 @@ module mod_var integer, parameter :: max_path_len=200 integer, parameter :: max_name_len=100 integer, parameter :: maxobs=5000 + integer, parameter :: recname_len=3 real(kind=8) :: jdatei, jdatef real(kind=8) :: jreldatei, jreldatef integer :: nrec integer :: nfiles - character(len=3), dimension(:), allocatable :: recname + character(len=recname_len), dimension(:), allocatable :: recname character(len=256), dimension(:), allocatable :: filelist real, dimension(:), allocatable :: reclon, reclat, recalt diff --git a/prep_flexpart/prep_outgrid.f90 b/prep_flexpart/prep_outgrid.f90 index 4a54ef1..4be6291 100644 --- a/prep_flexpart/prep_outgrid.f90 +++ b/prep_flexpart/prep_outgrid.f90 @@ -79,6 +79,17 @@ subroutine prep_outgrid(settings, jd, nr) ! preset namelist variables outlon0 = settings%outlonleft + ! correction for FLEXPART windfields + if ( settings%outlonleft.le.-180. ) then + if ( settings%windfield.eq.'oper' ) then + outlon0 = -179. + else if ( settings%windfield.eq.'era5' ) then + outlon0 = -179.5 + else + write(*,*) 'ERROR: prep_outgrid: unknown windfield type' + stop + endif + endif outlat0 = settings%outlatlower numxgrid = settings%numxgrid numygrid = settings%numygrid diff --git a/prep_flexpart/process_obs.f90 b/prep_flexpart/process_obs.f90 index 81fe389..b09f559 100644 --- a/prep_flexpart/process_obs.f90 +++ b/prep_flexpart/process_obs.f90 @@ -112,7 +112,7 @@ subroutine process_obs(settings, nobs, freq, jdobs, latobs, lonobs, altobs, conc do while( jdate.le.jdobs(nobs) ) jdt = dnint(jdate*1.e6)/1.e6 if ( n.ne.nprev ) then - if ( settings%intaverage.ge.6. ) then + if ( settings%intaverage.ge.2. ) then if ( n.eq.1 ) then ! if long averaging interval start at 00:00 jdatestart = dint(jdate) @@ -125,7 +125,7 @@ subroutine process_obs(settings, nobs, freq, jdobs, latobs, lonobs, altobs, conc jdatestart = jdt endif endif - if( n.eq.nprev .and. settings%intaverage.ge.6. ) then + if( n.eq.nprev .and. settings%intaverage.ge.2. ) then ! if no observation in last averaging interval (i.e. did not enter while loop below) ! increment start date print*, 'incrementing start date' diff --git a/prep_flexpart/read_basic.f90 b/prep_flexpart/read_basic.f90 index efbf336..fc0c5a2 100644 --- a/prep_flexpart/read_basic.f90 +++ b/prep_flexpart/read_basic.f90 @@ -60,6 +60,7 @@ subroutine read_basic(settings, jd, nr, nobs, obs) character(len=max_path_len) :: file_out character(len=max_path_len) :: file_obs + character(len=max_path_len) :: rowfmt character(len=200) :: line character(len=200), dimension(50) :: args character(len=3) :: flag @@ -202,9 +203,10 @@ subroutine read_basic(settings, jd, nr, nobs, obs) endif ! write observations to file + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)' do i = 1, nobs call caldate(obs%jdi(i), jjjjmmdd, hhmiss) - write(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)') & + write(100,fmt=rowfmt) & trim(recname(nr)), jjjjmmdd, hhmiss, obs%jdi(i), out_repr_time, obs%con(i), obs%err(i), obs%num(i) end do close(100) @@ -213,15 +215,16 @@ subroutine read_basic(settings, jd, nr, nobs, obs) print*, 'lat, lon , alt: ',obs%lat(1), obs%lon(1), obs%alt(1) file_out = trim(settings%path_obsout)//'stnlist_detail.txt' inquire(file=trim(file_out),exist=lexist) + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F7.2,1X,F7.2,1X,F7.1)' if( lexist ) then ! append to existing open(100,file=trim(file_out),status='old',action='write',access='append',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) else ! create new file open(100,file=trim(file_out),status='new',action='write',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) endif diff --git a/prep_flexpart/read_icos.f90 b/prep_flexpart/read_icos.f90 index afd049c..1368edd 100644 --- a/prep_flexpart/read_icos.f90 +++ b/prep_flexpart/read_icos.f90 @@ -60,6 +60,7 @@ subroutine read_icos(settings, jd, nr, nobs, obs) character(len=max_path_len) :: file_out character(len=max_path_len) :: file_obs + character(len=max_path_len) :: rowfmt character(len=200) :: line character(len=200), dimension(20) :: args character(len=1) :: flag @@ -137,6 +138,8 @@ subroutine read_icos(settings, jd, nr, nobs, obs) else if ( settings%zref.eq.2 ) then ! metres above sea level alt = alt + hgt + ! use adjusted altitude for model orography + alt = recalt(nr) endif print*, 'lat, lon, alt: ',lat, lon, alt @@ -225,9 +228,10 @@ subroutine read_icos(settings, jd, nr, nobs, obs) endif ! write observations to file + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)' do i = 1, nobs call caldate(obs%jdi(i), jjjjmmdd, hhmiss) - write(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)') & + write(100,fmt=rowfmt) & trim(recname(nr)), jjjjmmdd, hhmiss, obs%jdi(i), out_repr_time, obs%con(i), obs%err(i), obs%num(i) end do close(100) @@ -235,15 +239,16 @@ subroutine read_icos(settings, jd, nr, nobs, obs) ! write detailed station list file file_out = trim(settings%path_obsout)//'stnlist_detail.txt' inquire(file=trim(file_out),exist=lexist) + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F7.2,1X,F7.2,1X,F7.1)' if( lexist ) then ! append to existing open(100,file=trim(file_out),status='old',action='write',access='append',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) else ! create new file open(100,file=trim(file_out),status='new',action='write',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) endif diff --git a/prep_flexpart/read_noaa.f90 b/prep_flexpart/read_noaa.f90 index a375085..06f5943 100644 --- a/prep_flexpart/read_noaa.f90 +++ b/prep_flexpart/read_noaa.f90 @@ -60,6 +60,7 @@ subroutine read_noaa(settings, jd, nr, nobs, obs) character(len=max_path_len) :: file_out character(len=max_path_len) :: file_obs + character(len=max_path_len) :: rowfmt character(len=200) :: line character(len=200), dimension(50) :: args character(len=3) :: flag @@ -264,9 +265,10 @@ subroutine read_noaa(settings, jd, nr, nobs, obs) endif ! write observations to file + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)' do i = 1, nobs call caldate(obs%jdi(i), jjjjmmdd, hhmiss) - write(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)') & + write(100,fmt=rowfmt) & trim(recname(nr)), jjjjmmdd, hhmiss, obs%jdi(i), out_repr_time, obs%con(i), obs%err(i), obs%num(i) end do close(100) @@ -275,15 +277,16 @@ subroutine read_noaa(settings, jd, nr, nobs, obs) print*, 'lat, lon , alt: ',obs%lat(1), obs%lon(1), obs%alt(1) file_out = trim(settings%path_obsout)//'stnlist_detail.txt' inquire(file=trim(file_out),exist=lexist) + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F7.2,1X,F7.2,1X,F7.1)' if( lexist ) then ! append to existing open(100,file=trim(file_out),status='old',action='write',access='append',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) else ! create new file open(100,file=trim(file_out),status='new',action='write',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) endif diff --git a/prep_flexpart/read_obspack.f90 b/prep_flexpart/read_obspack.f90 index 3eee360..dc11b94 100644 --- a/prep_flexpart/read_obspack.f90 +++ b/prep_flexpart/read_obspack.f90 @@ -60,6 +60,7 @@ subroutine read_obspack(settings, jd, nr, nobs, obs) character(len=max_path_len) :: file_out character(len=max_path_len) :: file_obs + character(len=max_path_len) :: rowfmt character(len=200) :: line character(len=200), dimension(20) :: args character(len=4) :: version @@ -229,9 +230,10 @@ subroutine read_obspack(settings, jd, nr, nobs, obs) endif ! write observations to file + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)' do i = 1, nobs call caldate(obs%jdi(i), jjjjmmdd, hhmiss) - write(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)') & + write(100,fmt=rowfmt) & trim(recname(nr)), jjjjmmdd, hhmiss, obs%jdi(i), out_repr_time, obs%con(i), obs%err(i), obs%num(i) end do close(100) @@ -239,15 +241,16 @@ subroutine read_obspack(settings, jd, nr, nobs, obs) ! write detailed station list file file_out = trim(settings%path_obsout)//'stnlist_detail.txt' inquire(file=trim(file_out),exist=lexist) + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F7.2,1X,F7.2,1X,F7.1)' if( lexist ) then ! append to existing open(100,file=trim(file_out),status='old',action='write',access='append',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) else ! create new file open(100,file=trim(file_out),status='new',action='write',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) endif diff --git a/prep_flexpart/read_reclist.f90 b/prep_flexpart/read_reclist.f90 index 6494c33..e87a494 100644 --- a/prep_flexpart/read_reclist.f90 +++ b/prep_flexpart/read_reclist.f90 @@ -36,6 +36,7 @@ subroutine read_reclist(filename) character(len=max_path_len), intent(in) :: filename character(len=200) :: line + character(len=max_path_len) :: rowfmt integer :: ierr integer :: cnt @@ -69,8 +70,10 @@ subroutine read_reclist(filename) open(100,file=trim(filename),action='read',status='old',iostat=ierr) write(*,*) 'Receptors: ' +! write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F7.2,1X,F7.2,1X,F7.2)' + write(rowfmt,fmt='(A,I1,A)') '(A',3,',1X,F7.2,1X,F7.2,1X,F7.2)' do cnt = 1, nrec - read(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F7.2)') recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) + read(100,fmt=rowfmt) recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) print*, recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) end do diff --git a/prep_flexpart/read_wdcgg.f90 b/prep_flexpart/read_wdcgg.f90 index 47be546..48499d1 100644 --- a/prep_flexpart/read_wdcgg.f90 +++ b/prep_flexpart/read_wdcgg.f90 @@ -60,6 +60,7 @@ subroutine read_wdcgg(settings, jd, nr, nobs, obs) character(len=max_path_len) :: file_out character(len=max_path_len) :: file_obs + character(len=max_path_len) :: rowfmt character(len=200) :: line character(len=200), dimension(30) :: args character(len=10) :: adate @@ -214,9 +215,10 @@ subroutine read_wdcgg(settings, jd, nr, nobs, obs) endif ! write observations to file + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)' do i = 1, nobs call caldate(obs%jdi(i), jjjjmmdd, hhmiss) - write(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)') & + write(100,fmt=rowfmt) & trim(recname(nr)), jjjjmmdd, hhmiss, obs%jdi(i), out_repr_time, obs%con(i), obs%err(i), obs%num(i) end do close(100) @@ -224,15 +226,16 @@ subroutine read_wdcgg(settings, jd, nr, nobs, obs) ! write detailed station list file file_out = trim(settings%path_obsout)//'stnlist_detail.txt' inquire(file=trim(file_out),exist=lexist) + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F7.2,1X,F7.2,1X,F7.1)' if( lexist ) then ! append to existing open(100,file=trim(file_out),status='old',action='write',access='append',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) else ! create new file open(100,file=trim(file_out),status='new',action='write',iostat=ierr) - write(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F6.1)') trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) + write(100,fmt=rowfmt) trim(recname(nr)), obs%lat(1), obs%lon(1), obs%alt(1) close(100) endif diff --git a/prep_flexpart/run_flexpart.sh b/prep_flexpart/run_flexpart.sh index ad6f390..1d03b1b 100755 --- a/prep_flexpart/run_flexpart.sh +++ b/prep_flexpart/run_flexpart.sh @@ -6,8 +6,8 @@ # 1. edit the directories, exename, list of stations # months and year below # 2. for slurm set SLURM = 1 otherwise uses nohup -# 3. the meteorology used for flexpart can be -# specified by METEO +# 3. specific options for compiling flexpart given +# in variable COMMAND # 4. run the script: # ./run_flexpart.sh #--------------------------------------------------- @@ -21,13 +21,12 @@ ROOTDIR=${DIR:0:$LEN} # User settings SLURM=1 -#TIMELIM=2-00:00:00 TIMELIM=12:00:00 PARTITION="nilu" -EXENAME=FP_ecmwf_gfortran -METEO=ecmwf -DIRFLEX=/home/rthompson/REPOS/GITHUB/FLEXPART/ -DIROPTIONS=${ROOTDIR}TEST_INPUT/FLEXPART/GHG/NEST/ +EXENAME=FLEXPART +COMMAND="-j serial 'ncf=yes'" +DIRFLEX=/mypath/flexpart/ +DIROPTIONS=/mypath/flexpart_options/ STNLIST=(SSL) MONLIST=(01) YEAR=2012 @@ -46,7 +45,7 @@ ARRAYMON=`seq 1 ${NMON}` cd ${DIRFLEX}src/ if [ ! `ls ${DIRFLEX}src/${EXENAME}` ]; then - make ${METEO} + make ${COMMAND} if [ ! `ls ${DIRFLEX}src/${EXENAME}` ]; then echo "cannot create exec: "${DIRFLEX}src/${EXENAME} exit -1 diff --git a/prep_flexpart/sbatch_prep_flexpart.sh b/prep_flexpart/sbatch_prep_flexpart.sh index 403d640..6fc859b 100755 --- a/prep_flexpart/sbatch_prep_flexpart.sh +++ b/prep_flexpart/sbatch_prep_flexpart.sh @@ -1,7 +1,7 @@ #!/bin/bash #--------------------------------------------------- -partition=debug -settings_files='./SETTINGS_ghg' +partition=nilu +settings_files='./SETTINGS' #--------------------------------------------------- cat <<EOF > run_job.sh diff --git a/prep_fluxes/FLUXES_co2_bbg b/prep_fluxes/FLUXES_co2_bbg deleted file mode 100644 index 212a9f3..0000000 --- a/prep_fluxes/FLUXES_co2_bbg +++ /dev/null @@ -1,68 +0,0 @@ -# ================================================== -# -# FILE SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Input flux: -# midpoints (logical) lon/lat coordinates are grid cell midpoints? -# coeff = factor to convert from input to output units -# nx_in = number of longitudinal grid cells -# ny_in = number of latitudinal grid cells -# ntime = number of time steps -# timeref = ref date if timestamp is julian days (yyyymmdd) otherwise 0 -# timestamp = sec or month (if julian days and timeref is not 0 this is ignored) -file_in: /mypath/TEST_INPUT/FLUXES/CO2/CO2_BBG_2012_05x05.nc -varname_in: emisco2 -lonname_in: longitude -latname_in: latitude -timename_in: time -midpoints: .true. -coeff: 1. -nx_in: 720 -ny_in: 360 -ntime: 12 -indextime: 1 -timeref: 19000101 -timestamp: - -# Output flux: -# year = year for timestamp -# nx_out = number of longitudinal grid cells -# ny_out = number of latitudinal grid cells -# dx_out = longitudinal resolution -# dy_out = latitudinal resolution -# ntime_out = number of time steps -# llx_out = left longitude of domain -# lly_out = lower latitude of domain -file_out: /mypath/TEST_OUTPUT/FLUXES/CO2/CO2_BBG_2012_10x10.nc -varname_out: emisco2 -varunit: kgC/m2/h -lonname_out: longitude -latname_out: latitude -timename_out: time -year: 2012 -nx_out: 360 -ny_out: 180 -dx_out: 1.0 -dy_out: 1.0 -ntime_out: 12 -llx_out: -180 -lly_out: -90 - -# Monthly scalars: -# one file with monthly temporal resolution containing -# scalars for the annual fluxes in each grid cell -# spatial resolution must be the same as file_in -file_scalar: -varname_scalar: -lonname_scalar: -latname_scalar: -timename_scalar: - - - - diff --git a/prep_fluxes/FLUXES_co2_ff b/prep_fluxes/FLUXES_co2_ff deleted file mode 100644 index 1e95dcd..0000000 --- a/prep_fluxes/FLUXES_co2_ff +++ /dev/null @@ -1,68 +0,0 @@ -# ================================================== -# -# FILE SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Input flux: -# midpoints (logical) lon/lat coordinates are grid cell midpoints? -# coeff = factor to convert from input to output units -# nx_in = number of longitudinal grid cells -# ny_in = number of latitudinal grid cells -# ntime = number of time steps -# timeref = ref date if timestamp is julian days (yyyymmdd) otherwise 0 -# timestamp = sec, hour, or month (if julian days and timeref is not 0 this is ignored) -file_in: /mypath/TEST_INPUT/FLUXES/CO2/EDGARv4.3_BP2016_emissions.co2.global.0.5x0.5.1hr.201201.nc -varname_in: emission -lonname_in: lon -latname_in: lat -timename_in: time -midpoints: .true. -coeff: 1. -nx_in: 720 -ny_in: 360 -ntime: 744 -indextime: 1 -timeref: 0 -timestamp: hour - -# Output flux: -# year = year for timestamp -# nx_out = number of longitudinal grid cells -# ny_out = number of latitudinal grid cells -# dx_out = longitudinal resolution -# dy_out = latitudinal resolution -# ntime_out = number of time steps -# llx_out = left longitude of domain -# lly_out = lower latitude of domain -file_out: /mypath/TEST_OUTPUT/FLUXES/CO2/CO2_FF_201201_10x10.nc -varname_out: emisco2 -varunit: micromol/m2/s -lonname_out: longitude -latname_out: latitude -timename_out: time -year: 2012 -nx_out: 360 -ny_out: 180 -dx_out: 1.0 -dy_out: 1.0 -ntime_out: 744 -llx_out: -180 -lly_out: -90 - -# Monthly scalars: -# one file with monthly temporal resolution containing -# scalars for the annual fluxes in each grid cell -# spatial resolution must be the same as file_in -file_scalar: -varname_scalar: -lonname_scalar: -latname_scalar: -timename_scalar: - - - - diff --git a/prep_fluxes/FLUXES_co2_nee b/prep_fluxes/FLUXES_co2_nee deleted file mode 100644 index 05d0e94..0000000 --- a/prep_fluxes/FLUXES_co2_nee +++ /dev/null @@ -1,68 +0,0 @@ -# ================================================== -# -# FILE SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Input flux: -# midpoints (logical) lon/lat coordinates are grid cell midpoints? -# coeff = factor to convert from input to output units -# nx_in = number of longitudinal grid cells -# ny_in = number of latitudinal grid cells -# ntime = number of time steps -# timeref = ref date if timestamp is julian days (yyyymmdd) otherwise 0 -# timestamp = sec or month (if julian days and timeref is not 0 this is ignored) -file_in: /mypath/TEST_INPUT/FLUXES/CO2/CO2_NEE_2012_05x05.nc -varname_in: nee -lonname_in: longitude -latname_in: latitude -timename_in: time -midpoints: .true. -coeff: 1. -nx_in: 720 -ny_in: 360 -ntime: 2920 -indextime: 1 -timeref: 19000101 -timestamp: - -# Output flux: -# year = year for timestamp -# nx_out = number of longitudinal grid cells -# ny_out = number of latitudinal grid cells -# dx_out = longitudinal resolution -# dy_out = latitudinal resolution -# ntime_out = number of time steps -# llx_out = left longitude of domain -# lly_out = lower latitude of domain -file_out: /mypath/TEST_OUTPUT/FLUXES/CO2/CO2_NEE_2012_10x10.nc -varname_out: nee -varunit: kgC/m2/h -lonname_out: longitude -latname_out: latitude -timename_out: time -year: 2012 -nx_out: 360 -ny_out: 180 -dx_out: 1.0 -dy_out: 1.0 -ntime_out: 2920 -llx_out: -180 -lly_out: -90 - -# Monthly scalars: -# one file with monthly temporal resolution containing -# scalars for the annual fluxes in each grid cell -# spatial resolution must be the same as file_in -file_scalar: -varname_scalar: -lonname_scalar: -latname_scalar: -timename_scalar: - - - - diff --git a/prep_fluxes/FLUXES_co2_ocn b/prep_fluxes/FLUXES_co2_ocn deleted file mode 100644 index c55337e..0000000 --- a/prep_fluxes/FLUXES_co2_ocn +++ /dev/null @@ -1,68 +0,0 @@ -# ================================================== -# -# FILE SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Input flux: -# midpoints (logical) lon/lat coordinates are grid cell midpoints? -# coeff = factor to convert from input to output units -# nx_in = number of longitudinal grid cells -# ny_in = number of latitudinal grid cells -# ntime = number of time steps -# timeref = ref date if timestamp is julian days (yyyymmdd) otherwise 0 -# timestamp = sec or month (if julian days and timeref is not 0 this is ignored) -file_in: /mypath/TEST_INPUT/FLUXES/CO2/CO2_OCEAN_2012_05x05.nc -varname_in: emisco2 -lonname_in: longitude -latname_in: latitude -timename_in: time -midpoints: .true. -coeff: 1. -nx_in: 720 -ny_in: 360 -ntime: 12 -indextime: 1 -timeref: 19000101 -timestamp: - -# Output flux: -# year = year for timestamp -# nx_out = number of longitudinal grid cells -# ny_out = number of latitudinal grid cells -# dx_out = longitudinal resolution -# dy_out = latitudinal resolution -# ntime_out = number of time steps -# llx_out = left longitude of domain -# lly_out = lower latitude of domain -file_out: /mypath/TEST_OUTPUT/FLUXES/CO2/CO2_OCEAN_2012_10x10.nc -varname_out: emisco2 -varunit: kgC/m2/h -lonname_out: longitude -latname_out: latitude -timename_out: time -year: 2012 -nx_out: 360 -ny_out: 180 -dx_out: 1.0 -dy_out: 1.0 -ntime_out: 12 -llx_out: -180 -lly_out: -90 - -# Monthly scalars: -# one file with monthly temporal resolution containing -# scalars for the annual fluxes in each grid cell -# spatial resolution must be the same as file_in -file_scalar: -varname_scalar: -lonname_scalar: -latname_scalar: -timename_scalar: - - - - diff --git a/prep_fluxes/README_prepflux.txt b/prep_fluxes/README_prepflux.txt index a7bb335..a19d6be 100644 --- a/prep_fluxes/README_prepflux.txt +++ b/prep_fluxes/README_prepflux.txt @@ -13,11 +13,10 @@ Note: same resolution as the FLEXPART output files (grid_time) For annual mean fluxes (e.g. CO2 fossil fuel) can apply a monthly scaling factor to give monthly resolved fluxes - (see e.g. FLUXES_FF) Usage: 1) compile with gfortran using: make - 2) create/edit a settings file: e.g. FLUXES_test + 2) create/edit a settings file: e.g. SETTINGS_FLUX 3) edit the bash script: job_prep_flux.sh (or alternatively for slurm sbatch_prep_flux.sh) 4) run the bash script: ./job_prep_flux.sh diff --git a/prep_fluxes/FLUXES_ghg b/prep_fluxes/SETTINGS_FLUX similarity index 92% rename from prep_fluxes/FLUXES_ghg rename to prep_fluxes/SETTINGS_FLUX index f85dbb9..d902882 100644 --- a/prep_fluxes/FLUXES_ghg +++ b/prep_fluxes/SETTINGS_FLUX @@ -15,7 +15,7 @@ # ntime = number of time steps # timeref = ref date if timestamp is julian days (yyyymmdd) otherwise 0 # timestamp = sec or month (if julian days and timeref is not 0 this is ignored) -file_in: /mypath/TEST_INPUT/FLUXES/GHG/CH4_TOTAL_2012_05x05.nc +file_in: /mypath/flux/flux_input.nc varname_in: emisch4 lonname_in: longitude latname_in: latitude @@ -38,7 +38,7 @@ timestamp: # ntime_out = number of time steps # llx_out = left longitude of domain # lly_out = lower latitude of domain -file_out: /mypath/TEST_OUTPUT/FLUXES/GHG/CH4_TOTAL_2012_10x10.nc +file_out: /mypath/flux/flux_output.nc varname_out: emisch4 varunit: kgC/m2/h lonname_out: longitude diff --git a/prep_fluxes/job_prep_flux.sh b/prep_fluxes/job_prep_flux.sh index 4a2f798..369b9e2 100755 --- a/prep_fluxes/job_prep_flux.sh +++ b/prep_fluxes/job_prep_flux.sh @@ -1,6 +1,6 @@ #!/bin/bash #--------------------------------------------------- -file='./FLUXES_ghg' +file='./SETTINGS_FLUX' ./prep_flux ${file} diff --git a/prep_fluxes/sbatch_prep_flux.sh b/prep_fluxes/sbatch_prep_flux.sh index 45739b8..7d43b64 100755 --- a/prep_fluxes/sbatch_prep_flux.sh +++ b/prep_fluxes/sbatch_prep_flux.sh @@ -1,6 +1,6 @@ #!/bin/bash #--------------------------------------------------- -file='./FLUXES_ghg' +file='./SETTINGS_FLUX' #--------------------------------------------------- cat <<EOF > run_job.sh diff --git a/prep_regions/initialize.f90 b/prep_regions/initialize.f90 index 0cfb8ab..1f11671 100644 --- a/prep_regions/initialize.f90 +++ b/prep_regions/initialize.f90 @@ -49,6 +49,7 @@ subroutine initialize(files, config) character(max_path_len) :: filename logical :: lexist integer :: yyyymm + character(len=8) :: areldate character(6) :: adate character(4) :: ayear integer :: numpoint @@ -63,28 +64,38 @@ subroutine initialize(files, config) filename = trim(files%file_recept) inquire(file=trim(filename),exist=lexist) - if (.not.lexist) then + if ( .not.lexist.and.config%ground ) then write(*,*) 'ERROR: cannot find '//trim(filename) stop + else if ( lexist.and.config%ground ) then + write(*,*) 'Reading receptor list: '//trim(filename) + call read_reclist(filename) endif - write(*,*) 'Reading receptor list: '//trim(filename) - call read_reclist(filename) ! initialize flexpart variables ! ----------------------------- - yyyymm=config%datei/100 - write(adate,'(i6)') yyyymm - lexist = .false. - i = 1 - print*, 'nrec:',nrec - do while ( (.not.lexist).and.(i.le.nrec) ) - if ( .not.config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' - if ( config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' - print*, filename + ! read header for global output + if ( config%satellite.and.nrec.eq.0 ) then + ! satellite only + write(areldate,'(I8)') config%datei + if ( .not.config%nested ) filename = trim(files%path_flexsat)//areldate//'/header' + if ( config%nested ) filename = trim(files%path_flexsat)//areldate//'/header_nest' inquire(file=trim(filename),exist=lexist) - i = i + 1 - end do + else + ! ground-based output + yyyymm=config%datei/100 + write(adate,'(i6)') yyyymm + lexist = .false. + i = 1 + do while ( (.not.lexist).and.(i.le.nrec) ) + if ( .not.config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' + if ( config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' + print*, filename + inquire(file=trim(filename),exist=lexist) + i = i + 1 + end do + endif if ( .not.lexist ) then print*, 'ERROR initialize: cannot find flexpart header' stop @@ -129,6 +140,8 @@ subroutine initialize(files, config) if ( config%spec.eq.'co2' ) then if ( config%nested ) then files%filename_flx = str_replace( files%filenest_nee, 'YYYY', ayear) + print*, 'filenest_nee = ',files%filenest_nee + print*, 'filename_flx = ',files%filename_flx files%varname_flx = files%varnest_nee files%lonname_flx = files%lonnest_nee files%latname_flx = files%latnest_nee diff --git a/prep_regions/mod_settings.f90 b/prep_regions/mod_settings.f90 index f928a29..ddbdb4a 100644 --- a/prep_regions/mod_settings.f90 +++ b/prep_regions/mod_settings.f90 @@ -36,9 +36,11 @@ module mod_settings character(len=max_path_len) :: path_prior ! path to prior fluxes character(len=max_path_len) :: path_obs ! path to observations + character(len=max_path_len) :: path_satobs ! path to satellite observations character(len=max_path_len) :: path_output ! path to output character(len=max_name_len) :: suffix ! observation file name suffix character(len=max_path_len) :: path_flexpart ! path to flexpart output + character(len=max_path_len) :: path_flexsat ! path to flexpart output for satellites character(len=max_name_len) :: file_log ! log file name character(len=max_path_len) :: file_regions ! region definitions file character(len=max_name_len) :: varname_regs ! regions variable name @@ -84,6 +86,8 @@ module mod_settings integer :: datei ! start date (yyyymmdd) integer :: datef ! end date (yyyymmdd) logical :: nested ! use nested flexpart output (true or false) + logical :: satellite ! use satellite observations (true or false) + logical :: ground ! use ground-based observations (true or false) real :: w_edge_lon ! lon of western edge of inversion grid real :: s_edge_lat ! lat of southern edge of inversion grid real :: e_edge_lon ! lon of eastern edge of inversion grid @@ -319,12 +323,18 @@ module mod_settings identifier = "path_obs:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_obs = cc + identifier = "path_satobs:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) files%path_satobs = cc identifier = "suffix:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%suffix = cc identifier = "path_flexpart:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_flexpart = cc + identifier = "path_flexsat:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) files%path_flexsat = cc identifier = "path_output:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_output = cc @@ -448,6 +458,10 @@ module mod_settings logical :: match, cl character(len=20), dimension(3) :: temp + ! default logical settings + config%satellite = .false. + config%ground = .false. + ! open file open (100, file = trim (filename), status = 'old', iostat=ierr) if(ierr.gt.0) then @@ -484,6 +498,14 @@ module mod_settings call read_content (line, identifier, cc, cn, cl, match) if ( match ) config%datef = int(cn) + ! read observation settings + identifier = "satellite:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%satellite = cl + identifier = "ground:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%ground = cl + ! read inversion domain settings identifier = "nested:" call read_content (line, identifier, cc, cn, cl, match) @@ -514,6 +536,12 @@ module mod_settings end do read_loop + ! some initial checks + if ( .not.config%ground.and..not.config%satellite ) then + print*, 'ERROR: at least one of ground and satellite must be true' + stop + endif + end subroutine read_config_settings ! -------------------------------------------------- diff --git a/prep_regions/mod_strings.f90 b/prep_regions/mod_strings.f90 index bb42549..486199a 100644 --- a/prep_regions/mod_strings.f90 +++ b/prep_regions/mod_strings.f90 @@ -32,7 +32,7 @@ module mod_strings character(*), private, parameter :: lower_case = 'abcdefghijklmnopqrstuvwxyz' character(*), private, parameter :: upper_case = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - public :: to_upper, to_lower, str_replace + public :: to_upper, to_lower, str_replace, split contains @@ -111,20 +111,100 @@ module mod_strings character(*), intent(in) :: repstr character(max_path_len) :: string_out character(max_path_len) :: strtmp1, strtmp2 - integer :: strlen, replen, n + integer :: strlen, substrlen, replen, n strlen = len_trim(string_in) replen = len_trim(repstr) + substrlen = len_trim(substr) ! find first position of substr in string_in n = index( string_in, substr, back=.false. ) - strtmp1 = string_in(1:n-1) + if ((n-1).gt.0) then + strtmp1 = string_in(1:n-1) + else + ! empty string + strtmp1 = '' + n = 1 + endif +! print*, 'str_replace first pos n, strtmp1 = ',n, strtmp1 ! find last position of substr in string_in - n = index( string_in, substr, back=.true. ) - strtmp2 = string_in(n+replen:strlen) - string_out = trim(strtmp1)//trim(repstr)//trim(strtmp2) + n = index( string_in, substr, back=.true. ) + strtmp2 = string_in(n+substrlen:strlen) +! print*, 'str_replace last pos n, strtmp2 = ',n, strtmp2 + string_out = trim(strtmp1)//trim(repstr)//trim(strtmp2) end function str_replace + ! -------------------------------------------------- + ! split string + ! -------------------------------------------------- + + subroutine split(str,delims,before,sep) + + ! Routine finds the first instance of a character from 'delims' in the + ! the string 'str'. The characters before the found delimiter are + ! output in 'before'. The characters after the found delimiter are + ! output in 'str'. The optional output character 'sep' contains the + ! found delimiter. A delimiter in 'str' is treated like an ordinary + ! character if it is preceded by a backslash (\). If the backslash + ! character is desired in 'str', then precede it with another backslash. + + character(len=*) :: str, delims, before + character,optional :: sep + logical :: pres + character :: ch,cha + integer :: i, k, ibsl, ipos, iposa, lenstr + + pres=present(sep) + str=adjustl(str) + lenstr=len_trim(str) + if(lenstr == 0) return ! string str is empty + k=0 + ibsl=0 ! backslash initially inactive + before=' ' + do i=1,lenstr + ch=str(i:i) + if(ibsl == 1) then ! backslash active + k=k+1 + before(k:k)=ch + ibsl=0 + cycle + end if + if(ch == '\') then ! backslash with backslash inactive + k=k+1 + before(k:k)=ch + ibsl=1 + cycle + end if + ipos=index(delims,ch) + if(ipos == 0) then ! character is not a delimiter + k=k+1 + before(k:k)=ch + cycle + end if + if(ch /= ' ') then ! character is a delimiter that is not a space + str=str(i+1:) + if(pres) sep=ch + exit + end if + cha=str(i+1:i+1) ! character is a space delimiter + iposa=index(delims,cha) + if(iposa > 0) then ! next character is a delimiter + str=str(i+2:) + if(pres) sep=cha + exit + else + str=str(i+1:) + if(pres) sep=ch + exit + end if + end do + if(i >= lenstr) str='' + str=trim(str) + before=trim(before) + return + + end subroutine split + ! -------------------------------------------------- end module mod_strings diff --git a/prep_regions/mod_var.f90 b/prep_regions/mod_var.f90 index 14a5a58..6393249 100644 --- a/prep_regions/mod_var.f90 +++ b/prep_regions/mod_var.f90 @@ -32,7 +32,7 @@ module mod_var integer, parameter :: max_name_len=50 ! max character length of variable names integer, parameter :: recname_len=7 ! length of receptor names real, parameter :: numscale=1.e12 ! numeric scaling factor - integer, parameter :: maxpoint=1000 ! max number of releases per receptor per month + integer, parameter :: maxpoint=50000 ! max number of releases per receptor per month integer, parameter :: maxspec=10 ! max number of species in a flexpart run integer, parameter :: maxlev=50 ! max number of vertical levels in flexpart output integer, parameter :: maxobs=100000 ! max number of observations diff --git a/prep_regions/prep_regions.f90 b/prep_regions/prep_regions.f90 index 0c25d37..ad66a6d 100644 --- a/prep_regions/prep_regions.f90 +++ b/prep_regions/prep_regions.f90 @@ -45,7 +45,8 @@ program prep_regions real, dimension(:,:), allocatable :: surfinf real, dimension(:,:), allocatable :: flux, mask real(kind=8), dimension(:), allocatable :: obstimes, avetimes - character(4), dimension(:), allocatable :: recs + real, dimension(:), allocatable :: cdryair + character(recname_len), dimension(:), allocatable :: recs real, dimension(:,:), allocatable :: nbox_xy integer :: ierr,ix,jy logical :: lexist @@ -95,12 +96,16 @@ program prep_regions ! read observations allocate( obstimes(maxobs), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR: not enough memory' allocate( avetimes(maxobs), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR: not enough memory' allocate( recs(maxobs), stat=ierr ) if ( ierr.ne.0 ) stop 'ERROR: not enough memory' + allocate( cdryair(maxobs), stat=ierr ) + if ( ierr.ne.0 ) stop 'ERROR: not enough memory' obstimes(:) = 0d0 avetimes(:) = 0d0 - call read_obs(files, recs, obstimes, avetimes) + call read_obs(files, config, recs, cdryair, obstimes, avetimes) ! mean surface influence (i.e. flux sensitivity) allocate( surfinf(nxregrid,nyregrid), stat=ierr ) @@ -113,7 +118,7 @@ program prep_regions varname = 'surfinf' call read_ncdf(files, config, filename, varname, surfinf) else - call get_surfinf(files, config, recs, obstimes, avetimes, surfinf) + call get_surfinf(files, config, recs, cdryair, obstimes, avetimes, surfinf) endif ! calculate regions @@ -128,7 +133,7 @@ program prep_regions ! write lsm allocate( mask(nxregrid,nyregrid), stat=ierr) mask=lsm - open(100,file=trim(files%path_output)//'lsm.txt',action='write',status='new') + open(100,file=trim(files%path_output)//'lsm.txt',action='write',status='replace') write(rowfmt,'(A,I6,A)') '(',nxregrid,'(F5.2,1X))' do jy = 1,nyregrid write(100,rowfmt) lsm(:,jy) diff --git a/prep_regions/read_obs.f90 b/prep_regions/read_obs.f90 index 33a87bb..d6a5cdb 100644 --- a/prep_regions/read_obs.f90 +++ b/prep_regions/read_obs.f90 @@ -26,48 +26,63 @@ !! Inputs !! files - file data structure !! reclist - list of receptor IDs +!! cdryair - total column dry air concentration (mol/m2) !! obstimes - list of observation time stamps as julian days !! avetimes - list of observation averaging times as julian days !! !--------------------------------------------------------------------------------------- -subroutine read_obs(files, reclist, obstimes, avetimes) +subroutine read_obs(files, config, reclist, cdryair, obstimes, avetimes) + use netcdf use mod_settings use mod_strings use mod_var use mod_dates + use mod_ncdf, only : check implicit none type (files_t), intent(in) :: files + type (config_t), intent(in) :: config real(kind=8), dimension(maxobs), intent(in out) :: obstimes real(kind=8), dimension(maxobs), intent(in out) :: avetimes - character(4), dimension(maxobs), intent(in out) :: reclist + real, dimension(maxobs), intent(in out) :: cdryair + character(recname_len), dimension(maxobs), intent(in out) :: reclist character(len=max_path_len), dimension(:), allocatable :: filelist - character(len=7) :: recs - character(len=200) :: header + character(len=max_path_len) :: numfmt, rowfmt + character(len=recname_len) :: recs + character(len=200) :: header, species, string, before, sep character(len=200), dimension(18) :: args real, dimension(16) :: temp integer :: ierr integer :: cnt integer :: narg, n, reclen - integer :: nf, nfiles, nr + integer :: nf, nfiles integer :: yyyymmdd, hhmiss, yyyy, mm, dd, hh, mi, ss, hl real(kind=8) :: jdate, avetime real :: conc, err, lon, alt - integer :: num + integer :: num, nr, nretr + integer :: ncid, dimid, varid + integer, dimension(:), allocatable :: idate, itime + real, dimension(:), allocatable :: cdryair_loc ! list observation files ! ---------------------- - print*, trim(files%path_obs) - print*, trim(files%suffix) - print*, trim(files%path_output) - - call system('ls '//trim(files%path_obs)//'*'//trim(files%suffix)//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') - call system('ls '//trim(files%path_obs)//' | grep '//trim(files%suffix)//' >> '//trim(files%path_output)//'obsfiles.txt') + if ( config%ground.and.config%satellite ) then + call system('ls '//trim(files%path_obs)//'*'//trim(files%suffix)//& + ' '//trim(files%path_satobs)//'*.nc'//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_obs)//' | grep '//trim(files%suffix)//' >> '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_satobs)//' | grep .nc >> '//trim(files%path_output)//'obsfiles.txt') + else if ( config%ground ) then + call system('ls '//trim(files%path_obs)//'*'//trim(files%suffix)//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_obs)//' | grep '//trim(files%suffix)//' >> '//trim(files%path_output)//'obsfiles.txt') + else if ( config%satellite ) then + call system('ls '//trim(files%path_satobs)//'*.nc'//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_satobs)//' | grep .nc >> '//trim(files%path_output)//'obsfiles.txt') + endif open(100,file=trim(files%path_output)//'obsfiles.txt',action='read',status='old',iostat=ierr) if ( ierr.ne. 0 ) then @@ -88,89 +103,131 @@ subroutine read_obs(files, reclist, obstimes, avetimes) cnt = 0 do nf = 1, nfiles - ! check this file belongs to receptor in reclist - ! added option for 4 character length names - if (filelist(nf)(4:4).eq.'_' )then - recs = filelist(nf)(1:3) - reclen=3 - else - recs = filelist(nf)(1:4) - reclen=4 - endif - print*, 'read_obs: rec = ',recs - if ( .not.( any(recname(:)(1:4).eq.to_upper(recs)) ) .and. .not.( any(recname(:)(1:3).eq.to_upper(recs)) )) go to 10 - - ! open input file - open(100,file=trim(files%path_obs)//trim(filelist(nf)),action='read',status='old',iostat=ierr) - if ( ierr.ne.0 ) then - write(*,*) 'WARNING: cannot open: '//trim(files%path_obs)//trim(filelist(nf)) - go to 10 - endif - write(*,*) 'Reading file: '//trim(files%path_obs)//trim(filelist(nf)) - - ! station coordinates for data selection - do nr = 1, nrec - if ( to_upper(recs).eq.recname(nr) ) then - lon = reclon(nr) - alt = recalt(nr) - go to 20 - endif - end do + ! check if ground-based or satellite observation + string = filelist(nf) + call split(string,".",before,sep) + + print*, 'string = ',string + print*, 'before = ',before + + if ( trim(string).eq.'nc' ) then + + ! satellite + ! --------- + + ! check if data is in inversion time interval + read(before(19:26),*) yyyymmdd + print*, 'read_obs: before = ',before(19:26) + jdate = juldate(yyyymmdd, 0) + if ( jdate.lt.juldatei.or.jdate.gt.juldatef ) cycle + + ! read ncdf file + call check( nf90_open(trim(files%path_satobs)//trim(filelist(nf)),nf90_NOWRITE,ncid) ) + call check( nf90_inq_dimid(ncid,'retrieval',dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=nretr) ) + allocate( idate(nretr), stat=ierr ) + allocate( itime(nretr), stat=ierr ) + allocate( cdryair_loc(nretr), stat=ierr ) + call check( nf90_inq_varid(ncid,'idate',varid) ) + call check( nf90_get_var(ncid,varid,idate) ) + call check( nf90_inq_varid(ncid,'itime',varid) ) + call check( nf90_get_var(ncid,varid,itime) ) + call check( nf90_inq_varid(ncid,'cdryair',varid) ) + call check( nf90_get_var(ncid,varid,cdryair_loc) ) + call check( nf90_close(ncid) ) + + write(numfmt,fmt='(A,I1,A,I1,A)') '(I',recname_len,'.',recname_len,')' + do nr = 1, nretr + cnt = cnt + 1 + write(recs,fmt=numfmt) nr + jdate = juldate(idate(nr), itime(nr)) + obstimes(cnt) = jdate + avetimes(cnt) = 0d0 + cdryair(cnt) = cdryair_loc(nr) + reclist(cnt) = trim(recs) + end do + + deallocate( idate ) + deallocate( itime ) + deallocate( cdryair_loc ) + + else if ( string.eq.trim(files%suffix) ) then + + ! ground-based + ! ------------ + + ! check this file belongs to receptor in reclist + call split(before,"_",recs,sep) + species = trim(before) + reclen = len_trim(recs) + + print*, 'species = ',species + print*, 'recs = ',recs + + if ( .not.( any(recname(:)(1:reclen).eq.to_upper(recs)) ) ) go to 10 + + ! open input file + open(100,file=trim(files%path_obs)//trim(filelist(nf)),action='read',status='old',iostat=ierr) + if ( ierr.ne.0 ) then + write(*,*) 'WARNING: cannot open: '//trim(files%path_obs)//trim(filelist(nf)) + go to 10 + endif + write(*,*) 'Reading file: '//trim(files%path_obs)//trim(filelist(nf)) + + ! station coordinates for data selection + do nr = 1, nrec + if ( to_upper(recs).eq.recname(nr) ) then + lon = reclon(nr) + alt = recalt(nr) + go to 20 + endif + end do 20 continue - ! read header - read (100, fmt='(A)', iostat=ierr) header - print*, 'read_obs: header = ',header - - ! read data - read_loop: do - ! if obs file contains avetime - if ( reclen.eq.4 ) then - read(100,fmt='(A4,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & - recs, yyyymmdd, hhmiss, jdate, avetime, conc, err, num - else - read(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & - recs, yyyymmdd, hhmiss, jdate, avetime, conc, err, num - endif - ! if obs file doesn't contain avetime (backwards compatability) -! avetime = 0d0 -! if ( reclen.eq.4 ) then -! read(100,fmt='(A4,1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & -! recs, yyyymmdd, hhmiss, jdate, conc, err, num -! else -! read(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & -! recs, yyyymmdd, hhmiss, jdate, conc, err, num -! endif - if ( ierr.gt.0 ) exit read_loop - if ( jdate.ge.(juldatef+1d0) ) exit read_loop - if ( jdate.lt.juldatei ) cycle read_loop - if ( conc.le.-999. ) cycle read_loop - ! select day/night for low/high alt sites - hh = hhmiss/10000 - hl = hh + int(lon*24./360.) - if ( hl.lt.0 ) then - hl = hl + 24 - else if ( hl.ge.24 ) then - hl = hl - 24 - endif - if ( alt.le.1000. ) then - if ( (hl.lt.11).or.(hl.gt.15) ) cycle read_loop - else - if ( (hl.gt.3).and.(hl.lt.23) ) cycle read_loop - endif - if ( conc.le.-999. ) cycle read_loop - - cnt = cnt + 1 - obstimes(cnt) = jdate - avetimes(cnt) = avetime - reclist(cnt) = recs - end do read_loop - - ! close input file - close(100) + ! read header + read (100, fmt='(A)', iostat=ierr) header + print*, 'read_obs: header = ',header + + ! read data + read_loop: do + ! if obs file contains avetime + write(rowfmt,'(A,I1,A)') '(A',reclen,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)' + read(100,fmt=rowfmt,iostat=ierr) recs, yyyymmdd, hhmiss, jdate, avetime, conc, err, num + ! if obs file doesn't contain avetime (backwards compatability) +! avetime = 0d0 +! write(rowfmt,'(A,I1,A)') '(A',reclen,',1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)' +! read(100,fmt=rowfmt,iostat=ierr) recs, yyyymmdd, hhmiss, jdate, conc, err, num + if ( ierr.gt.0 ) exit read_loop + if ( jdate.ge.(juldatef+1d0) ) exit read_loop + if ( jdate.lt.juldatei ) cycle read_loop + if ( conc.le.-999. ) cycle read_loop + ! select day/night for low/high alt sites + hh = hhmiss/10000 + hl = hh + int(lon*24./360.) + if ( hl.lt.0 ) then + hl = hl + 24 + else if ( hl.ge.24 ) then + hl = hl - 24 + endif + if ( alt.le.1000. ) then + if ( (hl.lt.11).or.(hl.gt.15) ) cycle read_loop + else + if ( (hl.gt.3).and.(hl.lt.23) ) cycle read_loop + endif + if ( conc.le.-999. ) cycle read_loop + cnt = cnt + 1 + obstimes(cnt) = jdate + avetimes(cnt) = avetime + reclist(cnt) = trim(recs) + end do read_loop + + ! close input file + close(100) 10 continue + endif ! satellite or ground-based + end do print*, 'read_obs: cnt = ',cnt diff --git a/prep_regions/read_reclist.f90 b/prep_regions/read_reclist.f90 index 82bb1f4..253c273 100644 --- a/prep_regions/read_reclist.f90 +++ b/prep_regions/read_reclist.f90 @@ -30,6 +30,7 @@ subroutine read_reclist(filename) implicit none character(len=max_path_len), intent(in) :: filename + character(len=max_path_len) :: rowfmt character(len=200) :: line integer :: ierr integer :: cnt @@ -64,8 +65,10 @@ subroutine read_reclist(filename) open(100,file=trim(filename),action='read',status='old',iostat=ierr) write(*,*) 'Receptors: ' +! write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F6.2,1X,F6.2,1X,F7.2)' + write(rowfmt,fmt='(A,I1,A)') '(A',3,',1X,F7.2,1X,F7.2,1X,F7.2)' do cnt = 1, nrec - read(100,fmt='(A4,1X,F6.2,1X,F6.2,1X,F7.2)') recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) + read(100,fmt=rowfmt) recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) write(*,*) recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) ! read(100,*) recname(cnt) ! write(*,*) recname(cnt) diff --git a/prep_satellite/SETTINGS_bremen b/prep_satellite/SETTINGS_bremen new file mode 100644 index 0000000..4788ef4 --- /dev/null +++ b/prep_satellite/SETTINGS_bremen @@ -0,0 +1,155 @@ +# ================================================== +# +# SATELLITE DATA SETTINGS +# +# comment lines start with '#' +# each parameter line starts with 'parameter name:' +# +# ================================================== + +# PATHS AND FILES + +# satellite instrument/retrieval +satellite: bremen +# name to include in output files +satname: esa_ch4 +# flexpart source path +path_flexpart: /mypath/FLEXPART_v104/ +# path where to write options folders (folders are created daily) +path_options: /mypath/flexpart_input/ +# path where to write flexpart output +path_output: /mypath/flexpart_output/ +# path to OH fields (if use OH chemistry) +path_ohfield: /mypath/OH_fields/ +# path to satellite data +path_obs: /mypath/satellite_data/ +# suffix satellite files +suffix: ESACCI +# path where to write observation output +path_obsout: /mypath/satellite_obs_out/ +# AVAILABLE files +file_avail: /mypath/AVAILABLE +file_availnest: /mypath/AVAILABLE +# windfields to use (determines lon of domain for flexpart run) (oper, era5) +windfield: oper + +# SATELLITE VARIABLES + +# select only retrievals only land (no sunglint) +landonly: .true. +# number measurements along flight direction +nmeas_name: sounding_dim +# number of pixels across swath +npixel_name: ground_pixel +# number of lat/lon coordinates for each pixel +ncoord_name: corners_dim +# number of retrieval levels +nlevel_name: level_dim +# time variable +time_name: time +# quality flag +qa_name: xch4_quality_flag +# quality cutoff value (0 = good, 1 = bad, values > qa_cutoff discarded) +qa_cutoff: 0.1 +# mixing ratio (ppbv) +vmr_name: xch4 +# mixing ratio precision (ppb) +verr_name: xch4_uncertainty +# latitude bounds +lat_name: latitude_corners +# longitude bounds +lon_name: longitude_corners +# column averaging kernel +ak_name: xch4_averaging_kernel +# mixing ratio apriori (mol/m2) +apri_name: ch4_profile_apriori +# pressure levels (hPa) +pdel_name: pressure_levels +# pressure weighting +dryair_name: pressure_weight +# land fraction +landfrac_name: land_fraction + +# COMMAND + +# start date of simulation (yyyymmdd) +datei: 20200702 +# end date of simulation (yyyymmdd) +datef: 20200801 +# output rate (secs) +outrate: 86400 +# time average of output (secs) +outaverage: 86400 +# sampling rate of output (secs) +outsample: 1800 +# (mass unit = 1, mixing ratio unit = 2) +ind_source: 1 +# (mass unit = 1, mixing ratio unit = 2) +ind_receptor: 2 +# use nested output (0 = no, 1 = yes) +lnested: 0 +# output sensitivity to initial conditions (for backward runs, 0 = none, 1 = mass unit, 2 = mass mixing ratio) +linit_cond: 2 + +# OUTGRID + +# NOTE: domain specified will determine where retrievals are selected + +# longitude of lower left corner of output grid +# for global use -180 (adjusts automatically for windfield) +outlonleft: -180.00 +# latitude of lower left corner of output grid +outlatlower: -90.00 +# number of longitudinal grid cells in output grid +numxgrid: 180 +# number of latitudinal grid cells in output grid +numygrid: 90 +# longitudinal resolution in output grid +dxout: 2.0 +# latitudinal resolution in outout grid +dyout: 2.0 +# longitude of lower left corner of nested grid (used if nested_output = 1) +outlonnest: 50.0 +# latitude of lower left corner of nested grid (used if nested_output = 1) +outlatnest: 47.0 +# number of longitudinal grid cells in nested grid (used if nested_output = 1) +numxnest: 140 +# number of latitudinal grid cells in nested grid (used if nested_output = 1) +numynest: 60 +# longitudinal resolution in nested grid (used if nested_output = 1) +dxoutnest: 0.5 +# latitudinal resolution in nested grid (used if nested_output = 1) +dyoutnest: 0.5 +# comma-separated list of vertical levels in output grid (meters) +zlevel: 250, 750, 1500, 2500, 3500, 4500, 5500, 6500, 7500, 8500, 9500, 11000, 13000, 15000, 17000, 19000, 21250, 23750, 26250, 28750, 32500, 37500, 42500, 47500 + +# RELEASES + +# species (see: /options/SPECIES/spec_overview) +species: CH4 +# ageclass (sec) +ageclass: 1728000 +# number of particles per column +npart: 40000 +# mass of particles (arbitrary) +mass: 1000 + +# AVERAGING + +# average retrievals etc. (true/false) +avg_pixels: .true. +# If averaging need to set following (else ignored): +# use predefined grid from file (true/false) +usegridfile: .false. +# if usegridfile is true specify file: +filegrid: +# min grid resolution (degrees) +dmin: 1.0 +# number of resolution steps +nsteps: 2 +# cutoff standard deviation +cutoff: 15. +# min number of retrievals in grid box in order to keep it +nmin: 10 + + diff --git a/prep_satellite/SETTINGS_oco2 b/prep_satellite/SETTINGS_oco2 new file mode 100644 index 0000000..d0bf61f --- /dev/null +++ b/prep_satellite/SETTINGS_oco2 @@ -0,0 +1,157 @@ +# ================================================== +# +# SATELLITE DATA SETTINGS +# +# comment lines start with '#' +# each parameter line starts with 'parameter name:' +# +# ================================================== + +# PATHS AND FILES + +# satellite instrument/retrieval +satellite: oco2 +# name to include in output files +satname: oco2_co2 +# flexpart source path +path_flexpart: /mypath/FLEXPART_v104/ +# path where to write options folders (folders are created daily) +path_options: /mypath/flexpart_input/ +# path where to write flexpart output +path_output: /mypath/flexpart_output/ +# path to OH fields (if use OH chemistry) +path_ohfield: /mypath/OH_fields/ +# path to satellite data +path_obs: /mypath/satellite_data/ +# suffix satellite files +suffix: oco2_LtCO2 +# path where to write observation output +path_obsout: /mypath/satellite_obs_out/ +# AVAILABLE files +file_avail: /mypath/AVAILABLE +file_availnest: /mypath/AVAILABLE +# windfields to use (determines lon of domain for flexpart run) (oper, era5) +windfield: era5 + +# SATELLITE VARIABLES + +# select only retrievals only land +landonly: .false. +# select operation mode (-1 = Both, 0 = Nadir, 1 = Glint) +opmode_sel: -1 +# number measurements along flight direction +nmeas_name: sounding_id +# number of lat/lon coordinates for each pixel +ncoord_name: vertices +# number of retrieval levels +nlevel_name: levels +# operation mode +oper_name: Sounding/operation_mode +# quality flag +qa_name: xco2_quality_flag +# quality cutoff value (0 = good, 1 = bad, values > qa_cutoff discarded) +qa_cutoff: 0.1 +# times of soundings +time_name: time +# mixing ratio (ppm) +vmr_name: xco2 +# mixing ratio precision (ppm) +verr_name: xco2_uncertainty +# latitude bounds +lat_name: vertex_latitude +# longitude bounds +lon_name: vertex_longitude +# column averaging kernel +ak_name: xco2_averaging_kernel +# mixing ratio apriori (ppm) +apri_name: co2_profile_apriori +# pressure levels (hPa) +pdel_name: pressure_levels +# pressure weighting +dryair_name: pressure_weight +# land fraction +landfrac_name: Sounding/land_fraction + +# COMMAND + +# start date of simulation (yyyymmdd) +datei: 20200101 +# end date of simulation (yyyymmdd) +datef: 20200102 +# output rate (secs) +outrate: 10800 +# time average of output (secs) +outaverage: 10800 +# sampling rate of output (secs) +outsample: 1800 +# (mass unit = 1, mixing ratio unit = 2) +ind_source: 1 +# (mass unit = 1, mixing ratio unit = 2) +ind_receptor: 2 +# use nested output (0 = no, 1 = yes) +lnested: 0 +# output sensitivity to initial conditions (for backward runs, 0 = none, 1 = mass unit, 2 = mass mixing ratio) +linit_cond: 2 + +# OUTGRID + +# NOTE: domain specified will determine where retrievals are selected + +# longitude of lower left corner of output grid +# for global use -180 (adjusts automatically for windfield) +outlonleft: -180.00 +# latitude of lower left corner of output grid +outlatlower: -90.00 +# number of longitudinal grid cells in output grid +numxgrid: 180 +# number of latitudinal grid cells in output grid +numygrid: 90 +# longitudinal resolution in output grid +dxout: 2.0 +# latitudinal resolution in outout grid +dyout: 2.0 +# longitude of lower left corner of nested grid (used if nested_output = 1) +outlonnest: -15.0 +# latitude of lower left corner of nested grid (used if nested_output = 1) +outlatnest: 33.0 +# number of longitudinal grid cells in nested grid (used if nested_output = 1) +numxnest: 100 +# number of latitudinal grid cells in nested grid (used if nested_output = 1) +numynest: 80 +# longitudinal resolution in nested grid (used if nested_output = 1) +dxoutnest: 0.5 +# latitudinal resolution in nested grid (used if nested_output = 1) +dyoutnest: 0.5 +# comma-separated list of vertical levels in output grid (meters) +zlevel: 250, 750, 1500, 2500, 3500, 4500, 5500, 6500, 7500, 8500, 9500, 11000, 13000, 15000, 17000, 19000, 21250, 23750, 26250, 28750, 32500, 37500, 42500, 47500 + +# RELEASES + +# species (see: /options/SPECIES/spec_overview) +species: CO2 +# ageclass (sec) +ageclass: 864000 +# number of particles per column +npart: 40000 +# mass of particles (arbitrary) +mass: 1000 + +# AVERAGING + +# average retrievals etc. (true/false) +avg_pixels: .true. +# If averaging need to set following (else ignored): +# use predefined grid from file (true/false) +usegridfile: .false. +# if usegridfile is true specify file: +filegrid: +# min grid resolution (degrees) +dmin: 0.5 +# number of resolution steps +nsteps: 2 +# cutoff standard deviation +cutoff: 1. +# min number of retrievals in grid box in order to keep it +nmin: 1 + + diff --git a/prep_satellite/SETTINGS_tropomi b/prep_satellite/SETTINGS_tropomi new file mode 100644 index 0000000..d1142ff --- /dev/null +++ b/prep_satellite/SETTINGS_tropomi @@ -0,0 +1,159 @@ +# ================================================== +# +# SATELLITE DATA SETTINGS +# +# comment lines start with '#' +# each parameter line starts with 'parameter name:' +# +# ================================================== + +# PATHS AND FILES + +# satellite instrument/retrieval (tropomi, bremen) +satellite: tropomi +# name to include in output files +satname: s5p_ch4 +# flexpart source path +path_flexpart: /mypath/FLEXPART_v104/ +# path where to write options folders (folders are created daily) +path_options: /mypath/flexpart_input/ +# path where to write flexpart output +path_output: /mypath/flexpart_output/ +# path to OH fields (if use OH chemistry) +path_ohfield: /mypath/OH_fields/ +# path to satellite data +path_obs: /mypath/satellite_data/ +# suffix satellite files +suffix: S5P_RPRO_L2__CH4 +# path where to write observation output +path_obsout: /mypath/satellite_obs_out/ +# AVAILABLE files +file_avail: /mypath/AVAILABLE +file_availnest: /mypath/AVAILABLE +# windfields to use (determines lon of domain for flexpart run) (oper, era5) +windfield: oper + +# SATELLITE VARIABLES + +# select only retrievals only land (no sunglint) +landonly: .true. +# number measurements along flight direction +nmeas_name: PRODUCT/scanline +# number of pixels across swath +npixel_name: PRODUCT/ground_pixel +# number of lat/lon coordinates for each pixel +ncoord_name: PRODUCT/corner +# number of retrieval levels +nlevel_name: PRODUCT/level +# reference time variable (seconds since 2010-01-01 00:00 UTC) +time_name: PRODUCT/time +# time start of scan (string) +tdel_name: PRODUCT/delta_time +# quality flag +qa_name: PRODUCT/qa_value +# quality cutoff value (values < qa_cutoff discarded) +#qa_cutoff: 1.0 +qa_cutoff: 0.9999 +# mixing ratio (ppb) +vmr_name: PRODUCT/methane_mixing_ratio_bias_corrected +# mixing ratio precision (ppb) +verr_name: PRODUCT/methane_mixing_ratio_precision +# latitude bounds +lat_name: PRODUCT/SUPPORT_DATA/GEOLOCATIONS/latitude_bounds +# longitude bounds +lon_name: PRODUCT/SUPPORT_DATA/GEOLOCATIONS/longitude_bounds +# column averaging kernel +ak_name: PRODUCT/SUPPORT_DATA/DETAILED_RESULTS/column_averaging_kernel +# mixing ratio apriori (mol/m2) +apri_name: PRODUCT/SUPPORT_DATA/INPUT_DATA/methane_profile_apriori +# dry air subcolumns (mol/m2) +dryair_name: PRODUCT/SUPPORT_DATA/INPUT_DATA/dry_air_subcolumns +# surface pressure (Pa) +psurf_name: PRODUCT/SUPPORT_DATA/INPUT_DATA/surface_pressure +# pressure intervals (Pa) +pdel_name: PRODUCT/SUPPORT_DATA/INPUT_DATA/pressure_interval +# surface classification +surfclass_name: PRODUCT/SUPPORT_DATA/INPUT_DATA/surface_classification + +# COMMAND + +# start date of simulation (yyyymmdd) +datei: 20200701 +# end date of simulation (yyyymmdd) +datef: 20200702 +# output rate (secs) +outrate: 86400 +# time average of output (secs) +outaverage: 86400 +# sampling rate of output (secs) +outsample: 1800 +# (mass unit = 1, mixing ratio unit = 2) +ind_source: 1 +# (mass unit = 1, mixing ratio unit = 2) +ind_receptor: 2 +# use nested output (0 = no, 1 = yes) +lnested: 0 +# output sensitivity to initial conditions (for backward runs, 0 = none, 1 = mass unit, 2 = mass mixing ratio) +linit_cond: 2 + +# OUTGRID + +# NOTE: domain specified will determine where retrievals are selected + +# longitude of lower left corner of output grid +# for global use -180 (adjusts automatically for windfield) +outlonleft: -180.00 +# latitude of lower left corner of output grid +outlatlower: -90.00 +# number of longitudinal grid cells in output grid +numxgrid: 180 +# number of latitudinal grid cells in output grid +numygrid: 90 +# longitudinal resolution in output grid +dxout: 2.0 +# latitudinal resolution in outout grid +dyout: 2.0 +# longitude of lower left corner of nested grid (used if nested_output = 1) +outlonnest: 50.0 +# latitude of lower left corner of nested grid (used if nested_output = 1) +outlatnest: 47.0 +# number of longitudinal grid cells in nested grid (used if nested_output = 1) +numxnest: 140 +# number of latitudinal grid cells in nested grid (used if nested_output = 1) +numynest: 60 +# longitudinal resolution in nested grid (used if nested_output = 1) +dxoutnest: 0.5 +# latitudinal resolution in nested grid (used if nested_output = 1) +dyoutnest: 0.5 +# comma-separated list of vertical levels in output grid (meters) +zlevel: 250, 750, 1500, 2500, 3500, 4500, 5500, 6500, 7500, 8500, 9500, 11000, 13000, 15000, 17000, 19000, 21250, 23750, 26250, 28750, 32500, 37500, 42500, 47500 + +# RELEASES + +# species (see: /options/SPECIES/spec_overview) +species: CH4 +# ageclass (sec) +ageclass: 1728000 +# number of particles per column +npart: 40000 +# mass of particles (arbitrary) +mass: 1000 + +# AVERAGING + +# average retrievals etc (true/false) +avg_pixels: .true. +# If averaing need to specify following (else ignored): +# use predefined grid from file (true/false) +usegridfile: .false. +# if usegridfile is true specify file: +filegrid: +# min grid resolution (degrees) +dmin: 0.5 +# number of resolution steps +nsteps: 3 +# cutoff standard deviation +cutoff: 20. +# min number of retrievals in gridbox in order to keep +nmin: 10. + diff --git a/prep_satellite/average.f90 b/prep_satellite/average.f90 new file mode 100644 index 0000000..5ba267b --- /dev/null +++ b/prep_satellite/average.f90 @@ -0,0 +1,468 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: average +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> average +!! +!! Purpose: Averages individual retrievals to a grid of variable resolution +!! where the grid resolution is adapted based on the degree of variability +!! in the column mixing ratios +!! +!! column average concentration over N retrievals +!! c_av = cpri_av + sum(cak_av_i*v_av_i) + (1/N)*sum(sum(cak_ij*vpri_ij)) +!! where v_av_i is average modelled concentration each layer +!! v_av_i = H_av_i*x*vdryair_av_i +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! +!! Outputs +!! +!! Externals +!! +!--------------------------------------------------------------------------------------- + +subroutine average(settings, idate, itime, xpoints, ypoints, zpoint1, zpoint2, vmr_out, verr_out,& + vapri_out, cpri, cakpri, cak_out, cdryair, vdryair_out, nretr, ncoord, nlayer) + + use mod_settings + use mod_var + use mod_dates + + implicit none + + type (settings_t), intent(in) :: settings + integer, dimension(maxretr), intent(in out) :: idate, itime + real, dimension(maxretr,ncoord), intent(in out) :: xpoints, ypoints + real, dimension(maxretr,nlayer), intent(in out) :: zpoint1, zpoint2 + real, dimension(maxretr,nlayer), intent(in out) :: vapri_out, vdryair_out, cak_out + real, dimension(maxretr), intent(in out) :: vmr_out, verr_out + real, dimension(maxretr), intent(in out) :: cdryair, cpri, cakpri + integer, intent(in out) :: nretr + integer, intent(in) :: ncoord, nlayer + + character(max_name_len) :: line + real :: llx, lly, urx, ury, xav, yav + real, dimension(settings%nsteps) :: stepres + real :: res, box_mean, box_sd, box_err, rwork, area + integer :: nbox, nbox_new, num, numbox, numx, numy + integer :: ix, jy, ixy, n, nn, nr, ns, nb, nl, ierr + integer :: jjjjmmdd, hhmiss + integer, dimension(:), allocatable :: retr_nbox, iretr, iretr_keep, mark_box, idate_mean, itime_mean + real, dimension(:), allocatable :: llx_box, lly_box, res_box, warea + real, dimension(:), allocatable :: vmr_mean, vmr_sd, cpri_mean, cdryair_mean, cakpri_mean + real, dimension(:,:), allocatable :: cak_mean, vdryair_mean, xpts_box, ypts_box, zpt1_box, zpt2_box + integer, dimension(4) :: num_subbox + real(kind=8) :: jdmean + + ! if use predefined grid skip following + ! note: predefined grid overrides domain bounds given in SETTINGS + if ( settings%usegridfile ) then + go to 10 + endif + + ! domain bounds + if ( settings%lnested.eq.1 ) then + llx = settings%outlonnest + lly = settings%outlatnest + urx = llx + settings%numxnest*settings%dxoutnest + ury = lly + settings%numynest*settings%dyoutnest + else + llx = settings%outlonleft + lly = settings%outlatlower + urx = llx + settings%numxgrid*settings%dxout + ury = lly + settings%numygrid*settings%dyout + endif + print*, 'llx, lly, urx, ury = ',llx,lly,urx,ury + + ! resolution steps + stepres(1) = settings%dmin + do ns = 2, settings%nsteps + stepres(ns) = stepres(ns-1)*2 + end do + print*, 'stepres = ',stepres + + ! initialize at coarsest resolution + ! --------------------------------- + + allocate( retr_nbox(nretr) ) + allocate( iretr(nretr) ) + allocate( iretr_keep(nretr) ) + res = stepres(settings%nsteps) +! numx = (urx - llx)/res + numx = (urx - llx)/res + 1 ! to cover whole domain +! numy = (ury - lly)/res + numy = (ury - lly)/res + 1 + nbox = numx*numy + if ( nbox.gt.maxbox ) then + write(logid,*) 'ERROR: maxbox too small' + stop + endif + print*, 'numx, numy, nbox = ',numx, numy, nbox + allocate( llx_box(maxbox) ) + allocate( lly_box(maxbox) ) + allocate( res_box(maxbox) ) + allocate( mark_box(maxbox) ) + allocate( warea(maxbox) ) + mark_box(:) = 0 + mark_box(1:nbox) = 1 + ! allocate retrievals to corresponding box + do nr = 1, nretr + ix = floor((sum(xpoints(nr,:)/4.) - llx)/res) + 1 + jy = floor((sum(ypoints(nr,:)/4.) - lly)/res) + 1 + ixy = (jy - 1)*numx + ix + retr_nbox(nr) = ixy + if ( nr.lt.50 ) then + print*, 'xpoint, ypoint = ',sum(xpoints(nr,:))/4., sum(ypoints(nr,:))/4. + print*, 'ix, jy, ixy = ',ix,jy,ixy + endif + end do + ! calculate box coordinates + do nb = 1, nbox + jy = (nb - 1)/numx + 1 + ix = nb - (jy - 1)*numx + llx_box(nb) = llx + (ix - 1)*res + lly_box(nb) = lly + (jy - 1)*res + end do + !** test + print*, 'box coords:' + do nb = 1, 20 + print*, llx_box(nb), lly_box(nb) + end do + !** + res_box(:) = res + print*, 'retr_nbox(1:20) = ',retr_nbox(1:20) + + ! loop over resolution steps + ! -------------------------- + + do ns = settings%nsteps-1, 1, -1 + print*, 'STEP = ',ns + print*, '-----------' + res = stepres(ns) + print*, 'res = ',res + !--- loop over marked boxes + do nb = 1, nbox + if ( mark_box(nb).eq.0 ) cycle + print*, 'nb = ',nb + ! calculate sd of retrievals in each box + iretr(:) = 0 + num = 0 + do nr = 1, nretr + if ( retr_nbox(nr).eq.nb ) then + num = num + 1 + iretr(num) = nr + endif + end do + print*, 'num of retrievals in current box = ',num + if ( num.gt.1 ) then + box_mean = sum(vmr_out(iretr(1:num)))/real(num) + box_sd = sqrt(sum((vmr_out(iretr(1:num))-box_mean)**2)/real(num - 1)) + else if ( num.eq.1 ) then + box_mean = vmr_out(iretr(num)) + box_sd = 1. + else + box_mean = -999. + box_sd = -999. + endif + print*, 'box mean, sd = ',box_mean,box_sd + ! check if this box should be divided further + if ( (box_sd.lt.settings%cutoff) ) then + ! only keep if retrievals spread over all sub-boxes otherwise divide + num_subbox(:) = 0 + do nn = 1, num + nr = iretr(nn) + do n = 1, 4 + jy = (n - 1)/2 + 1 + ix = n - (jy - 1)*2 + if ( sum(ypoints(nr,:)/4.).gt.(lly_box(nb)+(jy-1)*res).and.sum(ypoints(nr,:)/4.).lt.(lly_box(nb)+jy*res).and.& + sum(xpoints(nr,:)/4.).gt.(llx_box(nb)+(ix-1)*res).and.sum(xpoints(nr,:)/4.).lt.(llx_box(nb)+ix*res) ) then + num_subbox(n) = num_subbox(n) + 1 + endif + end do ! n +! endif ! nb + end do ! nn + print*, 'num_subbox = ',num_subbox(:) + if ( all(num_subbox.gt.0) ) then + mark_box(nb) = 0 ! do not divide box + else if ( box_sd.eq.-999. ) then + mark_box(nb) = 0 ! no retrievals in box + endif + endif + end do ! nbox + !--- divide boxes + print*, 'DIVIDE' + print*, '------' + nbox_new = nbox + ! loop over boxes and divide + do nb = 1, nbox + if ( mark_box(nb).eq.0 ) cycle ! do not divide box + print*, 'nb = ',nb + ! divide box into 4 so add 3 new ones + ! first box keeps old nb, llx and lly bounds, but update res + res_box(nb) = res + ! new boxes get numbers nbox_nb + n + do n = 1, 3 + ! coordinates of new boxes + jy = n/2 + ix = n - jy*2 + llx_box(nbox_new+n) = llx_box(nb) + ix*res + lly_box(nbox_new+n) = lly_box(nb) + jy*res + res_box(nbox_new+n) = res + mark_box(nbox_new+n) = 1 + print*, 'nbox_new + n = ',nbox_new + n + print*, 'llx_box(nb) = ',llx_box(nb) + print*, 'llx_box(nbox_new+n) = ',llx_box(nbox_new+n) + print*, 'lly_box(nb) = ',lly_box(nb) + print*, 'lly_box(nbox_new+n) = ',lly_box(nbox_new+n) + end do + ! update retrieval box numbers + do nr = 1, nretr + if ( retr_nbox(nr).ne.nb ) cycle + ix = floor((sum(xpoints(nr,:)/4.) - llx_box(nb))/res) ! not +1 because resuse nbox number for first grid in divided box + jy = floor((sum(ypoints(nr,:)/4.) - lly_box(nb))/res) + print*, 'update retrieval box no.s: ix, jy = ',ix,jy + print*, 'retr_nbox(nr): old = ',retr_nbox(nr) + if ( .not.((ix.eq.0).and.(jy.eq.0)) ) retr_nbox(nr) = nbox_new + jy*2 + ix + print*, 'retr_nbox(nr): new = ',retr_nbox(nr) + end do + nbox_new = nbox_new + 3 + if ( nbox_new.gt.maxbox ) then + write(logid,*) 'ERROR: maxbox too small' + stop + endif + end do ! nb + ! update nbox + nbox = nbox_new + print*, 'nbox_new = ',nbox_new + end do ! ns + + ! TESTING +! open(100,file=trim(settings%filegrid),action='write',status='replace',iostat=ierr) +! write(100,*) 'llx_box lly_box res_box' +! print*, 'llx_box(1) = ',llx_box(1) +! do nb = 1, nbox +! write(100,fmt='(F7.2,1X,F7.2,1X,F7.2)') llx_box(nb), lly_box(nb), res_box(nb) +! end do +! close(100) +! open(100,file=trim(settings%path_obsout)//'retr_nbox_orig.txt',action='write',status='replace',iostat=ierr) +! do nr = 1, nretr +! write(100,fmt='(I8)') retr_nbox(nr) +! end do +! close(100) + +10 continue + if ( settings%usegridfile ) then + allocate( llx_box(maxbox) ) + allocate( lly_box(maxbox) ) + allocate( res_box(maxbox) ) + allocate( retr_nbox(nretr) ) + allocate( iretr(nretr) ) + ! read llx_box, lly_box, res_box from file + write(logid,*) 'Reading averaging grid from file' + open(100,file=trim(settings%filegrid),status='old',action='read',iostat=ierr) + if ( ierr.eq.0 ) then + read(100,*) line + do n = 1, maxbox + read(100,fmt='(F7.2,1X,F7.2,1X,F7.2)',iostat=ierr) llx_box(n), lly_box(n), res_box(n) + if ( ierr.ne.0 ) exit + end do + nbox = n - 1 + print*, 'nbox = ',nbox + close(100) + else + write(logid,*) 'ERROR: file not found ',settings%filegrid + stop + endif + ! allocate retrievals to corresponding box + print*, 'nretr = ',nretr + do nb = 1, nbox + do nr = 1, nretr + xav = sum(xpoints(nr,:))/4. + yav = sum(ypoints(nr,:))/4. + if ( (xav.gt.llx_box(nb)).and.(xav.lt.(llx_box(nb)+res_box(nb))).and. & + (yav.gt.lly_box(nb)).and.(yav.lt.(lly_box(nb)+res_box(nb))) ) then + retr_nbox(nr) = nb + endif + end do + end do + ! TESTING +! open(100,file=trim(settings%path_obsout)//'retr_nbox_file.txt',action='write',status='replace',iostat=ierr) +! do nr = 1, nretr +! write(100,fmt='(I8)') retr_nbox(nr) +! end do +! close(100) + endif ! usegridfile + + ! prepare output + ! -------------- + + allocate( vmr_mean(nbox) ) + allocate( vmr_sd(nbox) ) + allocate( cpri_mean(nbox) ) + allocate( cdryair_mean(nbox) ) + allocate( cakpri_mean(nbox) ) + allocate( cak_mean(nbox,nlayer) ) + allocate( vdryair_mean(nbox,nlayer) ) + allocate( xpts_box(nbox,ncoord) ) + allocate( ypts_box(nbox,ncoord) ) + allocate( zpt1_box(nbox,nlayer) ) + allocate( zpt2_box(nbox,nlayer) ) + allocate( idate_mean(nbox) ) + allocate( itime_mean(nbox) ) + + ! loop over boxes + numbox = 0 + do nb = 1, nbox + ! mean and sd of retrievals in each box + num = 0 + iretr(:) = 0 + do nr = 1, nretr + if ( retr_nbox(nr).eq.nb ) then + num = num + 1 + iretr(num) = nr + endif + end do + if ( num.ge.settings%nmin ) then + ! only use boxes with > nmin retrievals + ! area weighting + warea(:) = 0. + do n = 1, num + call geodarea(xpoints(iretr(n),:), ypoints(iretr(n),:), warea(n)) + end do + warea(1:num) = warea(1:num)/sum(warea(1:num)) + print*, 'sum(warea) = ',sum(warea(1:num)) + print*, 'range(warea) = ',minval(warea(1:num)),maxval(warea(1:num)) + box_mean = dot_product(warea(1:num), vmr_out(iretr(1:num))) + if ( num.eq.1 ) then + box_sd = verr_out(iretr(num)) + else + box_sd = sqrt(sum((vmr_out(iretr(1:num))-box_mean)**2)/real(num - 1)) + endif + box_err = sqrt(dot_product(warea(1:num), verr_out(iretr(1:num))**2)) + ! only keep retrievals within +/- 2 sd of mean + nn = 0 + iretr_keep(:) = 0 + do n = 1, num + if ( vmr_out(iretr(n)).gt.(box_mean-2.*box_sd).and.vmr_out(iretr(n)).lt.(box_mean+2.*box_sd) ) then + nn = nn + 1 + iretr_keep(nn) = iretr(n) + endif + end do + ! calculate mean of kept retrievals + warea(:) = 0. + do n = 1, nn + call geodarea(xpoints(iretr_keep(n),:), ypoints(iretr_keep(n),:), warea(n)) + end do + warea(1:nn) = warea(1:nn)/sum(warea(1:nn)) + print*, 'sum(warea) = ',sum(warea(1:nn)) + print*, 'range(warea) = ',minval(warea(1:nn)),maxval(warea(1:nn)) + box_mean = dot_product(warea(1:nn), vmr_out(iretr_keep(1:nn))) + if ( nn.eq.1 ) then + box_sd = vmr_out(iretr_keep(nn)) + else + box_sd = sqrt(sum((vmr_out(iretr_keep(1:nn))-box_mean)**2)/real(nn - 1)) + endif + box_err = sqrt(dot_product(warea(1:nn), verr_out(iretr_keep(1:nn))**2)) + ! for below use num and iretr + iretr(:) = 0 + iretr(:) = iretr_keep(:) + ! reset and update for this box + num = nn + numbox = numbox + 1 + else + ! reject this box -> too few retrievals + cycle + endif + ! mean column mixing ratio + vmr_mean(numbox) = box_mean + ! mean column uncertainty + vmr_sd(numbox) = box_err + ! mean column prior convolved with averaging kernel + rwork = 0. + do n = 1, num + if ( settings%satellite.eq.'tropomi' ) then + rwork = rwork + warea(n)*dot_product(cak_out(iretr(n),:),vapri_out(iretr(n),:)) + else if (settings%satellite.eq.'bremen' ) then + do nl = 1, nlayer + rwork = rwork + warea(n)*vdryair_out(iretr(n),nl)*vapri_out(iretr(n),nl)*cak_out(iretr(n),nl) + end do + endif + end do + cakpri_mean(numbox) = rwork + ! mean column prior + cpri_mean(numbox) = dot_product(warea(1:num),cpri(iretr(1:num))) + ! mean averaging kernel + cak_mean(numbox,:) = matmul(warea(1:num),cak_out(iretr(1:num),:)) + ! mean column dry air concentration + cdryair_mean(numbox) = dot_product(warea(1:num),cdryair(iretr(1:num))) + ! mean dry air concentration + vdryair_mean(numbox,:) = matmul(warea(1:num),vdryair_out(iretr(1:num),:)) + ! box coordinates + xpts_box(numbox,(/1,4/)) = llx_box(nb) + xpts_box(numbox,(/2,3/)) = llx_box(nb) + res_box(nb) + ypts_box(numbox,(/1,2/)) = lly_box(nb) + ypts_box(numbox,(/3,4/)) = lly_box(nb) + res_box(nb) + zpt1_box(numbox,:) = matmul(warea(1:num),zpoint1(iretr(1:num),:)) + zpt2_box(numbox,:) = matmul(warea(1:num),zpoint2(iretr(1:num),:)) + ! date and time + jdmean = 0d0 + do n = 1, num + jdmean = jdmean + juldate(idate(iretr(n)),itime(iretr(n))) + end do + jdmean = jdmean/dble(num) + call caldate(jdmean, jjjjmmdd, hhmiss) + idate_mean(numbox) = jjjjmmdd + itime_mean(numbox) = hhmiss + end do + print*, 'zpt1_box(1:10,:) = ',zpt1_box(1:10,:) + nretr = numbox + write(logid,*) 'Number of averaged retrievals = ',nretr + print*, 'nretr = ',nretr + vmr_out(1:nretr) = vmr_mean(1:nretr) + verr_out(1:nretr) = vmr_sd(1:nretr) + cdryair(1:nretr) = cdryair_mean(1:nretr) + vdryair_out(1:nretr,:) = vdryair_mean(1:nretr,:) + cak_out(1:nretr,:) = cak_mean(1:nretr,:) + cpri(1:nretr) = cpri_mean(1:nretr) + cakpri(1:nretr) = cakpri_mean(1:nretr) + xpoints(1:nretr,:) = xpts_box(1:nretr,:) + ypoints(1:nretr,:) = ypts_box(1:nretr,:) + zpoint1(1:nretr,:) = zpt1_box(1:nretr,:) + zpoint2(1:nretr,:) = zpt2_box(1:nretr,:) + idate(1:nretr) = idate_mean(1:nretr) + itime(1:nretr) = itime_mean(1:nretr) + deallocate( vmr_mean ) + deallocate( vmr_sd ) + deallocate( cdryair_mean ) + deallocate( vdryair_mean ) + deallocate( cak_mean ) + deallocate( cpri_mean ) + deallocate( cakpri_mean ) + deallocate( xpts_box ) + deallocate( ypts_box ) + deallocate( zpt1_box ) + deallocate( zpt2_box ) + deallocate( warea ) + deallocate( idate_mean ) + deallocate( itime_mean ) + +end subroutine average + diff --git a/prep_satellite/geodarea.f90 b/prep_satellite/geodarea.f90 new file mode 100644 index 0000000..34ce0bd --- /dev/null +++ b/prep_satellite/geodarea.f90 @@ -0,0 +1,61 @@ +!--------------------------------------------------------------------------------------- +! PREP_FLUXES: geodarea +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> geodarea +!! +!! Purpose: Calculates the area of a rectangle assuming spherical earth. +!! +!! Interface: +!! +!! Inputs +!! ypoints - latitudinal coordinates (length 4) +!! xpoints - longitudinal coordinates (length 4) +!! +!! Outputs +!! area - area of grid cell +!! +!--------------------------------------------------------------------------------------- + +subroutine geodarea(xpoints,ypoints,area) + + implicit none + + real, dimension(4), intent(in) :: xpoints, ypoints + real, intent(out) :: area + + real, parameter :: pi=3.14159265, rearth=6.371e6, pih=pi/180. + real :: work, distx, disty + real, dimension(4) :: rlons, rlats + + ! distance between two points using Haversine formula + rlons = xpoints*pih + rlats = ypoints*pih + ! longitude distance + work = cos(rlats(1))*cos(rlats(2))*(sin(rlons(2) - rlons(1))/2.)**2 + work = sin((rlats(2) - rlats(1))/2.)**2 + work + distx = 2.*rearth*asin(sqrt(work)) + ! latitude distance + work = cos(rlats(2))*cos(rlats(3))*(sin(rlons(3) - rlons(2))/2.)**2 + work = sin((rlats(3) - rlats(2))/2.)**2 + work + disty = 2.*rearth*asin(sqrt(work)) + work = tan(distx/(2.*rearth))*tan(disty/(2.*rearth)) + area = 4.*rearth**2*asin(work) + +end subroutine geodarea + diff --git a/prep_satellite/get_bremen.f90 b/prep_satellite/get_bremen.f90 new file mode 100644 index 0000000..e428896 --- /dev/null +++ b/prep_satellite/get_bremen.f90 @@ -0,0 +1,431 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: get_bremen +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> get_bremen +!! +!! Purpose: Reads observations and auxilliary data from the specified satellite and +!! and writes the output into files that can be directly used by FLEXPART +!! and, for inversions, by FLEXINVERT +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! +!! Outputs +!! +!! Externals +!! +!--------------------------------------------------------------------------------------- + +subroutine get_bremen(settings) + + use mod_settings + use mod_var + use mod_dates + use mod_save + use netcdf + + implicit none + + type (settings_t), intent(in) :: settings + + character(max_name_len), dimension(:), allocatable :: filelist, filereadlist + character(max_name_len) :: line, filename + character(len=8) :: adate + character(len=6) :: atime + character(len=3) :: aspec + character(len=6), dimension(4) :: units + integer :: i, ierr, n, nf, nl, nm, np, nt, nbits, iswater + integer :: nfiles, nread, nretr, npx_in_scan + real(kind=8) :: jdatei, jdatef, jd, days_since_ref, jdateref, jdmeas + real :: sec_since_midnight + integer :: jjjjmmdd, hhmiss, hh, mi, ss + integer, parameter :: refdate = 19700101 + integer, parameter :: lsynctime = 600 + integer :: ntime, nmeas, npixel, nlevel, ncoord, nlayer + real, dimension(:,:), allocatable :: lat_bnds, lon_bnds + real, dimension(:), allocatable :: vmr, verr, time, landfrac, qa + real, dimension(:,:), allocatable :: pres, vapri, vdryair, cak + real, dimension(:,:), allocatable :: xpoints, ypoints + real, dimension(:,:), allocatable :: zpoint1, zpoint2 + real, dimension(:,:), allocatable :: cak_out, vapri_out, vdryair_out + real, dimension(:), allocatable :: vmr_out, verr_out + real, dimension(:), allocatable :: cpri, cakpri, cdryair + integer, dimension(:), allocatable :: idate, itime + integer :: specnum, nchar + integer :: ncid, dimid, varid + real :: nest_llx, nest_lly, nest_urx, nest_ury + real :: llx, lly, urx, ury + + ! get species number + ! ------------------ + + open(100,file=trim(settings%path_flexpart)//'options/SPECIES/spec_overview',status='old',action='read',iostat=ierr) + nchar = len_trim(settings%species) + do while ( ierr.eq.0 ) + read (100, fmt='(A)', iostat=ierr) line + if ( line(len_trim(line)-nchar+1:len_trim(line)) == trim(settings%species) ) ierr = 1 + end do + read(line(9:11),*) specnum + write(logid,*) 'species: ',settings%species + write(logid,*) 'specnum: ',specnum + write(logid,*) 'qa cutoff: ',settings%qa_cutoff + close(100) + + ! define domains + ! -------------------- + + llx = settings%outlonleft + lly = settings%outlatlower + urx = llx + settings%numxgrid*settings%dxout + ury = lly + settings%numygrid*settings%dyout + + if ( settings%lnested.eq.1 ) then + nest_llx = settings%outlonnest + nest_lly = settings%outlatnest + nest_urx = nest_llx + settings%numxnest*settings%dxoutnest + nest_ury = nest_lly + settings%numynest*settings%dyoutnest + endif + print*, 'llx,lly,urx,ury = ',llx,lly,urx,ury + if (settings%lnested.eq.1) print*, 'nest_llx,nest_lly,nest_urx,nest_ury = ',nest_llx,nest_lly,nest_urx,nest_ury + + ! list satellite files + ! ---------------------- + + call system('find '//trim(settings%path_obs)//' -type f | grep '//trim(settings%suffix)//& + ' | wc -l > '//trim(settings%path_obsout)//'obsfiles.txt') + call system('find '//trim(settings%path_obs)//' -type f | grep '//trim(settings%suffix)//& + ' >> '//trim(settings%path_obsout)//'obsfiles.txt') + + open(100,file=trim(settings%path_obsout)//'obsfiles.txt',action='read',status='old',iostat=ierr) + if ( ierr.ne. 0 ) then + write(logid,*) 'ERROR: cannot open obsfiles.txt' + stop + endif + read(100,*,iostat=ierr) nfiles + allocate ( filelist(nfiles), stat = ierr ) + allocate ( filereadlist(nfiles), stat = ierr ) + do nf = 1, nfiles + read(100,fmt='(A)',iostat=ierr) filelist(nf) + if (ierr.ne.0) exit + end do + close(100) + + ! loop over days + ! -------------- + + jdatei = juldate(settings%datei, 0) + jdatef = juldate(settings%datef, 0) + jd = jdatei + + read_loop: do + + if ( jd.gt.jdatef ) exit read_loop + + ! search files for current date + call caldate(jd, jjjjmmdd, hhmiss) + write(adate,fmt='(I8.8)') jjjjmmdd + write(logid,*) 'current day: ',adate + nread = 0 + do n = 1, nfiles + do i = 1, len_trim(filelist(n)) + if ( filelist(n)(i:(i+7)).eq.adate ) then + nread = nread + 1 + filereadlist(nread) = filelist(n) + exit + endif + end do + end do + write(logid,*) 'number of files for current day: ',nread + if ( nread.eq.0 ) then + jd = jd + 1d0 + cycle read_loop + endif + + ! loop over files + ! --------------- + + do n = 1, nread + + ! open file + write(logid,*) 'reading file: ',filereadlist(n) + call check( nf90_open(trim(filereadlist(n)),nf90_NOWRITE,ncid) ) + + ! dimension variables + call check( nf90_inq_dimid(ncid,trim(settings%nmeas_name),dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=nmeas) ) + write(logid,*) 'number of retrievals: ',nmeas + call check( nf90_inq_dimid(ncid,trim(settings%ncoord_name),dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=ncoord) ) + write(logid,*) 'number of geocoordinates: ',ncoord + call check( nf90_inq_dimid(ncid,trim(settings%nlevel_name),dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=nlevel) ) + nlayer = nlevel - 1 + write(logid,*) 'number of vertical layers: ',nlayer + + ! geolocation variables + allocate( lat_bnds(ncoord,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate lat_bnds' + call check( nf90_inq_varid(ncid,trim(settings%lat_name),varid) ) + call check( nf90_get_var(ncid,varid,lat_bnds) ) + allocate( lon_bnds(ncoord,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate lon_bnds' + call check( nf90_inq_varid(ncid,trim(settings%lon_name),varid) ) + call check( nf90_get_var(ncid,varid,lon_bnds) ) + + ! product variables + allocate( time(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate time' + call check( nf90_inq_varid(ncid,trim(settings%time_name),varid) ) + call check( nf90_get_var(ncid,varid,time) ) + allocate( pres(nlevel,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate pres' + call check( nf90_inq_varid(ncid,trim(settings%pdel_name),varid) ) + call check( nf90_get_var(ncid,varid,pres) ) + allocate( qa(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate qa' + call check( nf90_inq_varid(ncid,trim(settings%qa_name),varid) ) + call check( nf90_get_var(ncid,varid,qa) ) + allocate( vmr(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vmr' + call check( nf90_inq_varid(ncid,trim(settings%vmr_name),varid) ) + call check( nf90_get_var(ncid,varid,vmr) ) + allocate( verr(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate verr' + call check( nf90_inq_varid(ncid,trim(settings%verr_name),varid) ) + call check( nf90_get_var(ncid,varid,verr) ) + ! convert hPa -> Pa for consistency with flexpart + pres = pres*100. + + ! detailed results variables + allocate( cak(nlayer,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate cak' + call check( nf90_inq_varid(ncid,trim(settings%ak_name),varid) ) + call check( nf90_get_var(ncid,varid,cak) ) + allocate( vapri(nlayer,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vapri' + call check( nf90_inq_varid(ncid,trim(settings%apri_name),varid) ) + call check( nf90_get_var(ncid,varid,vapri) ) + allocate( vdryair(nlayer,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vdryair' + call check( nf90_inq_varid(ncid,trim(settings%dryair_name),varid) ) + call check( nf90_get_var(ncid,varid,vdryair) ) + allocate( landfrac(nmeas), stat=ierr ) + call check( nf90_inq_varid(ncid,trim(settings%landfrac_name),varid) ) + call check( nf90_get_var(ncid,varid,landfrac) ) + + call check( nf90_close(ncid)) + + ! select retrievals and extract needed info + if( n.eq.1 ) then + allocate( xpoints(maxretr,ncoord) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate xpoints' + allocate( ypoints(maxretr,ncoord) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate ypoints' + allocate( zpoint1(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate zpoint1' + allocate( zpoint2(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate zpoint2' + allocate( idate(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate idate' + allocate( itime(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate itime' + allocate( cak_out(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate cak_out' + allocate( vapri_out(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vapri_out' + allocate( vdryair_out(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vdryair_out' + allocate( vmr_out(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vmr_out' + allocate( verr_out(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate verr_out' + nretr = 0 + endif + do nm = 1, nmeas + ! exclude retrievals above quality cutoff + if ( qa(nm).gt.settings%qa_cutoff ) cycle + ! if landonly exclude pixels with less than 50% land + if ( settings%landonly.and.landfrac(nm).lt.50. ) cycle + if ( settings%lnested.eq.1 ) then + ! if nested domain exclude pixels outside + if ( any(lon_bnds(:,nm).lt.nest_llx).or. & + any(lon_bnds(:,nm).gt.nest_urx).or. & + any(lat_bnds(:,nm).lt.nest_lly).or. & + any(lat_bnds(:,nm).gt.nest_ury) ) cycle + else + ! if not nested exclude pixels outside global domain + if ( any(lon_bnds(:,nm).lt.llx).or. & + any(lon_bnds(:,nm).gt.urx).or. & + any(lat_bnds(:,nm).lt.lly).or. & + any(lat_bnds(:,nm).gt.ury) ) cycle + endif + jdateref = juldate(refdate, 0) + days_since_ref = jd - jdateref + sec_since_midnight = time(nm) - sngl(days_since_ref)*24.*3600. + jdmeas = jd + dble(sec_since_midnight/24./3600.) + ! exclude retrievals if not for current day + if ( floor(jdmeas).ne.jd ) cycle + call caldate(jdmeas, jjjjmmdd, hhmiss) + nretr = nretr + 1 + idate(nretr) = jjjjmmdd + itime(nretr) = hhmiss + ! corners ordered anticlockwise from lower left + xpoints(nretr,:) = lon_bnds(:,nm) + ypoints(nretr,:) = lat_bnds(:,nm) + ! pressure levels + do nl = 1, nlevel + if ( nl.lt.nlevel ) zpoint1(nretr,nl) = pres(nl,nm) + if ( nl.gt.1 ) zpoint2(nretr,nl-1) = pres(nl,nm) + end do + cak_out(nretr,:) = cak(:,nm) + vapri_out(nretr,:) = vapri(:,nm) + vdryair_out(nretr,:) = vdryair(:,nm) + vmr_out(nretr) = vmr(nm) + ! unlike official retrieval no factor of 2 necessary for error + verr_out(nretr) = verr(nm) + end do + print*, 'range(pres) = ',minval(pres),maxval(pres) + print*, 'range(zpoint1) = ',minval(zpoint1),maxval(zpoint1) + + write(logid,*) 'number of retrievals: ',nretr + + if ( allocated(lat_bnds) ) deallocate(lat_bnds) + if ( allocated(lon_bnds) ) deallocate(lon_bnds) + if ( allocated(vapri) ) deallocate(vapri) + if ( allocated(vdryair) ) deallocate(vdryair) + if ( allocated(cak) ) deallocate(cak) + if ( allocated(time) ) deallocate(time) + if ( allocated(pres) ) deallocate(pres) + if ( allocated(qa) ) deallocate(qa) + if ( allocated(vmr) ) deallocate(vmr) + if ( allocated(verr) ) deallocate(verr) + if ( allocated(landfrac) ) deallocate(landfrac) + + end do ! nread + + if ( nretr.eq.0 ) go to 10 + + ! calculate column prior (units ppbv) + allocate( cpri(nretr), stat=ierr ) + do nm = 1, nretr + cpri(nm) = dot_product(vapri_out(nm,:), vdryair_out(nm,:)) + end do + + !! test + print*, 'vdryair_out(1:10,:) = ',vdryair_out(1:10,:) + + ! calculate column prior convolved with averaging kernel (units ppbv) + allocate( cakpri(nretr), stat=ierr ) + do nm = 1, nretr + do nl = 1, nlayer + cakpri(nm) = cakpri(nm) + vdryair_out(nm,nl)*vapri_out(nm,nl)*cak_out(nm,nl) + end do + end do + + ! column dry-air not needed but only as dummy variable + allocate( cdryair(nretr) ) + cdryair(:) = 1. + + ! average pixels + ! -------------- + + if ( settings%avg_pixels ) then + call average(settings, idate, itime, xpoints, ypoints, zpoint1, zpoint2, vmr_out, verr_out,& + vapri_out, cpri, cakpri, cak_out, cdryair, vdryair_out, nretr, ncoord, nlayer) + endif + + ! write output data + ! ----------------- + + ! adjust itime + ! needed so that with flexpart rounding to lsynctime all retrievals are still for current day + do nm = 1, nretr + if ( itime(nm).gt.(235959-lsynctime*100/60) ) then + itime(nm) = 235950-lsynctime*100/60 + endif + end do + + ! write command file + call prep_command(settings,jd) + + ! write outgrid file + call prep_outgrid(settings,jd) + + ! write ageclasses file + call prep_ageclass(settings,jd) + + ! write pathnames file + call prep_pathnames(settings,jd) + + ! units of output data: dryair, cdryair, cpri, vmr + units(1) = 'none' + units(2) = 'none' + units(3) = 'ppbv' + units(4) = 'ppbv' + + ! write releases data for flexpart + call write_releases(settings,nretr,nlayer,ncoord,xpoints,ypoints,zpoint1,zpoint2,& + idate,itime,cak_out,vdryair_out,specnum,units) + + ! write retrieval data for flexinvert + call write_retrieval(settings,nretr,ncoord,xpoints,ypoints,idate,itime,& + cpri,cakpri,vmr_out,verr_out,cdryair,units) + + ! copy standard input files to options folder + write(aspec,fmt='(I3.3)') specnum + call system('mkdir -p '//trim(settings%path_options)//adate//'/options/SPECIES/') + filename = trim(settings%path_flexpart)//'options/SPECIES/SPECIES_'//aspec + call system('cp '//trim(filename)//' '//trim(settings%path_options)//adate//'/options/SPECIES/') + call system('cp '//trim(settings%path_flexpart)//'options/*.dat'//' '//trim(settings%path_options)//adate//'/options/') + call system('cp '//trim(settings%path_flexpart)//'options/*.t'//' '//trim(settings%path_options)//adate//'/options/') + + ! no retrievals on this day +10 continue + + ! prepare for next day + ! -------------------- + + if ( allocated(xpoints) ) deallocate(xpoints) + if ( allocated(ypoints) ) deallocate(ypoints) + if ( allocated(zpoint1) ) deallocate(zpoint1) + if ( allocated(zpoint2) ) deallocate(zpoint2) + if ( allocated(idate) ) deallocate(idate) + if ( allocated(itime) ) deallocate(itime) + if ( allocated(cak_out) ) deallocate(cak_out) + if ( allocated(vapri_out) ) deallocate(vapri_out) + if ( allocated(vdryair_out) ) deallocate(vdryair_out) + if ( allocated(vmr_out) ) deallocate(vmr_out) + if ( allocated(verr_out) ) deallocate(verr_out) + if ( allocated(cpri) ) deallocate(cpri) + if ( allocated(cakpri) ) deallocate(cakpri) + if ( allocated(cdryair) ) deallocate(cdryair) + + ! iterate over days + jd = jd + 1d0 + + end do read_loop + + if ( allocated(filelist) ) deallocate(filelist) + if ( allocated(filereadlist) ) deallocate(filereadlist) + + +end subroutine get_bremen + diff --git a/prep_satellite/get_oco2.f90 b/prep_satellite/get_oco2.f90 new file mode 100644 index 0000000..e206ef5 --- /dev/null +++ b/prep_satellite/get_oco2.f90 @@ -0,0 +1,465 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: get_oco2 +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> get_oco2 +!! +!! Purpose: Reads observations and auxilliary data from the specified satellite and +!! and writes the output into files that can be directly used by FLEXPART +!! and, for inversions, by FLEXINVERT +!! +!! Note: Variables with vertical dimension are ordered from TOA to the surface +!! Retrieval made at pressure levels (interface of layers) +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! +!! Outputs +!! +!! Externals +!! +!--------------------------------------------------------------------------------------- + +subroutine get_oco2(settings) + + use mod_settings + use mod_var + use mod_dates + use mod_save + use netcdf + + implicit none + + type (settings_t), intent(in) :: settings + + character(max_name_len), dimension(:), allocatable :: filelist, filereadlist + character(max_name_len) :: line, filename, string, grpname + character(len=8) :: adate, strtmp + character(len=6) :: atime + character(len=3) :: aspec + character(len=6), dimension(4) :: units + integer :: i, ierr, n, nf, nl, ln, nm, np, nt + integer :: nfiles, nread, nretr + real(kind=8) :: jdatei, jdatef, jd, jdateref, jdmeas + integer :: jjjjmmdd, jjmmdd, hhmiss + integer :: hh, mi, ss + integer, parameter :: refdate = 19700101 + integer, parameter :: lsynctime = 600 + integer :: ntime, nmeas, nlevel, ncoord + real(kind=8), dimension(:), allocatable :: time + real, dimension(:,:), allocatable :: lat_bnds, lon_bnds + real, dimension(:), allocatable :: vmr, verr, landfrac, qa + real, dimension(:,:), allocatable :: pres, vapri, vdryair, cak + real, dimension(:,:), allocatable :: xpoints, ypoints + real, dimension(:,:), allocatable :: zpoint1, zpoint2 + real, dimension(:,:), allocatable :: cak_out, vapri_out, vdryair_out + real, dimension(:), allocatable :: vmr_out, verr_out + real, dimension(:), allocatable :: cpri, cakpri, cdryair + integer, dimension(:), allocatable :: idate, itime, oper_mode + integer :: specnum, nchar + integer :: ncid, grpid, dimid, varid + real :: nest_llx, nest_lly, nest_urx, nest_ury + real :: llx, lly, urx, ury + + ! get species number + ! ------------------ + + open(100,file=trim(settings%path_flexpart)//'options/SPECIES/spec_overview',status='old',action='read',iostat=ierr) + nchar = len_trim(settings%species) + do while ( ierr.eq.0 ) + read (100, fmt='(A)', iostat=ierr) line + if ( line(len_trim(line)-nchar+1:len_trim(line)) == trim(settings%species) ) ierr = 1 + end do + read(line(9:11),*) specnum + write(logid,*) 'species: ',settings%species + write(logid,*) 'specnum: ',specnum + write(logid,*) 'qa cutoff: ',settings%qa_cutoff + close(100) + + ! define domains + ! -------------------- + + llx = settings%outlonleft + lly = settings%outlatlower + urx = llx + settings%numxgrid*settings%dxout + ury = lly + settings%numygrid*settings%dyout + + if ( settings%lnested.eq.1 ) then + nest_llx = settings%outlonnest + nest_lly = settings%outlatnest + nest_urx = nest_llx + settings%numxnest*settings%dxoutnest + nest_ury = nest_lly + settings%numynest*settings%dyoutnest + endif + print*, 'llx,lly,urx,ury = ',llx,lly,urx,ury + if (settings%lnested.eq.1) print*, 'nest_llx,nest_lly,nest_urx,nest_ury = ',nest_llx,nest_lly,nest_urx,nest_ury + + ! list satellite files + ! ---------------------- + + call system('find '//trim(settings%path_obs)//' -type f | grep '//trim(settings%suffix)//& + ' | wc -l > '//trim(settings%path_obsout)//'obsfiles.txt') + call system('find '//trim(settings%path_obs)//' -type f | grep '//trim(settings%suffix)//& + ' >> '//trim(settings%path_obsout)//'obsfiles.txt') + + open(100,file=trim(settings%path_obsout)//'obsfiles.txt',action='read',status='old',iostat=ierr) + if ( ierr.ne. 0 ) then + write(logid,*) 'ERROR: cannot open obsfiles.txt' + stop + endif + read(100,*,iostat=ierr) nfiles + allocate ( filelist(nfiles), stat = ierr ) + allocate ( filereadlist(nfiles), stat = ierr ) + do nf = 1, nfiles + read(100,fmt='(A)',iostat=ierr) filelist(nf) + if (ierr.ne.0) exit + end do + close(100) + + ! loop over days + ! -------------- + + jdateref = juldate(refdate, 0) + jdatei = juldate(settings%datei, 0) + jdatef = juldate(settings%datef, 0) + jd = jdatei + + print*, settings%datei, settings%datef + + read_loop: do + + if ( jd.gt.jdatef ) exit read_loop + + ! search files for current date + call caldate(jd, jjjjmmdd, hhmiss) + write(strtmp,fmt='(I8)') jjjjmmdd + adate = strtmp(3:8) + write(logid,*) 'current day: ',trim(adate) + nread = 0 + do n = 1, nfiles + do i = 1, len_trim(filelist(n)) + if ( filelist(n)(i:(i+5)).eq.trim(adate) ) then + nread = nread + 1 + filereadlist(nread) = filelist(n) + exit + endif + end do + end do + write(logid,*) 'number of files for current day: ',nread + if ( nread.eq.0 ) then + jd = jd + 1d0 + cycle read_loop + endif + adate = strtmp + + ! loop over files + ! --------------- + + do n = 1, nread + + ! open file + write(logid,*) 'reading file: ',filereadlist(n) + call check( nf90_open(trim(filereadlist(n)),nf90_NOWRITE,ncid) ) + + ! dimension variables + call check( nf90_inq_dimid(ncid,trim(settings%nmeas_name),dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=nmeas) ) + write(logid,*) 'number of soundings: ',nmeas + call check( nf90_inq_dimid(ncid,trim(settings%ncoord_name),dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=ncoord) ) + write(logid,*) 'number of geocoordinates: ',ncoord + call check( nf90_inq_dimid(ncid,trim(settings%nlevel_name),dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=nlevel) ) + write(logid,*) 'number of vertical levels: ',nlevel + + ! geolocation variables + allocate( lat_bnds(ncoord,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate lat_bnds' + call check( nf90_inq_varid(ncid,trim(settings%lat_name),varid) ) + call check( nf90_get_var(ncid,varid,lat_bnds) ) + allocate( lon_bnds(ncoord,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate lon_bnds' + call check( nf90_inq_varid(ncid,trim(settings%lon_name),varid) ) + call check( nf90_get_var(ncid,varid,lon_bnds) ) + + ! sounding variables + allocate(oper_mode(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate oper_mode' + string = settings%oper_name + call split(string,'/',grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_varid(grpid,trim(string),varid) ) + call check( nf90_get_var(grpid,varid,oper_mode) ) + print*, 'read oper mode' + allocate( landfrac(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate landfrac' + string = settings%landfrac_name + call split(string,'/',grpname) + print*, 'grpname = ',grpname + print*, 'string = ',string + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + print*, 'grpid = ',grpid + call check( nf90_inq_varid(grpid,trim(string),varid) ) + print*, 'varid = ',varid + call check( nf90_get_var(grpid,varid,landfrac) ) + print*, 'read land fraction' + + ! product variables + allocate( time(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate time' + call check( nf90_inq_varid(ncid,trim(settings%time_name),varid) ) + call check( nf90_get_var(ncid,varid,time) ) + allocate( pres(nlevel,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate pres' + call check( nf90_inq_varid(ncid,trim(settings%pdel_name),varid) ) + call check( nf90_get_var(ncid,varid,pres) ) + ! convert hPa -> Pa for consistency with flexpart + pres = pres*100. + allocate( qa(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate qa' + call check( nf90_inq_varid(ncid,trim(settings%qa_name),varid) ) + call check( nf90_get_var(ncid,varid,qa) ) + allocate( vmr(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vmr' + call check( nf90_inq_varid(ncid,trim(settings%vmr_name),varid) ) + call check( nf90_get_var(ncid,varid,vmr) ) + allocate( verr(nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate verr' + call check( nf90_inq_varid(ncid,trim(settings%verr_name),varid) ) + call check( nf90_get_var(ncid,varid,verr) ) + ! multiply uncertainty by factor of 2 according to documentation + verr = verr*2. + allocate( cak(nlevel,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate cak' + call check( nf90_inq_varid(ncid,trim(settings%ak_name),varid) ) + call check( nf90_get_var(ncid,varid,cak) ) + allocate( vapri(nlevel,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vapri' + call check( nf90_inq_varid(ncid,trim(settings%apri_name),varid) ) + call check( nf90_get_var(ncid,varid,vapri) ) + allocate( vdryair(nlevel,nmeas), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vdryair' + call check( nf90_inq_varid(ncid,trim(settings%dryair_name),varid) ) + call check( nf90_get_var(ncid,varid,vdryair) ) + + call check( nf90_close(ncid)) + + ! select retrievals and extract needed info + if( n.eq.1 ) then + allocate( xpoints(maxretr,ncoord) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate xpoints' + allocate( ypoints(maxretr,ncoord) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate ypoints' + allocate( zpoint1(maxretr,nlevel) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate zpoint1' + allocate( zpoint2(maxretr,nlevel) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate zpoint2' + allocate( idate(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate idate' + allocate( itime(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate itime' + allocate( cak_out(maxretr,nlevel) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate cak_out' + allocate( vapri_out(maxretr,nlevel) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vapri_out' + allocate( vdryair_out(maxretr,nlevel) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vdryair_out' + allocate( vmr_out(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vmr_out' + allocate( verr_out(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate verr_out' + nretr = 0 + + ! checking + print*, 'range(landfrac) = ',minval(landfrac),maxval(landfrac) + print*, 'range(qa) = ',minval(qa),maxval(qa) + print*, 'range(vmr) = ',minval(vmr),maxval(vmr) + print*, 'range(verr) = ',minval(verr),maxval(verr) + + endif + do nm = 1, nmeas + ! exclude retrievals from certain operation mode + if ( (oper_mode(nm).ne.settings%opmode_sel).and.(settings%opmode_sel.ge.0) ) cycle + ! exclude retrievals above quality cutoff + if ( qa(nm).gt.settings%qa_cutoff ) cycle + ! if landonly exclude pixels with less than 50% land + if ( settings%landonly.and.landfrac(nm).lt.50. ) cycle + if ( settings%lnested.eq.1 ) then + ! if nested domain exclude pixels outside + if ( any(lon_bnds(:,nm).lt.nest_llx).or. & + any(lon_bnds(:,nm).gt.nest_urx).or. & + any(lat_bnds(:,nm).lt.nest_lly).or. & + any(lat_bnds(:,nm).gt.nest_ury) ) cycle + else + ! if not nested exclude pixels outside global domain + if ( any(lon_bnds(:,nm).lt.llx).or. & + any(lon_bnds(:,nm).gt.urx).or. & + any(lat_bnds(:,nm).lt.lly).or. & + any(lat_bnds(:,nm).gt.ury) ) cycle + endif + ! timestamp is seconds since ref date + jdmeas = time(nm)/3600d0/24d0 + jdateref + ! exclude retrievals if not for current day + if ( floor(jdmeas).ne.jd ) cycle + call caldate(jdmeas, jjjjmmdd, hhmiss) + nretr = nretr + 1 + idate(nretr) = jjjjmmdd + itime(nretr) = hhmiss + ! corners ordered anticlockwise from lower left + xpoints(nretr,:) = lon_bnds(:,nm) + ypoints(nretr,:) = lat_bnds(:,nm) + ! pressure levels + ! ordered from TOA to surface so reverse order for flexpart + do nl = 1, nlevel + ln = nlevel - nl + 1 + zpoint1(nretr,ln) = pres(nl,nm) + zpoint2(nretr,ln) = pres(nl,nm) + cak_out(nretr,ln) = cak(nl,nm) + vapri_out(nretr,ln) = vapri(nl,nm) + vdryair_out(nretr,ln) = vdryair(nl,nm) + end do + vmr_out(nretr) = vmr(nm) + verr_out(nretr) = verr(nm) + end do + + write(logid,*) 'number of retrievals: ',nretr + + if ( allocated(lat_bnds) ) deallocate(lat_bnds) + if ( allocated(lon_bnds) ) deallocate(lon_bnds) + if ( allocated(vapri) ) deallocate(vapri) + if ( allocated(vdryair) ) deallocate(vdryair) + if ( allocated(cak) ) deallocate(cak) + if ( allocated(time) ) deallocate(time) + if ( allocated(pres) ) deallocate(pres) + if ( allocated(qa) ) deallocate(qa) + if ( allocated(vmr) ) deallocate(vmr) + if ( allocated(verr) ) deallocate(verr) + if ( allocated(landfrac) ) deallocate(landfrac) + if ( allocated(oper_mode) ) deallocate(oper_mode) + + end do ! nread + + if ( nretr.eq.0 ) go to 10 + + ! calculate column prior (units ppbv) + allocate( cpri(nretr), stat=ierr ) + do nm = 1, nretr + cpri(nm) = dot_product(vapri_out(nm,:), vdryair_out(nm,:)) + end do + + !! test + print*, 'vdryair_out(1:10,:) = ',vdryair_out(1:10,:) + + ! calculate column prior convolved with averaging kernel (units ppbv) + allocate( cakpri(nretr), stat=ierr ) + do nm = 1, nretr + do nl = 1, nlevel + cakpri(nm) = cakpri(nm) + vdryair_out(nm,nl)*vapri_out(nm,nl)*cak_out(nm,nl) + end do + end do + + ! column dry-air not needed but only as dummy variable + allocate( cdryair(nretr) ) + cdryair(:) = 1. + + ! average pixels + ! -------------- + + if ( settings%avg_pixels ) then + call average(settings, idate, itime, xpoints, ypoints, zpoint1, zpoint2, vmr_out, verr_out,& + vapri_out, cpri, cakpri, cak_out, cdryair, vdryair_out, nretr, ncoord, nlevel) + endif + + ! write output data + ! ----------------- + + ! adjust itime + ! needed so that with flexpart rounding to lsynctime all retrievals are still for current day + do nm = 1, nretr + if ( itime(nm).gt.(235959-lsynctime*100/60) ) then + itime(nm) = 235950-lsynctime*100/60 + endif + end do + + ! write command file + call prep_command(settings,jd) + + ! write outgrid file + call prep_outgrid(settings,jd) + + ! write ageclasses file + call prep_ageclass(settings,jd) + + ! write pathnames file + call prep_pathnames(settings,jd) + + ! units of output data: dryair, cdryair, cpri, vmr + units(1) = 'none' + units(2) = 'none' + units(3) = 'ppmv' + units(4) = 'ppmv' + + ! write releases data for flexpart + call write_releases(settings,nretr,nlevel,ncoord,xpoints,ypoints,zpoint1,zpoint2,& + idate,itime,cak_out,vdryair_out,specnum,units) + + ! write retrieval data for flexinvert + call write_retrieval(settings,nretr,ncoord,xpoints,ypoints,idate,itime,& + cpri,cakpri,vmr_out,verr_out,cdryair,units) + + ! copy standard input files to options folder + write(aspec,fmt='(I3.3)') specnum + call system('mkdir -p '//trim(settings%path_options)//adate//'/options/SPECIES/') + filename = trim(settings%path_flexpart)//'options/SPECIES/SPECIES_'//aspec + call system('cp '//trim(filename)//' '//trim(settings%path_options)//adate//'/options/SPECIES/') + call system('cp '//trim(settings%path_flexpart)//'options/*.dat'//' '//trim(settings%path_options)//adate//'/options/') + call system('cp '//trim(settings%path_flexpart)//'options/*.t'//' '//trim(settings%path_options)//adate//'/options/') + + ! no retrievals on this day +10 continue + + ! prepare for next day + ! -------------------- + + if ( allocated(xpoints) ) deallocate(xpoints) + if ( allocated(ypoints) ) deallocate(ypoints) + if ( allocated(zpoint1) ) deallocate(zpoint1) + if ( allocated(zpoint2) ) deallocate(zpoint2) + if ( allocated(idate) ) deallocate(idate) + if ( allocated(itime) ) deallocate(itime) + if ( allocated(cak_out) ) deallocate(cak_out) + if ( allocated(vapri_out) ) deallocate(vapri_out) + if ( allocated(vdryair_out) ) deallocate(vdryair_out) + if ( allocated(vmr_out) ) deallocate(vmr_out) + if ( allocated(verr_out) ) deallocate(verr_out) + if ( allocated(cpri) ) deallocate(cpri) + if ( allocated(cakpri) ) deallocate(cakpri) + if ( allocated(cdryair) ) deallocate(cdryair) + + ! iterate over days + jd = jd + 1d0 + + end do read_loop + + if ( allocated(filelist) ) deallocate(filelist) + if ( allocated(filereadlist) ) deallocate(filereadlist) + + +end subroutine get_oco2 + diff --git a/prep_satellite/get_tropomi.f90 b/prep_satellite/get_tropomi.f90 new file mode 100644 index 0000000..bf8477d --- /dev/null +++ b/prep_satellite/get_tropomi.f90 @@ -0,0 +1,552 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: get_tropomi +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> get_tropomi +!! +!! Purpose: Reads observations and auxilliary data from the specified satellite and +!! and writes the output into files that can be directly used by FLEXPART +!! and, for inversions, by FLEXINVERT +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! +!! Outputs +!! +!! Externals +!! +!--------------------------------------------------------------------------------------- + +subroutine get_tropomi(settings) + + use mod_settings + use mod_var + use mod_dates + use mod_save + use netcdf + + implicit none + + type (settings_t), intent(in) :: settings + + character(max_name_len), dimension(:), allocatable :: filelist, filereadlist + character(max_name_len) :: line, string, grpname, filename + character(max_name_len) :: suppdata_grpname, geodata_grpname, indata_grpname, details_grpname + character(len=8) :: adate + character(len=8) :: surfclass_bin + character(len=6) :: atime + character(len=3) :: aspec + character(len=2) :: amon, aday + character(len=6), dimension(4) :: units + integer :: i, ierr, n, nf, nl, nm, np, nt, nbits, iswater + integer :: nfiles, nread, nretr + real(kind=8) :: jdatei, jdatef, jd, ndays, jref, jdmeas + integer :: jjjjmmdd, hhmiss, mm, dd, hh, mi, ss + integer, parameter :: refdate = 20100101 + integer, parameter :: reftime = 0 + integer, parameter :: lsynctime = 600 + integer :: ntime, nmeas, npixel, nlevel, ncoord, nlayer + real, dimension(:,:,:,:), allocatable :: lat_bnds, lon_bnds + real, dimension(:,:,:), allocatable :: psurf, pdel, vmr, verr, qa + real, dimension(:,:,:,:), allocatable :: vapri, vdryair, cak + real, dimension(:,:), allocatable :: xpoints, ypoints + real, dimension(:,:), allocatable :: zpoint1, zpoint2 + real, dimension(:,:), allocatable :: cak_out, vapri_out, vdryair_out + real, dimension(:), allocatable :: vmr_out, verr_out + real, dimension(:), allocatable :: cpri, cakpri, cdryair + integer, dimension(:), allocatable :: idate, itime, time + integer, dimension(:,:), allocatable :: timedel + integer, dimension(:,:,:), allocatable :: surfclass + integer :: specnum, nchar + integer :: ncid, grpid, supp_grpid, geo_grpid, in_grpid, det_grpid + integer :: dimid, varid + real :: nest_llx, nest_lly, nest_urx, nest_ury + real :: llx, lly, urx, ury + real :: sclfact, offset + real, parameter :: bignum=huge(0.) + + write(logid,*) 'largest number that can be handled (used to define NA) = ',bignum + + ! get species number + ! ------------------ + + open(100,file=trim(settings%path_flexpart)//'options/SPECIES/spec_overview',status='old',action='read',iostat=ierr) + nchar = len_trim(settings%species) + do while ( ierr.eq.0 ) + read (100, fmt='(A)', iostat=ierr) line + if ( line(len_trim(line)-nchar+1:len_trim(line)) == trim(settings%species) ) ierr = 1 + end do + read(line(9:11),*) specnum + write(logid,*) 'species: ',settings%species + write(logid,*) 'specnum: ',specnum + close(100) + + ! define domains + ! -------------- + + llx = settings%outlonleft + lly = settings%outlatlower + urx = llx + settings%numxgrid*settings%dxout + ury = lly + settings%numygrid*settings%dyout + + if ( settings%lnested.eq.1 ) then + nest_llx = settings%outlonnest + nest_lly = settings%outlatnest + nest_urx = nest_llx + settings%numxnest*settings%dxoutnest + nest_ury = nest_lly + settings%numynest*settings%dyoutnest + endif + + ! list satellite files + ! ---------------------- + + call system('find '//trim(settings%path_obs)//' -type f | grep '//trim(settings%suffix) & + //' | wc -l > '//trim(settings%path_obsout)//'obsfiles.txt',ierr) + if ( ierr.eq.0 ) then + call system('find '//trim(settings%path_obs)//' -type f | grep '//trim(settings%suffix) & + //' >> '//trim(settings%path_obsout)//'obsfiles.txt') + else + write(logid,*) 'ERROR: cannot find observation files' + stop + endif + + open(100,file=trim(settings%path_obsout)//'obsfiles.txt',action='read',status='old',iostat=ierr) + if ( ierr.ne. 0 ) then + write(logid,*) 'ERROR: cannot open obsfiles.txt' + stop + endif + read(100,*,iostat=ierr) nfiles + allocate ( filelist(nfiles), stat = ierr ) + allocate ( filereadlist(nfiles), stat = ierr ) + do nf = 1, nfiles + read(100,fmt='(A)',iostat=ierr) filelist(nf) + if (ierr.ne.0) exit + end do + close(100) + + ! loop over days + ! -------------- + + jdatei = juldate(settings%datei, 0) + jdatef = juldate(settings%datef, 0) + jd = jdatei + + read_loop: do + + if ( jd.gt.jdatef ) exit read_loop + + ! search files for current date + call caldate(jd, jjjjmmdd, hhmiss) + write(adate,fmt='(I8.8)') jjjjmmdd + write(logid,*) 'current day: ',adate + mm = jjjjmmdd/100 - (jjjjmmdd/10000)*100 + dd = jjjjmmdd - (jjjjmmdd/100)*100 + write(amon,fmt='(I2.2)') mm + write(aday,fmt='(I2.2)') dd + nread = 0 + do n = 1, nfiles + do i = 1, len_trim(filelist(n))-18 + if ( filelist(n)(i:(i+7)).eq.adate ) then + nread = nread + 1 + filereadlist(nread) = filelist(n) + exit + endif + end do + end do + write(logid,*) 'number of files for current day: ',nread + if ( nread.eq.0 ) then + jd = jd + 1d0 + cycle read_loop + endif + + ! loop over files + ! --------------- + + do n = 1, nread + + ! open file + write(logid,*) 'reading file: ',filereadlist(n) + call check( nf90_open(trim(filereadlist(n)),nf90_NOWRITE,ncid) ) + + ! dimension variables + string = settings%time_name + call split(string,'/',grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_dimid(grpid,trim(string),dimid) ) + call check( nf90_inquire_dimension(grpid,dimid,len=ntime) ) + write(logid,*) 'number reference times: ',ntime + string = settings%nmeas_name + call split(string,'/',grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_dimid(grpid,trim(string),dimid) ) + call check( nf90_inquire_dimension(grpid,dimid,len=nmeas) ) + write(logid,*) 'number of scanlines: ',nmeas + string = settings%npixel_name + call split(string,'/',grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_dimid(grpid,trim(string),dimid) ) + call check( nf90_inquire_dimension(grpid,dimid,len=npixel) ) + write(logid,*) 'number of pixels: ',npixel + string = settings%ncoord_name + call split(string,'/',grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_dimid(grpid,trim(string),dimid) ) + call check( nf90_inquire_dimension(grpid,dimid,len=ncoord) ) + write(logid,*) 'number of geocoordinates: ',ncoord + string = settings%nlevel_name + call split(string,'/',grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_dimid(grpid,trim(string),dimid) ) + call check( nf90_inquire_dimension(grpid,dimid,len=nlevel) ) + nlayer = nlevel - 1 + write(logid,*) 'number of vertical layers: ',nlayer + + ! geolocation variables + allocate( lat_bnds(ncoord,npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate lat_bnds' + allocate( lon_bnds(ncoord,npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate lon_bnds' + string = settings%lon_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',geodata_grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_ncid(grpid,trim(suppdata_grpname),supp_grpid) ) + call check( nf90_inq_ncid(supp_grpid,trim(geodata_grpname),geo_grpid) ) + call check( nf90_inq_varid(geo_grpid,trim(string),varid) ) + call check( nf90_get_var(geo_grpid,varid,lon_bnds) ) + write(logid,*) 'read longitude' + string = settings%lat_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',geodata_grpname) + call check( nf90_inq_varid(geo_grpid,trim(string),varid) ) + call check( nf90_get_var(geo_grpid,varid,lat_bnds) ) + write(logid,*) 'read latitude' + + ! input data variables + allocate( psurf(npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate psurf' + allocate( pdel(npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate pdel' + allocate( surfclass(npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate surfclass' + allocate( vapri(nlayer,npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vapri' + allocate( vdryair(nlayer,npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vdryair' + string = settings%psurf_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',indata_grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_ncid(grpid,trim(suppdata_grpname),supp_grpid) ) + call check( nf90_inq_ncid(supp_grpid,trim(indata_grpname),in_grpid) ) + call check( nf90_inq_varid(in_grpid,trim(string),varid) ) + call check( nf90_get_var(in_grpid,varid,psurf) ) + write(logid,*) 'read surface pressure' + string = settings%pdel_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',indata_grpname) + call check( nf90_inq_varid(in_grpid,trim(string),varid) ) + call check( nf90_get_var(in_grpid,varid,pdel) ) + write(logid,*) 'read pressure coordinates' + string = settings%apri_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',indata_grpname) + call check( nf90_inq_varid(in_grpid,trim(string),varid) ) + call check( nf90_get_var(in_grpid,varid,vapri) ) + write(logid,*) 'read prior vertical profile' + string = settings%dryair_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',indata_grpname) + call check( nf90_inq_varid(in_grpid,trim(string),varid) ) + call check( nf90_get_var(in_grpid,varid,vdryair) ) + write(logid,*) 'read dry air profile' + string = settings%surfclass_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',indata_grpname) + call check( nf90_inq_varid(in_grpid,trim(string),varid) ) + call check( nf90_get_var(in_grpid,varid,surfclass) ) + write(logid,*) 'read surface class' + + ! detailed results variables + allocate( cak(nlayer,npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate cak' + string = settings%ak_name + call split(string,'/',grpname) + call split(string,'/',suppdata_grpname) + call split(string,'/',details_grpname) + call check( nf90_inq_ncid(supp_grpid,trim(details_grpname),det_grpid) ) + call check( nf90_inq_varid(det_grpid,trim(string),varid) ) + call check( nf90_get_var(det_grpid,varid,cak) ) + write(logid,*) 'read column averaging kernel' + + ! product variables + allocate( qa(npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate qa' + allocate( vmr(npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vmr' + allocate( verr(npixel,nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate verr' + allocate( timedel(nmeas,ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate timedel' + allocate( time(ntime), stat=ierr ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate time' + string = settings%qa_name + call split(string,'/',grpname) + call check( nf90_inq_ncid(ncid,trim(grpname),grpid) ) + call check( nf90_inq_varid(grpid,trim(string),varid) ) + call check( nf90_get_var(grpid,varid,qa) ) + call check( nf90_get_att(grpid,varid,'scale_factor',sclfact) ) + call check( nf90_get_att(grpid,varid,'add_offset',offset) ) + qa = qa*sclfact + offset + + write(logid,*) 'read quality value' + string = settings%vmr_name + call split(string,'/',grpname) + call check( nf90_inq_varid(grpid,trim(string),varid) ) + call check( nf90_get_var(grpid,varid,vmr) ) + write(logid,*) 'read total column mixing ratio' + string = settings%verr_name + call split(string,'/',grpname) + call check( nf90_inq_varid(grpid,trim(string),varid) ) + call check( nf90_get_var(grpid,varid,verr) ) + write(logid,*) 'read total column error' + string = settings%tdel_name + call split(string,'/',grpname) + call check( nf90_inq_varid(grpid,trim(string),varid) ) + call check( nf90_get_var(grpid,varid,timedel) ) + write(logid,*) 'read delta time' + string = settings%time_name + call split(string,'/',grpname) + call check( nf90_inq_varid(grpid,trim(string),varid) ) + call check( nf90_get_var(grpid,varid,time) ) + write(logid,*) 'reference time = ', time + + call check( nf90_close(ncid)) + +! print*, 'range(qa) = ',minval(qa),maxval(qa) + + ! select retrievals and extract needed info + if( n.eq.1 ) then + allocate( xpoints(maxretr,ncoord) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate xpoints' + allocate( ypoints(maxretr,ncoord) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate ypoints' + allocate( zpoint1(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate zpoint1' + allocate( zpoint2(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate zpoint2' + allocate( idate(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate idate' + allocate( itime(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate itime' + allocate( cak_out(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate cak_out' + allocate( vapri_out(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vapri_out' + allocate( vdryair_out(maxretr,nlayer) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vdryair_out' + allocate( vmr_out(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate vmr_out' + allocate( verr_out(maxretr) ) + if ( ierr.ne.0 ) write(logid,*) 'ERROR: cannot allocate verr_out' + cak_out(:,:)=0. + vapri_out(:,:)=0. + vmr_out(:)=0. + verr_out(:)=0. + nretr = 0 + endif + do nt = 1, ntime + ndays = dble(time(nt))/3600d0/24d0 + jref = juldate(refdate, reftime) + do nm = 1, nmeas + do np = 1, npixel + ! exclude retrievals below quality cutoff + if ( qa(np,nm,nt).lt.settings%qa_cutoff ) cycle + ! exclude retrievals with few degrees of freedom + if ( sum(cak(:,np,nm,nt)).lt.10. ) cycle + write(surfclass_bin, fmt='(B0)') surfclass(np,nm,nt) + nbits = len_trim(surfclass_bin) + read(surfclass_bin(nbits:nbits),*) iswater + ! if landonly exclude pixels over water + if ( settings%landonly.and.iswater.eq.1 ) cycle + if ( settings%lnested.eq.1 ) then + ! if nested domain exclude pixels outside + if ( any(lon_bnds(:,np,nm,nt).lt.nest_llx).or. & + any(lon_bnds(:,np,nm,nt).gt.nest_urx).or. & + any(lat_bnds(:,np,nm,nt).lt.nest_lly).or. & + any(lat_bnds(:,np,nm,nt).gt.nest_ury) ) cycle + else + ! if not nested exclude pixels outside global domain + if ( any(lon_bnds(:,np,nm,nt).lt.llx).or. & + any(lon_bnds(:,np,nm,nt).gt.urx).or. & + any(lat_bnds(:,np,nm,nt).lt.lly).or. & + any(lat_bnds(:,np,nm,nt).gt.ury) ) cycle + endif + jdmeas = jref+ndays+dble(timedel(nm,nt))/1000d0/3600d0/24d0 + ! exclude retrievals if not for current day + if ( floor(jdmeas).ne.jd ) cycle + if ( vmr(np,nm,nt).ge.bignum ) cycle + call caldate(jdmeas, jjjjmmdd, hhmiss) + nretr = nretr + 1 + idate(nretr) = jjjjmmdd + itime(nretr) = hhmiss + ! corners ordered anticlockwise from lower left + xpoints(nretr,:) = lon_bnds(:,np,nm,nt) + ypoints(nretr,:) = lat_bnds(:,np,nm,nt) + ! pressure levels are equipressure apart + zpoint1(nretr,1) = psurf(np,nm,nt) + zpoint2(nretr,1) = psurf(np,nm,nt) - pdel(np,nm,nt) + do nl = 2, nlayer + zpoint1(nretr,nl) = zpoint1(nretr,nl-1) - pdel(np,nm,nt) + zpoint2(nretr,nl) = zpoint2(nretr,nl-1) - pdel(np,nm,nt) + end do + cak_out(nretr,:) = cak(:,np,nm,nt) + vapri_out(nretr,:) = vapri(:,np,nm,nt) + vdryair_out(nretr,:) = vdryair(:,np,nm,nt) + vmr_out(nretr) = vmr(np,nm,nt) + ! uncertainties for XCH4 based on single sounding precision due to measurement noise -> + ! for overall uncertainty estimate multiply by factor 2 to reflect scatter of single sounding errors + verr_out(nretr) = 2.*verr(np,nm,nt) + end do ! npixel + end do ! nmeas + end do ! ntime + + write(logid,*) 'number of retrievals: ',nretr + + if ( allocated(lat_bnds) ) deallocate(lat_bnds) + if ( allocated(lon_bnds) ) deallocate(lon_bnds) + if ( allocated(psurf) ) deallocate(psurf) + if ( allocated(pdel) ) deallocate(pdel) + if ( allocated(vapri) ) deallocate(vapri) + if ( allocated(vdryair) ) deallocate(vdryair) + if ( allocated(cak) ) deallocate(cak) + if ( allocated(timedel) ) deallocate(timedel) + if ( allocated(time) ) deallocate(time) + if ( allocated(qa) ) deallocate(qa) + if ( allocated(vmr) ) deallocate(vmr) + if ( allocated(verr) ) deallocate(verr) + if ( allocated(surfclass) ) deallocate(surfclass) + + end do ! nread + + print*, 'itime(1), itime(nretr) = ',itime(1),itime(nretr) + + ! calculate column prior (units mol/m2) + allocate( cpri(nretr), stat=ierr ) + cpri = sum(vapri_out(1:nretr,:), dim=2) + + ! calculate column prior convolved with averaging kernel (units mol/m2) + allocate( cakpri(nretr), stat=ierr ) + do nm = 1, nretr + cakpri(nm) = dot_product(vapri_out(nm,:), cak_out(nm,:)) + end do + + ! calculate column dry air (units mol/m2) + allocate( cdryair(nretr), stat=ierr ) + cdryair = sum(vdryair_out(1:nretr,:), dim=2) + + ! average pixels + ! -------------- + + if ( settings%avg_pixels ) then + call average(settings, idate, itime, xpoints, ypoints, zpoint1, zpoint2, vmr_out, verr_out,& + vapri_out, cpri, cakpri, cak_out, cdryair, vdryair_out, nretr, ncoord, nlayer) + endif + + ! write output data + ! ----------------- + + ! adjust itime + ! needed so that with flexpart rounding to lsynctime all retrievals are still for current day + do nm = 1, nretr + if ( itime(nm).gt.(235959-lsynctime*100/60) ) then + itime(nm) = 235950-lsynctime*100/60 + endif + end do + + ! write command file + call prep_command(settings,jd) + + ! write outgrid file + call prep_outgrid(settings,jd) + + ! write ageclasses file + call prep_ageclass(settings,jd) + + ! write pathnames file + call prep_pathnames(settings,jd) + + ! units of output data: dryair, cdryair, cpri, vmr + units(1) = 'mol/m2' + units(2) = 'mol/m2' + units(3) = 'mol/m2' + units(4) = 'ppbv' + + ! write releases data for flexpart + call write_releases(settings,nretr,nlayer,ncoord,xpoints,ypoints,zpoint1,zpoint2,& + idate,itime,cak_out,vdryair_out,specnum,units) + + ! write retrieval data for flexinvert + call write_retrieval(settings,nretr,ncoord,xpoints,ypoints,idate,itime,& + cpri,cakpri,vmr_out,verr_out,cdryair,units) + + ! copy standard input files to options folder + write(aspec,fmt='(I3.3)') specnum + call system('mkdir -p '//trim(settings%path_options)//adate//'/options/SPECIES/') + filename = trim(settings%path_flexpart)//'options/SPECIES/SPECIES_'//aspec + call system('cp '//trim(filename)//' '//trim(settings%path_options)//adate//'/options/SPECIES/') + call system('cp '//trim(settings%path_flexpart)//'options/*.dat'//' '//trim(settings%path_options)//adate//'/options/') + call system('cp '//trim(settings%path_flexpart)//'options/*.t'//' '//trim(settings%path_options)//adate//'/options/') + + ! prepare for next day + ! -------------------- + + if ( allocated(xpoints) ) deallocate(xpoints) + if ( allocated(ypoints) ) deallocate(ypoints) + if ( allocated(zpoint1) ) deallocate(zpoint1) + if ( allocated(zpoint2) ) deallocate(zpoint2) + if ( allocated(idate) ) deallocate(idate) + if ( allocated(itime) ) deallocate(itime) + if ( allocated(cak_out) ) deallocate(cak_out) + if ( allocated(vapri_out) ) deallocate(vapri_out) + if ( allocated(vdryair_out) ) deallocate(vdryair_out) + if ( allocated(vmr_out) ) deallocate(vmr_out) + if ( allocated(verr_out) ) deallocate(verr_out) + if ( allocated(cpri) ) deallocate(cpri) + if ( allocated(cakpri) ) deallocate(cakpri) + if ( allocated(cdryair) ) deallocate(cdryair) + + ! iterate over days + jd = jd + 1d0 + + end do read_loop + + if ( allocated(filelist) ) deallocate(filelist) + if ( allocated(filereadlist) ) deallocate(filereadlist) + + +end subroutine get_tropomi + diff --git a/prep_satellite/gridarea.f90 b/prep_satellite/gridarea.f90 new file mode 100644 index 0000000..25714df --- /dev/null +++ b/prep_satellite/gridarea.f90 @@ -0,0 +1,64 @@ +!--------------------------------------------------------------------------------------- +! PREP_FLUXES: gridarea +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> gridarea +!! +!! Purpose: Calculates the area of a grid cell. +!! +!! Interface: +!! +!! Inputs +!! ylat - latitude of grid centre +!! dy - resolution in latitudinal direction +!! dx - resolution in longitudinal direction +!! +!! Outputs +!! area - area of grid cell +!! +!--------------------------------------------------------------------------------------- + +subroutine gridarea(ylat,dy,dx,area) + + implicit none + + real, intent(in) :: ylat,dy,dx + real, intent(out) :: area + + real, parameter :: pi=3.14159265, rearth=6.371e6, pih=pi/180. + real :: ylata, ylatp, ylatm + real :: cosfact, cosfactp, cosfactm, hzone + + ylata=ylat + ylatp=ylat+0.5*dy + ylatm=ylat-0.5*dy + if (ylatm.lt.0.and.ylatp.gt.0) then + hzone = rearth*pih + else + cosfact=COS(ylata*pih)*rearth + cosfactp=COS(ylatp*pih)*rearth + cosfactm=COS(ylatm*pih)*rearth + if(cosfactp.lt.cosfactm) then + hzone=SQRT(rearth**2-cosfactp**2)-SQRT(rearth**2-cosfactm**2) + else + hzone=SQRT(rearth**2-cosfactm**2)-SQRT(rearth**2-cosfactp**2) + endif + endif + area=2.*pi*rearth*hzone*dx/360. + +end subroutine gridarea diff --git a/prep_satellite/main.f90 b/prep_satellite/main.f90 new file mode 100644 index 0000000..60685ff --- /dev/null +++ b/prep_satellite/main.f90 @@ -0,0 +1,86 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: main +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> main +!! +!! Purpose: Prepares the input files needed to run FLEXPART for satellite or other +!! column measurements to give a total column source-receptor-relationship +!! +!! Prepares the following: +!! +!! COMMAND - flexpart settings +!! OUTGRID - definition of flexpart output grid +!! AGECLASS - definition of age of particles +!! releases.nc - definition of all releases for all retrievals +!! auxilliary.nc - supporting info (averageing kernel) +!! +!! +!--------------------------------------------------------------------------------------- + +program main + + use mod_var + use mod_dates + use mod_settings + + implicit none + + ! set types + type (settings_t) :: settings + + character(max_path_len) :: settings_file, logfile + integer :: ierr + + ! initialization + ! -------------- + + call getarg(1,settings_file) + if (settings_file == '') then + stop 'ERROR: need to specify SETTINGS file' + endif + + call getarg(2,logfile) + if (logfile == '') then + stop 'ERROR: need to specify logfile' + endif + open(logid,file=trim(logfile),status='replace',action='write',iostat=ierr) + + ! read settings + call read_settings(settings_file, settings) + + ! get satellite data + ! ------------------ + + if ( settings%satellite.eq.'tropomi' ) then + ! tropomi + call get_tropomi(settings) + else if ( settings%satellite.eq.'bremen' ) then + ! bremen retrieval for tropomi + call get_bremen(settings) + else if ( settings%satellite.eq.'oco2' ) then + call get_oco2(settings) + else + ! add other satellites here + write(logid,*) 'ERROR: unknown satellite type' + stop + endif + + +end program + diff --git a/prep_satellite/makefile b/prep_satellite/makefile new file mode 100644 index 0000000..9b0516c --- /dev/null +++ b/prep_satellite/makefile @@ -0,0 +1,40 @@ +F90 = gfortran +LIBPATH = /usr/lib/ +INCPATH = /usr/include/ +LNK = -o +CMPL = -c +LIBS = -lnetcdf -lnetcdff +#FFLAGS = -O2 -m64 -mcmodel=medium -fconvert=little-endian -frecord-marker=4 -ffree-form +FFLAGS = -O0 -g -m64 -fbounds-check -mcmodel=medium -fconvert=little-endian -frecord-marker=4 -ffree-form \ + -fbacktrace +LDFLAGS = $(FFLAGS) -L$(LIBPATH) -I$(INCPATH) $(LIBS) + +MAIN = prep_satellite + +SRCS = mod_var.f90 \ + mod_dates.f90 \ + mod_settings.f90 \ + mod_save.f90 \ + gridarea.f90 \ + geodarea.f90 \ + prep_command.f90 \ + prep_outgrid.f90 \ + prep_ageclass.f90 \ + prep_pathnames.f90 \ + average.f90 \ + get_tropomi.f90 \ + get_bremen.f90 \ + get_oco2.f90 \ + main.f90 + + +OBJECTS = $(SRCS:.f90=.o) +$(MAIN): $(OBJECTS) $(MODULES) + $(F90) $(LNK) $(MAIN) $(OBJECTS) $(LIBS) +%.o : %.f90 + $(F90) $(LDFLAGS) $(CMPL) $< -o $@ + +clean: + rm -f $(OBJECTS) $(MODULES) + + diff --git a/prep_satellite/mod_dates.f90 b/prep_satellite/mod_dates.f90 new file mode 100644 index 0000000..dfe3afc --- /dev/null +++ b/prep_satellite/mod_dates.f90 @@ -0,0 +1,176 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: mod_dates +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> mod_dates +!! +!! Purpose: Module of date functions. +!! +!--------------------------------------------------------------------------------------- + +module mod_dates + + implicit none + + contains + + ! -------------------------------------------------- + ! caldate + ! -------------------------------------------------- + !> caldate + !! + !! Purpose: Converts a julian date number to date + !! and time format YYYYMMDD, HHMISS + !! according to the gregorian calender + !! + ! -------------------------------------------------- + + subroutine caldate(jdate, yyyymmdd, hhmiss) + + real(kind=8), intent(in) :: jdate + integer, intent(out) :: yyyymmdd, hhmiss + integer :: yyyy, mm, dd, hh, mi, ss + integer :: julday, ja, jb, jc, jd, je, jalpha + integer, parameter :: igreg = 2299161 + + julday=int(jdate) + if(julday.ge.igreg) then + jalpha=int(((julday-1867216)-0.25)/36524.25) + ja=julday+1+jalpha-int(0.25*jalpha) + else + ja=julday + endif + jb=ja+1524 + jc=int(6680.+((jb-2439870)-122.1)/365.25) + jd=365*jc+int(0.25*jc) + je=int((jb-jd)/30.6001) + dd=jb-jd-int(30.6001*je) + mm=je-1 + if(mm.gt.12) mm=mm-12 + yyyy=jc-4715 + if(mm.gt.2) yyyy=yyyy-1 + if(yyyy.le.0) yyyy=yyyy-1 + + yyyymmdd=10000*yyyy+100*mm+dd + hh=int(24.*(jdate-float(julday))) + mi=int(1440.*(jdate-float(julday))-60.*float(hh)) + ss=nint(86400.*(jdate-float(julday))-3600.*float(hh))-60.*float(mi) + if(ss.eq.60) then ! 60 seconds = 1 minute + ss=0 + mi=mi+1 + endif + if(mi.eq.60) then + mi=0 + hh=hh+1 + endif + hhmiss=10000*hh+100*mi+ss + + end subroutine caldate + + ! -------------------------------------------------- + ! julday + ! -------------------------------------------------- + !> julday + !! + !! Purpose: Converts the date and time format + !! YYYYMMDD and HHMMSS to a julian date + !! number. + !! + ! -------------------------------------------------- + + real(kind=8) function juldate(yyyymmdd,hhmiss) + + integer, intent(in) :: yyyymmdd,hhmiss + integer :: yyyy,mm,hh,dd,mi,ss + integer :: julday,jy,jm,ja + integer, parameter :: igreg=15+31*(10+12*1582) + + yyyy=yyyymmdd/10000 + mm=(yyyymmdd-10000*yyyy)/100 + dd=yyyymmdd-10000*yyyy-100*mm + hh=hhmiss/10000 + mi=(hhmiss-10000*hh)/100 + ss=hhmiss-10000*hh-100*mi + + if(yyyy.eq.0) print*, 'ERROR: there is no year zero' + if(yyyy.lt.0) yyyy=yyyy+1 + if(mm.gt.2) then + jy=yyyy + jm=mm+1 + else + jy=yyyy-1 + jm=mm+13 + endif + julday=int(365.25*jy)+int(30.6001*jm)+dd+1720995 + if (dd+31*(mm+12*yyyy).ge.igreg) then + ja=int(0.01*jy) + julday=julday+2-ja+int(0.25*ja) + endif + + juldate=dble(float(julday))+dble(float(hh)/24.)& + &+dble(float(mi)/1440.)+dble(float(ss)/86400.) + + end function juldate + + ! -------------------------------------------------- + ! calceomday + ! -------------------------------------------------- + !> calceomday + !! + !! Purpose: Calculates number of days in a given + !! year, month currently only considers + !! years after 1900. + !! + ! -------------------------------------------------- + + integer function calceomday(yyyymm) + + integer, intent(in) :: yyyymm + integer :: yyyy,mm + integer, dimension(12) :: leapdays,days + integer :: eomday + + leapdays=(/31,29,31,30,31,30,31,31,30,31,30,31/) + days=(/31,28,31,30,31,30,31,31,30,31,30,31/) + + yyyy=floor(yyyymm/100.) + mm=yyyymm-yyyy*100 + + if((float(yyyy)/100.).eq.float(yyyy/100)) then + if((float(yyyy)/400.).eq.float(yyyy/400)) then + eomday=leapdays(mm) + else + eomday=days(mm) + endif + else + if((float(yyyy)/4.).eq.float(yyyy/4)) then + eomday=leapdays(mm) + else + eomday=days(mm) + endif + endif + + calceomday=eomday + + end function calceomday + + ! -------------------------------------------------- + +end module mod_dates + + diff --git a/prep_satellite/mod_save.f90 b/prep_satellite/mod_save.f90 new file mode 100644 index 0000000..f3cfaf5 --- /dev/null +++ b/prep_satellite/mod_save.f90 @@ -0,0 +1,225 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: mod_save +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> mod_save +!! +!! Purpose: Writes retrieval and releases data to netcdf files +!! +!--------------------------------------------------------------------------------------- + +module mod_save + + use mod_settings + use mod_var + use netcdf + + implicit none + private + + public :: check, write_releases, write_retrieval + + contains + + ! -------------------------------------------------- + ! check + ! -------------------------------------------------- + + subroutine check(status) + + integer, intent(in) :: status + + if ( status.ne.nf90_noerr ) then + write(logid,*) trim(nf90_strerror(status)) + stop + end if + + end subroutine check + + ! -------------------------------------------------- + ! write_releases + ! + ! total column SRR calculated as + ! + ! SRR_col = sum(a_i * SRR_i * vdryair_i) + ! + ! where + ! a_i ith layer of column averaging kernel + ! vdryair_i dry air weighting in ith layer + ! SRR_col column SRR in units of s.mol/kg + ! (after dividing by height of surface + ! layer) + ! + ! -------------------------------------------------- + + subroutine write_releases(settings,nretr,nlayer,ncoord,xpoints,ypoints,zpoint1,zpoint2,& + idate,itime,cak_out,vdryair_out,specnum,units) + + type (settings_t), intent(in) :: settings + integer, intent(in) :: nretr, nlayer, ncoord + real, dimension(maxretr,ncoord), intent(in) :: xpoints, ypoints + real, dimension(maxretr,nlayer), intent(in) :: zpoint1, zpoint2 + integer, dimension(maxretr), intent(in) :: idate, itime + real, dimension(maxretr,nlayer), intent(in) :: cak_out, vdryair_out + integer, intent(in) :: specnum + character(len=6), dimension(4), intent(in) :: units + + character(len=8) :: adate + character(max_name_len) :: filename, pathout + integer :: ncid, nrid, nlid, ncoid + integer :: xptid, yptid, zpt1id, zpt2id + integer :: dateid, timeid, cakid, dairid, npartid + + write(adate,fmt='(I8.8)') idate(1) + filename = 'releases_'//trim(settings%satname)//'_'//adate//'.nc' + pathout = trim(settings%path_options)//adate//'/options/' + call system('mkdir '//trim(pathout)) + write(logid,*) 'writing file: ',trim(pathout)//trim(filename) + + ! create file + call check( nf90_create(trim(pathout)//trim(filename), nf90_clobber, ncid) ) + + ! define dimensions and variables + call check( nf90_def_dim(ncid, 'retrieval', nretr, nrid) ) + call check( nf90_def_dim(ncid, 'nlayer', nlayer, nlid) ) + call check( nf90_def_dim(ncid, 'ncoord', ncoord, ncoid) ) + call check( nf90_def_var(ncid, 'xpoints', nf90_real, (/nrid, ncoid/), xptid) ) + call check( nf90_put_att(ncid, xptid, 'units', 'degrees_east') ) + call check( nf90_def_var(ncid, 'ypoints', nf90_real, (/nrid, ncoid/), yptid) ) + call check( nf90_put_att(ncid, yptid, 'units', 'degrees_north') ) + call check( nf90_def_var(ncid, 'zpoint1', nf90_real, (/nrid, nlid/), zpt1id) ) + call check( nf90_put_att(ncid, zpt1id, 'units', 'Pa') ) + call check( nf90_def_var(ncid, 'zpoint2', nf90_real, (/nrid, nlid/), zpt2id) ) + call check( nf90_put_att(ncid, zpt2id, 'units', 'Pa') ) + call check( nf90_def_var(ncid, 'idate', nf90_int, nrid, dateid) ) + call check( nf90_put_att(ncid, dateid, 'units', 'none') ) + call check( nf90_def_var(ncid, 'itime', nf90_int, nrid, timeid) ) + call check( nf90_put_att(ncid, timeid, 'units', 'none') ) + call check( nf90_def_var(ncid, 'cak', nf90_real, (/nrid, nlid/), cakid) ) + call check( nf90_put_att(ncid, cakid, 'units', 'none') ) + call check( nf90_def_var(ncid, 'dryair', nf90_real, (/nrid, nlid/), dairid) ) + call check( nf90_put_att(ncid, dairid, 'units', trim(units(1))) ) + call check( nf90_put_att(ncid, nf90_global, 'specnum', specnum) ) + call check( nf90_put_att(ncid, nf90_global, 'mass', settings%mass) ) + call check( nf90_put_att(ncid, nf90_global, 'npart', settings%npart) ) + + ! exit define mode + call check( nf90_enddef(ncid) ) + + ! insert variables + call check( nf90_put_var(ncid, xptid, xpoints(1:nretr,:)) ) + call check( nf90_put_var(ncid, yptid, ypoints(1:nretr,:)) ) + call check( nf90_put_var(ncid, zpt1id, zpoint1(1:nretr,:)) ) + call check( nf90_put_var(ncid, zpt2id, zpoint2(1:nretr,:)) ) + call check( nf90_put_var(ncid, dateid, idate(1:nretr)) ) + call check( nf90_put_var(ncid, timeid, itime(1:nretr)) ) + call check( nf90_put_var(ncid, cakid, cak_out(1:nretr,:)) ) + call check( nf90_put_var(ncid, dairid, vdryair_out(1:nretr,:)) ) + + ! close file + call check( nf90_close(ncid) ) + + end subroutine write_releases + + ! -------------------------------------------------- + ! write_retrieval + ! + ! model total column is: + ! + ! vmod = cpri - cakpri + SRR_col * flux + ! + ! and total column mixing ratio (ppbv) is + ! + ! xmod = vmod/vdryair + ! + ! where + ! cpri sum(vpri_i) + ! cakpri sum(a_i*vpri_i) + ! vdryair total column dry air + ! + ! -------------------------------------------------- + + subroutine write_retrieval(settings,nretr,ncoord,xpoints,ypoints,idate,itime,& + cpri,cakpri,vmr_out,verr_out,cdryair,units) + + type (settings_t), intent(in) :: settings + integer, intent(in) :: nretr, ncoord + real, dimension(maxretr,ncoord), intent(in) :: xpoints, ypoints + integer, dimension(maxretr), intent(in) :: idate, itime + real, dimension(maxretr), intent(in) :: vmr_out, verr_out + real, dimension(nretr), intent(in) :: cpri, cakpri, cdryair + character(len=6), dimension(4), intent(in) :: units + + character(max_name_len) :: filename + character(len=8) :: adate + integer :: ncid, nrid, nlid, ncoid + integer :: xptid, yptid, dateid, timeid + integer :: cprid, cakprid, vmrid, verrid, cdairid + + write(adate,fmt='(I8.8)') idate(1) + filename = 'retrieval_'//trim(settings%satname)//'_'//adate//'.nc' + write(logid,*) 'writing file: ',trim(settings%path_obsout)//trim(filename) + + ! create file + call check( nf90_create(trim(settings%path_obsout)//trim(filename), nf90_clobber, ncid) ) + + ! define dimensions and variables + call check( nf90_def_dim(ncid, 'retrieval', nretr, nrid) ) + call check( nf90_def_dim(ncid, 'ncoord', ncoord, ncoid) ) + call check( nf90_def_var(ncid, 'xpoints', nf90_real, (/nrid, ncoid/), xptid) ) + call check( nf90_put_att(ncid, xptid, 'units', 'degrees_east') ) + call check( nf90_def_var(ncid, 'ypoints', nf90_real, (/nrid, ncoid/), yptid) ) + call check( nf90_put_att(ncid, yptid, 'units', 'degrees_north') ) + call check( nf90_def_var(ncid, 'idate', nf90_int, nrid, dateid) ) + call check( nf90_put_att(ncid, dateid, 'units', 'none') ) + call check( nf90_def_var(ncid, 'itime', nf90_int, nrid, timeid) ) + call check( nf90_put_att(ncid, timeid, 'units', 'none') ) + call check( nf90_def_var(ncid, 'cpri', nf90_real, nrid, cprid) ) + call check( nf90_put_att(ncid, cprid, 'units', trim(units(3))) ) + call check( nf90_def_var(ncid, 'cakpri', nf90_real, nrid, cakprid) ) + call check( nf90_put_att(ncid, cakprid, 'units', trim(units(3))) ) + call check( nf90_def_var(ncid, 'vmr', nf90_real, nrid, vmrid) ) + call check( nf90_put_att(ncid, vmrid, 'units', trim(units(4))) ) + call check( nf90_def_var(ncid, 'vmr_err', nf90_real, nrid, verrid) ) + call check( nf90_put_att(ncid, verrid, 'units', trim(units(4))) ) + call check( nf90_def_var(ncid, 'cdryair', nf90_real, nrid, cdairid) ) + call check( nf90_put_att(ncid, cdairid, 'units', trim(units(2))) ) + + ! end define mode + call check( nf90_enddef(ncid) ) + + ! insert variables + call check( nf90_put_var(ncid, xptid, xpoints(1:nretr,:)) ) + call check( nf90_put_var(ncid, yptid, ypoints(1:nretr,:)) ) + call check( nf90_put_var(ncid, dateid, idate(1:nretr)) ) + call check( nf90_put_var(ncid, timeid, itime(1:nretr)) ) + call check( nf90_put_var(ncid, cprid, cpri) ) + call check( nf90_put_var(ncid, cakprid, cakpri) ) + call check( nf90_put_var(ncid, vmrid, vmr_out(1:nretr)) ) + call check( nf90_put_var(ncid, verrid, verr_out(1:nretr)) ) + call check( nf90_put_var(ncid, cdairid, cdryair) ) + + ! close file + call check( nf90_close(ncid) ) + + end subroutine write_retrieval + + ! -------------------------------------------------- + +end module mod_save + diff --git a/prep_satellite/mod_settings.f90 b/prep_satellite/mod_settings.f90 new file mode 100644 index 0000000..e03234e --- /dev/null +++ b/prep_satellite/mod_settings.f90 @@ -0,0 +1,567 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: mod_settings +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> mod_settings +!! +!! Purpose: Module for the construction of the settings data structure. +!! +!--------------------------------------------------------------------------------------- + +module mod_settings + + use mod_var + + implicit none + private + + public :: settings_t, read_settings, split, parse_string + + ! settings_t contains all settings + + type :: settings_t + + character(len=max_name_len) :: satellite + character(len=max_name_len) :: satname + character(len=max_path_len) :: path_flexpart + character(len=max_path_len) :: path_options + character(len=max_path_len) :: path_output + character(len=max_path_len) :: path_ohfield + character(len=max_path_len) :: path_obs + character(len=max_path_len) :: path_obsout + character(len=max_name_len) :: suffix + character(len=max_path_len) :: file_avail + character(len=max_path_len) :: file_availnest + character(len=max_name_len) :: windfield + + logical :: landonly + integer :: opmode_sel + character(len=max_name_len) :: nmeas_name + character(len=max_name_len) :: npixel_name + character(len=max_name_len) :: ncoord_name + character(len=max_name_len) :: nlevel_name + character(len=max_name_len) :: time_name + character(len=max_name_len) :: tdel_name + character(len=max_name_len) :: qa_name + character(len=max_name_len) :: oper_name + real :: qa_cutoff + character(len=max_name_len) :: vmr_name + character(len=max_name_len) :: verr_name + character(len=max_name_len) :: lat_name + character(len=max_name_len) :: lon_name + character(len=max_name_len) :: ak_name + character(len=max_name_len) :: apri_name + character(len=max_name_len) :: dryair_name + character(len=max_name_len) :: psurf_name + character(len=max_name_len) :: pdel_name + character(len=max_name_len) :: surfclass_name + character(len=max_name_len) :: landfrac_name + + integer :: datei + integer :: datef + integer :: outrate + integer :: outaverage + integer :: outsample + integer :: ind_source + integer :: ind_receptor + integer :: lnested + integer :: linit_cond + + real :: outlonleft + real :: outlatlower + integer :: numxgrid + integer :: numygrid + integer :: numzgrid + real :: dxout + real :: dyout + real :: outlonnest + real :: outlatnest + integer :: numxnest + integer :: numynest + real :: dxoutnest + real :: dyoutnest + real, dimension(:), allocatable :: zlevel + + character(len=10) :: species + integer :: ageclass + integer :: npart + integer :: mass + logical :: avg_pixels + logical :: usegridfile + real :: dmin + integer :: nsteps + real :: cutoff + integer :: nmin + character(len=max_path_len) :: filegrid + + end type settings_t + + contains + + ! -------------------------------------------------- + ! check if input is numeric + ! -------------------------------------------------- + + logical function is_numeric(string) + + character(len=200), intent(in) :: string + real :: var + integer :: ierr + + read(string,*,iostat=ierr) var + if(ierr.eq.0) then + is_numeric=.true. + else + is_numeric=.false. + endif + + end function is_numeric + + ! -------------------------------------------------- + ! check if input is logical + ! -------------------------------------------------- + + logical function is_logical(string) + + character(len=200), intent(in) :: string + logical :: var + integer :: ierr + + read(string,*,iostat=ierr) var + if(ierr.eq.0) then + is_logical=.true. + else + is_logical=.false. + endif + + end function is_logical + + ! -------------------------------------------------- + ! split string + ! -------------------------------------------------- + + subroutine split(str,delims,before,sep) + + ! Routine finds the first instance of a character from 'delims' in the + ! the string 'str'. The characters before the found delimiter are + ! output in 'before'. The characters after the found delimiter are + ! output in 'str'. The optional output character 'sep' contains the + ! found delimiter. A delimiter in 'str' is treated like an ordinary + ! character if it is preceded by a backslash (\). If the backslash + ! character is desired in 'str', then precede it with another backslash. + + character(len=*) :: str, delims, before + character,optional :: sep + logical :: pres + character :: ch,cha + integer :: i, k, ibsl, ipos, iposa, lenstr + + pres=present(sep) + str=adjustl(str) + lenstr=len_trim(str) + if(lenstr == 0) return ! string str is empty + k=0 + ibsl=0 ! backslash initially inactive + before=' ' + do i=1,lenstr + ch=str(i:i) + if(ibsl == 1) then ! backslash active + k=k+1 + before(k:k)=ch + ibsl=0 + cycle + end if + if(ch == '\') then ! backslash with backslash inactive + k=k+1 + before(k:k)=ch + ibsl=1 + cycle + end if + ipos=index(delims,ch) + if(ipos == 0) then ! character is not a delimiter + k=k+1 + before(k:k)=ch + cycle + end if + if(ch /= ' ') then ! character is a delimiter that is not a space + str=str(i+1:) + if(pres) sep=ch + exit + end if + cha=str(i+1:i+1) ! character is a space delimiter + iposa=index(delims,cha) + if(iposa > 0) then ! next character is a delimiter + str=str(i+2:) + if(pres) sep=cha + exit + else + str=str(i+1:) + if(pres) sep=ch + exit + end if + end do + if(i >= lenstr) str='' + str=trim(str) + before=trim(before) + return + + end subroutine split + + ! -------------------------------------------------- + ! routine to identify and read in content + ! -------------------------------------------------- + + subroutine read_content (line, identifier, cc, cn, cl, match) + + character (len=200), intent (in) :: line, identifier + character (len=200), intent (out) :: cc ! character content + real(kind=8), intent (out) :: cn ! numeric content + logical, intent (out) :: cl ! logical content + logical, intent (out) :: match + + integer :: n + + n = len_trim (identifier) + + ! default: line does not match, cc="", cn=-9900.0 + cc = "" + cn = -9999.9 + match = .false. + if ( len_trim (line) >= n ) then + if ( line(:n) == identifier(:n) ) then + cc = adjustl (line(n+1:)) + if ( is_numeric(cc) ) then + read (line(n+1:),*) cn + else + if( is_logical(cc) ) read (line(n+1:),*) cl + endif + match = .true. + end if + end if + + end subroutine read_content + + ! -------------------------------------------------- + ! routine to parse a string + ! -------------------------------------------------- + + subroutine parse_string (str, delims, args, nargs) + + ! Parses the string 'str' into arguments args(1), ..., args(nargs) based on + ! the delimiters contained in the string 'delims'. Preceding a delimiter in + ! 'str' by a backslash (\) makes this particular instance not a delimiter. + ! The integer output variable nargs contains the number of arguments found. + + character(len=*) :: str, delims + character(len=len_trim(str)) :: strsav + character(len=*), dimension(:) :: args + integer :: i, na, lenstr, nargs + + strsav=str + na=size(args) + + do i=1,na + args(i)=' ' + end do + nargs=0 + lenstr=len_trim(str) + if(lenstr==0) return + + do + if(len_trim(str) == 0) exit + nargs=nargs+1 + call split(str,delims,args(nargs)) + end do + str=strsav + + end subroutine parse_string + + ! -------------------------------------------------- + ! read settings + ! -------------------------------------------------- + + subroutine read_settings(filename, settings) + + character(len=200), intent(in) :: filename + type(settings_t), intent(in out) :: settings + + integer :: ierr, i, n + character(len=200) :: line, identifier, cc + real(kind=8) :: cn + logical :: match, cl + character(len=100), dimension(100) :: temp + + ! default settings for logicals + settings%avg_pixels = .false. + settings%usegridfile = .false. + settings%landonly = .false. + settings%opmode_sel = -1 + + ! open file + open (100, file = trim (filename), status = 'old', iostat=ierr) + if(ierr.gt.0) then + write (*,*) 'ERROR: cannot find SETTINGS' + stop + endif + + ! loop over lines + read_loop: do + read (100, fmt='(A)', iostat=ierr) line + + ! first check if line exists or eof was reached + if ( ierr.gt.0 ) then + write (*,*) 'ERROR: check ', filename + exit read_loop + else if ( ierr.lt.0 ) then + exit read_loop + else + + ! omit comments ("#") and empty lines + if ( index (trim (line), "#") .eq. 1 ) cycle read_loop + if ( len (trim (line)) .eq. 0 ) cycle read_loop + + ! Path and file settings + identifier = "satellite:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%satellite = cc + identifier = "satname:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%satname = cc + identifier = "path_flexpart:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%path_flexpart = cc + identifier = "path_options:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%path_options = cc + identifier = "path_output:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%path_output = cc + identifier = "path_ohfield:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%path_ohfield = cc + identifier = "path_obs:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%path_obs = cc + identifier = "path_obsout:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%path_obsout = cc + identifier = "suffix:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%suffix = cc + identifier = "file_avail:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%file_avail = cc + identifier = "file_availnest:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%file_availnest = cc + identifier = "windfield:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%windfield = cc + + ! satellite variables + identifier = "landonly:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%landonly = cl + identifier = "opmode_sel:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%opmode_sel = cn + identifier = "nmeas_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%nmeas_name = cc + identifier = "npixel_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%npixel_name = cc + identifier = "ncoord_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%ncoord_name = cc + identifier = "nlevel_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%nlevel_name = cc + identifier = "oper_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%oper_name = cc + identifier = "time_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%time_name = cc + identifier = "tdel_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%tdel_name = cc + identifier = "qa_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%qa_name = cc + identifier = "qa_cutoff:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%qa_cutoff = cn + identifier = "vmr_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%vmr_name = cc + identifier = "verr_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%verr_name = cc + identifier = "lat_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%lat_name = cc + identifier = "lon_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%lon_name = cc + identifier = "ak_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%ak_name = cc + identifier = "apri_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%apri_name = cc + identifier = "dryair_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%dryair_name = cc + identifier = "psurf_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%psurf_name = cc + identifier = "pdel_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%pdel_name = cc + identifier = "surfclass_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%surfclass_name = cc + identifier = "landfrac_name:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%landfrac_name = cc + + ! COMMAND settings + identifier = "datei:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%datei = int(cn) + identifier = "datef:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%datef = int(cn) + identifier = "outrate:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%outrate = int(cn) + identifier = "outaverage:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%outaverage = int(cn) + identifier = "outsample:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%outsample = int(cn) + identifier = "ind_source:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%ind_source = int(cn) + identifier = "ind_receptor:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%ind_receptor = int(cn) + identifier = "lnested:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%lnested = int(cn) + identifier = "linit_cond:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%linit_cond = int(cn) + + ! OUTGRID settings + identifier = "outlonleft:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%outlonleft = real(cn) + identifier = "outlatlower:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%outlatlower = real(cn) + identifier = "numxgrid:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%numxgrid = real(cn) + identifier = "numygrid:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%numygrid = real(cn) + identifier = "dxout:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%dxout = real(cn) + identifier = "dyout:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%dyout = real(cn) + identifier = "outlonnest:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%outlonnest = real(cn) + identifier = "outlatnest:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%outlatnest = real(cn) + identifier = "numxnest:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%numxnest = real(cn) + identifier = "numynest:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%numynest = real(cn) + identifier = "dxoutnest:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%dxoutnest = real(cn) + identifier = "dyoutnest:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%dyoutnest = real(cn) + identifier = "zlevel:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) then + call parse_string (cc, ",", temp(:), n) + if( .not.allocated(settings%zlevel) ) allocate( settings%zlevel(n) ) + settings%numzgrid = n + do i = 1, n + read(temp(i),*) settings%zlevel(i) + enddo + endif + + ! RELEASES settings + identifier = "species:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%species = cc + identifier = "ageclass:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%ageclass = int(cn) + identifier = "npart:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%npart = int(cn) + identifier = "mass:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%mass = int(cn) + + ! AVERAGING settings + identifier = "avg_pixels:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%avg_pixels = cl + identifier = "usegridfile:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%usegridfile = cl + identifier = "dmin:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%dmin = cn + identifier = "nsteps:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%nsteps = cn + identifier = "cutoff:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%cutoff = cn + identifier = "nmin:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%nmin = int(cn) + identifier = "filegrid:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) settings%filegrid = cc + + endif + + end do read_loop + + end subroutine read_settings + + ! -------------------------------------------------- + +end module mod_settings + diff --git a/prep_satellite/mod_var.f90 b/prep_satellite/mod_var.f90 new file mode 100644 index 0000000..aed88d6 --- /dev/null +++ b/prep_satellite/mod_var.f90 @@ -0,0 +1,39 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: mod_var +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2022, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> mod_var +!! +!--------------------------------------------------------------------------------------- + +module mod_var + + implicit none + + ! general variables + ! ----------------- + + integer, parameter :: max_path_len = 200 + integer, parameter :: max_name_len = 200 + integer, parameter :: maxretr = 1000000 + integer, parameter :: logid = 500 + integer, parameter :: maxbox = 500000 + +end module mod_var + + diff --git a/prep_satellite/prep_ageclass.f90 b/prep_satellite/prep_ageclass.f90 new file mode 100644 index 0000000..50c60dc --- /dev/null +++ b/prep_satellite/prep_ageclass.f90 @@ -0,0 +1,69 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: prep_ageclass +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> prep_ageclass +!! +!! Purpose: Prepares the options file AGECLASS. +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! jd - julian day of start of month +!! +!! Externals +!! caldate +!! +!--------------------------------------------------------------------------------------- + +subroutine prep_ageclass(settings, jd) + + use mod_var + use mod_settings + use mod_dates + + implicit none + + type (settings_t), intent(in) :: settings + real(kind=8), intent(in) :: jd + + character(len=max_path_len) :: filename + character(len=8) :: adate + integer :: jjjjmmdd, hhmiss + integer :: ierr + integer :: nageclass, lage + + namelist /ageclass/ & + nageclass, & + lage + + ! preset namelist variables + nageclass = 1 + lage = settings%ageclass + + call caldate(jd, jjjjmmdd, hhmiss) + write(adate,fmt='(I8.8)') jjjjmmdd + filename = trim(settings%path_options)//adate//'/options/AGECLASSES' + + ! write namelist file format + open(100,file=trim(filename),status='replace',action='write',iostat=ierr) + write(100,nml=ageclass) + close(100) + +end subroutine prep_ageclass diff --git a/prep_satellite/prep_command.f90 b/prep_satellite/prep_command.f90 new file mode 100644 index 0000000..5e5b27d --- /dev/null +++ b/prep_satellite/prep_command.f90 @@ -0,0 +1,171 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: prep_command +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> prep_command +!! +!! Purpose: Prepares the options file COMMAND. +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! jd - julian day for start of month +!! +!! Externals +!! caldate +!! juldate +!! +!--------------------------------------------------------------------------------------- + +subroutine prep_command(settings, jd) + + use mod_var + use mod_settings + use mod_dates + + implicit none + + type (settings_t), intent(in) :: settings + real(kind=8), intent(in) :: jd + + character(len=max_path_len) :: filename + character(len=8) :: adate + integer :: jjjjmm, jjjjmmdd, hhmiss + integer :: datei, datef + integer :: ierr + + integer :: ldirect ! runtime mode (currently only backwards: ldirect = -1) + integer :: ibdate, ibtime + integer :: iedate, ietime + integer :: partsplit + integer :: loutstep + integer :: loutaver + integer :: loutsample + integer :: itsplit ! time constant for particle splitting (secs) + integer :: lsynctime ! synchronisation interval of flexpart (secs) + real :: ctl ! factor by which time step must be smaller than tl + integer :: ifine ! factor by which to decrease time step for vertical motion + integer :: iout ! output type (1 = concentration/residence time, 2 = mixing ratio, 3 = both, 4 = plume trajectory) + integer :: ipout ! particle dump (0 = never, 1 = every output interval, 2 = only at end) + integer :: lsubgrid ! use subgrid terrain effect parameterization (0 = no, 1 = yes) + integer :: lconvection ! use convection (0 = no, 1 = yes) + integer :: lagespectra ! calculate age spectra (0 = no, 1 = yes) + integer :: ipin ! continue simulation with dumped particle data (0 = no, 1 = yes) + integer :: ioutputforeachrelease ! create output file for each release location (0 = no, 1 = yes) + integer :: mdomainfill ! domain filling option (0 = no, 1 = yes) + integer :: mquasilag ! quasilagrangian mode to track particles (0 = no, 1 = yes) + integer :: surf_only ! save only surface layer in grid_time files = 1, full resolution = 0 + integer :: lnetcdfout ! netcdf output (0 = no, 1 = yes) + integer :: cblflag ! + integer :: linversionout ! one grid_time file per release = 1, or per timestep (original format) = 0 + integer :: satellite_releases ! releases for satellite retrievals + integer :: iflux ! calculate fluxes (0 = no, 1 = yes) + integer :: ind_source + integer :: ind_receptor + integer :: nested_output + integer :: linit_cond + character(len=max_path_len) :: ohfields_path + + namelist /command/ & + ldirect, & + ibdate,ibtime, & + iedate,ietime, & + loutstep, & + loutaver, & + loutsample, & + itsplit, & + lsynctime, & + ctl, & + ifine, & + iout, & + ipout, & + lsubgrid, & + lconvection, & + lagespectra, & + ipin, & + ioutputforeachrelease, & + iflux, & + mdomainfill, & + ind_source, & + ind_receptor, & + mquasilag, & + nested_output, & + linit_cond, & + lnetcdfout, & + surf_only, & + cblflag, & + linversionout, & + satellite_releases, & + ohfields_path + + ! initialize command file path and name + call caldate(jd, jjjjmmdd, hhmiss) + write(adate,fmt='(I8.8)') jjjjmmdd + filename = trim(settings%path_options)//adate//'/options/COMMAND' + + call system('mkdir -p '//trim(settings%path_options)//adate//'/options/') + + ! get dates + ! correct start date for length of back trajectory + call caldate((jd-settings%ageclass/3600d0/24d0), jjjjmmdd, hhmiss) + datei = jjjjmmdd + call caldate(jd+1d0, jjjjmmdd, hhmiss) + datef = jjjjmmdd + + ! preset namelist variables + ldirect = -1 + ibdate = datei + ibtime = 0 + iedate = datef + ietime = 0 + loutstep = settings%outrate + loutaver = settings%outaverage + loutsample = settings%outsample + itsplit = 999999999 + lsynctime = 600 + ctl = -5 + ifine = 4 + iout = 1 + ipout = 2 + lsubgrid = 1 + lconvection = 1 + lagespectra = 1 + ipin = 0 + ioutputforeachrelease = 1 + iflux = 0 + mdomainfill = 0 + ind_source = settings%ind_source + ind_receptor = settings%ind_receptor + mquasilag = 0 + nested_output = settings%lnested + linit_cond = settings%linit_cond + lnetcdfout = 0 + surf_only = 1 + cblflag = 0 + linversionout = 1 + satellite_releases = 1 + ohfields_path = trim(settings%path_ohfield) + + ! write command file + open(100,file=trim(filename),status='replace',action='write',iostat=ierr) + write(100,nml=command) + close(100) + +end subroutine prep_command + diff --git a/prep_satellite/prep_outgrid.f90 b/prep_satellite/prep_outgrid.f90 new file mode 100644 index 0000000..a659157 --- /dev/null +++ b/prep_satellite/prep_outgrid.f90 @@ -0,0 +1,131 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: prep_outgrid +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> prep_outgrid +!! +!! Purpose: Prepares the options file OUTGRID +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! jd - julian day for start of month +!! +!! Externals +!! caldate +!! skiplines +!! +!--------------------------------------------------------------------------------------- + +subroutine prep_outgrid(settings, jd) + + use mod_var + use mod_settings + use mod_dates + + implicit none + + type (settings_t), intent(in) :: settings + real(kind=8), intent(in) :: jd + + character(len=max_path_len) :: filename + character(len=8) :: adate + integer :: jjjjmmdd, hhmiss + integer :: ierr, n + + real :: outlon0, outlat0, outlon0n, outlat0n + integer :: numxgrid, numygrid, numxgridn, numygridn + real :: dxout, dyout, dxoutn, dyoutn + real, dimension(settings%numzgrid) :: outheights + + namelist /outgrid/ & + outlon0, & + outlat0, & + numxgrid, & + numygrid, & + dxout, & + dyout, & + outheights + + namelist /outgridn/ & + outlon0n, & + outlat0n, & + numxgridn, & + numygridn, & + dxoutn, & + dyoutn + + ! write OUTGRID + + ! preset namelist variables + outlon0 = settings%outlonleft + ! correction for FLEXPART windfields + if ( settings%outlonleft.le.-180. ) then + if ( settings%windfield.eq.'oper' ) then + outlon0 = -179. + else if ( settings%windfield.eq.'era5' ) then + outlon0 = -179.5 + else + write(logid,*) 'ERROR: prep_outgrid: unknown windfield type' + stop + endif + endif + outlat0 = settings%outlatlower + numxgrid = settings%numxgrid + numygrid = settings%numygrid + dxout = settings%dxout + dyout = settings%dyout + do n = 1, settings%numzgrid + outheights(n) = settings%zlevel(n) + end do + + call caldate(jd, jjjjmmdd, hhmiss) + write(adate,fmt='(I8.8)') jjjjmmdd + filename = trim(settings%path_options)//adate//'/options/OUTGRID' + + ! write namelist file format + open(100,file=trim(filename),status='replace',action='write',iostat=ierr) + write(100,nml=outgrid) + close(100) + + ! write OUTGRID_NEST + + if( settings%lnested.eq.1 ) then + + ! preset namelist variables + outlon0n = settings%outlonnest + outlat0n = settings%outlatnest + numxgridn = settings%numxnest + numygridn = settings%numynest + dxoutn = settings%dxoutnest + dyoutn = settings%dyoutnest + + call caldate(jd, jjjjmmdd, hhmiss) + write(adate,fmt='(I8.8)') jjjjmmdd + filename = trim(settings%path_options)//adate//'/options/OUTGRID_NEST' + + ! write namelist file format + open(100,file=trim(filename),status='replace',action='write',iostat=ierr) + write(100,nml=outgridn) + close(100) + + endif + +end subroutine prep_outgrid + diff --git a/prep_satellite/prep_pathnames.f90 b/prep_satellite/prep_pathnames.f90 new file mode 100644 index 0000000..9f7a839 --- /dev/null +++ b/prep_satellite/prep_pathnames.f90 @@ -0,0 +1,75 @@ +!--------------------------------------------------------------------------------------- +! PREP_SATELLITE: prep_pathnames +!--------------------------------------------------------------------------------------- +! FLEXINVERT is free software: you can redistribute it and/or modify +! it under the terms of the GNU General Public License as published by +! the Free Software Foundation, either version 3 of the License, or +! (at your option) any later version. +! +! FLEXINVERT is distributed in the hope that it will be useful, +! but WITHOUT ANY WARRANTY; without even the implied warranty of +! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +! GNU General Public License for more details. +! +! You should have received a copy of the GNU General Public License +! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. +! +! Copyright 2017, Rona Thompson +!--------------------------------------------------------------------------------------- +! +!> prep_pathnames +!! +!! Purpose: Prepares the file pathnames +!! +!! Interface: +!! +!! Inputs +!! settings - settings data structure +!! jd - julian day for start of month +!! +!! Externals +!! caldate +!! +!--------------------------------------------------------------------------------------- + +subroutine prep_pathnames(settings, jd) + + use mod_var + use mod_settings + use mod_dates + + implicit none + + type (settings_t), intent(in) :: settings + real(kind=8), intent(in) :: jd + + character(len=max_path_len) :: filename + character(len=8) :: adate + integer :: jjjjmmdd, hhmiss + integer :: ierr + + call caldate(jd, jjjjmmdd, hhmiss) + write(adate,fmt='(I8.8)') jjjjmmdd + filename = trim(settings%path_options)//adate//'/pathnames' + + call system('mkdir -p '//trim(settings%path_options)//adate) + call system('mkdir -p '//trim(settings%path_output)//adate) + + open(100,file=trim(filename),status='replace',action='write',iostat=ierr) + + write(100,fmt='(A)') trim(settings%path_options)//adate//'/options/' + write(100,fmt='(A)') trim(settings%path_output)//adate//'/' + write(100,fmt='(A1)') '/' + write(100,fmt='(A)') trim(settings%file_avail) + + if( settings%lnested.eq.1 ) then + write(100,fmt='(A1)') '/' + write(100,fmt='(A)') trim(settings%file_availnest) + write(100,fmt='(A)') '============================================' + write(100,fmt='(A1)') ' ' + endif + + close(100) + +end subroutine prep_pathnames + diff --git a/prep_satellite/run_flexpart.sh b/prep_satellite/run_flexpart.sh new file mode 100755 index 0000000..3a771c7 --- /dev/null +++ b/prep_satellite/run_flexpart.sh @@ -0,0 +1,97 @@ +#!/bin/bash +#--------------------------------------------------- +# Run FLEXPART jobs for satellite observations +#--------------------------------------------------- +# Guide to use: +# 1. edit the directories, exename, list of stations +# months and year below +# 2. for slurm set SLURM = 1 otherwise uses nohup +# 3. the meteorology used for flexpart can be +# specified by METEO +# 4. run the script: +# ./run_flexpart.sh +#--------------------------------------------------- + +# User settings +SLURM=1 +PRIORITY=normal +TIMELIM=2-00:00:00 +PARTITION=nilu +EXENAME=FLEXPART +DIRFLEX=/mypath/FLEXPART_v104/ +DIROPTIONS=/mypath/flexpart_input/ +PREFIX=releases_s5p_ch4 +YEAR=2020 +MONLIST=07 +DAYLIST=(01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 + 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31) + +#--------------------------------------------------- + +NDAY=${#DAYLIST[@]} +echo "number of days: "${NDAY} +ARRAYDAY=`seq 1 ${NDAY}` + +NMON=${#MONLIST[@]} +echo "number of months: "${NMON} +ARRAYMON=`seq 1 ${NMON}` + +# check FLEXPART executable + +cd ${DIRFLEX}src/ +if [ ! `ls ${DIRFLEX}src/${EXENAME}` ]; then + make -j serial ncf=yes + if [ ! `ls ${DIRFLEX}src/${EXENAME}` ]; then + echo "cannot create exec: "${DIRFLEX}src/${EXENAME} + exit -1 + fi +fi +chmod u+x ${EXENAME} + +# loop over FLEXPART runs + +for k in ${ARRAYMON} +do + j=`expr $k - 1` + MON=${MONLIST[$j]} + for i in ${ARRAYDAY} + do + l=`expr $i - 1` + DAY=${DAYLIST[$l]} + + # check RELEASES NCDF file exists if not go to next iteration + echo ${DIROPTIONS}${YEAR}${MON}${DAY}/options/${PREFIX}_${YEAR}${MON}${DAY}.nc + if [ ! `ls ${DIROPTIONS}${YEAR}${MON}${DAY}/options/${PREFIX}_${YEAR}${MON}${DAY}.nc` ]; then + continue + fi + + # set-up job + cd ${DIROPTIONS}${YEAR}${MON}${DAY} + rm -f job_${MON}${DAY} + rm -f flex.log + cp -f ${DIRFLEX}src/${EXENAME} job_${MON}${DAY} + OUTPUT=flex.log + echo "submitting job for: "${YEAR}${MON}${DAY} + + if [ ${SLURM} -eq 1 ]; then + # create submit.sh for submitting to slurm +cat <<EOF > submit.sh +#!/bin/bash +./job_${MON}${DAY} +EOF +#sbatch --job-name=job_${MON}${DAY} --qos=${PRIORITY} --partition=${PARTITION} --time=${TIMELIM} --mem-per-cpu=50000 --output=${OUTPUT} submit.sh +sbatch --job-name=job_${MON}${DAY} --partition=${PARTITION} --time=${TIMELIM} --mem-per-cpu=50000 --output=${OUTPUT} submit.sh + sleep 2 + rm -f submit.sh + else + # use nohup + nohup ./job_${MON}${DAY} > ${OUTPUT} + sleep 2 + fi + + done +done + +# the end + + diff --git a/prep_satellite/sbatch_satellite.sh b/prep_satellite/sbatch_satellite.sh new file mode 100755 index 0000000..1dd544e --- /dev/null +++ b/prep_satellite/sbatch_satellite.sh @@ -0,0 +1,15 @@ +#!/bin/bash +#--------------------------------------------------- +partition=nilu +settings_file='./SETTINGS_tropomi' +logfile='prep_satellite_tropomi.log' +#--------------------------------------------------- + +cat <<EOF > run_job.sh +#!/bin/bash +./prep_satellite ${settings_file} ${logfile} +EOF + +sbatch --job-name=prep_sat --mem-per-cpu=6000 --partition=${partition} run_job.sh + +rm -f run_job.sh diff --git a/prep_syndata/get_obs.f90 b/prep_syndata/get_obs.f90 index e248e4c..03c82d4 100644 --- a/prep_syndata/get_obs.f90 +++ b/prep_syndata/get_obs.f90 @@ -49,7 +49,7 @@ subroutine get_obs(config, files, obs, obserr) logical :: lexist integer :: jjjjmmdd, hhmiss, i, ierr real :: jdate, conc, post, delta, cinipos - real, dimension(maxobs) :: cini, bkg, nee, fff, ocn, prior, ghg + real, dimension(maxobs) :: cini, bkg, nee, fff, ocn, prior, ghg, cpri, cakpri character(len=3) :: recs obs(:) = 0. @@ -60,6 +60,8 @@ subroutine get_obs(config, files, obs, obserr) ocn(:) = 0. prior(:) = 0. ghg(:) = 0. + cpri(:) = 0. + cakpri(:) = 0. filename = trim(files%path_output)//'monitor.txt' inquire(file=trim(filename), exist=lexist) @@ -72,17 +74,17 @@ subroutine get_obs(config, files, obs, obserr) open(100,file=trim(filename),status='old',action='read',iostat=ierr) read(100,fmt='(A)') dump if ( config%spec.eq.'co2' ) then - write(rowfmt,'(A,I6,A)') '(A3,1X,I8,1X,I6,1X,F14.6,1X,',11,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,',13,'(F10.4,1X))' do i = 1, maxobs read(100,fmt=rowfmt,iostat=ierr) recs, jjjjmmdd, hhmiss, jdate, conc, cini(i), cinipos, bkg(i), & - nee(i), fff(i), ocn(i), prior(i), post, delta, obserr(i) + nee(i), fff(i), ocn(i), prior(i), post, cpri(i), cakpri(i), delta, obserr(i) if ( ierr.ne.0 ) exit end do else - write(rowfmt,'(A,I6,A)') '(A3,1X,I8,1X,I6,1X,F14.6,1X,',9,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,',11,'(F10.4,1X))' do i = 1, maxobs read(100,fmt=rowfmt,iostat=ierr) recs, jjjjmmdd, hhmiss, jdate, conc, cini(i), cinipos, bkg(i), & - ghg(i), prior(i), post, delta, obserr(i) + ghg(i), prior(i), post, cpri(i), cakpri(i), delta, obserr(i) if ( ierr.ne.0 ) exit end do endif @@ -98,14 +100,14 @@ subroutine get_obs(config, files, obs, obserr) end do if ( config%spec.eq.'co2' ) then - obs = cini + bkg + nee + fff + ocn + obs = cini + bkg + nee + fff + ocn + cpri - cakpri else if ( config%lognormal ) then ! lognormal - obs = cini + bkg + prior + obs = cini + bkg + prior + cpri - cakpri else ! normal - obs = cini + bkg + ghg + prior + obs = cini + bkg + ghg + prior + cpri - cakpri endif endif diff --git a/prep_syndata/initialize.f90 b/prep_syndata/initialize.f90 index 81f4c26..a2dbb47 100644 --- a/prep_syndata/initialize.f90 +++ b/prep_syndata/initialize.f90 @@ -41,6 +41,7 @@ subroutine initialize(files, config) logical :: lexist integer :: yyyymm, yyyymmdd, hhmiss character(6) :: adate + character(8) :: areldate character(4) :: ayear integer :: numpoint integer :: ibdate, ibtime @@ -59,27 +60,36 @@ subroutine initialize(files, config) filename = trim(files%file_recept) inquire(file=trim(filename),exist=lexist) - if (.not.lexist) then + if ( .not.lexist.and.config%ground ) then write(logid,*) 'ERROR: cannot find '//trim(filename) stop + else if ( lexist.and.config%ground ) then + write(logid,*) 'Reading receptor list: '//trim(filename) + call read_reclist(filename) endif - write(logid,*) 'Reading receptor list: '//trim(filename) - call read_reclist(filename) ! initialize flexpart variables ! ----------------------------- ! read header - yyyymm=config%datei/100 - write(adate,'(i6)') yyyymm - lexist = .false. - i = 1 - do while ( (.not.lexist).and.(i.le.nrec) ) - if ( .not.config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' - if ( config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' + if ( config%satellite.and.nrec.eq.0 ) then + ! satellite only + write(areldate,'(I8)') config%datei + filename = trim(files%path_flexsat)//areldate//'/header' inquire(file=trim(filename),exist=lexist) - i = i + 1 - end do + else + ! read from ground-based output + yyyymm=config%datei/100 + write(adate,'(i6)') yyyymm + lexist = .false. + i = 1 + do while ( (.not.lexist).and.(i.le.nrec) ) + if ( .not.config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' + if ( config%nested ) filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' + inquire(file=trim(filename),exist=lexist) + i = i + 1 + end do + endif ! satellite if ( .not.lexist ) then write(logid,*) 'ERROR initialize: cannot find flexpart header' stop @@ -138,7 +148,6 @@ subroutine initialize(files, config) statres_hr = config%statres_hr ndt = int(24./statres_hr) ntstate = int(real(nday)/statres) -! nflx = nday*nd_nee else ! statres is time resolution of state variables ! ntstate is also number of state time invervals @@ -146,7 +155,6 @@ subroutine initialize(files, config) statres = config%statres ntstate = int(real(nday)/statres) ndt = 1 -! nflx = nday*24/nt_flx endif ! adjust nday to match number of state time steps diff --git a/prep_syndata/job_prep_syndata.sh b/prep_syndata/job_prep_syndata.sh index 18a1741..6f8c2a4 100755 --- a/prep_syndata/job_prep_syndata.sh +++ b/prep_syndata/job_prep_syndata.sh @@ -1,8 +1,8 @@ #!/bin/bash #--------------------------------------------------- -settings_files='/mypath/settings/SETTINGS_ghg_files' -settings_config='/mypath/settings/SETTINGS_ghg_config' +settings_files='/mypath/settings/SETTINGS_files' +settings_config='/mypath/settings/SETTINGS_config' ./prep_syndata ${settings_files} ${settings_config} diff --git a/prep_syndata/mod_save.f90 b/prep_syndata/mod_save.f90 index a06c678..9ad81e3 100644 --- a/prep_syndata/mod_save.f90 +++ b/prep_syndata/mod_save.f90 @@ -299,6 +299,7 @@ module mod_save subroutine save_obs(config, files, obs, obserr) use mod_var + use mod_dates use mod_settings implicit none @@ -309,12 +310,22 @@ module mod_save character(len=max_path_len) :: filename character(len=max_name_len) :: rowfmt, dump + character(len=50) :: satname, string, before, sep, species, prefix character(len=recname_len), dimension(nobs) :: recs integer, dimension(nobs) :: jjjjmmdd, hhmiss + integer :: currentdate, currenttime real(kind=8), dimension(nobs) :: jdate + real(kind=8) :: jd, jdobs real :: conc, cini, cinipos, bkg, nee, fff, ocn, ghg, prior, post, delta, err + real, dimension(nobs) :: cpri, cakpri integer :: i, ierr - logical :: lexist + logical :: lexist, lsatellite + + ! satellites + integer :: nretr, numretr, cnt, nfiles, nf + real, dimension(nobs) :: vmr + character(len=8) :: adate + integer :: ncid, dimid, varid ! read monitor ! ------------ @@ -329,34 +340,118 @@ module mod_save open(100,file=trim(filename),status='old',action='read',iostat=ierr) read(100,fmt='(A)') dump if ( config%spec.eq.'co2' ) then - write(rowfmt,'(A,I6,A)') '(A3,1X,I8,1X,I6,1X,F14.6,1X,',11,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,',13,'(F10.4,1X))' do i = 1, nobs read(100,fmt=rowfmt) recs(i), jjjjmmdd(i), hhmiss(i), jdate(i), conc, cini, cinipos, bkg, & - nee, fff, ocn, prior, post, delta, err + nee, fff, ocn, prior, post, cpri(i), cakpri(i), delta, err end do else - write(rowfmt,'(A,I6,A)') '(A3,1X,I8,1X,I6,1X,F14.6,1X,',9,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,',11,'(F10.4,1X))' do i = 1, nobs read(100,fmt=rowfmt) recs(i), jjjjmmdd(i), hhmiss(i), jdate(i), conc, cini, cinipos, bkg, & - ghg, prior, post, delta, err + ghg, prior, post, cpri(i), cakpri(i), delta, err end do endif close(100) - ! write obs for each receptor - ! --------------------------- + ! write ground-based obs + ! ---------------------- + write(rowfmt,'(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)' do i = 1, nobs - filename=trim(files%path_output)//trim(recs(i))//'_'//trim(config%spec)//'.txt' - inquire(file=trim(filename),exist=lexist) - if ( .not.lexist ) then - open(100,file=trim(filename),action='write',status='replace',iostat=ierr) + ! check if observation is a satellite retrieval + read(recs(i),*,iostat=ierr) nretr + if ( ierr.eq.0 ) then + lsatellite = .true. else - open(100,file=trim(filename),action='write',status='old',access='append',iostat=ierr) + lsatellite = .false. endif - write(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & - recs(i), jjjjmmdd(i), hhmiss(i), jdate(i), obs(i), obserr(i), 1 + if ( .not.lsatellite ) then + filename=trim(files%path_output)//trim(recs(i))//'_'//trim(config%spec)//'.txt' + inquire(file=trim(filename),exist=lexist) + if ( .not.lexist ) then + open(100,file=trim(filename),action='write',status='replace',iostat=ierr) + else + open(100,file=trim(filename),action='write',status='old',access='append',iostat=ierr) + endif + write(100,fmt=rowfmt,iostat=ierr) recs(i), jjjjmmdd(i), hhmiss(i), jdate(i), obs(i), obserr(i), 1 + close(100) + endif + end do + + ! write satellite obs + ! ------------------- + + ! get prefix for satellite ncdf files + inquire(file=trim(files%path_output)//'obsfiles.txt',exist=lexist) + if ( lexist ) then + open(100,file=trim(files%path_output)//'obsfiles.txt',action='read',status='old',iostat=ierr) + read(100,*,iostat=ierr) nfiles + do nf = 1, nfiles + read(100,fmt='(A)',iostat=ierr) filename + if (ierr.ne.0) exit + string = trim(filename) + call split(string,".",before,sep) + if ( string.eq.'nc' ) then + ! assume ncdf file is for satellite obs + string = trim(filename) + ! first instance of delim + call split(string,"_",before,sep) + ! second instance of delim + call split(string,"_",prefix,sep) + ! third instance of delim + call split(string,"_",species,sep) + satname = trim(prefix)//'_'//trim(species) + write(logid,*) 'prefix satellite file = ',satname + exit + endif + end do close(100) + else + write(logid,*) 'ERROR save_obs: obsfiles.txt not found' + stop + endif + + ! loop over days writing one file per day + jd = juldatei + do while ( jd .le. juldatef ) + cnt = 0 + do i = 1, nobs + ! check if observation is a satellite retrieval + read(recs(i),*,iostat=ierr) nretr + if ( ierr.eq.0 ) then + lsatellite = .true. + else + lsatellite = .false. + endif + if ( lsatellite ) then + ! check if observation is for current day + jdobs = juldate(jjjjmmdd(i), 0) + if ( jdobs.ne.jd ) cycle + cnt = cnt + 1 + vmr(cnt) = obs(i) + endif + end do ! nobs + ! copy original ncdf file and replace vmr with obs + call caldate(jd, currentdate, currenttime) + write(adate,fmt='(I8.8)') currentdate + filename = 'retrieval_'//trim(satname)//'_'//adate//'.nc' + print*, 'mod_save: filename = ',filename + call system('cp '//trim(files%path_satobs)//trim(filename)//' '//trim(files%path_output)//trim(filename)) + call check( nf90_open(trim(files%path_output)//trim(filename),nf90_WRITE,ncid) ) + call check( nf90_inq_dimid(ncid,'retrieval',dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=numretr) ) + ! check consistent number of retrievals + if ( cnt.ne.numretr ) then + write(logid,*) 'ERROR: inconsistent number of retrievals for day ',currentdate,': numretr, cnt = ',numretr,cnt + stop + endif + call check( nf90_inq_varid(ncid,'vmr',varid) ) + call check( nf90_put_var(ncid, varid, vmr(1:numretr)) ) + call check( nf90_close(ncid) ) + ! next day + jd = jd + 1d0 + vmr(:) = 0. end do end subroutine save_obs diff --git a/prep_syndata/mod_settings.f90 b/prep_syndata/mod_settings.f90 index 15d09a1..d3061cf 100644 --- a/prep_syndata/mod_settings.f90 +++ b/prep_syndata/mod_settings.f90 @@ -31,7 +31,7 @@ module mod_settings implicit none private - public :: files_t, config_t, read_file_settings, read_config_settings, parse_string + public :: files_t, config_t, read_file_settings, read_config_settings, parse_string, split ! files_t contains information on paths and files @@ -39,9 +39,11 @@ module mod_settings character(len=max_path_len) :: path_prior ! path to prior fluxes character(len=max_path_len) :: path_obs ! path to observations + character(len=max_path_len) :: path_satobs ! path to satellite observations character(len=max_path_len) :: path_output ! path to output character(len=max_name_len) :: suffix ! observation file name suffix character(len=max_path_len) :: path_flexpart ! path to flexpart output + character(len=max_path_len) :: path_flexsat ! path to flexpart output for satellite obs character(len=max_name_len) :: file_log ! log file name character(len=max_path_len) :: file_regions ! region definitions file character(len=max_name_len) :: varname_regs ! regions variable name @@ -97,6 +99,8 @@ module mod_settings real :: statres_hr ! sub-daily temporal resolution of state vector (e.g. 6, 12 or 24h) real :: measerr ! measurement error logical :: lognormal ! use lognormal distribution in state space (true or false) + logical :: satellite ! use satellite observations (true or false) + logical :: ground ! use ground-based observations (true or false) real :: trunc ! truncation factor for eigenvalues of B end type config_t @@ -325,12 +329,18 @@ module mod_settings identifier = "path_obs:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_obs = cc + identifier = "path_satobs:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) files%path_satobs = cc identifier = "suffix:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%suffix = cc identifier = "path_flexpart:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_flexpart = cc + identifier = "path_flexsat:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) files%path_flexsat = cc identifier = "path_output:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_output = cc @@ -462,6 +472,8 @@ module mod_settings ! logical defaults config%lognormal = .false. + config%satellite = .false. + config%ground = .false. config%trunc = 1.e-3 ! open file @@ -514,6 +526,14 @@ module mod_settings identifier = "lognormal:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) config%lognormal = cl + identifier = "satellite:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%satellite = cl + identifier = "ground:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%ground = cl + + ! other settings identifier = "trunc:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) config%trunc = cn @@ -561,6 +581,10 @@ module mod_settings print*, 'WARNING read_config_settings: for CO2 cannot use lognormal -> changing to normal' config%lognormal = .false. endif + if ( .not.config%ground.and..not.config%satellite ) then + print*, 'ERROR: at least one of satellite or ground must be true' + stop + endif end subroutine read_config_settings diff --git a/prep_syndata/mod_strings.f90 b/prep_syndata/mod_strings.f90 index 342df59..b360d14 100644 --- a/prep_syndata/mod_strings.f90 +++ b/prep_syndata/mod_strings.f90 @@ -111,17 +111,26 @@ module mod_strings character(*), intent(in) :: repstr character(max_path_len) :: string_out character(max_path_len) :: strtmp1, strtmp2 - integer :: strlen, replen, n + integer :: strlen, substrlen, replen, n strlen = len_trim(string_in) replen = len_trim(repstr) + substrlen = len_trim(substr) ! find first position of substr in string_in n = index( string_in, substr, back=.false. ) - strtmp1 = string_in(1:n-1) + if ((n-1).gt.0) then + strtmp1 = string_in(1:n-1) + else + ! empty string + strtmp1 = '' + n = 1 + endif +! print*, 'str_replace first pos n, strtmp1 = ',n, strtmp1 ! find last position of substr in string_in - n = index( string_in, substr, back=.true. ) - strtmp2 = string_in(n+replen:strlen) - string_out = trim(strtmp1)//trim(repstr)//trim(strtmp2) + n = index( string_in, substr, back=.true. ) + strtmp2 = string_in(n+substrlen:strlen) +! print*, 'str_replace last pos n, strtmp2 = ',n, strtmp2 + string_out = trim(strtmp1)//trim(repstr)//trim(strtmp2) end function str_replace diff --git a/prep_syndata/sbatch.sh b/prep_syndata/sbatch.sh index c9a2770..b74a4e8 100755 --- a/prep_syndata/sbatch.sh +++ b/prep_syndata/sbatch.sh @@ -2,8 +2,8 @@ #--------------------------------------------------- priority=0 partition=nilu -settings_files='/mypath/settings/SETTINGS_ghg_files' -settings_config='/mypath/settings/SETTINGS_ghg_config' +settings_files='/mypath/settings/SETTINGS_files' +settings_config='/mypath/settings/SETTINGS_config' #--------------------------------------------------- cat <<EOF > run_job.sh diff --git a/settings/SETTINGS_ghg_nest_config b/settings/SETTINGS_ch4sat_config similarity index 78% rename from settings/SETTINGS_ghg_nest_config rename to settings/SETTINGS_ch4sat_config index ca53281..ccc7bfb 100644 --- a/settings/SETTINGS_ghg_nest_config +++ b/settings/SETTINGS_ch4sat_config @@ -20,8 +20,8 @@ seed: 100 # Run dates: format yyyymmdd # datei = start date # datef = end date -datei: 20120101 -datef: 20120131 +datei: 20200701 +datef: 20200731 # Use lognormal distribution (true or false) (only for non-CO2 species) # if true need to use m1qn3 method @@ -31,17 +31,17 @@ lognormal: .false. trunc: 1.e-4 # Inversion method ('analytic', 'congrad' or 'm1qn3') -method: congrad +method: congrad # Average/select flexpart releases (true or false) -average_fp: .true. +average_fp: .false. # Number of iterations # only used if method is 'congrad' or 'm1qn3' maxiter: 10 # Optimize ocean boxes (true or false) -inc_ocean: .true. +inc_ocean: .false. # Optimize initial mixing ratios (true or false) opt_cini: .false. @@ -55,9 +55,9 @@ spa_corr: .true. # 1 = use prior best guess (file must be specified in SETTINGS_files) prior_bg: 0 -# Restart a crashed run -# for congrad/m1qn3 will pick-up from last iteration -# for analytic will only use pre-calculated covariance matrix and boundary conditions +# Restart a crashed run +# for congrad will pick-up from last iteration +# for other methods will only use pre-calculated covariance matrix and boundary conditions # 0 = new run # 1 = restart crashed run restart: 0 @@ -69,14 +69,23 @@ verbose: .false. # Species ("co2" or "ghg") spec: ghg +# Use satellite observations (true or false) +satellite: .true. + +# Use ground-based observations (true or false) +ground: .false. + # Molar mass (in flux files, e.g. CO2-C=12, CH4=16) -molarmass: 16. +molarmass: 16. # Coefficient to convert from grid units of ppt to ppb coeff: 1.e-3 +# Coefficient to convert satellite data to units of ppbv or ppmv +coeffsat: 1. + # Use nested flexpart output (true or false) -nested: .true. +nested: .false. # Inversion domain: # if using nested output it must match the inversion domain @@ -86,12 +95,12 @@ nested: .true. # n_edge_lat = lat of northern inversion grid # xres = longitudinal grid resolution # yres = latitudinal grid resolution -w_edge_lon: -15 -s_edge_lat: 30 -e_edge_lon: 35 -n_edge_lat: 75 -xres: 0.5 -yres: 0.5 +w_edge_lon: -180 +s_edge_lat: -90 +e_edge_lon: 180 +n_edge_lat: 90 +xres: 2.0 +yres: 2.0 # Spatial aggregation of grid (true or false) regions: .true. @@ -99,9 +108,9 @@ regions: .true. # State vector (days): # temporal resolution at which NEE fluxes are optimized # must be an integer multiple of 1 day -statres: 10. +statres: 30. -# prior fluxes: +# other prior fluxes: # nstep_flx = time step of other fluxes (integer hours, for monthly data use 720) nstep_flx: 720 @@ -109,33 +118,32 @@ nstep_flx: 720 # used if error in obs input <= zero measerr: 5.0 -# Initial mixing ratio error: fraction +# Initial mixing ratio scalar error: fraction cinierr: 0.005 # Prior flux error: fraction -flxerr: 0.5 +flxerr: 1.0 # Lower limit flux error: unit (kg/m2/h) -flxerr_ll: 1.e-8 +flxerr_ll: 1.e-9 # Spatial correlation length for land: unit (km) -sigma_land: 250. +sigma_land: 200. # Spatial correlation length for ocean: unit (km) sigma_ocean: 1000. # Temporal correlation length: unit (days) -sigmatime: 30. +sigmatime: 90. # Total error for inversion domain (Tg/y) -# globerr <= 0: prior error covariance matrix not scaled -globerr: 10. +globerr: 100. # Settings for optimization of initial mixing ratios # comma separated list of northern edges of latitude bands cini_lat: -30.,0.,30.,90. # comma separated list of upper level of vertical bands (upper most level > outheight(nzgrid)) -#cini_alt: 2000., 10000., 55000. -cini_alt: 55000. +cini_alt: 2000., 10000., 55000. # time resolution for initial mixing ratio scalars (days) cinires: 30. + diff --git a/settings/SETTINGS_ghg_nest_files b/settings/SETTINGS_ch4sat_files similarity index 71% rename from settings/SETTINGS_ghg_nest_files rename to settings/SETTINGS_ch4sat_files index 9d1574b..b50b2ee 100644 --- a/settings/SETTINGS_ghg_nest_files +++ b/settings/SETTINGS_ch4sat_files @@ -29,47 +29,51 @@ # ================================================== # Paths: -path_obs: /mypath/TEST_OUTPUT/OBS/GHG/ -path_prior: /mypath/TEST_OUTPUT/FLUXES/GHG/ -path_output: /mypath/TEST_OUTPUT/RESULTS/GHG/NEST/ -path_flexpart: /mypath/TEST_OUTPUT/FLEXOUT/GHG/NEST/ -path_flexncdf: /mypath/TEST_OUTPUT/RESULTS/GHG/NEST/ +path_satobs: /mypath/satellite_obs/ +# path_obs is for ground observations +path_obs: /mypath/ground_obs/ +path_prior: /mypath/flux/ +path_output: /mypath/output/ +path_flexsat: /mypath/flexpart_output_sat/ +# path_flexpart is for ground observations +path_flexpart: /mypath/flexpart_output/ +path_flexncdf: /mypath/flexpart_ncdf_out/ # Prior best guess file (if prior_bg = 1 in SETTINGS_config) file_bg: # Suffix by which to identify observation files -suffix: CH4.txt +suffix: .txt # Log file: file_log: flexinvert.log -# Regions mask file: -# needs to correspond to flexpart domain and resolution (if using nested output needs to correspond to nest) -file_regions: /mypath/TEST_OUTPUT/regions_ghg_nest.nc -varname_regs: regions -lonname_regs: longitude -latname_regs: latitude - # Orography file: # needs to correspond to global flexpart domain and resolution -file_orog: /mypath/TEST_OUTPUT/elev.2-deg.nc +file_orog: /mypath/input/elev.2-deg.nc varname_orog: data lonname_orog: lon latname_orog: lat +# Regions mask file: +# needs to correspond to flexpart domain and resolution (if using nested output needs to correspond to nest) +file_regions: /mypath/output/regions_2x2.nc +varname_regs: regions +lonname_regs: longitude +latname_regs: latitude + # Land-sea mask file: # needs to correspond to flexpart domain and resolution (if using nested output needs to correspond to nest) -file_lsm: /mypath/TEST_INPUT/lsm_0.5x0.5.nc -varname_lsm: lsm -lonname_lsm: lon -latname_lsm: lat +file_lsm: /mypath/input/lsm_2x2.nc +varname_lsm: mask +lonname_lsm: longitude +latname_lsm: latitude # Global prior flux file: flux kg/m2/h # needs to correspond to flexpart domain and resolution # generic name with year YYYY filename_flx: CH4_TOTAL_YYYY_20x20.nc -varname_flx: emisch4 +varname_flx: emi_ch4 lonname_flx: longitude latname_flx: latitude timename_flx: time @@ -77,21 +81,22 @@ timename_flx: time # Regional prior flux file: flux kg/m2/h # only needed if using nested output (needs to correspond to nest) # generic name with year YYYY -filenest_flx: CH4_TOTAL_YYYY_05x05.nc -varnest_flx: emisch4 -lonnest_flx: longitude -latnest_flx: latitude -timenest_flx: time +filenest_flx: +varnest_flx: +lonnest_flx: +latnest_flx: +timenest_flx: # Initial concentrations: conc ppb # file_initconc given as generic name with year and month YYYYMM -path_initconc: /mypath/TEST_INPUT/INIT_CONC/GHG/ -file_initconc: ch4_noaa_YYYYMM.nc +path_initconc: /mypath/init_conc/ +file_initconc: cams73_v20r1_ch4_conc_surface_dm_YYYYMM.nc varname_init: CH4 # type of product (1 = CAMS CH4, 2 = EGG4, 3 = CAMS N2O, 4 = FP-CTM (daily), 5 = NOAA interpolated (monthly), 6 = FP-CTM (monthly)) -init_type: 5 +init_type: 1 + +# Receptor list file (only needed if ground observations are used): +file_recept: /mypath/input/stnlist_global_ch4.txt -# Receptor list file: -file_recept: /mypath/TEST_INPUT/reclist_ghg.txt diff --git a/settings/SETTINGS_co2_config b/settings/SETTINGS_co2_config index 011642e..c35647a 100644 --- a/settings/SETTINGS_co2_config +++ b/settings/SETTINGS_co2_config @@ -67,6 +67,12 @@ restart: 0 # only use for debugging small runs verbose: .false. +# Use satellite observations (true or false) +satellite: .false. + +# Use ground-based observations (true or false) +ground: .true. + # Species ("co2" or "ghg") spec: co2 diff --git a/settings/SETTINGS_co2_files b/settings/SETTINGS_co2_files index 4af6a1f..b8deaab 100644 --- a/settings/SETTINGS_co2_files +++ b/settings/SETTINGS_co2_files @@ -29,11 +29,11 @@ # ================================================== # Paths: -path_obs: /mypath/TEST_OUTPUT/OBS/CO2/ -path_prior: /mypath/TEST_OUTPUT/FLUXES/CO2/ -path_output: /mypath/TEST_OUTPUT/RESULTS/CO2/NO_NEST/ -path_flexpart: /mypath/TEST_OUTPUT/FLEXOUT/CO2/NO_NEST/ -path_flexncdf: /mypath/TEST_OUTPUT/RESULTS/CO2/NO_NEST/ +path_obs: /mypath/obs/ +path_prior: /mypath/flux/ +path_output: /mypath/output/ +path_flexpart: /mypath/flexpart_output/ +path_flexncdf: /mypath/flexpart_ncdf_out/ # Prior best guess file (if prior_bg = 1) file_bg: @@ -46,21 +46,21 @@ file_log: flexinvert.log # Regions mask file: # needs to correspond to flexpart resolution (for nested output needs to correspond to nested resolution) -file_regions: /mypath/TEST_OUTPUT/regions_co2.nc +file_regions: /mypath/output/regions_co2.nc varname_regs: regions lonname_regs: longitude latname_regs: latitude # Orography file: # needs to correspond to global flexpart domain and resolution -file_orog: /mypath/TEST_INPUT/elev.1-deg.nc +file_orog: /mypath/input/elev.1-deg.nc varname_orog: data lonname_orog: lon latname_orog: lat # Land-sea mask file: # needs to correspond to flexpart resolution (for nested output needs to correspond to nested resolution) -file_lsm: /mypath/TEST_INPUT/lsm_1x1.nc +file_lsm: /mypath/input/lsm_1x1.nc varname_lsm: lsm lonname_lsm: lon latname_lsm: lat @@ -74,7 +74,7 @@ lonname_nee: longitude latname_nee: latitude timename_nee: time -# Global prior fossil fuel (and bio-fuel/mass burning) flux file: flux kg/m2/h +# Global prior fossil fuel (and bio-fuel/bio-mass burning) flux file: flux kg/m2/h # needs to correspond to flexpart domain and resolution # generic name with year and month YYYYMM filename_ff: CO2_FF_YYYYMM_10x10.nc @@ -101,7 +101,7 @@ lonnest_nee: latnest_nee: timenest_nee: -# Regional prior fossil fuel (and bio-fuel/mass burning) flux file: flux kg/m2/h +# Regional prior fossil fuel (and bio-fuel/bio-mass burning) flux file: flux kg/m2/h # only specify if using nested output (needs to correspond to nested resolution) # generic name with year and month YYYYMM filenest_ff: @@ -122,13 +122,13 @@ timenest_ocn: # Initial concentrations: conc (default is ppm) # file_initconc given as generic name with year and month YYYYMM # also need months preceding and proceeding inversion interval -path_initconc: /mypath/TEST_INPUT/INIT_CONC/CO2/ +path_initconc: /mypath/init_conc/ file_initconc: co2_noaa_YYYYMM.nc varname_init: CO2 # type of product (1 = CAMS CH4, 2 = EGG4, 3 = CAMS N2O and CO2, 4 = FP-CTM (daily), 5 = NOAA interpolated (monthly), 6 = FP-CTM (monthly)) init_type: 5 # Receptor list file: -file_recept: /mypath/TEST_INPUT/reclist_co2.txt +file_recept: /mypath/input/reclist_co2.txt diff --git a/settings/SETTINGS_co2_nest_config b/settings/SETTINGS_co2_nest_config deleted file mode 100644 index 54ca08a..0000000 --- a/settings/SETTINGS_co2_nest_config +++ /dev/null @@ -1,161 +0,0 @@ -# ================================================== -# -# FLEXINVERT CONFIGURATION SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# ================================================== - -# Run mode: -# 0 = run forward model -# 1 = run optimization -# 2 = randomly perturb for MC -run_mode: 1 - -# Random number seed -# (only used if run_mode = 2) -seed: 100 - -# Run dates: format yyyymmdd -# datei = start date -# datef = end date -datei: 20120101 -datef: 20120131 - -# Use lognormal distribution (true or false) (only for non-CO2 species) -# if true need to use m1qn3 method -lognormal: .false. - -# Truncation of eigenvalues of B (cut at trunc x largest eigenvalue) -trunc: 1.e-6 - -# Inversion method ('analytic', 'congrad' or 'm1qn3') -method: m1qn3 - -# Average/select flexpart releases (true or false) -average_fp: .false. - -# Number of iterations -# only used if method is 'congrad' or 'm1qn3' -maxiter: 20 - -# Optimize ocean boxes (true or false) -# currently only for ghg species (CO2 ocean fluxes not optimized) -inc_ocean: .false. - -# Optimize initial mixing ratios (true or false) -opt_cini: .true. - -# Use spatial correlation in error covariance matrix (true or false) -# if use regions based ecosystems then should be false -spa_corr: .true. - -# Use best guess estimate from previous inversion (congrad only) -# 0 = no best guess -# 1 = use best guess (file must be specified in SETTINGS_files) -prior_bg: 0 - -# Restart a crashed run -# for congrad/m1qn3 will pick-up from last iteration -# for analytic will only use pre-calculated covariance matrix and boundary conditions -# 0 = new run -# 1 = restart crashed run -restart: 0 - -# Verbose output -# only use for debugging small runs -verbose: .false. - -# Species ("co2" or "ghg") -spec: co2 - -# Molar mass (in flux files, e.g. C=12, CH4=16) -molarmass: 12. - -# Coefficient to convert from grid units of ppt to observation unit (e.g. ppm) -coeff: 1.e-6 - -# Use nested flexpart output (true or false) -nested: .true. - -# Inversion domain: -# if using nested output it must match the inversion domain -# w_edge_lon = lon of western edge of inversion grid -# s_edge_lat = lat of southern edge inversion grid -# e_edge_lon = lon of eastern edge of inversion grid -# n_edge_lat = lat of northern inversion grid -# xres = longitudinal grid resolution -# yres = latitudinal grid resolution -w_edge_lon: -15 -s_edge_lat: 30 -e_edge_lon: 35 -n_edge_lat: 75 -xres: 0.5 -yres: 0.5 - -# Spatial aggregation of grid (true or false) -regions: .true. - -# State vector time resolution: -# time resolution at which NEE fluxes are optimized -# statres determines averaging interval over 1 or more days (given in days) -# statres_hr determines time resolution within 1 day (given in hours) -# e.g. statres = 10, statres_hr = 6 give 4 time intervals per day that are averaged over 10 days -statres: 10 -statres_hr: 12 - -# NEE prior fluxes: -# nstep_nee = time step of NEE fluxes (integer hours) -nstep_nee: 3 -nstep_nee_reg: 3 - -# Fossil fuel prior fluxes: -# nstep_ff = time step of fossil fuel emissions (integer hours, for monthly data use 720) -# coef_ff = coefficient to convert from input flux units to kg/m2/h -nstep_ff: 720 -nstep_ff_reg: 720 -coeff_ff: 1. -coeff_ff_reg: 1. - -# Ocean prior fluxes: -# nstep_ocn = time step of other fluxes (integer hours, for monthly data use 720) -nstep_ocn: 720 - -# Measurement error: unit same as obs -# used if error in obs input <= zero -measerr: 0.5 - -# Initial mixing ratio error: fraction -cinierr: 0.005 - -# Prior flux error: fraction -flxerr: 0.25 - -# Fossil fuel error: fraction -# only used to calculate uncertainty projected into observation space -ffferr: 0.05 - -# Spatial correlation length for land: unit (km) -sigma_land: 50. - -# Spatial correlation length for ocean: unit (km) -sigma_ocean: 2000. - -# Temporal correlation length: unit (days) -sigmatime: 30. - -# Total error for domain (Tg/y) -# globerr <= 0: prior error covariance matrix not scaled -globerr: 300. - -# Settings for optimization of initial mixing ratios -# comma separated list of northern edges of latitude bands -cini_lat: -30.,0.,30.,90. -# comma separated list of upper level of vertical bands (upper most level > outheight(nzgrid)) -#cini_alt: 2000., 10000., 55000. -cini_alt: 55000. -# time resolution for initial mixing ratio scalars (days) -cinires: 30. - - diff --git a/settings/SETTINGS_co2_nest_files b/settings/SETTINGS_co2_nest_files deleted file mode 100644 index 2420049..0000000 --- a/settings/SETTINGS_co2_nest_files +++ /dev/null @@ -1,133 +0,0 @@ -# ================================================== -# -# FLEXINVERT FILE SETTINGS -# -# comment lines start with '#' -# each parameter line starts with 'parameter name:' -# -# Prior fluxes file dimensions: -# time : days since 1900 -# latitude : latitude degrees north midpoints -# longitude : longitude degrees east midpoints -# -# Regional mask file dimensions: -# latitude : latitude degrees north midpoints -# longitude : longitude degrees east midpoints -# -# Land-sea mask file dimensions: -# latitude : latitude degrees north midpoints -# longitude : longitude degrees east midpoints -# -# Initial concentrations file dimensions: -# latitude : latitude degrees north south boundary -# longitude : longitude degrees east west boundary -# -# Receptor list file: -# ascii file containing one column list of -# receptor names -# -# ================================================== - -# Paths: -path_obs: /mypath/TEST_OUTPUT/OBS/CO2/ -path_prior: /mypath/TEST_OUTPUT/FLUXES/CO2/ -path_output: /mypath/TEST_OUTPUT/RESULTS/CO2/NEST/ -path_flexpart: /mypath/TEST_OUTPUT/FLEXOUT/CO2/NEST/ - -# Prior best guess file (if prior_bg = 1) -file_bg: - -# Suffix by which to identify observation files -suffix: .txt - -# Log file: -file_log: flexinvert.log - -# Regions mask file: -# needs to correspond to flexpart resolution (for nested output needs to correspond to nested resolution) -file_regions: /mypath/TEST_OUTPUT/regions_co2_nest.nc -varname_regs: regions -lonname_regs: longitude -latname_regs: latitude - -# Orography file: -# needs to correspond to global flexpart domain and resolution -file_orog: /mypath/TEST_INPUT/elev.2-deg.nc -varname_orog: data -lonname_orog: lon -latname_orog: lat - -# Land-sea mask file: -# needs to correspond to flexpart resolution (for nested output needs to correspond to nested resolution) -file_lsm: /mypath/TEST_INPUT/lsm_0.5x0.5.nc -varname_lsm: lsm -lonname_lsm: lon -latname_lsm: lat - -# Global prior NEE flux file: flux kg/m2/h -# needs to correspond to flexpart domain and resolution -# generic name with year YYYY -filename_nee: CO2_NEE_YYYY_20x20.nc -varname_nee: nee -lonname_nee: longitude -latname_nee: latitude -timename_nee: time - -# Global prior fossil fuel (and bio-fuel/mass burning) flux file: flux kg/m2/h -# needs to correspond to flexpart domain and resolution -# generic name with year and month YYYYMM -filename_ff: CO2_FF_YYYYMM_20x20.nc -varname_ff: emisco2 -lonname_ff: longitude -latname_ff: latitude -timename_ff: time - -# Global prior ocean flux file: flux kg/m2/h -# needs to correspond to flexpart domain and resolution -# generic name with year YYYY -filename_ocn: CO2_OCEAN_YYYY_20x20.nc -varname_ocn: emisco2 -lonname_ocn: longitude -latname_ocn: latitude -timename_ocn: time - -# Regional prior NEE flux file: flux kg/m2/h -# only specify if using nested output (needs to correspond to nested resolution) -# generic name with year YYYY -filenest_nee: CO2_NEE_YYYY_05x05.nc -varnest_nee: nee -lonnest_nee: longitude -latnest_nee: latitude -timenest_nee: time - -# Regional prior fossil fuel (and bio-fuel/mass burning) flux file: flux kg/m2/h -# only specify if using nested output (needs to correspond to nested resolution) -# generic name with year and month YYYYMM -filenest_ff: CO2_FF_YYYYMM_05x05.nc -varnest_ff: emission -lonnest_ff: lon -latnest_ff: lat -timenest_ff: time - -# Regional prior ocean flux file: flux kg/m2/h -# only specify if using nested output (needs to correspond to nested resolution) -# generic name with year YYYY -filenest_ocn: CO2_OCEAN_YYYY_05x05.nc -varnest_ocn: emisco2 -lonnest_ocn: longitude -latnest_ocn: latitude -timenest_ocn: time - -# Initial concentrations: conc (default is ppm) -# file_initconc given as generic name with year and month YYYYMM -# also need months preceding and proceeding inversion interval -path_initconc: /mypath/TEST_INPUT/INIT_CONC/CO2/ -file_initconc: co2_noaa_YYYYMM.nc -varname_init: CO2 -# type of product (1 = CAMS CH4, 2 = EGG4, 3 = CAMS N2O and CO2, 4 = FP-CTM (daily), 5 = NOAA interpolated (monthly), 6 = FP-CTM (monthly)) -init_type: 5 - -# Receptor list file: -file_recept: /mypath/TEST_INPUT/reclist_co2.txt - - diff --git a/settings/SETTINGS_ghg_config b/settings/SETTINGS_ghg_config index f684ff0..225cc30 100644 --- a/settings/SETTINGS_ghg_config +++ b/settings/SETTINGS_ghg_config @@ -31,14 +31,14 @@ lognormal: .false. trunc: 1.e-4 # Inversion method ('analytic', 'congrad' or 'm1qn3') -method: analytic +method: congrad # Average/select flexpart releases (true or false) average_fp: .true. # Number of iterations # only used if method is 'congrad' or 'm1qn3' -maxiter: 2 +maxiter: 10 # Optimize ocean boxes (true or false) inc_ocean: .true. @@ -66,6 +66,12 @@ restart: 0 # only use for debugging small runs verbose: .true. +# Use satellite observations (true or false) +satellite: .false. + +# Use ground-based observations (true or false) +ground: .true. + # Species ("co2" or "ghg") spec: ghg diff --git a/settings/SETTINGS_ghg_files b/settings/SETTINGS_ghg_files index 663abb9..9685143 100644 --- a/settings/SETTINGS_ghg_files +++ b/settings/SETTINGS_ghg_files @@ -29,11 +29,11 @@ # ================================================== # Paths: -path_obs: /mypath/TEST_OUTPUT/OBS/GHG/ -path_prior: /mypath/TEST_OUTPUT/FLUXES/GHG/ -path_output: /mypath/TEST_OUTPUT/RESULTS/GHG/NO_NEST/congrad/ -path_flexpart: /mypath/TEST_OUTPUT/FLEXOUT/GHG/NO_NEST/ -path_flexncdf: /mypath/TEST_OUTPUT/RESULTS/GHG/NO_NEST/congrad/ +path_obs: /mypath/obs/ +path_prior: /mypath/flux/ +path_output: /mypath/output/ +path_flexpart: /mypath/flexpart_output/ +path_flexncdf: /mypath/flexpart_ncdf_out/ # Prior best guess file (if prior_bg = 1 in SETTINGS_config) file_bg: @@ -46,21 +46,21 @@ file_log: flexinvert.log # Regions mask file: # needs to correspond to flexpart domain and resolution (if using nested output needs to correspond to nest) -file_regions: /mypath/TEST_OUTPUT/regions_ghg.nc +file_regions: /mypath/output/regions_ghg.nc varname_regs: regions lonname_regs: longitude latname_regs: latitude # Orography file: # needs to correspond to global flexpart domain and resolution -file_orog: /xnilu_wrk/users/rlt/LANDCOVER/elev.1-deg.nc +file_orog: /mypath/input/elev.1-deg.nc varname_orog: data lonname_orog: lon latname_orog: lat # Land-sea mask file: # needs to correspond to flexpart domain and resolution (if using nested output needs to correspond to nest) -file_lsm: /mypath/TEST_INPUT/lsm_1x1.nc +file_lsm: /mypath/input/lsm_1x1.nc varname_lsm: lsm lonname_lsm: lon latname_lsm: lat @@ -85,13 +85,13 @@ timenest_flx: # Initial concentrations: conc ppb # file_initconc given as generic name with year and month YYYYMM -path_initconc: /mypath/TEST_INPUT/INIT_CONC/GHG/ +path_initconc: /mypath/init_conc/ file_initconc: ch4_noaa_YYYYMM.nc varname_init: CH4 # type of product (1 = CAMS CH4, 2 = EGG4, 3 = CAMS N2O, 4 = FP-CTM (daily), 5 = NOAA interpolated (monthly), 6 = FP-CTM (monthly)) init_type: 5 # Receptor list file: -file_recept: /mypath/TEST_INPUT/reclist_ghg.txt +file_recept: /mypath/input/reclist_ghg.txt diff --git a/source/README_source.txt b/source/README_source.txt index ec2f2b3..b978b3a 100644 --- a/source/README_source.txt +++ b/source/README_source.txt @@ -1,11 +1,11 @@ ================================================================ - FLEXINVERT-Plus + FLEXINVERT source code ================================================================ Description: - FLEXINVERT-Plus is a Bayesian inversion framework + FLEXINVERT is a Bayesian inversion framework for optimizing fluxes of different atmospheric species (e.g. CO2, CH4, BC). diff --git a/source/average_fp.f90 b/source/average_fp.f90 index fc854fa..4600328 100755 --- a/source/average_fp.f90 +++ b/source/average_fp.f90 @@ -76,13 +76,14 @@ subroutine average_fp(files, config, obs) real, dimension(nnxgrid,nnygrid,maxngrid):: gridnest_tmp ! flexpart nest flux sensitivity (or footprints), temporary to later calculate average real(kind=8), dimension(maxngrid) :: gtime ! flux sensitivity time stamp (julian days) real(kind=8), dimension(maxngrid) :: gtime_tmp ! flux sensitivity time stamp (julian days) of first footprint per observation - integer :: ngrid + integer :: ngrid, nretr, ierr real :: bndx, bndy, delx, dely integer :: numx, numy, xshift, new_grid_time, standard_grid_time integer :: numpoint, release_nr, nr_footprints integer :: ibdate, ibtime real(kind=8), dimension(maxpoint,2) :: releases + logical :: lsatellite ! logical indicating if observation is from satellite ! loop over observations ! ---------------------- @@ -91,6 +92,15 @@ subroutine average_fp(files, config, obs) do i = 1, nobs + ! check if observation is from satellite and if so skip + read(obs%recs(i),*,iostat=ierr) nretr + if ( ierr.eq.0 ) then + lsatellite = .true. + else + lsatellite = .false. + endif + if ( lsatellite ) cycle + ! reset values gtime_tmp(:) = 0d0 grid_tmp(:,:,:) = 0. diff --git a/source/calc_conc.f90 b/source/calc_conc.f90 index e8f74af..d08fdd0 100644 --- a/source/calc_conc.f90 +++ b/source/calc_conc.f90 @@ -54,7 +54,7 @@ subroutine calc_conc(config, fluxes, obs, ngrid, gtime, hnest, hbkg, iobs, ix1, real, dimension(nxgrid,nygrid), intent (in) :: hbkg integer, intent (in) :: ngrid, iobs, ix1, ix2, jy1, jy2 - integer :: n, ns, ni, flag, ind, ilo, ihi + integer :: n, ns, ni, flag, ind, ilo, ihi, ierr real, dimension(nxgrid,nygrid) :: flxbg real :: bkgerr, ffferr @@ -149,9 +149,11 @@ subroutine calc_conc(config, fluxes, obs, ngrid, gtime, hnest, hbkg, iobs, ix1, ! ----------------------- if ( trim(config%spec).eq.'co2' ) then - obs%err(iobs) = sqrt(obs%measerr(iobs)**2 + bkgerr + ffferr) + ! CO2 + obs%err(iobs) = sqrt(obs%measerr(iobs)**2 + (sum(obs%cini(iobs,:))*config%cinierr)**2 + bkgerr + ffferr) else - obs%err(iobs) = sqrt(obs%measerr(iobs)**2 + bkgerr) + ! GHG + obs%err(iobs) = sqrt(obs%measerr(iobs)**2 + (sum(obs%cini(iobs,:))*config%cinierr)**2 + bkgerr) endif end subroutine calc_conc diff --git a/source/congrad.f90 b/source/congrad.f90 index e88fd53..e1ef950 100644 --- a/source/congrad.f90 +++ b/source/congrad.f90 @@ -14,7 +14,7 @@ ! You should have received a copy of the GNU General Public License ! along with FLEXINVERT. If not, see <http://www.gnu.org/licenses/>. ! -! Copyright 2017, Rona Thompson +! Adapted from the code of Mike Fisher (ECMWF) by Rona Thompson, 2017 !--------------------------------------------------------------------------------------- ! !> congrad @@ -43,7 +43,9 @@ !! !! References: Lanczos, C., J. Res. Natl. Bur. Standards, 45, 255-282, 1950 !! Paige, C. C. & Saunders, M. A., SIAM J. Numerical Aanalysis, 12(4), 1975 -!! Based on the code of Mike Fisher, ECMWF +!! +!! Based on the code of Mike Fisher, ECMWF, 2002 +!! and the adaptation of Frederic Chevallier, LSCE, 2004 ! !--------------------------------------------------------------------------------------- @@ -184,6 +186,10 @@ subroutine congrad(iter, grad, files, config, fluxes, obs, states, covar) cost = 0.5*(cost_p + cost_o) write(aiter,'(I2.2)') iter write(logid,*) 'Cost iteration '//aiter//': ',cost + if ( isnan(cost) ) then + write(logid,*) 'ERROR congrad: cost is nan' + stop + endif endif ! transform gradient to chi space diff --git a/source/get_initconc.f90 b/source/get_initconc.f90 index 35dc1ab..f4b335a 100644 --- a/source/get_initconc.f90 +++ b/source/get_initconc.f90 @@ -207,7 +207,7 @@ subroutine get_initconc(files, config, filename, ntime, concini) mpres = mpres*100. case (3) ! CAMS N2O and CO2 - if ( config%spec.eq.'n2o' ) then + if ( config%spec.eq.'ghg' ) then ! vertical coordinate is pressure allocate( mpres(nz) ) call check( nf90_inq_varid(ncid,trim(prsname),varid) ) @@ -313,7 +313,7 @@ subroutine get_initconc(files, config, filename, ntime, concini) zkind = 0 case (3) ! CAMS N2O and CO2 - if ( config%spec.eq.'n2o' ) then + if ( config%spec.eq.'ghg' ) then do n = 1, nz ! at present temp not included so use estimate geoh(:,:,n) = (gasc*ts/gc/mmol)*log(psurf/mpres(n)) @@ -330,9 +330,9 @@ subroutine get_initconc(files, config, filename, ntime, concini) rpres = sum(surfp(:,:,:),dim=3)/real(nt)/pres(:,:,n) geoh(:,:,n) = (gasc*ts/gc/mmol)*log(rpres) end do + deallocate( rpres ) + deallocate( surfp ) endif - deallocate( rpres ) - deallocate( surfp ) zkind = 0 case (4) ! FP-CTM diff --git a/source/init_cini.f90 b/source/init_cini.f90 index 19a493d..9577c63 100644 --- a/source/init_cini.f90 +++ b/source/init_cini.f90 @@ -63,7 +63,7 @@ subroutine init_cini(files, config, obs) integer :: jjjjmm, jjjjmmdd, hhmiss, eomday integer :: lasttime, startmonth integer :: month, prevmonth - integer :: ix, jy, kz, i, n, nt, nz, nhours, ntime, ierr + integer :: ix, jy, kz, i, iz, n, nt, nz, nhours, ntime, ierr real(kind=8) :: jdi real(kind=8), dimension(nobs) :: jdates real, dimension(nobs,ncini) :: cini @@ -76,6 +76,7 @@ subroutine init_cini(files, config, obs) integer :: numpoint, release_nr, nr_init integer :: ibdate, ibtime real(kind=8), dimension(maxpoint,2) :: releases + logical :: lsatellite integer :: nretr character(len=recname_len) :: anretr @@ -113,18 +114,31 @@ subroutine init_cini(files, config, obs) gridini(:,:,:) = 0. nr_init = 0 - print*, 'obs%recs(i) = ',obs%recs(ind(i)) + ! check if observation is a satellite retrieval + read(obs%recs(ind(i)),*,iostat=ierr) nretr + if ( ierr.eq.0 ) then + lsatellite = .true. + else + lsatellite = .false. + endif + + print*, 'init_cini: obs%recs(i) = ',obs%recs(ind(i)) ! check month of observation call caldate(jdates(i), jjjjmmdd, hhmiss) write(adate,'(I6.6)') jjjjmmdd/100 write(areldate,'(I8.8)') jjjjmmdd month = jjjjmmdd/100 - path_flexrec = trim(files%path_flexpart)//trim(obs%recs(ind(i)))//'/'//trim(adate)//'/' + if ( lsatellite ) then + path_flexrec = trim(files%path_flexsat)//trim(areldate)//'/' + else + path_flexrec = trim(files%path_flexpart)//trim(obs%recs(ind(i)))//'/'//trim(adate)//'/' + endif - if ( config%average_fp ) then + if ( config%average_fp.and..not.lsatellite ) then ! match and, if needed, average grid_initial files to observation timestamp + ! note: average_fp is not possible for satellite observations ! get header and release information related to current observation location ! (if not already read and same for previous observation) @@ -176,6 +190,8 @@ subroutine init_cini(files, config, obs) end do ! finished summing grid_initial ! convert from ppt to fraction of one gridini = gridini*1.e-12 + ! normalize to one + gridini = gridini/sum(gridini) gridini = gridini/real(nr_init) else ! filename not found @@ -190,7 +206,13 @@ subroutine init_cini(files, config, obs) ! define file name write(areldate,'(I8.8)') jjjjmmdd write(areltime,'(I6.6)') hhmiss - filename = trim(path_flexrec)//'grid_initial_'//trim(areldate)//trim(areltime)//'_001' + if ( lsatellite ) then + ! satellite observation + write(anretr,'(I6.6)') nretr + filename = trim(path_flexrec)//'grid_initial_'//trim(areldate)//'_'//trim(anretr) + else + filename = trim(path_flexrec)//'grid_initial_'//trim(areldate)//trim(areltime)//'_001' + endif print*, 'init_cini: filename = ',filename ! read flexpart grid_init file inquire(file=trim(filename),exist=lexist) @@ -198,6 +220,11 @@ subroutine init_cini(files, config, obs) call read_init(filename, gridini) ! convert from ppt to fraction of one gridini = gridini*1.e-12 + if ( lsatellite ) then + ! satellite convert to mole fraction + ! NO LONGER NEEDED WITH NEW FP VERSION +! gridini = gridini/obs%cdryair(ind(i)) + endif else write(logid,*) 'WARNING: cannot find ',filename go to 10 @@ -253,10 +280,12 @@ subroutine init_cini(files, config, obs) print*, 'init_cini: nhours = ',nhours print*, 'init_cini: nt = ',nt do kz = 1, nzgrid - nz = 0 - do while(.true.) - nz = nz + 1 - if ( outheight(kz).lt.cini_alt(nz) ) exit + nz = nzcini + do iz = 1, nzcini + if ( outheight(kz).lt.cini_alt(iz) ) then + nz = iz + exit + endif end do do jy = 1, nygrid n = 0 @@ -283,6 +312,7 @@ subroutine init_cini(files, config, obs) if (allocated( concini )) deallocate( concini ) + end subroutine init_cini diff --git a/source/init_cini_month.f90 b/source/init_cini_month.f90 index d709da6..f2f56d9 100644 --- a/source/init_cini_month.f90 +++ b/source/init_cini_month.f90 @@ -59,7 +59,7 @@ subroutine init_cini_month(files, config, obs) logical :: lexist integer :: lastjjjjmm, prejjjjmm, projjjjmm integer :: jjjjmm, jjjjmmdd, hhmiss, mm, eomday - integer :: ix, jy, kz, i, n, nz, ntime + integer :: ix, jy, kz, i, iz, n, nz, ntime integer :: month, prevmonth real :: conc real(kind=8) :: jdmid, jdpre, jdlast @@ -74,6 +74,7 @@ subroutine init_cini_month(files, config, obs) integer :: numpoint, release_nr, nr_init integer :: ibdate, ibtime real(kind=8), dimension(maxpoint,2) :: releases + logical :: lsatellite integer :: nretr, ierr character(len=6) :: anretr @@ -98,16 +99,31 @@ subroutine init_cini_month(files, config, obs) gridini(:,:,:) = 0. nr_init = 0 + ! check if observation is a satellite retrieval + read(obs%recs(ind(i)),*,iostat=ierr) nretr + if ( ierr.eq.0 ) then + lsatellite = .true. + else + lsatellite = .false. + endif + ! check month of observation call caldate(jdates(i), jjjjmmdd, hhmiss) write(adate,'(I6.6)') jjjjmmdd/100 write(areldate,'(I8.8)') jjjjmmdd month = jjjjmmdd/100 - path_flexrec = trim(files%path_flexpart)//trim(obs%recs(ind(i)))//'/'//trim(adate)//'/' + if ( lsatellite ) then + ! satellite + path_flexrec = trim(files%path_flexsat)//trim(areldate)//'/' + else + ! ground-based + path_flexrec = trim(files%path_flexpart)//trim(obs%recs(ind(i)))//'/'//trim(adate)//'/' + endif - if ( config%average_fp ) then + if ( config%average_fp.and..not.lsatellite ) then ! match and, if needed, average grid_initial files to observation timestamp + ! note: average_fp is not possible for satellite observations ! get header and release information related to current observation location ! (if not already read and same for previous observation) @@ -159,6 +175,8 @@ subroutine init_cini_month(files, config, obs) end do ! finished summing grid_initial ! convert from ppt to fraction of one gridini = gridini*1.e-12 + ! normalize to one + gridini = gridini/sum(gridini) gridini = gridini/real(nr_init) else ! filename not found @@ -173,7 +191,14 @@ subroutine init_cini_month(files, config, obs) ! define file name write(areldate,'(I8.8)') jjjjmmdd write(areltime,'(I6.6)') hhmiss - filename = trim(path_flexrec)//'grid_initial_'//trim(areldate)//trim(areltime)//'_001' + if ( lsatellite ) then + ! satellite observation + write(anretr,'(I6.6)') nretr + filename = trim(path_flexrec)//'grid_initial_'//trim(areldate)//'_'//trim(anretr) + else + ! ground based observation + filename = trim(path_flexrec)//'grid_initial_'//trim(areldate)//trim(areltime)//'_001' + endif print*, 'init_cini_month: filename = ',filename ! read flexpart grid_init file inquire(file=trim(filename),exist=lexist) @@ -181,6 +206,11 @@ subroutine init_cini_month(files, config, obs) call read_init(filename, gridini) ! convert from ppt to fraction of one gridini = gridini*1.e-12 + if ( lsatellite ) then + ! satellite convert to mole fraction + ! NO LONGER NEEDED WITH NEW FP VERSION +! gridini = gridini/obs%cdryair(ind(i)) + endif else write(logid,*) 'WARNING: cannot find ',filename go to 10 @@ -220,11 +250,11 @@ subroutine init_cini_month(files, config, obs) write(adate,'(I6.6)') projjjjmm write(ayear,'(I4.4)') projjjjmm/100 select case ( files%init_type ) - case (4) + case (5) pathname = str_replace(files%path_initconc, 'YYYY', ayear) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(pathname)//trim(filename) - case (5) + case (6) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(files%path_initconc)//trim(filename) end select @@ -242,11 +272,11 @@ subroutine init_cini_month(files, config, obs) write(adate,'(I6.6)') jjjjmm write(ayear,'(I4.4)') jjjjmm/100 select case ( files%init_type ) - case (4) + case (5) pathname = str_replace(files%path_initconc, 'YYYY', ayear) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(pathname)//trim(filename) - case (5) + case (6) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(files%path_initconc)//trim(filename) end select @@ -262,11 +292,11 @@ subroutine init_cini_month(files, config, obs) write(adate,'(I6.6)') prejjjjmm write(ayear,'(I4.4)') prejjjjmm/100 select case ( files%init_type ) - case (4) + case (5) pathname = str_replace(files%path_initconc, 'YYYY', ayear) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(pathname)//trim(filename) - case (5) + case (6) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(files%path_initconc)//trim(filename) end select @@ -282,11 +312,11 @@ subroutine init_cini_month(files, config, obs) write(adate,'(I6.6)') projjjjmm write(ayear,'(I4.4)') projjjjmm/100 select case ( files%init_type ) - case (4) + case (5) pathname = str_replace(files%path_initconc, 'YYYY', ayear) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(pathname)//trim(filename) - case (5) + case (6) filename = str_replace(files%file_initconc, 'YYYYMM', adate) filename = trim(files%path_initconc)//trim(filename) end select @@ -304,10 +334,12 @@ subroutine init_cini_month(files, config, obs) ! calculate contribution of initial concentration to receptor do kz = 1, nzgrid - nz = 0 - do while(.true.) - nz = nz + 1 - if ( outheight(kz).lt.cini_alt(nz) ) exit + nz = nzcini + do iz = 1, nzcini + if ( outheight(kz).lt.cini_alt(nz) ) then + nz = iz + exit + endif end do do jy = 1, nygrid n = 0 diff --git a/source/init_co2.f90 b/source/init_co2.f90 index 2c3032f..ba580d1 100644 --- a/source/init_co2.f90 +++ b/source/init_co2.f90 @@ -85,7 +85,7 @@ subroutine init_co2(files, config, fluxes, obs, states, covar) real, dimension(:,:,:), allocatable :: flx, flxnee, flxff, flxocn real, dimension(:), allocatable :: xerr integer, dimension(ndt) :: cnt - integer :: nread, num + integer :: nread, num, numocn integer :: eomday, eomday_save integer :: jjjjmmdd, hhmiss, hh real :: area @@ -94,7 +94,7 @@ subroutine init_co2(files, config, fluxes, obs, states, covar) integer :: n, nb, nd, nt, nts, i integer :: ierr, lenstr real(kind=8), dimension(:), allocatable :: jdate - character(len=3), dimension(:), allocatable :: recs + character(len=recname_len), dimension(:), allocatable :: recs real :: conc character(len=20) :: dump real, dimension(:,:), allocatable :: datain, dataout @@ -398,10 +398,11 @@ subroutine init_co2(files, config, fluxes, obs, states, covar) write(logid,*) 'Reading regional ocean fluxes' ! nested fluxes%flxocn_nest(:,:,:) = 0. - print*, 'init_co2: ocn reg timesteps input to read = ',(nday*24)/nt_ocn - allocate( flxocn(nxregrid,nyregrid,(nday*24)/nt_ocn), stat = ierr ) + numocn = max(1,(nday*24)/nt_ocn) + print*, 'init_co2: ocn reg timesteps input to read = ',numocn + allocate( flxocn(nxregrid,nyregrid,numocn), stat = ierr ) if ( ierr.ne.0 ) stop 'ERROR init_co2: not enough memory' - allocate( timeocn((nday*24)/nt_ocn) ) + allocate( timeocn(numocn) ) n = 0 jd = juldatei do while ( jd.le.juldatef ) @@ -438,7 +439,7 @@ subroutine init_co2(files, config, fluxes, obs, states, covar) flx) ! timestamp in julian days do i = 1, nread - if ( (n+i).gt.(nday*24)/nt_ocn ) exit + if ( (n+i).gt.numocn ) exit timeocn(n+i) = jd + dble((i-1)*nt_ocn)/24d0 flxocn(:,:,n+i) = flx(:,:,i) end do @@ -446,7 +447,7 @@ subroutine init_co2(files, config, fluxes, obs, states, covar) n = n + nread jd = jd + dble(eomday_save) end do - n = min(n, (nday*24)/nt_ocn) + n = min(n, numocn) print*, 'init_co2: ocnreg n = ',n print*, 'init_co2: timeocn = ',timeocn @@ -804,7 +805,7 @@ subroutine init_co2(files, config, fluxes, obs, states, covar) endif allocate( recs(nobs) ) allocate( jdate(nobs) ) - write(rowfmt,'(A,I6,A)') '(A3,1X,F14.6,1X,F10.4,1X,',ncini,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,F14.6,1X,F10.4,1X,',ncini,'(F10.4,1X))' read(100,*) dump do n = 1, nobs read(100,rowfmt,iostat=ierr) recs(n), jdate(n), conc, obs%cini(n,:) @@ -816,6 +817,10 @@ subroutine init_co2(files, config, fluxes, obs, states, covar) ! check consistency if ( recs(1).ne.obs%recs(1).or.recs(nobs).ne.obs%recs(nobs).or.jdate(1).ne.obs%jdate(1).or.jdate(nobs).ne.obs%jdate(nobs) ) then write(logid,*) 'ERROR init_co2: data in obsdatatype.txt inconsistent with run' + print*, recs(1), obs%recs(1) + print*, recs(nobs), obs%recs(nobs) + print*, jdate(1), obs%jdate(1) + print*, jdate(nobs), obs%jdate(nobs) stop endif deallocate( recs ) diff --git a/source/init_ghg.f90 b/source/init_ghg.f90 index 3ad2314..63cb925 100644 --- a/source/init_ghg.f90 +++ b/source/init_ghg.f90 @@ -89,7 +89,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) integer :: i, n, nb, nread, num, eomday integer :: ierr real(kind=8), dimension(:), allocatable :: jdate, timeghg - character(len=3), dimension(:), allocatable:: recs + character(len=recname_len), dimension(:), allocatable:: recs real :: conc character(len=20) :: dump real, parameter :: smallnum=1.e-15 @@ -107,9 +107,9 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) ! loop over prior flux files - allocate( flxghg(nxgrid,nygrid,(nday*24/nt_flx)), stat = ierr ) + allocate( flxghg(nxgrid,nygrid,max(1,(nday*24/nt_flx))), stat = ierr ) if ( ierr.ne.0 ) stop 'ERROR init_ghg: not enough memory' - allocate( timeghg(nday*24/nt_flx) ) + allocate( timeghg(max(1,nday*24/nt_flx)) ) n = 0 jd = juldatei @@ -144,7 +144,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) jd, nread, num, & flx) do i = 1, nread - if ( (n+i).gt.(nday*24/nt_flx) ) exit + if ( ((n+i).gt.(nday*24/nt_flx)).and.(nday*24/nt_flx.ne.0) ) exit flxghg(:,:,n+i) = flx(:,:,i) timeghg(n+i) = jd + dble((i-1)*nt_flx)/24d0 end do @@ -153,6 +153,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) jd = jd + dble(eomday) end do n = min(n, nday*24/nt_flx) + n = max(n, 1) print*, 'init_ghg: n = ',n print*, 'init_ghg: timeghg = ',timeghg @@ -174,7 +175,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) call average(nxgrid, n, timeghg, datain, ntstate, fluxes%time, dataout) fluxes%flx(:,jy,:) = dataout(:,:) end do - else if ( statres.gt.real(nt_flx/24) ) then + else if ( statres.lt.real(nt_flx/24) ) then ! interpolate flux do jy = 1, nygrid datain = flxghg(:,jy,:) @@ -207,9 +208,9 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) if ( config%nested ) then - allocate( flxghg(nxregrid,nyregrid,(nday*24/nt_flx)), stat = ierr ) + allocate( flxghg(nxregrid,nyregrid,(max(1,nday*24/nt_flx))), stat = ierr ) if ( ierr.ne.0 ) stop 'ERROR init_ghg: not enough memory' - allocate( timeghg(nday*24/nt_flx) ) + allocate( timeghg(max(1,nday*24/nt_flx)) ) n = 0 jd = juldatei @@ -244,7 +245,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) jd, nread, num, & flx) do i = 1, nread - if ( (n+i).gt.(nday*24)/nt_flx ) exit + if ( ((n+i).gt.(nday*24)/nt_flx).and.((nday*24)/nt_flx.ne.0) ) exit timeghg(n+i) = jd + dble((i-1)*nt_flx)/24d0 flxghg(:,:,n+i) = flx(:,:,i) end do @@ -253,6 +254,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) jd = jd + dble(eomday) end do n = min(n, (nday*24)/nt_flx) + n = max(n, 1) print*, 'init_ghg: reg n = ',n print*, 'init_ghg: reg timeghg = ',timeghg @@ -271,7 +273,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) call average(nxregrid, n, timeghg, datain, ntstate, fluxes%time, dataout) fluxes%flx_nest(:,jy,:) = dataout(:,:) end do - else if ( statres.gt.real(nt_flx/24) ) then + else if ( statres.lt.real(nt_flx/24) ) then ! interpolate flux do jy = 1, nyregrid datain = flxghg(:,jy,:) @@ -513,7 +515,7 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) endif allocate( recs(nobs) ) allocate( jdate(nobs) ) - write(rowfmt,'(A,I6,A)') '(A3,1X,F14.6,1X,F10.4,1X,',ncini,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,F14.6,1X,F10.4,1X,',ncini,'(F10.4,1X))' read(100,*) dump do n = 1, nobs read(100,rowfmt,iostat=ierr) recs(n), jdate(n), conc, obs%cini(n,:) @@ -525,6 +527,10 @@ subroutine init_ghg(files, config, fluxes, obs, states, covar) ! check consistency if ( recs(1).ne.obs%recs(1).or.recs(nobs).ne.obs%recs(nobs).or.jdate(1).ne.obs%jdate(1).or.jdate(nobs).ne.obs%jdate(nobs) ) then write(logid,*) 'ERROR init_ghg: data in obsdatatype.txt inconsistent with run' + print*, recs(1), obs%recs(1) + print*, recs(nobs), obs%recs(nobs) + print*, jdate(1), obs%jdate(1) + print*, jdate(nobs), obs%jdate(nobs) stop endif deallocate( recs ) diff --git a/source/initialize.f90 b/source/initialize.f90 index 64e4ef4..adbb03d 100644 --- a/source/initialize.f90 +++ b/source/initialize.f90 @@ -49,6 +49,7 @@ subroutine initialize(files, config) logical :: lexist integer :: yyyymm character(len=6) :: adate + character(len=8) :: areldate character(len=2) :: aiter integer :: numpoint integer :: ibdate, ibtime, jjjjmmdd, hhmiss @@ -110,26 +111,35 @@ subroutine initialize(files, config) filename = trim(files%file_recept) inquire(file=trim(filename),exist=lexist) - if (.not.lexist) then + if ( .not.lexist.and.config%ground ) then write(logid,*) 'ERROR: cannot find '//trim(filename) stop + else if ( lexist.and.config%ground ) then + write(logid,*) 'Reading receptor list: '//trim(filename) + call read_reclist(config, filename) endif - write(logid,*) 'Reading receptor list: '//trim(filename) - call read_reclist(filename) ! initialize flexpart variables ! ----------------------------- ! read header for global output - yyyymm=config%datei/100 - write(adate,'(i6)') yyyymm - lexist = .false. - i = 1 - do while ( (.not.lexist).and.(i.le.nrec) ) - filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' + if ( config%satellite.and.nrec.eq.0 ) then + ! satellite only + write(areldate,'(I8)') config%datei + filename = trim(files%path_flexsat)//areldate//'/header' inquire(file=trim(filename),exist=lexist) - i = i + 1 - end do + else + ! read from ground-based output + yyyymm=config%datei/100 + write(adate,'(I6)') yyyymm + lexist = .false. + i = 1 + do while ( (.not.lexist).and.(i.le.nrec) ) + filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header' + inquire(file=trim(filename),exist=lexist) + i = i + 1 + end do + endif ! satellite if ( .not.lexist ) then write(logid,*) 'ERROR initialize: cannot find flexpart header' stop @@ -137,6 +147,7 @@ subroutine initialize(files, config) write(logid,*) 'Reading flexpart header: '//trim(filename) call read_header(filename, numpoint, ibdate, ibtime, releases, & numx, numy, bndx, bndy, delx, dely, xshift) + ! global domain variables nxgrid = numx nygrid = numy @@ -156,13 +167,21 @@ subroutine initialize(files, config) ! read header for nested output if ( config%nested ) then - lexist = .false. - i = 1 - do while ( (.not.lexist).and.(i.le.nrec) ) - filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' + if ( config%satellite.and.nrec.eq.0 ) then + ! satellite only + write(areldate,'(I8)') config%datei + filename = trim(files%path_flexsat)//areldate//'/header_nest' inquire(file=trim(filename),exist=lexist) - i = i + 1 - end do + else + ! read from ground-based output + lexist = .false. + i = 1 + do while ( (.not.lexist).and.(i.le.nrec) ) + filename = trim(files%path_flexpart)//trim(recname(i))//'/'//adate//'/header_nest' + inquire(file=trim(filename),exist=lexist) + i = i + 1 + end do + endif ! satellite if ( .not.lexist ) then write(logid,*) 'ERROR initialize: cannot find flexpart header_nest' stop @@ -170,6 +189,7 @@ subroutine initialize(files, config) write(logid,*) 'Reading flexpart header_nest: '//trim(filename) call read_header(filename, numpoint, ibdate, ibtime, releases, & numx, numy, bndx, bndy, delx, dely, xshift) + ! nested domain variables nnxgrid = numx nnygrid = numy @@ -188,6 +208,25 @@ subroutine initialize(files, config) end do endif + ! some checks for CO2 + ! ------------------- + + if (config%spec.eq.'co2') then + if (ndgrid.lt.6) then + write(logid,*) 'ERROR initialize: footprints in flexpart runs not' + write(logid,*) 'adequately resolved, ndgrid = ',ndgrid + stop + endif + if ( (mod((24/ndgrid),config%nstep_nee_reg).ne.0).and. & + (mod(config%nstep_nee_reg,(24/ndgrid)).ne.0) ) then + write(logid,*) 'ERROR initialize: footprint resolution and NEE prior resolution' + write(logid,*) 'should be an integral multiple of one or the other' + write(logid,*) 'ntstep_nee_reg = ',config%nstep_nee_reg + write(logid,*) 'ndgrid = ',ndgrid + stop + endif + endif + ! nzgrid is z dimension of grid_init files (read from header) ! nzlev is z dimension of grid_time files with surface only nzlev = 1 diff --git a/source/m1qn3_interface.f90 b/source/m1qn3_interface.f90 index 884d458..3fcbb38 100644 --- a/source/m1qn3_interface.f90 +++ b/source/m1qn3_interface.f90 @@ -209,6 +209,10 @@ subroutine m1qn3_interface(iter, grad, files, config, fluxes, obs, states, covar write(logid,*) 'Cost 2Jo iteration '//aiter//' = ',cost_o write(logid,*) 'Cost 2Jp iteration '//aiter//' = ',cost_p write(logid,*) 'Cost iteration '//aiter//': ',cost + if ( isnan(cost) ) then + write(logid,*) 'ERROR congrad: cost is nan' + stop + endif ! save cost in case of a restart open(100,file=trim(files%path_output)//'cost.txt',status='unknown',access='append',action='write',iostat=ierr) write(100,fmt='(F13.4)') cost diff --git a/source/mod_flexpart.f90 b/source/mod_flexpart.f90 index 7438cb1..08780c0 100755 --- a/source/mod_flexpart.f90 +++ b/source/mod_flexpart.f90 @@ -189,7 +189,7 @@ module mod_flexpart real(kind=8), dimension(maxngrid), intent(in out) :: gtime real, parameter :: scaleconc=1.e12 - real, parameter :: smallnum=1.e-38 + real, parameter :: smallnum=tiny(0.) logical :: lexist integer :: ierr integer :: nread @@ -284,6 +284,12 @@ module mod_flexpart ! reverse order in time dimension do nt = 1, ngrid n = ngrid - nt + 1 + ! remove spurious grid cells in footprint + do jy = 1, numy + do ix = 1, numx + if ( work(ix,jy,nt)>huge(0.) ) work(ix,jy,nt) = 0. + end do + end do grid(:,:,n) = work(:,:,nt) gtime(n) = jdtime(nt) end do @@ -366,7 +372,7 @@ module mod_flexpart real, dimension(nxgrid,nygrid,nzgrid), intent(in out) :: gridinit real, parameter :: scaleconc=1.e12 - real, parameter :: smallnum=1.e-38 + real, parameter :: smallnum=tiny(0.) logical :: lexist integer :: ierr integer :: sp_count_i, sp_count_r @@ -443,7 +449,7 @@ module mod_flexpart integer, intent(in) :: numx, numy, xshift real, dimension(numx,numy,nread), intent(in out) :: factor - real, parameter :: smallnum=1.e-38 + real, parameter :: smallnum=tiny(0.) logical :: lexist integer :: ierr integer :: sp_count_i, sp_count_r diff --git a/source/mod_obs.f90 b/source/mod_obs.f90 index feaa559..c4f3f28 100644 --- a/source/mod_obs.f90 +++ b/source/mod_obs.f90 @@ -39,7 +39,7 @@ module mod_obs type :: obs_t real(kind=8), dimension(:), allocatable :: jdate ! julian dates - character(len=max_name_len), dimension(:), allocatable :: recs ! receptor names + character(len=recname_len), dimension(:), allocatable :: recs ! receptor names real, dimension(:), allocatable :: conc ! observed concentrations real(kind=8), dimension(:), allocatable :: avetime ! time over which observation was made or averaged real, dimension(:), allocatable :: ghg ! ghg best guess contribution to mixing ratio @@ -54,6 +54,10 @@ module mod_obs real, dimension(:), allocatable :: cinipos ! posterior inital concentrations real, dimension(:), allocatable :: measerr ! measurement error real, dimension(:), allocatable :: err ! total observation error + real, dimension(:), allocatable :: cpri ! prior total column mixing ratio for satellite + real, dimension(:), allocatable :: cakpri ! prior total column mixing ratio convolved with AK for satellite + real, dimension(:), allocatable :: cdryair ! total column moles of dry air (mol/m2) + real, dimension(:), allocatable :: cakav ! mean of column averaging kernel end type @@ -139,6 +143,26 @@ module mod_obs write(logid,*) 'ERROR alloc_obs: not enough memory' stop endif + allocate( obs%cpri(nobs), stat = ierr ) + if ( ierr.ne.0 ) then + write(logid,*) 'ERROR alloc_obs: not enough memory' + stop + endif + allocate( obs%cakpri(nobs), stat = ierr ) + if ( ierr.ne.0 ) then + write(logid,*) 'ERROR alloc_obs: not enough memory' + stop + endif + allocate( obs%cdryair(nobs), stat = ierr ) + if ( ierr.ne.0 ) then + write(logid,*) 'ERROR alloc_obs: not enough memory' + stop + endif + allocate( obs%cakav(nobs), stat = ierr ) + if ( ierr.ne.0 ) then + write(logid,*) 'ERROR alloc_obs: not enough memory' + stop + endif if ( config%spec.eq.'co2' ) then allocate( obs%nee(nobs), stat = ierr ) @@ -166,6 +190,10 @@ module mod_obs obs%measerr(:) = 0. obs%err(:) = 0. obs%ocn(:) = 0. + obs%cpri(:) = 0. + obs%cakpri(:) = 0. + obs%cdryair(:) = 0. + obs%cakav(:) = 1. if ( config%spec.eq.'co2' ) then obs%nee(:) = 0. @@ -197,6 +225,10 @@ module mod_obs if ( allocated(obs%ocn) ) deallocate(obs%ocn) if ( allocated(obs%nee) ) deallocate(obs%nee) if ( allocated(obs%fff) ) deallocate(obs%fff) + if ( allocated(obs%cpri) ) deallocate(obs%cpri) + if ( allocated(obs%cakpri) ) deallocate(obs%cakpri) + if ( allocated(obs%cdryair) ) deallocate(obs%cdryair) + if ( allocated(obs%cakav) ) deallocate(obs%cakav) end subroutine dealloc_obs @@ -212,11 +244,11 @@ module mod_obs type (files_t), intent(in) :: files type (obs_t), intent(in out) :: obs - character(len=max_path_len) :: header + character(len=max_path_len) :: header, rowfmt integer :: yyyymmdd, hhmiss - character(len=4) :: recs + character(len=recname_len) :: recs real(kind=8) :: jdate, avetime - real :: conc, err + real :: conc, err, cpri, cakpri, cdryair, cakav integer :: ierr integer :: i @@ -226,17 +258,19 @@ module mod_obs ! read all observations open(200,file=trim(files%path_output)//'obsread.txt',action='read',status='old',iostat=ierr) read(200,*) header + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,D14.6,1X,F10.6,1X,F10.4,1X,F10.4,F10.5,1X,F10.5,1X,F10.2,1X,F6.4)' do i = 1, nobs -! read(200,fmt='(A4,1X,I8,1X,I6,1X,D14.7,1X,F10.6,1X,F10.4,1X,F10.4)') & -! recs, yyyymmdd, hhmiss, jdate, avetime, conc, err - read(200,fmt='(A4,1X,I8,1X,I6,1X,D14.6,1X,F10.6,1X,F10.4,1X,F10.4)') & - recs, yyyymmdd, hhmiss, jdate, avetime, conc, err - obs%recs(i) = recs + read(200,fmt=rowfmt) recs, yyyymmdd, hhmiss, jdate, avetime, conc, err, cpri, cakpri, cdryair, cakav + obs%recs(i) = trim(recs) obs%jdate(i) = jdate obs%avetime(i) = avetime obs%conc(i) = conc obs%measerr(i) = err obs%err(i) = err + obs%cpri(i) = cpri + obs%cakpri(i) = cakpri + obs%cdryair(i) = cdryair + obs%cakav(i) = cakav end do close(200) diff --git a/source/mod_save.f90 b/source/mod_save.f90 index 94a61e6..aed5666 100644 --- a/source/mod_save.f90 +++ b/source/mod_save.f90 @@ -76,32 +76,30 @@ module mod_save if ( config%spec.eq.'co2' ) then ! CO2 species - write(100,*) 'rec yyyymmdd hhmmss juldate conc cini cinipos bkg nee fff ocn prior post diff error' + write(100,*) 'rec yyyymmdd hhmmss juldate conc cini cinipos bkg nee fff ocn prior post cpri cakpri diff error' do i = 1, nobs cinipri = sum(obs%cini(i,:)) call caldate(obs%jdate(i), jjjjmmdd, hhmiss) - write(rowfmt,'(A,I6,A)') '(A3,1X,I8,1X,I6,1X,F14.6,1X,',11,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,',13,'(F10.4,1X))' + print*, 'save_obs: ',rowfmt write(100,fmt=rowfmt) obs%recs(i), jjjjmmdd, hhmiss, obs%jdate(i), & obs%conc(i), cinipri, obs%cinipos(i), obs%bkg(i), & - obs%nee(i), obs%fff(i), obs%ocn(i), obs%prior(i), obs%model(i), obs%delta(i), obs%err(i) + obs%nee(i), obs%fff(i), obs%ocn(i), obs%prior(i), & + obs%model(i), obs%cpri(i), obs%cakpri(i), obs%delta(i), obs%err(i) end do else ! GHG species - write(100,*) 'rec yyyymmdd hhmmss juldate conc cini cinipos bkg ghg prior post diff error' - write(rowfmt,'(A,I6,A)') '(A3,1X,I8,1X,I6,1X,F14.6,1X,',9,'(F10.4,1X))' + write(100,*) 'rec yyyymmdd hhmmss juldate conc cini cinipos bkg ghg prior post cpri cakpri diff error' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,',11,'(F10.4,1X))' do i = 1, nobs cinipri = sum(obs%cini(i,:)) call caldate(obs%jdate(i), jjjjmmdd, hhmiss) - if ( config%lognormal.and..not.config%inc_ocean ) then - ! if ocean grid cells not optimized include ocean contribution in prior and posterior model - write(100,fmt=rowfmt) obs%recs(i), jjjjmmdd, hhmiss, obs%jdate(i), & - obs%conc(i), cinipri, obs%cinipos(i), obs%bkg(i), & - obs%ghg(i), obs%prior(i)+obs%ocn(i), obs%model(i)+obs%ocn(i), obs%delta(i), obs%err(i) - else - write(100,fmt=rowfmt) obs%recs(i), jjjjmmdd, hhmiss, obs%jdate(i), & - obs%conc(i), cinipri, obs%cinipos(i), obs%bkg(i), & - obs%ghg(i), obs%prior(i), obs%model(i), obs%delta(i), obs%err(i) - endif + ! if log-normal and ocean grid cells not optimized include ocean contribution in prior and posterior model + ! in other case obs%ocn is zero + write(100,fmt=rowfmt) obs%recs(i), jjjjmmdd, hhmiss, obs%jdate(i), & + obs%conc(i), cinipri, obs%cinipos(i), obs%bkg(i), & + obs%ghg(i), obs%prior(i)+obs%ocn(i), obs%model(i)+obs%ocn(i), & + obs%cpri(i), obs%cakpri(i), obs%delta(i), obs%err(i) end do endif @@ -138,7 +136,7 @@ module mod_save call save_ghg(files, config, fluxes, states) endif if ( config%opt_cini ) then - call save_scalar_cini(files, states) + call save_scalar_cini(files, config, states) endif end subroutine save_state @@ -299,10 +297,16 @@ module mod_save do jy = 1, nyregrid ! average to state vector timestamp datain = fluxes%flxnee_nest(:,jy,:) - call average(nxregrid, nday*ndgrid, fluxes%timefp, datain, ntstate, states%xtime, dataout) + if ((nday*ndgrid).gt.1.and.ntstate.gt.1) then + call average(nxregrid, nday*ndgrid, fluxes%timefp, datain, ntstate, states%xtime, dataout) + else if ((nday*ndgrid).gt.1) then + n = minloc(fluxes%timefp,dim=1,mask=abs(fluxes%timefp-states%xtime(1)).eq.minval(abs(fluxes%timefp-states%xtime(1)))) + dataout(:,ntstate) = datain(:,n) + endif fpri(:,jy,:) = fpri(:,jy,:) + dataout(:,:)/numscale fpos(:,jy,:) = fpos(:,jy,:) + dataout(:,:)/numscale end do + print*, 'completed NEE' deallocate( datain ) deallocate( dataout ) @@ -328,12 +332,18 @@ module mod_save do jy = 1, nyregrid ! average to state vector timestamp datain = fluxes%flxff_nest(:,jy,:) - call average(nxregrid, nday*ndgrid, fluxes%timefp, datain, ntstate, states%xtime, dataout) + if ((nday*ndgrid).gt.1.and.ntstate.gt.1) then + call average(nxregrid, nday*ndgrid, fluxes%timefp, datain, ntstate, states%xtime, dataout) + else if ((nday*ndgrid).gt.1) then + n = minloc(fluxes%timefp,dim=1,mask=abs(fluxes%timefp-states%xtime(1)).eq.minval(abs(fluxes%timefp-states%xtime(1)))) + dataout(:,ntstate) = datain(:,n) + endif fff_reg(:,jy,:) = dataout(:,:)/numscale end do + print*, 'completed fossil fuel' deallocate( datain ) deallocate( dataout ) - + ! write ncdf file ! --------------- @@ -793,24 +803,30 @@ module mod_save !! ! -------------------------------------------------- - subroutine save_scalar_cini(files, states) + subroutine save_scalar_cini(files, config, states) use mod_settings use mod_states use mod_var type (files_t), intent(in) :: files + type (config_t), intent(in) :: config type (states_t), intent(in) :: states character(len=max_path_len) :: rowfmt integer :: n, ierr ! write to file - ! for lognormal these are the log transformed scalars open(100,file=trim(files%path_output)//'scalars_cini.txt',status='replace',action='write',iostat=ierr) write(rowfmt,'(A,I6,A)') '(',4,'(E11.4,1X))' write(100,*) 'prior, pri_err, post, pos_err' do n = 1, ntcini*ncini - write(100,rowfmt) states%px0(npvar+n), states%pxerr0(npvar+n), states%px(npvar+n), states%pxerr(npvar+n) + if (config%lognormal) then + ! for lognormal these are the log transformed scalars + ! so undo the log transformation here + write(100,rowfmt) exp(states%px0(npvar+n)), exp(states%pxerr0(npvar+n)), exp(states%px(npvar+n)), exp(states%pxerr(npvar+n)) + else + write(100,rowfmt) states%px0(npvar+n), states%pxerr0(npvar+n), states%px(npvar+n), states%pxerr(npvar+n) + endif end do close(100) @@ -839,7 +855,7 @@ module mod_save ! write to file open(100,file=trim(files%path_output)//'obsdatatype.txt',status='replace',action='write',iostat=ierr) - write(rowfmt,'(A,I6,A)') '(A3,1X,F14.6,1X,F10.4,1X,',ncini,'(F10.4,1X))' + write(rowfmt,'(A,I1,A,I6,A)') '(A',recname_len,',1X,F14.6,1X,F10.4,1X,',ncini,'(F10.4,1X))' write(100,*) 'recs jdate conc cini' do i = 1, nobs write(100,rowfmt) obs%recs(i), obs%jdate(i), obs%conc(i), obs%cini(i,:) diff --git a/source/mod_settings.f90 b/source/mod_settings.f90 index 2df1248..7f847fd 100644 --- a/source/mod_settings.f90 +++ b/source/mod_settings.f90 @@ -38,10 +38,13 @@ module mod_settings type :: files_t character(len=max_path_len) :: path_obs ! path to observations + character(len=max_path_len) :: path_satobs ! path to satellite observations character(len=max_path_len) :: path_prior ! path to prior fluxes character(len=max_path_len) :: path_output ! path to output - character(len=max_path_len) :: path_flexpart ! path to flexpart output + character(len=max_path_len) :: path_flexpart ! path to flexpart output for ground-based obs + character(len=max_path_len) :: path_flexsat ! path to flexpart output for satellite obs character(len=max_path_len) :: path_flexncdf ! path to flexpart netcdf output (from previous run) +! character(len=max_path_len) :: path_satrel ! path to satellite releases info character(len=max_path_len) :: file_bg ! prior best guess file character(len=max_name_len) :: suffix ! observation file suffix character(len=max_name_len) :: file_log ! log file name @@ -127,6 +130,9 @@ module mod_settings integer :: restart ! restart a run that crashed (0=false, 1=true) real :: molarmass ! molar mass of species (corresponds to flux files, e.g. C=12, CH4=16) real :: coeff ! conversion coefficient from ppt to ppm or ppbv + logical :: satellite ! use satellite observations (true or false) + logical :: ground ! use ground-based observations (true or false) + real :: coeffsat ! conversion coefficient for satellite data to ppm or ppbv logical :: nested ! use nested flexpart output (true or false) real :: w_edge_lon ! lon of western edge of inversion grid real :: s_edge_lat ! lat of southern edge of inversion grid @@ -378,15 +384,24 @@ module mod_settings identifier = "path_obs:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_obs = cc + identifier = "path_satobs:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) files%path_satobs = cc identifier = "path_prior:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_prior = cc identifier = "path_flexpart:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_flexpart = cc + identifier = "path_flexsat:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) files%path_flexsat = cc identifier = "path_flexncdf:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_flexncdf = cc +! identifier = "path_satrel:" +! call read_content (line, identifier, cc, cn, cl, match) +! if ( match ) files%path_satrel = cc identifier = "path_output:" call read_content (line, identifier, cc, cn, cl, match) if ( match ) files%path_output = cc @@ -625,6 +640,8 @@ module mod_settings config%spa_corr = .false. config%verbose = .false. config%lognormal = .false. + config%satellite = .false. + config%ground = .false. config%trunc = 1.e-3 ! open file @@ -720,6 +737,17 @@ module mod_settings call read_content (line, identifier, cc, cn, cl, match) if ( match ) config%coeff = real(cn,kind=4) + ! read observation settings + identifier = "satellite:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%satellite = cl + identifier = "ground:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%ground = cl + identifier = "coeffsat:" + call read_content (line, identifier, cc, cn, cl, match) + if ( match ) config%coeffsat = real(cn,kind=4) + ! read inversion domain settings identifier = "nested:" call read_content (line, identifier, cc, cn, cl, match) @@ -893,6 +921,10 @@ module mod_settings print*, 'WARNING read_config_settings: for CO2 ocean fluxes are not optimized inc_ocean -> false' config%inc_ocean = .false. endif + if ( .not.config%ground.and..not.config%satellite ) then + print*, 'ERROR: at least one of satellite or ground must be true' + stop + endif end subroutine read_config_settings diff --git a/source/mod_strings.f90 b/source/mod_strings.f90 index ed4d3a7..826b3de 100644 --- a/source/mod_strings.f90 +++ b/source/mod_strings.f90 @@ -111,16 +111,25 @@ module mod_strings character(*), intent(in) :: repstr character(max_path_len) :: string_out character(max_path_len) :: strtmp1, strtmp2 - integer :: strlen, replen, n + integer :: strlen, substrlen, replen, n strlen = len_trim(string_in) replen = len_trim(repstr) + substrlen = len_trim(substr) ! find first position of substr in string_in n = index( string_in, substr, back=.false. ) - strtmp1 = string_in(1:n-1) + if ((n-1).gt.0) then + strtmp1 = string_in(1:n-1) + else + ! empty string + strtmp1 = '' + n = 1 + endif +! print*, 'str_replace first pos n, strtmp1 = ',n, strtmp1 ! find last position of substr in string_in n = index( string_in, substr, back=.true. ) - strtmp2 = string_in(n+replen:strlen) + strtmp2 = string_in(n+substrlen:strlen) +! print*, 'str_replace last pos n, strtmp2 = ',n, strtmp2 string_out = trim(strtmp1)//trim(repstr)//trim(strtmp2) end function str_replace diff --git a/source/read_obs.f90 b/source/read_obs.f90 index 28c5cf0..a237704 100755 --- a/source/read_obs.f90 +++ b/source/read_obs.f90 @@ -25,32 +25,52 @@ subroutine read_obs(config, files) + use netcdf use mod_settings use mod_var use mod_dates use mod_strings + use mod_save, only : check implicit none type (config_t), intent(in) :: config type (files_t), intent(in) :: files character(len=max_path_len), dimension(:), allocatable :: filelist - character(len=4) :: recs - character(len=20) :: species, before, sep + character(len=max_path_len) :: rowfmt, numfmt, filerelease + character(len=recname_len) :: recs + character(len=50) :: species, before, sep character(len=200) :: header, string + character(len=8) :: adate integer :: ierr integer :: cnt - integer :: nr, nf, nfiles + integer :: i, nr, nf, nfiles, strlen integer :: yyyymmdd, hhmiss, hl, hh real(kind=8) :: jdate, avetime real :: conc, err, measerr, lon, alt - integer :: num, reclen + integer :: num, reclen, nretr, nrelease, nlayer + integer :: ncid, varid, dimid + integer, dimension(:), allocatable :: idate, itime + real, dimension(:), allocatable :: cpri, cakpri, cdryair, vmr, vmr_err +! real, dimension(:,:), allocatable :: cak + real :: xcpri, xcakpri, scl_cpri, scl_cakpri, scl_cdryair !, cak_av + logical :: lexist ! list observation files ! ---------------------- - call system('ls '//trim(files%path_obs)//'*'//trim(files%suffix)//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') - call system('ls '//trim(files%path_obs)//' | grep '//trim(files%suffix)//' >> '//trim(files%path_output)//'obsfiles.txt') + if ( config%ground.and.config%satellite ) then + call system('ls '//trim(files%path_obs)//'*'//trim(files%suffix)//& + ' '//trim(files%path_satobs)//'*.nc'//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_obs)//' | grep '//trim(files%suffix)//' >> '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_satobs)//' | grep .nc >> '//trim(files%path_output)//'obsfiles.txt') + else if ( config%ground ) then + call system('ls '//trim(files%path_obs)//'*'//trim(files%suffix)//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_obs)//' | grep '//trim(files%suffix)//' >> '//trim(files%path_output)//'obsfiles.txt') + else if ( config%satellite ) then + call system('ls '//trim(files%path_satobs)//'*.nc'//' | wc -l > '//trim(files%path_output)//'obsfiles.txt') + call system('ls '//trim(files%path_satobs)//' | grep .nc >> '//trim(files%path_output)//'obsfiles.txt') + endif open(100,file=trim(files%path_output)//'obsfiles.txt',action='read',status='old',iostat=ierr) if ( ierr.ne. 0 ) then @@ -74,94 +94,195 @@ subroutine read_obs(config, files) write(logid,*) 'ERROR: cannot open obsread.txt' stop endif - write(200,*) 'rec yyyymmdd hhmmss juldate avetime conc error' +! write(200,*) 'rec yyyymmdd hhmmss juldate avetime conc error cpri cakpri cdryair cakav' + write(200,*) 'rec yyyymmdd hhmmss juldate avetime conc error cpri cakpri cdryair' cnt = 0 do nf = 1, nfiles - ! check this file belongs to receptor in reclist + ! check if ground-based or satellite observation string = filelist(nf) call split(string,".",before,sep) - call split(before,"_",recs,sep) - species = trim(before) - reclen = len_trim(recs) - - if ( .not.( any(recname(:)(1:reclen).eq.to_upper(recs)) ) ) go to 10 - - ! open input file - open(100,file=trim(files%path_obs)//trim(filelist(nf)),action='read',status='old',iostat=ierr) - if ( ierr.ne.0 ) then - write(logid,*) 'WARNING: cannot open: '//trim(files%path_obs)//trim(filelist(nf)) - go to 10 - endif - write(logid,*) 'Reading file: '//trim(files%path_obs)//trim(filelist(nf)) - - ! read header - read (100, fmt='(A)', iostat=ierr) header - - ! station coordinates for data selection - do nr = 1, nrec - if ( to_upper(recs).eq.recname(nr) ) then - lon = reclon(nr) - alt = recalt(nr) - go to 20 - endif - end do -20 continue - if ( lon.eq.0.and.alt.eq.0 ) then - write(logid,*) 'ERROR: read_obs: lon and alt are both zero -> check recepter list file' - stop - endif - - ! read data - read_loop: do - ! if avetime included in obs file - if (reclen.eq.4) then - read(100,fmt='(A4,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & - recs, yyyymmdd, hhmiss, jdate, avetime, conc, err, num - elseif (reclen.eq.3) then - read(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & - recs, yyyymmdd, hhmiss, jdate, avetime, conc, err, num - endif - ! if avetime not included (backwards compatability with observations prepared before update) -! avetime = 0d0 -! if (reclen.eq.4) then -! read(100,fmt='(A4,1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & -! recs, yyyymmdd, hhmiss, jdate, conc, err, num -! elseif (reclen.eq.3) then -! read(100,fmt='(A3,1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)',iostat=ierr) & -! recs, yyyymmdd, hhmiss, jdate, conc, err, num + + if ( string.eq.'nc' ) then + + ! satellite + ! --------- + + ! check if data is in inversion time interval + read(before(19:26),*) yyyymmdd + print*, 'readobs: string before = ',before(19:26) + jdate = juldate(yyyymmdd, 0) + if ( jdate.lt.juldatei.or.jdate.gt.juldatef ) cycle + + ! read ncdf file + call check( nf90_open(trim(files%path_satobs)//trim(filelist(nf)),nf90_NOWRITE,ncid) ) + call check( nf90_inq_dimid(ncid,'retrieval',dimid) ) + call check( nf90_inquire_dimension(ncid,dimid,len=nretr) ) + allocate( idate(nretr), stat=ierr ) + allocate( itime(nretr), stat=ierr ) + allocate( cpri(nretr), stat=ierr ) + allocate( cakpri(nretr), stat=ierr ) + allocate( cdryair(nretr), stat=ierr ) + allocate( vmr(nretr), stat=ierr ) + allocate( vmr_err(nretr), stat=ierr ) + call check( nf90_inq_varid(ncid,'idate',varid) ) + call check( nf90_get_var(ncid,varid,idate) ) + call check( nf90_inq_varid(ncid,'itime',varid) ) + call check( nf90_get_var(ncid,varid,itime) ) + call check( nf90_inq_varid(ncid,'cpri',varid) ) + call check( nf90_get_var(ncid,varid,cpri) ) + call check( nf90_inq_varid(ncid,'cakpri',varid) ) + call check( nf90_get_var(ncid,varid,cakpri) ) + call check( nf90_inq_varid(ncid,'cdryair',varid) ) + call check( nf90_get_var(ncid,varid,cdryair) ) + call check( nf90_inq_varid(ncid,'vmr',varid) ) + call check( nf90_get_var(ncid,varid,vmr) ) + call check( nf90_inq_varid(ncid,'vmr_err',varid) ) + call check( nf90_get_var(ncid,varid,vmr_err) ) + call check( nf90_close(ncid) ) + + ! read ncdf file of releases (for AK info) + ! this is not needed +! write(adate,'(I8)') yyyymmdd +! print*, trim(filelist(nf)) +! filerelease=str_replace(trim(filelist(nf)),'retrieval','releases') +! inquire(file=trim(files%path_satrel)//adate//'/options/'//trim(filerelease),exist=lexist) +! if (.not.lexist) then +! write(logid,*) 'ERROR: file not found ',trim(files%path_satrel)//adate//'/options/'//trim(filerelease) +! stop ! endif - if ( ierr.ne.0 ) exit read_loop - if ( jdate.ge.(juldatef+1d0) ) exit read_loop - if ( jdate.lt.juldatei ) cycle read_loop - ! select day/night for low/high alt sites - hh = hhmiss/10000 - hl = hh + int(lon*24./360.) - if ( hl.lt.0 ) then - hl = hl + 24 - else if ( hl.ge.24 ) then - hl = hl - 24 - endif - if ( alt.le.1000. ) then -! if ( (hl.lt.11).or.(hl.gt.15) ) cycle read_loop - else -! if ( (hl.gt.3).and.(hl.lt.23) ) cycle read_loop +! call check( nf90_open(trim(files%path_satrel)//adate//'/options/'//trim(filerelease),nf90_NOWRITE,ncid) ) +! call check( nf90_inq_dimid(ncid,'retrieval',dimid) ) +! call check( nf90_inquire_dimension(ncid,dimid,len=nrelease) ) +! call check( nf90_inq_dimid(ncid,'nlayer',dimid) ) +! call check( nf90_inquire_dimension(ncid,dimid,len=nlayer) ) +! if (nrelease.ne.nretr) then +! write(logid,*) 'ERROR: number of releases does not much number of retrievals' +! stop +! endif +! allocate( cak(nretr,nlayer), stat=ierr ) +! call check( nf90_inq_varid(ncid,'cak',varid) ) +! call check( nf90_get_var(ncid,varid,cak) ) +! call check( nf90_close(ncid) ) + + ! loop over retrievals + avetime = 0d0 + write(numfmt,fmt='(A,I1,A,I1,A)') '(I',recname_len,'.',recname_len,')' +! write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,F10.5,1X,F10.5,1X,F10.2,1X,F6.4)' + write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,F10.5,1X,F10.5,1X,F10.2)' + do nr = 1, nretr + cnt = cnt + 1 + write(recs,fmt=numfmt) nr + ! convert from input unit (e.g. mol/m2) to mole fraction + xcpri = cpri(nr)*config%coeffsat/cdryair(nr) + xcakpri = cakpri(nr)*config%coeffsat/cdryair(nr) +! cak_av = sum(cak(nr,:))/real(nlayer) + print*, 'read_obs: xcpri, xcakpri = ',xcpri, xcakpri + print*, 'read_obs: cpri, cdryair = ',cpri(nr), cdryair(nr) + print*, 'read_obs: coeffsat = ',config%coeffsat + ! julian time + jdate = juldate(idate(nr), itime(nr)) + write(200,fmt=rowfmt) recs, idate(nr), itime(nr), jdate, avetime, vmr(nr), vmr_err(nr), & + xcpri, xcakpri, cdryair(nr) !, cak_av + end do + +! deallocate( idate, itime, cpri, cakpri, cdryair, vmr, vmr_err, cak ) + deallocate( idate, itime, cpri, cakpri, cdryair, vmr, vmr_err ) + + else + + ! check if this file is one to read + strlen = len_trim(files%suffix) + string = filelist(nf) + do i = 1, len_trim(string) + if ( string(i:(i+strlen)).eq.trim(files%suffix) ) then + lexist = .true. + endif + end do + if ( .not.lexist ) cycle + + ! ground-based + ! ------------ + + ! check this file belongs to receptor in reclist + call split(before,"_",recs,sep) + species = trim(before) + reclen = len_trim(recs) + + if ( .not.( any(recname(:)(1:reclen).eq.to_upper(recs)) ) ) go to 10 + + ! open input file + open(100,file=trim(files%path_obs)//trim(filelist(nf)),action='read',status='old',iostat=ierr) + if ( ierr.ne.0 ) then + write(logid,*) 'WARNING: cannot open: '//trim(files%path_obs)//trim(filelist(nf)) + go to 10 + endif + write(logid,*) 'Reading file: '//trim(files%path_obs)//trim(filelist(nf)) + + ! read header + read (100, fmt='(A)', iostat=ierr) header + + ! station coordinates for data selection + do nr = 1, nrec + if ( to_upper(recs).eq.recname(nr) ) then + lon = reclon(nr) + alt = recalt(nr) + go to 20 + endif + end do +20 continue + if ( lon.eq.0.and.alt.eq.0 ) then + write(logid,*) 'ERROR: read_obs: lon and alt are both zero -> check recepter list file' + stop endif - if ( conc.le.-999. ) cycle read_loop - measerr = max(config%measerr, err) - cnt = cnt + 1 - ! write to output file - write(200,fmt='(A4,1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4)') & - recs, yyyymmdd, hhmiss, jdate, avetime, conc, measerr - end do read_loop - ! close input file - close(100) + ! read data + read_loop: do + ! if avetime included in obs file + write(rowfmt,'(A,I1,A)') '(A',reclen,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,1X,I4)' + read(100,fmt=rowfmt,iostat=ierr) recs, yyyymmdd, hhmiss, jdate, avetime, conc, err, num + ! if avetime not included (backwards compatability with observations prepared before update) +! avetime = 0d0 +! write(rowfmt,'(A,I1,A)') '(A',reclen,',1X,I8,1X,I6,1X,F14.6,1X,F10.4,1X,F10.4,1X,I4)' +! read(100,fmt=rowfmt,iostat=ierr) recs, yyyymmdd, hhmiss, jdate, conc, err, num + if ( ierr.ne.0 ) exit read_loop + if ( jdate.ge.(juldatef+1d0) ) exit read_loop + if ( jdate.lt.juldatei ) cycle read_loop + ! select day/night for low/high alt sites + hh = hhmiss/10000 + hl = hh + int(lon*24./360.) + if ( hl.lt.0 ) then + hl = hl + 24 + else if ( hl.ge.24 ) then + hl = hl - 24 + endif + if ( alt.le.1000. ) then + if ( (hl.lt.11).or.(hl.gt.15) ) cycle read_loop + else + if ( (hl.gt.3).and.(hl.lt.23) ) cycle read_loop + endif + if ( conc.le.-999. ) cycle read_loop + measerr = max(config%measerr, err) + scl_cpri = 0. + scl_cakpri = 0. + scl_cdryair = 0. +! cak_av = 1. + cnt = cnt + 1 + ! write to output file +! write(rowfmt,'(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,F10.5,1X,F10.5,1X,F10.2,1X,F6.4)' + write(rowfmt,'(A,I1,A)') '(A',recname_len,',1X,I8,1X,I6,1X,F14.6,1X,F10.6,1X,F10.4,1X,F10.4,F10.5,1X,F10.5,1X,F10.2)' + write(200,fmt=rowfmt) recs, yyyymmdd, hhmiss, jdate, avetime, conc, measerr, scl_cpri, scl_cakpri, scl_cdryair !, cak_av + end do read_loop + + ! close input file + close(100) 10 continue - end do + endif ! satellite of ground-based + + end do ! loop over files ! close output file close(200) diff --git a/source/read_reclist.f90 b/source/read_reclist.f90 index 17c3d27..f9490b3 100644 --- a/source/read_reclist.f90 +++ b/source/read_reclist.f90 @@ -23,7 +23,7 @@ !! !--------------------------------------------------------------------------------------- -subroutine read_reclist(filename) +subroutine read_reclist(config, filename) use mod_var use mod_settings @@ -31,7 +31,9 @@ subroutine read_reclist(filename) implicit none + type (config_t), intent(in) :: config character(len=max_path_len), intent(in) :: filename + character(len=max_path_len) :: rowfmt character(len=200) :: line character(len=10) :: foo, sep integer :: ierr @@ -39,10 +41,14 @@ subroutine read_reclist(filename) ! count number of receptors + nrec = 0 open(100,file=trim(filename),action='read',status='old',iostat=ierr) - if(ierr.gt.0) then + if( ierr.gt.0.and..not.config%satellite ) then write(logid,*) 'ERROR: cannot open: '//trim(filename) stop + else if ( ierr.gt.0.and.config%satellite ) then + write(logid,*) 'WARNING: no ground-based observations found' + go to 20 endif write(logid,*) 'Reading receptors file: '//trim(filename) @@ -69,18 +75,17 @@ subroutine read_reclist(filename) open(100,file=trim(filename),action='read',status='old',iostat=ierr) write(logid,*) 'Receptors: ' +! write(rowfmt,fmt='(A,I1,A)') '(A',recname_len,',1X,F7.2,1X,F7.2,1X,F7.2)' + write(rowfmt,fmt='(A,I1,A)') '(A',3,',1X,F7.2,1X,F7.2,1X,F7.2)' do cnt = 1, nrec - if ( reclen.eq.4 ) then - read(100,fmt='(A4,1X,F6.2,1X,F6.2,1X,F7.2)') recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) - write(logid,*) recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) - else if ( reclen.eq.3 ) then - read(100,fmt='(A3,1X,F6.2,1X,F6.2,1X,F7.2)') recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) - endif + read(100,fmt=rowfmt) recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) write(logid,*) recname(cnt), reclat(cnt), reclon(cnt), recalt(cnt) end do close(100) +20 continue + end subroutine read_reclist diff --git a/source/sbatch_flexinvert.sh b/source/sbatch_flexinvert.sh index c29c2f7..b82c3b9 100755 --- a/source/sbatch_flexinvert.sh +++ b/source/sbatch_flexinvert.sh @@ -1,9 +1,9 @@ #!/bin/bash #--------------------------------------------------- partition=nilu -jobname=testco2 -settings_files='/home/rthompson/REPOS/GITHUB/FLEXINVERTplus_clean/settings/SETTINGS_co2_files' -settings_config='/home/rthompson/REPOS/GITHUB/FLEXINVERTplus_clean/settings/SETTINGS_co2_config' +jobname=ghg +settings_files='/mypath/settings/SETTINGS_ghg_files' +settings_config='/mypath/settings/SETTINGS_ghg_config' #--------------------------------------------------- cat <<EOF > run_job.sh diff --git a/source/simulate.f90 b/source/simulate.f90 index d55f4c6..be349b7 100644 --- a/source/simulate.f90 +++ b/source/simulate.f90 @@ -75,6 +75,7 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) character(len=max_path_len) :: filedates, filefactor character(len=max_path_len) :: rowfmt character(len=6) :: adate + character(len=recname_len) :: anretr character(len=2) :: aiter character(len=8) :: areldate, areltime logical :: lexist @@ -87,13 +88,14 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) integer :: ind, ihi, ilo, flag, ntstep integer :: countrate integer :: tloop1, tloop2, time1, time2 - integer :: nread, ngrid + integer :: nread, ngrid, nretr integer, dimension(:), allocatable :: istate, istateuni ! indices to state vector real, dimension(:), allocatable :: rstateuni ! indices to state vector real, dimension(nxgrid,nygrid,maxngrid) :: grid ! flexpart flux sensitivity (or footprints) real, dimension(nnxgrid,nnygrid,maxngrid):: gridnest ! flexpart nest flux sensitivity (or footprints) real, dimension(:,:,:), allocatable :: factor ! correction factor for dry air + real, dimension(:,:,:), allocatable :: factor_nest ! correction factor for dry air for regional domain real(kind=8), dimension(maxngrid) :: gtime ! flux sensitivity time stamp (julian days) real(kind=8), dimension(:), allocatable :: ftime ! correction factor time stamp (julian days) real(kind=8), dimension(:), allocatable :: dates ! correction factor time stamp (jjjjmmddhhmiss) @@ -102,7 +104,7 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) real, dimension(nxgrid,nygrid) :: hbkg ! flux sensitivity for global minus regional domain real, dimension(:), allocatable :: px ! state variables corresponding to back trajectory time real, dimension(:), allocatable :: hx ! transport operator at state vector resolution -! real, dimension(nxregrid*nyregrid) :: gridnestvec ! vectorized form of grid or gridnest for one timestep + logical :: lsatellite ! logical indicating if observation is from satellite ! initialize ! ---------- @@ -142,32 +144,61 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) write(areldate,'(I8.8)') jjjjmmdd write(areltime,'(I6.6)') hhmiss + ! check if observation is a satellite retrieval + read(obs%recs(i),*,iostat=ierr) nretr + if ( ierr.eq.0 ) then + lsatellite = .true. + else + lsatellite = .false. + endif + print*, 'simulate: lsatellite = ',lsatellite + ! read correction factor for dry dir (ratio rho_wet/rho_dry) -! if ( month.ne.prevmonth ) then -! if ( allocated(dates) ) deallocate(dates) -! if ( allocated(ftime) ) deallocate(ftime) -! if ( allocated(factor) ) deallocate(factor) -! call get_nread(filedates, nread) -! allocate ( dates(nread) ) -! allocate ( ftime(nread) ) -! allocate ( factor(nxgrid,nygrid,nread) ) -! call read_dates(filedates, nread, dates) -! do n = 1, nread -! ftime(n) = juldate(int(dates(n)/1d6),int(dates(n)-floor(dates(n)/1d6)*1d6)) -! end do -! factor(:,:,:) = 1. -! filefactor = trim(path_flexrec)//'factor_drygrid' -! inquire ( file=trim(filefactor),exist=lexist ) -! if ( lexist ) then -! write(logid,*) 'Reading correction factor :'//trim(filefactor) -! call read_factor(filefactor, nread, nxgrid, nygrid, nxshift, factor) -! endif -! endif - ! for nested fp run: assume dry air correction is negligible + if ( month.ne.prevmonth ) then + if ( lsatellite ) then + path_flexrec = trim(files%path_flexsat)//trim(areldate)//'/' + else + path_flexrec = trim(files%path_flexpart)//trim(obs%recs(i))//'/'//trim(adate)//'/' + endif + filedates = trim(path_flexrec)//'dates' + if ( allocated(dates) ) deallocate(dates) + if ( allocated(ftime) ) deallocate(ftime) + if ( allocated(factor) ) deallocate(factor) + call get_nread(filedates, nread) + allocate ( dates(nread) ) + allocate ( ftime(nread) ) + allocate ( factor(nxgrid,nygrid,nread) ) + call read_dates(filedates, nread, dates) + do n = 1, nread + ftime(n) = juldate(int(dates(n)/1d6),int(dates(n)-floor(dates(n)/1d6)*1d6)) + end do + factor(:,:,:) = 1. + filefactor = trim(path_flexrec)//'factor_drygrid' + inquire ( file=trim(filefactor),exist=lexist ) + if ( lexist ) then + write(logid,*) 'Reading correction factor :'//trim(filefactor) + call read_factor(filefactor, nread, nxgrid, nygrid, nxshift, factor) + endif + endif ! read flexpart footprints call system_clock(time1, countrate) - if ( .not.config%average_fp ) then + if ( lsatellite ) then + ! footprint for satellite retrieval + write(anretr,'(I6.6)') nretr + path_flexrec = trim(files%path_flexsat)//trim(areldate)//'/' + filename = trim(path_flexrec)//'grid_time_'//trim(areldate)//'_'//trim(anretr) + filedates = trim(path_flexrec)//'dates' + inquire ( file=trim(filename),exist=lexist ) + if ( lexist ) then + write(logid,*) 'Reading flexpart :'//trim(filename) + ! read footprints from binary files + call read_grid(filename, filedates, obs%jdate(i), nxgrid, nygrid, nxshift, ngrid, grid, gtime) + else + write(logid,*) 'WARNING: cannot find '//trim(filename) + go to 10 + endif + else if ( .not.config%average_fp ) then ! footprint times match observation timestamps path_flexrec = trim(files%path_flexpart)//trim(obs%recs(i))//'/'//trim(adate)//'/' filename = trim(path_flexrec)//'grid_time_'//trim(areldate)//trim(areltime)//'_001' @@ -200,20 +231,49 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) if (i.lt.10.and.iter.eq.0) write(logid,*) 'simulate: time to read grid (ms) = ',(time2-time1)*1000/countrate ! correction for dry air (only if obs are mixing ratios) -! do n = 1, ngrid -! ind = minloc(ftime,dim=1,mask=abs(ftime-gtime(n)).eq.minval(abs(ftime-gtime(n)))) -! grid(:,:,n) = grid(:,:,n)*factor(:,:,ind) -! end do - ! convert from ppt to ppmv or ppbv - grid = grid*config%coeff*mmair/config%molarmass + do n = 1, ngrid + ind = minloc(ftime,dim=1,mask=abs(ftime-gtime(n)).eq.minval(abs(ftime-gtime(n)))) + grid(:,:,n) = grid(:,:,n)*factor(:,:,ind) + end do ! convert s.m3/kg to s.m2/kg grid = grid/outheight(1) + ! convert from equivalent ppt to observation units (e.g. ppmv) + grid = grid*config%coeff*mmair/config%molarmass ! apply numerical scaling grid = grid/numscale +! if ( lsatellite ) then + ! convert from column SRR units to that consistent with mole fractions + ! NO LONGER NEEDED WITH NEW FP CODE +! grid = grid/obs%cdryair(i) +! endif - ! read nested flexpart footprints + ! read nested flexpart output if ( config%nested ) then - if ( .not.config%average_fp ) then + ! read correction factor for dry dir (ratio rho_wet/rho_dry) + if ( month.ne.prevmonth ) then + if ( allocated(factor_nest) ) deallocate(factor_nest) + allocate ( factor_nest(nnxgrid,nnygrid,nread) ) + factor(:,:,:) = 1. + filefactor = trim(path_flexrec)//'factor_drygrid_nest' + inquire ( file=trim(filefactor),exist=lexist ) + if ( lexist ) then + write(logid,*) 'Reading correction factor for nest :'//trim(filefactor) + call read_factor(filefactor, nread, nnxgrid, nnygrid, nnxshift, factor_nest) + endif + endif + if ( lsatellite ) then + ! footprint for satellite retrieval + filenest = trim(path_flexrec)//'grid_time_nest_'//trim(areldate)//'_'//trim(anretr) + inquire ( file=trim(filenest),exist=lexist ) + if ( lexist ) then + write(logid,*) 'Reading flexpart :'//trim(filenest) + ! read footprints from binary files + call read_grid(filenest, filedates, obs%jdate(i), nnxgrid, nnygrid, nnxshift, ngrid, gridnest, gtime) + else + write(logid,*) 'WARNING: cannot find '//trim(filenest) + go to 10 + endif + else if ( .not.config%average_fp ) then ! footprint times match observation timestamps filenest = trim(path_flexrec)//'grid_time_nest_'//trim(areldate)//trim(areltime)//'_001' inquire ( file=trim(filenest),exist=lexist ) @@ -238,12 +298,22 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) go to 10 endif endif - ! convert from ppt to ppmv or ppbv - gridnest = gridnest*config%coeff*mmair/config%molarmass + ! correction for dry air (only if obs are mixing ratios) + do n = 1, ngrid + ind = minloc(ftime,dim=1,mask=abs(ftime-gtime(n)).eq.minval(abs(ftime-gtime(n)))) + gridnest(:,:,n) = gridnest(:,:,n)*factor_nest(:,:,ind) + end do ! convert s.m3/kg to s.m2/kg gridnest = gridnest/outheight(1) + ! convert from equivalent ppt to observation units (e.g. ppmv) + gridnest = gridnest*config%coeff*mmair/config%molarmass ! apply numerical scaling gridnest = gridnest/numscale +! if ( lsatellite ) then + ! convert column SRR units to that consistent with mole fractions + ! NO LONG NEEDED WITH NEW FP CODE +! gridnest = gridnest/obs%cdryair(i) +! endif endif ! calculate indices to state vector for footprint times @@ -442,7 +512,7 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) ! model mixing ratios ! ------------------- - if ( iter.eq.0.or.(config%restart.eq.1.and.iter.eq.(lastiter+1)) ) then + if ( iter.eq.0.or.(config%restart.eq.1.and.((iter.eq.(lastiter+1)).or.(iter.eq.99))) ) then call system_clock(time1, countrate) ! compute fixed mixing ratios call calc_conc(config, fluxes, obs, ngrid, gtime, hnest, hbkg, i, ix1, ix2, jy1, jy2) @@ -490,16 +560,19 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) ! model-observation difference ! ydel = Hf(p) + Hf_fix - yobs + yini + ybkg if (config%spec.eq.'co2') then - obs%delta(i) = obs%model(i) + obs%nee(i) + obs%fff(i) + obs%ocn(i) - & - obs%conc(i) + obs%bkg(i) + obs%cinipos(i) + obs%delta(i) = obs%cpri(i) - obs%cakpri(i) + & + obs%model(i) + obs%nee(i) + obs%fff(i) + obs%ocn(i) + & + obs%bkg(i) + obs%cinipos(i) - obs%conc(i) else if ( config%lognormal ) then ! optimize scalar of fluxes so no contribution from best guess fluxes (ghg) ! but add ocn contribution (zero if ocean pixels optimized) - obs%delta(i) = obs%model(i) - obs%conc(i) + obs%bkg(i) + obs%cinipos(i) + obs%ocn(i) + obs%delta(i) = obs%cpri(i) - obs%cakpri(i) + & + obs%model(i) + obs%ocn(i) + obs%bkg(i) + obs%cinipos(i) - obs%conc(i) else ! optimize offsets so account for contribution from best guess fluxes (ghg) - obs%delta(i) = obs%model(i) + obs%ghg(i) - obs%conc(i) + obs%bkg(i) + obs%cinipos(i) + obs%delta(i) = obs%cpri(i) - obs%cakpri(i) + & + obs%model(i) + obs%ghg(i) + obs%bkg(i) + obs%cinipos(i) - obs%conc(i) endif endif @@ -590,7 +663,6 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) filename = trim(files%path_output)//'grad_'//aiter//'.txt' open(100,file=trim(filename),status='replace',action='write',iostat=ierr) do i = 1, nvar -! write(100,fmt='(E11.4)') grad_o(i) write(100,fmt='(E13.6)') grad_o(i) end do close(100) @@ -602,6 +674,7 @@ subroutine simulate(iter, files, config, fluxes, obs, states, grad_o, cost_o) if ( allocated(dates) ) deallocate(dates) if ( allocated(ftime) ) deallocate(ftime) if ( allocated(factor) ) deallocate(factor) + if ( allocated(factor_nest) ) deallocate(factor_nest) end subroutine simulate -- GitLab