geoprior.utils.subsidence_utils#

Utility helpers for subsidence data, units, and coordinates.

Functions

add_subsidence_mm_columns(df[, cfg, base, ...])

Add (or overwrite) subsidence columns in millimeters.

add_subsidence_native_unit_columns(df[, ...])

Add columns in the cfg-inferred native unit.

clean_gwl_zsurf(df, *[, lon_col, lat_col, ...])

Fix GWL units + robust z_surf, then recompute head_m.

convert_eval_payload_units(payload[, cfg, ...])

Convert GeoPriorSubsNet evaluation-payload units for reporting.

convert_target_units_df(df, *, base[, ...])

Convert target-like columns between "m" and "mm".

cumulative_to_rate(df, *[, cum_col, ...])

Recover a rate series from cumulative displacement.

detect_subsidence_mode(df, *[, rate_col, ...])

Infer whether subsidence columns represent 'rate', 'cumulative', or an inconsistent pair.

finalize_si_affines_and_units(...[, ...])

Prevent double SI conversion in GeoPrior scaling_kwargs.

finalize_si_scaling_kwargs(scaling_kwargs, ...)

Prevent double SI conversion in GeoPrior scaling_kwargs.

infer_utm_epsg_from_lonlat(lon_deg, lat_deg)

Infer a UTM EPSG code from lon/lat (EPSG:4326).

lonlat_to_utm_m(lon_deg, lat_deg, *[, ...])

Convert lon/lat degrees to UTM meters using pyproj.

make_txy_coords(t, x, y, *[, time_shift, ...])

Build coords tensor (t, x, y) with OPTIONAL shifting (translation only).

normalize_gwl_alias(df, gwl_col_user, *[, ...])

Normalize common GWL naming aliases.

postprocess_eval_json(eval_json, *[, cfg, ...])

Post-hoc convert a Stage-2 evaluation JSON from SI to interpretable units.

rate_to_cumulative(df, *[, rate_col, ...])

Build cumulative displacement from a rate series.

resolve_gwl_for_physics(df, gwl_col_user, *)

Pick meters GWL for physics; keep z-score ML-only.

resolve_head_column(df, *, depth_col[, ...])

Resolve a head column, creating one if needed.

subs_native_unit([cfg, default])

Infer the "native" subsidence unit from cfg.

subs_unit_to_si([cfg, default, ...])

Subsidence unit->SI factor (to meters).

Classes

CoordsPack(coords, coord_mins, coord_ranges, ...)

geoprior.utils.subsidence_utils.normalize_gwl_alias(df, gwl_col_user, *, prefer_depth_bgs=True, verbose=True)[source]#

Normalize common GWL naming aliases.

Naming-only: unit conversion happens later.

Parameters:
Return type:

tuple[DataFrame, str | None]

geoprior.utils.subsidence_utils.resolve_gwl_for_physics(df, gwl_col_user, *, prefer_depth_bgs=True, allow_keep_zscore_as_ml=True, verbose=True)[source]#

Pick meters GWL for physics; keep z-score ML-only.

Parameters:
Return type:

tuple[str, str | None]

geoprior.utils.subsidence_utils.resolve_head_column(df, *, depth_col, head_col='head_m', z_surf_col=None, use_head_proxy=True)[source]#

Resolve a head column, creating one if needed.

Parameters:
Return type:

tuple[str, str | None]

geoprior.utils.subsidence_utils.convert_target_units_df(df, *, base, from_unit='m', to_unit='mm', mode='overwrite', suffix='_mm', columns=None, unit_col=None, copy_df=True, overwrite_cols=False, strict=False)[source]#

Convert target-like columns between “m” and “mm”.

Selected columns: - base - base + “_*” (quantiles, intervals, …)

If mode=”add”, new columns use suffix.

Parameters:
Return type:

DataFrame | None

geoprior.utils.subsidence_utils.subs_unit_to_si(cfg=None, *, default=0.001, units_prov_key='units_provenance', stage1_key='subs_unit_to_si_applied_stage1', cfg_key='SUBS_UNIT_TO_SI')[source]#

Subsidence unit->SI factor (to meters).

Priority: 1) cfg[units_prov_key][stage1_key] 2) cfg[cfg_key] 3) default

Parameters:
Return type:

float

geoprior.utils.subsidence_utils.subs_native_unit(cfg=None, *, default='mm')[source]#

Infer the “native” subsidence unit from cfg.

  • unit_to_si ~= 1e-3 -> “mm”

  • unit_to_si ~= 1.0 -> “m”

