2.7.2.1. pycsamt.airborne.site#

Airborne station containers: the non-EDI counterpart of pycsamt.site.

Site/Sites are, by design, EDI-shaped: a 2x2 impedance \(Z\), a 1x2 tipper, one flat station per file. Every numeric accessor on Site (.tipper, .z, .rho, .phase) routes through edi, and pycsamt.emtf.converters.edi.emtf_to_edi() deliberately refuses to build an EDI from a tipper-only document (no impedance transfer function) – pycsamt.airborne.ztem.tests.test_ztem_interop .test_ztem_is_not_silently_coerced_to_standard_edi enforces exactly this, and pycsamt.airborne.mobilemt.tests.test_mobilemt_interop gives MobileMT’s admittance the identical guarantee. So a genuine ZTEM/AFMAG measurement – tipper-only, no electric channel at all – or a MobileMT measurement – a 3x2 admittance tensor, not a tipper – cannot reach Site/Sites today, and forcing either into that 2x2-impedance-shaped model would mean either bending Site until it stops meaning “ground EDI station,” or leaving MobileMT permanently unrepresentable regardless.

This module is the parallel container family built instead, directly on the airborne scientific model that already exists (AirborneEMRecord/ AirborneEMLine/ AirborneEMDataset, holding a real EMTF document per sample – no EDI bridge, no impedance requirement):

  • AirborneSite – one flight-line sample. Read directly from its EMTF document: .tipper, .freq, and, unlike Site, .admittance (for MobileMT) alongside the typed metadata surface Site already exposes (.site_meta, .processing, .provenance, …), since AirborneEMRecord.emtf is an EMTF document too.

  • AirborneSites – an ordered collection, mirroring Sites’s indexing/lookup/select/map/ closest/write_xml surface where the concepts genuinely carry over. Bulk-editing helpers (edit_all, with_topography, to_profile) are deliberately not mirrored yet – they are real Sites features this module does not need for the emtools integration it exists to unblock, so they are left out rather than stubbed.

  • ensure_asites() – the ensure_sites counterpart: the single entry point every future airborne-aware emtools function should coerce its input through.

Reads EMTF-XML today (AirborneSites.from_xml_dir(), AirborneSite.from_xml()); a native H5 reader is a real, separate task for whenever a verified delivery schema exists (see pycsamt.airborne.io’s own module docstring for why pyCSAMT does not guess native formats), not something this module invents now.

Ordering#

Sites has to infer a spatial line order from station coordinates, because ground stations can arrive in arbitrary order. Airborne samples do not have that problem – NavigationTrack already records the definitive flight-line order – so AirborneSites has no ordered()/ordering analogue: AirborneSites.from_line() and AirborneSites.from_dataset() simply preserve navigation order, which is already correct.

Functions

ensure_asites(obj, *[, recursive, on_dup, ...])

Normalize arbitrary airborne input to an AirborneSites.

Classes

AirborneSite(record, *[, line_id, ...])

One airborne flight-line sample, read directly from its EMTF.

AirborneSites(items)

Ordered collection of AirborneSite objects.

class pycsamt.airborne.site.AirborneSite(record, *, line_id=None, technology=None, coords=None)[source]

Bases: CoreObject

One airborne flight-line sample, read directly from its EMTF.

Unlike Site, every numeric accessor here reads straight from the wrapped AirborneEMRecord’s EMTF document – there is no EDI bridge to materialize and no impedance requirement to satisfy.

Parameters:
  • record (AirborneEMRecord) – The sample this site wraps.

  • line_id (str, optional) – Owning flight-line identifier, for provenance/grouping. None when unknown (e.g. a bare, line-less record).

  • technology (str, optional) – Technology label ("ztem", "mobilemt", "afmag", …), the explicit differentiator against ground MT. Falls back to the record’s EMTF.subtype when not given explicitly; see technology.

  • coords ((float, float, float), optional) – Explicit (lat, lon, elev) override, normally supplied by the constructing classmethod (AirborneSites.from_line()/from_dataset) from the parent line’s NavigationTrack. When omitted, coords falls back to the record’s own EMTF.site.location, then to (nan, nan, nan).

