Skip to content

esmda⚓︎

ES-MDA type schemes

ESMDA ⚓︎

Bases: AssimilationScheme

Ensemble Smoother with Multiple Data Assimilation (ES-MDA).

An iterative ensemble smoother that assimilates all data repeatedly over a fixed number of steps, inflating the data-error covariance at each one so that the repeated conditioning does not over-fit. With inflation factors :math:\alpha_i satisfying :math:\sum_i 1/\alpha_i = 1, each step applies

.. math::

m \leftarrow m + C_{md} (C_{dd} + \alpha_i C_d)^{-1} (d_{obs} - g(m))

with the observations re-perturbed as :math:d_{obs} = d_{true} + \sqrt{\alpha_i} C_d^{1/2} Z.

The schedule is fixed rather than convergence-driven, so a run normally ends by exhausting its steps and reports success=False. That is the expected outcome, not a failure.

Parameters:

Name Type Description Default
keys_da dict

Parsed dataassim configuration. Besides the keys every scheme reads -- data, datavar, obsname, truedataindex -- the ones this scheme acts on are listed under Notes.

required
keys_en dict

Parsed ensemble configuration: ensemble size ne, the state variable names, and the prior_<name> blocks describing each.

required
sim object

Forward simulator instance, e.g. simulator.opm.flow.

required
analysis (approx, full, subspace)

Analysis flavour, i.e. how the ensemble-approximated sensitivity is inverted. Defaults to the analysis key in keys_da, falling back to 'approx'. The flavours differ in cost and in how they handle a rank-deficient ensemble; they solve the same update equation.

'approx'

Attributes:

Name Type Description
ensemble AssimilationEnsemble

Collaborator holding the state realisations, observed data and simulator. Its state is exposed as properties on the scheme, so scheme.enX and scheme.keys_da read straight through.

analysis AnalysisBase

The bound analysis object. Note the constructor takes analysis as a name and this attribute holds the resulting object, the way Model(optimizer="adam").optimizer is an optimizer instance.

analysis_name str

The flavour name that was resolved, e.g. 'approx'.

iteration int

Accepted iterations completed so far.

data_misfit, prior_data_misfit float

Current and initial mean data misfit.

Notes

Configured through the mda block of keys_da:

tot_assim_steps Number of assimilation steps, e.g. 3. inflation_param Inflation factors, one per step, e.g. [3, 3, 3]. Their reciprocals must sum to 1, which is asserted at construction. Defaults to tot_assim_steps repeated, which satisfies the constraint.

Examples:

>>> result = ESMDA.assimilate(keys_da, keys_en, flow(keys_sim))
>>> result.nit
3
References

Emerick and Reynolds, Ensemble smoother with multiple data assimilation emerick2013a.

See Also

ES : Single-step smoother; ES-MDA with one assimilation step. LMEnRML : Iterates to convergence instead of on a fixed schedule.

__init__(keys_da, keys_en, sim, analysis=None, ensemble=None) ⚓︎

Build the ensemble from the config (or take the one given) and bind the analysis.

See the class docstring for the parameters; ensemble is a ready-made collaborator to run on instead of building one.

calc_analysis() ⚓︎

Analysis step of ES-MDA. The analysis algorithm is similar to EnKF analysis, only difference is that the data covariance matrix is inflated with an inflation parameter alpha. The update is done as an iterative smoother where all data is assimilated at once.

Notes

ES-MDA is an iterative ensemble smoother with a predefined number of iterations, where the updates is done with the EnKF update equations but where the data covariance matrix have been inflated:

\[ \begin{align} d_{obs} &= d_{true} + \sqrt{\alpha}C_d^{1/2}Z \\ m &= m_{prior} + C_{md}(C_g + \alpha C_d)^{-1}(g(m) - d_{obs}) \end{align} \]

where \(d_{true}\) is the true observed data, \(\alpha\) is the inflation factor, \(C_d\) is the data covariance matrix, \(Z\) is a standard normal random variable, \(C_{md}\) and \(C_{g}\) are sample covariance matrices, \(m\) is the model parameter, and \(g(\)\) is the predicted data. Note that \(\alpha\) can have a different value in each assimilation step and must fulfill:

\[ \sum_{i=1}^{N_a} \frac{1}{\alpha} = 1 \]

where \(N_a\) being the total number of assimilation steps.

check_convergence() ⚓︎

ES-MDA runs its full schedule of inflated steps; nothing stops early.

log_columns(prior_run=False) ⚓︎

ES-MDA reports the inflation factor for the step just taken.

score(pred_data=None) ⚓︎

Data misfit against the un-inflated perturbed observations.

enObs is redrawn each step with the covariance inflated by alpha[iteration], so scoring against it would compare every iteration to a different yardstick. enObs_conv is the copy taken before any inflation, which is what makes the misfit trajectory comparable across the schedule.

score_and_commit() ⚓︎

Score the forecast that followed the analysis, then commit the step.

Was the second half of check_convergence: ES-MDA never actually tested for convergence there, it recomputed the misfit, logged the iteration and promoted enX_temp. Under the new contract the convergence question lives in :meth:check_convergence and this keeps the bookkeeping.

Returns:

Type Description
dict

The why_stop record, also stored on self.why_stop.

update_step() ⚓︎

Run one ES-MDA assimilation step.

Computes the inflated analysis, forecasts the trial state, then scores the resulting misfit and promotes the state. Scoring after the forecast is what lets outlier replacement, which runs in between, feed into the number the scheme sees.

Returns:

Type Description
bool

Always True. ES-MDA takes a fixed number of inflated steps and never rejects one. The success flag it logs compares the misfit against the previous iteration and is a reporting signal only -- returning it here would make the base class discard accepted steps.