Parameters:
Return type:

str

geoprior.utils.subsidence_utils.add_subsidence_mm_columns(df, cfg=None, *, base='subsidence', columns=None, suffix='_mm', unit_col=None, copy_df=True, overwrite=False)[source]#

Add (or overwrite) subsidence columns in millimeters.

Always assumes the current values are in meters.

Parameters:
Return type:

DataFrame | None

geoprior.utils.subsidence_utils.add_subsidence_native_unit_columns(df, cfg=None, *, base='subsidence', columns=None, suffix='_native', unit_col=None, copy_df=True, overwrite=False)[source]#

Add columns in the cfg-inferred native unit.

If cfg says the native unit was meters, this becomes a no-op aside from optional unit_col.

Parameters:
Return type:

DataFrame | None

geoprior.utils.subsidence_utils.finalize_si_scaling_kwargs(scaling_kwargs, *, subs_in_si, head_in_si, thickness_in_si, force_identity_affine_if_si=True, warn=True)[source]#

Prevent double SI conversion in GeoPrior scaling_kwargs.

Parameters:
Return type:

dict[str, Any]

geoprior.utils.subsidence_utils.finalize_si_affines_and_units(scaling_kwargs, *, subs_in_si, head_in_si, thickness_in_si, force_identity_affine_if_si=True, warn=True)#

Prevent double SI conversion in GeoPrior scaling_kwargs.

Parameters:
Return type:

dict[str, Any]

geoprior.utils.subsidence_utils.infer_utm_epsg_from_lonlat(lon_deg, lat_deg)[source]#

Infer a UTM EPSG code from lon/lat (EPSG:4326).

Uses standard UTM zoning:

zone = floor((lon + 180)/6) + 1 EPSG = 32600 + zone (north), 32700 + zone (south)

Parameters:
Return type:

int

geoprior.utils.subsidence_utils.lonlat_to_utm_m(lon_deg, lat_deg, *, src_epsg=4326, target_epsg=None)[source]#

Convert lon/lat degrees to UTM meters using pyproj.

Return type:

x_m, y_m, target_epsg

Parameters:
class geoprior.utils.subsidence_utils.CoordsPack(coords: 'np.ndarray', coord_mins: 'dict[str, float]', coord_ranges: 'dict[str, float]', meta: 'dict[str, Any]')[source]#

Bases: object

Parameters:
coords: ndarray#
coord_mins: dict[str, float]#
coord_ranges: dict[str, float]#
meta: dict[str, Any]#
__init__(coords, coord_mins, coord_ranges, meta)#
Parameters:
Return type:

None

geoprior.utils.subsidence_utils.make_txy_coords(t, x, y, *, time_shift='min', xy_shift='min', time_shift_value=None, x_shift_value=None, y_shift_value=None, dtype='float32')[source]#

Build coords tensor (t, x, y) with OPTIONAL shifting (translation only).

This is designed for your “not normalized” workflow:
  • You keep SI units (years and meters),

  • but you avoid feeding huge UTM magnitudes (e.g. 3e5, 2.5e6) into coord MLPs by shifting x,y (and optionally t).

Notes

  • This does NOT min-max scale to [0,1]. It only translates.

  • Returning coord_mins/coord_ranges is still useful for logging/debug.

Parameters:
Return type:

CoordsPack

geoprior.utils.subsidence_utils.detect_subsidence_mode(df, *, rate_col='subsidence', cum_col='subsidence_cum', time_col='year', group_cols=('longitude', 'latitude', 'city'), tol_rel=0.05, min_points=3, max_groups=200, random_state=42)[source]#

Infer whether subsidence columns represent ‘rate’, ‘cumulative’, or an inconsistent pair.

Parameters:
Return type:

dict

geoprior.utils.subsidence_utils.rate_to_cumulative(df, *, rate_col='subsidence', cum_col='subsidence_cum', time_col='year', group_cols=('longitude', 'latitude', 'city'), initial='first_equals_rate_dt', inplace=False)[source]#

Build cumulative displacement from a rate series.

Parameters:
Return type:

DataFrame

geoprior.utils.subsidence_utils.cumulative_to_rate(df, *, cum_col='subsidence_cum', rate_col='subsidence', time_col='year', group_cols=('longitude', 'latitude', 'city'), first='cum_over_dtref', inplace=False)[source]#

Recover a rate series from cumulative displacement.

rate(t_i) = (cum(t_i) - cum(t_{i-1})) / dt_i for i>=1

first:
  • ‘nan’: first rate is NaN

  • ‘cum_over_dtref’: rate(t0) = cum(t0)/dt_ref (dt_ref median dt)