Raises:

TypeError – If record is not an AirborneEMRecord.

See also

AirborneSites

Ordered collection of these.

pycsamt.site.base.Site

The ground-MT, EDI-shaped counterpart.

classmethod from_xml(source, *, line_id=None, technology=None)[source]

Build one site directly from an EMTF-XML file or document.

Parameters:
Return type:

AirborneSite

Notes

The sample identifier is resolved from document.site.site_id, then document.station, then the source file’s stem, in that order.

property record: AirborneEMRecord[source]

The wrapped AirborneEMRecord.

property emtf: EMTF | None[source]

The underlying EMTF.

property tf: EMTF | None[source]

Alias for emtf, matching Site.tf’s naming.

property sample_id: str[source]

Navigation sample identifier (stable; independent of any richer name resolved from metadata).

property line_id: str | None[source]

Owning flight-line identifier, or None if unknown.

property technology: str | None[source]

Technology label ("ztem", "mobilemt", …).

Explicit at construction, else the record’s EMTF.subtype, else None.

property name: str[source]

Station identifier resolved from metadata, else sample_id.

Resolution order: EMTF.site.site_id, EMTF.station, sample_id.

property station: str[source]

Alias for name, matching Site.name’s role.

property coords: tuple[float, float, float][source]

(lat, lon, elev) in decimal degrees and metres.

Explicit constructor override first, then the record’s own EMTF.site.location, then (nan, nan, nan) – never a fabricated position.

property freq: ndarray | None[source]

Frequency vector [Hz], or None if unknown.

property tipper: ndarray | None[source]

Tipper array, shape (nf, 1, 2), or None if absent.

property z: ndarray | None[source]

Impedance array, shape (nf, 2, 2), or None if absent (the normal case for ZTEM/AFMAG/MobileMT).

property admittance: ndarray | None[source]

MobileMT admittance array, shape (nf, 3, 2).

None for any record without an attached mobilemt_admittance transfer function – there is no analogue of this accessor on Site, since a 3x2 admittance cannot be represented by Site.z’s 2x2 shape.

property interstation_tensor: ndarray | None[source]

Tensor AFMAG/AirMt interstation magnetic TF, shape (nf, 3, 2).

None for any record without an attached interstation_transfer_functions transfer function. Deliberately a separate accessor from admittance (also (nf, 3, 2)-shaped): AirMt’s tensor relates ground-reference magnetic fields to airborne magnetic fields (Hx,Hy -> Hx,Hy,Hz), while MobileMT’s admittance relates ground electric fields to airborne magnetic fields (Ex,Ey -> Hx,Hy,Hz) – physically different responses that happen to share a matrix shape, so this module keeps them under different names rather than one generic “3x2 tensor” accessor that would blur which is which. See pycsamt.airborne.afmag’s module docstring for why the original-comparator generation (afmag_tilt_deg) is kept separate again from this one.

property afmag_tilt_deg: ndarray | None[source]

Original comparator AFMAG scalar tilt/deflection, shape (nf,).

None for any record without an attached afmag_tilt transfer function. Unlike interstation_tensor or a ground tipper, this is a single real number per frequency – the historical comparator has no polarization-ellipse decomposition available at all, only a line-direction deflection (see pycsamt.airborne.afmag’s module docstring).

property afmag_amplification_parameter: ndarray | None[source]

AirMt rotation-invariant amplification parameter, shape (nf,).

None for any record without an attached airmt_amplification_parameter transfer function; see pycsamt.airborne.afmag.compute_airmt_amplification_parameter() for the formula that derives it from interstation_tensor.

property quality: dict[str, Any][source]

Sample-level quality flags/scores (technology-defined).

property fields: dict[str, Any][source]

Auxiliary decoded fields (technology-defined).

