pycsamt.interp.timelapse#

Time-lapse EM monitoring for hydrogeological change detection.

Compares a sequence of EM resistivity models (acquired at different times) to detect and quantify:

  • Resistivity change Δlog₁₀(ρ) — raw geophysical signal

  • Water-saturation change ΔSw — via Archie inverse

  • Volumetric water-content change Δθ = φ · ΔSw

  • Water-table displacement (rise/fall in metres)

Primary applications in an EM-hydro context:

  • TDEM time-lapse — monitoring recharge, dewatering, or saline intrusion at 10–500 m depth. High sensitivity to near-surface saturation changes.

  • AMT time-lapse — seasonal or induced changes in regional aquifers and fractured basement (100–2 000 m).

All outputs are referenced to a baseline survey (default: index 0). The grids of all surveys must match (same x_centers and z_centers). Use assert_compatible_grids() to check before computing.

Typical use#

>>> from pycsamt.interp import ResistivityModel
>>> from pycsamt.interp.petrophysics import ArchieModel
>>> from pycsamt.interp.timelapse import TimeLapseEM
>>>
>>> tl = TimeLapseEM(
...     surveys=[model_dry, model_wet, model_recharge],
...     labels=["dry", "wet", "recharge"],
... )
>>> delta_rho = tl.resistivity_change()    # list of (n_z, n_x) arrays
>>> delta_Sw  = tl.saturation_change(ArchieModel(), rho_w=0.025)
>>> delta_wt  = tl.water_table_displacement(ArchieModel())  # (n_surveys-1, n_x)

Functions

assert_compatible_grids(surveys, *[, rtol])

Raise ValueError if surveys have incompatible grids.

Classes

TimeLapseEM(surveys, *[, times, labels])

Time-lapse EM analysis for hydrogeological change detection.

class pycsamt.interp.timelapse.TimeLapseEM(surveys, *, times=None, labels=None)[source]#

Bases: PyCSAMTObject

Time-lapse EM analysis for hydrogeological change detection.

Parameters:
  • surveys (sequence of ResistivityModel) – Time-ordered EM inversion results. All must share the same grid (checked on construction via assert_compatible_grids()).

  • times (sequence of float, optional) – Time stamps for each survey (any consistent unit: days, months, etc.). Used for labelling only — no numerical time-derivative is computed.

  • labels (sequence of str, optional) – Human-readable survey labels (e.g. ['dry2022', 'wet2023']).

Variables:
property n_surveys: int[source]#
property n_x: int[source]#
property n_z: int[source]#
resistivity_change(baseline_idx=0)[source]#

Resistivity change relative to the baseline survey.

\[\Delta\log_{10}\rho_i = \log_{10}\rho_i - \log_{10}\rho_\text{baseline}\]
Parameters:

baseline_idx (int) – Index of the baseline survey (default 0).

Returns:

One array per non-baseline survey, in time order. Positive values indicate resistivity increase (drying/desaturation). Negative values indicate resistivity decrease (wetting/salinisation).

Return type:

list of ndarray (n_z, n_x)

saturation_change(petro, *, phi=0.25, rho_w=0.025, baseline_idx=0)[source]#

Water-saturation change Δ*Sw* via Archie/WS inverse.

\[\Delta S_{w,i} = S_w(\rho_i) - S_w(\rho_\text{baseline})\]
Parameters:
  • petro (ArchieModel or WaxmanSmitsModel) – Petrophysical model for ρ → Sw inversion.

  • phi (float or ndarray) – Porosity used in the inversion. Scalar for a uniform profile; 2-D array (n_z, n_x) for a spatially varying prior.

  • rho_w (float) – Pore-water resistivity (Ω·m).

  • baseline_idx (int) – Index of the baseline survey (default 0).

Returns:

ΔSw arrays, one per non-baseline survey. Positive = wetting (saturation increase). Negative = drying (saturation decrease).

Return type:

list of ndarray (n_z, n_x)

water_content_change(petro, *, phi=0.25, rho_w=0.025, baseline_idx=0)[source]#

Volumetric water-content change Δθ = φ · ΔSw.

Parameters:
Returns:

Δθ arrays (dimensionless, range ≈ −φ to +φ).

Return type:

list of ndarray (n_z, n_x)

water_table_displacement(petro, *, rho_w=0.025, Sw_threshold=0.85, min_depth=0.5, baseline_idx=0)[source]#

Water-table depth change relative to the baseline survey.

\[\Delta z_{wt} = z_{wt}(t_i) - z_{wt}(t_\text{baseline})\]

Positive values → water table dropped (deeper, e.g. dry season). Negative values → water table rose (shallower, e.g. recharge).

nan is returned where the water table cannot be detected in either the baseline or the comparison survey.

Parameters:
  • petro (ArchieModel or WaxmanSmitsModel)

  • rho_w (float)

  • Sw_threshold (float) – Saturation level that defines the water table (default 0.85).

  • min_depth (float) – Minimum search depth in metres (default 0.5).

  • baseline_idx (int)

Returns:

Water-table displacement in metres, one row per non-baseline survey.

Return type:

ndarray (n_surveys−1, n_x)

water_table_map(petro, *, rho_w=0.025, Sw_threshold=0.85, min_depth=0.5)[source]#

Water-table depth (m) for every survey and every column.

Returns:

nan where the water table could not be detected.

Return type:

ndarray (n_surveys, n_x)

Parameters:
resistivity_stats(baseline_idx=0)[source]#

Summary statistics of resistivity change across all surveys.

Returns:

mean_delta, std_delta, max_increase, max_decrease — each an ndarray (n_z, n_x).

Return type:

dict with keys

Parameters:

baseline_idx (int)

pycsamt.interp.timelapse.assert_compatible_grids(surveys, *, rtol=0.0001)[source]#

Raise ValueError if surveys have incompatible grids.

Parameters:
  • surveys (sequence of ResistivityModel)

  • rtol (float) – Relative tolerance for coordinate comparison (default 1e-4).

Return type:

None