2.5. pycsamt.metadata#

Format-neutral site, survey, provenance, channel, orientation, processing, quality, frequency, instrument, geology, and rock metadata helpers shared by the EDI and EMTF XML adapters.

Survey metadata, frequency bands, references, and software provenance.

class pycsamt.metadata.Reference(author, title, journal=None, year=None, volume=None, pages=None, doi=None, extra=<factory>)#

Bases: object

Bibliographic reference information.

Variables:
  • author (str) – Author list, e.g. “Doe, J.; Smith, A.”.

  • title (str) – Title of the work.

  • journal (Optional[str]) – Journal or publication name.

  • year (Optional[int]) – Year of publication.

  • volume (Optional[str]) – Volume number or identifier.

  • pages (Optional[str]) – Page range, e.g. “123–130”.

  • doi (Optional[str]) – Digital Object Identifier, e.g. “10.1000/xyz123”.

  • extra (Dict[str, Any]) – Any additional fields.

Parameters:
author: str#
title: str#
journal: str | None = None#
year: int | None = None#
volume: str | None = None#
pages: str | None = None#
doi: str | None = None#
extra: dict[str, Any]#
DOI_PATTERN = re.compile('^10\\.\\d{4,9}/[-._;()/:A-Z0-9]+$', re.IGNORECASE)#
to_dict()#

Return all reference fields as a dict.

Return type:

dict[str, Any]

classmethod from_dict(data)#

Construct a Reference from a dict.

Parameters:

data (dict[str, Any])

Return type:

Reference

to_bibtex(key)#

Export this reference as a BibTeX entry.

Parameters:

key (str) – Citation key, e.g. “Doe2021”.

Returns:

Multi-line BibTeX entry.

Return type:

str

class pycsamt.metadata.CopyrightInfo(release_status, conditions_of_use, reference=<factory>, extra=<factory>)#

Bases: object

Copyright and usage conditions.

Variables:
  • release_status (str) – e.g. “open”, “public”, “proprietary”.

  • conditions_of_use (str) – Text describing usage terms.

  • reference (Reference) – Citation for data source.

  • extra (Dict[str, Any]) – Additional fields.

Parameters:
release_status: str#
conditions_of_use: str#
reference: Reference#
extra: dict[str, Any]#
to_dict()#
Return type:

dict[str, Any]

class pycsamt.metadata.Person(name=None, email=None, organization=None, organization_url=None, extra=<factory>)#

Bases: object

Person contact information.

Variables:
  • name (Optional[str])

  • email (Optional[str])

  • organization (Optional[str])

  • organization_url (Optional[str])

  • extra (Dict[str, Any])

Parameters:
  • name (str | None)

  • email (str | None)

  • organization (str | None)

  • organization_url (str | None)

  • extra (dict[str, Any])

name: str | None = None#
email: str | None = None#
organization: str | None = None#
organization_url: str | None = None#
extra: dict[str, Any]#
to_dict()#
Return type:

dict[str, Any]

class pycsamt.metadata.Software(name, version=None, release=None, author=<factory>, extra=<factory>)#

Bases: object

Software metadata.

Variables:
  • name (str)

  • version (Optional[str])

  • release (Optional[str])

  • author (Person)

  • extra (Dict[str, Any])

Parameters:
name: str#
version: str | None = None#
release: str | None = None#
author: Person#
extra: dict[str, Any]#
to_dict()#
Return type:

dict[str, Any]

exception pycsamt.metadata.MetadataError#

Bases: Exception

Base exception for metadata classes.

class pycsamt.metadata.ProvenanceMeta(create_time=None, creating_application=None, creator=None, submitter=None, extra=<factory>)#

Bases: PyCSAMTObject

Describe creation and stewardship of a scientific data product.

This object is deliberately independent of EDI and XML syntax. It maps naturally to EMTF provenance fields while remaining reusable by future formats and airborne data products.

Parameters:
  • create_time (datetime or str, optional) – Product creation time. Strings are preserved verbatim so historical metadata can be retained without inventing a timezone or precision.

  • creating_application (str, optional) – Application that created the data product.

  • creator (Person, optional) – Original creator and person responsible for submission/archiving.

  • submitter (Person, optional) – Original creator and person responsible for submission/archiving.

  • extra (dict) – Unmodelled provenance fields retained losslessly in memory.

create_time: datetime | str | None = None#
creating_application: str | None = None#
creator: Person | None = None#
submitter: Person | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

class pycsamt.metadata.LocationMeta(latitude=None, longitude=None, elevation=None, datum='WGS84', elevation_units='meters', declination=None, declination_epoch=None, extra=<factory>)#

Bases: PyCSAMTObject

