NEDAS.utils.conversion module
- NEDAS.utils.conversion.units_convert(units_from, units_to, var)[source]
Convert units for a given variable.
- Parameters:
units_from (str or numbers.Number) – Source units for the input variable
units_to (str or numbers.Number) – Target units to convert to
var (np.ndarray) – The input variable
- Returns:
Variable with converted units
- Return type:
np.ndarray
- NEDAS.utils.conversion.proj2dict(proj: Proj) dict[source]
Convert map projection name in pyproj.Proj to a dictionary of human-readable parameters
- Parameters:
proj (pyproj.Proj) – Map projection object.
- Returns:
A dictionary of projection parameters, such as
name,lat_0,lon_0, etc.- Return type:
dict
- NEDAS.utils.conversion.t2h(t: datetime) float[source]
Convert datetime object to hours since 1900-1-1 00:00
- NEDAS.utils.conversion.h2t(h: float) datetime[source]
Convert hours since 1900-1-1 00:00 to datetime object
- NEDAS.utils.conversion.t2s(t: datetime) str[source]
Convert datetime object to a time string
'ccyymmddHHMM'
- NEDAS.utils.conversion.s2t(s: str) datetime[source]
Convert a time string
'ccyymmddHHMM'to a datetime object
- NEDAS.utils.conversion.seconds_to_timestr(seconds: int) str[source]
Convert from seconds to time duration string ‘HH:MM:SS’
- NEDAS.utils.conversion.ensure_list(v) list[source]
If the input
vis a list, return itself; if not, return[v].
- NEDAS.utils.conversion.resolve_iter_dict(value, iter: int, niter: int)[source]
Resolve a config value that may be given as a per-outer-loop-iteration override dict, keyed
'iter0','iter1', …,'iter{niter-1}'(optionally plus a'default'key).Detection is purely by type (dict vs. anything else), never by list length or shape – so this can never collide with a value that is legitimately a list (or nested list) for some other, unrelated reason (e.g. a multiscale
hcorrlist for one perturbation variable, which has nothing to do with the outer loop). An earlier design that tried to infer per-iteration intent fromlen(value) == niterwas rejected for exactly this reason – see NEDAS/DA-algorithms.md’s “Outer-loop design” notes.If
valueis not a dict, it is returned unchanged: the same value is used at every outer-loop iteration (today’s behavior, for every existing config).If
valueis a dict, every key must be either'default'or matchiterNwith0 <= N < niter– anything else (e.g. a typo like'itr0', or an out-of-range index) raises immediately rather than being silently ignored. The current iteration’s owniterNkey is used if present; otherwise'default'is used if present; otherwise a ValueError is raised (no silent fallback to an unspecified value).- Parameters:
value – the config value to resolve (any type).
iter (int) – the current outer-loop iteration index.
niter (int) – the total number of outer-loop iterations.
- Returns:
The resolved value for this iteration.
- NEDAS.utils.conversion.is_iter_keyed_dict(value) bool[source]
True if value is a non-empty dict whose keys are ALL either ‘default’ or match ‘iterN’ – i.e. exactly the shape resolve_iter_dict() treats as a per-iteration override, as opposed to an ordinary dict whose keys mean something else entirely.
Needed only where the OLD format for a config value can ALREADY legitimately be a bare dict (e.g. transform_def’s single-transform-spec form, {‘type’: …, ‘decompose_obs’: …}) – there, resolve_iter_dict’s own type-based detection (dict vs. anything else) is not enough on its own, since a dict already has an established meaning at that nesting level; this lets a caller check the dict’s shape first, before deciding whether to run it through resolve_iter_dict at all. Most callers (hroi, err.std, assimilator_def/updator_def’s ‘type’, inflation_def’s ‘type’/’coef’/’adaptive’) never had a legitimate bare-dict old format, so they don’t need this – resolve_iter_dict’s detection is already unambiguous for them.
- Parameters:
value – the config value to check (any type).
- Returns:
True if value should be treated as a per-iteration override dict.
- Return type:
bool
- NEDAS.utils.conversion.expand_scale_dict(value)[source]
If value is a dict keyed
'scale0','scale1', …,'scale{n-1}'(n>=1, no gaps, no stray keys), return the equivalent ORDERED LIST[value['scale0'], value['scale1'], ...].This is an explicit alternative to inferring the number of scales from a bare list’s length (today’s existing convention for one perturbation variable’s own multiscale decomposition, PerturbField’s
nscale) – it EXPANDS a keyed dict into the equivalent positional list every downstream consumer already expects, rather than SELECTING a single entry the wayresolve_iter_dictdoes for the (separate, unrelated) DA-scheme outer loop – perturb’s own multiscale loop needs all nscale components at once (generate_perturb’s ownfor s in range(ns)), not one at a time.If value is not such a dict, it is returned unchanged: a bare scalar (nscale=1) or a bare list (nscale=len(list), today’s existing convention) both keep working exactly as before – introducing this explicit dict form doesn’t create any new ambiguity at this nesting level, since a dict was never a valid value here before.
- Parameters:
value – the config value to expand (any type).
- Returns:
The expanded ordered list if value was a scale-keyed dict, otherwise value unchanged.