Skip to content

Commit c4a7eb9

Browse files
committed
refactor 4d tests
1 parent 73da6f0 commit c4a7eb9

3 files changed

Lines changed: 121 additions & 81 deletions

File tree

tests/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
import pytest
88

99

10+
@pytest.fixture(scope="session")
11+
def fake_segy_tmp(tmp_path_factory):
12+
"""Make a temp file for the fake SEG-Y files we are going to create."""
13+
tmp_dir = tmp_path_factory.mktemp(r"fake_segy")
14+
return tmp_dir
15+
16+
1017
@pytest.fixture(scope="session")
1118
def segy_input(tmp_path_factory):
1219
"""Download teapot dome dataset for testing."""

tests/integration/conftest.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""Test configuration before everything runs."""
2+
import os
3+
4+
import numpy as np
5+
import pytest
6+
import segyio
7+
8+
9+
def create_segy_mock_4d(
10+
fake_segy_tmp: str,
11+
num_samples: int,
12+
shots: list,
13+
cables: list,
14+
receivers_per_cable: list,
15+
chan_header_type: str = "a",
16+
) -> str:
17+
"""Dummy 4D SEG-Y file for use in tests."""
18+
spec = segyio.spec()
19+
segy_file = os.path.join(fake_segy_tmp, f"4d_type_{chan_header_type}.sgy")
20+
21+
shot_count = len(shots)
22+
total_chan = np.sum(receivers_per_cable)
23+
trace_count = shot_count * total_chan
24+
25+
spec.format = 1
26+
spec.samples = range(num_samples)
27+
spec.tracecount = trace_count
28+
spec.endian = "big"
29+
30+
# Calculate shot, cable, channel/receiver numbers and header values
31+
cable_headers = []
32+
channel_headers = []
33+
34+
# TODO: Add strict=True and remove noqa when minimum Python is 3.10
35+
for cable, num_rec in zip(cables, receivers_per_cable): # noqa: B905
36+
cable_headers.append(np.repeat(cable, num_rec))
37+
38+
channel_headers.append(np.arange(num_rec) + 1)
39+
40+
cable_headers = np.hstack(cable_headers)
41+
channel_headers = np.hstack(channel_headers)
42+
43+
if chan_header_type == "b":
44+
channel_headers = np.arange(total_chan) + 1
45+
46+
shot_headers = np.hstack([np.repeat(shot, total_chan) for shot in shots])
47+
cable_headers = np.tile(cable_headers, shot_count)
48+
channel_headers = np.tile(channel_headers, shot_count)
49+
50+
with segyio.create(segy_file, spec) as f:
51+
for trc_idx in range(trace_count):
52+
shot = shot_headers[trc_idx]
53+
cable = cable_headers[trc_idx]
54+
channel = channel_headers[trc_idx]
55+
56+
# offset is byte location 37 - offset 4 bytes
57+
# fldr is byte location 9 - shot 4 byte
58+
# ep is byte location 17 - shot 4 byte
59+
# stae is byte location 137 - cable 2 byte
60+
# tracf is byte location 13 - channel 4 byte
61+
62+
f.header[trc_idx].update(
63+
offset=0,
64+
fldr=shot,
65+
ep=shot,
66+
stae=cable,
67+
tracf=channel,
68+
)
69+
70+
samples = np.linspace(start=shot, stop=shot + 1, num=num_samples)
71+
f.trace[trc_idx] = samples.astype("float32")
72+
73+
f.bin.update()
74+
75+
return segy_file
76+
77+
78+
@pytest.fixture(scope="module")
79+
def segy_mock_4d_shots(fake_segy_tmp: str) -> dict[str, str]:
80+
"""Generate mock 4D shot SEG-Y files."""
81+
num_samples = 25
82+
shots = [2, 3, 5]
83+
cables = [0, 101, 201, 301]
84+
receivers_per_cable = [1, 5, 7, 5]
85+
86+
segy_paths = {}
87+
88+
for type_ in ["a", "b"]:
89+
segy_paths[type_] = create_segy_mock_4d(
90+
fake_segy_tmp,
91+
num_samples=num_samples,
92+
shots=shots,
93+
cables=cables,
94+
receivers_per_cable=receivers_per_cable,
95+
chan_header_type=type_,
96+
)
97+
98+
return segy_paths

tests/integration/test_segy_import_export.py

Lines changed: 16 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
"""End to end testing for SEG-Y to MDIO conversion and back."""
22

3-
import os
43
from os.path import getsize
5-
from typing import List
64

75
import dask
86
import numpy as np
@@ -19,66 +17,6 @@
1917
dask.config.set(scheduler="synchronous")
2018

2119

22-
def create_4d_segy(
23-
file_path: str,
24-
num_samples: int,
25-
fldrs: List,
26-
cables: List,
27-
num_traces: List,
28-
chan_header_type: str = "a",
29-
):
30-
"""Dummy 4D segy file for use in tests."""
31-
import segyio
32-
33-
spec = segyio.spec()
34-
d = os.path.join(file_path, "data")
35-
os.makedirs(d, exist_ok=True)
36-
segy_file = os.path.join(d, f"4d_type_{chan_header_type}.sgy")
37-
spec.format = 1
38-
spec.samples = range(num_samples)
39-
40-
trace_count = len(fldrs) * np.sum(num_traces)
41-
spec.tracecount = trace_count
42-
spec.endian = "big"
43-
44-
with segyio.create(segy_file, spec) as f:
45-
trno = 0
46-
47-
tracf = 0
48-
for fldr in fldrs:
49-
if chan_header_type == "b":
50-
tracf = 1
51-
# TODO: Add strict=True and remove noqa when min supported Python is 3.10
52-
for cable, length in zip(cables, num_traces): # noqa: B905
53-
if chan_header_type == "a":
54-
tracf = 1
55-
for _i in range(length):
56-
# segyio names and byte locations for headers can be found at:
57-
# https://segyio.readthedocs.io/en/latest/segyio.html
58-
# fldr is byte location 9 - shot 4 byte
59-
# ep is byte location 17 - shot 4 byte
60-
# stae is byte location 137 - cable 2 byte
61-
# tracf is byte location 13 - channel 4 byte
62-
63-
f.header[trno].update(
64-
offset=1,
65-
fldr=fldr,
66-
ep=fldr,
67-
stae=cable,
68-
tracf=tracf,
69-
)
70-
71-
trace = np.linspace(
72-
start=fldr, stop=fldr + 1, num=len(spec.samples)
73-
)
74-
f.trace[trno] = trace
75-
trno += 1
76-
tracf += 1
77-
78-
f.bin.update()
79-
return segy_file
80-
81-
8220
@pytest.mark.parametrize("header_locations", [(17, 137, 13)])
8321
@pytest.mark.parametrize("header_names", [("shot", "cable", "channel")])
8422
@pytest.mark.parametrize("header_types", [("int32", "int16", "int32")])
@@ -92,7 +30,7 @@ class TestImport4D:
9230

9331
def test_import_4d_segy(
9432
self,
95-
tmp_path,
33+
segy_mock_4d_shots,
9634
zarr_tmp,
9735
header_locations,
9836
header_names,
@@ -102,18 +40,7 @@ def test_import_4d_segy(
10240
chan_header_type,
10341
):
10442
"""Test importing a SEG-Y file to MDIO."""
105-
num_samples = 25
106-
fldrs = [2, 3, 5]
107-
cables = [0, 101, 201, 301]
108-
num_traces = [1, 5, 7, 5]
109-
segy_path = create_4d_segy(
110-
tmp_path,
111-
num_samples=num_samples,
112-
fldrs=fldrs,
113-
cables=cables,
114-
num_traces=num_traces,
115-
chan_header_type=chan_header_type,
116-
)
43+
segy_path = segy_mock_4d_shots[chan_header_type]
11744

11845
segy_to_mdio(
11946
segy_path=segy_path,
@@ -127,25 +54,33 @@ def test_import_4d_segy(
12754
grid_overrides=grid_overrides,
12855
)
12956

57+
# Expected values
58+
num_samples = 25
59+
shots = [2, 3, 5]
60+
cables = [0, 101, 201, 301]
61+
receivers_per_cable = [1, 5, 7, 5]
62+
13063
# QC mdio output
13164
mdio = MDIOReader(zarr_tmp.__str__(), access_pattern="0123")
13265
assert mdio.binary_header["Samples"] == num_samples
13366
grid = mdio.grid
13467

135-
assert grid.select_dim(header_names[0]) == Dimension(fldrs, header_names[0])
68+
assert grid.select_dim(header_names[0]) == Dimension(shots, header_names[0])
13669
assert grid.select_dim(header_names[1]) == Dimension(cables, header_names[1])
13770

13871
if "b" in chan_header_type and grid_overrides is None:
72+
print()
73+
print(grid.select_dim(header_names[2]))
13974
assert grid.select_dim(header_names[2]) == Dimension(
140-
range(1, np.sum(num_traces) + 1), header_names[2]
75+
range(1, np.sum(receivers_per_cable) + 1), header_names[2]
14176
)
14277
else:
14378
assert grid.select_dim(header_names[2]) == Dimension(
144-
range(1, np.amax(num_traces) + 1), header_names[2]
79+
range(1, np.amax(receivers_per_cable) + 1), header_names[2]
14580
)
146-
assert grid.select_dim("sample") == Dimension(
147-
range(0, num_samples, 1), "sample"
148-
)
81+
82+
samples_exp = Dimension(range(0, num_samples, 1), "sample")
83+
assert grid.select_dim("sample") == samples_exp
14984

15085

15186
@pytest.mark.parametrize("header_locations", [(17, 13)])

0 commit comments

Comments
 (0)