Geographic location and optional magnetic declination metadata.

Parameters:
latitude: float | None = None#
longitude: float | None = None#
elevation: float | None = None#
datum: str | None = 'WGS84'#
elevation_units: str = 'meters'#
declination: float | None = None#
declination_epoch: float | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

property has_horizontal_coordinates: bool#
class pycsamt.metadata.SiteMeta(project=None, survey=None, year_collected=None, country=None, site_id=None, name=None, location=None, acquired_by=None, start=None, end=None, extra=<factory>)#

Bases: PyCSAMTObject

Site identity, acquisition interval, and geographic location.

Parameters:
project: str | None = None#
survey: str | None = None#
year_collected: int | None = None#
country: str | None = None#
site_id: str | int | None = None#
name: str | None = None#
location: LocationMeta | None = None#
acquired_by: str | None = None#
start: datetime | str | None = None#
end: datetime | str | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

property preferred_name: str | None#

Return human site name, falling back to the site identifier.

class pycsamt.metadata.OrientationMeta(mode=None, angle_to_geographic_north=None, rotation_info=None, extra=<factory>)#

Bases: PyCSAMTObject

Describe the coordinate orientation of transfer-function data.

OrientationMeta describes the orientation of the data, not the physical sensor geometry. Physical channel geometry is retained by SiteLayout so rotations remain reversible where sufficient information is available.

Parameters:
  • mode ({"orthogonal", "sitelayout"}, optional) – "orthogonal" means the TF matrix is expressed in an orthogonal coordinate frame. "sitelayout" means the TF follows the original site channel layout.

  • angle_to_geographic_north (float, optional) – Clockwise angle in degrees of the orthogonal x-axis relative to geographic north. It is intentionally not fabricated when unknown.

  • rotation_info (str, optional) – Human-readable record of known rotation history or ambiguity.

  • extra (dict) – Additional orientation metadata.

mode: str | None = None#
angle_to_geographic_north: float | None = None#
rotation_info: str | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

property is_orthogonal: bool#

Return whether the data are in an orthogonal coordinate frame.

property follows_site_layout: bool#

Return whether data orientation follows the sensor layout.

class pycsamt.metadata.ChannelMeta(name, field_type, orientation=None, tilt=None, x=None, y=None, z=None, x2=None, y2=None, z2=None, units=None, reference=None, sensor_id=None, extra=<factory>)#

Bases: PyCSAMTObject

Describe one electric, magnetic, or auxiliary EM channel.

Coordinates describe the physical site layout and are independent of the orientation applied to transfer-function matrices.

Parameters:
name: str#
field_type: str#
orientation: float | None = None#
tilt: float | None = None#
x: float | None = None#
y: float | None = None#
z: float | None = None#
x2: float | None = None#
y2: float | None = None#
z2: float | None = None#
units: str | None = None#
reference: str | None = None#
sensor_id: str | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

property is_electric: bool#
property is_magnetic: bool#
property has_second_endpoint: bool#
class pycsamt.metadata.SiteLayout(input_channels=<factory>, output_channels=<factory>, input_units=None, output_units=None, input_reference=None, output_reference=None, extra=<factory>)#

Bases: PyCSAMTObject

Original physical input/output channel geometry at a site.

Parameters:
input_channels: list[ChannelMeta]#
output_channels: list[ChannelMeta]#
input_units: str | None = None#
output_units: str | None = None#
input_reference: str | None = None#
output_reference: str | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

property input_names: tuple[str, ...]#
property output_names: tuple[str, ...]#
property n_input: int#
property n_output: int#
get_channel(name, *, role=None)#

Return a channel by name, optionally restricted by role.

Parameters:
  • name (str)

  • role (str | None)

Return type:

ChannelMeta | None

class pycsamt.metadata.RemoteReferenceMeta(reference_type=None, site=None, extra=<factory>)#

Bases: PyCSAMTObject

Describe remote-reference processing when it was used.

Parameters:
reference_type: str | None = None#
site: str | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

class pycsamt.metadata.ProcessingMeta(sign_convention=None, processed_by=None, software=None, remote_reference=None, processing_tag=None, run_list=None, extra=<factory>)#

Bases: PyCSAMTObject

Processing assumptions and software associated with an EMTF product.

This is the format-neutral canonical model. The historical pycsamt.seg.property.Processing object remains unchanged for EDI compatibility and can be adapted later by the EDI interoperability layer.

Parameters:
sign_convention: str | None = None#
processed_by: str | None = None#
software: Software | None = None#
remote_reference: RemoteReferenceMeta | None = None#
processing_tag: str | None = None#
run_list: list[str] | None = None#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

pycsamt.metadata.normalize_sign_convention(value)#

