Creating a new Observation Operator in UFO¶
Existing Observation Operators¶
Before implementing a new observation operator, check if one of the observation operators already implemented in UFO is suitable.
Creating files for a new Observation Operator¶
If the observation operator is not on the list of already implemented observation operators, it may have to be implemented and added to UFO. Typically, all the files for a new observation operator are in a new directory under ufo/src/ufo
.
New observation operators can be written in C++ or in Fortran.
All the observation operators written in Fortran have to have a C++ interface, because all observation operators have to be accessed by a generic data assimilation layer written in C++ in oops. A directory for an observation operator written in Fortran typically consists of the following files (example from atmvertinterp):
ObsAtmVertInterp.cc
,ObsAtmVertInterp.h
: C++ files defining the ObsOperator class. The methods (functions) there call Fortran subroutines.ObsAtmVertInterp.interface.F90
,ObsAtmVertInterp.interface.h
: C++ and Fortan files defining interfaces between Fortran and C++.ufo_atmvertinterp_mod.F90
- Fortran module containing the code to run observation operator.
For new observation operators written in Fortran, files from (1-2) can be generated, and the developer would only need to modify the Fortran module (3).
To generate the ObsOperator files, one can run the following script: ufo/tools/new_obsop/create_obsop_fromexample.sh <ObsOperatorName> <directory>
<ObsOperatorName>
is the name of the obs operator in UpperCamelCase format. <directory>
is a directory name in ufo/src/ufo
. Examples for existing obsoperators: atmvertinterp, crtm, identity.
Example of calling create_obsop_fromexample.sh
:
$> ./create_obsop_fromexample.sh MyOperator myoperator
After the directory with the new obsoperator is created, add it to ufo/src/ufo/CMakeLists.txt
:
add_subdirectory( identity )
add_subdirectory( myoperator )
list( APPEND ufo_src_files
${identity_src_files}
${myoperator_src_files}
and try to compile/build the code.
Adding an Observation Operator test¶
After this skeleton code is generated, create a test for your new observation operator. Even if the test fails because of missing data or a mismatch between computed and provided values, the test will still call your operator and any print statements or other calls you perform within the Fortran subroutines will execute.
For observation operator test one needs a sample observation file and a corresponding geovals file.
All observation operator tests in UFO use the OOPS ObsOperator test. To create a new one, add an entry to ufo/test/CMakeLists.txt
similar to:
ecbuild_add_test( TARGET test_ufo_opr_myoperator # test name
COMMAND ${CMAKE_BINARY_DIR}/bin/test_ObsOperator.x # test executable name
ARGS "testinput/myoperator.yaml" # config file
ENVIRONMENT OOPS_TRAPFPE=1
DEPENDS test_ObsOperator.x
TEST_DEPENDS ufo_get_ioda_test_data ufo_get_ufo_test_data )
Other changes required in ufo/test/CMakeLists.txt
:
Link the config file you will be using for the test:
list( APPEND ufo_test_input
testinput/myoperator.yaml
To configure the test, create config file ufo/test/testinput/myoperator.yaml
and fill appropriately. For examples see ufo/test/testinput/amsua_crtm.yaml
, ufo/test/testinput/radiosonde.yaml
.
Adding substance to the new Observation Operator¶
To implement the Observation Operator, one needs to:
Specify input variable names (requested from the model) in
ufo_obsoperator_mod.F90
, subroutineufo_obsoperator_setup
. The input variable names need to be saved inself%geovars
. The variables that need to be simulated by the observation operator are already set inself%obsvars
(these are the variables fromobs space.simulated variables
section of configuration file. See examples inufo/src/ufo/atmvertinterp/ufo_atmvertinterp_mod.F90
andufo/src/ufo/crtm/ufo_radiancecrtm_mod.F90
. The variables can be hard-coded or controlled from the config file depending on your observation operator.Fill in
ufo_obsoperator_simobs
routine. This subroutine is for calculating H(x). Inputs:geovals
(horizontally interpolated to obs locations model fields for the variables specified inself%geovars
above),obss
(observation space, can be used to request observation metadata). Output:hofx(nvars, nlocs)
(obs vector to hold H(x),nvars
are equal to the size ofself%obsvars
). Note that thehofx
vector was allocated before the call toufo_obsoperator_simobs
, and only needs to be filled in.
Observation Operator test¶
All observation operator tests in UFO use the OOPS ObsOperator test from oops/src/test/interface/ObsOperator.h
.
There are two parts of this test:
testConstructor
: tests that ObsOperator objects can be created and destroyed
testSimulateObs
: tests observation operator calculation in the following way:
Creates observation operator, calls
ufo_obsoperator_setup
Reads “GeoVaLs” (vertical profiles of relevant model variables, interpolated to observation lat-lon location) from the geovals file
Computes H(x) by calling
ufo_obsoperator_simobs
Reads reference and compares the result to the reference. Options for specifying reference:
if full vector reference H(x) available in the obs file:
vector ref
entry in the config specifies variable name for the reference H(x) in the obs file. Test passes if the norm(benchmark H(x) - H(x)) < tolerance, with tolerance defined in the config bytolerance
.norm ref
entry in the config specifies variable name for the reference H(x) in the obs file. Test passes if the norm((benchmark H(x) - H(x))/H(x)) < tolerance, with tolerance defined in the config bytolerance
.otherwise, the expected reference norm(H(x)) can be specified in the
rms ref
entry in the config. Test passes if reference norm is close to the norm(H(x)) with tolerance defined in the config bytolerance
: