Skip to content

Commit 1c983d5

Browse files
authored
Merge pull request #238 from NCAR/SIF_converter
Add Solar Induced Fluorescence Observation Converter - closes #237
2 parents 10eb97f + 0078a43 commit 1c983d5

21 files changed

+1205
-14
lines changed

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ individual files.
2222

2323
The changes are now listed with the most recent at the top.
2424

25+
**June 8 2021 :: New observation converter for Solar Induced Fluorescence (SIF). Tag: v9.11.0**
26+
27+
- Converter for harmonized SIF retrievals
28+
2529
**Jun 7 2021 :: fix typos in POP documentation Tag: v9.10.6**
2630

2731
- fix some spelling mistakes, does not change meaning.

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ References
309309
observations/obs_converters/NCEP/prep_bufr/prep_bufr
310310
observations/obs_converters/NCEP/ascii_to_obs/create_real_obs
311311
observations/obs_converters/ROMS/ROMS
312+
observations/obs_converters/SIF/SIF_to_obs_netcdf
312313
observations/obs_converters/SSEC/SSEC
313314
observations/obs_converters/SST/SST
314315
observations/obs_converters/SSUSI/convert_f16_edr_dsk

conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
author = 'Data Assimilation Research Section'
2222

2323
# The full version, including alpha/beta/rc tags
24-
release = '9.10.6'
24+
release = '9.11.0'
2525
master_doc = 'README'
2626

2727
# -- General configuration ---------------------------------------------------

guide/available-observation-converters.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Each directory has at least one converter:
2323
- ``NCEP``: (prepbufr -> ascii) :doc:`../observations/obs_converters/NCEP/prep_bufr/prep_bufr`
2424
- ``NCEP``: (ascii -> obs_seq) :doc:`../observations/obs_converters/NCEP/ascii_to_obs/create_real_obs`
2525
- ``ROMS``: :doc:`../observations/obs_converters/ROMS/ROMS`
26+
- ``SIF``: :doc:`../observations/obs_converters/SIF/SIF_to_obs_netcdf`
2627
- ``SSEC``: :doc:`../observations/obs_converters/SSEC/SSEC`
2728
- ``SST``: :doc:`../observations/obs_converters/SST/SST`
2829
- ``SSUSI``: :doc:`../observations/obs_converters/SSUSI/convert_f16_edr_dsk`

observations/forward_operators/obs_def_land_mod.f90

Lines changed: 236 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,18 @@
7777
!SURFACE_ALBEDO, QTY_SURFACE_ALBEDO
7878
!OCO2_SIF, QTY_SOLAR_INDUCED_FLUORESCENCE, COMMON_CODE
7979
!ECOSTRESS_ET, QTY_LATENT_HEAT_FLUX, COMMON_CODE
80+
!HARMONIZED_SIF, QTY_SOLAR_INDUCED_FLUORESCENCE
8081
! END DART PREPROCESS TYPE DEFINITIONS
8182

8283
!-----------------------------------------------------------------------------
8384
! BEGIN DART PREPROCESS USE OF SPECIAL OBS_DEF MODULE
8485
! use obs_def_land_mod, only : calculate_albedo, &
8586
! calculate_biomass, &
86-
! calculate_fpar
87+
! calculate_fpar, &
88+
! calculate_sif, &
89+
! read_sif_wavelength, &
90+
! write_sif_wavelength, &
91+
! interactive_sif_wavelength
8792
! END DART PREPROCESS USE OF SPECIAL OBS_DEF MODULE
8893
!-----------------------------------------------------------------------------
8994

@@ -95,6 +100,8 @@
95100
! call calculate_biomass(state_handle, ens_size, location, expected_obs, istatus)
96101
! case(MODIS_FPAR)
97102
! call calculate_fpar(state_handle, ens_size, location, expected_obs, istatus)
103+
! case(HARMONIZED_SIF)
104+
! call calculate_sif(state_handle, ens_size, location, expected_obs, istatus)
98105
! END DART PREPROCESS GET_EXPECTED_OBS_FROM_DEF
99106
!-----------------------------------------------------------------------------
100107

