The conesDict reference
system/conesDict is where the Data Assimilation problem itself is
configured, on top of the plain OpenFOAM case. This page goes through every
option and ties it back to the EnKF formulation introduced in
Data Assimilation, using the same notation: the extended state
\(\mathbf{x}_k = [\mathbf{u}_k;\, \theta_k]\), observations
\(\mathbf{y}_k\), sampling matrix \(\mathcal{H}(\mathbf{x}_k)\),
anomalies \(\mathbf{X}\), \(\mathbf{S}\), observation covariance
\(\mathbf{R}_k\) and Kalman gain \(\mathbf{K}\).
Algorithm and estimation switches
Key |
Meaning |
|---|---|
|
The DA method. Currently only |
|
Whether the flow state \(\mathbf{u}_k\) is part of the extended
state \(\mathbf{x}_k\) ( |
|
Whether the model parameters \(\theta_k\) are part of
\(\mathbf{x}_k\) ( |
With both switches true, CONES performs the joint state-parameter EnKF
described in Data Assimilation (“Ensemble Kalman Filter with extended
state”): \(\mathbf{x}_k\) stacks the flow state and the parameters, and
a single Kalman gain corrects both at once. With stateEstSwitch false,
\(\mathbf{x}_k\) reduces to \(\theta_k\) alone — a pure parameter
(e.g. boundary condition) calibration against observations, which is the
setting used by the cavity tutorial.
State and observations
Key |
Meaning |
|---|---|
|
The flow variables making up \(\mathbf{u}_k\), as a string of
one-letter codes, e.g. |
|
The observed variables making up \(\mathbf{y}_k\), e.g.
|
|
Path (relative to the case) to the observation netCDF database
(mean and standard deviation of each |
|
Number of model time steps the forecast runs between two analysis
phases. Together with
\[n_{\text{cycles}} = \left\lfloor \frac{\text{endTime}}{
\text{observationWindow} \times \text{deltaT}} \right\rfloor\]
|
Parameter estimation
Only read when paramEstSwitch true; together they define what
\(\theta_k\) actually contains.
Key |
Meaning |
|---|---|
|
Where the parameter lives in the model, e.g. |
|
Which field component is estimated, e.g. |
|
The boundary patch the parameter is read from/written to, e.g.
|
|
Size of \(\theta_k\), i.e. how many scalar parameters are appended to the state. |
Multiple Data Assimilation (MDA)
Key |
Meaning |
|---|---|
|
Number of ES-MDA (Ensemble Smoother with Multiple Data Assimilation) sub-iterations performed at each analysis phase. |
At every sub-iteration, set_R()
inflates the observation covariance by the iteration count \(N_{\text{mda}}\)
(mdaIterations):
and setObs() perturbs the
observations with matching noise scaled by \(\sqrt{N_{\text{mda}}}\), so
that summing the \(1/N_{\text{mda}}\) inflation factors over all
sub-iterations recovers the original \(\mathbf{R}\), per the classic
even-split ES-MDA scheme (Emerick & Reynolds). The Kalman gain and update
are then recomputed for each sub-iteration, using the previous
sub-iteration’s updated \(\mathbf{x}\) as the new background
(update_background()).
mdaIterations 1 is the classic, single-pass EnKF.
Inflation
Ensemble methods systematically underestimate the error covariance (the ensemble collapses over cycles); inflation counteracts this by artificially widening the ensemble spread of the updated state and/or parameters after each analysis.
Key |
Meaning |
|---|---|
|
|
|
Inflation coefficient \(\lambda\) applied to the updated state.
|
|
Inflation coefficient \(\lambda\) applied to the updated
parameters. |
Given an updated ensemble matrix (state or parameters) with per-member
columns \(\mathbf{a}_i\) and ensemble mean \(\bar{\mathbf{a}}\),
calculateInflation()
implements:
Deterministic: every member is inflated by the same factor around the ensemble mean,
\[\mathbf{a}_i \leftarrow \bar{\mathbf{a}} + (1+\lambda)(\mathbf{a}_i - \bar{\mathbf{a}})\]Stochastic: each member gets its own inflation factor, drawn from a normal distribution of mean 0 and standard deviation \(\lambda\), truncated to \([-2\lambda, 2\lambda]\),
\[\mathbf{a}_i \leftarrow (1+\epsilon_i)\, \mathbf{a}_i, \qquad \epsilon_i \sim \mathcal{N}(0, \lambda^2) \big|_{[-2\lambda, 2\lambda]}\]
Localisation: physical regions and covariance tapering
With few ensemble members, sampling noise creates spurious long-range
correlations between the state and observations that have nothing to do
with the real physics. CONES suppresses them in two independent, composable
ways: a hard, physical partition of the domain (via topoSet, not a
conesDict key), and a smooth, distance-based taper of the Kalman gain
inside each partition (stateCovarianceLocalisation below). Both can be
used together, only one, or neither.
Physical localisation (domain decomposition into regions)
This is what the tutorial’s system/topoSetDict sets up,
before CONES itself even starts: each cellSet action defines a region
of the mesh (a “clip”). At start-up, get_clippings()
reads these clips, mergeClips() merges
any that share cells, and one conesRegion
is built per merged clip — keeping only the observations whose cell falls
inside it, and dropping regions left with none.
Each region then runs its own, independent EnKF analysis
(conesEnKF), built from only
that region’s state cells and observations. An observation in one clip has
zero influence on the update anywhere outside it, however close it might
physically be to that clip’s boundary — this is a hard cutoff, decided once
by the mesh topology, not by any conesDict option. If your case has a
single topoSetDict action covering the whole DA domain (or none, when
LHYPER_LOCALISATION_SWITCH is off on the Meso-NH side), there is only
ever one region, and physical localisation plays no role.
State covariance localisation
Key |
Meaning |
|---|---|
|
Whether to taper the Kalman gain with distance ( |
|
Characteristic length \(\ell\) of the localisation, in the same units as the mesh coordinates. |
Where physical localisation draws a hard line between regions, covariance
localisation works within a single region’s analysis: it damps the
Kalman gain continuously with distance, instead of cutting it off, so
state cells close to an observation are still corrected more than distant
ones without discarding either.
setL()
builds a Gaussian weight from the euclidean distance \(d\) between each
state cell and each observation:
so that the correlation has decayed to 1% of its peak value at distance
\(d = \ell\) — localisationLength is the practical “radius of
influence” of each observation. \(L\) is then column-normalized (each
observation’s weights sum to 1) and applied to the update as an
element-wise (Schur/Hadamard) product on the Kalman gain term, rather than
a matrix product:
Parameters are not localised (their weight is 1 for every observation), since they are typically global quantities (e.g. a single boundary condition) rather than spatially distributed ones.