pycsamt.seg.spectra#

Functions

spectra_from_Z(z_obj, *[, S_HH, H_psd, ...])

Synthesize a Spectra from a transfer function Z and optional tipper.

Classes

Spectra([name, verbose])

Container for >SPECTRA blocks grouped per frequency.

SpectraIO(*args[, verbose, logger])

Read and write >SPECTRA data blocks.

SpectraMixin()

Convenience facade for spectra access.

SpectraSECT(*args[, verbose, logger])

Minimal container for the >=SPECTRASECT header.

class pycsamt.seg.spectra.SpectraSECT(*args, verbose=0, logger=None, **kws)[source]#

Bases: EDIComponentBase

Minimal container for the >=SPECTRASECT header.

The class parses and serializes the spectra section header that precedes one or more >SPECTRA data blocks. It collects the option key/values and the ordered set of measurement IDs that the spectra apply to, as described by the SEG EDI convention [1]_.

Parameters:
  • verbose (int or bool, optional) – Verbosity flag propagated from Base.

  • logger (object, optional) – Logger instance to use. If None, a default null-safe logger is attached.

  • **kws – Additional field overrides. Keys may include any attribute listed below.

  • args (Any)

Variables:
  • sectid (str or None) – Section identifier, often a site name. Some files omit this or use a numeric ID.

  • nchan (int or None) – Number of channels in the spectra set.

  • nfreq (int or None) – Number of frequencies expected in the section.

  • maxblks (int or None) – Maximum number of blocks. Rarely used.

  • meas_ids (list of str) – Ordered measurement ID list that follows the option lines in >=SPECTRASECT.

  • start_data_lines_num (int or None) – Line index in the EDI where the first >SPECTRA block begins. Set by from_file().

Notes

  • Parsing is tolerant to case and extra whitespace.

  • Unknown header keys are ignored instead of raising.

  • The measurement ID list is collected from the header body once option lines end.

  • The start of the spectra data is detected by the first >SPECTRA tag, by the next >=... tag, or by end of file, whichever comes first.

  • For consistent processing, maintain the same frequency set across related data sections, as recommended in the EDI spec [1]_.

See also

SpectraIO

Reader/writer for the >SPECTRA data blocks.

MTEMAP

Header for >=MTSECT or >=EMAPSECT. The spectra frequency set should match the MT set.

TSect

Header for >=TSERIESSECT (time series).

Examples

Read only the header and measurement IDs:

>>> sect = SpectraSECT.from_file("site.edi")
>>> sect.nfreq, sect.nchan
(128, 5)
>>> sect.meas_ids[:2]
['HX1', 'HY1']

Serialize a header:

>>> sect.nfreq = 3
>>> sect.meas_ids = ["HX", "HY", "EX", "EY"]
>>> lines = sect.write()
>>> print("".join(lines).strip())
>=SPECTRASECT
  SECTID=...
  NCHAN=...
  NFREQ=3
  MAXBLKS=...
    // 4
     HX
     HY
     EX
     EY

References

KEY_ORDER: list[str] = ['sectid', 'nchan', 'nfreq', 'maxblks']#
classmethod from_file(edi_path)[source]#
Parameters:

edi_path (str)

Return type:

SpectraSECT

write()[source]#
Return type:

list[str]

class pycsamt.seg.spectra.SpectraIO(*args, verbose=0, logger=None, **kws)[source]#

Bases: EDIComponentBase

Read and write >SPECTRA data blocks.

A spectra section contains one block per frequency. Each block begins with a >SPECTRA line that holds options such as frequency and bandwidth, optionally followed by a comment with the number of values, then one or more lines of numeric values.

Known options are normalized:

  • FREQ : float

  • ROTSPEC : int

  • BW : float

  • AVGT : float

Unrecognized options are preserved in a free-form mapping so that vendor-specific metadata is not lost.