Normalize common Fourier sign-convention spellings.

Unknown non-empty values are preserved rather than silently coerced. This is intentionally stricter than the historical SEG helper, which defaulted unrecognized values to the positive convention.

Parameters:

value (str | None)

Return type:

str | None

class pycsamt.metadata.QualityComment(text, author=None)#

Bases: PyCSAMTObject

Free-text transfer-function quality comment with optional author.

Parameters:
  • text (str)

  • author (str | None)

text: str#
author: str | None = None#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

class pycsamt.metadata.TransferFunctionQuality(rating=None, good_from_period=None, good_to_period=None, comments=<factory>, warning_flag=None, warnings=<factory>, extra=<factory>)#

Bases: PyCSAMTObject

Archival quality assessment attached to an EM transfer function.

This class intentionally does not replace DataQuality. DataQuality is a pyCSAMT-computed coverage/SNR assessment, whereas TransferFunctionQuality preserves expert/archive metadata such as the EMTF 0–5 rating, accepted period range, warning flag, and comments.

Parameters:
rating: int | None = None#
good_from_period: float | None = None#
good_to_period: float | None = None#
comments: list[QualityComment]#
warning_flag: int | None = None#
warnings: list[QualityComment]#
extra: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

property has_warning: bool#

Return whether an archive warning is explicitly present.

property rating_label: str | None#

Return the conventional EMTF rating interpretation.

class pycsamt.metadata.RockProperties#

Bases: object

Access and query geological rock metadata:
  • resistivity ranges

  • plotting hatch patterns

Examples

>>> rp = RockProperties()
>>> rp.get_resistivity("shale")
[50.12, 32.0]
>>> pattern, color = rp.get_pattern("shale")
"=", (0.0, 0.0, 0.7)
property resistivity_ranges: dict[str, list[float]]#

All resistivity ranges keyed by rock name.

property hatch_patterns: dict[str, tuple[str, tuple[float, float, float]]]#

All hatch patterns keyed by rock name.

get_resistivity(rock)#

Return [max, min] resistivity for a given rock.

Raises KeyError if rock not found.

Parameters:

rock (str)

Return type:

list[float]

get_pattern(rock)#

Return (hatch, color) tuple for a given rock.

Raises KeyError if rock not found.

Parameters:

rock (str)

Return type:

tuple[str, tuple[float, float, float]]

find_matching_rocks(keyword)#

Return list of rock names containing keyword (case‑insensitive).

Parameters:

keyword (str)

Return type:

list[str]

class pycsamt.metadata.BBox(lat_min, lat_max, lon_min, lon_max)#

Bases: object

Geographic bounding box in decimal degrees (WGS-84 default).

Parameters:
  • lat_min (float) – Latitude bounds in decimal degrees (south → north).

  • lat_max (float) – Latitude bounds in decimal degrees (south → north).

  • lon_min (float) – Longitude bounds in decimal degrees (west → east).

  • lon_max (float) – Longitude bounds in decimal degrees (west → east).

Examples

bbox = BBox(27.8, 28.9, 101.5, 103.2)
assert 28.0 in bbox  # latitude membership test
print(bbox.centre)  # (28.35, 102.35)
print(bbox.area_deg2)  # 1.1 × 1.7 ≈ 1.87 deg²
lat_min: float#
lat_max: float#
lon_min: float#
lon_max: float#
property centre: tuple[float, float]#

Return (lat, lon) centre of the box.

property area_deg2: float#

Return the area of the box in squared degrees.

property span_lat: float#
property span_lon: float#
contains(lat, lon)#

Return True when (lat, lon) lies inside or on the boundary.

Parameters:
Return type:

bool

to_dict()#
Return type:

dict[str, float]

classmethod from_dict(d)#
Parameters:

d (dict[str, Any])

Return type:

BBox

classmethod from_coords(lats, lons)#

Build a tight bounding box from lists of coordinates.

Parameters:
Return type:

BBox

class pycsamt.metadata.SurveyMeta(name, project=None, operator=None, method='MT', bbox=None, date_start=None, date_end=None, crs='WGS84', n_stations=None, notes='', extra=<factory>)#

Bases: object

Campaign-level descriptor for an MT/AMT/CSAMT/TEM survey.

Parameters:
  • name (str) – Short survey identifier (e.g. "WILLY_L18_2023").

  • project (str, optional) – Parent project name (e.g. "Copper-gold exploration").

  • operator (str, optional) – Acquisition company or institution.

  • method (str, default "MT") – EM method: "MT", "AMT", "CSAMT", "TEM", "CSEM", "AEM", "BBMT", "LAMT", or "LMT".

  • bbox (BBox, optional) – Geographic bounding box of all station locations.

  • date_start (date, optional) – First day of field acquisition.

  • date_end (date, optional) – Last day of field acquisition.

  • crs (str, default "WGS84") – Coordinate reference system name.

  • n_stations (int, optional) – Total number of stations.

  • notes (str) – Free-form annotation.

  • extra (dict) – Arbitrary additional fields preserved on round-trips.

