pycsamt.forward.maxwell.contracts#
Solver-neutral contracts for frequency-domain Maxwell simulations.
The canonical model order is (z, x) in 2-D and (z, y, x) in 3-D.
Canonical impedance output uses (station, frequency, component). This
module contains no discretization, sparse solver, or optional backend import.
Classes
|
Store canonical impedance predictions from a Maxwell backend. |
|
Describe a rectilinear finite-volume or finite-element mesh. |
|
Define one isotropic frequency-domain MT boundary-value problem. |
|
Define named receiver locations in mesh coordinates. |
|
Record convergence information for every frequency and source solve. |
- class pycsamt.forward.maxwell.contracts.MaxwellMesh(x_edges_m, z_edges_m, y_edges_m=None, crs=None)[source]
Bases:
objectDescribe a rectilinear finite-volume or finite-element mesh.
- Parameters:
x_edges_m (array-like) – Strictly increasing cell-edge coordinates in metres. Depth
zincreases downward.z_edges_m (array-like) – Strictly increasing cell-edge coordinates in metres. Depth
zincreases downward.y_edges_m (array-like or None, optional) – Second horizontal axis. Omit for a 2-D mesh.
crs (str or None, optional) – Coordinate reference system for horizontal coordinates.
Examples
>>> mesh = MaxwellMesh([0, 100, 250], [0, 50, 150]) >>> mesh.shape, mesh.dimension ((2, 2), 2) >>> mesh.cell_centres_m["x"].tolist() [50.0, 175.0]
- x_edges_m: ndarray
- z_edges_m: ndarray
- property dimension: int[source]
Return the spatial dimension.
- Returns:
Mesh dimension.
- Return type:
{2, 3}
Examples
>>> MaxwellMesh([0, 1, 2], [0, 1, 2]).dimension 2
- property shape: tuple[int, ...][source]
Return canonical cell-array shape.
Examples
>>> MaxwellMesh([0, 1, 2], [0, 1, 2, 3]).shape (3, 2)
- property cell_widths_m: Mapping[str, ndarray][source]
Return read-only cell widths keyed by axis.
- Returns:
Keys are
xandz, plusyfor 3-D.- Return type:
mapping
Examples
>>> mesh = MaxwellMesh([0, 2, 5], [0, 1, 3]) >>> mesh.cell_widths_m["x"].tolist() [2.0, 3.0]
- property cell_centres_m: Mapping[str, ndarray][source]
Return read-only cell centres keyed by axis.
- Returns:
Centre coordinates in metres.
- Return type:
mapping
Examples
>>> mesh = MaxwellMesh([0, 2, 4], [0, 10, 20]) >>> mesh.cell_centres_m["z"].tolist() [5.0, 15.0]
- to_dict()[source]
Return a JSON-compatible mesh representation.
- Returns:
Versioned mesh state.
- Return type:
Examples
>>> MaxwellMesh([0, 1, 2], [0, 1, 2]).to_dict()["schema_version"] 1
- classmethod from_dict(data)[source]
Restore a validated mesh from serialized state.
- Parameters:
data (mapping) – State returned by
to_dict().- Returns:
Restored immutable mesh.
- Return type:
Examples
>>> mesh = MaxwellMesh([0, 1, 2], [0, 2, 4]) >>> MaxwellMesh.from_dict(mesh.to_dict()).shape (2, 2)
- class pycsamt.forward.maxwell.contracts.ReceiverSet(coordinates_m, names, orientation_deg=0.0)[source]
Bases:
objectDefine named receiver locations in mesh coordinates.
- Parameters:
Examples
>>> receivers = ReceiverSet([[50, 0], [150, 0]], ["S00", "S01"]) >>> receivers.dimension, receivers.count (2, 2)
- coordinates_m: ndarray
- orientation_deg: float = 0.0
- property count: int[source]
Return the number of receivers.
- Returns:
Receiver count.
- Return type:
Examples
>>> ReceiverSet([[0, 0]], ["S00"]).count 1
- property dimension: int[source]
Return the coordinate dimension.
- Returns:
Number of coordinate columns.
- Return type:
{2, 3}
Examples
>>> ReceiverSet([[0, 0, 0]], ["S00"]).dimension 3
- to_dict()[source]
Return JSON-compatible receiver state.
- Returns:
Versioned receiver coordinates and names.
- Return type:
Examples
>>> ReceiverSet([[0, 0]], ["S00"]).to_dict()["names"] ['S00']
- classmethod from_dict(data)[source]
Restore receivers from serialized state.
- Parameters:
data (mapping) – State returned by
to_dict().- Returns:
Validated receiver collection.
- Return type:
Examples
>>> state = ReceiverSet([[0, 0]], ["S00"]).to_dict() >>> ReceiverSet.from_dict(state).names ('S00',)
- class pycsamt.forward.maxwell.contracts.MaxwellProblem(mesh, conductivity_s_m, frequencies_hz, receivers, components=('zxy', 'zyx'), active_cells=None, time_dependence='exp(+iwt)', magnetic_permeability_h_m=1.2566370614359173e-06, metadata=<factory>)[source]
Bases:
objectDefine one isotropic frequency-domain MT boundary-value problem.
- Parameters:
mesh (MaxwellMesh) – Rectilinear simulation mesh.
conductivity_s_m (array-like) – Positive isotropic conductivity in S/m, shaped like
mesh.frequencies_hz (array-like, shape (n_frequency,)) – Positive, unique frequencies. Input order is retained.
receivers (ReceiverSet) – Observation locations with the same dimension as
mesh.components (sequence of {"zxx", "zxy", "zyx", "zyy"}) – Requested impedance components in explicit output order.
active_cells (array-like of bool or None, optional) – Cells participating in the physical model. Air cells can remain active with a small conductivity or be marked inactive for capable backends.
time_dependence ({"exp(+iwt)", "exp(-iwt)"}, default="exp(+iwt)") – Complex phasor convention.
magnetic_permeability_h_m (float, default=1.25663706212e-6) – Uniform scalar permeability in H/m.
metadata (mapping, optional) – Finite JSON-compatible provenance; never interpreted by a backend.
Examples
>>> mesh = MaxwellMesh([0, 100, 200], [0, 50, 100]) >>> receivers = ReceiverSet([[50, 0]], ["S00"]) >>> problem = MaxwellProblem( ... mesh, np.full(mesh.shape, 0.01), [10, 1], receivers ... ) >>> problem.problem_hash == problem.problem_hash True
- mesh: MaxwellMesh
- conductivity_s_m: ndarray
- frequencies_hz: ndarray
- receivers: ReceiverSet
- time_dependence: str = 'exp(+iwt)'
- magnetic_permeability_h_m: float = 1.2566370614359173e-06
- property problem_hash: str[source]
Return a deterministic SHA-256 digest of all physical inputs.
- Returns:
Digest suitable for cache keys.
- Return type:
Examples
>>> mesh = MaxwellMesh([0, 1, 2], [0, 1, 2]) >>> p = MaxwellProblem( ... mesh, np.ones(mesh.shape), [1], ReceiverSet([[0, 0]], ["S"]) ... ) >>> len(p.problem_hash) 64
- provenance()[source]
Return JSON-compatible problem provenance excluding large arrays.
- Returns:
Mesh, receiver, convention, components, and metadata state.
- Return type:
Examples
>>> mesh = MaxwellMesh([0, 1, 2], [0, 1, 2]) >>> p = MaxwellProblem( ... mesh, np.ones(mesh.shape), [1], ReceiverSet([[0, 0]], ["S"]) ... ) >>> p.provenance()["components"] ['zxy', 'zyx']
- to_npz(path)[source]
Write a pickle-free problem archive.
- Parameters:
path (str or pathlib.Path) – Destination archive.
- Returns:
Destination path.
- Return type:
Examples
>>> from tempfile import TemporaryDirectory >>> p = MaxwellProblem( ... MaxwellMesh([0, 1, 2], [0, 1, 2]), ... np.ones((2, 2)), ... [1], ... ReceiverSet([[0, 0]], ["S"]), ... ) >>> with TemporaryDirectory() as d: ... restored = MaxwellProblem.from_npz(p.to_npz(Path(d) / "p.npz")) >>> restored.problem_hash == p.problem_hash True
- classmethod from_npz(path)[source]
Restore and validate a problem archive.
- Parameters:
path (str or pathlib.Path) – Archive written by
to_npz().- Returns:
Restored problem.
- Return type:
Examples
>>> from tempfile import TemporaryDirectory >>> p = MaxwellProblem( ... MaxwellMesh([0, 1, 2], [0, 1, 2]), ... np.ones((2, 2)), ... [1], ... ReceiverSet([[0, 0]], ["S"]), ... ) >>> with TemporaryDirectory() as d: ... q = MaxwellProblem.from_npz(p.to_npz(Path(d) / "p.npz")) >>> np.array_equal(q.conductivity_s_m, p.conductivity_s_m) True
- class pycsamt.forward.maxwell.contracts.SolverDiagnostics(converged, iterations, relative_residual, runtime_s, messages=())[source]
Bases:
objectRecord convergence information for every frequency and source solve.
- Parameters:
converged (array-like of bool, shape (n_frequency, n_source)) – Whether each linear solve met its tolerance.
iterations (array-like of int, same shape) – Iteration count; zero is valid for direct solvers.
relative_residual (array-like of float, same shape) – Final non-negative relative residual.
runtime_s (float) – Total non-negative solver runtime in seconds.
messages (sequence of str, optional) – Backend messages for failed or noteworthy solves.
Examples
>>> d = SolverDiagnostics( ... [[True], [False]], [[4], [20]], [[1e-8], [1e-2]], 0.5 ... ) >>> d.success, d.maximum_relative_residual (False, 0.01)
- converged: ndarray
- iterations: ndarray
- relative_residual: ndarray
- runtime_s: float
- property success: bool[source]
Return whether every solve converged.
- Returns:
True only when all convergence flags are true.
- Return type:
Examples
>>> SolverDiagnostics([[True]], [[1]], [[1e-9]], 0).success True
- property maximum_relative_residual: float[source]
Return the largest reported relative residual.
- Returns:
Worst solve residual.
- Return type:
Examples
>>> SolverDiagnostics( ... [[True]], [[1]], [[1e-7]], 0 ... ).maximum_relative_residual 1e-07
- to_dict()[source]
Return JSON-compatible convergence diagnostics.
- Returns:
Versioned diagnostic state.
- Return type:
Examples
>>> SolverDiagnostics([[True]], [[2]], [[1e-8]], 0.1).to_dict()[ ... "runtime_s" ... ] 0.1
- classmethod from_dict(data)[source]
Restore validated convergence diagnostics.
- Parameters:
data (mapping) – State returned by
to_dict().- Returns:
Restored diagnostic record.
- Return type:
Examples
>>> state = SolverDiagnostics([[True]], [[2]], [[1e-8]], 0.1).to_dict() >>> SolverDiagnostics.from_dict(state).success True
- class pycsamt.forward.maxwell.contracts.ForwardResult(problem_hash, frequencies_hz, receiver_names, components, impedance_v_a, valid, backend_name, backend_version, diagnostics, metadata=<factory>)[source]
Bases:
objectStore canonical impedance predictions from a Maxwell backend.
- Parameters:
problem_hash (str) – Hash of the exact
MaxwellProblemsolved.frequencies_hz (array-like) – Frequency vector in problem order.
receiver_names (sequence of str) – Explicit station and tensor-component axes.
components (sequence of str) – Explicit station and tensor-component axes.
impedance_v_a (complex array, shape (station, frequency, component)) – Predicted SI impedance.
valid (bool array or None, optional) – Validity mask with the same shape. Defaults to finite predictions.
backend_name (str) – Solver identity required for reproducibility.
backend_version (str) – Solver identity required for reproducibility.
diagnostics (SolverDiagnostics) – Per-solve convergence record.
metadata (mapping, optional) – Additional finite JSON-compatible backend provenance.
Examples
>>> d = SolverDiagnostics([[True]], [[3]], [[1e-9]], 0.01) >>> r = ForwardResult( ... "a" * 64, [1], ["S"], ["zxy"], [[[1 + 2j]]], None, "demo", "1", d ... ) >>> r.shape, r.success ((1, 1, 1), True)
- problem_hash: str
- frequencies_hz: ndarray
- impedance_v_a: ndarray
- backend_name: str
- backend_version: str
- diagnostics: SolverDiagnostics
- property shape: tuple[int, int, int][source]
Return canonical impedance shape.
Examples
>>> d = SolverDiagnostics([[True]], [[0]], [[0]], 0) >>> ForwardResult( ... "0" * 64, [1], ["S"], ["zxy"], [[[1j]]], None, "b", "1", d ... ).shape (1, 1, 1)
- property success: bool[source]
Return whether all solves converged and predictions are valid.
- Returns:
Combined numerical and observation validity status.
- Return type:
Examples
>>> d = SolverDiagnostics([[True]], [[0]], [[0]], 0) >>> ForwardResult( ... "0" * 64, [1], ["S"], ["zxy"], [[[1j]]], None, "b", "1", d ... ).success True
- validate_against(problem)[source]
Raise if this result does not exactly match a problem contract.
- Parameters:
problem (MaxwellProblem) – Expected input problem.
- Raises:
ValueError – If hash or any output axis differs.
- Return type:
None
Examples
>>> mesh = MaxwellMesh([0, 1, 2], [0, 1, 2]) >>> p = MaxwellProblem( ... mesh, ... np.ones((2, 2)), ... [1], ... ReceiverSet([[0, 0]], ["S"]), ... ("zxy",), ... ) >>> d = SolverDiagnostics([[True]], [[0]], [[0]], 0) >>> ForwardResult( ... p.problem_hash, ... [1], ... ["S"], ... ["zxy"], ... [[[1j]]], ... None, ... "b", ... "1", ... d, ... ).validate_against(p)
- provenance()[source]
Return JSON-compatible solver and output-axis provenance.
- Returns:
Problem identity, axes, backend, diagnostics, and metadata.
- Return type:
Examples
>>> d = SolverDiagnostics([[True]], [[0]], [[0]], 0) >>> r = ForwardResult( ... "0" * 64, [1], ["S"], ["zxy"], [[[1j]]], None, "b", "1", d ... ) >>> r.provenance()["backend_name"] 'b'
- to_npz(path)[source]
Write a versioned, pickle-free result archive.
- Parameters:
path (str or pathlib.Path) – Destination archive.
- Returns:
Destination path.
- Return type:
Examples
>>> from tempfile import TemporaryDirectory >>> d = SolverDiagnostics([[True]], [[0]], [[0]], 0) >>> r = ForwardResult( ... "0" * 64, [1], ["S"], ["zxy"], [[[1j]]], None, "b", "1", d ... ) >>> with TemporaryDirectory() as directory: ... restored = ForwardResult.from_npz( ... r.to_npz(Path(directory) / "r.npz") ... ) >>> restored.backend_name, restored.shape ('b', (1, 1, 1))
- classmethod from_npz(path)[source]
Restore and validate a result archive without pickle.
- Parameters:
path (str or pathlib.Path) – Archive written by
to_npz().- Returns:
Restored canonical result.
- Return type:
Examples
>>> from tempfile import TemporaryDirectory >>> d = SolverDiagnostics([[True]], [[0]], [[0]], 0) >>> r = ForwardResult( ... "0" * 64, [1], ["S"], ["zxy"], [[[1j]]], None, "b", "1", d ... ) >>> with TemporaryDirectory() as directory: ... restored = ForwardResult.from_npz( ... r.to_npz(Path(directory) / "r.npz") ... ) >>> np.array_equal(restored.impedance_v_a, r.impedance_v_a) True