Parameters:
  • verbose (int or bool, optional) – Verbosity flag propagated from Base.

  • logger (object, optional) – Logger instance to use. If None, a default null-safe logger is attached.

  • **kws – Additional field overrides.

  • args (Any)

Variables:

blocks (list of _SpectraBlock) – Parsed spectra blocks, one per frequency. Each block stores header options, the optional value count hint, and the numeric values.

Notes

  • from_file() reads successive >SPECTRA blocks starting from a given line or from the first match in the file.

  • Values are parsed as floats; non-numeric tokens in data lines are ignored rather than raising.

  • The writer orders known options first in the header line, then appends extra options sorted by key. Both option keys and values are written in upper case.

  • Line formatting uses the per-line and float format defaults from Base unless you provide explicit overrides.

See also

SpectraSECT

Header container for spectra sections.

TSIO

Time-series counterpart for >TSERIES.

Examples

Read all spectra blocks:

>>> io = SpectraIO.from_file("site.edi")
>>> len(io.blocks)
128
>>> b0 = io.blocks[0]
>>> b0.freq, b0.bw
(..., ...)

Build and serialize blocks:

>>> from pycsamt.seg.spectra import _SpectraBlock
>>> io = SpectraIO()
>>> blk = _SpectraBlock()
>>> blk.freq = 10.0
>>> blk.rotspec = 1
>>> blk.values = [0.1, 0.2, 0.3]
>>> io.blocks.append(blk)
>>> lines = io.write(per_line=2, float_fmt="{: .3E}")
>>> print("".join(lines).strip())
>SPECTRA FREQ=10.0 ROTSPEC=1 // 3
  1.000E-01  2.000E-01
  3.000E-01

References

blocks: list[_SpectraBlock]#
classmethod from_file(edi_path, start_line=None)[source]#
Parameters:
  • edi_path (str)

  • start_line (int | None)

Return type:

SpectraIO

write(per_line=None, float_fmt=None)[source]#
Parameters:
  • per_line (int | None)

  • float_fmt (str | None)

Return type:

list[str]

class pycsamt.seg.spectra.SpectraMixin[source]#

Bases: object

Convenience facade for spectra access.

This mixin exposes a compact API that host classes can reuse to discover and read spectra sections in an EDI file.

from_file(edi_fn)[source]#

Return a SpectraSECT parsed from the first >=SPECTRASECT header in edi_fn.

Parameters:

edi_fn (str)

Return type:

SpectraSECT

read_blocks(edi_fn)[source]#

Return a SpectraIO by scanning all subsequent >SPECTRA blocks that belong to the section discovered by SpectraSECT.

Parameters:

edi_fn (str)

Return type:

SpectraIO

Notes

Use this mixin in higher-level readers so spectra handling remains consistent and centralized. The method pair mirrors the design used for MT/EMAP headers and for time series sections.

See also

SpectraSECT

Header parsing and serialization.

SpectraIO

Data block reader/writer.

MTEMAP

MT/EMAP section header, often used alongside spectra for the same dataset.

Examples

>>> class Reader(SpectraMixin):
...     pass
>>> sect = Reader.from_file("site.edi")
>>> io = Reader.read_blocks("site.edi")
>>> len(io.blocks) > 0
True

References

classmethod from_file(edi_fn)[source]#
Parameters:

edi_fn (str)

Return type:

SpectraSECT

classmethod read_blocks(edi_fn)[source]#
Parameters:

edi_fn (str)

Return type:

SpectraIO

class pycsamt.seg.spectra.Spectra(name=None, *, verbose=0)[source]#

Bases: EMBase

Container for >SPECTRA blocks grouped per frequency.

The class gathers one spectra record per frequency and exposes typed header fields (frequency, rotation flag, bandwidth, and averaging time) together with the numeric values stored in each block. It is a compact, array- oriented view on top of SpectraSECT and SpectraIO.

Parameters:
  • name (str, optional) – Display name forwarded to BaseEM.

  • verbose (int, default 0) – Verbosity level, forwarded to BaseEM.