For MobileMT, this is where a vendor-delivered native apparent_conductivity vector lives when present; see pycsamt.airborne.mobilemt.MOBILEMT_APPARENT_CONDUCTIVITY_FIELD.

property site_meta: Any[source]

pycsamt.metadata.SiteMeta (via emtf).

property site_layout: Any[source]

pycsamt.metadata.SiteLayout channel geometry.

property provenance: Any[source]

pycsamt.metadata.ProvenanceMeta creator/submitter info.

property processing: Any[source]

pycsamt.metadata.ProcessingMeta processing/software info.

property copyright: Any[source]

pycsamt.metadata.CopyrightInfo release/citation info.

property quality_meta: Any[source]

pycsamt.metadata.TransferFunctionQuality QC rating.

has_component(comp)[source]

Whether comp exists and has at least one finite value.

Parameters:

comp (str) – "tip"/"tx"/"ty"/"tipper" for the tipper; "admittance"/"y" for the MobileMT admittance; "interstation_tensor"/"ti" for the AirMt tensor; "afmag_tilt"/"tilt" for the original-comparator AFMAG scalar; "amplification_parameter"/"ap" for the AirMt derived parameter; anything else is looked up against z.

Return type:

bool

to_dataframe(kind='tipper')[source]

Export this site’s data to a tidy pandas.DataFrame.

Parameters:

kind ({"tipper", "admittance", "z"}, default "tipper")

Returns:

Indexed by frequency (name "f"). Columns depend on kind: Tx, Ty; Yxx, Yxy, Yyx, Yyy, Yhzx, Yhzy; or Zxx, Zxy, Zyx, Zyy.

Return type:

pandas.DataFrame

Raises:

ValueError – If kind is not recognized.

summary()[source]

Summarize identity, geometry, and data coverage.

Returns:

Keys: name, line_id, sample_id, technology, nfreq, lat, lon, elev, tipper, admittance (booleans).

Return type:

dict

to_xml(target=None, **kwargs)[source]

Serialize this site’s document to EMTF XML.

Parameters:
  • target (str or pathlib.Path, optional) – Destination path. If None, the XML is returned as a string.

  • **kwargs – Forwarded to write_xml()/to_xml.

Return type:

str or Any

Raises:

ValueError – If this site has no attached EMTF document.

class pycsamt.airborne.site.AirborneSites(items)[source]

Bases: CoreObject

Ordered collection of AirborneSite objects.

The airborne counterpart of Sites. See the module docstring for why order here is simply preserved navigation order rather than something to infer.

Parameters:

items (AirborneSite, AirborneEMRecord, EMTF, str, Path, or iterable) – A single item, or an iterable of them. Raw AirborneEMRecord/EMTF/path items are coerced via AirborneSite.from_xml()-style construction with no line context; prefer from_line()/from_dataset() when that context is available.

Raises:

TypeError – If an item cannot be coerced to AirborneSite.

See also

ensure_asites

Flexible entry-point coercion, including directories of EMTF-XML files and duplicate-name policy.

pycsamt.site.base.Sites

The ground-MT counterpart.

classmethod from_xml_dir(path, *, recursive=True, pattern='*.xml', line_id=None, strict=False)[source]

Read every EMTF-XML file under path into one container.

Parameters:
  • path (str or pathlib.Path) – A single EMTF-XML file, or a directory to search.

  • recursive (bool, default True) – Search subdirectories too (Path.rglob) instead of only the top level (Path.glob).

  • pattern (str, default "*.xml") – Glob pattern used when path is a directory.

  • line_id (str, optional) – Forwarded to every AirborneSite.from_xml() call.

  • strict (bool, default False) – If True, a file that fails to parse raises instead of being skipped, and an empty result raises too.

Returns:

Sites in sorted-filename order.

Return type:

AirborneSites

Raises:

ValueError – If strict and nothing could be read.

classmethod from_line(line, *, technology=None)[source]

Build a container from one already-constructed flight line.