Examples

meta = SurveyMeta(
    name="WILLY_L18",
    project="Phase-III drill targeting",
    operator="EarthAI-Tech",
    method="AMT",
    bbox=BBox(27.8, 28.9, 101.5, 103.2),
    n_stations=128,
)
print(meta.duration_days)  # None (no dates set)

# build from a Sites collection:
meta2 = SurveyMeta.from_sites(sites, name="survey_A")

# JSON round-trip:
meta.to_json("survey_meta.json")
meta3 = SurveyMeta.from_json("survey_meta.json")
name: str#
project: str | None = None#
operator: str | None = None#
method: str = 'MT'#
bbox: BBox | None = None#
date_start: date | None = None#
date_end: date | None = None#
crs: str = 'WGS84'#
n_stations: int | None = None#
notes: str = ''#
extra: dict[str, Any]#
property duration_days: int | None#

Number of acquisition days, or None when dates are incomplete.

property is_complete: bool#

Return True when all key descriptors are populated.

classmethod from_sites(sites, name='survey', method='MT', **kwargs)#

Build a SurveyMeta from a Sites collection.

Parameters:
  • sites (Any) – Sites or any iterable of Site-like objects exposing .coords (lat, lon, elev).

  • name (str) – Survey name to assign.

  • method (str) – EM method label.

  • **kwargs – Additional fields forwarded to the constructor.

Return type:

SurveyMeta

update_edi_head(head)#

Push survey fields into an EDI Head.

Only non-None fields are written so existing values are not overwritten unless a replacement is explicitly provided.

Parameters:

head (Head) – An EDI Head instance.

Return type:

None

to_dict()#

Return all fields as a plain JSON-serialisable dict.

Return type:

dict[str, Any]

classmethod from_dict(d)#

Reconstruct from a plain dict (as produced by to_dict()).

Parameters:

d (dict[str, Any])

Return type:

SurveyMeta

to_json(path=None)#

Serialise to a JSON string (optionally write to path).

Parameters:

path (str | Path | None)

Return type:

str

classmethod from_json(path)#

Load from a JSON file.

Parameters:

path (str | Path)

Return type:

SurveyMeta

to_yaml(path=None)#

Serialise to a YAML string (optionally write to path).

Parameters:

path (str | Path | None)

Return type:

str

classmethod from_yaml(path)#

Load from a YAML file.

Parameters:

path (str | Path)

Return type:

SurveyMeta

summary()#

Return a compact one-paragraph summary string.

Return type:

str

class pycsamt.metadata.Formation(name, resistivity_range, depth_range, n_layers_range, description='', rock_types=<factory>, extra=<factory>)#

Bases: object

A named geological scenario for layered-Earth modelling.

Parameters:
  • name (str) – Unique identifier (lower-case, e.g. "sedimentary").

  • resistivity_range (tuple[float, float]) – Expected resistivity interval (Ω·m) as (rho_min, rho_max).

  • depth_range (tuple[float, float]) – Typical investigation depth range (m) as (depth_min, depth_max).

  • n_layers_range (tuple[int, int]) – Plausible number of layers for synthetic generation, (n_min, n_max).

  • description (str) – Free-text geological description.

  • rock_types (list[str]) – Rock type names that correspond to entries in GEO_ROCK_RESISTIVITY.

  • extra (dict) – Arbitrary additional parameters preserved on round-trips.

Examples

f = Formation(
    name="hydrothermal",
    resistivity_range=(1.0, 1e3),
    depth_range=(200, 3000),
    n_layers_range=(3, 6),
    description="Hydrothermal alteration zone",
)
print(f.rho_mid, f.log_rho_range)
name: str#
resistivity_range: tuple[float, float]#
depth_range: tuple[float, float]#
n_layers_range: tuple[int, int]#
description: str = ''#
rock_types: list[str]#
extra: dict[str, Any]#
property log_rho_range: tuple[float, float]#

Log10-resistivity interval (log_rho_min, log_rho_max).

property rho_mid: float#

Geometric-mean resistivity (Ω·m).

property depth_mid: float#

Arithmetic-mean depth (m).

property n_layers_mid: int#

Midpoint of the n_layers range, rounded.

to_prior()#

Return a dict compatible with LayeredModel.from_geology(name).

