pycsamt.core#
Core configuration, base objects, registries, adapters, and mixins shared across pyCSAMT v2.
Core survey objects, configuration, registries, and transfer-function data.
- class pycsamt.core.CoreObject#
Bases:
PyCSAMTObjectMinimal base class for PyCSAMT objects and mixins.
The goal is to provide stable, readable
__repr__and__str__implementations, plus a couple of helpers for light introspection and serialization that do not pull heavy dependencies.Notes
__repr__shows a compact field summary. Large arrays or containers are summarized safely (shape/len only).Subclasses may override
_repr_fields()to control which attributes appear in the summary.as_dict()is shallow by default and avoids importing external libs. It aims to be robust, not exhaustive.
Examples
>>> class P(CoreObject): ... def __init__(self): ... self.name = "S001" ... self.data = [1, 2, 3] ... >>> P() P(name='S001', data=[len=3])
See also
- summary(*, max_fields=6)#
Return a short one-line description.
- as_dict(*, public_only=True, max_depth=1)#
Serialize to a lightweight
dictsafely.- Parameters:
- Returns:
A JSON-friendly mapping (best-effort).
- Return type:
- class pycsamt.core.MTBase#
Bases:
CoreObjectCommon electromagnetic and MT utilities.
This mixin-like base provides numerically safe, vectorized helpers built on NumPy. All methods accept array-like inputs and return NumPy arrays with broadcast semantics.
Notes
Shapes are preserved and operations are element-wise unless stated otherwise. For impedance tensors shaped
(..., 2, 2), “determinant” operations collapse the last two axes.Unit conventions. Two equivalent formulas are common for apparent resistivity from impedance magnitude
|Z|:SI (E in V/m, H in A/m):
ρₐ = |Z|² / (μ₀·ω) = |Z|² · RHO_FACTOR / f, withω = 2π fandRHO_FACTOR = 1/(μ₀·2π).Field units (E in mV/km, B in nT):
ρₐ ≈ (0.2 / f) · |E/H|²— a legacy Zonge-style form. This is reflected byZONGE_RHO_FACTOR, i.e.0.2.
Be consistent. Converting mV/km → V/m and nT → T, and B → H via
H = B/μ₀, recovers the SI formulation.Constants#
- MU0float
Magnetic permeability of free space (H/m).
4π × 10⁻⁷.- EPS0float
Electric permittivity of free space (F/m).
8.854187817 × 10⁻¹².- Cfloat
Speed of light in vacuum (m/s). From CODATA.
- C0float
1/√(μ₀·ε₀)(m/s). Equal toCby definition.- ETA0float
Wave impedance of free space (Ω).
√(μ₀/ε₀).- TWO_PIfloat
2π. Handy forω = 2π fconversions.- DEG2RADfloat
Degrees to radians.
π/180.- RAD2DEGfloat
Radians to degrees.
180/π.- RHO_FACTORfloat
1/(μ₀·2π). Forρₐ = |Z|² · RHO_FACTOR / f(SI).- ZONGE_RHO_FACTORfloat
0.2. Legacy factor inρₐ ≈ (0.2/f)·|E/H|²when using E in mV/km and B/H in nT without explicit SI conversion in the formula.- B_TO_Hfloat
Conversion factor from magnetic flux density to field intensity (A/m per Tesla).
1/μ₀.
# Unit scales (field practice) MICROVOLTS_TO_VOLTS : float
1e-6.- PICOTESLA_TO_TESLAfloat
1e-12.- NANOTESLA_TO_TESLAfloat
1e-9.- MV_PER_KM_TO_V_PER_Mfloat
1e-6. Since1 mV/km = 1e-3 V / 1e3 m.- METERS_TO_KILOMETERSfloat
1e-3.- PERCENT_FACTORfloat
100.0. For percent amplitudes or coherence.- Z_UNIT_MVK_NT_TO_SIfloat
(1e-6) / (1e-9) = 1e3. ConvertsZexpressed as(mV/km)/nTinto SI-consistent(V/m)/T.
See also
rho_phase_from_zApparent resistivity and phase from
Z.z_from_rho_phaseBuild
Zmagnitude/phase fromρ, φ.determinant_zRotation-invariant impedance.
rho_phase_from_detInvariants from determinant impedance.
rotate_impedanceRotate 2×2 impedance tensors.
skin_depthDiffusive skin depth.
tipper_amp_phaseAmplitude and phase of tipper vector.
Examples
Compute SI apparent resistivity from
|Z|and frequency:>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> Zmag = np.asarray([5.0, 10.0]) # |Z| in ohms >>> f = np.asarray([1.0, 0.1]) # Hz >>> rho_a = (Zmag**2) * MTBase.RHO_FACTOR / f >>> rho_a.shape (2,)
Convert field units to SI and compare formulas:
>>> E_mVkm = 100.0 # mV/km >>> B_nT = 50.0 # nT (≈ μ0·H if not converted) >>> Z_field = (E_mVkm * MTBase.MV_PER_KM_TO_V_PER_M) / \ ... (B_nT * MTBase.NANOTESLA_TO_TESLA) >>> Z_si_like = Z_field # (V/m)/T >>> f = 1.0 # Hz >>> rho_si = (Z_si_like**2) * MTBase.RHO_FACTOR / f >>> rho_zonge = (MTBase.ZONGE_RHO_FACTOR / f) * (E_mVkm / B_nT)**2 >>> rho_si > 0 and rho_zonge > 0 True
Rotate an impedance tensor by a site azimuth:
>>> Z = np.zeros((3, 2, 2), dtype=complex) # demo only >>> phi = 30.0 * MTBase.DEG2RAD >>> # rotate_impedance would apply R(φ) Z Rᵀ(φ) >>> # Z_rot = MTBase.rotate_impedance(Z, phi) # hypothetical
References
- rho_phase_from_z(z, f, *, phase_unit='deg')#
Apparent resistivity and phase from impedance.
- Parameters:
z (array_like) – Impedance, scalar or tensor
(..., 2, 2).f (array_like) – Frequency (Hz), broadcastable with
z.phase_unit ({'deg', 'rad', 'mrad'}, optional) – Output unit for phase. Default is degrees.
- Returns:
rho (ndarray) – Apparent resistivity (Ω·m), element-wise.
phi (ndarray) – Phase in requested unit, element-wise.
- Return type:
Notes
Uses
ρa = |Z|^2 / (μ0 ω)per component. For tensor invariants seerho_phase_from_det().
- z_from_rho_phase(rho, phi, f, *, phase_unit='deg')#
Build impedance magnitude/phase from
ρaandφ.- Parameters:
rho (array_like) – Apparent resistivity (Ω·m).
phi (array_like) – Phase in
phase_unit.f (array_like) – Frequency (Hz).
phase_unit ({'deg', 'rad', 'mrad'}, optional) – Unit of
phi. Default is degrees.
- Returns:
Z – Complex impedance with
|Z| = sqrt(μ0 ω ρa)and angle =φ. Shape follows broadcasting rules.- Return type:
ndarray
- static determinant_z(z)#
Determinant impedance
Z_det = sqrt(-Z_xy Z_yx).- Parameters:
z (array_like, shape (..., 2, 2)) – Impedance tensor(s).
- Returns:
Z_det – Rotation-invariant complex impedance.
- Return type:
ndarray, shape (…)
Notes
The minus sign assumes the common MT sign convention.
- rho_phase_from_det(z, f, *, phase_unit='deg')#
Invariant apparent resistivity and phase from
Z.- Parameters:
z (array_like, shape (..., 2, 2)) – Impedance tensor(s).
f (array_like) – Frequency (Hz).
phase_unit ({'deg', 'rad', 'mrad'}, optional) – Output unit for phase.
- Returns:
rho_det (ndarray) – Determinant apparent resistivity (Ω·m).
phi_det (ndarray) – Determinant phase in requested unit.
- Return type:
- static rotate_impedance(z, theta_deg)#
Rotate 2×2 impedance tensor(s) by
theta_deg.- Parameters:
- Returns:
Z_rot – Rotated impedance tensors.
- Return type:
ndarray, shape (…, 2, 2)
Notes
Uses
Z' = R Z RᵀwithR = [[cos θ, sin θ], [-sin θ, cos θ]].
- skin_depth(f, rho)#
Skin depth
δ = sqrt(2ρ / (μ0 ω))in meters.- Parameters:
f (array_like) – Frequency (Hz).
rho (array_like) – Resistivity (Ω·m).
- Returns:
δ – Skin depth in meters.
- Return type:
ndarray
Notes
For quick estimates,
δ ≈ 503 √(ρ/f)(ρ in Ω·m, f in Hz).
- static freq_to_period(f)#
Convert frequency (Hz) to period (s), safe at zero.
- static period_to_freq(t)#
Convert period (s) to frequency (Hz), safe at inf.
- static tipper_amp_phase(t, *, phase_unit='deg')#
Amplitude and phase of tipper vectors.
- Parameters:
t (array_like, shape (..., 2) or (..., 1, 2)) – Tipper components
(Tx, Ty).phase_unit ({'deg', 'rad', 'mrad'}, optional) – Output unit for phase. Default is degrees.
- Returns:
amp (ndarray) – Vector magnitude
sqrt(Tx² + Ty²).phi (ndarray) – Phase of the complex vector
Tx + i Ty.
- Return type:
Notes
If shape is
(..., 1, 2)it is squeezed to(..., 2)prior to computation.
- phase_tensor(z)#
Phase tensor
Φ = X⁺ Yfrom impedanceZ = X + iY.This computes a real 2×2 phase tensor using a robust pseudoinverse
X⁺of the real part ofZ. Shapes are broadcast over the leading axes.- Parameters:
z (array_like, shape (..., 2, 2)) – Impedance tensor(s)
Z = X + iY.- Returns:
Phi – Real phase tensor.
- Return type:
ndarray, shape (…, 2, 2)
Notes
If
Xis ill-conditioned, the pseudoinverse stabilizes the solution.Φis invariant to site gain and captures the 2-D/3-D character of the response.Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> Z = np.array([[[0+1j, 2+3j],[4+5j, 6+7j]]], dtype=complex) >>> Phi = MTBase().phase_tensor(Z) >>> Phi.shape (1, 2, 2)
See also
- phase_tensor_params(z, *, angle_unit='deg')#
Principal phase angles, azimuth, skew, and ellipticity.
Derives quick-look parameters from the phase tensor
Φ: principal phase angles (φ_max,φ_min), phase tensor azimuthα, skew angleβ, and ellipticity.- Parameters:
z (array_like, shape (..., 2, 2)) – Impedance tensor(s).
angle_unit ({'deg', 'rad', 'mrad'}, optional) – Output unit for angular quantities. Default is degrees.
- Returns:
phi_max (ndarray, shape (…)) – Larger principal phase angle derived from
Φ.phi_min (ndarray, shape (…)) – Smaller principal phase angle derived from
Φ.alpha (ndarray, shape (…)) – Phase tensor azimuth.
beta (ndarray, shape (…)) – Phase tensor skew angle.
ellipt (ndarray, shape (…)) – Ellipticity proxy
(λ_max - λ_min)/(λ_max + λ_min), whereλare the eigenvalues used internally.
- Return type:
Notes
This implementation obtains parameters from an eigen decomposition of
Φand simple angle formulas (Caldwell- style azimuth/skew heuristics). It is suitable for quality control and strike reconnaissance.Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> Z = np.zeros((5, 2, 2), complex) >>> z = MTBase() >>> pmax, pmin, az, skew, eps = z.phase_tensor_params(Z) >>> pmax.shape == pmin.shape == az.shape == skew.shape == eps.shape True
See also
- phase_tensor_azimuth(z, *, unit='deg')#
Azimuth of the phase tensor principal axis.
- Parameters:
z (array_like, shape (..., 2, 2)) – Impedance tensor(s).
unit ({'deg', 'rad', 'mrad'}, optional) – Output unit. Default is degrees.
- Returns:
alpha – Phase tensor azimuth.
- Return type:
ndarray, shape (…)
Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> Z = np.zeros((3, 2, 2), complex) >>> MTBase().phase_tensor_azimuth(Z).shape (3,)
See also
- static tipper_rotate(t, theta_deg)#
Rotate tipper vectors by
theta_degin the horizontal plane.- Parameters:
t (array_like, shape (..., 2) or (..., 1, 2)) – Tipper components
(T_x, T_y). A trailing(1, 2)is squeezed.theta_deg (float) – Rotation angle in degrees. Positive angles rotate the coordinates clockwise (same convention as
rotate_impedance()).
- Returns:
t_rot – Rotated tipper vectors.
- Return type:
ndarray, shape (…, 2)
Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> T = np.array([[0.2+0.1j, -0.1+0.3j]]) >>> MTBase.tipper_rotate(T, 30.0).shape (1, 2)
See also
- static induction_arrows(t, *, convention='wiese', use_imag=False)#
Compute 2-D induction arrows from tipper components.
- Parameters:
t (array_like, shape (..., 2) or (..., 1, 2)) – Tipper components
(T_x, T_y).convention ({'wiese', 'parkinson'}, optional) – Arrow convention.
'wiese'uses a 90° rotation from the real (or imaginary) tipper;'parkinson'uses the components directly. Default'wiese'.use_imag (bool, optional) – If
True, use imaginary parts; else use real parts. DefaultFalse.
- Returns:
ax (ndarray, shape (…)) – X-component of the arrow.
ay (ndarray, shape (…)) – Y-component of the arrow.
- Return type:
Notes
Wiese arrows often highlight current channeling (2-D/3-D). Use imaginary arrows (
use_imag=True) for deeper structure emphasis at higher periods.Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> T = np.array([[0.2+0.1j, -0.1+0.3j]]) >>> ax, ay = MTBase.induction_arrows(T, convention='wiese') >>> ax.shape == ay.shape True
See also
- swift_skew(z, *, unit='deg')#
Swift skew parameter from the impedance tensor.
Computes the complex Swift skew ratio
s = (Z_xx + Z_yy) / (Z_xy - Z_yx),returning its magnitude and angle.
- Parameters:
z (array_like, shape (..., 2, 2)) – Impedance tensor(s).
unit ({'deg', 'rad', 'mrad'}, optional) – Unit for the returned angle. Default is degrees.
- Returns:
amp (ndarray, shape (…)) – Magnitude
|s|(dimensionless).ang (ndarray, shape (…)) – Argument of
sin the requested unit.
- Return type:
Notes
Low
|s|values are consistent with 1-D/2-D structure; an elevated skew suggests 3-D effects or distortion.Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> Z = np.ones((4, 2, 2), dtype=complex) >>> amp, ang = MTBase().swift_skew(Z) >>> amp.shape == ang.shape True
- apparent_conductivity_from_z(z, f)#
Apparent conductivity
σₐ = μ₀ ω / |Z|²(S/m).- Parameters:
z (array_like) – Impedance (per component or invariant).
f (array_like) – Frequency (Hz), broadcastable to
z.
- Returns:
sigma_a – Apparent conductivity (S/m).
- Return type:
ndarray
Notes
This is the reciprocal of the common SI resistivity formula
ρₐ = |Z|² / (μ₀ ω).Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> Z = np.array([5.0, 10.0]) # Ω >>> f = np.array([1.0, 0.1]) # Hz >>> MTBase().apparent_conductivity_from_z(Z, f).shape (2,)
See also
- halfspace_impedance(f, rho)#
Plane-wave impedance over a uniform half-space.
For a homogeneous half-space of resistivity
ρ, the surface impedance isZ = (1 + i) / √2 · √(μ₀ ω ρ),with a constant 45° phase and amplitude
∝ √(ω ρ).- Parameters:
f (array_like) – Frequency (Hz).
rho (array_like) – Resistivity (Ω·m).
- Returns:
Z – Complex half-space impedance.
- Return type:
ndarray
Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> f = np.logspace(-3, 3, 5) >>> Z = MTBase().halfspace_impedance(f, 100.0) >>> Z.shape (5,)
- z_mvk_nt_to_ohms(z)#
Convert
Zfrom(mV/km)/nTto ohms (Ω).This helper assumes the denominator is magnetic flux density
Bin nT (notH). It converts to SI(V/m)/Tand then multiplies byμ₀to obtainV/A = Ω.- Parameters:
z (array_like) – Impedance in mixed field units
(mV/km)/nT.- Returns:
Z_ohms – Impedance in ohms (Ω).
- Return type:
ndarray
Notes
If your denominator is already the magnetic field intensity
Hin A/m, you should not multiply byμ₀. Use careful unit bookkeeping in mixed-unit workflows.Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> z_field = np.array([50.0]) # (mV/km)/nT >>> Z_ohm = MTBase().z_mvk_nt_to_ohms(z_field) >>> Z_ohm.shape (1,)
- static rotate_fields(e, h, theta_deg)#
Rotate horizontal electric and magnetic field vectors.
- Parameters:
- Returns:
e_rot (ndarray, shape (…, 2)) – Rotated electric field components.
h_rot (ndarray, shape (…, 2)) – Rotated magnetic field components.
- Return type:
Examples
>>> from pycsamt.core.base import MTBase >>> import numpy as np >>> e = np.array([[1.0, 0.0]]) >>> h = np.array([[0.0, 1.0]]) >>> e_r, h_r = MTBase.rotate_fields(e, h, 45.0) >>> e_r.shape == h_r.shape True
See also
- class pycsamt.core.TFBundle(freq=None, z=None, z_err=None, tipper=None, tipper_err=None, rho=None, phase=None, station=None, station_id=None, lat=None, lon=None, elev=None, azimuth=None, attrs=<factory>)#
Bases:
CoreObjectLightweight, neutral payload for transfer functions.
The bundle holds frequency-indexed arrays and optional site metadata. It is intentionally permissive about array types (e.g., lists or NumPy arrays) so that backends can populate and consume it without hard dependencies.
- Parameters:
freq (array_like or None) – Frequency vector, length
n.z (array_like or None) – Impedance tensor, shape
(n, 2, 2)(complex).z_err (array_like or None) – Errors for
z, shape(n, 2, 2)(float).tipper (array_like or None) – Magnetic tipper. Common shapes are
(n, 2)or(n, 1, 2); callers may normalize as needed.tipper_err (array_like or None) – Errors for the tipper, shape like
tipper.rho (array_like or None) – Apparent resistivity, length
n(float).phase (array_like or None) – Phase, length
n. Units are backend-specific; the Zonge path often stores milliradians.station (str or None) – Preferred station/site name.
station_id (str, int or None) – Identifier used to synthesize a name when missing.
lat (float or None) – Optional geographic location and elevation.
lon (float or None) – Optional geographic location and elevation.
elev (float or None) – Optional geographic location and elevation.
azimuth (float or None) – Sensor azimuth or site orientation (degrees).
attrs (dict) – Free-form attributes that accompany the data.
Notes
Only one of
zor(rho, phase)is required for most workflows. Transformers may fill the missing part using package settings (seepycsamt.core.config).Examples
>>> from pycsamt.core.base import TFBundle >>> TFBundle(freq=[1.0], z=[[ [0+1j, 0], [0, 0+1j] ]], ... station=\"S001\") TFBundle(...)
- pycsamt.core.to_edi(source, *, key=None, **kwargs)#
Dispatch
sourceto a registered adapter and return EDI.This function consults the adapter registry managed by
pycsamt.core.config. It attempts to infer the adapter key when not provided and calls the associated factory.- Parameters:
- Returns:
An EDI object or an EDI collection, depending on the adapter.
- Return type:
- Raises:
RuntimeError – If the key cannot be inferred or no adapter is registered for it.
See also
pycsamt.core.config.register_adapterRegister new adapters at runtime.
pick_adapter_keyLightweight key inference from object metadata.
Examples
Register a trivial adapter and convert an object:
>>> from pycsamt.core.config import register_adapter >>> class Dummy: pass >>> Dummy.__module__ = 'pycsamt.zonge.avg' >>> register_adapter('avg', lambda src, **k: {'edi': True}) >>> to_edi(Dummy()) {'edi': True}
- exception pycsamt.core.RegistryError#
Bases:
RuntimeErrorError raised for registry and manifest operations.
Use this for signaling inconsistencies, corruption, or unsupported formats detected while loading or saving manifests or while mutating registry content.
- class pycsamt.core.Record(rid, kind, path=None, fmt=None, dataid=None, station_id=None, tags=<factory>, meta=<factory>, checksum=None, created=<factory>, updated=<factory>)#
Bases:
CoreObjectA single registry item with identifiers and metadata.
- Parameters:
rid (str) – Unique record id. Often a value from
_uuid().kind (str) – Logical category. See
guess_kind()for common labels (e.g."edi","avg").path (str or None, optional) – Path (relative to
Manifest.rootor absolute) to the primary file associated with this record.fmt (str or None, optional) – Short format tag (e.g.
"edi"or"json"). Free form and optional.dataid (str or None, optional) – External data id or survey id if applicable.
station_id (str or None, optional) – Station or site identifier for field data.
tags (list of str, optional) – Free-form labels for grouping or filtering.
meta (dict, optional) – Arbitrary JSON-serializable metadata.
checksum (str or None, optional) – SHA-256 hex digest of the file at
path. Use_sha256()to compute it. Optional.created (float, optional) – Creation timestamp in seconds since epoch. Defaults to
_now().updated (float, optional) – Last update timestamp in seconds since epoch. Defaults to
_now().
- Variables:
Notes
The class is a
dataclassesdataclass and is designed to round-trip withto_dict()andfrom_dict().Examples
Create a record with a checksum:
from pathlib import Path p = Path("data/site001.edi") r = Record(rid=_uuid(), kind="edi", path=str(p), checksum=_sha256(p))
See also
- touch()#
Bump
updatedto the current time.Notes
Uses
_now(). Does not mutate any other fields.- Return type:
None
- to_dict()#
Return a JSON-ready mapping for this record.
- Returns:
A plain
dictproduced viadataclasses.asdict().- Return type:
- class pycsamt.core.Manifest(root, version=1, records=<factory>)#
Bases:
CoreObjectContainer mapping record ids to
Recordobjects.- Parameters:
- Variables:
Notes
The manifest is serializable through
to_dict()andfrom_dict(). Storage format (JSON, TOML, etc.) is left toManifestStoreimplementations.Examples
Build a manifest programmatically:
man = Manifest(root="data") rec = Record(rid=_uuid(), kind="edi", path="a.edi") man.records[rec.rid] = rec
See also
- to_dict()#
Return a nested mapping suitable for JSON or TOML.
A
dictwithroot,version, and a nestedrecordsmapping of ids to plain dicts.
- class pycsamt.core.ManifestStore#
Bases:
CoreObjectInterface for loading and saving
Manifestobjects.Subclasses provide concrete persistence (e.g. JSON on disk, TOML, or a database). The interface is intentionally small.
Notes
Implementations should validate the manifest version and any schema expectations relevant to their storage.
Examples
A minimal JSON store sketch:
class JsonStore(ManifestStore): def load(self, path: Path) -> Manifest: data = json.loads(path.read_text()) return Manifest.from_dict(data) def save(self, man: Manifest, path: Path) -> None: path.write_text(json.dumps(man.to_dict(), indent=2))
- class pycsamt.core.FileManifestStore#
Bases:
ManifestStoreDisk-backed
Manifestpersistence utilities.This store loads and saves manifests from files on disk. Reading supports JSON by default, and can parse TOML when
tomllibis available. Saving is JSON-only in this implementation.Behavior#
If the given
pathdoes not exist on load, a new emptyManifestis returned withrootset to the parent directory ofpath.Files are read and written with
utf-8encoding.TOML saving is not implemented and raises
RegistryError.
Notes
This class is intentionally minimal and does not implement atomic writes or file locking. For concurrent writers use a safer store or wrap calls with your own locking.
Examples
>>> from pathlib import Path >>> from pycsamt.core._registry import ( ... FileManifestStore, Manifest) >>> store = FileManifestStore() >>> mpath = Path("manifest.json") >>> man = store.load(mpath) >>> isinstance(man, Manifest) True >>> store.save(man, mpath)
See also
References
[1] Python Standard Library. json module.
[2] Python 3.11+. tomllib — TOML parser.
- load(path)#
Load a
Manifestfrompath.- Parameters:
path (pathlib.Path) – File to read. If it does not exist, return a new empty manifest with
rootset topath.parent.- Returns:
Deserialized manifest.
- Return type:
- Raises:
json.JSONDecodeError – If JSON parsing fails.
OSError – For I/O errors.
Notes
When the suffix is
.tomlandtomllibis missing, the content is parsed as JSON, which will likely fail. Installtomllib(Python 3.11+) to read TOML files.
- save(man, path)#
Serialize
manto JSON atpathusingutf-8.- Parameters:
man (Manifest) – Manifest to write.
path (pathlib.Path) – Destination file. The suffix determines support.
- Raises:
RegistryError – If a TOML file is requested (
.tomlsuffix).OSError – For write or permission errors.
- Return type:
None
Notes
This method writes pretty-printed JSON (
indent=2). TOML saving is not implemented here by design.
- class pycsamt.core.Registry(root, *, manifest_name='manifest.json', store=None)#
Bases:
CoreObjectHigh-level helper to manage a file-backed registry.
A
Registrybinds a filesystemrootwith a manifest file and provides convenience methods to add files or objects, query records, and persist changes.- Parameters:
root (path-like) – Base directory for data and the manifest file.
manifest_name (str, optional) – Manifest filename within
root. Defaults to"manifest.json".store (ManifestStore or None, optional) – Custom persistence backend. Defaults to
FileManifestStore.
- Variables:
root (pathlib.Path) – Absolute path to the registry root.
manifest_path (pathlib.Path) – Full path of the manifest file.
store (ManifestStore) – Persistence backend in use.
manifest (Manifest) – In-memory manifest loaded at construction.
Notes
On initialization, the manifest is loaded (or created if missing) and its
rootis set to the absoluterootpath. All relative paths passed later are resolved underroot.Examples
Create a registry and register an EDI file:
>>> from pycsamt.core._registry import Registry >>> reg = Registry("data") >>> rec = reg.add_path("site001.edi", kind="edi", ... fmt="edi") >>> reg.get(rec.rid).kind 'edi'
Register a Python object and tag it:
>>> class Dummy: ... station = "S01" ... station_id = 1 >>> d = Dummy() >>> rec = reg.add_obj(d, tags=["demo"]) >>> "demo" in rec.tags True
Query and update metadata:
>>> regs = reg.list(kind="edi") >>> found = reg.find(kind="edi") >>> _ = reg.update_meta(rec.rid, site="S001")
See also
Record,Manifest,ManifestStore,FileManifestStore,guess_kindReferences
[1] D. Wight (1991). SEG MT/EMAP EDI Standard. Society of Exploration Geophysicists.
[2] Python Standard Library. pathlib module.
- save()#
Persist the in-memory manifest to
manifest_path.Notes
Delegates to
store. Use this after batch updates if you have modifiedmanifestdirectly.- Return type:
None
- add_path(p, *, kind=None, fmt=None, dataid=None, station_id=None, tags=None, meta=None, with_hash=True)#
Add a file-based record into the registry.
- Parameters:
p (path-like) – File path. If relative, it is resolved under
root.kind (str or None, optional) – Logical kind for the record. Defaults to
"meta"when not provided.fmt (str or None, optional) – Short format tag, such as
"edi".dataid (str or None, optional) – External dataset id or survey id.
station_id (str or int or None, optional) – Station identifier. Converted to string if provided.
tags (Iterable[str] or None, optional) – User-defined labels.
meta (dict or None, optional) – Extra JSON-serializable metadata.
with_hash (bool, optional) – If
Trueand the file exists, compute SHA-256 and setRecord.checksum. Defaults toTrue.
- Returns:
The created record.
- Return type:
Notes
When the file does not exist, the checksum is left unset. The manifest is saved immediately after insertion.
Examples
>>> from pycsamt.core._registry import Registry >>> reg = Registry("data") >>> rec = reg.add_path("a.edi", kind="edi")
- add_obj(obj, *, rid=None, kind=None, tags=None, meta=None, path=None)#
Add an object-backed record using heuristics for
kind.- Parameters:
obj (Any) – Object to classify.
guess_kind()is used whenkindis not provided.rid (str or None, optional) – Optional explicit record id. Defaults to a random id.
kind (str or None, optional) – Override classification if given.
tags (Iterable[str] or None, optional) – User-defined labels.
meta (dict or None, optional) – Extra JSON-serializable metadata.
path (str or pathlib.Path or None, optional) – Optional path reference to associate with the object.
- Returns:
The created record.
- Return type:
Notes
If the object exposes attributes
stationorstation_id, they are copied into the record when available. The manifest is saved immediately.Examples
>>> from pycsamt.core._registry import Registry >>> reg = Registry("data") >>> class Obj: ... station = "S02"; station_id = 2 >>> rec = reg.add_obj(Obj(), tags=["x"])
- list(*, kind=None)#
List records, optionally filtering by
kind.
- find(*, tag=None, kind=None, dataid=None)#
Filter records by any combination of tag, kind, or dataid.
- Parameters:
- Returns:
Records satisfying all provided predicates.
- Return type:
Examples
>>> from pycsamt.core._registry import Registry >>> reg = Registry("data") >>> reg.find(kind="edi")
- update_meta(rid, **fields)#
Update
metafields of the record with idrid.- Parameters:
rid (str) – Record identifier.
**fields (Any) – Key-value pairs merged into
Record.meta.
- Returns:
The updated record.
- Return type:
Notes
Updates the
updatedtimestamp and saves the manifest.
- pycsamt.core.guess_kind(obj)#
Best-effort classification for common MT/EM artefacts.
The function inspects
obj.__class__.__module__and class name to assign a coarse category usable by higher-level tools. It recognizes common terms such as"seg","edi","zonge","avg", and"jones".- Parameters:
obj (Any) – Any Python object. Only its class name and module path are inspected. Iterables are handled specially.
- Returns:
One of the following labels:
"edi"— an EDI item."edi_col"— an EDI collection."avg"— a Zonge AVG artefact."j"— a Jones J-file object."j_col"— a collection of J-files."list"— a list or tuple of items."meta"— a fallback for other metadata.
- Return type:
Notes
This is heuristic and string-based. It is intentionally liberal to remain dependency-free. Extend or override at the call site if stricter typing is needed.
Examples
>>> class Dummy: ... >>> guess_kind(Dummy()) 'meta' >>> guess_kind([]) 'list'
- pycsamt.core.register_packer(kind, packer)#
Register a serializer/deserializer pair for a given
kind.- Parameters:
- Raises:
ValueError – If
kindis empty or not astr.- Return type:
None
Notes
Registration is global to the current process via an internal dictionary. Re-registering the same
kindwill overwrite the previous pair.Examples
>>> def pack(x): return {"v": x} >>> def unpack(d): return d["v"] >>> register_packer("toy", (pack, unpack)) >>> get_packer("toy") is not None True
See also
References
[1] NumPy.
np.savez_compressedfor NPZ payloads.
- pycsamt.core.get_packer(kind)#
Return the registered packer for
kindorNone.- Parameters:
kind (str) – Case-insensitive key used at registration time.
- Returns:
The
(pack, unpack)pair if found, elseNone.- Return type:
tuple(callable, callable) or None
Examples
>>> _ = get_packer("missing") is None >>> # After registering: >>> # register_packer("toy", (pack, unpack)) >>> pk = get_packer("toy") >>> callable(pk[0]) and callable(pk[1]) True
See also
- pycsamt.core.list_packers()#
List available packers as a mapping of kind to signatures.
- Returns:
Mapping
kind -> "pack_name | unpack_name"for quick inspection.- Return type:
Notes
Only names available via
__name__are shown. For callables without a name,repris used.Examples
>>> isinstance(list_packers(), dict) True
See also
- pycsamt.core.pack_to_file(obj, path, *, kind='bundle')#
Serialize an object to
.npzusing a registered packer.- Parameters:
- Returns:
The path actually written.
- Return type:
- Raises:
ValueError – If no packer is registered for
kind.OSError – For filesystem write errors.
Notes
Data are written with
numpy.savez_compressed(), which stores arrays losslessly and compresses the archive.Examples
Pack a bundle to disk:
>>> # Assume a TFBundle instance: bndl >>> out = pack_to_file(bndl, "site001.npz", kind="bundle") >>> out.name.endswith(".npz") True
References
[1] NumPy Reference. np.savez_compressed.
- pycsamt.core.unpack_from_file(path, *, kind='bundle')#
Deserialize an object from
.npzusing a registered packer.- Parameters:
path (path-like) – Input NPZ path created by
pack_to_file().kind (str, optional) – Packer name. Defaults to
"bundle". Must match the packer used during serialization.
- Returns:
The reconstructed object.
- Return type:
- Raises:
ValueError – If no packer is registered for
kind.OSError – For filesystem read errors.
KeyError – If expected payload keys are missing for the packer.
Notes
Loading uses
allow_pickle=Falsefor safety; payloads must be composed of basic NumPy/JSON types.Examples
Load a previously saved bundle:
>>> obj = unpack_from_file("site001.npz", kind="bundle") >>> hasattr(obj, "z") and hasattr(obj, "freq") True
See also
- class pycsamt.core.RegistryAPI(root, *, manifest_name='manifest.json')#
Bases:
CoreObjectConvenience façade over
Registry.This high-level helper wires a registry root, ensures a
packs/folder exists under that root, and exposes both thin proxies to low-level registry calls and ergonomic helpers for packing objects to NPZ bundles and restoring them later.- Parameters:
root (path-like) – Base directory that will contain the manifest and, if needed, a
packs/subdirectory for NPZ bundles.manifest_name (str, optional) – Manifest filename within
root. Defaults to"manifest.json".
- Variables:
low (Registry) – The underlying registry instance that performs the core operations (load/save, add, query).
root (pathlib.Path) – Absolute path to the registry root directory.
pack_dir (pathlib.Path) – Directory where NPZ bundles are stored (
root/packs).
Notes
RegistryAPIaims to be pragmatic and file-system first. It does not do locking or concurrency control. For multi- process scenarios, add your own synchronization.The packing flow defaults to the
"bundle"packer, which serializesTFBundle-compatible objects into NPZ usingnumpy.savez_compressed().Examples
Create an API, add an EDI file, and list records:
>>> from pycsamt.core.registry import RegistryAPI >>> api = RegistryAPI("data") >>> _ = api.add_file("site001.edi", kind="edi", fmt="edi") >>> [r.kind for r in api.list(kind="edi")] ['edi']
Pack an object to an NPZ bundle and store it in
packs:>>> class Dummy: ... station = "S01"; station_id = 1 >>> rec = api.add_object(Dummy(), pack=True, ... pack_name="S01.npz") >>> rec.kind 'bundle'
Materialize a record back into a Python object:
>>> obj = api.materialize(rec.rid) # may be Dummy or path
Convert a record to an EDI representation:
>>> edi_like = api.to_edi(rec.rid)
See also
pycsamt.core.registry.Registry,pycsamt.core.registry.pack_to_file,pycsamt.core.registry.unpack_from_file,pycsamt.core.registry.guess_kind,pycsamt.seg.edi.EDIFile,pycsamt.zonge.avg.AVG,pycsamt.jones.j.JFile,pycsamt.core.base.to_ediReferences
[1] Wight (1991). SEG MT/EMAP EDI Standard.
[2] NumPy Reference. np.savez_compressed, np.load.
- save()#
Persist the manifest to disk.
Thin proxy to
Registry.save(). Use after mutating the underlying manifest directly, though typical helpers call save for you.- Return type:
None
- list(*, kind=None)#
List all records, optionally filtered by kind.
- Parameters:
kind (str or None, optional) – If provided, only records whose
kindmatches are returned.- Returns:
Shallow views of stored
Recordobjects.- Return type:
Examples
>>> from pycsamt.core.registry import RegistryAPI >>> api = RegistryAPI("data") >>> isinstance(api.list(), list) True
- get(rid)#
Return the record by id or raise an error.
- Parameters:
rid (str) – Record identifier returned at insertion time.
- Returns:
The matching record.
- Return type:
- Raises:
RegistryError – If the id does not exist in the manifest.
- find(*, tag=None, kind=None, dataid=None)#
Filter records by tag, kind, and/or data id.
- Parameters:
- Returns:
Records satisfying all given predicates.
- Return type:
Examples
>>> from pycsamt.core.registry import RegistryAPI >>> api = RegistryAPI("data") >>> api.find(kind="edi") [...] # list of records
- add_file(path, *, kind=None, fmt=None, dataid=None, station_id=None, tags=None, meta=None, with_hash=True)#
Register a file path as a new record.
- Parameters:
path (path-like) – File path. Relative paths are resolved under
root.kind (str or None, optional) – Logical kind (e.g.
"edi","avg"). If omitted, defaults to"meta".fmt (str or None, optional) – Short format hint (e.g.
"edi").dataid (str or None, optional) – External data or survey id.
station_id (str or int or None, optional) – Station identifier. Cast to string if provided.
tags (list[str] or None, optional) – User-defined labels to ease later queries.
meta (dict[str, Any] or None, optional) – Extra JSON-serializable metadata.
with_hash (bool, optional) – If
Trueand the file exists, compute and store a SHA-256 checksum. Defaults toTrue.
- Returns:
The created and persisted record.
- Return type:
Notes
This method saves the manifest on success.
Examples
>>> from pycsamt.core.registry import RegistryAPI >>> api = RegistryAPI("data") >>> rec = api.add_file("site001.edi", ... kind="edi", fmt="edi") >>> rec.kind, rec.fmt ('edi', 'edi')
- add_object(obj, *, kind=None, tags=None, meta=None, pack=False, pack_name=None)#
Register an object and, optionally, pack it to an NPZ bundle.
- Parameters:
obj (Any) – Object to register. If
kindisNone, a coarse label is inferred viaguess_kind().kind (str or None, optional) – Explicit kind override (e.g.
"edi","bundle").meta (dict[str, Any] or None, optional) – Extra JSON-serializable metadata for the record.
pack (bool, optional) – If
True, serializeobjwith the"bundle"packer intopack_dirand rewrite the record to refer to that NPZ on disk. Defaults toFalse.pack_name (str or None, optional) – Output NPZ filename when
pack=True. Defaults to"<rid>.npz".
- Returns:
The created record. When
pack=True, the recordkindbecomes"bundle"andpathpoints to the NPZ file insidepack_dir.- Return type:
Notes
Packing uses
pack_to_file()withkind="bundle". The manifest is saved after packing.Examples
>>> from pycsamt.core.registry import RegistryAPI >>> api = RegistryAPI("data") >>> class Obj: station="S02"; station_id=2 >>> rec = api.add_object(Obj(), tags=["demo"]) >>> isinstance(rec.rid, str) True
Pack immediately to NPZ:
>>> rec2 = api.add_object(Obj(), pack=True, ... pack_name="S02.npz") >>> rec2.kind 'bundle'
See also
pycsamt.core.registry.pack_to_file,pycsamt.core.registry.guess_kind
- materialize(rid)#
Load a record into a concrete Python object when possible.
- Parameters:
rid (str) – Record id to resolve.
- Returns:
If the record is a
"bundle"with a valid path, returns the unpacked object viaunpack_from_file(). For known kinds with readable files this returns:"edi"->pycsamt.seg.edi.EDIFile"avg"->pycsamt.zonge.avg.AVG"j"or"j_col"->pycsamt.jones.j.JFile
Otherwise, returns the string path (or
None) of the record.- Return type:
Notes
Errors during import or read are swallowed and the path is returned instead. This keeps the method side-effect free with respect to the registry while remaining robust.
Examples
>>> from pycsamt.core.registry import RegistryAPI >>> api = RegistryAPI("data") >>> # Assuming a valid record id: >>> # obj = api.materialize(rid)
- to_edi(rid, **kw)#
Convert a record’s materialized object into an EDI-like form.
- Parameters:
- Returns:
An object produced by
to_edi(). The concrete type depends on the available converters and inputs.- Return type:
Notes
This is a convenience wrapper that first calls
materialize(), then delegates topycsamt.core.base.to_edi().Examples
>>> from pycsamt.core.registry import RegistryAPI >>> api = RegistryAPI("data") >>> # Convert an existing record to an EDI representation >>> edi_like = api.to_edi("some-record-id")
- pycsamt.core.bundle_from_edi(obj)#
Build a
TFBundlefrom an EDI-like object.The function probes common attribute names found in EDI objects and populates a neutral bundle. It tolerates missing fields and does not enforce array types.
- Parameters:
obj (Any) – EDI-like object. Attributes such as
freq,z,z_err,tipper,rho,phase,station,lat,lon,elev,azimuthare searched with several conventional aliases.- Returns:
A bundle with fields filled where data could be found.
- Return type:
Notes
If the tipper has shape
(n, 1, 2)it is normalized to(n, 2). Unknown shapes are left unchanged.Examples
>>> from pycsamt.core.mixins import bundle_from_edi >>> class E: ... def __init__(self): ... self.freq = [1.0, 2.0] ... self.phase = [45.0, 50.0] ... self.rho = [100.0, 120.0] ... >>> b = bundle_from_edi(E()) >>> b.freq, b.phase[0] ([1.0, 2.0], 45.0)
- class pycsamt.core.BundleMixin#
Bases:
CoreObjectMixin for single items convertible to and from bundles.
Subclasses implement
to_bundleandfrom_bundleto map between their internal representation and the neutralTFBundle.Notes
to_edidelegates topycsamt.core.base.to_edi(), which uses the adapter registry to pick the right converter.Examples
>>> from pycsamt.core.mixins import BundleMixin >>> from pycsamt.core.base import TFBundle >>> >>> class Item(BundleMixin): ... def __init__(self, b): self._b = b ... def to_bundle(self): return self._b ... @classmethod ... def from_bundle(cls, b): return cls(b) ... >>> b = TFBundle(freq=[1.0], rho=[100.], phase=[45.]) >>> it = Item.from_bundle(b) >>> isinstance(it.to_bundle(), TFBundle) True
- classmethod from_bundle(bundle)#
Construct an instance from a
TFBundle.Returns a new instance of the implementing class.
Must be implemented by subclasses.
- Parameters:
bundle (TFBundle)
- static ensure_station_name(name, station_id)#
Validate or synthesize a station name via policy.
- Parameters:
- Returns:
Validated or synthetic name.
- Return type:
See also
pycsamt.core.base.ensure_stationUnderlying helper with policy lookup.
- to_edi(*, key=None, **k)#
Convert
selfto EDI or EDICollection via dispatch.- Parameters:
- Returns:
EDI object or EDICollection, depending on adapter.
- Return type:
Notes
Dispatch is handled by
pycsamt.core.base.to_edi(). A suitable adapter must have been registered, otherwise a runtime error is raised.
- classmethod from_edi(edi_obj)#
Construct instance(s) from an EDI object or a collection.
If
edi_objlooks like a collection, every element is converted to aTFBundleand fed tofrom_bundle. Otherwise the single object is converted and fed tofrom_bundle.- Parameters:
edi_obj (Any) – EDI object or an iterable of EDI objects.
- Returns:
Instance or list of instances of the implementing class.
- Return type:
Examples
>>> from pycsamt.core.mixins import BundleMixin, bundle_from_edi >>> class I(BundleMixin): ... def __init__(self, b): self._b = b ... def to_bundle(self): return self._b ... @classmethod ... def from_bundle(cls, b): return cls(b) ... >>> e = type('E', (), {'freq':[1.0], 'rho':[100.], 'phase':[45.]})() >>> obj = I.from_edi(e)
- class pycsamt.core.BundleContainerMixin#
Bases:
BundleMixinMixin for containers of items that implement
to_bundle.This mixin provides iteration over bundles and a convenience method to convert all items to an EDI collection using the adapter dispatch.
Notes
Detection of children is permissive: if the container has an
items()method, values are inspected; otherwise the container is iterated directly.Examples
>>> from pycsamt.core.mixins import BundleContainerMixin >>> from pycsamt.core.base import TFBundle >>> >>> class Bag(dict, BundleContainerMixin): ... pass ... >>> _ = Bag({0: type('X',(),{'to_bundle':lambda s: TFBundle()})()}) >>> list(_.iter_bundles())[0].__class__.__name__ 'TFBundle'
- to_edi_collection(*, key=None, **k)#
Convert all child bundles through adapter dispatch.
- Parameters:
- Returns:
A list of EDI objects. Callers may wrap this into an actual
EDICollection.- Return type:
Examples
>>> from pycsamt.core.mixins import BundleContainerMixin >>> # Requires registered adapters to actually run: >>> # objs = MyContainer(...).to_edi_collection()
- class pycsamt.core.StationNamePolicy(allow_pattern='A-Za-z0-9_\\-', maxlen=32, prefix='S', pad=3, strip=True, custom_normalize=<function StationNamePolicy.<lambda>>)#
Bases:
objectRules to validate and synthesize station names.
This policy is applied during inter-format conversion (AVG or Jones -> EDI). When a provided name is missing or invalid, a synthetic name is derived from the station id.
- Parameters:
allow_pattern (str, optional) – Regex character class (without brackets) that defines the whitelist of allowed characters. The default accepts ASCII letters, digits, underscore and dash.
maxlen (int, optional) – Maximum length of a station name after normalization.
prefix (str, optional) – Prefix used when creating synthetic names.
pad (int, optional) – Zero-padding width for numeric station ids.
strip (bool, optional) – If True, strip leading and trailing whitespace first.
custom_normalize (Callable[[str], str], optional) – Hook called before validation. Can perform additional transliteration or case normalization.
Notes
Normalization runs as: strip → custom_normalize → filter by
allow_pattern→ truncate tomaxlen. Empty results are treated as invalid.Examples
>>> from pycsamt.core.config import StationNamePolicy >>> pol = StationNamePolicy(prefix=\"S\", pad=4) >>> pol.ensure(\" Site-1 \", station_id=None) 'Site-1' >>> pol.ensure(None, station_id=7) 'S0007'
- static custom_normalize(s)#
- validate(name)#
Validate and normalize a name.
- Parameters:
name (str or None) – Candidate station name.
- Returns:
Normalized name if valid; otherwise
None.- Return type:
str or None
Notes
The method does not synthesize names. Use
ensurefor a name-or-fallback behavior.
- synthesize(station_id)#
Create a deterministic synthetic name from a station id.
- Parameters:
station_id (Any or None) – Station identifier. Numeric ids are zero-padded. Non- numeric ids are compacted by removing non-word chars.
- Returns:
Synthetic station name.
- Return type:
Examples
>>> StationNamePolicy().synthesize(12) 'S012' >>> StationNamePolicy(prefix='X').synthesize('AB-01') 'AB01'
- ensure(name, station_id)#
Return a valid station name, validating or synthesizing.
- Parameters:
- Returns:
Validated name or a synthetic fallback.
- Return type:
See also
validateValidate only, without fallback.
synthesizeCreate a name from the station id only.
- class pycsamt.core.CoreConfig(empty=1e+32, strict=False, case_sensitive_sections=False, on_duplicate_station='replace', target_format='edi', log_level='WARNING', freq_order='desc', freq_tol=1e-09, compute_res_from_z=True, compute_z_from_res=True, load_spectra=True, load_time_series=False, station_policy=<factory>, error_fill_value=nan, infer_errors=True, encoding='utf-8', newline='\n', backend=<factory>)#
Bases:
objectGlobal configuration container for
pycsamt.The object stores defaults for parsing, conversion, and population of EDI data structures. Use
configure()for updates orconfig_context()for temporary overrides.- Parameters:
empty (float) – Sentinel used in legacy files to mark missing values.
strict (bool) – If True, raise on recoverable parse issues; otherwise, degrade gracefully when possible.
case_sensitive_sections (bool) – Treat section names as case-sensitive when reading.
on_duplicate_station ({'replace', 'keep', 'error'}) – Policy when collection loaders encounter duplicate station ids.
target_format ({'edi'}) – Preferred internal representation for processing. Keep as ‘edi’ to normalize all inputs to EDI objects.
log_level (str) – Default logging level for package loggers.
freq_order ({'asc', 'desc'}) – Preferred order for frequency vectors after normalization.
freq_tol (float) – Relative tolerance for de-duplicating near-equal frequencies.
compute_res_from_z (bool) – Compute (rho, phase) from Z when absent.
compute_z_from_res (bool) – Reconstruct Z from (rho, phase) when Z is missing.
load_spectra (bool) – If True, preserve spectra sections when available.
load_time_series (bool) – If True, preserve time-series sections when available.
station_policy (StationNamePolicy) – Validation and synthesis rules for station names.
error_fill_value (float) – Fill value used when error arrays are required by a consumer but not available.
infer_errors (bool) – If True, allow simple heuristics to infer missing errors from weights or coherency measures when present.
encoding (str) – Default text encoding for reading legacy files.
newline (str) – Line terminator to use when writing files.
backend (dict) – Reserved space for per-backend knobs.
Notes
The instance stored in this module is mutable by design. Use
configure()orconfig_context()instead of assigning attributes directly, so validation is applied.Examples
>>> from pycsamt.core.config import configure, get_config >>> _ = configure(freq_order='desc', strict=False) >>> get_config().freq_order 'desc'
- station_policy: StationNamePolicy#
- copy()#
Return a deep copy of the configuration.
- Returns:
A detached copy that can be safely mutated.
- Return type:
Notes
Used internally by
config_context()to restore the previous state atomically.
- pycsamt.core.get_config()#
Return the live
CoreConfigsingleton.- Returns:
The active configuration object.
- Return type:
Notes
The returned instance is mutable. Prefer
configure()orconfig_context()for controlled updates.
- pycsamt.core.configure(**kwargs)#
Update configuration fields with validation.
- Parameters:
**kwargs (Any) – Field names and values to set on the global config.
- Returns:
The updated configuration object.
- Return type:
- Raises:
AttributeError – If an unknown field name is provided.
ValueError – If a value is invalid for a known field.
Notes
Also aligns the package logger level to
log_level, if that logger has been created.Examples
>>> from pycsamt.core.config import configure >>> _ = configure(freq_order='asc', infer_errors=False)
- pycsamt.core.reset_config()#
Reset the global configuration to factory defaults.
Notes
A new
CoreConfiginstance replaces the existing singleton. Any references to the previous instance will not reflect subsequent changes.- Return type:
None
- pycsamt.core.config_context(**overrides)#
Context manager for temporary configuration overrides.
- Parameters:
**overrides (Any) – Field names and temporary values.
- Yields:
CoreConfig – The active configuration during the context.
- Return type:
Notes
On exit, the previous configuration is restored atomically, even if an exception is raised.
Examples
>>> from pycsamt.core.config import config_context, get_config >>> with config_context(strict=True): ... assert get_config().strict is True >>> assert get_config().strict is False
- pycsamt.core.to_dict()#
Serialize the current configuration to a plain dict.
- Returns:
A JSON-serializable mapping of all fields.
- Return type:
Examples
>>> from pycsamt.core.config import to_dict >>> d = to_dict() >>> 'freq_order' in d True
- pycsamt.core.register_adapter(key, factory)#
Register a format adapter that yields an EDI object or collection.
- Parameters:
- Raises:
ValueError – If
keyis not a non-empty string.- Return type:
None
Notes
Adapters are resolved lazily at call time, which avoids heavy imports and circular dependencies.
See also
get_adapterRetrieve a registered adapter.
list_adaptersInspect the registry.
- pycsamt.core.get_adapter(key)#
Return the adapter factory for a key.
- pycsamt.core.list_adapters()#
List registered adapters with readable names.
- Returns:
Mapping
{key: 'qualname'}for registered factories.- Return type:
Examples
>>> from pycsamt.core.config import list_adapters >>> isinstance(list_adapters(), dict) True
Core Modules#
Foundational objects and transfer-function containers for pyCSAMT. |
|
Runtime configuration, adapter registration, and configuration contexts. |
|
Reusable mixins for creating and manipulating transfer-function bundles. |
|
Public packer registry and serialization helpers for survey objects. |
Transformer Base#
- class pycsamt.core.TransformerMixin#
Bases:
MTBaseTemplate utilities for AVG/J -> EDI transformations.
This module defines
TransformerMixin, a light template that standardizes the steps needed to convert foreign transfer-function containers (e.g. Zonge AVG or Jones J) into EDI files or collections.The mixin focuses on finalization of a neutral payload (
pycsamt.core.base.TFBundle): validating the station name, ordering and de-duplicating frequencies, and optionally filling missing pieces (Zvsrho/phase) according to user config.Notes
Subclasses provide the data plumbing:
TransformerMixin.extract()pulls aTFBundleout of the source object or file.TransformerMixin.emit_edi()builds an EDI object from the finalized bundle.TransformerMixin.post_emit()can attach metadata or perform small last-mile fixes.
The default hooks for filling missing parts are no-ops. Backends can override
TransformerMixin.compute_res_from_z()andTransformerMixin.compute_z_from_res().Subclasses implement the data path while this mixin handles standard finalization steps on a
TFBundle:Validate or synthesize the station name using the global policy.
Order frequencies per config (asc/desc).
De-duplicate nearly equal frequencies using a relative tolerance.
Optionally fill missing parts (
Zvsrho/phase) via overridable hooks.
Finalization obeys the global configuration from
pycsamt.core.config. In particular:freq_ordercontrols sorting.freq_tolcontrols de-duplication.compute_res_from_zandcompute_z_from_restoggle filling behavior.
See also
pycsamt.core.transformers.AVGtoEDIConcrete AVG → EDI/EDICollection converter.
pycsamt.core.transformers.JtoEDIConcrete J → EDI/EDICollection converter.
pycsamt.core.base.TFBundleNeutral payload used across backends.
pycsamt.core.configGlobal configuration and frequency policies.
Examples
A minimal custom transformer:
>>> from pycsamt.core._transformers import TransformerMixin >>> from pycsamt.core.base import TFBundle >>> >>> class MyX(TransformerMixin): ... def extract(self, source): ... # pull TF data from source into a bundle ... return TFBundle(freq=[1.0], rho=[100.], phase=[45.]) ... def emit_edi(self, bundle): ... # return an EDI-like stub for illustration ... return {'freq': bundle.freq, 'rho': bundle.rho} ... >>> out = MyX().transform(object()) >>> isinstance(out, dict) True
References
[1] Simpson, F. & Bahr, K. (2005). Practical MT.
[2] Egbert, G. D. (1997). Robust MT processing.
- extract(source)#
Extract a
TFBundlefromsource.This is the ingest step. Implementors parse the foreign object (or file path) and return a neutral bundle. Keep the method free of side effects.
- Parameters:
source (Any) – Backend-specific object or path.
- Returns:
A bundle containing frequencies and transfer functions.
- Return type:
- Raises:
NotImplementedError – Must be provided by subclasses.
Notes
Prefer leaving station naming and frequency manipulation to the mixin. Provide raw content here.
- emit_edi(bundle)#
Materialize an EDI object from a finalized bundle.
This is the emit step. Implementors build and return an EDI object (or a compatible stub).
Must be provided by subclasses.
Notes
Do not attempt to reorder frequencies here. The bundle has already been finalized by
_finalize().
- post_emit(edi_obj, source, bundle)#
Optional last-mile adjustments after EDI creation.
Use this hook to enrich the EDI object with auxiliary metadata (e.g. site location, elevation) that is awkward to thread through earlier steps.
- Parameters:
edi_obj (Any) – The EDI object returned by
emit_edi().source (Any) – The original source object used in
extract().bundle (TFBundle) – The finalized bundle.
- Returns:
The potentially modified EDI object.
- Return type:
Notes
The default implementation returns
edi_objunmodified.
- compute_res_from_z(b)#
Compute apparent resistivity/phase from
Z.This hook is called by
_fill_missing()whencompute_res_from_zis True in the global config, and the bundle holdsZbut lacks(rho, phase).- Parameters:
b (TFBundle) – Input bundle.
- Returns:
The updated bundle (may be the same instance).
- Return type:
Notes
The default implementation is a no-op. Subclasses may override to apply the standard MT relations [1]_ [2]_.
- compute_z_from_res(b)#
Reconstruct
Zfrom apparent resistivity/phase.This hook is called by
_fill_missing()whencompute_z_from_resis True in the global config, and the bundle lacksZbut holds(rho, phase).- Parameters:
b (TFBundle) – Input bundle.
- Returns:
The updated bundle (may be the same instance).
- Return type:
Notes
The default implementation is a no-op. Subclasses may override to apply physically consistent reconstruction.
- transform(source, *, name=None, station_id=None)#
One-shot transform: extract → finalize → emit → post.
This orchestrates the whole conversion pipeline:
_finalize()to normalize the bundle.emit_edi()to create the EDI object.post_emit()for optional enrichment.
- Parameters:
- Returns:
The resulting EDI object (or compatible subtype).
- Return type:
Examples
>>> from pycsamt.core._transformers import TransformerMixin >>> class Mini(TransformerMixin): ... def extract(self, s): ... from pycsamt.core.base import TFBundle ... return TFBundle(freq=[1.], rho=[100.], phase=[45.]) ... def emit_edi(self, b): ... return {'ok': True, 'n': len(b.freq)} ... >>> Mini().transform(object())['ok'] True