Parameters:
  • line (AirborneEMLine) – Records are visited via iter_records(), i.e. in navigation order, skipping samples with no attached record.

  • technology (str, optional) – Forwarded to every AirborneSite; falls back to line.attrs["technology"] when not given.

Return type:

AirborneSites

Raises:

TypeError – If line is not an AirborneEMLine.

classmethod from_dataset(dataset, *, technology=None)[source]

Flatten every line of a dataset into one container.

Parameters:
  • dataset (AirborneEMDataset) – Lines are visited via iter_lines() in insertion order; see from_line() for the per-line ordering.

  • technology (str, optional) – Forwarded to from_line() for every line; falls back to dataset.attrs["technology"] when not given.

Return type:

AirborneSites

Raises:

TypeError – If dataset is not an AirborneEMDataset.

by_index(i)[source]

Retrieve by zero-based index.

Parameters:

i (int)

Return type:

AirborneSite

get(name)[source]

Safe lookup by case-insensitive name; None if absent.

Parameters:

name (str)

Return type:

AirborneSite | None

as_list()[source]

The underlying list of AirborneSite objects.

Return type:

list[AirborneSite]

to_emtf_list()[source]

The underlying EMTF documents, in site order.

Return type:

list[Any]

property technologies: tuple[str, ...][source]

Sorted, deduplicated AirborneSite.technology values present in this container.

property line_ids: tuple[str, ...][source]

Sorted, deduplicated AirborneSite.line_id values present in this container.

select(names=None, predicate=None)[source]

Filter by explicit names or by a boolean predicate.

Parameters:
  • names (sequence of str, optional) – Case-insensitive names to retain; takes precedence over predicate.

  • predicate (callable, optional) – predicate(site) -> bool.

Returns:

A new container; a shallow copy when neither argument is given.

Return type:

AirborneSites

map(fn)[source]

Apply fn(site) -> Any to every site; collect results.

Parameters:

fn (Any)

Return type:

list[Any]

closest(lat, lon, tol=None)[source]

Nearest site to a target coordinate (great-circle distance).

Parameters:
  • lat (float) – Target location in decimal degrees.

  • lon (float) – Target location in decimal degrees.

  • tol (float, optional) – Maximum allowed distance in metres; farther than that returns None.

Returns:

None if every site lacks finite coordinates, or the nearest is farther than tol.

Return type:

AirborneSite or None

write_xml(outdir, **kwargs)[source]

Write one EMTF-XML file per site into a directory.

Parameters:
  • outdir (str or pathlib.Path) – Destination directory; created if missing.

  • **kwargs – Forwarded to AirborneSite.to_xml() for each site.

Returns:

Paths written, named "{name}.xml".

Return type:

list of pathlib.Path

pycsamt.airborne.site.ensure_asites(obj, *, recursive=True, on_dup='replace', strict=False, verbose=0)[source]

Normalize arbitrary airborne input to an AirborneSites.

The single entry-point coercion for airborne-aware emtools functions, mirroring the role ensure_sites() plays for the rest of emtools.

Parameters:
  • obj (Any) – Accepts: an existing AirborneSites; an AirborneEMDataset (AirborneSites.from_dataset()); an AirborneEMLine (AirborneSites.from_line()); a path to a single EMTF-XML file or a directory of them (AirborneSites.from_xml_dir()); or an iterable mixing any of the above with bare AirborneEMRecord/EMTF/path items (each coerced with no line context).

  • recursive (bool, default True) – Forwarded to AirborneSites.from_xml_dir() for any directory encountered.

  • on_dup ({"replace", "keep_first", "keep_last", "raise"}, default "replace") – Duplicate-name policy; see pycsamt.site.base.to_sites() for the identical semantics on the ground side.

  • strict (bool, default False) – If True, raise when nothing can be resolved (or, for a directory, when no file parses).

  • verbose (int, default 0) – >0 warns when the result is empty and strict is False.

Return type:

AirborneSites

Raises:

ValueError – If obj is None; if on_dup is invalid; or, in strict mode, if nothing could be resolved.