pycsamt.format.multiline#
Multiline PCSF builder/reader — Phase 5 of the PCSF format plan.
Formalizes what pycsamt/app/web/callbacks/map3d.py currently
reconstructs at render time from a stack of independent 2-D sections:
_line_real_offsets/_assemble_3d_grid derive real-vs-synthetic
line offsets and resample each line onto a common (x, z) grid, purely
in memory, from whatever cached InversionResult or pseudo-section
data the current session happens to hold. Nothing is ever persisted.
This module is the inverse pair:
build_multiline_pcsf()turns a profiles dict — the same{line_name: {"x", "z", "rho", "sta_x", "sta_names", ...}}shapemap3d.py’s own_profiles_from_pseudo/_profiles_from_inversion_resultalready produce — into aPCSFModelwith amultilinegeometry, one real (unresampled)LineEntryper line.multiline_pcsf_to_profiles()reconstructs that same profiles dict shape from a loaded file, so it is a drop-in alternate data source formap3d.py’s existing renderers (_build_fence_fig/_build_block_fig/_assemble_3d_grid) — no rendering code needs to change to consume a persisted file.line_offsets_from_stations()andstack_lines_to_common_grid()are the same real-offset / common-grid-resampling algorithmsmap3d.pyalready implements privately as_line_real_offsets/_assemble_3d_grid, promoted here so both the live rendering path and the persisted-file path are provably the same computation, not two copies that can drift.
Functions
|
Build a |
|
Cross-strike offset (m) for each line, from real station lat/lon. |
|
Reconstruct a profiles dict from a |
|
Resample every line onto the first line's own (x, z) grid. |
- pycsamt.format.multiline.line_offsets_from_stations(profiles)[source]
Cross-strike offset (m) for each line, from real station lat/lon.
Same algorithm as
map3d.py’s private_line_real_offsets(built onpycsamt.map.geometry.survey_uv()), so both the live-cache rendering path and this persisted-file path place lines identically. Requires every line to carrysta_lat/sta_lon/sta_namesof equal length; returnsNoneotherwise so callers fall back to a synthetic index-based stack viaresolve_offset().
- pycsamt.format.multiline.stack_lines_to_common_grid(profiles, *, line_spacing=1.0, fallback_unit=1000.0)[source]
Resample every line onto the first line’s own (x, z) grid.
Same algorithm as
map3d.py’s private_assemble_3d_grid: the first profile’s grid is the reference; other lines are resampled onto it via_resample_line_to_grid().- Returns:
x_arr, z_arr (ndarray) – The reference line’s own coordinates.
y_arr (ndarray, shape (n_lines,)) – Per-line cross-strike offset, from
line_offsets_from_stations()when available, otherwise a syntheticidx * spacing * fallback_unitstack (seepycsamt.map.geometry.resolve_offset()).rho_3d (ndarray, shape (n_lines, n_x, n_z))
- Parameters:
- Return type:
- pycsamt.format.multiline.build_multiline_pcsf(profiles, *, line_spacing=1.0, fallback_unit=1000.0, cache_derived_volume=True, topo=None, epsg=None, utm_zone=None, latlon=False, on_mismatch='raise', survey=None, source_backend='generic', created_by='', crs=None, description='')[source]
Build a
multilinePCSFModelfrom a profiles dict.- Parameters:
profiles (mapping of str to mapping) –
{line_name: {"x": (n_x,), "z": (n_z,), "rho": (n_z, n_x), ...}}, the exact shapemap3d.py’s_profiles_from_pseudo/_profiles_from_inversion_resultalready produce.rhomust already be linear ohm.m (call_rho_log_to_ohm_m-equivalent conversion first, matching every other PCSF adapter’s canonical-linear convention). Optional per-line keyssta_x,sta_names,sta_elev,sta_lat,sta_lonpopulatePCSFModel.stations/PCSFModel.topographywhen present.sta_lat/sta_londo double duty:line_offsets_from_stations()uses them (viapycsamt.map.geometry.survey_uv()) to compute each line’s real cross-strike offset when every line carries them, falling back to a synthetic index-based stack otherwise (seeoffset_kindbelow) – and the same values are also persisted intoPCSFModel.stations’lon/lat, so a loaded multiline file is self-sufficiently geo-referenced too, not just correctly spaced.line_spacing (float) – Forwarded to
stack_lines_to_common_grid()for the optional cachedDerivedVolume(real per-line geometry itself never depends on these — only the synthetic offset fallback does).fallback_unit (float) – Forwarded to
stack_lines_to_common_grid()for the optional cachedDerivedVolume(real per-line geometry itself never depends on these — only the synthetic offset fallback does).cache_derived_volume (bool, default True) – When
Trueand there are at least two lines, also cache aDerivedVolume(each line resampled onto a common grid) so a large multiline file does not need to re-resample on every render. SetFalseto keep the file smaller when that convenience volume is not needed.topo (optional) – A “smart” real-coordinate source resolved via
pycsamt.format.topo_source.resolve_topo()– seepycsamt.format.adapters.occam2d.occam2d_to_pcsf()’s identical parameter for the full description of accepted source types. Populates each line’s ownsta_lat/sta_lonbefore the real-offset computation above runs, so passing topo is enough to get both a real cross-strike offset per line and a self-georeferenced file – no separate offset step is needed. Accepts either a single source matched by station name across every line combined (a .stn/.csv/Sites source covering the whole survey), or a{line_name: source}mapping / one-source-per-line sequence (inprofiles’s own key order) for e.g. one.blnfile per surveyed line. Takes precedence over anysta_lat/sta_lonalready present in profiles for every station it resolves (with aUserWarningif both were supplied). If topo only partially covers a line’s stations, the resultingnanentries makeline_offsets_from_stations()fall back to a synthetic offset for every line (mixing a real and a synthetic stack would look inconsistent – see that function’s own all-or-nothing behaviour) rather than silently using a partially-real one; the stations themselves still keep whatever real lon/lat topo did resolve.None(the default) leaves this function’s behaviour exactly as it was before topo existed.epsg (int | None) – Forwarded to
pycsamt.format.topo_source.resolve_topo(); see occam2d_to_pcsf’s identical parameters.utm_zone (Any | None) – Forwarded to
pycsamt.format.topo_source.resolve_topo(); see occam2d_to_pcsf’s identical parameters.latlon (bool) – Forwarded to
pycsamt.format.topo_source.resolve_topo(); see occam2d_to_pcsf’s identical parameters.on_mismatch (str) – Forwarded to
pycsamt.format.topo_source.resolve_topo(); see occam2d_to_pcsf’s identical parameters.survey (Any | Mapping[str, Any] | None) – Passed straight through to
PCSFModel.source_backend (str) – Passed straight through to
PCSFModel.created_by (str) – Passed straight through to
PCSFModel.crs (str | None) – Passed straight through to
PCSFModel.description (str) – Passed straight through to
PCSFModel.
- Raises:
ValueError – If profiles is empty, or any line is missing
x/z/rho.- Return type:
Examples
>>> import numpy as np >>> from pycsamt.format.multiline import build_multiline_pcsf >>> profiles = { ... "L1": {"x": np.array([0.0, 100.0]), "z": np.array([10.0, 50.0]), ... "rho": np.array([[100.0, 110.0], [50.0, 55.0]])}, ... "L2": {"x": np.array([0.0, 100.0]), "z": np.array([10.0, 50.0]), ... "rho": np.array([[200.0, 210.0], [90.0, 95.0]])}, ... } >>> model = build_multiline_pcsf(profiles, cache_derived_volume=False) >>> model.kind 'multiline' >>> [line.line_id for line in model.geometry.lines] ['L1', 'L2']
- pycsamt.format.multiline.multiline_pcsf_to_profiles(model)[source]
Reconstruct a profiles dict from a
multilinePCSFModel.The exact inverse of
build_multiline_pcsf()’s per-line conversion — the result is a drop-in alternate data source formap3d.py’s existing_build_fence_fig/_assemble_3d_grid/_build_block_fig, which only need the{"x", "z", "rho", ...}shape, not any particular origin.- Parameters:
model (PCSFModel) – Must have
geometry.kind == "multiline".- Returns:
{line_id: {"x", "z", "rho", "sta_x", "sta_names", "sta_elev", "sta_lat", "sta_lon"}}. Station keys are populated fromPCSFModel.stationswhen present (matched to each line viaStationTable.line_id);sta_lat/sta_loncome fromStationTable.lat/.lonwhen the file has them (seebuild_multiline_pcsf()), else stay empty lists – an older file written before those fields existed round-trips the same way it always did.- Return type:
- Raises:
ValueError – If model is not a
multilinegeometry.