Return type:

DataFrame with rate_col added/overwritten.

Parameters:
geoprior.utils.subsidence_utils.convert_eval_payload_units(payload, cfg=None, *, mode='si', scope='all', savefile=None, fmt='json', indent=2, copy_payload=True)[source]#

Convert GeoPriorSubsNet evaluation-payload units for reporting.

This is a post-processing helper meant for stage-2 evaluation JSON payloads (e.g. geoprior_eval_phys_<timestamp>.json).

Parameters:
  • payload (Mapping[str, Any]) – The evaluation payload dict. It is expected to contain sections like metrics_evaluate, point_metrics, per_horizon, interval_calibration and censor_stratified.

  • cfg (mapping or module, optional) – The experiment config (e.g. config module or globals()). The helper reads SUBS_UNIT_TO_SI (or stage-1 provenance) and TIME_UNITS from this object when available.

  • mode ({"si", "interpretable"}, default "si") – "si" leaves values untouched. "interpretable" converts selected subsidence and physics metrics from SI into the native units implied by SUBS_UNIT_TO_SI.

  • scope ({"all", "subsidence", "physics"}, default "all") – Which parts to convert when mode="interpretable". "subsidence" converts only subsidence metrics such as MAE, MSE, and sharpness to the native unit. "physics" converts only unambiguous physics residual rates, currently epsilon_cons_raw and epsilon_gw_raw. "all" applies both conversions.

  • savefile (str, optional) – If provided, write the converted payload to this path.

  • fmt ({"json"}, default "json") – Output format when savefile is provided.

  • indent (int, default 2) – JSON indentation.

  • copy_payload (bool, default True) – If True, operate on a deep copy of payload. If False, convert in-place (dangerous).

Returns:

Converted payload as a plain dict.

Return type:

dict

Notes

For subsidence metrics, linear quantities such as MAE and sharpness scale by 1 / SUBS_UNIT_TO_SI, while squared quantities such as MSE scale by (1 / SUBS_UNIT_TO_SI) ** 2.

When physics conversion is enabled, epsilon_cons_raw is treated as a rate in m/s and converted to <subs_native_unit>/<TIME_UNITS> (for example mm/yr), while epsilon_gw_raw is treated as a rate in 1/s and converted to 1/<TIME_UNITS>.

The helper records unit provenance under payload["units"].

geoprior.utils.subsidence_utils.clean_gwl_zsurf(df, *, lon_col='longitude', lat_col='latitude', z_col='z_surf', gwl_col='GWL_depth_bgs', gwl_scale='auto', max_depth_m=50.0, z_outlier_iqr_mult=3.0, knn_k=25, return_report=True, savefile=None)[source]#

Fix GWL units + robust z_surf, then recompute head_m.

Parameters:
Return type:

tuple[DataFrame, dict[str, Any] | None, str | None]

geoprior.utils.subsidence_utils.postprocess_eval_json(eval_json, *, cfg=None, scope='all', out_path=None, overwrite=False, add_rmse=True, force=False, indent=2)[source]#

Post-hoc convert a Stage-2 evaluation JSON from SI to interpretable units.

This is a safe wrapper around convert_eval_payload_units(…) that: - loads a JSON from disk (or accepts a payload dict), - infers unit factors from payload[“units”] when cfg is missing, - avoids double conversion unless force=True, - optionally adds RMSE fields (sqrt(MSE)), - writes a converted JSON file if out_path is provided.

Parameters:
  • eval_json (str | Mapping[str, Any]) – Either a file path to the saved JSON, or an in-memory mapping.

  • cfg (Mapping[str, object] | None) – Optional config mapping/module. If missing (or incomplete), this helper will synthesize the minimal keys needed for conversion: - “SUBS_UNIT_TO_SI” - “TIME_UNITS”

  • scope (Literal['all', 'subsidence', 'physics']) – Forwarded to convert_eval_payload_units(…).

  • out_path (str | None) – If given, write the converted payload there. If a directory is given, a filename is generated next to the input name (or “geoprior_eval…”).

  • overwrite (bool) – If False and out_path exists, raise.

  • add_rmse (bool) – If True, add RMSE fields wherever MSE is present (metrics_evaluate, point_metrics, per_horizon).

  • force (bool) – If False, skip conversion when payload already declares an interpretable subsidence unit (e.g., “mm”). If True, always convert.

  • indent (int) – JSON indent used when writing.

Returns:

The converted payload (always returned as a dict).

Return type:

dict