@@ -104,6 +111,8 @@
104111
! BIOMASS, &
105112
! MODIS_FPAR)
106113
! continue
114+
! case(HARMONIZED_SIF)
115+
! call read_SIF_wavelength(obs_def%key, key, ifile, fform)
107116
! END DART PREPROCESS READ_OBS_DEF
108117
!-----------------------------------------------------------------------------
109118

@@ -113,6 +122,8 @@
113122
! BIOMASS, &
114123
! MODIS_FPAR)
115124
! continue
125+
! case(HARMONIZED_SIF)
126+
! call write_SIF_wavelength(key, ifile, fform)
116127
! END DART PREPROCESS WRITE_OBS_DEF
117128
!-----------------------------------------------------------------------------
118129

@@ -122,6 +133,8 @@
122133
! BIOMASS, &
123134
! MODIS_FPAR)
124135
! continue
136+
! case(HARMONIZED_SIF)
137+
! call interactive_SIF_wavelength(obs_def%key)
125138
! END DART PREPROCESS INTERACTIVE_OBS_DEF
126139
!-----------------------------------------------------------------------------
127140

@@ -138,10 +151,9 @@ module obs_def_land_mod
138151
use location_mod, only : location_type, &
139152
write_location
140153

141-
use utilities_mod, only : register_module, &
142-
error_handler, &
154+
use utilities_mod, only : error_handler, &
143155
E_ERR, E_MSG, &
144-
do_output
156+
do_output, ascii_file_format
145157

146158
use assim_model_mod, only : interpolate
147159

@@ -157,18 +169,22 @@ module obs_def_land_mod
157169
QTY_FRACTION_ABSORBED_PAR, &
158170
QTY_PAR_DIRECT, &
159171
QTY_PAR_DIFFUSE, &
160-
QTY_ABSORBED_PAR
172+
QTY_ABSORBED_PAR, &
173+
QTY_SOLAR_INDUCED_FLUORESCENCE
161174

162175
implicit none
163176
private
164177

165178
public :: calculate_albedo, &
166179
calculate_biomass, &
167-
calculate_fpar
180+
calculate_fpar, &
181+
calculate_sif, &
182+
set_SIF_wavelength, &
183+
read_SIF_wavelength, &
184+
write_SIF_wavelength, &
185+
interactive_SIF_wavelength
168186

169-
character(len=*), parameter :: source = 'obs_def_land_mod.f90'
170-
character(len=*), parameter :: revision = ''
171-
character(len=*), parameter :: revdate = ''
187+
character(len=*), parameter :: source = 'obs_def_land_mod.f90'
172188

173189
logical :: module_initialized = .false.
174190

@@ -177,6 +193,11 @@ module obs_def_land_mod
177193
! This might be useful, but not enough to warrant a namelist ... yet
178194
logical :: debug = .false.
179195

196+
! Bits and bobs for the solar-induced fluorescence metadata
197+
integer :: max_num_sif_obs = 200000
198+
integer :: sifkey = 0
199+
integer, allocatable :: sif_wavelength(:)
200+
character(len=*), parameter :: SIF_STRING = 'lambda'
180201

181202
!===============================================================================
182203
contains
@@ -189,8 +210,7 @@ subroutine initialize_module()
189210

190211
module_initialized = .true.
191212

192-
! Log the version of this source file.
193-
call register_module(source, revision, revdate)
213+
allocate(sif_wavelength(max_num_sif_obs))
194214

195215
end subroutine initialize_module
196216

@@ -219,8 +239,7 @@ subroutine calculate_albedo(state_handle, ens_size, location, obs_val, istatus)
219239
istatus = 1 ! 0 == success, anything else is a failure
220240
obs_val = MISSING_R8
221241