The returned dict has keys n_layers, log_rho_range, depth_max_range, and description — exactly the format expected by GEOLOGY_PRIORS in forward/synthetic.py.

Return type:

dict[str, Any]

to_dict()#
Return type:

dict[str, Any]

classmethod from_dict(d)#
Parameters:

d (dict[str, Any])

Return type:

Formation

classmethod from_prior(name, prior)#

Build a Formation from a GEOLOGY_PRIORS entry.

Parameters:
Return type:

Formation

class pycsamt.metadata.GeologyCatalog(formations=None)#

Bases: object

Registry of geological formations for layered-Earth modelling.

Parameters:

formations (list of Formation, optional) – Initial formations. When omitted the built-in catalog is loaded.

Examples

from pycsamt.metadata.geology import CATALOG

# list all names
print(CATALOG.names())

# look up by name
f = CATALOG.get("sedimentary")

# find closest by resistivity
matches = CATALOG.lookup_by_resistivity(50.0, n=3)

# add a custom formation
CATALOG.add(
    Formation(
        name="custom",
        resistivity_range=(5.0, 200.0),
        depth_range=(100, 500),
        n_layers_range=(3, 5),
    )
)

# export to a pandas DataFrame
df = CATALOG.to_dataframe()
add(formation)#

Register formation, overwriting any existing entry with the same name.

Parameters:

formation (Formation)

Return type:

None

remove(name)#

Remove formation by name (raises KeyError when absent).

Parameters:

name (str)

Return type:

None

reset()#

Restore the built-in catalog, discarding custom additions.

Return type:

None

get(name)#

Return the Formation for name.

Raises:

KeyError – When name is not in the catalog.

Parameters:

name (str)

Return type:

Formation

names()#

Return a sorted list of all registered formation names.

Return type:

list[str]

lookup_by_resistivity(rho, n=3)#

Return the n formations whose resistivity range best covers rho.

Matching priority:

  1. Formations whose interval contains rho (sorted by narrowest range).

  2. Formations nearest to rho in log10 space.

Parameters:
  • rho (float) – Query resistivity in Ω·m.

  • n (int) – Maximum number of results to return.

Return type:

list[Formation]

lookup_by_depth(depth_m, n=3)#

Return formations whose depth range contains depth_m (metres).

Parameters:
Return type:

list[Formation]

lookup_by_rock_type(rock_type)#

Return formations that list rock_type in their rock_types.

Parameters:

rock_type (str)

Return type:

list[Formation]

all_scenarios()#

Return a copy of the internal store (name → Formation).

Return type:

dict[str, Formation]

to_prior(name)#

Return a GEOLOGY_PRIORS-compatible dict for name.

This is the adapter used by from_geology().

Parameters:

name (str)

Return type:

dict[str, Any]

to_dataframe(*, api=None)#

Return a pandas.DataFrame with one row per formation.

Parameters:

api (bool | None)

Return type:

Any

pycsamt.metadata.geology_prior(name)#

Return a GEOLOGY_PRIORS-compatible dict for name.

This is a drop-in replacement for direct access to the old GEOLOGY_PRIORS dict in forward/synthetic.py.

Parameters:

name (str) – Formation name (case-insensitive).

Returns:

Keys: n_layers, log_rho_range, depth_max_range, description.

Return type:

dict

class pycsamt.metadata.QualityFlag(*values)#

Bases: str, Enum

Data-quality classification for one component or a whole station.

Variables:
  • GOOD ("good") – Coverage ≥ 90 %; component is reliable for inversion.

  • PARTIAL ("partial") – 50 % ≤ coverage < 90 %; component has significant gaps.

  • POOR ("poor") – 0 % < coverage < 50 %; component is largely missing or noisy.

  • MISSING ("missing") – Zero finite values; component is absent.

GOOD = 'good'#
PARTIAL = 'partial'#
POOR = 'poor'#
MISSING = 'missing'#
classmethod from_coverage(coverage)#

Return the flag that matches coverage (0–1).

Parameters:

coverage (float)

Return type:

QualityFlag

property rank: int#

Numeric severity rank (0 = worst, 3 = best).

classmethod worst(flags)#

Return the worst (lowest-rank) flag in flags.

Parameters:

flags (list[QualityFlag])

Return type:

QualityFlag

classmethod best(flags)#

Return the best (highest-rank) flag in flags.

Parameters:

flags (list[QualityFlag])

Return type:

QualityFlag

class pycsamt.metadata.ComponentQuality(name, coverage, n_valid, n_total, snr_mean=None, snr_std=None)#

Bases: object

Quality metrics for a single impedance component.

