pycsamt.interp#
Geological and hydrogeological interpretation helpers, lithology catalogues, borehole calibration, uncertainty, fusion, and report/export routines.
pycsamt.interp — geophysical-geological interpretation for EM methods.
This package converts 2-D EM resistivity models (from any inversion code — Occam2D, ModEM, AI-based, or generic arrays) into geological deliverables: pseudo-stratigraphic logs, borehole-calibrated models, and industry-standard exports.
It supersedes the geodrill module from pyCSAMT v1, extending its
scope beyond OCCAM2D and CSAMT to cover MT, AMT, CSAMT, and CSEM.
Quickstart#
>>> from pycsamt.interp import ResistivityModel, ModelCalibrator
>>> from pycsamt.interp import export, plot as iplot
>>>
>>> # Adapt any inversion result
>>> model = ResistivityModel.from_occam2d(result)
>>>
>>> # Optional: calibrate with borehole data
>>> from pycsamt.interp import Borehole
>>> bh = Borehole.from_csv("boreholes/Bo.csv", x=1050.0)
>>> cal = ModelCalibrator(ptol=0.10).fit(model, [bh])
>>>
>>> # Build stratigraphic logs
>>> logs = cal.stratigraphic_logs()
>>>
>>> # Export
>>> export.to_oasis_montaj_xyz(logs, "profile.xyz")
>>> export.to_las(logs[0], "S17.las")
>>> export.to_csv(logs, "all_stations.csv")
>>>
>>> # Plot
>>> fig = iplot.PlotStratigraphicLog(logs[0]).plot()
>>> fig = iplot.PlotFenceDiagram(logs).plot()
>>> fig = iplot.PlotCalibratedModel(model, cal.calibrated_model(),
... cal.misfit_map()).plot()
Package layout#
_baseResistivityModel— unified model container; adapters for Occam2D, ModEM, AI outputs, and raw numpy arrays.boreholeBorehole,Interval— ground-truth depth-interval data; readers for CSV and LAS 2.0.calibrateModelCalibrator— generalised NM construction (Kouadio et al. 2022), borehole-constrained or database-only.lithologyRockDatabase,StratigraphicLog,Layer— resistivity-to-lithology classification; built-in 25-rock EM database.exportto_oasis_montaj_xyz(),to_las(),to_csv(),to_vtk()— industry-format writers.plotPlotStratigraphicLog,PlotFenceDiagram,PlotCalibratedModel— matplotlib-based visuals.
References
- class pycsamt.interp.ResistivityModel(x_centers, z_centers, rho_2d, station_x=<factory>, station_names=<factory>, method='generic', rms=nan)#
Bases:
objectUnified 2-D resistivity model container.
All spatial arrays use metres; depth is positive downward.
- Parameters:
x_centers (ndarray (n_x,)) – Horizontal cell-centre positions along the profile, metres.
z_centers (ndarray (n_z,)) – Depth cell-centre positions, metres (positive downward).
rho_2d (ndarray (n_z, n_x)) – \(\log_{10}(\rho / \Omega\mathrm{m})\) on the cell grid.
station_x (ndarray (n_sta,)) – Station positions along the profile, metres.
station_names (list of str) – Station labels, same length as station_x.
method (str) – Source inversion method tag (
'occam2d','modem','ai','generic').rms (float) – Final RMS misfit from the inversion;
nanif unknown.
- column_nearest(x)#
Return the log10-rho column (n_z,) nearest to position x.
- station_column(station_name)#
Return the column nearest to the named station.
- classmethod from_occam2d(result)#
Build from an
InversionResult.- Parameters:
result (InversionResult) – A loaded Occam2D post-inversion result.
- Raises:
ValueError – If result does not contain a valid
rho_2dgrid.- Return type:
- classmethod from_array(rho_2d, x_centers, z_centers, *, station_x=None, station_names=None, method='generic', rms=nan)#
Build directly from numpy arrays.
- class pycsamt.interp.Borehole(name, x, intervals, *, collar_elevation=0.0)#
Bases:
objectBorehole / well log with depth-interval data.
- Parameters:
name (str) – Well / borehole identifier.
x (float) – Position along the survey profile, metres.
collar_elevation (float) – Surface elevation at the borehole collar, metres a.s.l. Used only when elevation-corrected exports are requested.
intervals (list of Interval) – Depth-interval log, sorted ascending by top.
- interval_at_depth(z)#
Return the interval that contains depth z, or
None.
- tres_at_depth(z)#
Return TRES (Ω·m) at depth z, or
Noneif unknown.
- lithology_at_depth(z)#
Return the lithology name at depth z, or
None.
- tres_column(z_centers)#
Return TRES values at z_centers as a float array.
Depths not covered by any interval are
nan.
- classmethod from_csv(path, *, name=None, x=0.0, collar_elevation=0.0, delimiter=',', top_col='top', bottom_col='bottom', lithology_col='lithology', resistivity_col='resistivity')#
Load from a CSV file.
Expected columns (case-insensitive header):
top, bottom, lithology[, resistivity]- Parameters:
path (path-like)
name (str, optional) – Defaults to the file stem.
x (float) – Profile position.
collar_elevation (float)
delimiter (str)
top_col (str) – Column header names.
bottom_col (str) – Column header names.
lithology_col (str) – Column header names.
resistivity_col (str) – Column header names.
- Return type:
- classmethod from_las(path, *, x=0.0, collar_elevation=0.0, depth_curve='DEPT', resistivity_curve='RESD', lithology_curve='LITH', null_value=-9999.25, step=None)#
Load from a LAS 2.0 well-log file.
Converts the continuous depth log into discrete intervals by grouping consecutive samples with the same lithology code.
- Parameters:
path (path-like)
x (float) – Profile position.
depth_curve (str) – Curve mnemonics. lithology_curve may be
Noneto assign a generic label.resistivity_curve (str) – Curve mnemonics. lithology_curve may be
Noneto assign a generic label.lithology_curve (str | None) – Curve mnemonics. lithology_curve may be
Noneto assign a generic label.null_value (float) – LAS null sentinel replaced with
nan.step (float, optional) – If provided, override the step value from the LAS header.
collar_elevation (float)
- Return type:
- to_dataframe()#
Return intervals as a
pandas.DataFrame.
- class pycsamt.interp.Interval(top, bottom, lithology, resistivity=None)#
Bases:
objectA single depth interval in a borehole log.
- Parameters:
top (float) – Depth to the top of the interval, metres.
bottom (float) – Depth to the bottom of the interval, metres.
lithology (str) – Geological formation / lithology name.
resistivity (float or None) – True resistivity (TRES) in Ω·m (linear, not log₁₀).
Nonewhen no electrical measurement is available.
- class pycsamt.interp.ModelCalibrator(ptol=0.1, *, max_borehole_distance=500.0, db=None, verbose=True)#
Bases:
objectConstrain a 2-D EM resistivity model with borehole TRES values.
- Parameters:
ptol (float, default 0.10) – Soft-error threshold — fractional tolerance between a CRM resistivity value and a TRES value for accepting a replace. The paper uses 10 % (
ptol = 0.10).max_borehole_distance (float, default 500.0) – Maximum profile-distance (metres) from a station to the nearest borehole. Stations farther than this threshold are calibrated using the rock database only (no TRES constraint).
db (RockDatabase, optional) – Rock physics database for autolayer assignment. Defaults to
default().verbose (bool, default True)
- fit(model, boreholes=None)#
Calibrate model against boreholes.
- Parameters:
model (ResistivityModel) – The Calculated Resistivity Model (CRM) from any EM inversion.
boreholes (list of Borehole, optional) – Ground-truth borehole logs. If
Noneor empty, the calibration falls back to rock-database autolayer assignment for every cell.
- Return type:
self
- calibrated_model()#
Return a
ResistivityModelcontaining the NM.- Raises:
RuntimeError – If
fit()has not been called.- Return type:
- misfit_map()#
Return the per-column misfit G (%), shape (n_z, n_x).
Values range 0 – 100 %. Low values indicate good CRM–TRES agreement; high values flag where the inversion needed the most correction.
- Return type:
- stratigraphic_logs(*, db=None, model='nm', merge_tolerance=0.2)#
Build a
StratigraphicLogfor every station.- Parameters:
db (RockDatabase, optional) – Override the classifier database.
model ({'nm', 'crm'}) – Which model to classify: the calibrated NM or the original CRM.
merge_tolerance (float) – Log-space merging tolerance passed to
from_column().
- Return type:
- class pycsamt.interp.AquiferZone(station, x, top, bottom, mean_rho_ohm_m, confidence, zone_type='aquifer', metadata=<factory>)#
Bases:
PyCSAMTObject,MetadataMixinContiguous aquifer-favourable interval at one station/profile column.
- Parameters:
- class pycsamt.interp.HydroGeophysicalModel(resistivity_model, unit_map, confidence, zones=<factory>, logs=<factory>, metadata=<factory>)#
Bases:
PyCSAMTObject,MetadataMixinHydrogeological interpretation product for one resistivity model.
- Parameters:
resistivity_model (ResistivityModel)
unit_map (ndarray)
confidence (ndarray)
zones (list[AquiferZone])
logs (list[StratigraphicLog])
- resistivity_model: ResistivityModel#
- zones: list[AquiferZone]#
- logs: list[StratigraphicLog]#
- aquifer_zones(*, min_confidence=0.0)#
Return aquifer-favourable zones above a confidence threshold.
- Parameters:
min_confidence (float)
- Return type:
- station_summary()#
Return compact per-station hydrogeological summaries.
- to_csv(path)#
Write cell-level hydro units and aquifer zones to CSV.
- class pycsamt.interp.HydroInterpreter(*, context='', db=None, water_table_depth=None, aquifer_range=(5.0, 300.0), fracture_range=(50.0, 800.0), clay_max=20.0, saline_max=3.0, basement_min=1000.0, min_zone_thickness=1.0, calibration_ptol=0.1, max_borehole_distance=500.0)#
Bases:
PyCSAMTObjectRule-based hydrogeophysical interpreter for EM resistivity sections.
The default thresholds are conservative and intended as a first-pass screening model. Local calibration can be introduced by passing boreholes, a custom
RockDatabase, or explicit threshold overrides.- Parameters:
- fit(model, *, boreholes=None)#
Interpret a resistivity model or inversion result.
- Parameters:
- Return type:
- class pycsamt.interp.HydroUnit(x, z, rho_ohm_m, rho_log10, unit, lithology, confidence=1.0, metadata=<factory>)#
Bases:
PyCSAMTObject,MetadataMixinOne classified hydrogeophysical cell.
- Parameters:
- class pycsamt.interp.PetrophysicalConfig(petro=<factory>, rho_w=20.0, porosity_prior=0.25, Sw_water_table_threshold=0.85, d50_m=0.00025, kozeny_C=180.0, kozeny_tortuosity=0.5, fracture_depth_m=None, fracture_rho_matrix=5000.0, specific_storage=0.0001, min_wt_search_depth=0.5)#
Bases:
PyCSAMTObjectAll petrophysical and hydraulic parameters needed by
EMHydroModel.- Parameters:
petro (ArchieModel or WaxmanSmitsModel) – Petrophysical model for ρ ↔ (φ, Sw) transforms. Default is
ArchieModel(m=1.8, n=2.0)— appropriate for clean sands targeted by shallow TDEM and AMT surveys.rho_w (float) – Pore-water resistivity (Ω·m). Default 20 Ω·m (EC ≈ 0.5 mS/cm, potable fresh water at 25 °C; range: 0.2 Ω·m seawater – 100+ Ω·m pristine groundwater). Constrained by water-quality measurements or EC logs via
pycsamt.interp.constraints.porosity_prior (float) – Reference porosity used in the vadose zone and as an upper-clip for unrealistic Archie-inferred porosities (default 0.25).
Sw_water_table_threshold (float) – Saturation value that marks the water table in the Archie inverse (default 0.85; lower it for coarser-grained aquifers).
d50_m (float) – Median grain size (m) for Kozeny-Carman K (default 2.5 × 10⁻⁴ m, fine sand). Increase to ~1 × 10⁻³ m for coarse sand/gravel.
kozeny_C (float) – Kozeny constant × shape factor (default 180 for spheres).
kozeny_tortuosity (float) – Tortuosity correction in Kozeny-Carman (default 0.5).
fracture_depth_m (float or None) – Depth (m) below which
fractured_zone_K()replaces Kozeny-Carman. Use for AMT/MT basement targets.Nonedisables fracture K everywhere (default: None).fracture_rho_matrix (float) – Background resistivity of intact rock for the cubic-law fracture K (Ω·m; default 5 000).
specific_storage (float) – Specific storage Ss (m⁻¹) for confined storativity (default 10⁻⁴).
min_wt_search_depth (float) – Minimum depth (m) for water-table detection; skips near-surface noise (default 0.5 m).
- petro: ArchieModel | WaxmanSmitsModel#
- class pycsamt.interp.EMHydroModel(resistivity_model, config=None, *, petro=None, rho_w=None, porosity_prior=None, method_tag='')#
Bases:
PyCSAMTObjectQuantitative hydrogeological model from an EM resistivity section.
Wires the petrophysical transforms in
pycsamt.interp.petrophysicsonto a full 2-DResistivityModelto produce spatially continuous porosity, saturation, hydraulic-conductivity, and water-table maps.The computation follows a two-pass strategy to break the Archie chicken-and-egg (φ needs Sw, Sw needs φ):
Water-table pass — scan each column with the Archie-inverse Sw estimator to find the saturation front (Sw ≥ threshold).
Cell-property pass — for cells below the water table assume full saturation (Sw = 1) and solve for φ; for cells above use
porosity_priorand solve for Sw.
- Parameters:
resistivity_model (ResistivityModel) – Source inversion model.
config (PetrophysicalConfig, optional) – Full parameter bundle. If
None, default parameters are used. Individual keyword arguments below override specific fields.petro (ArchieModel or WaxmanSmitsModel, optional) – Petrophysical model.
rho_w (float, optional) – Pore-water resistivity (Ω·m).
porosity_prior (float, optional) – Prior porosity.
method_tag (str, optional) – EM method label (
'TDEM','AMT','MT','EMAP').
Examples
>>> cfg = PetrophysicalConfig(rho_w=0.03, porosity_prior=0.20, ... fracture_depth_m=300.0) >>> result = EMHydroModel(rm, cfg, method_tag="AMT").fit() >>> result.water_table # array of depths, one per x-column >>> result.transmissivity # T profile (m²/s)
- fit()#
Run all petrophysical transforms and return
EMHydroResult.- Return type:
- class pycsamt.interp.EMHydroResult(resistivity_model, config, porosity, saturation, hydraulic_K, water_table, transmissivity, storativity_confined, storativity_unconfined, dar_zarrouk_TR, dar_zarrouk_S, tds, method_tag='', metadata=<factory>)#
Bases:
PyCSAMTObjectQuantitative hydrogeological output of one EM resistivity section.
All 2-D arrays have shape
(n_z, n_x)matching the sourceResistivityModel; 1-D station arrays have shape(n_x,).- Variables:
resistivity_model (ResistivityModel)
config (PetrophysicalConfig)
porosity (ndarray (n_z, n_x)) – Effective porosity per cell (dimensionless, 0–1). Below the water table: Archie inverse at Sw = 1. Above the water table:
config.porosity_prior.saturation (ndarray (n_z, n_x)) – Water saturation Sw per cell (0–1). Below the water table: 1.0. Above the water table: Archie inverse at φ = porosity_prior.
hydraulic_K (ndarray (n_z, n_x)) – Hydraulic conductivity K (m/s). Porous media (z < fracture_depth_m): Kozeny-Carman from φ. Fractured basement (z ≥ fracture_depth_m): cubic-law from ρ contrast.
water_table (ndarray (n_x,)) – Estimated water-table depth (m, positive downward).
nanwhere detection failed (resistivity column shows no saturation transition above the Sw threshold).transmissivity (ndarray (n_x,)) – Aquifer transmissivity T = ∫K dz over the saturated zone (m²/s).
storativity_confined (ndarray (n_x,)) – Confined storativity S = Ss × b_sat (dimensionless).
storativity_unconfined (ndarray (n_x,)) – Unconfined storativity ≈ mean porosity in saturated zone (≈ specific yield).
dar_zarrouk_TR (ndarray (n_x,)) – Transverse resistance TR = Σ ρᵢ hᵢ (Ω·m²) over all cells.
dar_zarrouk_S (ndarray (n_x,)) – Longitudinal conductance S = Σ hᵢ/ρᵢ (siemens) over all cells.
tds (float) – Estimated total dissolved solids (mg/L) from
config.rho_w.method_tag (str) – Source EM method label (e.g.
'TDEM','AMT').metadata (dict) – Provenance and parameter snapshot.
- Parameters:
resistivity_model (ResistivityModel)
config (PetrophysicalConfig)
porosity (ndarray)
saturation (ndarray)
hydraulic_K (ndarray)
water_table (ndarray)
transmissivity (ndarray)
storativity_confined (ndarray)
storativity_unconfined (ndarray)
dar_zarrouk_TR (ndarray)
dar_zarrouk_S (ndarray)
tds (float)
method_tag (str)
metadata (dict)
- resistivity_model: ResistivityModel#
- config: PetrophysicalConfig#
- station_report()#
Return one dict per model column with key hydro indicators.
- to_csv(path)#
Write cell-level hydro parameters to a flat CSV file.
- station_report_csv(path)#
Write per-station hydro summary to CSV.
- to_dataframe()#
Return station summary as a
pandas.DataFrame(requires pandas).- Return type:
- class pycsamt.interp.ConstrainedCalibrator(constraints, *, calibrate_rho_w=True, calibrate_m=False, calibrate_phi_prior=False, rho_w_bounds=(0.003, 2.0), m_bounds=(1.2, 2.8), phi_bounds=(0.03, 0.6), n_restarts=1, verbose=False)#
Bases:
PyCSAMTObjectCalibrate petrophysical parameters to match hydrogeological field data.
Uses
scipy.optimize.minimize(L-BFGS-B) to find the parameter set that minimises the weighted sum of squared residuals between the EM-derived hydrogeological quantities and the supplied observations.The optimisation is deterministic (single run from the current parameter values). If convergence is poor, set
n_restarts > 1to try multiple random starting points within the parameter bounds.- Parameters:
constraints (sequence of constraint objects) – Any mix of
WaterLevelConstraint,PumpingTestConstraint,SlugTestConstraint,ECConstraint.calibrate_rho_w (bool) – Fit pore-water resistivity ρ_w (default True). Almost always needed.
calibrate_m (bool) – Fit Archie cementation exponent m (default False).
calibrate_phi_prior (bool) – Fit prior porosity (default False).
rho_w_bounds ((float, float)) – Search bounds for ρ_w in Ω·m (default 0.003 – 2.0).
m_bounds ((float, float)) – Search bounds for Archie m (default 1.2 – 2.8).
phi_bounds ((float, float)) – Search bounds for porosity prior (default 0.03 – 0.60).
n_restarts (int) – Number of optimisation restarts with random initialisations (default 1).
verbose (bool) – Print iteration count and final misfit (default False).
:param Attributes (set after
fit()): :param ———————————–: :param calibrated_config_: The fitted configuration. :type calibrated_config_: PetrophysicalConfig :param misfit_history_: Final misfit value per restart. :type misfit_history_: list of float :param opt_result_: Raw scipy result from the best restart. :type opt_result_: OptimizeResult or None- fit(em_hydro_model)#
Calibrate and return the best
EMHydroResult.- Parameters:
em_hydro_model (EMHydroModel) – The model to calibrate. Its
configprovides the starting point for the optimisation.- Returns:
Model result evaluated at the calibrated parameters.
- Return type:
- constraint_residuals(result)#
Return per-constraint residuals for the given result (for diagnostics).
- Parameters:
result (EMHydroResult)
- Return type:
- class pycsamt.interp.WaterLevelConstraint(x, depth_m, uncertainty_m=1.0, station='')#
Bases:
PyCSAMTObjectPiezometer or well water-level measurement.
The calibrator matches the EM-derived water-table depth at the nearest model column to the observed depth.
- Parameters:
- class pycsamt.interp.PumpingTestConstraint(x, T_m2s, S=None, uncertainty_factor=3.0, station='')#
Bases:
PyCSAMTObjectTransmissivity (and optionally storativity) from a pumping test.
Misfit is computed in log₁₀ space because T spans orders of magnitude.
- Parameters:
x (float) – Profile position of the pumping well (m).
T_m2s (float) – Observed transmissivity (m²/s).
S (float, optional) – Observed storativity (dimensionless). Currently used for reporting only; storativity calibration will be added in a future release.
uncertainty_factor (float) – Multiplicative uncertainty on T (default 3 → ±0.5 in log₁₀).
station (str) – Optional label.
- class pycsamt.interp.SlugTestConstraint(x, K_ms, depth_m, uncertainty_factor=5.0, station='')#
Bases:
PyCSAMTObjectHydraulic conductivity K from a slug test or bail test.
Matches the model K at the nearest cell to
(x, depth_m).- Parameters:
- class pycsamt.interp.ECConstraint(x, ec_mscm, uncertainty_mscm=2.0, station='')#
Bases:
PyCSAMTObjectElectrical conductivity of formation water (e.g., from an EC log or sample).
Directly constrains
rho_wvia the EC ↔ ρ_w conversion.- Parameters:
- class pycsamt.interp.TimeLapseEM(surveys, *, times=None, labels=None)#
Bases:
PyCSAMTObjectTime-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:
- resistivity_change(baseline_idx=0)#
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:
- saturation_change(petro, *, phi=0.25, rho_w=0.025, baseline_idx=0)#
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:
- water_content_change(petro, *, phi=0.25, rho_w=0.025, baseline_idx=0)#
Volumetric water-content change Δθ = φ · ΔSw.
- Parameters:
petro (ArchieModel | WaxmanSmitsModel) – Same as
saturation_change().phi (float | ndarray) – Same as
saturation_change().rho_w (float) – Same as
saturation_change().baseline_idx (int) – Same as
saturation_change().
- Returns:
Δθ arrays (dimensionless, range ≈ −φ to +φ).
- Return type:
- water_table_displacement(petro, *, rho_w=0.025, Sw_threshold=0.85, min_depth=0.5, baseline_idx=0)#
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).
nanis 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)#
Water-table depth (m) for every survey and every column.
- Returns:
nanwhere the water table could not be detected.- Return type:
- Parameters:
petro (ArchieModel | WaxmanSmitsModel)
rho_w (float)
Sw_threshold (float)
min_depth (float)
- pycsamt.interp.assert_compatible_grids(surveys, *, rtol=0.0001)#
Raise
ValueErrorif surveys have incompatible grids.- Parameters:
surveys (sequence of ResistivityModel)
rtol (float) – Relative tolerance for coordinate comparison (default 1e-4).
- Return type:
None
- class pycsamt.interp.MultiMethodEMModel(primary, secondary, *, primary_max_depth=None, secondary_min_depth=None, blend='linear', blend_overlap=None, z_grid=None, sigmoid_k=0.02)#
Bases:
PyCSAMTObjectFuse two EM resistivity models onto a single depth grid.
- Parameters:
primary (ResistivityModel) – The model trusted at shallow depths (e.g. TDEM, EMAP).
secondary (ResistivityModel) – The model trusted at deeper depths (e.g. AMT, MT).
primary_max_depth (float, optional) – Override the primary model’s maximum contributing depth (m). Defaults to
primary.z_centers[-1].secondary_min_depth (float, optional) – Override the secondary model’s minimum contributing depth (m). Defaults to
secondary.z_centers[0].blend (str) –
Blend strategy in the overlap zone:
'linear'(default)Linear weight ramp from primary (top of overlap) to secondary (bottom of overlap).
'sigmoid'Smooth S-curve parameterised by sigmoid_k. Avoids the slope discontinuity at the ends of the transition zone.
'rms_weighted'Constant weights throughout: primary weight = rms_secondary / (rms_primary + rms_secondary). Requires both models to carry a valid
rmsvalue. Falls back to'linear'if either RMS isnan.
blend_overlap (float, optional) – Restrict the blend transition to a window of this width (m) centred on the mid-point of the natural overlap zone.
Noneuses the full overlap.z_grid (ndarray, optional) – Explicit output depth-cell centres (m). Overrides the automatic union grid. Both models are interpolated onto this grid.
sigmoid_k (float) – Shape parameter for the sigmoid blend (m⁻¹; default 0.02, giving a smooth ~100 m transition for a 500 m overlap zone).
- Variables:
diagnostics (FusionDiagnostics or None) – Set after
merge()is called.
Examples
>>> fused_model = MultiMethodEMModel( ... tdem_model, amt_model, blend='sigmoid', sigmoid_k=0.03 ... ).merge() >>> fused_model.method 'TDEM+AMT'
- merge()#
Produce the fused
ResistivityModel.- Returns:
Unified model on the output depth grid.
methodis set to'<primary_method>+<secondary_method>'(e.g.'TDEM+AMT').- Return type:
- class pycsamt.interp.FusionDiagnostics(z_overlap_start, z_overlap_end, has_overlap, blend_mode, primary_method, secondary_method, primary_rms, secondary_rms, n_z_fused, blend_weights)#
Bases:
PyCSAMTObjectMetadata about a completed fusion operation.
- Variables:
z_overlap_start (float) – Top of the depth zone where both methods contribute (m).
z_overlap_end (float) – Bottom of the overlap zone (m).
has_overlap (bool) –
Falseif the two depth ranges are disjoint (simple concatenation).blend_mode (str)
primary_method (str)
secondary_method (str)
primary_rms (float)
secondary_rms (float)
n_z_fused (int) – Total number of depth cells in the fused model.
blend_weights (ndarray (n_z,)) – Primary-model weight at each depth cell (1 = all primary, 0 = all secondary).
- Parameters:
- class pycsamt.interp.UncertaintyBounds(rho_w_range=None, m_range=None, n_range=None, phi_prior_range=None, dist='uniform')#
Bases:
PyCSAMTObjectPrior distribution specification for Monte Carlo sampling.
Each
*_rangeparameter activates that parameter as a free variable. Un-specified parameters are held fixed at the centralPetrophysicalConfigvalues.- Parameters:
rho_w_range ((float, float), optional) – Pore-water resistivity range (Ω·m). uniform: (low, high); normal: (mean, std). Typical fresh-water range:
(5, 100); brackish:(0.5, 20).m_range ((float, float), optional) – Archie cementation exponent. Typical:
(1.3, 2.5)for mixed lithology.n_range ((float, float), optional) – Archie saturation exponent. Typical:
(1.8, 2.5)for clean sand.phi_prior_range ((float, float), optional) – Porosity prior. Typical:
(0.10, 0.45).dist (str) – Sampling distribution:
'uniform'(default, non-informative) or'normal'(Gaussian — range is interpreted as (mean, std)).
Notes
At least one
*_rangemust be set.rho_w_rangealone covers the dominant source of uncertainty in most shallow EM surveys.- sample(cfg, n, rng)#
Draw n parameter samples and return a list of configs.
- Parameters:
cfg (PetrophysicalConfig) – Central (best-estimate) configuration — used as fallback for fixed parameters.
n (int) – Number of Monte Carlo samples.
rng (np.random.Generator)
- Return type:
list of PetrophysicalConfig, length n
- class pycsamt.interp.UncertaintyResult(resistivity_model, config, bounds, n_samples, method_tag='', sampled_params=<factory>, mean_K=<factory>, std_K=<factory>, p10_K=<factory>, p50_K=<factory>, p90_K=<factory>, cv_K=<factory>, mean_Sw=<factory>, std_Sw=<factory>, p10_Sw=<factory>, p90_Sw=<factory>, mean_phi=<factory>, std_phi=<factory>, mean_wt=<factory>, std_wt=<factory>, p10_wt=<factory>, p50_wt=<factory>, p90_wt=<factory>, wt_detection_rate=<factory>, mean_T=<factory>, std_T=<factory>, p10_T=<factory>, p50_T=<factory>, p90_T=<factory>)#
Bases:
PyCSAMTObjectEnsemble statistics from a
MonteCarloHydrorun.All 2-D arrays have shape
(n_z, n_x); 1-D station arrays have shape(n_x,). K values are stored on the linear scale (m/s).- Variables:
resistivity_model (ResistivityModel)
config (PetrophysicalConfig) – Central configuration used to seed the Monte Carlo.
bounds (UncertaintyBounds)
n_samples (int)
method_tag (str)
sampled_params (ndarray (n_samples, n_free)) – Matrix of sampled parameter values, columns in
bounds.free_namesorder.(m/s) (Hydraulic conductivity K)
------------------------------
cv_K (ndarray (n_z, n_x)) – Coefficient of variation std_K / mean_K. Dimensionless uncertainty map.
Sw (Water saturation)
-------------------
φ (Porosity)
----------
std_phi (mean_phi,)
(m) (Water-table depth)
---------------------
wt_detection_rate (ndarray (n_x,)) – Fraction of samples in which the water table was detected (0–1).
(m²/s) (Transmissivity T)
-----------------------
- Parameters:
resistivity_model (ResistivityModel)
config (PetrophysicalConfig)
bounds (UncertaintyBounds)
n_samples (int)
method_tag (str)
sampled_params (ndarray)
mean_K (ndarray)
std_K (ndarray)
p10_K (ndarray)
p50_K (ndarray)
p90_K (ndarray)
cv_K (ndarray)
mean_Sw (ndarray)
std_Sw (ndarray)
p10_Sw (ndarray)
p90_Sw (ndarray)
mean_phi (ndarray)
std_phi (ndarray)
mean_wt (ndarray)
std_wt (ndarray)
p10_wt (ndarray)
p50_wt (ndarray)
p90_wt (ndarray)
wt_detection_rate (ndarray)
mean_T (ndarray)
std_T (ndarray)
p10_T (ndarray)
p50_T (ndarray)
p90_T (ndarray)
- resistivity_model: ResistivityModel#
- config: PetrophysicalConfig#
- bounds: UncertaintyBounds#
- prob_wt_shallower_than(depth_m, *, wt_ensemble=None)#
P(water-table depth < depth_m) per station column.
- Parameters:
depth_m (float) – Reference depth (m, positive downward).
wt_ensemble (ndarray (n_samples, n_x), optional) – Full water-table ensemble from
MonteCarloHydro.run_ensemble(). IfNone, approximated from the stored P10/P50/P90 assuming a Gaussian distribution with mean=mean_wt, std=std_wt.
- Return type:
ndarray (n_x,) — probability in [0, 1]
- class pycsamt.interp.MonteCarloHydro(resistivity_model, config, bounds, *, n_samples=200, seed=42, method_tag='', verbose=False)#
Bases:
PyCSAMTObjectMonte Carlo uncertainty propagation for a quantitative EM hydro model.
- Parameters:
resistivity_model (ResistivityModel) – Source inversion model.
config (PetrophysicalConfig) – Central (best-estimate) petrophysical configuration.
bounds (UncertaintyBounds) – Prior distributions for the free parameters.
n_samples (int) – Number of Monte Carlo realisations (default 200). Typical: 100 for quick runs, 500–1000 for publication-quality.
seed (int) – Random seed for reproducibility (default 42).
method_tag (str, optional) – EM method label inherited by the output.
verbose (bool) – Print progress every 50 iterations (default False).
Examples
>>> bounds = UncertaintyBounds(rho_w_range=(5.0, 100.0), m_range=(1.4, 2.4)) >>> mc = MonteCarloHydro(rm, cfg, bounds, n_samples=300) >>> unc = mc.run() >>> unc.cv_K.max() # worst-case K uncertainty (fraction) >>> unc.p90_wt - unc.p10_wt # WT depth spread per station (m)
- run()#
Execute the Monte Carlo ensemble and return
UncertaintyResult.- Return type:
- class pycsamt.interp.RockDatabase(entries)#
Bases:
objectExtensible rock physics database for EM resistivity interpretation.
Example
>>> db = RockDatabase.default() >>> db.classify(180.0).name 'Fractured zone'
- classmethod default()#
Return a database pre-loaded with 25 built-in rock entries.
- Return type:
- classmethod from_csv(path)#
Load from a CSV file.
Required columns:
name, rho_min, rho_maxOptional columns:color, description, code- Parameters:
- Return type:
- classify(rho_ohm_m, method='nearest')#
Return the best-matching rock entry for rho_ohm_m.
- class pycsamt.interp.RockEntry(name, rho_min, rho_max, color='#AAAAAA', description='', code=0)#
Bases:
objectA single entry in the rock physics database.
- Parameters:
name (str) – Geological unit / lithology name.
rho_min (float) – Resistivity range in Ω·m (linear scale).
rho_max (float) – Resistivity range in Ω·m (linear scale).
color (str) – Hex colour code for plotting (e.g.
'#28B463').description (str) – Optional free-text note.
code (int) – Integer code used in LAS exports.
- class pycsamt.interp.Layer(top, bottom, rho_log10, lithology, color='#AAAAAA', confidence=1.0)#
Bases:
objectOne geological unit in a pseudo-stratigraphic log.
- Parameters:
top (float) – Depth in metres (positive downward).
bottom (float) – Depth in metres (positive downward).
rho_log10 (float) – Representative \(\log_{10}(\rho)\) of the layer.
lithology (str) – Rock name from
RockDatabase.color (str) – Hex colour for plotting.
confidence (float) – Fraction of depth cells whose DB classification matches the reported lithology (0 – 1).
- class pycsamt.interp.StratigraphicLog(station_name, station_x, z_centers, rho_log10, layers)#
Bases:
objectPer-station pseudo-stratigraphic depth profile.
Constructed from a 1-D log10-rho column and a
RockDatabase, it merges adjacent cells that share the same lithology into discreteLayerobjects.- Parameters:
- classmethod from_column(station_name, x, z_centers, rho_log10, db=None, *, merge_tolerance=0.2)#
Build a log from a 1-D resistivity column.
- Parameters:
station_name (str)
x (float) – Station position, metres.
z_centers (ndarray (n_z,))
rho_log10 (ndarray (n_z,))
db (RockDatabase, optional) – Defaults to
RockDatabase.default().merge_tolerance (float) – Log10-rho difference threshold for merging adjacent cells into one layer (default 0.2 decade).
- Return type:
- to_dataframe()#
Return layers as a
pandas.DataFrame.
Interpretation Modules#
Borehole — ground-truth data model for EM geological interpretation. |
|
ModelCalibrator — constrain a 2-D EM model with borehole ground truth. |
|
Field-measurement constraints for petrophysical calibration. |
|
export — write EM interpretation results to industry formats. |
|
Multi-method EM fusion — merge shallow and deep resistivity models. |
|
Hydrogeophysical interpretation from EM resistivity models. |
|
Quantitative hydro-geophysical model from EM resistivity sections. |
|
Lithology — resistivity-to-geology classification for EM methods. |
|
Petrophysical transforms for EM hydrogeophysics. |
|
plot — visualization for pycsamt.interp results. |
|
Time-lapse EM monitoring for hydrogeological change detection. |
|
Monte Carlo uncertainty propagation for EM hydro-geophysical models. |