Skip to content

Commit 15bd8c6

Browse files
committed
Support attributes operations on tracefield
1 parent 7b36ed0 commit 15bd8c6

5 files changed

Lines changed: 72 additions & 61 deletions

File tree

python/docs/segyio.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Trace and binary header
9696
:exclude-members: count
9797

9898
.. autoclass:: segyio.trace.FieldLayoutEntry()
99+
:special-members: __getitem__
99100

100101
Tools
101102
=====

python/examples/flip-endianness.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,12 @@ def main():
3232
dst.bin = src.bin
3333
dst.trace = src.trace
3434

35-
# intention:
36-
# dst.traceheader = src.traceheader
37-
3835
# traceheader mapping was copied after open. At the moment of creation
3936
# of this example file code can't deal with that, so we must reopen the
4037
# file after bin and ext_headers are copied so that mapping is
4138
# recognized.
4239
with segyio.open(dstfile, "r+", endian="little", ignore_geometry=True) as dst:
43-
for trace_index in range(src.tracecount):
44-
for traceheader_index in range(src.traceheader_count):
45-
traceheader = src.segyfd.getth(
46-
trace_index, traceheader_index, bytearray(240))
47-
dst.segyfd.putth(
48-
trace_index, traceheader_index, traceheader)
40+
dst.traceheader = src.traceheader
4941

5042

5143
if __name__ == '__main__':

python/segyio/segy.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,16 @@ def tracefield(self):
414414
>>> f.tracefield.SEG00000.iline.offset()
415415
>>> f.tracefield[0].xline.offset()
416416
417+
Get values of field for many traces:
418+
419+
>>> f.tracefield.SEG00001.dt[0, 10, 20]
420+
421+
Note:
422+
423+
- this works similar to :meth:`.attributes`
424+
- use :meth:`.traceheader` if you need to get many fields from a single
425+
trace.
426+
417427
Notes
418428
-----
419429
.. versionadded:: 2.0
@@ -546,8 +556,7 @@ def attributes(self, field):
546556
.. versionadded:: 1.1
547557
548558
"""
549-
traceheader_layout = self._traceheader_layouts["SEG00000"]
550-
return Attributes(field, self.segyfd, traceheader_layout, self.tracecount)
559+
return Attributes(self, field, 0)
551560

552561
@property
553562
def trace(self):

python/segyio/trace.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,12 +1057,14 @@ class Attributes(Sequence):
10571057
"string8": np.dtype('S8'),
10581058
}
10591059

1060-
def __init__(self, field, segyfd, traceheader_layout, tracecount):
1061-
super(Attributes, self).__init__(tracecount)
1060+
def __init__(self, segyfile, field, traceheader_index):
1061+
super(Attributes, self).__init__(segyfile.tracecount)
10621062
self.field = field
1063-
self.segyfd = segyfd
1064-
self.tracecount = tracecount
1063+
self.traceheader_index = traceheader_index
1064+
self.segyfd = segyfile.segyfd
1065+
self.tracecount = segyfile.tracecount
10651066

1067+
traceheader_layout = list(segyfile._traceheader_layouts.values())[traceheader_index]
10661068
entry = traceheader_layout.entry_by_byte(field)
10671069
self.dtype = Attributes.ENTRY_TYPE_TO_NUMPY[entry.type]
10681070

@@ -1109,7 +1111,7 @@ def __getitem__(self, i):
11091111
xs = np.asarray(i, dtype=np.int32)
11101112
xs = xs.astype(dtype=np.int32, order='C', copy=False)
11111113
attrs = np.empty(len(xs), dtype = self.dtype)
1112-
return self.segyfd.field_foreach(attrs, 0, xs, self.field)
1114+
return self.segyfd.field_foreach(attrs, self.traceheader_index, xs, self.field)
11131115

11141116
except TypeError:
11151117
try:
@@ -1124,7 +1126,7 @@ def __getitem__(self, i):
11241126
start, stop, step = i.indices(traces)
11251127
indices = range(start, stop, step)
11261128
attrs = np.empty(len(indices), dtype = self.dtype)
1127-
return segyfd.field_forall(attrs, 0, start, stop, step, field)
1129+
return segyfd.field_forall(attrs, self.traceheader_index, start, stop, step, field)
11281130

11291131
class Text(Sequence):
11301132
"""Interact with segy in text mode
@@ -1596,3 +1598,27 @@ def __str__(self):
15961598
type = self.type()
15971599
non_zero = self.use_only_if_non_zero()
15981600
return f"{name} (offset={offset}, type={type}, use_only_if_non_zero={non_zero})"
1601+
1602+
def __getitem__(self, key):
1603+
""" tracefield[key]
1604+
1605+
Returns values of traces provided in 'key'.
1606+
Wrapper around :class:`.Attributes`, so refer to it for more
1607+
information.
1608+
1609+
Parameters
1610+
----------
1611+
key : int or slice
1612+
Index or slice.
1613+
1614+
Returns
1615+
-------
1616+
array of numbers//bytearray
1617+
1618+
Examples
1619+
--------
1620+
Get the cdp_x attribute from trace header extension 1 for first 10 traces:
1621+
1622+
>>> f.tracefield.SEG00001.cdp_x[0:10]
1623+
"""
1624+
return Attributes(self.segyfile, self.entry.byte, self.traceheader_index)[key]

python/test/segy.py

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,19 @@ def test_traces_traceheaders(small):
10791079
assert f.traceheader[2][i] == f.traceheader[f.tracecount - 3][i]
10801080

10811081