Parameters:
  • name (str) – Component label: "Zxx", "Zxy", "Zyx", "Zyy", or "Tipper".

  • coverage (float) – Fraction of finite values in the frequency range (0–1).

  • n_valid (int) – Number of frequencies with finite values.

  • n_total (int) – Total number of frequencies.

  • flag (QualityFlag) – Auto-computed classification from coverage.

  • snr_mean (float, optional) – Mean signal-to-noise ratio (dB) when available.

  • snr_std (float, optional) – Standard deviation of the SNR (dB) when available.

Examples

cq = ComponentQuality.from_array("Zxy", z_arr)
print(cq.flag)  # QualityFlag.GOOD
print(cq.pct_str)  # "100%"
name: str#
coverage: float#
n_valid: int#
n_total: int#
flag: QualityFlag#
snr_mean: float | None = None#
snr_std: float | None = None#
property pct_str: str#

Coverage as a formatted percentage string.

classmethod from_array(name, arr, snr=None)#

Compute quality from a raw array.

Parameters:
  • name (str) – Component label.

  • arr (array-like or None) – Raw complex or real impedance values; shape (n,) or (n, 2, 2) (only the relevant component is expected).

  • snr (array-like, optional) – Per-frequency SNR values in dB (same length as arr).

Return type:

ComponentQuality

to_dict()#
Return type:

dict[str, Any]

class pycsamt.metadata.DataQuality(station, n_freq, freq_min=None, freq_max=None, components=<factory>)#

Bases: object

Data-quality summary for a single MT station.

Parameters:
  • station (str) – Station identifier.

  • n_freq (int) – Total number of frequencies.

  • freq_min (float, optional) – Minimum frequency (Hz).

  • freq_max (float, optional) – Maximum frequency (Hz).

  • components (list of ComponentQuality) – Per-component quality records.

  • overall (QualityFlag) – Worst flag across all present components (auto-computed).

Examples

dq = DataQuality.from_site(site)
print(dq.overall)
print(dq.get("Zxy").coverage)
df_row = dq.to_dict()
station: str#
n_freq: int#
freq_min: float | None = None#
freq_max: float | None = None#
components: list[ComponentQuality]#
overall: QualityFlag#
classmethod from_site(site)#

Build a DataQuality from a Site-like object.

Accepts any object exposing .name, .freq, .z, and .tipper (as per SiteMixin).

Parameters:

site (Any)

Return type:

DataQuality

classmethod from_edi(edi_path)#

Build from a spectra/impedance EDI file path.

Parameters:

edi_path (Any)

Return type:

DataQuality

get(name)#

Return ComponentQuality for name, or None.

Parameters:

name (str)

Return type:

ComponentQuality | None

property z_components: list[ComponentQuality]#

Return only the Z-tensor component records.

property has_tipper: bool#
property mean_coverage: float#

Mean coverage across all components that have data.

summary()#

Return a compact multi-line summary.

Return type:

str

to_dict()#
Return type:

dict[str, Any]

pycsamt.metadata.assess_collection(sites)#

Compute DataQuality for every site in sites.

Parameters:

sites (Any) – Iterable of Site-like objects (e.g. Sites).

Return type:

list of DataQuality

pycsamt.metadata.quality_dataframe(sites, *, api=None)#

Return a pandas.DataFrame with one quality row per station.

Columns: station, n_freq, freq_min, freq_max, overall, mean_coverage, and one cov_<component> column per impedance component.

Parameters:
Return type:

Any

class pycsamt.metadata.FrequencyBand(name, label, f_min, f_max, method, doi_ref_rho=100.0, notes='')#

Bases: object

Specification of an EM geophysical frequency band.

Parameters:
  • name (str) – Short identifier, e.g. "AMT".

  • label (str) – Human-readable name, e.g. "Audio-frequency MT".

  • f_min (float) – Minimum operational frequency in Hz.

  • f_max (float) – Maximum operational frequency in Hz.

  • method (str) – EM acquisition method: "MT", "AMT", "CSAMT", "TEM", "CSEM", etc.

  • doi_ref_rho (float, default 100.0) – Reference resistivity (Ω·m) used to compute DOI estimates via the skin-depth formula.

  • notes (str) – Optional annotation.

  • attributes (Computed)

  • -------------------

  • period_min (float) – Period bounds derived from f_max and f_min.

  • period_max (float) – Period bounds derived from f_max and f_min.

  • n_decades (float) – Number of frequency decades spanned by the band.

Examples

b = MT_BANDS["AMT"]
print(b.f_min, b.f_max)  # 10.0  100000.0
print(b.period_range)  # (1e-5, 0.1) s
print(b.doi_range_m())  # DOI at 100 Ω·m
print(b.doi_range_m(rho=10.0))  # DOI at 10 Ω·m

