API Reference¶
Primary solvers¶
Both solvers follow the same lifecycle:
flex.initialize()
flex.run()
w = flex.w # read deflection before finalize clears it
flex.output() # optional: save to file or display plots
flex.finalize() # releases w, qs, and the coefficient matrix
Warning
finalize() deletes w, qs, and the cached coefficient
matrix. Read w (and call output() if
needed) before calling finalize. Accessing w afterwards
raises AttributeError.
- class gflex.F2D(filename=None)[source]¶
Bases:
FlexureTwo-dimensional lithospheric flexure solver.
Computes the deflection w(x, y) of a thin elastic plate overlying an inviscid fluid (mantle) given a surface load stress qs. Supports spatially variable elastic thickness Te.
Set instance attributes, then call
initialize(),run(), andfinalize()in sequence. Readflex.wbefore callingfinalize(); finalize clears all model state includingw.- method¶
Solution method.
'fd'(finite difference, supports variable Te),'fft'(spectral, requires scalar Te; 2-D only),'sas'(superposition of analytical solutions, constant Te only), or'sas_ng'(SAS on an ungridded point cloud).- Type:
- rho_fill¶
Infill material density [kg m⁻³] (0 for air, ~1000 for water, ~2700 for rock).
- Type:
- T_e¶
Elastic thickness [m]. A scalar is broadcast to the full grid.
- Type:
float or ndarray of shape (M, N)
- qs¶
Surface load stress [Pa].
- Type:
ndarray of shape (M, N)
- bc_west, bc_east, bc_north, bc_south
Boundary conditions on the west, east, north, and south edges. FD options:
'zero_displacement_zero_slope'(alias'clamped'),'zero_displacement_zero_moment'(alias'pinned'),'zero_moment_zero_shear'(alias'free'),'zero_slope_zero_shear'(alias'mirror'),'periodic','no_outside_loads'(auto-pad by one flexural wavelength and apply'zero_displacement_zero_slope'at the new outer edges;self.wis trimmed to the original domain). SAS option:'no_outside_loads'(the default when unset). FFT: each opposite pair (west/east, north/south) is treated independently — set both sides of a pair to'periodic'for exact periodicity along that axis, or leave them unset (or set to'no_outside_loads') to zero-pad that axis. Mixing periodic and non-periodic axes is valid. Setting only one side of a pair to'periodic'raises aUserWarningand falls back to zero-padding for that axis.- Type:
- sigma_xx¶
Normal in-plane stress in the x-direction \(\sigma_{xx}\) [Pa]. Supported by
fdandfft. Default0.- Type:
float, optional
- sigma_yy¶
Normal in-plane stress in the y-direction \(\sigma_{yy}\) [Pa]. Supported by
fdandfft. Default0.- Type:
float, optional
- sigma_xy¶
In-plane shear stress \(\sigma_{xy}\) [Pa]. Supported by
fdandfft. Default0.- Type:
float, optional
- fft_pad_n_alpha¶
Number of 2-D flexural-parameter units (α₂D = (D/Δρg)^0.25) to zero-pad on each side for non-periodic FFT runs. Periodic images of the load are separated by 2 ×
fft_pad_n_alpha× α₂D. Default4(8α₂D total separation). Ignored whenmethod != 'fft'or when both sides of the relevant axis are'periodic'.
- cache_factorization¶
Controls LU factorisation caching for the FD
'direct'solver.False(default) — re-factorises on everyrun()call.True— caches the LU factorisation and reuses it (the coefficient matrix is freed once the factors are built). Reuse is safe because smart invalidation clears the cache automatically whenT_e,dx,dy, boundary conditions, or physical parameters are reassigned, and array inputs are read-only (in-place edits raise)."no_check"is a deprecated alias forTrue. Ignored whenmethod != 'fd'.- Type:
Examples
Minimal finite-difference run:
import numpy as np from gflex import F2D flex = F2D() flex.quiet = True flex.method = 'fd' flex.solver = 'direct' flex.g = 9.8 flex.E = 65e9 flex.nu = 0.25 flex.rho_m = 3300. flex.rho_fill = 0. flex.T_e = 30e3 * np.ones((50, 50)) flex.qs = np.zeros((50, 50)) flex.qs[20:30, 20:30] = 1e6 # 10 × 10 cell load flex.dx = flex.dy = 5000. # 5 km grid flex.bc_west = flex.bc_east = flex.bc_south = flex.bc_north = 'zero_moment_zero_shear' flex.initialize() flex.run() flex.finalize() deflection = flex.w # (50, 50) array, negative downward
- initialize(filename=None)[source]¶
Validate inputs and prepare the solver.
Must be called once before
run(). If a configuration-file path was passed to the constructor (or to this method), parameters are read from that file; otherwise they are taken from the instance attributes set by the caller.- Parameters:
filename (str, optional) – Path to a gFlex YAML configuration file. Overrides any filename supplied to the constructor.
- run()[source]¶
Execute the flexural solution.
Selects and runs the method specified by
self.method. The deflection array is stored inself.won return. Callfinalize()afterwards to restore any internally modified state.For repeated solves (e.g. a coupling loop), set
cache_factorization = Truebeforeinitialize()to reuse the LU factorisation when onlyqschanges.
- class gflex.F1D(filename=None)[source]¶
Bases:
FlexureOne-dimensional lithospheric flexure solver.
Computes the deflection w(x) of a thin elastic beam overlying an inviscid fluid (mantle) given a surface load stress qs. Supports spatially variable elastic thickness Te.
Set instance attributes, then call
initialize(),run(), andfinalize()in sequence. Readflex.wbefore callingfinalize(); finalize clears all model state includingw.- method¶
Solution method.
'fd'(finite difference, supports variable Te),'fft'(spectral, requires scalar Te),'sas'(superposition of analytical solutions, constant Te only), or'sas_ng'(SAS on an ungridded point array).- Type:
- rho_fill¶
Infill material density [kg m⁻³] (0 for air, ~1000 for water, ~2700 for rock).
- Type:
- T_e¶
Elastic thickness [m]. A scalar is broadcast to the full grid.
- Type:
float or ndarray of shape (N,)
- qs¶
Surface load stress [Pa].
- Type:
ndarray of shape (N,)
- bc_west, bc_east
Boundary conditions on the west (left) and east (right) ends. FD options:
'zero_displacement_zero_slope'(alias'clamped'),'zero_displacement_zero_moment'(alias'pinned'),'zero_moment_zero_shear'(alias'free'),'zero_slope_zero_shear'(alias'mirror'),'periodic','no_outside_loads'(auto-pad by one flexural wavelength and apply'zero_displacement_zero_slope'at the new outer edge;self.wis trimmed to the original domain),'sandbox'. SAS option:'no_outside_loads'(the default when unset). FFT: set both to'periodic'for exact periodic behavior; any other value (including unset) uses zero-padding to approximate'no_outside_loads'. Setting only one to'periodic'raises aUserWarningand falls back to zero-padding.- Type:
- fft_pad_n_alpha¶
Number of 1-D flexural-parameter units (α₁D = (4D/Δρg)^0.25) to zero-pad on each side for non-periodic FFT runs. Periodic images of the load are separated by 2 ×
fft_pad_n_alpha× α₁D. Default4(8α₁D total separation). Ignored whenmethod != 'fft'or when all BCs are'periodic'.
- cache_factorization¶
Controls LU factorisation caching for the FD
'direct'solver.False(default) — re-factorises on everyrun()call.True— caches the LU factorisation and reuses it (the coefficient matrix is freed once the factors are built). Reuse is safe because smart invalidation clears the cache automatically whenT_e,dx, boundary conditions, or physical parameters are reassigned, and array inputs are read-only (in-place edits raise)."no_check"is a deprecated alias forTrue. Ignored whenmethod != 'fd'.- Type:
Examples
Minimal finite-difference run:
import numpy as np from gflex import F1D flex = F1D() flex.quiet = True flex.method = 'fd' flex.solver = 'direct' flex.g = 9.8 flex.E = 65e9 flex.nu = 0.25 flex.rho_m = 3300. flex.rho_fill = 1000. flex.T_e = 30e3 flex.qs = np.zeros(300) flex.qs[100:200] = 1e6 # 100-cell load flex.dx = 4000. # 4 km grid flex.bc_west = 'zero_displacement_zero_slope' flex.bc_east = 'zero_moment_zero_shear' flex.initialize() flex.run() flex.finalize() deflection = flex.w # (300,) array, negative downward
- initialize(filename=None)[source]¶
Validate inputs and prepare the solver.
Must be called once before
run(). If a configuration-file path was passed to the constructor (or to this method), parameters are read from that file; otherwise they are taken from the instance attributes set by the caller.- Parameters:
filename (str, optional) – Path to a gFlex YAML configuration file. Overrides any filename supplied to the constructor.
- run()[source]¶
Execute the flexural solution.
Selects and runs the method specified by
self.method. The deflection array is stored inself.won return. Callfinalize()afterwards to restore any internally modified state.For repeated solves (e.g. a coupling loop), set
cache_factorization = Truebeforeinitialize()to reuse the LU factorisation when onlyqschanges.
Boundary-condition properties¶
Added in version 2.0.0.
The boundary conditions are set as properties on the solver instance before
calling initialize(). Each accepts a canonical BC string, a short
alias, or a dict for inhomogeneous (prescribed-value) conditions:
flex.bc_west = "zero_displacement_zero_slope" # or "clamped"
flex.bc_east = "zero_moment_zero_shear" # or "free"
flex.bc_south = "zero_slope_zero_shear" # or "mirror"
flex.bc_north = {"displacement": w_arr, "slope": dw_arr}
All valid strings are listed in gflex.VALID_BC_STRINGS_1D and
gflex.VALID_BC_STRINGS_2D.
Output¶
- Flexure.output()[source]¶
Save deflection to file and/or plot, based on optional attributes.
Does nothing if neither
w_out_filenorplot_choicehas been set. Setw_out_fileto a path ending in'.npy'for a binary NumPy array, or any other extension for an ASCII grid. Setplot_choiceto'q','w','both', or (1D only)'combo'to display plots.
In-plane stresses¶
Added in version 1.4.0.
In-plane stresses are set as attributes directly on the solver instance
before calling initialize(). They are not available as configuration
file keys.
Attribute |
Solvers |
Description |
|---|---|---|
|
FD, FFT (1-D and 2-D) |
Normal stress in the x-direction \(\sigma_{xx}\) [Pa].
Default |
|
FD, FFT (2-D only) |
Normal stress in the y-direction \(\sigma_{yy}\) [Pa].
Default |
|
FD, FFT (2-D only) |
Shear stress \(\sigma_{xy}\) [Pa]. Default |
All three default to zero if not assigned; setting any of them with
SAS or SAS_NG raises a RuntimeWarning and has no effect.
See Theory and Numerics for the governing equations that include these terms.
Domain-padding utilities¶
These functions help when running F1D or F2D
with a spatially variable elastic thickness grid. A smooth padding zone
reduces spurious deflections at the domain boundary caused by sharp rigidity
gradients, and ensures that the flexural forebulge can develop freely before
reaching the boundary.
All-in-one helper
pad_domain() handles both 1-D and 2-D grids and both scalar
and array elastic thickness. It calls the lower-level helpers below and
is the recommended starting point.
Added in version 1.4.0: 1-D support (dispatching on qs.ndim) added in 2.0.0.
- gflex.pad_domain(Te, qs, dx, dy=None, n_wavelengths=1.0, Te_out=None, E=65000000000.0, nu=0.25, rho_m=3300.0, rho_fill=0.0, g=9.8)[source]¶
Pad a flexure domain for use with
F1DorF2D.Dispatches to a 1-D or 2-D implementation based on the shape of qs. For array Te, the padding region is tapered from the inner-domain edge values toward Te_out to avoid an abrupt rigidity step. For scalar Te, only the load array is zero-padded; Te is returned unchanged as a float.
The returned pad width p can be used to trim the deflection output after the run:
# 2-D w_inner = flex.w[p:-p, p:-p] # 1-D w_inner = flex.w[p:-p]
- Parameters:
Te (scalar or array) – Elastic thickness [m]. A scalar is broadcast to the full padded grid internally by
F1D/F2D; no tapering is applied. A 1-D array is expected when qs is 1-D; a 2-D array when qs is 2-D.qs (1-D or 2-D array) – Surface load [Pa] for the inner domain. Shape determines whether 1-D or 2-D padding is applied.
dx (float) – Grid cell size [m]. For 2-D grids, this is the x-direction spacing.
dy (float, optional) – Grid cell size in the y-direction [m]. 2-D only; ignored for 1-D. Defaults to dx.
n_wavelengths (float, optional) – Padding width expressed as a number of flexural wavelengths. Default 1.0; use 0.5 for a less conservative (narrower) padding.
Te_out (float, optional) – Te value at the outer edge of the padding region. Only used for array Te; defaults to
Te.mean().E (float, optional) – Young’s modulus [Pa]. Default 65 GPa.
nu (float, optional) – Poisson’s ratio. Default 0.25.
rho_m (float, optional) – Mantle density [kg m^-3]. Default 3300.
rho_fill (float, optional) – Infill density [kg m^-3]. Default 0 (air).
g (float, optional) – Gravitational acceleration [m s^-2]. Default 9.8.
- Returns:
Te_padded (float or array) – Elastic thickness for the padded domain. Float when Te is scalar; array of shape
(len(qs) + 2p,)(1-D) or(M+2p, N+2p)(2-D) when Te is an array.qs_padded (array) – Surface load zero-padded to the padded domain shape.
p (int) – Pad width in grid cells (same on both ends / all four sides).
Examples
2-D scalar Te:
>>> import numpy as np >>> from gflex import pad_domain >>> qs = np.zeros((5, 5)) >>> Te_pad, qs_pad, p = pad_domain(10e3, qs, dx=10000., ... E=65e9, nu=0.25, rho_m=3300., rho_fill=0., g=9.8) >>> p 13 >>> qs_pad.shape (31, 31) >>> Te_pad # scalar returned unchanged 10000.0
2-D array Te:
>>> Te = 10e3 * np.ones((5, 5)) >>> Te_pad, qs_pad, p = pad_domain(Te, qs, dx=10000., ... E=65e9, nu=0.25, rho_m=3300., rho_fill=0., g=9.8) >>> Te_pad.shape (31, 31)
1-D:
>>> qs_1d = np.zeros(5) >>> Te_pad, qs_pad, p = pad_domain(10e3, qs_1d, dx=10000.) >>> qs_pad.shape (43,)
Lower-level building blocks
These two pairs of functions are the building blocks used by
pad_domain(). Use them directly when you need finer control —
for example, to compute the pad width once and apply it to multiple arrays,
or to inspect the tapered Te grid before running the solver.
Recommended pad width (number of cells):
Added in version 1.4.0.
- gflex.recommended_pad_width(Te, dx, E=65000000000.0, nu=0.25, rho_m=3300.0, rho_fill=0.0, g=9.8, n_wavelengths=1.0)[source]¶
Return the recommended padding width in grid cells for a variable-Te run.
The padded domain boundary should be at least one flexural wavelength from the load so that the plate’s response to the load is negligible at the boundary. Half a wavelength is often sufficient in practice.
The 2-D flexural wavelength is computed from the maximum Te value via
flexural_wavelengths(), giving the most conservative (widest) padding estimate.- Parameters:
Te (scalar or 2-D array) – Elastic thickness [m]. The maximum value is used.
dx (float) – Grid cell size [m]. Use the smaller of dx and dy if they differ.
E (float, optional) – Young’s modulus [Pa]. Default 65 GPa.
nu (float, optional) – Poisson’s ratio. Default 0.25.
rho_m (float, optional) – Mantle density [kg m^-3]. Default 3300.
rho_fill (float, optional) – Infill density [kg m^-3]. Default 0 (air).
g (float, optional) – Gravitational acceleration [m s^-2]. Default 9.8.
n_wavelengths (float, optional) – Number of flexural wavelengths to use as the padding width. Default 1.0. Use 0.5 for a less conservative estimate.
- Returns:
Recommended padding width in grid cells.
- Return type:
- gflex.recommended_pad_width_1d(Te, dx, E=65000000000.0, nu=0.25, rho_m=3300.0, rho_fill=0.0, g=9.8, n_wavelengths=1.0)[source]¶
Return the recommended padding width in grid cells for a 1-D variable-Te run.
The padded domain boundary should be at least one 1-D flexural wavelength from the load so that the plate’s response is negligible at the boundary.
The 1-D flexural wavelength is computed from the maximum Te value, giving the most conservative (widest) padding estimate.
- Parameters:
Te (scalar or 1-D array) – Elastic thickness [m]. The maximum value is used.
dx (float) – Grid cell size [m].
E (float, optional) – Young’s modulus [Pa]. Default 65 GPa.
nu (float, optional) – Poisson’s ratio. Default 0.25.
rho_m (float, optional) – Mantle density [kg m^-3]. Default 3300.
rho_fill (float, optional) – Infill density [kg m^-3]. Default 0 (air).
g (float, optional) – Gravitational acceleration [m s^-2]. Default 9.8.
n_wavelengths (float, optional) – Number of flexural wavelengths to use as the padding width. Default 1.0. Use 0.5 for a less conservative estimate.
- Returns:
Recommended padding width in grid cells.
- Return type:
Examples
>>> recommended_pad_width_1d(Te=35e3, dx=5000.) 94
Smooth Te taper (extends a Te array into the padding zone):
Added in version 1.4.0.
- gflex.smooth_pad_Te(Te, pad_width, Te_out=None)[source]¶
Pad a 2-D elastic thickness array with a smooth linear taper.
When a spatially variable Te grid is padded with a constant value before being passed to
F2D, the abrupt step in flexural rigidity D at the inner/outer boundary drives spurious deflections via the D-derivative terms in the vWC1994 stencil (issue #45).This function eliminates that step by linearly blending the inner-domain edge values toward Te_out across the padding ring, reducing the rigidity gradient at the inner/outer boundary by a factor of ~pad_width compared with an abrupt step.
The corresponding surface load array should be padded with zeros, e.g.:
qs_padded = numpy.pad(qs, pad_width, mode='constant')
- Parameters:
- Returns:
Te_padded – Padded elastic thickness with a smooth linear taper.
- Return type:
(M + 2*pad_width, N + 2*pad_width) array
- gflex.smooth_pad_Te_1d(Te, pad_width, Te_out=None)[source]¶
Pad a 1-D elastic thickness array with a smooth linear taper.
When a spatially variable Te array is padded with a constant value before being passed to
F1D, the abrupt step in flexural rigidity D at the inner/outer boundary drives spurious deflections via the D-derivative terms in the FD stencil.This function eliminates that step by linearly blending the inner-domain edge values toward Te_out across the padding region.
The corresponding surface load array should be padded with zeros, e.g.:
qs_padded = numpy.pad(qs, pad_width, mode='constant')
- Parameters:
- Returns:
Te_padded – Padded elastic thickness with a smooth linear taper.
- Return type:
1-D array of length
len(Te) + 2 * pad_width
FD boundary-condition warnings¶
When running F1D or F2D with the finite-difference
solver, gFlex issues UserWarning messages for two categories of potentially
problematic boundary conditions.
BC-type warnings fire whenever a side carries a BC whose physical interpretation deserves verification:
'zero_moment_zero_shear'(alias'free') — assumes a free broken plate end (zero moment and shear force). Physically appropriate for rifted or passive continental margins, subduction trenches with an applied edge load (slab pull), and broken-plate flexure (Turcotte & Schubert). Often applied uncritically elsewhere in the literature — verify that one of these settings applies.
Proximity warnings fire for 'zero_displacement_zero_slope' (alias 'clamped') boundaries when the
nearest loaded cell is within one flexural wavelength
(\(\lambda = 2\pi\alpha\), where
\(\alpha = (4D / \Delta\rho g)^{1/4}\))
of the boundary. Within this distance the flexural forebulge — which peaks at
\(\approx \pi\alpha\) from the load — will be suppressed by the zero-displacement
condition, contaminating the solution. The warning message reports the distance as
a fraction of the local flexural wavelength and directs you to the domain-padding
utilities.
Warning deduplication in model-coupling loops
Python’s default warning filter shows each unique warning once per call site per interpreter session. In a time-stepping or iterative-coupling loop such as:
for load in loads:
flex.qs = load
flex.run() # warning fires on first iteration, silenced thereafter
the proximity warning fires on the first iteration and is not repeated, even if the load subsequently moves closer to the boundary. To re-enable the warning on every call:
import warnings
warnings.filterwarnings("always", category=UserWarning, module="gflex")
Suppressing warnings you have verified
Once you have confirmed that a boundary condition is appropriate for your setup, suppress the corresponding warning by message text:
import warnings
warnings.filterwarnings("ignore", message=".*zero_moment_zero_shear.*")
To suppress all gFlex warnings:
warnings.filterwarnings("ignore", module="gflex")
Or use a context manager for a single run:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
flex.run()
LU factorization cache¶
Added in version 2.0.0.
For coupling workflows that call run() (or
run()) repeatedly with the same grid, elastic thickness,
and boundary conditions, the sparse-LU factorization of the coefficient
matrix can be cached to avoid re-factorizing on every call. Set the
attribute before calling initialize():
Value |
Behaviour |
|---|---|
|
No caching. The matrix is factorized on every |
|
Cache the LU factorization and reuse it on every call. The coefficient matrix is freed from memory immediately after factorization; only the LU factors are retained. Reuse is safe because smart invalidation (see below) clears the cache when a matrix-determining input is reassigned, and array inputs are read-only, so the cached factorization can never silently desynchronise. |
|
Deprecated alias for |
Smart invalidation
Reassigning any matrix-determining attribute — T_e, E, nu,
g, rho_m, rho_fill, dx, dy, boundary conditions, or
in-plane stresses — automatically clears the cached coefficient matrix and
LU factorization. No explicit cache management is needed between solves
when only qs changes.
Note
Smart invalidation is triggered by assignment (flex.T_e = new_array).
Because the cache is keyed to these values, the matrix-determining array
inputs (T_e, sigma_xx, sigma_yy, sigma_xy) are stored
read-only, so an in-place edit raises ValueError rather than
silently leaving the cache stale. To change a value, reassign the whole
array:
te = flex.T_e.copy()
te[5] = 40e3
flex.T_e = te # triggers invalidation
Example (coupling loop):
flex.cache_factorization = True
flex.initialize()
for load in load_sequence:
flex.qs = load
flex.run()
w = flex.w
# ... process w ...
flex.finalize()
The cache is cleared by finalize().
Unpaired periodic boundaries¶
Added in version 2.0.0.
For the finite-difference solver, a one-sided periodic boundary — 'periodic'
on only one side of an opposite pair — is not well-posed, and
run() raises a ValueError. Set
allow_unpaired_periodic = True to override the guard and solve anyway;
enabling it emits a one-time UserWarning that the safety check is disabled.
Defaults to False.
Timing attributes¶
Added in version 2.0.0.
After each run() call, the following read-only
attributes report wall-clock times measured with time.perf_counter():
Attribute |
Solvers |
Description |
|---|---|---|
|
All |
Total solve wall time [s] from the start of |
|
FD only |
Time [s] to construct the sparse coefficient matrix. Not set for
SAS or FFT solvers. In a coupling loop with
|
|
FD only |
Time [s] for the LU backsolve (triangular solve). Not set for SAS or FFT solvers. |
coeff_creation_time and linear_solve_time together account for most
of time_to_solve; the remainder is boundary-condition setup and array
housekeeping. Both are most useful in coupling loops:
coeff_creation_time confirms the coefficient matrix was reused (value ≈ 0)
and linear_solve_time shows the marginal cost per run() call.
Flexural wavelengths¶
Added in version 1.4.0.
- gflex.flexural_wavelengths(Te, E, nu, rho_m, rho_fill, g)[source]¶
Compute flexural parameters and wavelengths for a thin elastic plate.
- Parameters:
Te (float or ndarray of float) – Elastic thickness [m]. Arrays are supported and return arrays of the same shape for every output key (used, e.g., by the per-cell load-proximity guard).
E (float) – Young’s modulus [Pa].
nu (float) – Poisson’s ratio.
rho_m (float) – Mantle density [kg m^-3].
rho_fill (float) – Infill density [kg m^-3] (e.g. 0 for air, 1000 for water).
g (float) – Gravitational acceleration [m s^-2].
- Returns:
Keys
alpha_1D,lambda_1D,zero_crossing_1D,alpha_2D,lambda_2D,zero_crossing_2D— all in metres.- Return type:
Examples
>>> from gflex import flexural_wavelengths >>> r = flexural_wavelengths(Te=30e3, E=65e9, nu=0.25, ... rho_m=3300., rho_fill=0., g=9.8) >>> round(r["alpha_2D"] / 1e3, 1) # km 46.9 >>> round(r["lambda_1D"] / r["lambda_2D"], 6) 1.414214
Coupling guide¶
gFlex is material-agnostic: it receives a surface-normal stress [Pa] and returns a deflection [m], regardless of whether the load comes from ice, water, sediment, volcanic edifices, or any combination. The caller is responsible for converting source-specific quantities into Pa before passing them to gFlex.
Load conversion
# Glacial isostasy
qs = rho_ice * g * ice_thickness # kg m⁻³ × m s⁻² × m → Pa
# Sediment or volcanic load
qs = rho_sediment * g * sediment_thickness
# Multiple sources: sum them
qs = rho_ice * g * h_ice + rho_sed * g * h_sed + rho_water * g * h_water
The rho_fill parameter should be set to the density of the material that
replaces the load inside the flexural depression — rho_fill=0 for
subaerially exposed basins, rho_fill=1030 for submarine basins,
rho_fill=2000–2700 for sediment-filled basins.
Applying deflection to topography
gFlex returns the total instantaneous deflection, not an increment. In a time-stepping loop, apply only the change to topography:
flex.qs = qs
flex.run()
w_new = flex.w
topo += w_new - w_prev
w_prev = w_new.copy()
QGIS Processing provider
For GIS-based workflows where load and elastic-thickness data are already rasters, processing_gflex exposes gFlex as a no-code algorithm in the QGIS Processing Toolbox and Graphical Modeler. It supports all 2-D solution methods (FD, FFT, SAS), variable or scalar \(T_e\), all boundary conditions, and in-plane stresses. Installation:
pip install "gflex>=2.0.0" # auto-installed by the plugin on first use
Requires QGIS ≥ 3.16. Usable from the Toolbox, Graphical Modeler, or
headlessly via processing.run().
BMI interface¶
BmiGflex exposes the CSDMS Basic Model Interface, enabling
gFlex to be coupled with other models in the CSDMS framework. It requires
the optional bmipy dependency (pip install gflex[bmi]).
BMI variables
Grid 0 — spatial flexure grid:
Name |
Direction |
Units |
Description |
|---|---|---|---|
|
input |
Pa |
Surface-normal load stress \(q_s\). Material-agnostic: convert
ice, water, sediment, etc. to Pa before calling |
|
input |
m |
Elastic thickness \(T_e\). Usually set once at initialisation;
updating it between |
|
output |
m |
Deflection \(w\) (downward negative). |
Grid 1 — scalar physical constants, exposed as the BMI 'scalar' grid type
(rank 0) and read or written as single-element arrays:
Name |
Direction |
Units |
Description |
|---|---|---|---|
|
input |
Pa |
Young’s modulus \(E\). |
|
input |
1 |
Poisson’s ratio \(\nu\) (dimensionless). |
|
input |
kg m⁻³ |
Mantle density \(\rho_m\). |
|
input |
kg m⁻³ |
Infill material density \(\rho_\text{fill}\). The only constant with a runtime-update use case: a basin transitioning from subaerial (\(\rho_\text{fill}=0\)) to subaqueous (\(\rho_\text{fill}=1030\)) during a simulation. |
|
input |
m s⁻² |
Gravitational acceleration \(g\). |
Updating any scalar constant via set_value() propagates to the solver
immediately and invalidates the cached LU factorisation, so the next
update() rebuilds the stiffness matrix automatically.
Coupling example
from gflex import BmiGflex
import numpy as np
bmi = BmiGflex()
bmi.initialize("my_config.yaml")
n = bmi.get_grid_size(0)
load = np.zeros(n)
for step in range(n_steps):
load[:] = rho_ice * g * ice_thickness.ravel()
bmi.set_value("load__normal_component_of_stress", load)
bmi.update()
w = np.empty(n)
bmi.get_value("lithosphere__vertical_displacement", w)
# … apply w to topography …
bmi.finalize()
- class gflex.BmiGflex[source]¶
Bases:
objectBMI wrapper for gFlex lithospheric flexure.
Implements the CSDMS Basic Model Interface v2 specification. Supports 1-D and 2-D gridded flexure solutions (FD, FFT, SAS methods). The SAS_NG point-load method is not suited to the BMI grid model.
Grids¶
- Grid 0 — the spatial flexure grid (uniform rectilinear).
Shape (nrows,) in 1-D or (nrows, ncols) in 2-D, with spacing (dy, dx) and origin at (0, 0).
- Grid 1 — scalar parameter grid (uniform rectilinear, shape (1,)).
Holds the five physical constants below. These are spatially uniform by assumption; exposing them as single-element arrays supports introspection and ensemble initialisation via the BMI.
Time¶
gFlex solves instantaneous elastic equilibrium. Time is therefore nominal: start=0, step=1, end=inf. Each call to update() applies the current load and computes deflection.
Variables — grid 0¶
- Input:
load__normal_component_of_stress[Pa] Surface-normal load stress q_s = ρ g h. Material-agnostic.
- Input:
lithosphere__elastic_thickness[m] Elastic thickness T_e. Updating it invalidates the cached LU factorisation; the next update() rebuilds the stiffness matrix.
- Output:
lithosphere__vertical_displacement[m] Lithospheric deflection w (downward negative).
Variables — grid 1 (scalar constants)¶
Input:
lithosphere__young_modulus[Pa] Input:lithosphere__poisson_ratio[1] Input:mantle__mass-per-volume_density[kg m-3] Input:infill_material__mass-per-volume_density[kg m-3] Input:planet_surface__gravitational_acceleration[m s-2]Changes to any scalar constant via set_value() are pushed to the solver immediately and invalidate the cached LU factorisation, so the next update() rebuilds the stiffness matrix automatically.
- initialize(config_file: str) None[source]¶
Initialize gFlex from a configuration file.
- Parameters:
config_file (str) – Path to a gFlex YAML configuration file.
- update() None[source]¶
Compute flexural deflection for the current load.
Writes the current
load__normal_component_of_stressarray into the model’s internalqsfield, runs the solver, then copies the result intolithosphere__vertical_displacement.
- get_value(name: str, dest: NDArray[Any]) NDArray[Any][source]¶
Copy the flattened values of variable name into dest and return it.
- set_value(name: str, src: NDArray[Any]) None[source]¶
Overwrite the entire array for variable name with values from src.
For
lithosphere__elastic_thicknessand the five scalar physical constants, the new values are pushed to the solver immediately, invalidating the cached coefficient matrix so that the nextupdate()uses the updated parameters.
Landlab component¶
The Landlab component (landlab.components.gFlex) exposes gFlex within
the Landlab Earth-surface modelling
framework. It uses the same underlying solver and the same CSDMS Standard
Name fields as the BMI, but follows Landlab conventions: construction via
__init__ and time-stepping via run_one_step().
Comparison with the BMI
gFlex BMI |
Landlab component |
|
|---|---|---|
Lifecycle |
|
|
Load input |
|
|
\(T_e\) update |
|
|
Deflection output |
|
|
Field names
CSDMS Standard Name |
Direction |
Units |
Notes |
|---|---|---|---|
|
input |
Pa |
Required. Maps to gFlex internal \(q_s\). |
|
input |
m |
Optional. Re-read on every |
|
output |
m |
Total deflection \(w\) (downward negative). |
rho_fill defaults to 0.0 (air; no infill) in the Landlab component.
Set it explicitly at construction for marine (rho_fill=1030) or
sediment-filled (rho_fill=2000–2700) basins.
Coupling example
import numpy as np
from landlab import RasterModelGrid
from landlab.components import gFlex
mg = RasterModelGrid((100, 100), xy_spacing=5000.0)
mg.add_zeros("load__normal_component_of_stress", at="node")
mg.add_zeros("topographic__elevation", at="node")
gf = gFlex(mg, Youngs_modulus=65e9, Poissons_ratio=0.25,
rho_mantle=3300, rho_fill=0, elastic_thickness=35e3)
w_prev = np.zeros(mg.number_of_nodes)
for step in range(n_steps):
mg.at_node["load__normal_component_of_stress"][:] = (
rho_ice * g * ice_thickness.ravel()
)
gf.run_one_step()
w = mg.at_node["lithosphere__vertical_displacement"]
mg.at_node["topographic__elevation"] += w - w_prev
w_prev = w.copy()
Installation
pip install landlab
Requires a Landlab release that includes the gFlex v2 component
(landlab/landlab#2420).