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 itsEMTFdocument:.tipper,.freq, and, unlikeSite,.admittance(for MobileMT) alongside the typed metadata surfaceSitealready exposes (.site_meta,.processing,.provenance, …), sinceAirborneEMRecord.emtfis anEMTFdocument too.AirborneSites– an ordered collection, mirroringSites’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 realSitesfeatures this module does not need for theemtoolsintegration it exists to unblock, so they are left out rather than stubbed.ensure_asites()– theensure_sitescounterpart: the single entry point every future airborne-awareemtoolsfunction 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
|
Normalize arbitrary airborne input to an |
Classes
|
One airborne flight-line sample, read directly from its EMTF. |
|
Ordered collection of |
- class pycsamt.airborne.site.AirborneSite(record, *, line_id=None, technology=None, coords=None)[source]
Bases:
CoreObjectOne airborne flight-line sample, read directly from its EMTF.
Unlike
Site, every numeric accessor here reads straight from the wrappedAirborneEMRecord’sEMTFdocument – 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.
Nonewhen 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’sEMTF.subtypewhen not given explicitly; seetechnology.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’sNavigationTrack. When omitted,coordsfalls back to the record’s ownEMTF.site.location, then to(nan, nan, nan).
- Raises:
TypeError – If record is not an
AirborneEMRecord.
See also
AirborneSitesOrdered collection of these.
pycsamt.site.base.SiteThe 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:
source (pycsamt.emtf.document.EMTF or str or pathlib.Path) – Parsed document, or a path read via
from_xml().line_id (str, optional) – Forwarded to the constructor.
technology (str, optional) – Forwarded to the constructor.
- Return type:
Notes
The sample identifier is resolved from
document.site.site_id, thendocument.station, then the source file’s stem, in that order.
- property record: AirborneEMRecord[source]
The wrapped
AirborneEMRecord.
- property sample_id: str[source]
Navigation sample identifier (stable; independent of any richer
nameresolved from metadata).
- property technology: str | None[source]
Technology label (
"ztem","mobilemt", …).Explicit at construction, else the record’s
EMTF.subtype, elseNone.
- property name: str[source]
Station identifier resolved from metadata, else
sample_id.Resolution order:
EMTF.site.site_id,EMTF.station,sample_id.
- 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 z: ndarray | None[source]
Impedance array, shape
(nf, 2, 2), orNoneif absent (the normal case for ZTEM/AFMAG/MobileMT).
- property admittance: ndarray | None[source]
MobileMT admittance array, shape
(nf, 3, 2).Nonefor any record without an attachedmobilemt_admittancetransfer function – there is no analogue of this accessor onSite, since a 3x2 admittance cannot be represented bySite.z’s 2x2 shape.
- property interstation_tensor: ndarray | None[source]
Tensor AFMAG/AirMt interstation magnetic TF, shape
(nf, 3, 2).Nonefor any record without an attachedinterstation_transfer_functionstransfer function. Deliberately a separate accessor fromadmittance(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. Seepycsamt.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,).Nonefor any record without an attachedafmag_tilttransfer function. Unlikeinterstation_tensoror 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 (seepycsamt.airborne.afmag’s module docstring).
- property afmag_amplification_parameter: ndarray | None[source]
AirMt rotation-invariant amplification parameter, shape
(nf,).Nonefor any record without an attachedairmt_amplification_parametertransfer function; seepycsamt.airborne.afmag.compute_airmt_amplification_parameter()for the formula that derives it frominterstation_tensor.
- property fields: dict[str, Any][source]
Auxiliary decoded fields (technology-defined).
For MobileMT, this is where a vendor-delivered native
apparent_conductivityvector lives when present; seepycsamt.airborne.mobilemt.MOBILEMT_APPARENT_CONDUCTIVITY_FIELD.
- 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 againstz.- Return type:
- 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; orZxx, Zxy, Zyx, Zyy.- Return type:
- 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:
- 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
EMTFdocument.
- class pycsamt.airborne.site.AirborneSites(items)[source]
Bases:
CoreObjectOrdered collection of
AirborneSiteobjects.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 viaAirborneSite.from_xml()-style construction with no line context; preferfrom_line()/from_dataset()when that context is available.- Raises:
TypeError – If an item cannot be coerced to
AirborneSite.
See also
ensure_asitesFlexible entry-point coercion, including directories of EMTF-XML files and duplicate-name policy.
pycsamt.site.base.SitesThe 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:
- 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 toline.attrs["technology"]when not given.
- Return type:
- 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; seefrom_line()for the per-line ordering.technology (str, optional) – Forwarded to
from_line()for every line; falls back todataset.attrs["technology"]when not given.
- Return type:
- Raises:
TypeError – If dataset is not an
AirborneEMDataset.
- get(name)[source]
Safe lookup by case-insensitive name;
Noneif absent.- Parameters:
name (str)
- Return type:
AirborneSite | None
- as_list()[source]
The underlying list of
AirborneSiteobjects.- Return type:
- property technologies: tuple[str, ...][source]
Sorted, deduplicated
AirborneSite.technologyvalues present in this container.
- property line_ids: tuple[str, ...][source]
Sorted, deduplicated
AirborneSite.line_idvalues 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:
- map(fn)[source]
Apply
fn(site) -> Anyto every site; collect results.
- closest(lat, lon, tol=None)[source]
Nearest site to a target coordinate (great-circle distance).
- Parameters:
- Returns:
Noneif 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
emtoolsfunctions, mirroring the roleensure_sites()plays for the rest ofemtools.- Parameters:
obj (Any) – Accepts: an existing
AirborneSites; anAirborneEMDataset(AirborneSites.from_dataset()); anAirborneEMLine(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 bareAirborneEMRecord/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) –
>0warns when the result is empty and strict isFalse.
- Return type:
- Raises:
ValueError – If obj is
None; if on_dup is invalid; or, in strict mode, if nothing could be resolved.