222-
call error_handler(E_ERR,'calculate_albedo','routine untested - stopping.', &
223-
source, revision, revdate)
242+
call error_handler(E_ERR,'calculate_albedo','routine untested - stopping.', source )
224243

225244
if ( .not. module_initialized ) call initialize_module()
226245

@@ -416,6 +435,210 @@ subroutine calculate_fpar(state_handle, ens_size, location, obs_val, istatus)
416435

417436
end subroutine calculate_fpar
418437

438+
439+
!-------------------------------------------------------------------------------
440+
441+
442+
subroutine calculate_sif(state_handle, ens_size, location, obs_val, istatus)
443+
444+
type(ensemble_type), intent(in) :: state_handle
445+
integer, intent(in) :: ens_size
446+
type(location_type), intent(in) :: location
447+
real(r8), intent(out) :: obs_val(ens_size)
448+
integer, intent(out) :: istatus(ens_size)
449+
450+
if ( .not. module_initialized ) call initialize_module()
451+
452+
! If the model state has it directly, this is simple.
453+
! If it does not ... nothing else to try at the moment
454+
455+
call interpolate(state_handle, ens_size, location, &
456+
QTY_SOLAR_INDUCED_FLUORESCENCE, obs_val, istatus)
457+
458+
end subroutine calculate_sif
459+
460+
461+
!-------------------------------------------------------------------------------
462+
!> stuff the value into the local metadata array
463+
464+
function set_SIF_wavelength(lambda) result(key)
465+
466+
integer, intent(in) :: lambda
467+
integer :: key
468+
469+
if ( .not. module_initialized ) call initialize_module
470+
471+
! update the index into the module array
472+
sifkey = sifkey + 1
473+
474+
! check that it fits
475+
if (sifkey > max_num_sif_obs) call double_metadata()
476+
477+
sif_wavelength(sifkey) = lambda
478+
key = sifkey
479+
480+
end function set_SIF_wavelength
481+
482+
483+
!-------------------------------------------------------------------------------
484+
!> writes the metadata for SIF observations.
485+
486+
subroutine read_SIF_wavelength(key, obsID, ifile, fform)
487+
488+
integer, intent(out) :: key
489+
integer, intent(in) :: obsID
490+
integer, intent(in) :: ifile
491+
character(len=*), intent(in), optional :: fform
492+
493+
character(len=*), parameter :: routine = 'read_SIF_wavelength'
494+
495+
logical :: is_asciifile
496+
integer :: lambda
497+
character(len=6) :: header
498+
integer :: ierr
499+
character(len=512) :: msgstring
500+
501+
if ( .not. module_initialized ) call initialize_module
502+
503+
! create string for error reporting
504+
write(msgstring,*)'observation # ',obsID
505+
506+
! given the index into the local metadata arrays - retrieve
507+
! the metadata for this particular observation.
508+
509+
is_asciifile = ascii_file_format(fform)
510+
511+
if (is_asciifile) then
512+
read(ifile, *, iostat=ierr) header
513+
call check_iostat(ierr,routine,'header',msgstring)
514+
read(ifile, *, iostat=ierr) lambda
515+
call check_iostat(ierr,routine,'lambda',msgstring)
516+
read(ifile, *, iostat=ierr) key
517+
call check_iostat(ierr,routine,'key',msgstring)
518+
else
519+
read(ifile , iostat=ierr) header
520+
call check_iostat(ierr,routine,'header',msgstring)
521+
read(ifile , iostat=ierr) lambda
522+
call check_iostat(ierr,routine,'lambda',msgstring)
523+
read(ifile , iostat=ierr) key
524+
call check_iostat(ierr,routine,'key',msgstring)
525+
endif
526+
527+
sifkey = sifkey + 1
528+
529+
! check that it fits
530+
if (sifkey > max_num_sif_obs) call double_metadata()
531+
532+
sif_wavelength(sifkey) = lambda
533+
key = sifkey
534+
535+
end subroutine read_SIF_wavelength
536+
537+
!-------------------------------------------------------------------------------
538+
!> writes the metadata for SIF observations.
539+
540+
subroutine write_SIF_wavelength(key, ifile, fform)
541+
542+
integer, intent(in) :: key
543+
integer, intent(in) :: ifile
544+
character(len=*), intent(in), optional :: fform
545+
546+
logical :: is_asciifile
547+
integer :: lambda
548+
549+
if ( .not. module_initialized ) call initialize_module
550+
551+
! given the index into the local metadata arrays - retrieve
552+
! the metadata for this particular observation.
553+
554+
lambda = sif_wavelength(key)
555+
556+
is_asciifile = ascii_file_format(fform)
557+
558+
if (is_asciifile) then
559+
write(ifile, *) trim(SIF_STRING)
560+
write(ifile, *) lambda
561+
write(ifile, *) key
562+
else
563+
write(ifile ) trim(SIF_STRING)
564+
write(ifile ) lambda
565+
write(ifile ) key
566+
endif
567+
568+
end subroutine write_SIF_wavelength
569+
570+
571+
!-------------------------------------------------------------------------------
572+
!> interactively queries for metadata required for SIF observations.
573+
574+
subroutine interactive_SIF_wavelength(key)
575+
576+
integer, intent(out) :: key
577+
578+
character(len=*), parameter :: routine = 'interactive_SIF_wavelength'
579+
integer :: lambda
580+
581+
if ( .not. module_initialized ) call initialize_module
582+
583+
write(*,*) 'Input wavelength of SIF (nm)'
584+
read(*,*)lambda
585+
586+
key = set_SIF_wavelength(lambda)
587+
588+
end subroutine interactive_SIF_wavelength
589+
590+
591+
!-------------------------------------------------------------------------------
592+
!> creates enough space for more SIF metadata
593+
594+
subroutine double_metadata()
595+
596+
integer, allocatable :: temp_array(:)
597+
integer :: existing_length
598+
integer :: new_length
599+
600+
existing_length = size(sif_wavelength)
601+
new_length = 2 * existing_length
602+
603+
write(string1,*)'increasing metadata length from ',existing_length, &
604+
' to ',new_length
605+
call error_handler(E_MSG,'double_metadata',string1,source)
606+
607+
allocate(temp_array(existing_length))
608+
609+
temp_array = sif_wavelength
610+
611+
deallocate(sif_wavelength)
612+
allocate( sif_wavelength(new_length))
613+
614+
sif_wavelength(1:existing_length) = temp_array
615+
616+
deallocate(temp_array)
617+
618+
max_num_sif_obs = new_length
619+
620+
end subroutine double_metadata
621+
622+
623+
!-------------------------------------------------------------------------------
624+
!> simple error handling routine
625+
626+
subroutine check_iostat(istat, routine, context, msgstring)
627+
628+
integer, intent(in) :: istat
629+
character(len=*), intent(in) :: routine
630+
character(len=*), intent(in) :: context
631+
character(len=*), intent(in) :: msgstring
632+
633+
if ( istat /= 0 ) then
634+
write(string1,*)'istat should be 0 but is ',istat,' for '//context
635+
call error_handler(E_ERR, routine, string1, source, text2=msgstring)
636+
end if
637+
638+
end subroutine check_iostat
639+
640+
641+
419642
end module obs_def_land_mod
420643

421644
! END DART PREPROCESS MODULE CODE

observations/obs_converters/README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ converters) include:
381381
- `NCEP (prepbufr->ascii) <NCEP/prep_bufr/prep_bufr.html>`__
382382
- `NCEP (ascii->obs_seq) <NCEP/ascii_to_obs/create_real_obs.html>`__
383383
- `ROMS <ROMS/ROMS.html>`__
384+
- `SIF <SIF/SIF_to_obs_netcdf.html>`__
384385
- `SSEC <SSEC/SSEC.html>`__
385386
- `SST <SST/SST.html>`__
386387
- `SSUSI <SSUSI/convert_f16_edr_dsk.html>`__

0 commit comments

Comments
 (0)