NEDAS.assim_tools.updators.alignment package

class NEDAS.assim_tools.updators.alignment.AlignmentUpdator(c: Context)[source]

Bases: Updator

Updator class with alignment technique.

When interp_displaced_fields=False (default): displacement is applied by physically moving the model grid coordinates (Lagrangian, via model.displace if available).

When interp_displaced_fields=True: displacement is applied by interpolating each field to the displaced grid positions without moving grid points themselves.

For a vector-valued variable (is_vector=True, e.g. a (u,v) wind field with no separate scalar diagnostic to align on): optical flow itself only operates on scalar images, so compute_increment derives displacement from a scalar image of the field rather than passing the raw (2,ny,nx) vector array to the optical-flow solver – the resulting single displacement field is then applied to both vector components identically (update_files already handled this correctly; only the displacement derivation was vector-unaware). The scalar image is selectable via the updator_def ‘vector_image’ option: ‘vorticity’ (default – a compact single-signed monopole at a vortex core, the sharpest feature to track), ‘speed’ (|V| – for a vortex an annulus with a central minimum, weaker aperture properties but no differencing noise), or ‘deformation’ (total deformation sqrt(stretching^2+shearing^2) – highlights fronts/shear zones rather than rotation).

displace = {}
compute_increment(c: Context)[source]

Compute optical flows from the prior/posterior state variable field pair.

update_files(c, mem_id, rec_id)[source]

Apply displacement to model state variables.

See Ying 2019 for details on the alignment technique.

Ying (2019)’s design keeps scale components in separate arrays: the current scale’s own posterior is finalized directly (no warp), and the displacement derived from it is applied only to the not-yet-processed finer-scale remainder, so already-finalized coarser scales are never re-displaced by a later iteration’s (independently, noisily estimated) displacement.

NEDAS stores only one recombined array per field, so var_prior (read below) already mixes finalized coarser scales, the current scale’s old value, and the not-yet-processed remainder together. To reproduce Ying (2019)’s bookkeeping without a separate per-scale storage, we reconstruct the split on the analysis grid (bands are additive and telescope, see utils/multiscale.py), subtract it out at native model resolution to isolate the remainder, warp only that remainder, and leave the finalized bands untouched.

This frozen/remainder/splice-fld_post scheme only means something when this iteration is genuinely part of a spectral decomposition, i.e. transform_def uses scale_bandpass this iteration (see _scale_bandpass_character_length). When it isn’t (e.g. transform_def: identity, as in a non-multiscale 2-step align-then- filter scheme), fld_post may not be trustworthy analyzed content to splice in at all (e.g. InterpolationAssimilator’s obs-only registration target) – in that case the full native prior is warped directly and fld_post’s own values never enter var_post; only its role (via compute_increment’s optical flow) in deriving displace matters.

Submodules