2.10. pycsamt.z#
Transfer-function containers and helpers for impedance, apparent resistivity, phase, tipper, and Z object workflows.
Impedance-tensor
- class pycsamt.z.Z(z_array=None, z_err_array=None, freq=None, *, name=None, meta=None, verbose=0)#
Bases:
ResPhaseHigh-level impedance tensor container built on
ResPhase.Zstores complex impedances with shape(n, 2, 2)and manages absolute uncertainties of the same shape. A single(2, 2)matrix is promoted to a one-row stack.Frequency is held as a 1-D vector
(n,)in Hz. When eitherZ.zorZ.freqis set, apparent resistivity \(\rho\) and phase \(\phi\) are recomputed via the parent class.- Parameters:
z_array (ndarray, shape
(n, 2, 2)or(2, 2), optional) – Complex impedance tensor(s). Real arrays are cast to complex.z_err_array (ndarray, optional) – Absolute per-component errors on
Z.z. Shapes as forz_arrayafter any promotion.freq (array-like of float, optional) – Frequency vector in Hz. Must be 1-D and strictly positive. Length must equal the number of rows in
Z.z.name (str, optional) – Display name forwarded to
BaseEM.meta (dict, optional) – Arbitrary metadata forwarded to
BaseEM.verbose (int, default 0) – Verbosity forwarded to
BaseEM.
- Variables:
z (ndarray or None) – Complex impedance stack, shape
(n, 2, 2).z_err (ndarray or None) – Absolute errors on
Z.z.freq (ndarray or None) – Frequency vector in Hz.
rotation_angle (float or ndarray) – Accumulated rotation(s) in degrees (CW positive). A scalar before data are set; a length-
nvector once data exist.
Notes
The index convention is
Zxx→[:, 0, 0]Zxy→[:, 0, 1]Zyx→[:, 1, 0]Zyy→[:, 1, 1]
Uncertainties are absolute (not relative) and are propagated to \(\rho\) and \(\phi\) when available.
Examples
Create from a single tensor and frequency:
>>> import numpy as np >>> from pycsamt.z.z import Z >>> z = np.array([[0+0j, 1+1j], ... [-1-1j, 0+0j]]) >>> obj = Z(z_array=z, freq=[1.0]) >>> obj.resistivity.shape (1, 2, 2)
See also
pycsamt.z.resphase.ResPhaseImplements the \(\rho\) / \(\phi\) mapping and error propagation.
pycsamt.z.base.BaseEMMinimal mixin used for naming, metadata, and summary.
References
- classmethod from_res_phase(rho, phi, freq, *, rho_err=None, phi_err=None, name=None, meta=None, verbose=0)#
Build a
Zinstance from \((\rho, \phi, f)\) (with errors).This convenience constructor reconstructs complex impedances from apparent resistivity \(\rho\), phase \(\phi\) (deg), and frequency, and attaches optional uncertainties. Internally it calls
set_res_phase().- Parameters:
rho (ndarray, shape
(n, 2, 2)) – Apparent resistivity (Ω·m).phi (ndarray, shape
(n, 2, 2)) – Phase (degrees).freq (ndarray, shape
(n,)) – Frequency in Hz. Must be strictly positive.rho_err (ndarray, optional) – Absolute error on \(\rho\) (same shape).
phi_err (ndarray, optional) – Absolute error on \(\phi\) (deg, same shape).
name (str | None) – Forwarded to the constructor.
meta (dict | None) – Forwarded to the constructor.
verbose (int) – Forwarded to the constructor.
- Returns:
A fully initialized impedance container.
- Return type:
Notes
The magnitude is recovered via
\[|Z| = \sqrt{5 \, f \, \rho} \, ,\]followed by Euler reconstruction using \(\phi\).
Examples
>>> import numpy as np >>> rho = np.ones((2, 2, 2)) >>> phi = np.zeros_like(rho) >>> f = np.array([10.0, 1.0]) >>> Z.from_res_phase(rho, phi, f).z.shape (2, 2, 2)
See also
pycsamt.z.resphase.ResPhase.set_res_phaseUnderlying implementation used here.
- property inverse: ndarray#
Inverse tensor \(Z^{-1}\) for each frequency.
If
z_erris present, errors are propagated internally, but only the inverted tensor is returned (legacy API).
- rotate(alpha)#
Rotate
Z.zby angle(s)alpha(degrees, CW positive).The rotation is referenced to geographic axes (X→North, Y→East). A single angle is applied to all frequencies; a length-
nsequence applies element-wise angles. Errors are rotated consistently when present.- Parameters:
alpha (float or sequence of float) – Rotation angle(s) in degrees. If a sequence is given, its length must be 1 or equal to
n.- Raises:
pycsamt.exceptions.ZError – If
Z.zis missing or the number of angles is invalid.- Return type:
None
Notes
The following are updated:
Rotation uses
rotatematrix_incl_errors()per frequency.Examples
>>> import numpy as np >>> from pycsamt.z.z import Z >>> z = np.zeros((2, 2, 2), complex) >>> f = np.array([10.0, 1.0]) >>> obj = Z(z_array=z, freq=f) >>> obj.rotate(30.0) # single angle for all rows >>> obj.rotation_angle.shape (2,)
- remove_static_shift(reduce_res_factor_x=1.0, reduce_res_factor_y=1.0)#
Remove static shift using resistivity-scale correction factors.
Assumes the observed tensor is \(Z = S \cdot Z_0\), with:
\[\begin{split}S = \begin{bmatrix} \sqrt{f_x} & 0 \\ 0 & \sqrt{f_y} \end{bmatrix}\end{split}\]where
f_xandf_yare resistivity-scale factors. The corrected tensor is then:\[Z_0 = S^{-1} \cdot Z\]- Parameters:
- Returns:
static_shift (ndarray, shape (n_freq, 2, 2)) – Static-shift matrices applied at each frequency.
z_corrected (ndarray, shape (n_freq, 2, 2)) – Corrected impedance tensor \(Z_0\).
- Raises:
ZError – If Z is missing or factor lengths are invalid.
- Return type:
Notes
Factors are in ρ scale; the matrix entries use their square roots. No uncertainty propagation is applied here.
- remove_distortion(distortion_tensor, distortion_err_tensor=None)#
Remove galvanic distortion
Dfrom the observed impedance tensor \(Z\) to obtain the undistorted tensor \(Z_0 = D^{-1} Z\).Uncertainty propagation is included (first-order, 1-norm). If either
z_errordistortion_err_tensoris missing, zeros are assumed for the corresponding errors.- Parameters:
distortion_tensor (ndarray, shape (2, 2) or (n, 2, 2)) – Real 2×2 galvanic distortion matrix
D. If a stack is provided, only the first slice is used (distortion is assumed time-invariant).distortion_err_tensor (ndarray, shape (2, 2) or (n, 2, 2), ) – optional Absolute errors on
D. If omitted, zeros are assumed. If a stack is provided, only the first slice is used.
- Returns:
D (ndarray, shape (2, 2)) – The (real) distortion tensor used.
Z0 (ndarray, shape (n_freq, 2, 2)) – Corrected impedance tensor \(Z_0 = D^{-1} Z\).
Z0_err (ndarray or None, shape (n_freq, 2, 2)) – Propagated absolute errors on \(Z_0\).
Noneif both input errors wereNone.
- Raises:
ZError – If
Zis missing, distortion shapes are invalid, or the distortion matrix is singular.- Return type:
Notes
Error propagation for a component \((Z_0)_{ij} = \sum_k (D^{-1})_{ik} Z_{kj}\) uses the simple 1-norm bound:
\[\begin{split}\Delta (Z_0)_{ij} \approx \sum_k \\big( |\Delta (D^{-1})_{ik}| \cdot |Z_{kj}| + |(D^{-1})_{ik}| \\cdot |\Delta Z_{kj}| \big).\end{split}\]Examples
>>> D = np.array([[1.2, 0.5], [0.35, 2.1]]) >>> D_used, Z0, Z0_err = zobj.remove_distortion(D)
- property only_1d: ndarray#
Return a 1-D-like version of \(Z\).
The diagonal entries are set to zero. The off-diagonal entries retain their complex signs, but their magnitudes are set to the mean of the original off-diagonal magnitudes at each frequency:
|Z01|, |Z10| → m = 0.5 (|Z01| + |Z10|) Z01 := sign(Z01) * m Z10 := sign(Z10) * m
- Returns:
New tensor with shape
(n_freq, 2, 2).- Return type:
ndarray
- property only_2d: ndarray#
Return a 2-D-like version of \(Z\).
The diagonal entries are set to zero; off-diagonal terms are kept unchanged.
- Returns:
New tensor with shape
(n_freq, 2, 2).- Return type:
ndarray
- property trace: ndarray#
Trace of \(Z\) at each frequency.
- Returns:
Array of shape
(n_freq,)with \(\operatorname{tr}(Z)\).- Return type:
ndarray
- property trace_err: ndarray | None#
Approximate error on \(\operatorname{tr}(Z)\).
- Returns:
If
z_erris available, returns an array with shape(n_freq,)computed by summing the diagonal errors \(\Delta Z_{00} + \Delta Z_{11}\). OtherwiseNone.- Return type:
ndarray or None
- property skew: ndarray#
Linear-algebra skew \(Z_{01} - Z_{10}\).
Note
This is not the MT skew used in dimensionality analysis; it is the simple matrix skew.
- Returns:
Array of shape
(n_freq,)with \(Z_{01} - Z_{10}\).- Return type:
ndarray
- property skew_err: ndarray | None#
Approximate error on the linear-algebra skew.
- Returns:
If
z_erris available, returns an array with shape(n_freq,)computed as \(\Delta Z_{01} + \Delta Z_{10}\) (1-norm bound). OtherwiseNone.- Return type:
ndarray or None
- property det: ndarray#
Determinant of \(Z\) at each frequency.
- Returns:
Array of shape
(n_freq,)with \(\det(Z)\).- Return type:
ndarray
- property det_err: ndarray | None#
Approximate error on \(\det(Z)\).
Uses a central-difference style perturbation:
\[\begin{split}\Delta \det(Z) \approx \tfrac{1}{2}\\,\big|\det(Z+\Delta Z) - \det(Z-\Delta Z)\big|.\end{split}\]- Returns:
Array of shape
(n_freq,)with an error proxy, orNoneifz_erris not available.- Return type:
ndarray or None
- property norm: ndarray#
Frobenius norm \(\lVert Z \rVert_F\) at each frequency.
- Returns:
Array of shape
(n_freq,)with Frobenius norms.- Return type:
ndarray
- property norm_err: ndarray | None#
Approximate error on the Frobenius norm.
Uses a first-order bound combining real and imaginary parts.
- Returns:
Array of shape
(n_freq,)orNoneifz_erris not available.- Return type:
ndarray or None
- property invariants: dict[str, ndarray]#
Compute several algebraic invariants of \(Z\).
- Returns:
Dictionary with keys:
'z1'\(= (Z_{01} - Z_{10}) / 2\)'det'determinant of \(Z\)'det_real'determinant of \(\Re(Z)\)'det_imag'determinant of \(\Im(Z)\)'trace'trace of \(Z\)'skew'linear-algebra skew'norm'Frobenius norm'lambda_plus','lambda_minus': \(z_1 \pm \sqrt{z_1^2 / \det(Z)}\)'sigma_plus','sigma_minus': scalar combinations of norm and determinant magnitudes.
- Return type:
Notes
lambda_±are simple combinations frequently used in analytic manipulations of 2×2 matrices.sigma_±follow the historical form found in legacy code; they reduce to simple combinations of \(\lVert Z \rVert_F\) and \(|\det|\).
- remove_ss(reduce_res_factor_x=1.0, reduce_res_factor_y=1.0)#
Remove static shift using resistivity-scale correction factors.
Assumes the observed tensor is \(Z = S \cdot Z_0\), with:
\[\begin{split}S = \begin{bmatrix} \sqrt{f_x} & 0 \\ 0 & \sqrt{f_y} \end{bmatrix}\end{split}\]where
f_xandf_yare resistivity-scale factors. The corrected tensor is then:\[Z_0 = S^{-1} \cdot Z\]- Parameters:
- Returns:
static_shift (ndarray, shape (n_freq, 2, 2)) – Static-shift matrices applied at each frequency.
z_corrected (ndarray, shape (n_freq, 2, 2)) – Corrected impedance tensor \(Z_0\).
- Raises:
ZError – If Z is missing or factor lengths are invalid.
- Return type:
Notes
Factors are in ρ scale; the matrix entries use their square roots. No uncertainty propagation is applied here.
- class pycsamt.z.ResPhase(z_array=None, z_err_array=None, freq=None, **kwargs)#
Bases:
EMBaseResistivity/phase container backed by complex Z.
ResPhasecomputes apparent resistivity \(\rho\) and phase \(\phi\) from a complex impedance tensor Z, and supports the inverse map \((\rho,\ \phi) \rightarrow \mathbf{Z}\) with error propagation. It is a light, independent container;Zinherits from it to add higher-level conveniences.- Parameters:
z_array (ndarray, shape (n_freq, 2, 2), optional) – Complex impedance tensor Z. If omitted, call
compute_resistivity_phase()later.z_err_array (ndarray, shape (n_freq, 2, 2), optional) – Absolute per-component uncertainty on Z. If omitted, uncertainties on \(\rho\) and \(\phi\) remain
None.freq (ndarray, shape (n_freq,), optional) – Frequency vector in Hz. Must be 1-D, finite and strictly positive.
**kwargs – Forwarded to
BaseEM(e.g.,name,meta).
- Variables:
resistivity (ndarray, shape (n_freq, 2, 2)) – Apparent resistivity \(\rho\) (Ω·m). Set by
compute_resistivity_phase()orset_res_phase().phase (ndarray, shape (n_freq, 2, 2)) – Phase \(\phi\) in degrees. Set alongside
ResPhase.resistivity.resistivity_err (ndarray or None, shape (n_freq, 2, 2)) – Absolute uncertainty on \(\rho\) (Ω·m) or
Noneif no Z errors were provided.phase_err (ndarray or None, shape (n_freq, 2, 2)) – Absolute phase uncertainty (deg) or
None.z (ndarray or None, shape (n_freq, 2, 2)) – Complex Z, when known (set by the inverse path).
z_err (ndarray or None, shape (n_freq, 2, 2)) – Absolute uncertainty on Z, when propagated.
freq (ndarray or None, shape (n_freq,)) – Frequency vector in Hz (1-D, finite, > 0).
n_freq (int) – Inferred number of frequencies (from
ResPhase.freqor the first dimension of known arrays).
Notes
Forward path. \(\rho\) and \(\phi\) are computed from Z using
\[\rho \;=\; 0.2\,\frac{|Z|^{2}}{f}, \qquad \phi \;=\; \angle Z \quad (\text{in degrees}).\]Error propagation (forward). If Z errors are available, the per-entry relative amplitude error is \(\Delta Z/|Z|\). We use
z_error2r_phi_error()to map this to the \(\rho\) relative error (×2) and to an absolute phase uncertainty (deg, capped at \(90^{\circ}\)).Inverse path. Given \(\rho\) and \(\phi\), we recover \(|Z|\) from
\[|Z| \;=\; \sqrt{\,5\,f\,\rho\,},\]then build Z in Euler form. When \(\rho\) and \(\phi\) errors are given, the \(|Z|\) error follows
\[\frac{d|Z|}{|Z|} \;=\; \tfrac{1}{2}\,\frac{d\rho}{\rho},\]and, together with the phase error, is converted to a single absolute Z error per component via
propagate_error_polar2rect().Examples
Compute \(\rho\) and \(\phi\) from a stack of Z:
>>> import numpy as np >>> from pycsamt.z.resphase import ResPhase >>> z = np.ones((2, 2, 2), complex) >>> f = np.array([10.0, 1.0]) >>> rp = ResPhase() >>> rp.compute_resistivity_phase(z_array=z, freq=f) >>> rp.resistivity.shape, rp.phase.shape ((2, 2, 2), (2, 2, 2))
Reconstruct Z from \(\rho,\ \phi\) (no uncertainties):
>>> rho = (0.2 / f)[:, None, None] * np.ones((2, 2, 2)) >>> phi = np.zeros_like(rho) >>> rp.set_res_phase(rho, phi, f) >>> rp._z.shape (2, 2, 2)
See also
pycsamt.z.z.ZHigh-level impedance container built on
ResPhase.pycsamt.utils.zmath.z_error2r_phi_errorForward error mapping for \(\rho\) and \(\phi\).
pycsamt.utils.zmath.propagate_error_polar2rectPolar → rectangular error propagation for Z.
References
[ResPhase-1]Chave, A. D., & Jones, A. G. (2012). The Magnetotelluric Method: Theory and Practice. CUP.
[ResPhase-2]Simpson, F., & Bahr, K. (2005). Practical Magnetotellurics. CUP.
- compute_resistivity_phase(z_array=None, z_err_array=None, freq=None)#
Compute \(rho\) and :math:`phi`(and their errors) from complex Z.
Any provided inputs override the instance state. On success,
ResPhase.resistivity,ResPhase.phase, and, when applicable,ResPhase.resistivity_errandResPhase.phase_errare set.- Parameters:
z_array (ndarray, shape (n_freq, 2, 2), optional) – Complex impedance tensor Z. If given, it replaces the internal value used for the computation.
z_err_array (ndarray, shape (n_freq, 2, 2), optional) – Absolute Z error. If omitted, ρ and φ uncertainties are set to
None.freq (ndarray, shape (n_freq,), optional) – Frequency vector in Hz. Must be 1-D, finite and > 0.
- Returns:
Results are stored on the instance.
- Return type:
None
- Raises:
ZError – If Z is missing, shapes are inconsistent, values are not finite, or frequencies are not strictly positive.
Notes
We use \(\rho = 0.2\,|Z|^2 / f\) and \(\phi = \angle Z\) (in degrees).
If
z_err_arrayis given, per-entry uncertainties are computed viaz_error2r_phi_error(). The resistivity error is absolute (Ω·m). The phase error is absolute (deg) and is capped at \(90^\circ\).Examples
>>> import numpy as np >>> z = np.ones((2, 2, 2), complex) >>> f = np.array([10.0, 1.0]) >>> rp = ResPhase() >>> rp.compute_resistivity_phase(z_array=z, freq=f) >>> rp.resistivity.shape (2, 2, 2)
- set_res_phase(res_array, phase_array, freq, res_err_array=None, phase_err_array=None)#
Attach \(rho\) and \(phi\) (with optional errors) and reconstruct Z.
This inverse path accepts apparent resistivity (ρ) and phase (φ) at each frequency, reconstructs |Z| via
|Z| = sqrt(5 f ρ), and builds the complex tensor Z. If both ρ and φ errors are supplied, a per-entry absolute Z uncertainty is propagated in polar coordinates and converted to rectangular form.- Parameters:
res_array (ndarray, shape (n_freq, 2, 2)) – Apparent resistivity (Ω·m). Must be real and finite.
phase_array (ndarray, shape (n_freq, 2, 2)) – Phase in degrees. Must be real and finite.
freq (ndarray, shape (n_freq,)) – Frequency in Hz (1-D, finite, strictly positive).
res_err_array (ndarray, shape (n_freq, 2, 2), optional) – Absolute error on ρ (Ω·m). If omitted, Z error is left
None.phase_err_array (ndarray, shape (n_freq, 2, 2), optional) – Absolute phase error in degrees.
- Returns:
Results are stored on the instance (
ResPhase._z,ResPhase._z_err,ResPhase.resistivity,ResPhase.phase).- Return type:
None
- Raises:
ResistivityError – If ρ contains complex values.
PhaseError – If φ contains complex values.
ZError – If shapes are inconsistent, ρ or φ contain +/-inf, or frequencies are not strictly positive. NaN entries in ρ/φ are tolerated (missing component) and propagate to NaN in the reconstructed Z.
Notes
The relationship between \(|Z|\) and \(\rho\) implies
..math:
\frac{d|Z|}{|Z|} = \tfrac{1}{2}\,\frac{d\rho}{\rho}.
When error arrays are supplied, |Z| error follows the above and is combined with phase error by
propagate_error_polar2rect()to yield a single absolute Z error per component.Examples
>>> import numpy as np >>> f = np.array([10.0, 1.0]) >>> rho = (0.2 / f)[:, None, None] * np.ones((2, 2, 2)) >>> phi = np.zeros_like(rho) >>> rp = ResPhase() >>> rp.set_res_phase(rho, phi, f) >>> rp._z.shape (2, 2, 2)
- compute_rho_phi(z_array=None, z_err_array=None, freq=None)#
Compute \(rho\) and :math:`phi`(and their errors) from complex Z.
Any provided inputs override the instance state. On success,
ResPhase.resistivity,ResPhase.phase, and, when applicable,ResPhase.resistivity_errandResPhase.phase_errare set.- Parameters:
z_array (ndarray, shape (n_freq, 2, 2), optional) – Complex impedance tensor Z. If given, it replaces the internal value used for the computation.
z_err_array (ndarray, shape (n_freq, 2, 2), optional) – Absolute Z error. If omitted, ρ and φ uncertainties are set to
None.freq (ndarray, shape (n_freq,), optional) – Frequency vector in Hz. Must be 1-D, finite and > 0.
- Returns:
Results are stored on the instance.
- Return type:
None
- Raises:
ZError – If Z is missing, shapes are inconsistent, values are not finite, or frequencies are not strictly positive.
Notes
We use \(\rho = 0.2\,|Z|^2 / f\) and \(\phi = \angle Z\) (in degrees).
If
z_err_arrayis given, per-entry uncertainties are computed viaz_error2r_phi_error(). The resistivity error is absolute (Ω·m). The phase error is absolute (deg) and is capped at \(90^\circ\).Examples
>>> import numpy as np >>> z = np.ones((2, 2, 2), complex) >>> f = np.array([10.0, 1.0]) >>> rp = ResPhase() >>> rp.compute_resistivity_phase(z_array=z, freq=f) >>> rp.resistivity.shape (2, 2, 2)
- set_rho_phi(res_array, phase_array, freq, res_err_array=None, phase_err_array=None)#
Attach \(rho\) and \(phi\) (with optional errors) and reconstruct Z.
This inverse path accepts apparent resistivity (ρ) and phase (φ) at each frequency, reconstructs |Z| via
|Z| = sqrt(5 f ρ), and builds the complex tensor Z. If both ρ and φ errors are supplied, a per-entry absolute Z uncertainty is propagated in polar coordinates and converted to rectangular form.- Parameters:
res_array (ndarray, shape (n_freq, 2, 2)) – Apparent resistivity (Ω·m). Must be real and finite.
phase_array (ndarray, shape (n_freq, 2, 2)) – Phase in degrees. Must be real and finite.
freq (ndarray, shape (n_freq,)) – Frequency in Hz (1-D, finite, strictly positive).
res_err_array (ndarray, shape (n_freq, 2, 2), optional) – Absolute error on ρ (Ω·m). If omitted, Z error is left
None.phase_err_array (ndarray, shape (n_freq, 2, 2), optional) – Absolute phase error in degrees.
- Returns:
Results are stored on the instance (
ResPhase._z,ResPhase._z_err,ResPhase.resistivity,ResPhase.phase).- Return type:
None
- Raises:
ResistivityError – If ρ contains complex values.
PhaseError – If φ contains complex values.
ZError – If shapes are inconsistent, ρ or φ contain +/-inf, or frequencies are not strictly positive. NaN entries in ρ/φ are tolerated (missing component) and propagate to NaN in the reconstructed Z.
Notes
The relationship between \(|Z|\) and \(\rho\) implies
..math:
\frac{d|Z|}{|Z|} = \tfrac{1}{2}\,\frac{d\rho}{\rho}.
When error arrays are supplied, |Z| error follows the above and is combined with phase error by
propagate_error_polar2rect()to yield a single absolute Z error per component.Examples
>>> import numpy as np >>> f = np.array([10.0, 1.0]) >>> rho = (0.2 / f)[:, None, None] * np.ones((2, 2, 2)) >>> phi = np.zeros_like(rho) >>> rp = ResPhase() >>> rp.set_res_phase(rho, phi, f) >>> rp._z.shape (2, 2, 2)
- exception pycsamt.z.ZError#
Bases:
PycsamtErrorGeneral ZError.