pycsamt.z.z#

Impedance tensor (Z) built on top of ResPhase.

This module defines the high-level Z class, which inherits from ResPhase. It adds conveniences around construction, input validation, storage of the rotation history, and property setters for the impedance tensor and the frequency vector.

Classes

Z([z_array, z_err_array, freq, name, meta, ...])

High-level impedance tensor container built on ResPhase.

class pycsamt.z.z.Z(z_array=None, z_err_array=None, freq=None, *, name=None, meta=None, verbose=0)[source]#

Bases: ResPhase

High-level impedance tensor container built on ResPhase.

Z stores 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 either :pyattr:`z` or :pyattr:`freq` is 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 :pyattr:`z`. Shapes as for z_array after 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 :pyattr:`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 :pyattr:`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-n vector 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.ResPhase

Implements the \(\rho\) / \(\phi\) mapping and error propagation.

pycsamt.z.base.BaseEM

Minimal 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)[source]#

Build a Z instance 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:

Z

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_phase

Underlying implementation used here.

property freq: ndarray | None[source]#

Frequency vector in Hz, shape (n_freq,) or None.

property z: ndarray | None[source]#

Complex Z, shape (n_freq, 2, 2) or None.

property z_err: ndarray | None[source]#

Absolute errors on complex Z, shape (n, 2, 2) or None.

property inverse: ndarray[source]#

Inverse tensor \(Z^{-1}\) for each frequency.

If z_err is present, errors are propagated internally, but only the inverted tensor is returned (legacy API).

rotate(alpha)[source]#

Rotate :pyattr:`z` by 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-n sequence 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 :pyattr:`z` is 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)[source]#

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_x and f_y are resistivity-scale factors. The corrected tensor is then:

\[Z_0 = S^{-1} \cdot Z\]
Parameters:
  • reduce_res_factor_x (float or sequence, default=1.0) – Factor(s) for X components (resistivity scale). Either a single value or one per frequency.

  • reduce_res_factor_y (float or sequence, default=1.0) – Factor(s) for Y components (resistivity scale). Either a single value or one per frequency.

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:

tuple[ndarray, ndarray]

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)[source]#

Remove galvanic distortion D from 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_err or distortion_err_tensor is 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\). None if both input errors were None.

Raises:

ZError – If Z is missing, distortion shapes are invalid, or the distortion matrix is singular.

Return type:

tuple[ndarray, ndarray, ndarray | None]

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[source]#

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[source]#

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[source]#

Trace of \(Z\) at each frequency.

Returns:

Array of shape (n_freq,) with \(\operatorname{tr}(Z)\).

Return type:

ndarray

property trace_err: ndarray | None[source]#

Approximate error on \(\operatorname{tr}(Z)\).

Returns:

If z_err is available, returns an array with shape (n_freq,) computed by summing the diagonal errors \(\Delta Z_{00} + \Delta Z_{11}\). Otherwise None.

Return type:

ndarray or None

property skew: ndarray[source]#

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[source]#

Approximate error on the linear-algebra skew.

Returns:

If z_err is available, returns an array with shape (n_freq,) computed as \(\Delta Z_{01} + \Delta Z_{10}\) (1-norm bound). Otherwise None.

Return type:

ndarray or None

property det: ndarray[source]#

Determinant of \(Z\) at each frequency.

Returns:

Array of shape (n_freq,) with \(\det(Z)\).

Return type:

ndarray

property det_err: ndarray | None[source]#

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, or None if z_err is not available.

Return type:

ndarray or None

property norm: ndarray[source]#

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[source]#

Approximate error on the Frobenius norm.

Uses a first-order bound combining real and imaginary parts.

Returns:

Array of shape (n_freq,) or None if z_err is not available.

Return type:

ndarray or None

property invariants: dict[str, ndarray][source]#

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:

dict

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|\).

property z_xx: ndarray[source]#
property z_xy: ndarray[source]#
property z_yx: ndarray[source]#
property z_yy: ndarray[source]#
property z_err_xx: ndarray | None[source]#
property z_err_xy: ndarray | None[source]#
property z_err_yx: ndarray | None[source]#
property z_err_yy: ndarray | None[source]#
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_x and f_y are resistivity-scale factors. The corrected tensor is then:

\[Z_0 = S^{-1} \cdot Z\]
Parameters:
  • reduce_res_factor_x (float or sequence, default=1.0) – Factor(s) for X components (resistivity scale). Either a single value or one per frequency.

  • reduce_res_factor_y (float or sequence, default=1.0) – Factor(s) for Y components (resistivity scale). Either a single value or one per frequency.

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:

tuple[ndarray, ndarray]

Notes

Factors are in ρ scale; the matrix entries use their square roots. No uncertainty propagation is applied here.