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