# log-spaced frequencies
freqs = b.logspace(30)

# test membership
assert 1000.0 in b
name: str#
label: str#
f_min: float#
f_max: float#
method: str#
doi_ref_rho: float = 100.0#
notes: str = ''#
property period_min: float#

Minimum period in seconds (= 1 / f_max).

property period_max: float#

Maximum period in seconds (= 1 / f_min).

property period_range: tuple[float, float]#

(period_min, period_max) in seconds.

property frequency_range: tuple[float, float]#

(f_min, f_max) in Hz.

property n_decades: float#

Number of frequency decades in the band.

skin_depth_m(f, rho=None)#

Skin depth δ (m) at frequency f and resistivity rho.

Formula: δ ≈ 503.3 √(ρ / f)

Parameters:
  • f (float) – Frequency in Hz.

  • rho (float, optional) – Resistivity in Ω·m; defaults to doi_ref_rho.

Return type:

float

doi_range_m(rho=None)#

Return the (shallow, deep) depth-of-investigation estimate in metres.

Shallow DOI corresponds to f_max; deep DOI to f_min.

Parameters:

rho (float, optional) – Resistivity in Ω·m; defaults to doi_ref_rho.

Return type:

tuple[float, float]

logspace(n=30)#

Return n log-spaced frequencies inside the band (Hz).

Parameters:

n (int, default 30) – Number of frequencies.

Return type:

ndarray

contains(f)#

Return True when f (Hz) lies within the band.

Parameters:

f (float)

Return type:

bool

overlaps(other)#

Return True when self and other share any frequency.

Parameters:

other (FrequencyBand)

Return type:

bool

intersection(other)#

Return the (f_min, f_max) intersection with other, or None.

Parameters:

other (FrequencyBand)

Return type:

tuple[float, float] | None

clip_frequencies(freqs)#

Return only those elements of freqs that lie inside the band.

Parameters:

freqs (Any)

Return type:

ndarray

to_dict()#
Return type:

dict[str, Any]

pycsamt.metadata.band_for_frequency(f, registry=None)#

Return all bands in the registry that contain f Hz.

Parameters:
  • f (float) – Query frequency in Hz.

  • registry (dict, optional) – Registry to search; defaults to REGISTRY.

Returns:

Sorted from narrowest to widest (fewest to most decades).

Return type:

list of FrequencyBand

pycsamt.metadata.frequency_range(method, registry=None)#

Return (f_min, f_max) for a named method or band.

Parameters:
  • method (str) – Band name (e.g. "AMT") or method string (e.g. "CSAMT"). Case-insensitive. When multiple bands share the same method, the union range is returned.

  • registry (dict, optional) – Registry to search; defaults to REGISTRY.

Returns:

(f_min, f_max) in Hz.

Return type:

tuple[float, float]

Raises:

KeyError – When method is not found in any band.

pycsamt.metadata.register_band(band)#

Add or replace a FrequencyBand in the global registry.

Parameters:

band (FrequencyBand) – The band to register under band.name.

Return type:

None

class pycsamt.metadata.SensorSpec(sensor_type='induction_coil', model='', frequency_range=None, notes='')#

Bases: object

Specification for a single sensor (magnetic or electric).

Parameters:
  • sensor_type ({"induction_coil", "fluxgate", "electrode", "other"}) – Physical transducer principle.

  • model (str) – Manufacturer model string, e.g. "MTC-150H".

  • frequency_range (tuple[float, float] or None) – (f_min, f_max) in Hz that the sensor is rated for.

  • notes (str) – Free-text annotation (calibration file name, mounting details, etc.).

Examples

>>> s = SensorSpec("induction_coil", "MTC-150H", (1e-5, 1e4))
>>> s.covers(0.1)
True
>>> s.covers(1e6)
False
sensor_type: Literal['induction_coil', 'fluxgate', 'electrode', 'other'] = 'induction_coil'#
model: str = ''#
frequency_range: tuple[float, float] | None = None#
notes: str = ''#
covers(frequency_hz)#

Return True if frequency_hz lies within the rated band.

Parameters:

frequency_hz (float)

Return type:

bool

class pycsamt.metadata.InstrumentMeta(system='', serial=None, magnetic_sensor=None, electric_sensor=None, software_version='', notes='')#

Bases: object

Structured descriptor for an EM acquisition system.

Parameters:
  • system (str) – Human-readable system name, e.g. "Phoenix V8" or "Metronix ADU-07e".

  • serial (str or None) – Logger serial number or site-specific tag.

  • magnetic_sensor (SensorSpec or None) – Magnetic-field transducer (H field) specification.

  • electric_sensor (SensorSpec or None) – Electric-field transducer (E field) specification.

  • software_version (str) – Processing / acquisition software version string.

  • notes (str) – Free-text annotation (calibration date, operator remarks, …).