Variables:
  • freq (ndarray, shape (n_blk,)) – Frequency (Hz) per block. Missing values are set to np.nan.

  • rotspec (ndarray of int, shape (n_blk,)) – Rotation specifier per block. Missing values are set to -1.

  • bw (ndarray, shape (n_blk,)) – Nominal bandwidth (Hz) per block or np.nan.

  • avgt (ndarray, shape (n_blk,)) – Averaging time (s) per block or np.nan.

  • values (list of ndarray) – Numeric payload for each block. Lengths may differ across blocks, as allowed by the SEG format.

  • n_values (ndarray of int, shape (n_blk,)) – Number of values in each block (as parsed or counted).

Notes

Blocks may contain vendor-specific options beyond the canonical FREQ, ROTSPEC, BW, and AVGT. Those options are preserved when round-tripping via to_io(). The class does not impose a common length across spectra vectors; if you require a 2-D array, pad the values list explicitly.

The constructor itself does not read files. Use from_io() or from_file() to populate an instance from sections and data blocks.

from_io(sect, io) : classmethod

Build a Spectra from SpectraSECT and SpectraIO.

from_file(path) : classmethod

Convenience that calls SpectraSECT.from_file and SpectraIO.from_file, then delegates to from_io().

to_io()[source]#

Serialize the current state to a fresh pair (SpectraSECT, SpectraIO) that can be written back to an EDI file.

Return type:

tuple[SpectraSECT, SpectraIO]

Examples

Read, inspect, and serialize spectra:

from pycsamt.seg.spectra import Spectra

sp = Spectra.from_file("site.edi")
f = sp.freq
first = sp.values[0]

sect2, io2 = sp.to_io()
# writer can now combine sect2.write() and io2.write()

See also

pycsamt.seg.spectra.SpectraSECT

Header for >=SPECTRASECT sections.

pycsamt.seg.spectra.SpectraIO

Reader/writer for >SPECTRA blocks.

pycsamt.seg.EDIFile

High-level dispatcher that can attach spectra to an EDI session.

References

band: list[str]#
chan_ids: list[str]#
id_to_chtype: dict[str, str]#
property freq: ndarray[source]#
property S: ndarray[source]#
property n_freq: int[source]#
property n_chan: int[source]#
classmethod from_io(sect, io, *, empty=1e+32, verbose=0)[source]#
Parameters:
Return type:

Spectra

classmethod from_file(path, *, empty=1e+32, verbose=0)[source]#

Read a Spectra directly from an EDI file path.

Convenience wrapper around from_io() that calls SpectraSECT.from_file and SpectraIO.from_file internally.

Parameters:
  • path (str or Path) – Path to the EDI file containing >=SPECTRASECT and >SPECTRA blocks.

  • empty (float) – Sentinel value for missing spectra entries. Default 1e32.

  • verbose (int) – Verbosity level forwarded to from_io().

Return type:

Spectra

to_edi(source_edi=None, *, station_name=None, e_labels=('EX', 'EY'), h_labels=('HX', 'HY'), ridge=None, estimate_error=False, dof=None)[source]#

Convert cross-spectra to an MT-impedance EDIFile.

Calls to_Z() and assembles a complete >=MTSECT / >FREQ / >ZXXR / >ZXYR / … EDI ready to be saved with write().

The structural sections (>HEAD, >INFO, >=DEFINEMEAS) are re-used from source_edi when provided, preserving all acquisition metadata; otherwise a minimal header is synthesised from the Spectra metadata.

According to the SEG EDI standard (§ 7.53, 12.1), an MT data section requires:

>=MTSECT
  SECTID=...
  NFREQ=...
  HX=...  HY=...  HZ=...  EX=...  EY=...
>FREQ   //N
  ...
>ZXXR  ROT=ZROT  //N
  ...
>ZXXI  ROT=ZROT  //N
  ...
