Scaling and conventions (scaling_kwargs)#
GeoPrior-v3 treats scaling_kwargs as part of the model
contract, not as optional preprocessing metadata.
This is one of the most important design choices in the project. The scaling payload determines how the model interprets:
subsidence targets,
groundwater variables,
thickness fields,
coordinate order and normalization,
time-unit conversion,
SI conversion,
head-from-depth proxy logic,
residual scaling,
bounds and priors,
and selected training / diagnostics policies.
The current code and model docstrings describe
scaling_kwargs as the single most important
configuration for GeoPrior, precisely because it controls
how raw dataset values become the internal quantities used by
the physics core. If this payload changes, the physics
problem definition itself changes.
This page explains:
why scaling is scientifically central in GeoPrior,
how
GeoPriorScalingConfigworks,what kinds of fields belong in
scaling_kwargs,and how scaling affects derivatives, priors, bounds, diagnostics, and saved-model reuse.
Why scaling is physics-critical#
GeoPrior is not a standard tabular forecaster.
Its physics core differentiates with respect to the forecast coordinate tensor, converts those derivatives into SI-consistent quantities, and assembles residuals for:
groundwater flow,
consolidation relaxation,
timescale consistency,
smoothness,
and bounds.
That means the model is only scientifically meaningful if the coordinate spans, time units, head conventions, and target units are handled consistently. This is especially important for groundwater-driven subsidence, where storage, drawdown, drainage thickness, and delayed compaction must remain physically interpretable [6, 8, 14].
A useful practical rule is:
if scaling is wrong,
the physics branch is wrong
even when the supervised loss still appears to behave well.
A compact summary#
A useful summary is:
raw dataset values
↓
scaling_kwargs defines meaning
↓
GeoPriorScalingConfig resolves a canonical payload
↓
model uses SI-consistent states and residuals
↓
training, inference, and saved-model reuse remain aligned
This is the core role of scaling in GeoPrior.
The scaling configuration object#
GeoPrior wraps the scaling payload in a small Keras-serializable container:
The implementation describes this object as the container that stores and reconstructs the physics scaling and slicing controls used by GeoPriorSubsNet. This matters because a reloaded model must preserve the same coordinate handling, time-unit interpretation, groundwater conventions, bounds, and residual scaling behavior it had during training.
The class supports construction from:
None(empty config),a mapping / dict-like payload,
a filesystem path to a JSON or YAML config file,
or an existing
GeoPriorScalingConfiginstance.
The resolution pipeline#
The scaling object does not assume the incoming payload is already clean.
The current implementation resolves the scaling contract through a four-step pipeline:
load the payload,
canonicalize keys and fill defaults,
enforce alias consistency,
validate values and required fields.
This is important because it makes reloaded models behave the same way as models built from a fresh training run.
A typical usage pattern is:
from geoprior.models.subsidence.scaling import (
GeoPriorScalingConfig,
)
cfg = GeoPriorScalingConfig.from_any(
"path/to/scaling_kwargs.json"
)
scaling_kwargs = cfg.resolve()
That pattern is also the right mental model for the docs: the user may write an initial payload, but the resolved payload is what actually defines the model’s physics meaning.
Where scaling_kwargs enters the workflow#
In the GeoPrior workflow, scaling is not introduced only at model build time. It begins earlier and then persists through the whole staged pipeline.
The older scaling guide already captured the right structure:
Stage-1 writes scaling_kwargs.json as a compact resolved
record of dataset conventions, feature naming, SI conversion
constants, coordinate mode, and physics-control knobs, and
Stage-2 then consumes that payload during training.
In the current codebase, that logic goes further:
the resolved scaling config is wrapped in
GeoPriorScalingConfig;the model stores it as a serialized Keras object in
get_config();from_config()rehydrates it during model reload.
This is why scaling belongs to the scientific foundations section, not only to the user guide.
What scaling_kwargs governs#
The internal doc templates are very explicit about the role of the scaling payload. The resolved payload is used by:
target formatting for subsidence and head,
coordinate handling (order, normalization, SI conversion),
PDE derivative scaling and chain-rule correction,
physics closures such as drawdown rules and head-from-depth proxy logic,
priors and bounds for \(K\), \(S_s\), \(\tau\), and \(H\),
training policies such as warmups, ramps, and diagnostics.
So a good way to think about scaling_kwargs is:
it defines what the data mean,
not only how the data are numerically scaled.
Target scaling and conventions#
The first role of scaling_kwargs is to define how the raw
targets become SI-consistent internal quantities.
Subsidence#
The payload can declare the semantic type of subsidence through a field such as:
subsidence_kindUsually
"cumulative"or"incremental".
The corresponding linear conversion is:
Typical interpretation:
if raw subsidence is stored in millimeters, then
subs_scale_si = 1e-3converts it to meters;if it is already in meters,
subs_scale_si = 1.0is appropriate.
Groundwater / head#
The payload may also include:
head_scale_sihead_bias_si
with the same general form:
This is especially important when the workflow mixes:
already-physical head values,
depth-to-water values later converted into head,
or preprocessed standardized inputs that must be restored before physics is evaluated.
Thickness#
Thickness enters both the equilibrium settlement closure and the timescale prior. The scaling payload therefore also supports:
H_scale_siH_bias_si
so that:
This is a high-impact quantity, because incorrect thickness scaling can distort:
equilibrium settlement,
effective drainage thickness,
and the closure-based timescale prior.
Groundwater semantics and head proxies#
GeoPrior explicitly tracks how groundwater is represented.
The scaling payload may define conventions such as:
gwl_kindgwl_signuse_head_proxy.
This matters because different datasets may store:
hydraulic head directly,
depth to groundwater,
or a head-like quantity reconstructed from surface elevation and groundwater depth.
The model docstring already makes this explicit: one of the key ideas of GeoPrior is to convert GWL to hydraulic head using a scaling configuration before computing physics losses.
A useful conceptual conversion is:
when groundwater is stored as positive downward depth below surface.
The important point for users is not only the formula, but that the formula belongs to the scaling contract rather than to ad hoc hand-written preprocessing.
Drawdown rules#
The compaction side of GeoPrior depends on drawdown rather than only on the raw groundwater variable.
The scaling payload can therefore also control:
drawdown definition,
sign policy,
optional head-reference behavior,
and whether head should be reconstructed from a proxy.
This is one reason scaling_kwargs belongs in the model
config itself: drawdown convention changes the meaning of the
consolidation residual and the equilibrium settlement
closure.
Coordinate handling#
The next major job of scaling is to define the derivative frame.
GeoPrior expects the forecast coordinate tensor to follow a declared order, typically:
["t", "x", "y"]
This order is part of the scaling contract, not a hard-coded
assumption. The current model documentation explicitly states
that the coordinate axis order is expected to be
['t','x','y'] unless overridden by
scaling_kwargs['coord_order'].
A wrong coordinate order may not cause a shape error, but it makes the PDE residuals meaningless.
Normalized coordinates and chain-rule scaling#
One of the most important scaling roles is derivative correction when coordinates are normalized.
The current doc templates describe this explicitly:
if coordinates are normalized and
scale_pde_residuals=True, GeoPrior applies the chain rule
using the coordinate ranges. For example, if
then:
Similarly for the spatial coordinates:
This is one of the clearest examples of why scaling is physics-critical. Raw autodiff operates in model coordinate space, but the residuals must be interpreted in SI-consistent physical space.
The corresponding payload fields typically include:
coords_normalizedcoord_rangescoord_ranges_sicoord_order.
Time units#
Time handling is another central part of the scaling contract.
The current docs for the model parameters define
time_units as the name of the time unit used by the model
coordinates, for example:
"year""month""day""second".
This field influences:
conversion from dataset time steps to SI seconds,
the magnitude of time derivatives in PDE residuals,
and the interpretation of \(\tau\) priors.
A useful practical rule is:
always set
time_unitsexplicitly,even if the training setup feels obvious to you.
A wrong time-unit declaration silently corrupts both:
the groundwater residual,
and the consolidation timescale logic.
Longitude / latitude vs projected coordinates#
The scaling payload also controls whether spatial coordinates are already metric or are still in angular units.
Typical fields include:
coords_in_degreesdeg_to_m_londeg_to_m_lat.
This is essential because a longitude-latitude derivative is not directly a meter-based spatial derivative. If the physics core interprets raw degrees as meters, the spatial PDE terms are distorted by large and site-dependent scaling errors.
A good rule is:
projected coordinates are preferred for spatial PDE interpretation;
if you must use lon/lat, the degree-to-meter conversion must be explicit in the scaling payload.
Residual scaling policy#
GeoPrior also uses the scaling payload to control how residuals themselves are nondimensionalized and audited.
The model docs already highlight:
residual display/scaling,
warmup/ramp gates,
training policies,
and diagnostics
as part of the scaling story.
This is important because the same payload influences both:
the physical meaning of the states and fields,
and the optimization behavior of the physics losses.
In other words, scaling is not only about units. It is also about keeping the loss geometry stable and comparable across runs.
Bounds and priors#
One of the less obvious but very important jobs of
scaling_kwargs is to define priors and plausible bounds.
The internal docs explicitly list priors and bounds for \(K\), \(S_s\), \(\tau\), and \(H\) among the core responsibilities of the payload.
This matters because GeoPrior learns effective physical fields and then regularizes them. Without a stable scaling contract, these fields can become:
numerically unstable,
physically implausible,
or inconsistent across saved and reloaded models.
PoroElasticSubsNet even applies a “fill missing bounds”
policy on scaling_kwargs['bounds'] by default, which
shows how central the payload is to the scientific posture of
the model family.
Forcing-term meaning#
The forcing term \(Q\) in GeoPrior’s groundwater residual also depends on scaling conventions.
The current math helpers normalize the meaning of Q using
a field such as:
Q_kind/q_kind/gw_q_kind
and support meanings such as:
per_volumerecharge_ratehead_rate.
The point is not that every user must expose a learnable forcing term. The point is that when forcing is present, its units and semantics must be declared explicitly, because the groundwater residual expects an SI-consistent source term.
Serialization and reproducibility#
One of the strongest reasons GeoPrior uses a dedicated scaling object is saved-model reproducibility.
The current model serialization logic stores
scaling_kwargs as a serialized Keras object representing
the validated scaling configuration, and the model docs state
explicitly that this preserves the exact conventions
(units, coordinate normalization, bounds) used during
training and is critical for consistent inference.
This means a saved GeoPrior model is not fully specified by:
architecture weights,
loss settings,
and optimizer state
alone.
It also depends on the resolved scaling payload.
A useful rule is:
Best practice
Save the resolved scaling payload alongside all serious model artifacts.
If two runs differ unexpectedly across sites, the first thing to compare is often the resolved scaling configuration.
Why the scaling payload is JSON-safe#
The scaling helper includes defensive serialization through
_jsonify(...), which converts nested objects, tuples,
sets, and NumPy scalars into stable JSON-safe Python types.
This may look like an engineering detail, but it is scientifically important: reproducibility is weaker if the saved scaling contract depends on non-deterministic or non-serializable objects.
A minimal example#
A compact test-style payload already used in the current GeoPrior code looks like this:
scaling_kwargs = {
"time_units": "year",
"gwl_kind": "head",
"gwl_sign": "up_positive",
"use_head_proxy": False,
"subsidence_kind": "cumulative",
"coords_normalized": False,
"track_aux_metrics": False,
"allow_subs_residual": False,
}
This is intentionally small, but it already shows the two main roles of the payload:
unit / meaning declaration,
physics policy declaration.
A richer payload may also include coordinate ranges, bounds, feature indices, SI conversion factors, and diagnostics settings.
Common mistakes#
Treating scaling as optional
In GeoPrior, it is not optional. It defines the model’s scientific interpretation.
Using normalized coordinates without range metadata
This breaks the chain-rule conversion needed for PDE derivatives.
Forgetting to declare time units
This silently distorts time derivatives and timescale priors.
Confusing head with depth-to-water
This changes the meaning of drawdown and therefore the consolidation closure.
Saving a model without its resolved scaling payload
This makes later inference or transfer much harder to trust.
A practical checklist#
Before trusting a GeoPrior run, confirm the following:
coord_ordermatches the actual coordinate tensor;coords_normalizedand coordinate spans are correct;time_unitsmatches the time axis meaning;subs_scale_siconverts subsidence to meters;head / groundwater semantics are explicit and correct;
thickness scaling is correct and in meters internally;
bounds and priors reflect the intended scientific regime;
the saved model stores the same resolved scaling payload that was used during training.
If those items are correct, then later instability is more likely to reflect:
optimization difficulty,
residual stiffness,
or identifiability issues
rather than a basic contract error.
A compact scaling map#
The GeoPrior scaling story can be summarized as:
scaling_kwargs
├── target conventions
│ subsidence, head, thickness
├── coordinate conventions
│ order, normalization, SI conversion
├── groundwater semantics
│ head, depth, drawdown, proxy logic
├── priors and bounds
│ K, Ss, tau, H, closure controls
├── residual policies
│ derivative scaling, diagnostics, warmups
└── serialization
preserved through GeoPriorScalingConfig
Relationship to the rest of the docs#
This page explains what scaling controls.
The companion pages explain related parts of the story:
Data, units, and conventions explains the variable meanings and dataset contract;
Physics formulation explains the governing equations and closures;
Residual assembly explains how scaled quantities become residual maps and losses;
Losses and training explains how those scaled residuals affect optimization;
Identifiability explains how scaling and bounds interact with parameter trade-offs.