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