...
>END

The measurement IDs for HX, HY, … in >=MTSECT are resolved from id_to_chtype (populated by SpectraSECT from >HMEAS / >EMEAS lines).

Parameters:
  • source_edi (str, Path, or EDIFile, optional) – Spectra EDI file whose >HEAD, >INFO, and >=DEFINEMEAS sections are copied into the output. Pass the same path used with from_file() to produce a fully metadata-rich result. When None, a minimal header is synthesised.

  • station_name (str, optional) – Override for the DATAID in >HEAD and SECTID in >=MTSECT. Defaults to name.

  • e_labels (tuple of str) – Electric channel type labels forwarded to to_Z().

  • h_labels (tuple of str) – Horizontal magnetic channel type labels forwarded to to_Z().

  • ridge (float, optional) – Tikhonov regularisation forwarded to to_Z().

  • estimate_error (bool) – If True, propagate 1-σ errors into >ZXX.VAR … blocks.

  • dof (float or ndarray, optional) – Effective degrees of freedom forwarded to to_Z().

Returns:

Fully populated MT-impedance container. Call write() to save.

Return type:

EDIFile

Raises:

EdIDataError – If to_Z() fails (channel types not resolved, singular magnetic block, etc.).

Examples

Convert and save:

sp  = Spectra.from_file("site.edi")
ed  = sp.to_edi("site.edi", estimate_error=False)
out = ed.write(savepath="mt_output/")

Convert with a custom station name and error propagation:

ed  = sp.to_edi("site.edi", station_name="HBH03_imp",
                estimate_error=True, dof=24.0)
out = ed.write(savepath="mt_output/")

Verify the round-trip:

from pycsamt.seg.edi import EDIFile
ed2 = EDIFile(out)
assert ed2.Z.n_freq == sp.n_freq
to_io()[source]#
Return type:

tuple[SpectraSECT, SpectraIO]

matrix(k)[source]#
Parameters:

k (int)

Return type:

ndarray

psd(idx)[source]#
Parameters:

idx (int)

Return type:

ndarray

cross(i, j)[source]#
Parameters:
Return type:

ndarray

rotate(theta_deg, *, pairs=None)[source]#
Parameters:
Return type:

None

to_Z(*, id_to_chtype=None, e_labels=('EX', 'EY'), h_labels=('HX', 'HY'), use_remote=False, ridge=None, estimate_error=True, dof=None)[source]#

Recover an impedance tensor Z and, if available, the tipper from cross-spectra stored in this Spectra.

The method resolves channel types, extracts the electric and magnetic sub-blocks, and computes per-frequency Z = S_EH @ inv(S_HH). If a vertical magnetic channel is present it also computes the tipper T = S_ZH @ inv(S_HH). Optional ridge regularization can be applied to stabilize the magnetic block.

Parameters:
  • id_to_chtype (dict of str to str, optional) – Mapping from measurement IDs (as recorded in >=SPECTRASECT or DefineMeas) to channel types ("HX", "HY", "HZ", "EX", "EY"). If omitted, the method uses self.id_to_chtype when available, otherwise it interprets self.chan_ids directly as labels.

  • e_labels (tuple of str, default ("EX", "EY")) – Labels that identify the two electric channels used for the E block.

  • h_labels (tuple of str, default ("HX", "HY")) – Labels that identify the two horizontal magnetic channels used for the H block.

  • use_remote (bool, default False) – When duplicate electric channels exist (e.g., local and remote), choose the second occurrence for the E block if True; otherwise choose the first.

  • ridge (float, optional) – Non-negative Tikhonov regularization added to S_HH prior to inversion, S_HH + ridge * I.

  • estimate_error (bool, default True) – If True, estimate per-component 1-sigma standard errors for Z (and tipper when available) using compute_errors_from_S and the degrees of freedom given by dof (or inferred; see Notes).

  • dof (float or ndarray, optional) – Effective degrees of freedom per frequency. If an array is provided it must broadcast to n_freq. If None and estimate_error is True, the method tries to infer DoF from metadata via effective_dof_from_meta using segnum, or avgt * bw as a fallback.

