The general approach is that observation operators will be implemented as a hierarchy of classes all inheriting from an abstract base class. During the execution of an application, it is intended that one instance of each class will be created per observation type being used in that run, grouping all observations of that type in this object. This grouping has two aims: lower level data can be organized as arrays for greater efficiency (better memory access patterns and threading) and it will be easier to implement methods accounting for correlations within each observation type.
The abstract interfaces defined by the base types are the ones that will be used by the JEDI abstract layer. All data should be passed by interface (no global variables) or be private to a given observation type and contained in the object. The use of common interfaces will ensure easy exchanges of observation operators between partners and easy addition of new observation types without any modification in data assimilation algorithms.
The abstract base class at the Fortran level is not in principle needed by a system like JEDI which relies on its own mechanism for handling observation types. However, it is convenient for two reasons: it defines an interface that guaranties compatibility for higher level code and it avoids duplication of common code between types.
Unit tests will be developed and implemented with each method.
The interfaces below are the minimum requirement for the JEDI abstract layer. Each implementation could can define its own additional methods, although their use should be limited to internal purposes.
type, abstract :: obs_operator
contains
procedure(op_create), deferred :: create ! Constructor
procedure(op_delete), deferred :: delete ! Destructor
procedure(op_h_oper), deferred :: h_oper ! Observation operator
procedure(op_inputs), deferred :: input_variables ! input variables required by observation operator
procedure(op_print), deferred :: print ! Prints human readable info
end type obs_operator
abstract interface
subroutine op_create(self, config)
class(obs_operator), intent(inout) :: self
type(config), intent(in) :: config
end subroutine op_create
end interface
abstract interface
subroutine op_delete(self)
class(obs_operator), intent(inout) :: self
end subroutine op_delete
end interface
abstract interface
subroutine op_h_oper(self, gom, hofx, bias)
class(obs_operator), intent(in) :: self
type(state_at_locations), intent(in) :: gom ! Model values after interpolations
type(obs_data), intent(inout) :: hofx ! H(x) output values
type(obs_aux_variable), optional, intent(in) :: bias ! Bias correction predictors
end subroutine op_h_oper
end interface
abstract interface
function op_inputs(self)
class(variables), pointer :: op_inputs
class(obs_error), intent(in) :: self
end function op_inputs
end interface
abstract interface
subroutine op_print(self)
class(obs_operator), intent(in) :: self
end subroutine op_print
end interface
The linear observation operators are kept in a separate class. They can be implemented as tangent linear and adjoint code or explicit Jacobians, the interface stays the same.
type, abstract :: linear_obs_operator
contains
procedure(op_create), deferred :: create ! Constructor (compute/store trajectory/Jacobians)
procedure(op_delete), deferred :: delete ! Destructor
procedure(op_h_optl), deferred :: h_oper_tl ! Linearized observation operator
procedure(op_h_opad), deferred :: h_oper_ad ! Adjoint observation operator
procedure(op_print), deferred :: print ! Prints human readable info
end type linear_obs_operator
abstract interface
subroutine op_create(self, traj, bias, config)
class(linear_obs_operator), intent(inout) :: self
class(obs_operator), intent(in) :: oper ! Nonlinear observation operator
type(state_at_locations), intent(in) :: traj ! Nonlinear trajectory "state"
type(obs_aux_variable), optional, intent(in) :: bias ! Nonlinear trajectory bias predictors
type(config), intent(in) :: config
end subroutine op_create
end interface
abstract interface
subroutine op_delete(self)
class(linear_obs_operator), intent(inout) :: self
end subroutine op_delete
end interface
abstract interface
subroutine op_h_optl(self, dgom, dhofx, dbias)
class(linear_obs_operator), intent(in) :: self
type(state_at_locations), intent(in) :: dgom ! Increment values after interpolations
type(obs_vector), intent(inout) :: dhofx ! H.dx output values
type(obs_aux_incr), optional, intent(in) :: dbias ! Bias correction predictors increment
end subroutine op_h_optl
abstract interface
subroutine op_h_opad(self, dgom, dhofx, dbias)
class(linear_obs_operator), intent(in) :: self
type(state_at_locations), intent(inout) :: dgom ! H^T.dy output values (at obs locations)
type(obs_vector), intent(in) :: dhofx ! dy input
type(obs_aux_incr), optional, intent(inout) :: dbias ! Bias correction predictors gradient
end subroutine op_h_opad
end interface
abstract interface
subroutine op_print(self)
class(linear_obs_operator), intent(in) :: self
end subroutine op_print
end interface