NEDAS.assim_tools.updators.alignment package
- class NEDAS.assim_tools.updators.alignment.AlignmentUpdator(c: Context)[source]
Bases:
UpdatorUpdator 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.