Returns:

  • z_obj (pycsamt.z.z.Z) – Impedance object on the spectra frequency grid with z populated and, when estimated, z_err set.

  • tip (pycsamt.z.tipper.Tipper or None) – Tipper on the same grid when HZ is available. When errors are estimated, tipper uncertainties are attached.

Raises:

EdIDataError – If spectra are empty, channel types cannot be resolved, or the stabilized magnetic block is singular.

Notes

Per frequency, Z is formed as Z = S_EH @ inv(S_HH), where S_EH is the cross-spectra between E and H, and S_HH is the magnetic auto/cross block. If a vertical magnetic channel is available, the tipper is computed as T = S_ZH @ inv(S_HH).

Channel type resolution proceeds in this order:

  1. explicit id_to_chtype argument,

  2. self.id_to_chtype from the section header or DefineMeas,

  3. direct interpretation of self.chan_ids.

When both local and remote electric channels are present, setting use_remote=True chooses the second occurrence as a simple heuristic. Frequency ordering is preserved.

Uncertainties are computed by first-order propagation under a complex-Wishart model and scale as 1 / sqrt(DoF). If DoF cannot be determined, errors are left as NaN.

Examples

>>> Zhat, That = spectra.to_Z()
>>> Zhat, _ = spectra.to_Z(
...     use_remote=True, ridge=1e-6
... )
>>> Zhat, _ = spectra.to_Z(
...     dof=np.full(spectra.n_freq, 24.0)
... )

See also

Spectra.from_Z

Inverse operation that synthesizes spectra.

spectra_from_Z

Functional wrapper for the inverse operation.

effective_dof_from_meta

Infer DoF from segnum, avgt and bw.

compute_errors_from_S

Per-frequency uncertainty estimator.

References

classmethod from_Z(z_obj, **kws)[source]#

Create a Spectra from a transfer function Z.

This class method is a thin, convenience wrapper around spectra_from_Z(). It synthesizes a full Hermitian cross–spectral density tensor from the impedance tensor Z(f) and optional inputs that control magnetic power and tipper usage.

Parameters:
  • z_obj (Z) – Input impedance object. The attributes z_obj.z (shape (n, 2, 2)) and z_obj.freq (shape (n,)) must be set.

  • **kws (Any) – Forwarded to spectra_from_Z(). See that function for the complete set of options such as S_HH, H_psd, tipper, include_hz, and chan_order.

Returns:

A spectra container on the same frequency grid as z_obj. Channel order follows the requested chan_order (default: HX, HY, EX, EY).

Return type:

Spectra

Raises:

EdIDataError – If z_obj is incomplete (missing z or freq).

Notes

Absolute spectral levels are not carried by the impedance tensor. To obtain physically scaled spectra, provide magnetic spectra via S_HH or H_psd. If neither is given, a unit–power assumption is used (S_HH = I), which is suitable for tests but not for quantitative analysis.

This method does not infer per–frequency metadata such as bandwidth or averaging time; those fields are initialized with zeros/NaNs.

Examples

>>> ed = EDIFile("site_imp.edi")
>>> sp = Spectra.from_Z(
...     ed.Z,
...     H_psd=(np.ones(ed.Z.n_freq),
...            np.ones(ed.Z.n_freq),
...            None),
... )
>>> sect, io = sp.to_io()
>>> _ = ed.write_new_edi(
...     edi_fn="site_with_synth_spec.edi",
...     Spectra=sp,
... )

See also

spectra_from_Z

Functional API that performs the synthesis.

pycsamt.seg.ops.synthesize_spectra_from_z

Low–level array helper used under the hood.

Spectra.to_Z

Inverse operation (spectra → Z).

References