Core (Base Classes) of DA (Inversion) Methods#

Abstract classes for data assimilation algorithms (inverse problems); including both filtering (time-independent) and smoothing (time-dependent).

  • In filtering, one observation is used to update model state/parameter or the underlying probability distribution. In smoothing, on the other hand, multiple observations (e.g., at different times) are used. The output is an estimate of the model state/parameter uncertainty measure, e.g., posterior covariance, provided if a Bayesian approach is followed.

    Note

    There is a crucial design decision that requires making at this point. The register_observation, register_observation_error_model, and register_observation_operator functionality are related to each others. The experimental design (if an observation design (e.g., sensor locations)), influences the dimensionality of the observation(s) which depends on PyOED’s projection approach as defined by the settings module, specifically by pyoed.configs.SETTINGS.project_onto_active_design_space.

    The is_observation_vector() available for each observation operator checks if an object is a valid observation vector by inspecting each of the following:

    1. It is of a proper type (typically a 1d array)

    2. The size of the observation vector is equal to:

      • the full observation space if pyoed.configs.SETTINGS.project_onto_active_design_space is False.

      • the number of active entries (non-zero design values) if pyoed.configs.SETTINGS.project_onto_active_design_space is True.

    Since observations are collected in reality based on the current experimental design, the observation vector must match the observation operator active space as well as the observation error model active space if pyoed.configs.SETTINGS.project_onto_active_design_space is True. Otherwise, the observation vector need to match the full observation space as well as the full observation noise space.

    Thus, the three registration methods above check those rules when attempting to register the corresponding object.

    Note

    Here are the registration rules:

    1. Any observation to be registerd must ignore the experimental design. This means, the observations stored must be applicable even when the experimental design changes. This allows solving the assimilation problems for any design, and also allows proper evaluation of the OED functionality.

    2. The observations() attributes in any DA object (inverse problem) properly extracts the observations according to the current experimental design (based on the global PyOED configurations in pyoed.configs.SETTINGS.project_onto_active_design_space.

    3. The observation operator is a function of the simulation model state. Thus, the operator domain size (full shape ignoring design) must match the model state size.

    4. The observation(s) are generated by the observation operator. To avoid any confusion, the registered observations must be valid observations, and thus they must comply with the dimension of the observation space accounting for existing experimental design. The assimilation procedures, however, need to be agnostic to the experimental design as it is assumed constant during the assimilation step. Thus, an observation vector is valid if the registered observation operator agrees that the observation vector is valid. Thus, to register observation(s):

      1. a valid observation operator must be registered first,

      2. the observation operator’s method is_valid_observation() returns True for an observation to be acceptable and properly registered.

    5. the observation error model weights the simulation and the observational data for any experimental design. Thus, an observation error model must satisfy that:

      1. the error model full space size (ignoring the design) must match the full co-domain of the observation operator (ignoring the design).

    Finally, any assimilation procedure must make sure the experimental design is consistent and is not different in the observation operator and the observation error model.

class InverseProblemConfigs(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, model=None, prior=None, observation_error_model=None, observation_operator=None, observations=None, optimizer=None, optimizer_configs=None)[source]#

Bases: PyOEDConfigs

Configurations class for the InverseProblem abstract base class. This class inherits functionality from PyOEDConfigs and only adds new class-level variables which can be updated as needed.

See PyOEDConfigs for more details on the functionality of this class along with a few additional fields. Otherwise InverseProblemConfigs provides the following fields:

Parameters:
  • verbose (bool) – a boolean flag to control verbosity of the object.

  • debug (bool) – a boolean flag that enables adding extra functionality in a debug mode

  • output_dir (str | Path) – the base directory where the output files will be saved.

  • name (str | None) – name of the DA (inverse problem solver) approach/method.

  • model (None | SimulationModel) – the simulation model.

  • prior (None | ErrorModel) – Background/Prior model (e.g., GaussianErrorModel)

  • observation_operator (None | ObservationOperator) – operator to map model state to observation space

  • observation_error_model (None | ErrorModel) – Observation error model (e.g., GaussianErrorModel)

  • observations (None | Any) – Observational data (the data type is very much dependent of the DA method)

  • optimizer (None | Optimizer) –

    the optimization routine (optimizer) to be registered and later used for solving the OED problem. This can be one of the following:

    • None: In this case, no optimizer is registered, and the solve() won’t be functional until an optimization routine is registered.

    • An optimizer instance (object that inherits :py:class`Optimizer`). In this case, the optimizer is registered as is and is updated with the passed configurations if available.

    • The class (subclass of Optimizer) to be used to instantiate the optimizer.

  • optimizer_configs (None | dict | OptimizerConfigs) –

    the configurations of the optimization routine. This can be one of the following:

    • None, in this case configurations are discarded, and whatever default configurations of the selected/passed optimizer are employed.

    • A dict holding full/partial configurations of the selected optimizer. These are either used to instantiate or update the optimizer configurations based on the type of the passed optimizer.

    • A class providing implementations of the configurations (this must be a subclass of OptimizerConfigs.

    • An instance of a subclass of OptimizerConfigs which is to set/udpate optimizer configurations.

    Note

    Not all DA (inverse problem) objects are optimization-based. For example, particle-based (EnKF, PF, etc.) employ a sample to estimate the flow of the distribution through the model dynamics (prior -> posterior). Thus, the optimizer (and configs) in this case (the default) are set to None. For optimization-based methods such as 3DVar, 4DVar, etc., an optimizer must be registered for the inverse problem to be solved.

name: str | None#
model: None | SimulationModel#
prior: None | ErrorModel#
observation_error_model: None | ErrorModel#
observation_operator: None | ObservationOperator#
observations: None | Any#
optimizer: None | Optimizer#
optimizer_configs: None | dict | OptimizerConfigs#
__init__(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, model=None, prior=None, observation_error_model=None, observation_operator=None, observations=None, optimizer=None, optimizer_configs=None)#
class InverseProblem(configs=None)[source]#

Bases: PyOEDObject

Base class for implementations of Inversion/Inference/DA (Data Assimilation) methods/approaches.

Note

The optimizer configuration attribute can be assigned an optimization routine to be used for solving the inverse problem (for optimization-based DA methods). For optimization based DA methods, the method solve() relies on this object for solving the OED optimization problem. Since not all DA methods are optimization-base, this is None by default and need to be created by derived classes. One can, however, discard this attribute and write full functionality in the solve() method. However, employing this attribute is expected for consistency.

Parameters:

configs (dict | InverseProblemConfigs | None) – (optional) configurations for the optimization object

Raises:

PyOEDConfigsValidationError – if passed invalid configs

__init__(configs=None)[source]#
validate_configurations(configs, raise_for_invalid=True)[source]#

Each simulation optimizer SHOULD implement it’s own function that validates its own configurations. If the validation is self contained (validates all configuations), then that’s it. However, one can just validate the configurations of of the immediate class and call super to validate configurations associated with the parent class.

If one does not wish to do any validation (we strongly advise against that), simply add the signature of this function to the optimizer class.

Note

The purpose of this method is to make sure that the settings in the configurations object self._CONFIGURATIONS are of the right type/values and are conformable with each other. This function is called upon instantiation of the object, and each time a configuration value is updated. Thus, this function need to be inexpensive and should not do heavy computations.

Parameters:

configs (dict | InverseProblemConfigs) – configurations to validate. If a InverseProblemonfigs object is passed, validation is performed on the entire set of configurations. However, if a dictionary is passed, validation is performed only on the configurations corresponding to the keys in the dictionary.

Raises:
update_configurations(**kwargs)[source]#

Take any set of keyword arguments, and lookup each in the configurations, and update as nessesary/possible/valid

Raises:

PyOEDConfigsValidationError – if invalid configurations passed

register_model(model=None)[source]#

Register (and return) the simulation model.

Parameters:

model (None | SimulationModel) – the simulation model to register, or None to clear it.

Returns:

the registered model (or None).

Return type:

SimulationModel | None

Raises:

PyOEDConfigsValidationError – if the type of passed model is not supported

register_prior(prior=None)[source]#

Register (and return) the prior (background) error model.

Parameters:

prior (None | ErrorModel) – the prior error model to register, or None to clear it.

Returns:

the registered prior (or None).

Return type:

ErrorModel | None

Raises:

PyOEDConfigsValidationError – if the type of passed prior is not supported

register_observation_operator(observation_operator=None)[source]#

Register (and return) the observation operator.

Validates that the operator is consistent with the registered simulation model (domain), observation error model (co-domain), observations, and experimental design.

Parameters:

observation_operator (None | ObservationOperator) – the observation operator to register, or None to clear it.

Returns:

the registered observation operator (or None).

Return type:

ObservationOperator | None

Raises:

PyOEDConfigsValidationError – if the type of passed observation operator is not supported

register_observation_error_model(observation_error_model=None)[source]#

Register (and return) the observation error model.

Validates that the error model size is consistent with the registered observation operator co-domain and that their experimental designs match.

Parameters:

observation_error_model (None | ErrorModel) – the observation error model to register, or None to clear it.

Returns:

the registered observation error model (or None).

Return type:

ErrorModel | None

Raises:

PyOEDConfigsValidationError – if the type of passed observation error model is not supported

register_observations(observations)[source]#

Register (and return) the observational data.

Validates that observations are consistent with the registered observation operator. An observation operator must be registered before actual observations can be registered (except for empty dictionaries used by smoothers).

Parameters:

observations – the observational data to register. Can be a numpy array (for filters) or a dict keyed by time (for smoothers), or None to clear.

Returns:

the registered observations.

Raises:

PyOEDConfigsValidationError – if the type of passed observational data is not supported

register_optimizer(optimizer, *args, **kwargs)[source]#

Register (and return) the passed optimizer.

Note

This method does not create a new optimizer instance. It just takes the created optimizer, makes sure it is an instance derived from the pyoed.optimization.Optimizer and associates it with this assimilation (DA) object.

Note

A derived class is expected to create this optimizer, and pass it up by calling super().register_optimizer(optimizer) so that it can be registered properly.

Returns:

the registered optimizer.

Raises:

PyOEDConfigsValidationError – if the type of passed optimizer is not supported

Return type:

Optimizer | None

design_is_consistent(first, second)[source]#

Check that the experimental design is consistent in both first and second. We define a consistent design here as a design that:

  1. If either of first or second is None, the design is assumed consistent (until properly set of course). Thus, this method needs to be called upon registeration of objects those need to be matched.

  2. the design in the two objects is either a dictionary dict, or a numpy array. To allow other types, one has to reimplement this method.

  3. for 1d numpy arrays, the two designs has the same size ignoring active/inactive entries, and the two designs has the same active entries.

  4. for dictionaries, both designs must be indexed with the same keys (e.g., observation times), and the value for each key is a 1d array that satisfies the conditions 2 and 3 above for all keys.

  5. The two objects provide a method project_onto_active_design_space which returns the same value True/False.

  6. If the design of one object is a numpy array, and the design of the other object is a dictionary, all values in the dictionary must be consistent with the numpy array design of the other.

If the two objects first and second have experimental designs accessible through design property, and the conditions above are met, this method returns True. If any of the conditions are violated, the method returns False. If any of the objects does not provide design or project_onto_active_design_space properties, a TypeError is raised.

solve_inverse_problem(*args, **kwargs)[source]#

Start solving the inverse problem (DA) for the registered configuration with passed arguments.

Warning

This method is added for backward compatibility, and it will be deprecated soon. User need to call solve() instead.

apply_forward_operator(*args, **kwargs)[source]#

Apply F, the forward operator to the passed state. The forward operator here, refers to the simulation model followed by the observation operator. The result is a data point (an observation), or a dictionary of observations indexed by the time (for time-dependent models).

For time-dependent simulations with multiple observation points (e.g., in 4D-Var settings), the observations are evaluated at the simulation time instances (corresponding to registered observations) over the registered time window.

apply_forward_operator_adjoint(*args, **kwargs)[source]#

Apply F^*, the adjoint of the forward operator to the passed observation.

show_registered_elements(display=True)[source]#

Compose and (optionally) print out a message containing the elements of the inverse problem and show what’s registered and what’s not

Parameters:

display – if True print out the composed message about registered/unregistered elements

Returns:

the composed message

Return type:

None

check_registered_elements(*args, **kwargs)[source]#

Check if all elements of the inverse problem (simulation model, observation operator, prior, observation error model, and observational data) are registered or not.

Note

This method SHOULD be modified by derived classed to check other elements. For example, a smoother requires observation times, assimilation window, etc.

solve(init_guess=None)[source]#

Start solving the inverse problem.

Note

This method needs to be replicated (rewritten) for any DA (inverse problem) object so that it can replace the returned results object with teh appropriate one.

Parameters:

init_guess – The initial guess of the design to be used as starting point of the optimization routine

Returns:

an instance of (derived from) InverseProblemResults holding results obtained by solving the inverse problem

plot_results(results, overwrite=False, bruteforce_results=None, num_active=None, uncertain_parameter_sample=None, exhaustive_parameter_search_results=None, show_legend=True, output_dir=None, keep_plots=False, fontsize=20, line_width=2, usetex=True, show_axis_grids=True, axis_grids_alpha=(0.25, 0.4), plots_format='pdf')[source]#

Generic plotting function for inverse problems. Given the results returned by solve(), visualize the results. Additional plotters can be added to this method or derived methods…

Raises:

TypeError – if no valid optimizer is registered

property optimizer#

The registered optimizer.

property model#

The registered simulation (forward) model.

An instance of SimulationModel (or one of its subclasses) used to propagate the state and produce model predictions. Register or update the model via register_model() or by direct assignment:

da.model = my_simulation_model
Return type:

SimulationModel | None

property prior#

The registered prior probability distribution.

An instance of ErrorModel (or one of its subclasses) representing the prior knowledge about the state or parameters before any observations are assimilated. Register or update it via register_prior() or by direct assignment:

da.prior = GaussianErrorModel(configs={"mean": mu_b, "variance": B})
Return type:

ErrorModel | None

property posterior#

The posterior distribution (result of the last assimilation solve).

Populated by solve_inverse_problem() after a successful assimilation step. The exact type depends on the concrete DA implementation (e.g., a ErrorModel for filtering, or a sequence of error models for smoothing). None before the first solve.

property observation_error_model#

The registered observation error / noise model.

An instance of ErrorModel characterising the statistical properties of the observation noise. Its full-space size must match the co-domain of the registered observation_operator. Register or update it via register_observation_error_model() or by direct assignment:

da.observation_error_model = GaussianErrorModel(configs={"variance": R})
Return type:

ErrorModel | None

property observation_operator#

The registered observation operator.

An instance of ObservationOperator that maps model states to the observation space. Its domain size must match the model state size. Register or update it via register_observation_operator() or by direct assignment:

da.observation_operator = IdentityObservationOperator(configs={...})
Return type:

ObservationOperator | None

property observations#

The registered observational data. This property needs to be overridden by inheriting class to pose effect of the experimental design on the retrieved data.

property project_onto_active_design_space#

Project observations onto active design space or not. This is determined by the same property project_onto_active_design_space associated with both the registered observation observation operator and observation error model. If the projection strategies are inconsistent, a PyOEDConfigsValidationError is raised.

Raises:
class InverseProblemResults(*, inverse_problem=None, optimization_results=None)[source]#

Bases: PyOEDData

Base class to hold DA (inverse problems) data/results.

Parameters:
  • inverse_problem (InverseProblem | None) – instance of a class derived from InverseProblem.

  • optimization_results (OptimizerResults | None) – results returned by the optimizer after solving the inverse problem (for optimization-based DA methods). None for non-optimization-based methods (e.g., ensemble filters).

inverse_problem: InverseProblem | None#
optimization_results: OptimizerResults | None#
write_results(saveto)[source]#

Save the underlying InverseProblem results to pickled dictionary.

Parameters:

saveto – name/path of the file to save data to.

Raises:
  • TypeError – if saveto is not a valid file path

  • IOError – if writing failed

classmethod load_results(readfrom)[source]#

Inspect pickled file, and load inverse problem results;

Raises:

IOError – if loading failed

__init__(*, inverse_problem=None, optimization_results=None)#
class FilterConfigs(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, model=None, prior=None, observation_error_model=None, observation_operator=None, observations=None, optimizer=None, optimizer_configs=None)[source]#

Bases: InverseProblemConfigs

Configurations class for the Filter abstract base class.

Parameters:
  • verbose (bool) – a boolean flag to control verbosity of the object.

  • debug (bool) – a boolean flag that enables adding extra functionality in a debug mode

  • output_dir (str | Path) – the base directory where the output files will be saved.

  • name (str | None) – name of the DA (inverse problem solver) approach/method.

  • model (None | SimulationModel) – the simulation model.

  • prior (None | ErrorModel) – Background/Prior model (e.g., GaussianErrorModel)

  • observation_operator (None | ObservationOperator) – operator to map model state to observation space

  • observation_error_model (None | ErrorModel) – Observation error model (e.g., GaussianErrorModel)

  • observations (None | Any) – Observational data (the data type is very much dependent of the DA method)

  • optimizer (None | Optimizer) –

    the optimization routine (optimizer) to be registered and later used for solving the OED problem. This can be one of the following:

    • None: In this case, no optimizer is registered, and the solve() won’t be functional until an optimization routine is registered.

    • An optimizer instance (object that inherits :py:class`Optimizer`). In this case, the optimizer is registered as is and is updated with the passed configurations if available.

    • The class (subclass of Optimizer) to be used to instantiate the optimizer.

  • optimizer_configs (None | dict | OptimizerConfigs) –

    the configurations of the optimization routine. This can be one of the following:

    • None, in this case configurations are discarded, and whatever default configurations of the selected/passed optimizer are employed.

    • A dict holding full/partial configurations of the selected optimizer. These are either used to instantiate or update the optimizer configurations based on the type of the passed optimizer.

    • A class providing implementations of the configurations (this must be a subclass of OptimizerConfigs.

    • An instance of a subclass of OptimizerConfigs which is to set/udpate optimizer configurations.

    Note

    Not all DA (inverse problem) objects are optimization-based. For example, particle-based (EnKF, PF, etc.) employ a sample to estimate the flow of the distribution through the model dynamics (prior -> posterior). Thus, the optimizer (and configs) in this case (the default) are set to None. For optimization-based methods such as 3DVar, 4DVar, etc., an optimizer must be registered for the inverse problem to be solved.

__init__(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, model=None, prior=None, observation_error_model=None, observation_operator=None, observations=None, optimizer=None, optimizer_configs=None)#
class Filter(configs=None)[source]#

Bases: InverseProblem

Base class for all filtering (time-independent) DA implementations.

Filters solve inverse problems where a single observation vector is used to update the model state or parameter estimate. This includes methods such as Kalman filters, 3D-Var, and ensemble-based filters (e.g., EnKF).

Compared to the parent InverseProblem, a Filter enforces:

  • The observation operator must not be time-dependent.

  • The observation error model must not be time-dependent.

  • Observations must be a single array (not a dictionary keyed by time).

Parameters:

configs (dict | FilterConfigs | None) – configurations for the filter

__init__(configs=None)[source]#
register_model(model=None)[source]#

Register (and return) the simulation model to be registered. This calls InverseProblem.register_model and adds extra assertions/functionality specific for filters.

Raises:

PyOEDConfigsValidationError – if the type of passed model is not supported

register_prior(prior=None)[source]#

Register (and return) the prior to be registered. This calls InverseProblem.register_prior and adds extra assertions/functionality specific for filters.

Raises:

PyOEDConfigsValidationError – if the type of passed prior is not supported

register_observation_operator(observation_operator=None)[source]#

Register (and return) the observation operator to be registered. This calls InverseProblem.register_observation_operator and adds extra assertions/functionality specific for filters.

Raises:

PyOEDConfigsValidationError – if the type of passed observation operator is not supported

register_observation_error_model(observation_error_model=None)[source]#

Register (and return) the observation error model to be registered. This calls InverseProblem.register_observation_error_model and adds extra assertions/functionality specific for filters.

Raises:

TypeError – if the type of passed observation error model is not supported

register_observations(observations)[source]#

Register (and return) the observational data. This calls InverseProblem.register_observations and adds extra assertions/functionality specific for filters.

Raises:

TypeError – if the type of passed observational data is not supported

property observations#

The registered observational data. This property needs to be overridden by inheriting class to pose effect of the experimental design on the retrieved data.

class FilterResults(*, inverse_problem=None, optimization_results=None)[source]#

Bases: InverseProblemResults

Base class for objects holding results of Filter.

Derived filter implementations (e.g., 3D-Var, EnKF) should subclass this and add fields specific to their output (e.g., posterior state, analysis increment, ensemble).

Parameters:
  • inverse_problem (InverseProblem | None) – instance of a class derived from InverseProblem.

  • optimization_results (OptimizerResults | None) – results returned by the optimizer after solving the inverse problem (for optimization-based DA methods). None for non-optimization-based methods (e.g., ensemble filters).

__init__(*, inverse_problem=None, optimization_results=None)#
class SmootherConfigs(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, model=None, prior=None, observation_error_model=None, observation_operator=None, observations=None, optimizer=None, optimizer_configs=None, window=None)[source]#

Bases: InverseProblemConfigs

Configurations class for the Smoother abstract base class.

Parameters:
  • verbose (bool) – a boolean flag to control verbosity of the object.

  • debug (bool) – a boolean flag that enables adding extra functionality in a debug mode

  • output_dir (str | Path) – the base directory where the output files will be saved.

  • name (str | None) – name of the DA (inverse problem solver) approach/method.

  • model (None | SimulationModel) – the simulation model.

  • prior (None | ErrorModel) – Background/Prior model (e.g., GaussianErrorModel)

  • observation_operator (None | ObservationOperator) – operator to map model state to observation space

  • observation_error_model (None | ErrorModel) – Observation error model (e.g., GaussianErrorModel)

  • observations (None | Any) – Observational data (the data type is very much dependent of the DA method)

  • optimizer (None | Optimizer) –

    the optimization routine (optimizer) to be registered and later used for solving the OED problem. This can be one of the following:

    • None: In this case, no optimizer is registered, and the solve() won’t be functional until an optimization routine is registered.

    • An optimizer instance (object that inherits :py:class`Optimizer`). In this case, the optimizer is registered as is and is updated with the passed configurations if available.

    • The class (subclass of Optimizer) to be used to instantiate the optimizer.

  • optimizer_configs (None | dict | OptimizerConfigs) –

    the configurations of the optimization routine. This can be one of the following:

    • None, in this case configurations are discarded, and whatever default configurations of the selected/passed optimizer are employed.

    • A dict holding full/partial configurations of the selected optimizer. These are either used to instantiate or update the optimizer configurations based on the type of the passed optimizer.

    • A class providing implementations of the configurations (this must be a subclass of OptimizerConfigs.

    • An instance of a subclass of OptimizerConfigs which is to set/udpate optimizer configurations.

    Note

    Not all DA (inverse problem) objects are optimization-based. For example, particle-based (EnKF, PF, etc.) employ a sample to estimate the flow of the distribution through the model dynamics (prior -> posterior). Thus, the optimizer (and configs) in this case (the default) are set to None. For optimization-based methods such as 3DVar, 4DVar, etc., an optimizer must be registered for the inverse problem to be solved.

  • window (None | Iterable) – the assimilation window (t0, tf)

window: None | Iterable#
__init__(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', name=None, model=None, prior=None, observation_error_model=None, observation_operator=None, observations=None, optimizer=None, optimizer_configs=None, window=None)#
class Smoother(configs=None)[source]#

Bases: InverseProblem

Base class for all smoothing (time-dependent) DA implementations.

Smoothers solve inverse problems where multiple observations collected at different times are used to update the model state or parameter trajectory. This includes methods such as 4D-Var, Kalman smoothers, and ensemble smoothers.

Compared to Filter, a Smoother:

  • Requires a TimeDependentModel as the simulation model.

  • Stores observations as a dict keyed by observation time.

  • Requires an assimilation window (t0, tf) to be registered.

Parameters:

configs (dict | SmootherConfigs | None) – configurations for the smoother

__init__(configs=None)[source]#
register_model(model=None)[source]#

Register (and return) the simulation model to be registered. This calls InverseProblem.register_model and adds extra assertions/functionality specific for filters.

Raises:

TypeError – if the type of passed model is not supported

register_prior(prior=None)[source]#

Register (and return) the prior to be registered. This calls InverseProblem.register_prior and adds extra assertions/functionality specific for filters.

Raises:

TypeError – if the type of passed prior is not supported

register_window(window)[source]#

Update the assimilation/inversion time window (cycle)

Parameters:

window (None | Iterable) – an iterable with two entries \((t_0, t_1)\) indicating the beginning and the end of the inversion time window.

Raises:

TypeError – if window is invalid iterable with two entries

register_observation_operator(observation_operator=None)[source]#

Register (and return) the observation operator to be registered. This calls InverseProblem.register_observation_operator and adds extra assertions/functionality specific for filters.

Raises:

TypeError – if the type of passed observation operator is not supported

register_observation_error_model(observation_error_model=None)[source]#

Register (and return) the observation error model to be registered. This calls InverseProblem.register_observation_error_model and adds extra assertions/functionality specific for filters.

Raises:

TypeError – if the type of passed observation error model is not supported

register_observations(observations)[source]#

Register (and return) the observational data. This calls InverseProblem.register_observations and adds extra assertions/functionality specific for filters.

Raises:

TypeError – if the type of passed observational data is not supported

register_observation(t, observation, overwrite=False)[source]#

Given an observation instance/vector and the associated time t, update observation/data information

Parameters:
  • t (float) – time at which the passed observation is registered

  • observation – an observation vector; this should be specified by the forward or the observation operator

  • overwrite (bool) – overwrite an existing observation if already registered at the passed time

Raises:
  • ValueError – is raised if: + overwrite=False and another observation exists at time t + No valid observation operator has been registered yet + The assimilation window is not yet registered

  • PyOEDConfigsValidationError – if an observation operator is registerd first.

  • TypeError – is raised if the observation is not validated by the associated observation operator

find_observation_time(t, time_keys=None)[source]#

Local function to find the key in self.observations reresenting time equal to or within _TIME_EPS of the passed time

Parameters:
  • t – the time to lookup

  • time_keys – the timespan to look into. If None, the registered observation times will be used.

Returns:

the matched time (if found) or None

check_registered_elements(*args, **kwargs)[source]#

Check if all elements of the inverse problem (simulation model, observation operator, prior, observation error model, and observational data) are registered or not.

Note

This method SHOULD be modified by derived classed to check other elements. For example, a smoother requires observation times, assimilation window, etc.

show_registered_elements(display=True)[source]#

Compose and (optionally) print out a message containing the elements of the inverse problem and show what’s registered and what’s not

Parameters:

display – if True print out the composed message about registered/unregistered elements

Returns:

the composed message

Return type:

None

property observations#

The registered observational data. This property needs to be overridden by inheriting class to pose effect of the experimental design on the retrieved data.

property observation_times#

Return a numpy array holding registered observation times.

property window#

The registered assimilation time window (t0, tf).

class SmootherResults(*, inverse_problem=None, optimization_results=None)[source]#

Bases: InverseProblemResults

Base class for objects holding results of Smoother.

Derived smoother implementations (e.g., 4D-Var, Kalman smoother) should subclass this and add fields specific to their output (e.g., state trajectory, innovation sequence, cost function history).

Parameters:
  • inverse_problem (InverseProblem | None) – instance of a class derived from InverseProblem.

  • optimization_results (OptimizerResults | None) – results returned by the optimizer after solving the inverse problem (for optimization-based DA methods). None for non-optimization-based methods (e.g., ensemble filters).

__init__(*, inverse_problem=None, optimization_results=None)#
class GaussianPosteriorConfigs(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', design=True, size=None, mean=0.0, variance=1.0, sparse=True, random_seed=None)[source]#

Bases: GaussianErrorModelConfigs

Just renaming (possibly add more features later) around Gaussian distribution/model configurations.

Parameters:
  • verbose (bool) – a boolean flag to control verbosity of the object.

  • debug (bool) – a boolean flag that enables adding extra functionality in a debug mode

  • output_dir (str | Path) – the base directory where the output files will be saved.

  • design (None | bool | Sequence[bool] | ndarray) –

    an experimental design to define active/inactive entries of the random variable (mean, variance/covariance matrix).

    Note

    • If the design is None, it is set to all ones; that is everything is observed (default)

    • If the design is a binary vector ( or int dtype attributes with 0/1 entries) the mean, the covariance, and all random vectors are projected onto the space identified by the 1/True entries.

  • size (int | None) – dimension of the error model space. Detected from mean if None.

  • mean (float | ndarray) – mean of the error model

  • variance (float | ndarray | spmatrix) – variance/covariance of the error model

  • sparse (bool) – convert covariance to scipy.csc_matrix used if size > 1

  • random_seed (int | None) – random seed used when the Gaussian model is initiated. This is useful for reproductivity.

__init__(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', design=True, size=None, mean=0.0, variance=1.0, sparse=True, random_seed=None)#
class GaussianPosterior(configs=None)[source]#

Bases: GaussianErrorModel

A class approximating the posterior distribution of the 3D-Var problem around the MAP estimate. Everything is the same as in GaussianErrorModel, except for the attribute __STDEV which is involved in the methods covariance_matvec, and generate_noise. These two functions are replaced with matrix-free versions. Here, we do not construct the covariance (posterior covariance), instead, we use the fact that the posterior covariance is (or can be approximated by):

\[\mathbf{A}:= \left( \mathbf{B}^{-1} + \mathbf{M}^T \mathbf{H}^T \mathbf{R}_k^{-1} \mathbf{H} \mathbf{M} \right)^{-1} \,,\]

where \(\mathbf{B}\) is the prior covariance matrix, \(\mathbf{H}\) is the linear observation operator (or the linearized, e.g., Jacobian, around the MAP estimate),

\(\mathbf{R}\) is the observation error covariance matrix, and \(\mathbf{M}\) is the tangent linear of the simulation model (TLM) evaluated at the MAP estimate

__init__(configs=None)[source]#

Initialize the random number generator

class ComplexGaussianPosteriorConfigs(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', design=1.0, size=None, mean=0.0, variance=1 + 0j, relation=0j, sparse=True, map_to_real=False, random_seed=None)[source]#

Bases: ComplexGaussianErrorModelConfigs

Just renaming (possibly add more features later) around Complex-valued Gaussian distribution/model configurations.

Parameters:
  • verbose (bool) – a boolean flag to control verbosity of the object.

  • debug (bool) – a boolean flag that enables adding extra functionality in a debug mode

  • output_dir (str | Path) – the base directory where the output files will be saved.

  • design (Sequence[int] | Sequence[bool] | ndarray) –

    an experimental design to define active/inactive entries of the random variable (mean, variance/covariance matrix).

    Note

    • If the design is None, it is set to all ones; that is everything is observed (default)

    • If the design is a binary vector ( or int dtype attributes with 0/1 entries) the mean, the covariance, and all random vectors are projected onto the space identified by the 1/True entries.

  • size (int | None) – dimension of the error model space. Detected from mean if None.

  • mean (complex | Sequence[complex] | ndarray) – mean of the error model

  • variance (complex | Sequence[complex] | Sequence[Sequence[complex]] | ndarray | spmatrix) – variance/covariance of the error model

  • sparse (bool) – convert covariance to scipy.csc_array used if size > 1

  • map_to_real (bool) – apply computations (e.g., pdf, etc.) by mapping to the real domain using the duality between with the composite real vector

  • random_seed (int | None) – random seed used when the Gaussian model is initiated. This is useful for reproductivity.

__init__(*, debug=False, verbose=False, output_dir='./_PYOED_RESULTS_', design=1.0, size=None, mean=0.0, variance=1 + 0j, relation=0j, sparse=True, map_to_real=False, random_seed=None)#
class ComplexGaussianPosterior(configs=None)[source]#

Bases: ComplexGaussianErrorModel

A class approximating the posterior distribution of the 3D-Var problem around the MAP estimate.

__init__(configs=None)[source]#

Initialize the random number generator