15.4. EDI SPECTRA Recovery#
The historical SEG SPECTRA section stores a Hermitian cross-power
matrix at every frequency – far more information than the impedance
and error values in a standard EDI Z/ZVAR block.
spectra_to_emtf() and recover_spectra_transfer_functions()
recover transfer functions and the two covariance factors needed
for statistically correct rotation directly from those cross-power
matrices, following the same equations EMTF FCU v4.1 uses for
single-station and remote-reference processing. EMTF.from_edi prefers this recovery over the
plain impedance/tipper blocks whenever a file has a usable SPECTRA
section, and this page shows exactly why that default matters, not
just what it returns. Everything imports from the top level –
from pycsamt.emtf import EMTF, spectra_to_emtf – except
SpectraChannelMap and
resolve_spectra_channels(),
which are not re-exported at the package level and are used here from
pycsamt.emtf.converters.spectra directly.
15.4.1. A Real Remote-Reference File#
data/MT/SPECTRA/spectra01.edi is a de-identified, sanitized real
field file kept in the repository specifically for SPECTRA examples
(only location/operator/hardware identifiers were replaced; every
numeric cross-power value is untouched). Its channel list carries two
separate horizontal-magnetic pairs – one local, one remote –
which resolve_spectra_channels()
detects automatically:
>>> from pycsamt.seg.spectra import Spectra
>>> from pycsamt.emtf.converters.spectra import resolve_spectra_channels
>>> sp = Spectra.from_file("data/MT/SPECTRA/spectra01.edi")
>>> channel_map = resolve_spectra_channels(sp)
>>> print(channel_map)
SpectraChannelMap(hx=0, hy=1, ex=3, ey=4, hz=2, rx=5, ry=6, channel_types=('HX', 'HY', 'HZ', 'EX', 'EY', 'HX', 'HY'))
>>> print(channel_map.reference_type)
remote_reference
>>> print(channel_map.local_h, channel_map.remote_h, channel_map.outputs)
(0, 1) (5, 6) (2, 3, 4)
No explicit RX/RY label is present in this file; a second
HX/HY pair is enough on its own for
reference_type
to resolve to "remote_reference" rather than
"single_station" – exactly the historical convention EMTF FCU
itself follows, as the function’s own docstring notes.
15.4.2. Recovering Transfer Functions and Full Covariance#
recover_spectra_transfer_functions()
applies the FCU recovery equations to every frequency and returns a
SpectraRecoveryResult:
>>> from pycsamt.emtf.converters.spectra import recover_spectra_transfer_functions
>>> result = recover_spectra_transfer_functions(sp)
>>> print(result.used_indices.size, result.skipped_indices.size)
51 0
>>> print(result.impedance.data.shape, result.tipper.data.shape)
(51, 2, 2) (51, 1, 2)
>>> print(sorted(result.impedance.estimates.keys()))
['inverse_signal_covariance', 'residual_covariance', 'variance']
>>> print(result.impedance.estimates["inverse_signal_covariance"].data.shape)
(51, 2, 2)
>>> print(result.impedance.estimates["residual_covariance"].data.shape)
(51, 2, 2)
All 51 frequencies were usable here (skipped_indices is empty);
frequencies whose required cross-spectra are missing are skipped or
raise, controlled by missing_policy. Both impedance and tipper
come back with all three estimate kinds attached –
Transfer Functions and Estimates covers variance,
inverse_signal_covariance, and residual_covariance as
concepts; here they are real, non-trivial arrays recovered from
genuine cross-power data, not empty placeholders.
15.4.3. spectra_to_emtf and EMTF.from_edi’s Default Preference#
spectra_to_emtf() wraps the recovery above into a full
EMTF document, reusing the same EDI
metadata mappings covered in EDI Interoperability for everything that
is not scientific data:
>>> from pycsamt.emtf import spectra_to_emtf
>>> doc = spectra_to_emtf("data/MT/SPECTRA/spectra01.edi")
>>> print(doc.attrs["source_format"])
edi_spectra
>>> print(doc.processing.remote_reference.reference_type)
Remote Reference
>>> print(doc.metadata["edi_spectra"]["full_covariance_recovered"])
True
>>> print(doc.metadata["edi_spectra"]["covariance_algorithm"])
EMTF_FCU_v4.1
EMTF.from_edi reaches
this exact path automatically, with no extra argument, whenever a
usable SPECTRA section is present:
>>> from pycsamt.emtf import EMTF
>>> doc2 = EMTF.from_edi("data/MT/SPECTRA/spectra01.edi")
>>> print(doc2.attrs["source_format"])
edi_spectra
15.4.4. Why the Preferred Path Matters: a Real Sign Difference#
Passing prefer_spectra=False forces the historical plain-block
path instead – the same code path EDI Interoperability describes for
an ordinary EDI file. This particular file has no literal Z block
at all, so pycsamt synthesizes one from the same SPECTRA data through
a different internal method,
Spectra.to_Z():
>>> doc_plain = EMTF.from_edi("data/MT/SPECTRA/spectra01.edi", prefer_spectra=False)
>>> print(sorted(doc_plain.impedance.estimates.keys()))
[]
That fallback already loses something real – zero estimates, not
even a plain variance, because the internal call underneath uses
estimate_error=False. It also does not just lose precision; it
uses a genuinely different sign convention. The magnitudes still
agree:
>>> import numpy as np
>>> print(np.allclose(np.abs(doc2.z), np.abs(doc_plain.z), rtol=0.05))
True
but the imaginary part does not – every value from the two paths is an exact complex conjugate of the other:
>>> phase_spectra = np.degrees(np.angle(doc2.z[:, 0, 1]))
>>> phase_plain = np.degrees(np.angle(doc_plain.z[:, 0, 1]))
>>> print(np.allclose(phase_spectra, -phase_plain))
True
The same real file’s Zxy phase, computed two ways. The
FCU-compatible recovery (blue) stays in the physically expected
quadrant for this component; the legacy fallback (red) is its
exact mirror image at every single period.#
This is not a coincidence, and it is fully explained by two named,
tested Spectra views: fcu_cross_spectra
(the packed SEG/FCU convention that
recover_spectra_transfer_functions requires) and the legacy S
view that to_Z() uses, which are
exact complex conjugates of each other for this file:
>>> print(np.allclose(sp.fcu_cross_spectra, np.conjugate(sp.S)))
True
This is precisely the risk the recovery function’s own module
docstring warns about: reinterpreting the legacy convention as an FCU
archive “can conjugate the recovered transfer function.” Both
conventions are internally consistent and deliberately tested against
each other; the point is that they are not interchangeable, and
EMTF.from_edi’s default of prefer_spectra=True exists
specifically so that a caller gets the FCU-compatible, fully
covariance-attached recovery without having to know this distinction
exists.
15.4.5. Choosing the Right Function#
Need |
Function |
Notes |
|---|---|---|
Full document from a SPECTRA-bearing EDI |
|
The latter dispatches here automatically when SPECTRA is usable. |
Just the recovered TFs and covariance, no document |
|
Returns a |
Which channels are local/remote/output |
|
Detects a second |
Force the legacy, lower-information path |
|
Loses covariance and may use a different sign convention – see above. |
15.4.6. Next Steps#
EDI Interoperability covers the plain EDI adapter this page contrasts against, and the metadata mappings
spectra_to_emtfreuses;Transfer Functions and Estimates covers what
variance,inverse_signal_covariance, andresidual_covariancemean as general concepts;Rotation and Covariance covers how these full-covariance estimates transform once a rotation is applied.