Notes

The class deliberately stays thin — only fields that appear (or could appear) in a standard EDI >HEAD block or in a compact YAML survey manifest are stored here. Calibration tables belong in separate files referenced by notes.

Examples

>>> from pycsamt.metadata.instrument import InstrumentMeta
>>> inst = InstrumentMeta(system="Phoenix V8", serial="V8-00123")
>>> inst.to_head_fields()
{'acqby': 'Phoenix V8 / V8-00123', 'progvers': 'pyCSAMT'}
system: str = ''#
serial: str | None = None#
magnetic_sensor: SensorSpec | None = None#
electric_sensor: SensorSpec | None = None#
software_version: str = ''#
notes: str = ''#
property label: str#

"<system> / <serial>" or just system.

Type:

Short identifier

summary()#

Return a multi-line human-readable summary.

Return type:

str

to_head_fields()#

Return a dict of EDI >HEAD key-value pairs.

The mapping is:

acqby    ← system / serial  (or just system)
progvers ← software_version (or the package default)
Returns:

Ready to pass to update().

Return type:

dict

classmethod from_head(head)#

Construct from a Head object.

Only acqby and progvers are extracted; sensor details cannot be inferred from the EDI header alone.

Parameters:

head (Any) – A pycsamt.seg.heads.Head instance (or any object with acqby and progvers string attributes).

Return type:

InstrumentMeta

classmethod from_preset(name)#

Build from a named preset in KNOWN_SYSTEMS.

Parameters:

name (str) – Case-insensitive preset key, e.g. "phoenix_v8".

Return type:

InstrumentMeta

Raises:

KeyError – If name is not in KNOWN_SYSTEMS.

Examples

>>> inst = InstrumentMeta.from_preset("lemi_424")
>>> inst.system
'LEMI-424'
to_dict()#

Serialise to a plain dict (JSON-safe).

Return type:

dict[str, Any]

to_json(indent=2)#

Serialise to a JSON string.

Parameters:

indent (int)

Return type:

str

classmethod from_json(s)#

Deserialise from a JSON string.

Parameters:

s (str)

Return type:

InstrumentMeta

to_yaml()#

Serialise to a YAML string (requires PyYAML).

Return type:

str

classmethod from_yaml(s)#

Deserialise from a YAML string (requires PyYAML).

Parameters:

s (str)

Return type:

InstrumentMeta

save(path, fmt='json')#

Write to path as JSON or YAML.

Parameters:
  • path (str) – Destination file path.

  • fmt ({"json", "yaml"})

Return type:

None

classmethod load(path)#

Load from a JSON or YAML file (format detected by extension).

Parameters:

path (str)

Return type:

InstrumentMeta

pycsamt.metadata.known_system(name)#

Shortcut for InstrumentMeta.from_preset().

Parameters:

name (str) – Preset key (case-insensitive, spaces/hyphens treated as underscores).

Return type:

InstrumentMeta

Examples

>>> from pycsamt.metadata.instrument import known_system
>>> inst = known_system("phoenix_v8")
>>> inst.system
'Phoenix V8'
pycsamt.metadata.list_presets()#

Return sorted list of available preset keys.

Examples

>>> from pycsamt.metadata.instrument import list_presets
>>> list_presets()
['geometrics_stratagem', 'generic_fluxgate', 'lemi_424', ...]
Return type:

list

2.5.1. Metadata Modules#

pycsamt.metadata.site

Site-level metadata for electromagnetic transfer functions.

pycsamt.metadata.survey

pycsamt.metadata.survey

pycsamt.metadata.provenance

Reusable provenance metadata for electromagnetic data products.

pycsamt.metadata.channels

Reusable electromagnetic channel and site-layout metadata.

pycsamt.metadata.orientation

Transfer-function orientation metadata independent of file format.

pycsamt.metadata.instrument

pycsamt.metadata.instrument

pycsamt.metadata.processing

Format-neutral electromagnetic processing metadata.

pycsamt.metadata.quality

pycsamt.metadata.quality

pycsamt.metadata.frequency

pycsamt.metadata.frequency

pycsamt.metadata.geology

pycsamt.metadata.geology

pycsamt.metadata.rocks

2.5.2. Frequency Helpers#

pycsamt.metadata.doi_estimate(f, rho=100.0)#

Return the skin-depth DOI estimate in metres at frequency f.

Parameters:
  • f (float) – Frequency in Hz.

  • rho (float, default 100.0) – Reference resistivity in Ω·m.

Return type:

float