pycsamt.metadata#
Survey, frequency, quality, instrument, geology, and rock metadata helpers.
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:
objectBibliographic 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”.
- Parameters:
- DOI_PATTERN = re.compile('^10\\.\\d{4,9}/[-._;()/:A-Z0-9]+$', re.IGNORECASE)#
- classmethod from_dict(data)#
Construct a Reference from a dict.
- class pycsamt.metadata.CopyrightInfo(release_status, conditions_of_use, reference=<factory>, extra=<factory>)#
Bases:
objectCopyright and usage conditions.
- Variables:
- Parameters:
- class pycsamt.metadata.Person(name=None, email=None, organization=None, organization_url=None, extra=<factory>)#
Bases:
objectPerson contact information.
- Variables:
- Parameters:
- class pycsamt.metadata.Software(name, version=None, release=None, author=<factory>, extra=<factory>)#
Bases:
objectSoftware metadata.
- Variables:
- Parameters:
- 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 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.
- get_pattern(rock)#
Return (hatch, color) tuple for a given rock.
Raises KeyError if rock not found.
- class pycsamt.metadata.BBox(lat_min, lat_max, lon_min, lon_max)#
Bases:
objectGeographic bounding box in decimal degrees (WGS-84 default).
- Parameters:
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²
- contains(lat, lon)#
Return True when (lat, lon) lies inside or on the boundary.
- 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:
objectCampaign-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","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")
- classmethod from_sites(sites, name='survey', method='MT', **kwargs)#
Build a
SurveyMetafrom a Sites collection.- Parameters:
- Return type:
- 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.
- classmethod from_dict(d)#
Reconstruct from a plain dict (as produced by
to_dict()).- Parameters:
- Return type:
- to_json(path=None)#
Serialise to a JSON string (optionally write to path).
- classmethod from_json(path)#
Load from a JSON file.
- Parameters:
- Return type:
- to_yaml(path=None)#
Serialise to a YAML string (optionally write to path).
- classmethod from_yaml(path)#
Load from a YAML file.
- Parameters:
- Return type:
- class pycsamt.metadata.Formation(name, resistivity_range, depth_range, n_layers_range, description='', rock_types=<factory>, extra=<factory>)#
Bases:
objectA 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)
- to_prior()#
Return a dict compatible with
LayeredModel.from_geology(name).The returned dict has keys
n_layers,log_rho_range,depth_max_range, anddescription— exactly the format expected byGEOLOGY_PRIORSinforward/synthetic.py.
- class pycsamt.metadata.GeologyCatalog(formations=None)#
Bases:
objectRegistry 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
- lookup_by_resistivity(rho, n=3)#
Return the n formations whose resistivity range best covers rho.
Matching priority:
Formations whose interval contains rho (sorted by narrowest range).
Formations nearest to rho in log10 space.
- lookup_by_depth(depth_m, n=3)#
Return formations whose depth range contains depth_m (metres).
- lookup_by_rock_type(rock_type)#
Return formations that list rock_type in their
rock_types.
- all_scenarios()#
Return a copy of the internal store (name → Formation).
- to_prior(name)#
Return a
GEOLOGY_PRIORS-compatible dict for name.This is the adapter used by
from_geology().
- to_dataframe(*, api=None)#
Return a
pandas.DataFramewith one row per formation.
- 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_PRIORSdict inforward/synthetic.py.
- class pycsamt.metadata.QualityFlag(*values)#
-
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:
- classmethod worst(flags)#
Return the worst (lowest-rank) flag in flags.
- Parameters:
flags (list[QualityFlag])
- Return type:
- classmethod best(flags)#
Return the best (highest-rank) flag in flags.
- Parameters:
flags (list[QualityFlag])
- Return type:
- class pycsamt.metadata.ComponentQuality(name, coverage, n_valid, n_total, snr_mean=None, snr_std=None)#
Bases:
objectQuality 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%"
- flag: QualityFlag#
- 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:
- class pycsamt.metadata.DataQuality(station, n_freq, freq_min=None, freq_max=None, components=<factory>)#
Bases:
objectData-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()
- components: list[ComponentQuality]#
- overall: QualityFlag#
- classmethod from_site(site)#
Build a
DataQualityfrom a Site-like object.Accepts any object exposing
.name,.freq,.z, and.tipper(as perSiteMixin).- Parameters:
site (Any)
- Return type:
- classmethod from_edi(edi_path)#
Build from a spectra/impedance EDI file path.
- Parameters:
edi_path (Any)
- Return type:
- get(name)#
Return
ComponentQualityfor name, or None.- Parameters:
name (str)
- Return type:
ComponentQuality | None
- property z_components: list[ComponentQuality]#
Return only the Z-tensor component records.
- pycsamt.metadata.assess_collection(sites)#
Compute
DataQualityfor every site in sites.- Parameters:
- Return type:
list of DataQuality
- pycsamt.metadata.quality_dataframe(sites, *, api=None)#
Return a
pandas.DataFramewith one quality row per station.Columns:
station,n_freq,freq_min,freq_max,overall,mean_coverage, and onecov_<component>column per impedance component.
- class pycsamt.metadata.FrequencyBand(name, label, f_min, f_max, method, doi_ref_rho=100.0, notes='')#
Bases:
objectSpecification 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_maxandf_min.period_max (float) – Period bounds derived from
f_maxandf_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
- 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:
- doi_range_m(rho=None)#
Return the (shallow, deep) depth-of-investigation estimate in metres.
Shallow DOI corresponds to
f_max; deep DOI tof_min.- Parameters:
rho (float, optional) – Resistivity in Ω·m; defaults to
doi_ref_rho.- Return type:
- logspace(n=30)#
Return n log-spaced frequencies inside the band (Hz).
- overlaps(other)#
Return True when self and other share any frequency.
- Parameters:
other (FrequencyBand)
- Return type:
- intersection(other)#
Return the (f_min, f_max) intersection with other, or None.
- Parameters:
other (FrequencyBand)
- Return type:
- clip_frequencies(freqs)#
Return only those elements of freqs that lie inside the band.
- pycsamt.metadata.band_for_frequency(f, registry=None)#
Return all bands in the registry that contain f Hz.
- Parameters:
- Returns:
Sorted from narrowest to widest (fewest to most decades).
- Return type:
- pycsamt.metadata.frequency_range(method, registry=None)#
Return
(f_min, f_max)for a named method or band.- Parameters:
- Returns:
(f_min, f_max)in Hz.- Return type:
- Raises:
KeyError – When method is not found in any band.
- pycsamt.metadata.doi_estimate(f, rho=100.0)#
Return the skin-depth DOI estimate in metres at frequency f.
- pycsamt.metadata.register_band(band)#
Add or replace a
FrequencyBandin 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:
objectSpecification 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
- class pycsamt.metadata.InstrumentMeta(system='', serial=None, magnetic_sensor=None, electric_sensor=None, software_version='', notes='')#
Bases:
objectStructured 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
>HEADblock or in a compact YAML survey manifest are stored here. Calibration tables belong in separate files referenced bynotes.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'}
- magnetic_sensor: SensorSpec | None = None#
- electric_sensor: SensorSpec | None = None#
- to_head_fields()#
Return a dict of EDI
>HEADkey-value pairs.The mapping is:
acqby ← system / serial (or just system) progvers ← software_version (or the package default)
- classmethod from_head(head)#
Construct from a
Headobject.Only
acqbyandprogversare extracted; sensor details cannot be inferred from the EDI header alone.- Parameters:
head (Any) – A
pycsamt.seg.heads.Headinstance (or any object withacqbyandprogversstring attributes).- Return type:
- 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:
- Raises:
KeyError – If name is not in
KNOWN_SYSTEMS.
Examples
>>> inst = InstrumentMeta.from_preset("lemi_424") >>> inst.system 'LEMI-424'
- classmethod from_yaml(s)#
Deserialise from a YAML string (requires PyYAML).
- Parameters:
s (str)
- Return type:
- save(path, fmt='json')#
Write to path as JSON or YAML.
- Parameters:
path (str) – Destination file path.
fmt ({"json", "yaml"})
- Return type:
None
- pycsamt.metadata.known_system(name)#
Shortcut for
InstrumentMeta.from_preset().- Parameters:
name (str) – Preset key (case-insensitive, spaces/hyphens treated as underscores).
- Return type:
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:
Metadata Modules#
pycsamt.metadata.frequency |
|
pycsamt.metadata.geology |
|
pycsamt.metadata.instrument |
|
pycsamt.metadata.quality |
|
pycsamt.metadata.survey |