Skip to content

Commit 105fe6a

Browse files
committed
Performance enhancements for geometry calculation
1 parent 343631b commit 105fe6a

3 files changed

Lines changed: 74 additions & 6 deletions

File tree

src/mdio/segy/geometry.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -631,13 +631,11 @@ def transform(
631631
else:
632632
# Type A: shot points are already unique per gun, create 0-based index from unique values
633633
for line_val in unique_lines:
634-
line_idxs = np.where(index_headers[line_field][:] == line_val)[0]
634+
line_idxs = np.where(index_headers[line_field][:] == line_val)
635635
shot_points = index_headers["shot_point"][line_idxs]
636-
unique_shots = np.sort(np.unique(shot_points))
637-
# Map each shot_point to its 0-based index
638-
shot_to_idx = {sp: i for i, sp in enumerate(unique_shots)}
639-
for i, idx in enumerate(line_idxs):
640-
index_headers["shot_index"][idx] = shot_to_idx[shot_points[i]]
636+
# np.unique returns sorted values; searchsorted maps each shot_point to its 0-based index
637+
unique_shots = np.unique(shot_points)
638+
index_headers["shot_index"][line_idxs] = np.searchsorted(unique_shots, shot_points)
641639

642640
return index_headers
643641

tests/integration/conftest.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,36 @@ def segy_mock_obn_multiline_type_a(fake_segy_tmp: Path) -> Path:
382382
components=components,
383383
filename_suffix="multiline_type_a",
384384
)
385+
386+
387+
@pytest.fixture(scope="module")
388+
def segy_mock_obn_multiline_type_a_sparse(fake_segy_tmp: Path) -> Path:
389+
"""Generate mock OBN SEG-Y file with Type A geometry and sparse shot points.
390+
391+
Variant of segy_mock_obn_multiline_type_a using non-contiguous shot point
392+
values per gun. Exercises the vectorized Type A shot_index mapping
393+
(np.searchsorted over np.unique) on sparse values to ensure indices are
394+
assigned positionally within the sorted unique set, not by value.
395+
"""
396+
num_samples = 25
397+
receivers = [101, 102]
398+
shot_lines = [1, 2]
399+
guns = [1, 2]
400+
components = [1]
401+
402+
# Sparse, non-contiguous shot points; same set per gun keeps geometry Type A
403+
shot_points_per_gun = {
404+
1: [10, 50, 100],
405+
2: [10, 50, 100],
406+
}
407+
408+
return create_segy_mock_obn(
409+
fake_segy_tmp,
410+
num_samples=num_samples,
411+
receivers=receivers,
412+
shot_lines=shot_lines,
413+
guns=guns,
414+
shot_points_per_gun=shot_points_per_gun,
415+
components=components,
416+
filename_suffix="multiline_type_a_sparse",
417+
)

tests/integration/test_import_obn_grid_overrides.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import TYPE_CHECKING
77

88
import dask
9+
import numpy as np
910
import pytest
1011
import xarray.testing as xrt
1112
from tests.integration.conftest import get_segy_mock_obn_spec
@@ -223,3 +224,39 @@ def test_import_obn_multiline_type_a_all_lines_processed(
223224
# Verify shot_point is preserved as a coordinate
224225
assert "shot_point" in ds.coords
225226
assert ds["shot_point"].dims == ("shot_line", "gun", "shot_index")
227+
228+
def test_import_obn_multiline_type_a_sparse_shot_points(
229+
self,
230+
segy_mock_obn_multiline_type_a_sparse: Path,
231+
zarr_tmp: Path,
232+
) -> None:
233+
"""Test Type A shot_index mapping with sparse, non-contiguous shot points.
234+
235+
Guards the vectorized Type A path (np.searchsorted over np.unique) by
236+
using shot points that are not 0/1-based or contiguous. shot_index must
237+
be a dense 0-based sequence over the sorted unique shot points, and the
238+
original shot_point values must be preserved as a coordinate.
239+
"""
240+
segy_spec = get_segy_mock_obn_spec(include_component=True)
241+
grid_override = {"CalculateShotIndex": True}
242+
243+
segy_to_mdio(
244+
segy_spec=segy_spec,
245+
mdio_template=TemplateRegistry().get("ObnReceiverGathers3D"),
246+
input_path=segy_mock_obn_multiline_type_a_sparse,
247+
output_path=zarr_tmp,
248+
overwrite=True,
249+
grid_overrides=grid_override,
250+
)
251+
252+
ds = open_mdio(zarr_tmp)
253+
254+
# Sparse shot points [10, 50, 100] map to dense 0-based indices [0, 1, 2]
255+
expected_shot_index = [0, 1, 2]
256+
xrt.assert_duckarray_equal(ds["shot_index"], expected_shot_index)
257+
258+
# Original shot_point values preserved as coordinate, not remapped
259+
assert "shot_point" in ds.coords
260+
assert ds["shot_point"].dims == ("shot_line", "gun", "shot_index")
261+
unique_shot_points = np.unique(ds["shot_point"].values)
262+
xrt.assert_duckarray_equal(unique_shot_points, [10, 50, 100])

0 commit comments

Comments
 (0)