1082+
def test_traceheader_attributes(small):
1083+
with segyio.open(small, "r") as f:
1084+
attrs = f.attributes(f.tracefield.SEG00000.xline.offset())
1085+
assert len(attrs) == f.tracecount
1086+
assert np.array_equal(list(attrs), [20, 21, 22, 23, 24] * 5)
1087+
1088+
assert np.array_equal(f.tracefield.SEG00000.xline[:], attrs)
1089+
assert np.array_equal(f.tracefield.SEG00000.xline[0:5], attrs[0:5])
1090+
assert np.array_equal(f.tracefield.SEG00000.xline[0, 5, 11, 12], attrs[0, 5, 11, 12])
1091+
1092+
assert f.tracefield.SEG00000.xline[0] == 20
1093+
1094+
10821095
def test_depricated_fields(small):
10831096
with segyio.open(small, "r") as f:
10841097
assert f.bin[BinField.EnsembleTraces] == 25
@@ -2487,7 +2500,7 @@ class HeaderTest:
24872500
opened: numbers.Real
24882501
changed: numbers.Real
24892502

2490-
trace0_ext_header1_cases = [
2503+
trace0_proprietary_cases = [
24912504
HeaderTest("int2", 1, -101),
24922505
HeaderTest("int4", 2, -102),
24932506
HeaderTest("int8", 3, -103),
@@ -2512,7 +2525,7 @@ class HeaderTest:
25122525
HeaderTest("header_name", b"TYPES\x00\x00\x00", b"TYPES "),
25132526
]
25142527

2515-
trace1_ext_header1_cases = [
2528+
trace1_proprietary_cases = [
25162529
HeaderTest("int2", -1001, 10001),
25172530
HeaderTest("int4", -1002, 2000002),
25182531
HeaderTest("int8", -1003, 3000000003),
@@ -2537,65 +2550,35 @@ class HeaderTest:
25372550
]
25382551

25392552
headers = {
2540-
0: trace0_ext_header1_cases,
2541-
1: trace1_ext_header1_cases,
2553+
0: trace0_proprietary_cases,
2554+
1: trace1_proprietary_cases,
25422555
}
25432556

25442557
traceheader_index = 1
25452558

2546-
# all internal calls are temporary!
2547-
# they would be exchanged with proper ones once python interface is in place
25482559
with segyio.open(tmpdir / filename, mode='r+', endian=endianness) as f:
2549-
def mkempty():
2550-
return bytearray(240)
2551-
2552-
types_layout = f._traceheader_layouts["TYPES"]
2560+
types = f.tracefield[traceheader_index]
25532561
for trace_index, header_cases in headers.items():
25542562
for case in header_cases:
2555-
offset = types_layout.entry_by_name(case.name).byte
2563+
offset = types.__getattr__(case.name).offset()
25562564

2557-
# assert f.traceheader[trace_index][traceheader_index][offset] == case.opened
2558-
traceheader = f.segyfd.getth(
2559-
trace_index, traceheader_index, mkempty())
2560-
assert f.segyfd.getfield(
2561-
traceheader, traceheader_index, offset) == case.opened
2562-
2563-
# f.traceheader[trace_index][traceheader_index][offset] = case.changed
2564-
f.segyfd.putfield(
2565-
traceheader, traceheader_index, offset, case.changed)
2566-
f.segyfd.putth(trace_index, traceheader_index, traceheader)
2567-
2568-
# assert f.traceheader[trace_index][traceheader_index][offset] == case.changed
2569-
traceheader = f.segyfd.getth(
2570-
trace_index, traceheader_index, mkempty())
2571-
assert f.segyfd.getfield(
2572-
traceheader, traceheader_index, offset) == case.changed
2565+
assert f.traceheader[trace_index][traceheader_index][offset] == case.opened
2566+
f.traceheader[trace_index][traceheader_index][offset] = case.changed
2567+
assert f.traceheader[trace_index][traceheader_index][offset] == case.changed
25732568

25742569
with pytest.raises(ValueError, match=r"Value out of range*"):
2575-
offset = types_layout.entry_by_name("int2").byte
2576-
2577-
# f.traceheader[trace_index][traceheader_index][offset] = 50000
2578-
f.segyfd.putfield(traceheader, traceheader_index, offset, 50000)
2570+
offset = types.int2.offset()
2571+
f.traceheader[trace_index][traceheader_index][offset] = 50000
25792572

2580-
from segyio.trace import Attributes
2581-
for i, case in enumerate(trace0_ext_header1_cases):
2573+
for i, case in enumerate(trace0_proprietary_cases):
25822574
if (case.name == "scale6_mant") or (case.name == "scale6_exp"):
25832575
# scale6 is not properly supported yet
25842576
continue
25852577

2586-
if case.name == "header_name":
2587-
type = "string8"
2588-
else:
2589-
type = case.name
2590-
2591-
offset = types_layout.entry_by_name(case.name).byte
25922578
expected = np.array(
2593-
[trace0_ext_header1_cases[i].changed, trace1_ext_header1_cases[i].changed])
2594-
2595-
# attrs = f.attributes(offset, traceheader_index)
2596-
attrs = np.empty(2, dtype=Attributes.ENTRY_TYPE_TO_NUMPY[type])
2597-
f.segyfd.field_forall(attrs, traceheader_index, 0, 2, 1, offset)
2579+
[trace0_proprietary_cases[i].changed, trace1_proprietary_cases[i].changed])
25982580

2581+
attrs = types.__getattr__(case.name)[:]
25992582
np.testing.assert_array_equal(attrs, expected)
26002583

26012584

0 commit comments

Comments
 (0)