segyio 2.0.0a1__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
segyio/trace.py ADDED
@@ -0,0 +1,1624 @@
1
+ try:
2
+ from collections.abc import Sequence # noqa
3
+ except ImportError:
4
+ from collections import Sequence # noqa
5
+
6
+ import contextlib
7
+ import itertools
8
+ import warnings
9
+ import sys
10
+ try: from future_builtins import zip
11
+ except ImportError: pass
12
+
13
+ import numpy as np
14
+
15
+ from .line import HeaderLine
16
+ from .field import Field, HeaderFieldAccessor
17
+ from .utils import castarray
18
+
19
+ class Sequence(Sequence):
20
+
21
+ # unify the common optimisations and boilerplate of Trace, RawTrace, and
22
+ # Header, which all obey the same index-oriented interface, and all share
23
+ # length and wrap-around properties.
24
+ #
25
+ # It provides a useful negative-wrap index method which deals
26
+ # appropriately with IndexError and python2-3 differences.
27
+
28
+ def __init__(self, length):
29
+ self.length = length
30
+
31
+ def __len__(self):
32
+ """x.__len__() <==> len(x)"""
33
+ return self.length
34
+
35
+ def __iter__(self):
36
+ """x.__iter__() <==> iter(x)"""
37
+ # __iter__ has a reasonable default implementation from Sequence. It's
38
+ # essentially this loop:
39
+ # for i in range(len(self)): yield self[i]
40
+ # However, in segyio that means the double-buffering, buffer reuse does
41
+ # not happen, which is *much* slower (the allocation of otherwised
42
+ # reused numpy objects takes about half the execution time), so
43
+ # explicitly implement it as [:]
44
+ return self[:]
45
+
46
+ def wrapindex(self, i):
47
+ if i < 0:
48
+ i += len(self)
49
+
50
+ if not 0 <= i < len(self):
51
+ # in python2, int-slice comparison does not raise a type error,
52
+ # (but returns False), so force a type-error if this still isn't an
53
+ # int-like.
54
+ _ = i + 0
55
+ raise IndexError('trace index out of range')
56
+
57
+ return i
58
+
59
+ class Trace(Sequence):
60
+ """
61
+ The Trace implements the array interface, where every array element, the
62
+ data trace, is a numpy.ndarray. As all arrays, it can be random accessed,
63
+ iterated over, and read strided. Data is read lazily from disk, so
64
+ iteration does not consume much memory. If you want eager reading, use
65
+ Trace.raw.
66
+
67
+ This mode gives access to reading and writing functionality for traces.
68
+ The primary data type is ``numpy.ndarray``. Traces can be accessed
69
+ individually or with python slices, and writing is done via assignment.
70
+
71
+ Notes
72
+ -----
73
+ .. versionadded:: 1.1
74
+
75
+ .. versionchanged:: 1.6
76
+ common list operations (Sequence)
77
+
78
+ Examples
79
+ --------
80
+ Read all traces in file f and store in a list:
81
+
82
+ >>> l = [numpy.copy(tr) for tr in trace[:]]
83
+
84
+ Do numpy operations on a trace:
85
+
86
+ >>> tr = trace[10]
87
+ >>> tr = tr * 2
88
+ >>> tr = tr - 100
89
+ >>> avg = numpy.average(tr)
90
+
91
+ Perform some seismic processing on a trace. E.g resample from 2ms spacing
92
+ to 4ms spacing (note there is no anti-alias filtering in this example):
93
+
94
+ >>> tr = scipy.signal.resample(tr, len(tr)/2)
95
+
96
+ Double every trace value and write to disk. Since accessing a trace
97
+ gives a numpy value, to write to the respective trace we need its index:
98
+
99
+ >>> for i, tr in enumerate(trace):
100
+ ... tr = tr * 2
101
+ ... trace[i] = tr
102
+
103
+ """
104
+
105
+ def __init__(self, segyfd, dtype, tracecount, samples, readonly):
106
+ super(Trace, self).__init__(tracecount)
107
+ self.segyfd = segyfd
108
+ self.dtype = dtype
109
+ self.shape = samples
110
+ self.readonly = readonly
111
+
112
+ def __getitem__(self, i):
113
+ """trace[i] or trace[i, j]
114
+
115
+ ith trace of the file, starting at 0. trace[i] returns a numpy array,
116
+ and changes to this array will *not* be reflected on disk.
117
+
118
+ When i is a tuple, the second index j (int or slice) is the depth index
119
+ or interval, respectively. j starts at 0.
120
+
121
+ When i is a slice, a generator of numpy arrays is returned.
122
+
123
+ Parameters
124
+ ----------
125
+ i : int or slice
126
+ j : int or slice
127
+
128
+ Returns
129
+ -------
130
+ trace : numpy.ndarray of dtype or generator of numpy.ndarray of dtype
131
+
132
+ Notes
133
+ -----
134
+ .. versionadded:: 1.1
135
+
136
+ Behaves like [] for lists.
137
+
138
+ .. note::
139
+
140
+ This operator reads lazily from the file, meaning the file is read
141
+ on ``next()``, and only one trace is fixed in memory. This means
142
+ segyio can run through arbitrarily large files without consuming
143
+ much memory, but it is potentially slow if the goal is to read the
144
+ entire file into memory. If that is the case, consider using
145
+ `trace.raw`, which reads eagerly.
146
+
147
+ Examples
148
+ --------
149
+ Read every other trace:
150
+
151
+ >>> for tr in trace[::2]:
152
+ ... print(tr)
153
+
154
+ Read all traces, last-to-first:
155
+
156
+ >>> for tr in trace[::-1]:
157
+ ... tr.mean()
158
+
159
+ Read a single value. The second [] is regular numpy array indexing, and
160
+ supports all numpy operations, including negative indexing and slicing:
161
+
162
+ >>> trace[0][0]
163
+ 1490.2
164
+ >>> trace[0][1]
165
+ 1490.8
166
+ >>> trace[0][-1]
167
+ 1871.3
168
+ >>> trace[-1][100]
169
+ 1562.0
170
+
171
+ Read only an interval in a trace:
172
+ >>> trace[0, 5:10]
173
+ """
174
+
175
+ try:
176
+ # optimize for the default case when i is a single trace index
177
+ i = self.wrapindex(i)
178
+ buf = np.zeros(self.shape, dtype = self.dtype)
179
+ return self.segyfd.gettr(buf, i, 1, 1, 0, self.shape, 1, self.shape)
180
+ except TypeError:
181
+ pass
182
+
183
+ try:
184
+ i, j = i
185
+ except TypeError:
186
+ # index is not a tuple. Set j to be a slice that causes the entire
187
+ # trace to be loaded.
188
+ j = slice(0, self.shape, 1)
189
+
190
+ single = False
191
+ try:
192
+ start, stop, step = j.indices(self.shape)
193
+ except AttributeError:
194
+ # j is not a slice, set start stop and step so that a single sample
195
+ # at position j is loaded.
196
+ start = int(j) % self.shape
197
+ stop = start + 1
198
+ step = 1
199
+ single = True
200
+
201
+ n_elements = len(range(start, stop, step))
202
+
203
+ try:
204
+ i = self.wrapindex(i)
205
+ buf = np.zeros(n_elements, dtype = self.dtype)
206
+ tr = self.segyfd.gettr(buf, i, 1, 1, start, stop, step, n_elements)
207
+ return tr[0] if single else tr
208
+ except TypeError:
209
+ pass
210
+
211
+ try:
212
+ indices = i.indices(len(self))
213
+ def gen():
214
+ # double-buffer the trace. when iterating over a range, we want
215
+ # to make sure the visible change happens as late as possible,
216
+ # and that in the case of exception the last valid trace was
217
+ # untouched. this allows for some fancy control flow, and more
218
+ # importantly helps debugging because you can fully inspect and
219
+ # interact with the last good value.
220
+ x = np.zeros(n_elements, dtype=self.dtype)
221
+ y = np.zeros(n_elements, dtype=self.dtype)
222
+
223
+ for k in range(*indices):
224
+ self.segyfd.gettr(x, k, 1, 1, start, stop, step, n_elements)
225
+ x, y = y, x
226
+ yield y
227
+
228
+ return gen()
229
+ except AttributeError:
230
+ # At this point we have tried to unpack index as a single int, a
231
+ # slice and a pair with either element being an int or slice.
232
+ msg = 'trace indices must be integers or slices, not {}'
233
+ raise TypeError(msg.format(type(i).__name__))
234
+
235
+
236
+ def __setitem__(self, i, val):
237
+ """trace[i] = val
238
+
239
+ Write the ith trace of the file, starting at 0. It accepts any
240
+ array_like, but val must be at least as big as the underlying data
241
+ trace.
242
+
243
+ If val is longer than the underlying trace, it is essentially
244
+ truncated.
245
+
246
+ For the best performance, val should be a numpy.ndarray of sufficient
247
+ size and same dtype as the file. segyio will warn on mismatched types,
248
+ and attempt a conversion for you.
249
+
250
+ Data is written immediately to disk. If writing multiple traces at
251
+ once, and a write fails partway through, the resulting file is left in
252
+ an unspecified state.
253
+
254
+ Parameters
255
+ ----------
256
+ i : int or slice
257
+ val : array_like
258
+
259
+ Notes
260
+ -----
261
+ .. versionadded:: 1.1
262
+
263
+ Behaves like [] for lists.
264
+
265
+ Examples
266
+ --------
267
+ Write a single trace:
268
+
269
+ >>> trace[10] = list(range(1000))
270
+
271
+ Write multiple traces:
272
+
273
+ >>> trace[10:15] = np.array([cube[i] for i in range(5)])
274
+
275
+ Write multiple traces with stride:
276
+
277
+ >>> trace[10:20:2] = np.array([cube[i] for i in range(5)])
278
+
279
+ """
280
+ if isinstance(i, slice):
281
+ for j, x in zip(range(*i.indices(len(self))), val):
282
+ self[j] = x
283
+
284
+ return
285
+
286
+ xs = castarray(val, self.dtype)
287
+
288
+ # TODO: check if len(xs) > shape, and optionally warn on truncating
289
+ # writes
290
+ self.segyfd.puttr(self.wrapindex(i), xs)
291
+
292
+ def __repr__(self):
293
+ return "Trace(traces = {}, samples = {})".format(len(self), self.shape)
294
+
295
+ @property
296
+ def raw(self):
297
+ """
298
+ An eager version of Trace
299
+
300
+ Returns
301
+ -------
302
+ raw : RawTrace
303
+ """
304
+ return RawTrace(self.segyfd,
305
+ self.dtype,
306
+ len(self),
307
+ self.shape,
308
+ self.readonly,
309
+ )
310
+
311
+ @property
312
+ @contextlib.contextmanager
313
+ def ref(self):
314
+ """
315
+ A write-back version of Trace
316
+
317
+ Returns
318
+ -------
319
+ ref : RefTrace
320
+ `ref` is returned in a context manager, and must be in a ``with``
321
+ statement
322
+
323
+ Notes
324
+ -----
325
+ .. versionadded:: 1.6
326
+
327
+ Examples
328
+ --------
329
+ >>> with trace.ref as ref:
330
+ ... ref[10] += 1.617
331
+ """
332
+
333
+ x = RefTrace(self.segyfd,
334
+ self.dtype,
335
+ len(self),
336
+ self.shape,
337
+ self.readonly,
338
+ )
339
+ yield x
340
+ x.flush()
341
+
342
+ class RawTrace(Trace):
343
+ """
344
+ Behaves exactly like trace, except reads are done eagerly and returned as
345
+ numpy.ndarray, instead of generators of numpy.ndarray.
346
+ """
347
+ def __init__(self, *args):
348
+ super(RawTrace, self).__init__(*args)
349
+
350
+ def __getitem__(self, i):
351
+ """trace[i]
352
+
353
+ Eagerly read the ith trace of the file, starting at 0. trace[i] returns
354
+ a numpy array, and changes to this array will *not* be reflected on
355
+ disk.
356
+
357
+ When i is a slice, this returns a 2-dimensional numpy.ndarray .
358
+
359
+ Parameters
360
+ ----------
361
+ i : int or slice
362
+
363
+ Returns
364
+ -------
365
+ trace : numpy.ndarray of dtype
366
+
367
+ Notes
368
+ -----
369
+ .. versionadded:: 1.1
370
+
371
+ Behaves like [] for lists.
372
+
373
+ .. note::
374
+
375
+ Reading this way is more efficient if you know you can afford the
376
+ extra memory usage. It reads the requested traces immediately to
377
+ memory.
378
+
379
+ """
380
+ try:
381
+ i = self.wrapindex(i)
382
+ buf = np.zeros(self.shape, dtype = self.dtype)
383
+ return self.segyfd.gettr(buf, i, 1, 1, 0, self.shape, 1, self.shape)
384
+ except TypeError:
385
+ try:
386
+ indices = i.indices(len(self))
387
+ except AttributeError:
388
+ msg = 'trace indices must be integers or slices, not {}'
389
+ raise TypeError(msg.format(type(i).__name__))
390
+ start, _, step = indices
391
+ length = len(range(*indices))
392
+ buf = np.empty((length, self.shape), dtype = self.dtype)
393
+ return self.segyfd.gettr(buf, start, step, length, 0, self.shape, 1, self.shape)
394
+
395
+
396
+ def fingerprint(x):
397
+ return hash(bytes(x.data))
398
+
399
+ class RefTrace(Trace):
400
+ """
401
+ Behaves like trace, except changes to the returned numpy arrays *are*
402
+ reflected on disk. Operations have to be in-place on the numpy array, so
403
+ assignment on a trace will not work.
404
+
405
+ This feature exists to support code like::
406
+
407
+ >>> with ref as r:
408
+ ... for x, y in zip(r, src):
409
+ ... numpy.copyto(x, y + 10)
410
+
411
+ This class is not meant to be instantiated directly, but returned by
412
+ :attr:`Trace.ref`. This feature requires a context manager, to guarantee
413
+ modifications are written back to disk.
414
+ """
415
+ def __init__(self, *args):
416
+ super(RefTrace, self).__init__(*args)
417
+ self.refs = {}
418
+
419
+ def flush(self):
420
+ """
421
+ Commit cached writes to the file handle. Does not flush libc buffers or
422
+ notifies the kernel, so these changes may not immediately be visible to
423
+ other processes.
424
+
425
+ Updates the fingerprints whena writes happen, so successive ``flush()``
426
+ invocations are no-ops.
427
+
428
+ It is not necessary to call this method in user code.
429
+
430
+ Notes
431
+ -----
432
+ .. versionadded:: 1.6
433
+
434
+ This method is not intended as user-oriented functionality, but might
435
+ be useful in certain contexts to provide stronger guarantees.
436
+ """
437
+ garbage = []
438
+ # If there are no external references to the data (so only internal
439
+ # references remain), the reference count is
440
+ # - 2 (Python >= 3.14)
441
+ # - 3 (Python < 3.14)
442
+ garbage_threshold = 3 if sys.version_info < (3, 14) else 2
443
+
444
+ for i, (x, signature) in self.refs.items():
445
+ if sys.getrefcount(x) == garbage_threshold:
446
+ garbage.append(i)
447
+
448
+ if fingerprint(x) == signature: continue
449
+
450
+ self.segyfd.puttr(i, x)
451
+ signature = fingerprint(x)
452
+
453
+
454
+ # to avoid too many resource leaks, when this dict is the only one
455
+ # holding references to already-produced traces, clear them
456
+ for i in garbage:
457
+ del self.refs[i]
458
+
459
+ def fetch(self, i, buf = None):
460
+ if buf is None:
461
+ buf = np.zeros(self.shape, dtype = self.dtype)
462
+
463
+ try:
464
+ self.segyfd.gettr(buf, i, 1, 1, 0, self.shape, 1, self.shape)
465
+ except IOError:
466
+ if not self.readonly:
467
+ # if the file is opened read-only and this happens, there's no
468
+ # way to actually write and the error is an actual error
469
+ buf.fill(0)
470
+ else: raise
471
+
472
+ return buf
473
+
474
+ def __getitem__(self, i):
475
+ """trace[i]
476
+
477
+ Read the ith trace of the file, starting at 0. trace[i] returns a numpy
478
+ array, but unlike Trace, changes to this array *will* be reflected on
479
+ disk. The modifications must happen to the actual array (views are ok),
480
+ so in-place operations work, but assignments will not::
481
+
482
+ >>> with ref as ref:
483
+ ... x = ref[10]
484
+ ... x += 1.617 # in-place, works
485
+ ... numpy.copyto(x, x + 10) # works
486
+ ... x = x + 10 # re-assignment, won't change the original x
487
+
488
+ Works on newly created files that has yet to have any traces written,
489
+ which opens up a natural way of filling newly created files with data.
490
+ When getting unwritten traces, a trace filled with zeros is returned.
491
+
492
+ Parameters
493
+ ----------
494
+ i : int or slice
495
+
496
+ Returns
497
+ -------
498
+ trace : numpy.ndarray of dtype
499
+
500
+ Notes
501
+ -----
502
+ .. versionadded:: 1.6
503
+
504
+ Behaves like [] for lists.
505
+
506
+ Examples
507
+ --------
508
+ Merge two files with a binary operation. Relies on python3 iterator
509
+ zip:
510
+
511
+ >>> with ref as ref:
512
+ ... for x, lhs, rhs in zip(ref, L, R):
513
+ ... numpy.copyto(x, lhs + rhs)
514
+
515
+ Create a file and fill with data (the repeated trace index):
516
+
517
+ >>> f = create()
518
+ >>> with f.trace.ref as ref:
519
+ ... for i, x in enumerate(ref):
520
+ ... x.fill(i)
521
+ """
522
+ try:
523
+ i = self.wrapindex(i)
524
+
525
+ # we know this class is only used in context managers, so we know
526
+ # refs don't escape (with expectation of being written), so
527
+ # preserve all refs yielded with getitem(int)
528
+ #
529
+ # using ref[int] is problematic and pointless, we need to handle
530
+ # this scenario gracefully:
531
+ # with f.trace.ref as ref:
532
+ # x = ref[10]
533
+ # x[5] = 0
534
+ # # invalidate other refs
535
+ # y = ref[11]
536
+ # y[6] = 1.6721
537
+ #
538
+ # # if we don't preserve returned individual getitems, this
539
+ # # write is lost
540
+ # x[5] = 52
541
+ #
542
+ # for slices, we know that references terminate with every
543
+ # iteration anyway, multiple live references cannot happen
544
+
545
+ if i in self.refs:
546
+ return self.refs[i][0]
547
+
548
+ x = self.fetch(i)
549
+ self.refs[i] = (x, fingerprint(x))
550
+ return x
551
+
552
+ except TypeError:
553
+ try:
554
+ indices = i.indices(len(self))
555
+ except AttributeError:
556
+ msg = 'trace indices must be integers or slices, not {}'
557
+ raise TypeError(msg.format(type(i).__name__))
558
+
559
+ def gen():
560
+ x = np.zeros(self.shape, dtype = self.dtype)
561
+ try:
562
+ for j in range(*indices):
563
+ x = self.fetch(j, x)
564
+ y = fingerprint(x)
565
+
566
+ yield x
567
+
568
+ if not fingerprint(x) == y:
569
+ self.segyfd.puttr(j, x)
570
+
571
+ finally:
572
+ # the last yielded item is available after the loop, so
573
+ # preserve it and check if it's been updated on exit
574
+ self.refs[j] = (x, y)
575
+
576
+ return gen()
577
+
578
+ class Header(Sequence):
579
+ """Interact with segy in header mode
580
+
581
+ This mode gives access to reading and writing functionality of headers,
582
+ both in individual (trace) mode and line mode. The returned header
583
+ implements a dict_like object with a fixed set of keys, given by the SEG-Y
584
+ standard.
585
+
586
+ The Header implements the array interface, where every array element, the
587
+ data trace, is a numpy.ndarray. As all arrays, it can be random accessed,
588
+ iterated over, and read strided. Data is read lazily from disk, so
589
+ iteration does not consume much memory.
590
+
591
+ Notes
592
+ -----
593
+ .. versionadded:: 1.1
594
+
595
+ .. versionchanged:: 1.6
596
+ common list operations (Sequence)
597
+
598
+ """
599
+ def __init__(self, segyfile):
600
+ self.segyfile = segyfile
601
+ super(Header, self).__init__(segyfile.tracecount)
602
+
603
+ def __getitem__(self, i):
604
+ """header[i]
605
+
606
+ ith header of the file, starting at 0.
607
+
608
+ Parameters
609
+ ----------
610
+ i : int or slice
611
+
612
+ Returns
613
+ -------
614
+ field : Field
615
+ dict_like header
616
+
617
+ Notes
618
+ -----
619
+ .. versionadded:: 1.1
620
+
621
+ Behaves like [] for lists.
622
+
623
+ Examples
624
+ --------
625
+ Reading a header:
626
+
627
+ >>> header[10]
628
+
629
+ Read a field in the first 5 headers:
630
+
631
+ >>> [x[25] for x in header[:5]]
632
+ [1, 2, 3, 4, 5]
633
+
634
+ Read a field in every other header:
635
+
636
+ >>> [x[37] for x in header[::2]]
637
+ [1, 3, 1, 3, 1, 3]
638
+ """
639
+ try:
640
+ i = self.wrapindex(i)
641
+ return Field.trace(traceno = i, traceheader_index = 0, segyfile = self.segyfile)
642
+
643
+ except TypeError:
644
+ try:
645
+ indices = i.indices(len(self))
646
+ except AttributeError:
647
+ msg = 'trace indices must be integers or slices, not {}'
648
+ raise TypeError(msg.format(type(i).__name__))
649
+
650
+ def gen():
651
+ # double-buffer the header. when iterating over a range, we
652
+ # want to make sure the visible change happens as late as
653
+ # possible, and that in the case of exception the last valid
654
+ # header was untouched. this allows for some fancy control
655
+ # flow, and more importantly helps debugging because you can
656
+ # fully inspect and interact with the last good value.
657
+ x = Field.trace(traceno = None, traceheader_index = 0, segyfile = self.segyfile)
658
+ buf = bytearray(x.buf)
659
+ for j in range(*indices):
660
+ # skip re-invoking __getitem__, just update the buffer
661
+ # directly with fetch, and save some initialisation work
662
+ buf = x.fetch(buf, j)
663
+ x.buf[:] = buf
664
+ x.traceno = j
665
+ yield x
666
+
667
+ return gen()
668
+
669
+ def __setitem__(self, i, val):
670
+ """header[i] = val
671
+
672
+ Write the ith header of the file, starting at 0. Unlike data traces
673
+ (which return numpy.ndarrays), changes to returned headers being
674
+ iterated over *will* be reflected on disk.
675
+
676
+ Parameters
677
+ ----------
678
+ i : int or slice
679
+ val : Field or array_like of dict_like
680
+
681
+ Notes
682
+ -----
683
+ .. versionadded:: 1.1
684
+
685
+ Behaves like [] for lists
686
+
687
+ Examples
688
+ --------
689
+ Copy a header to a different trace:
690
+
691
+ >>> header[28] = header[29]
692
+
693
+ Write multiple fields in a trace:
694
+
695
+ >>> header[10] = { 37: 5, TraceField.INLINE_3D: 2484 }
696
+
697
+ Set a fixed set of values in all headers:
698
+
699
+ >>> for x in header[:]:
700
+ ... x[37] = 1
701
+ ... x.update({ TraceField.offset: 1, 2484: 10 })
702
+
703
+ Write a field in multiple headers
704
+
705
+ >>> for x in header[:10]:
706
+ ... x.update({ TraceField.offset : 2 })
707
+
708
+ Write a field in every other header:
709
+
710
+ >>> for x in header[::2]:
711
+ ... x.update({ TraceField.offset : 2 })
712
+ """
713
+
714
+ x = self[i]
715
+
716
+ try:
717
+ x.update(val)
718
+ except AttributeError:
719
+ if isinstance(val, Field) or isinstance(val, dict):
720
+ val = itertools.repeat(val)
721
+
722
+ for h, v in zip(x, val):
723
+ h.update(v)
724
+
725
+ @property
726
+ def iline(self):
727
+ """
728
+ Headers, accessed by inline
729
+
730
+ Returns
731
+ -------
732
+ line : HeaderLine
733
+ """
734
+ return HeaderLine(self, self.segyfile.iline, 'inline')
735
+
736
+ @iline.setter
737
+ def iline(self, value):
738
+ """Write iterables to lines
739
+
740
+ Examples:
741
+ Supports writing to *all* crosslines via assignment, regardless of
742
+ data source and format. Will respect the sample size and structure
743
+ of the file being assigned to, so if the argument traces are longer
744
+ than that of the file being written to the surplus data will be
745
+ ignored. Uses same rules for writing as `f.iline[i] = x`.
746
+ """
747
+ for i, src in zip(self.segyfile.ilines, value):
748
+ self.iline[i] = src
749
+
750
+ @property
751
+ def xline(self):
752
+ """
753
+ Headers, accessed by crossline
754
+
755
+ Returns
756
+ -------
757
+ line : HeaderLine
758
+ """
759
+ return HeaderLine(self, self.segyfile.xline, 'crossline')
760
+
761
+ @xline.setter
762
+ def xline(self, value):
763
+ """Write iterables to lines
764
+
765
+ Examples:
766
+ Supports writing to *all* crosslines via assignment, regardless of
767
+ data source and format. Will respect the sample size and structure
768
+ of the file being assigned to, so if the argument traces are longer
769
+ than that of the file being written to the surplus data will be
770
+ ignored. Uses same rules for writing as `f.xline[i] = x`.
771
+ """
772
+
773
+ for i, src in zip(self.segyfile.xlines, value):
774
+ self.xline[i] = src
775
+
776
+
777
+ class FileFieldAccessor(Sequence):
778
+ """
779
+ Sequence of rows of trace headers in a SEG-Y file.
780
+
781
+ Provides access to all trace headers defined in the SEG-Y file.
782
+
783
+ Notes
784
+ -----
785
+ .. versionadded:: 2.0
786
+
787
+ """
788
+
789
+ def __init__(self, segyfile):
790
+ self.segyfile = segyfile
791
+ super().__init__(segyfile.tracecount)
792
+
793
+ def __getitem__(self, i):
794
+ """traceheader[i]
795
+
796
+ All headers belonging to ith trace, starting at 0.
797
+
798
+ Parameters
799
+ ----------
800
+ i : int or slice
801
+
802
+ Returns
803
+ -------
804
+ traceheaders : RowFieldAccessor
805
+
806
+ Examples
807
+ --------
808
+ Accessing headers in trace 10 (11th in the file):
809
+
810
+ >>> traceheader[10]
811
+ """
812
+ try:
813
+ trace_index = self.wrapindex(i)
814
+ return RowFieldAccessor(segyfile=self.segyfile, trace_index=trace_index)
815
+
816
+ except TypeError:
817
+ try:
818
+ trace_indices = i.indices(len(self))
819
+ except AttributeError:
820
+ msg = 'trace indices must be integers or slices, not {}'
821
+ raise TypeError(msg.format(type(i).__name__))
822
+
823
+ def gen():
824
+ for trace_index in range(*trace_indices):
825
+ yield RowFieldAccessor(segyfile=self.segyfile, trace_index=trace_index)
826
+
827
+ return gen()
828
+
829
+ def __setitem__(self, i, val):
830
+ """traceheader[i] = val
831
+
832
+ Write the ith header of the file, starting at 0.
833
+
834
+ Parameters
835
+ ----------
836
+ i : int or slice
837
+ val : another FileFieldAccessor or array like
838
+
839
+ Examples
840
+ --------
841
+ Copy all trace's headers to a different trace:
842
+
843
+ >>> traceheader[28] = traceheader[29]
844
+ """
845
+ if not isinstance(i, slice):
846
+ trace_index = self.wrapindex(i)
847
+ if not isinstance(val, RowFieldAccessor):
848
+ raise TypeError(
849
+ "Unassignable type. f.traceheader[i] can only be assigned f.traceheader[j]")
850
+ self[trace_index][:] = val
851
+ return
852
+
853
+ trace_indices = range(*i.indices(len(self)))
854
+ for trace_index, value in zip(trace_indices, val):
855
+ self[trace_index] = value
856
+
857
+
858
+ class RowFieldAccessor(Sequence):
859
+ """
860
+ Sequence of trace headers in a single trace (row).
861
+
862
+ Provides access to all trace headers defined in the trace.
863
+
864
+ Notes
865
+ -----
866
+ .. versionadded:: 2.0
867
+
868
+ """
869
+
870
+ def __init__(self, segyfile, trace_index):
871
+ self.segyfile = segyfile
872
+ self.trace_index = trace_index
873
+ super().__init__(segyfile.traceheader_count)
874
+
875
+ def __getitem__(self, i):
876
+ """traceheader[i]
877
+
878
+ ith header of the trace, starting at 0.
879
+
880
+ Note that this function loads trace header into memory, so if you wish
881
+ to access multiple fields, caching the result of this function is more
882
+ efficient than calling it anew with each of the fields.
883
+
884
+ Parameters
885
+ ----------
886
+ i : int or slice
887
+
888
+ Returns
889
+ -------
890
+ field : Field
891
+ dict_like header
892
+
893
+ Examples
894
+ --------
895
+ Reading a header:
896
+
897
+ >>> traceheader[10]
898
+
899
+ Read a field in the first 5 headers:
900
+
901
+ >>> [x[25] for x in traceheader[:5]]
902
+ [1, 2, 3, 4, 5]
903
+ """
904
+ try:
905
+ return HeaderFieldAccessor.trace(
906
+ traceno=self.trace_index, traceheader_index=self.wrapindex(i), segyfile=self.segyfile
907
+ )
908
+
909
+ except TypeError:
910
+ try:
911
+ traceheader_indices = i.indices(len(self))
912
+ except AttributeError:
913
+ msg = 'trace header indices must be integers or slices, not {}'
914
+ raise TypeError(msg.format(type(i).__name__))
915
+
916
+ def gen():
917
+ # see Header.__getitem__ for logic explanation
918
+ x = HeaderFieldAccessor.trace(
919
+ traceno=self.trace_index, traceheader_index=None, segyfile=self.segyfile
920
+ )
921
+ buf = bytearray(x.buf)
922
+ for traceheader_index in range(*traceheader_indices):
923
+ buf = x.fetch(buf, self.trace_index, traceheader_index)
924
+ x.buf[:] = buf
925
+ x.traceheader_index = traceheader_index
926
+ traceheader_layout = x.segyfile.tracefield[traceheader_index]
927
+ x._keys = [field.offset() for field in traceheader_layout]
928
+ yield x
929
+
930
+ return gen()
931
+
932
+ def __getattr__(self, name):
933
+ """traceheaders.name
934
+
935
+ Header of the trace by name `name`. `name` must be a name defined by
936
+ the file layouts.
937
+
938
+ Wrapper around :meth:`.__getitem__`, so refer to it for more
939
+ information.
940
+
941
+ Parameters
942
+ ----------
943
+ name : str
944
+
945
+ Returns
946
+ -------
947
+ field : Field
948
+ dict_like header
949
+
950
+ Examples
951
+ --------
952
+ Reading a header:
953
+
954
+ >>> traceheaders.SEG00001
955
+ """
956
+ index = self.segyfile._traceheader_names.index(name)
957
+ return self[index]
958
+
959
+ def __setitem__(self, i, val):
960
+ """traceheader[i] = val
961
+
962
+ Write the ith header of the trace, starting at 0.
963
+
964
+ Parameters
965
+ ----------
966
+ i : int or slice
967
+ val : Field or array_like of dict_like
968
+
969
+ Examples
970
+ --------
971
+ Copy standard header to a different trace:
972
+
973
+ >>> f.traceheader[0][0] = f.traceheader[1][0]
974
+
975
+ Writing fields via `traceheader[trace_index][traceheader_index] =
976
+ {(offset: value)}` is not supported. Use :class:`segyio.field.Field`
977
+ interface instead:
978
+
979
+ >>> f.traceheader[0][0].update({ 37: 5, 1: 2484 })
980
+
981
+ """
982
+ if not isinstance(i, slice):
983
+ traceheader_index = self.wrapindex(i)
984
+ field_sequence = self[traceheader_index]
985
+ if isinstance(val, Field):
986
+ field_sequence.update(val)
987
+ return
988
+ raise TypeError(
989
+ "Unassignable type. f.traceheader[i][j] can only be assigned another Field.")
990
+
991
+ traceheader_indices = range(*i.indices(len(self)))
992
+
993
+ for traceheader_index, value in zip(traceheader_indices, val):
994
+ self[traceheader_index] = value
995
+
996
+ def __setattr__(self, name, value):
997
+ """traceheaders.name = value
998
+
999
+ Write header of the trace by name `name`. `name` must be a name defined by
1000
+ the file layouts.
1001
+
1002
+ Wrapper around :meth:`.__setitem__`, so refer to it for more
1003
+ information.
1004
+
1005
+ Parameters
1006
+ ----------
1007
+ name : str
1008
+
1009
+ Examples
1010
+ --------
1011
+ Copying a header:
1012
+
1013
+ >>> f.traceheader[0].SEG00001 = f.traceheader[1].SEG00001
1014
+ """
1015
+ if name == "segyfile":
1016
+ super().__setattr__(name, value)
1017
+ return
1018
+
1019
+ try:
1020
+ index = self.segyfile._traceheader_names.index(name)
1021
+ self[index] = value
1022
+ except ValueError:
1023
+ super().__setattr__(name, value)
1024
+
1025
+
1026
+ class Attributes(Sequence):
1027
+ """File-wide attribute (header word) reading
1028
+
1029
+ Lazily read a single header word for every trace in the file. The
1030
+ Attributes implement the array interface, and will behave as expected when
1031
+ indexed and sliced.
1032
+
1033
+ Notes
1034
+ -----
1035
+ .. versionadded:: 1.1
1036
+ """
1037
+
1038
+ ENTRY_TYPE_TO_NUMPY = {
1039
+ "int2": np.int16,
1040
+ "int4": np.int32,
1041
+ "int8": np.int64,
1042
+ "uint2": np.uint16,
1043
+ "uint4": np.uint32,
1044
+ "uint8": np.uint64,
1045
+ "ibmfp": np.float32,
1046
+ "ieee32": np.float32,
1047
+ "ieee64": np.float64,
1048
+ "linetrc": np.uint32,
1049
+ "reeltrc": np.uint32,
1050
+ "linetrc8": np.uint64,
1051
+ "reeltrc8": np.uint64,
1052
+ "coor4": np.int32,
1053
+ "elev4": np.int32,
1054
+ "time2": np.int16,
1055
+ "spnum4": np.int32,
1056
+ # "scale6" unspported, unclear what to do yet
1057
+ "string8": np.dtype('S8'),
1058
+ }
1059
+
1060
+ def __init__(self, segyfile, field, traceheader_index):
1061
+ super(Attributes, self).__init__(segyfile.tracecount)
1062
+ self.field = field
1063
+ self.traceheader_index = traceheader_index
1064
+ self.segyfd = segyfile.segyfd
1065
+ self.tracecount = segyfile.tracecount
1066
+
1067
+ traceheader_layout = list(segyfile._traceheader_layouts.values())[traceheader_index]
1068
+ entry = traceheader_layout.entry_by_byte(field)
1069
+ self.dtype = Attributes.ENTRY_TYPE_TO_NUMPY[entry.type]
1070
+
1071
+ def __iter__(self):
1072
+ # attributes requires a custom iter, because self[:] returns a numpy
1073
+ # array, which in itself is iterable, but not an iterator
1074
+ return iter(self[:])
1075
+
1076
+ def __getitem__(self, i):
1077
+ """attributes[:]
1078
+
1079
+ Parameters
1080
+ ----------
1081
+ i : int or slice or array_like
1082
+
1083
+ Returns
1084
+ -------
1085
+ attributes : array_like of dtype
1086
+
1087
+ Examples
1088
+ --------
1089
+ Assuming file with default layout mapping, read all unique sweep
1090
+ frequency end:
1091
+
1092
+ >>> end = segyio.TraceField.SweepFrequencyEnd
1093
+ >>> sfe = np.unique(f.attributes( end )[:])
1094
+
1095
+ Discover the first traces of each unique sweep frequency end:
1096
+
1097
+ >>> end = segyio.TraceField.SweepFrequencyEnd
1098
+ >>> attrs = f.attributes(end)
1099
+ >>> sfe, tracenos = np.unique(attrs[:], return_index = True)
1100
+
1101
+ Scatter plot group x/y-coordinates with SFEs (using matplotlib):
1102
+
1103
+ >>> end = segyio.TraceField.SweepFrequencyEnd
1104
+ >>> attrs = f.attributes(end)
1105
+ >>> _, tracenos = np.unique(attrs[:], return_index = True)
1106
+ >>> gx = f.attributes(segyio.TraceField.GroupX)[tracenos]
1107
+ >>> gy = f.attributes(segyio.TraceField.GroupY)[tracenos]
1108
+ >>> scatter(gx, gy)
1109
+ """
1110
+ try:
1111
+ xs = np.asarray(i, dtype=np.int32)
1112
+ xs = xs.astype(dtype=np.int32, order='C', copy=False)
1113
+ attrs = np.empty(len(xs), dtype = self.dtype)
1114
+ return self.segyfd.field_foreach(attrs, self.traceheader_index, xs, self.field)
1115
+
1116
+ except TypeError:
1117
+ try:
1118
+ i = slice(i, i + 1, 1)
1119
+ except TypeError:
1120
+ pass
1121
+
1122
+ traces = self.tracecount
1123
+ segyfd = self.segyfd
1124
+ field = self.field
1125
+
1126
+ start, stop, step = i.indices(traces)
1127
+ indices = range(start, stop, step)
1128
+ attrs = np.empty(len(indices), dtype = self.dtype)
1129
+ return segyfd.field_forall(attrs, self.traceheader_index, start, stop, step, field)
1130
+
1131
+ class Text(Sequence):
1132
+ """Interact with segy in text mode
1133
+
1134
+ This mode gives access to reading and writing functionality for textual
1135
+ headers.
1136
+
1137
+ The primary data type is the python string. Reading textual headers is done
1138
+ with [], and writing is done via assignment. No additional structure is
1139
+ built around the textual header, so everything is treated as one long
1140
+ string without line breaks.
1141
+
1142
+ Notes
1143
+ -----
1144
+ .. versionchanged:: 1.7
1145
+ common list operations (Sequence)
1146
+
1147
+ """
1148
+
1149
+ def __init__(self, segyfd, textcount):
1150
+ super(Text, self).__init__(textcount)
1151
+ self.segyfd = segyfd
1152
+
1153
+ def __getitem__(self, i):
1154
+ """text[i]
1155
+
1156
+ Read the text header at i. 0 is the mandatory, main
1157
+
1158
+ Examples
1159
+ --------
1160
+ Print the textual header:
1161
+
1162
+ >>> print(f.text[0])
1163
+
1164
+ Print the first extended textual header:
1165
+
1166
+ >>> print(f.text[1])
1167
+
1168
+ Print a textual header line-by-line:
1169
+
1170
+ >>> # using zip, from the zip documentation
1171
+ >>> text = f.text[0].decode('ascii', errors='replace')
1172
+ >>> lines = map(''.join, zip( *[iter(text)] * 80))
1173
+ >>> for line in lines:
1174
+ ... print(line)
1175
+ ...
1176
+
1177
+ Or use segyio.tools.wrap:
1178
+ >>> text = segyio.tools.wrap(f.text[0].decode('latin-1'))
1179
+ """
1180
+ try:
1181
+ i = self.wrapindex(i)
1182
+ return self.segyfd.gettext(i)
1183
+
1184
+ except TypeError:
1185
+ try:
1186
+ indices = i.indices(len(self))
1187
+ except AttributeError:
1188
+ msg = 'trace indices must be integers or slices, not {}'
1189
+ raise TypeError(msg.format(type(i).__name__))
1190
+
1191
+ def gen():
1192
+ for j in range(*indices):
1193
+ yield self.segyfd.gettext(j)
1194
+ return gen()
1195
+
1196
+ def __setitem__(self, i, val):
1197
+ """text[i] = val
1198
+
1199
+ Write the ith text header of the file, starting at 0.
1200
+ If val is instance of Text or iterable of Text,
1201
+ value is set to be the first element of every Text
1202
+
1203
+ Parameters
1204
+ ----------
1205
+ i : int or slice
1206
+ val : str, Text or iterable if i is slice
1207
+
1208
+ Examples
1209
+ --------
1210
+ Write a new textual header:
1211
+
1212
+ >>> f.text[0] = make_new_header()
1213
+ >>> f.text[1:3] = ["new_header1", "new_header_2"]
1214
+
1215
+ Copy a textual header:
1216
+
1217
+ >>> f.text[1] = g.text[0]
1218
+
1219
+ Write a textual header based on Text:
1220
+
1221
+ >>> f.text[1] = g.text
1222
+ >>> assert f.text[1] == g.text[0]
1223
+
1224
+ >>> f.text[1:3] = [g1.text, g2.text]
1225
+ >>> assert f.text[1] == g1.text[0]
1226
+ >>> assert f.text[2] == g2.text[0]
1227
+
1228
+ """
1229
+ if isinstance(val, Text):
1230
+ self[i] = val[0]
1231
+ return
1232
+
1233
+ try:
1234
+ i = self.wrapindex(i)
1235
+ self.segyfd.puttext(i, val)
1236
+
1237
+ except TypeError:
1238
+ try:
1239
+ indices = i.indices(len(self))
1240
+ except AttributeError:
1241
+ msg = 'trace indices must be integers or slices, not {}'
1242
+ raise TypeError(msg.format(type(i).__name__))
1243
+
1244
+ for i, text in zip(range(*indices), val):
1245
+ if isinstance(text, Text):
1246
+ text = text[0]
1247
+ self.segyfd.puttext(i, text)
1248
+
1249
+
1250
+ class Stanza(Sequence):
1251
+ """Access stanza data from extended textual headers.
1252
+
1253
+ Reading stanzas is done with []. Keys are stanza indices, values are raw
1254
+ stanza data as bytes. Stanza name (stanza header) is not included in the
1255
+ return bytes.
1256
+
1257
+ Writing stanzas is not supported.
1258
+
1259
+ Notes
1260
+ -----
1261
+ .. versionadded:: 2.0
1262
+
1263
+ Examples
1264
+ --------
1265
+ Get all stanza names:
1266
+
1267
+ >>> names = f.stanza.names()
1268
+ ... ['OGP:P1/11:text/csv', 'SEG:catalog', 'TROIKA:IMAGEPNG:image/png:51929']
1269
+
1270
+ Access stanza data:
1271
+
1272
+ >>> data = f.stanza[2]
1273
+ """
1274
+
1275
+ def __init__(self, segyfd):
1276
+ self.segyfd = segyfd
1277
+ self._names = self.segyfd.stanza_names()
1278
+ super(Stanza, self).__init__(len(self._names))
1279
+
1280
+ def names(self):
1281
+ """Get all stanza names. Names are the same as they appear in the file.
1282
+
1283
+ Returns
1284
+ -------
1285
+ names : list of str
1286
+ List of all stanza names in the file in order of appearance
1287
+ """
1288
+ return self._names
1289
+
1290
+ def __getitem__(self, key):
1291
+ """Get stanza data by index.
1292
+
1293
+ Parameters
1294
+ ----------
1295
+ key : int or slice
1296
+ Stanza index or indices
1297
+
1298
+ Returns
1299
+ -------
1300
+ data : bytes
1301
+ Stanza data for single stanza, or generator for slice
1302
+
1303
+ Examples
1304
+ --------
1305
+ Access by index:
1306
+
1307
+ >>> data = f.stanza[0]
1308
+
1309
+ Find stanza that fits name pattern:
1310
+
1311
+ >>> for i, name in enumerate(f.stanza.names()):
1312
+ >>> if name.upper().startswith("SEG:LAYOUT"):
1313
+ >>> layout_stanza_index = i
1314
+ >>> data = f.stanza[layout_stanza_index]
1315
+ """
1316
+ try:
1317
+ index = self.wrapindex(key)
1318
+ return self.segyfd.getstanza(index)
1319
+ except TypeError:
1320
+ try:
1321
+ indices = key.indices(len(self))
1322
+ except AttributeError:
1323
+ msg = 'stanza indices must be integers, strings, or slices, not {}'
1324
+ raise TypeError(msg.format(type(key).__name__))
1325
+
1326
+ def gen():
1327
+ for j in range(*indices):
1328
+ yield self.segyfd.getstanza(j)
1329
+ return gen()
1330
+
1331
+
1332
+ class RowLayoutEntries(Sequence):
1333
+ """
1334
+ Sequence of trace header layouts in one trace (row).
1335
+
1336
+ Provides access to all trace header layouts defined in the SEG-Y file.
1337
+
1338
+ Notes
1339
+ -----
1340
+ .. versionadded:: 2.0
1341
+ """
1342
+
1343
+ def __init__(self, segyfile):
1344
+ self.segyfile = segyfile
1345
+ self._layouts = segyfile._traceheader_layouts
1346
+ self._layout_names = list(self._layouts.keys())
1347
+
1348
+ super().__init__(segyfile.traceheader_count)
1349
+
1350
+ def __getitem__(self, key):
1351
+ """
1352
+ Get a trace header fields layout by index or slice.
1353
+
1354
+ Parameters
1355
+ ----------
1356
+ key : int or slice
1357
+ Index or range.
1358
+
1359
+ Returns
1360
+ -------
1361
+ HeaderLayoutEntries or generator of HeaderLayoutEntries
1362
+
1363
+ Examples
1364
+ --------
1365
+ Get the layout via [] notation:
1366
+
1367
+ >>> layout = traceheader_layouts[0]
1368
+ """
1369
+ try:
1370
+ traceheader_index = self.wrapindex(key)
1371
+ name = self._layout_names[traceheader_index]
1372
+ layout = self._layouts[name]
1373
+ return HeaderLayoutEntries(self.segyfile, traceheader_index, layout)
1374
+ except TypeError:
1375
+ indices = key.indices(len(self))
1376
+
1377
+ def gen():
1378
+ for traceheader_index in range(*indices):
1379
+ name = self._layout_names[traceheader_index]
1380
+ layout = self._layouts[name]
1381
+ yield HeaderLayoutEntries(self.segyfile, traceheader_index, layout)
1382
+ return gen()
1383
+
1384
+ def __getattr__(self, name):
1385
+ """
1386
+ Get trace header fields layout as an attribute.
1387
+
1388
+ Parameters
1389
+ ----------
1390
+ name : str
1391
+ Name of the layout. Name is case-sensitive.
1392
+
1393
+ Returns
1394
+ -------
1395
+ HeaderLayoutEntries
1396
+
1397
+ Examples
1398
+ --------
1399
+ Get the field layout via . notation:
1400
+
1401
+ >>> layout = traceheader_layouts.SEG00000
1402
+
1403
+ Raises
1404
+ ------
1405
+ AttributeError
1406
+ If the layout does not exist.
1407
+ """
1408
+ if name in self._layouts:
1409
+ traceheader_index = self._layout_names.index(name)
1410
+ return HeaderLayoutEntries(self.segyfile, traceheader_index, self._layouts[name])
1411
+ raise AttributeError(f"No header with name: {name}")
1412
+
1413
+ def names(self):
1414
+ """
1415
+ List all trace header names.
1416
+
1417
+ Returns
1418
+ -------
1419
+ list of str
1420
+ Names of all traceheaders.
1421
+ """
1422
+ return self._layout_names
1423
+
1424
+ def __repr__(self):
1425
+ return f"RowLayoutEntries({self._layout_names})"
1426
+
1427
+
1428
+ class HeaderLayoutEntries(Sequence):
1429
+ """
1430
+ Sequence of field layout entries in a trace header.
1431
+
1432
+ Provides access to the individual fields (entries) within a trace header
1433
+ layout.
1434
+
1435
+ Notes
1436
+ -----
1437
+ .. versionadded:: 2.0
1438
+ """
1439
+
1440
+ def __init__(self, segyfile, traceheader_index, layout):
1441
+ self.segyfile = segyfile
1442
+ self._layout = layout
1443
+ self.traceheader_index = traceheader_index
1444
+
1445
+ super().__init__(len(layout))
1446
+
1447
+ def __getitem__(self, key):
1448
+ """
1449
+ Get field entries by index or slice.
1450
+
1451
+ Parameters
1452
+ ----------
1453
+ key : int or slice
1454
+ Index or range.
1455
+
1456
+ Returns
1457
+ -------
1458
+ FieldLayoutEntry or generator of FieldLayoutEntry
1459
+
1460
+ Examples
1461
+ --------
1462
+ Get the entry via [] notation:
1463
+
1464
+ >>> entry = layout[3]
1465
+ """
1466
+ try:
1467
+ entry_index = self.wrapindex(key)
1468
+ entry = self._layout.entries[entry_index]
1469
+ return FieldLayoutEntry(self.segyfile, entry, self.traceheader_index)
1470
+
1471
+ except TypeError:
1472
+ indices = key.indices(len(self))
1473
+
1474
+ def gen():
1475
+ for entry_index in range(*indices):
1476
+ entry = self._layout.entries[entry_index]
1477
+ yield FieldLayoutEntry(self.segyfile, entry, self.traceheader_index)
1478
+ return gen()
1479
+
1480
+ def __getattr__(self, name):
1481
+ """
1482
+ Get field entry as an attribute.
1483
+
1484
+ Parameters
1485
+ ----------
1486
+ name : str
1487
+ Name of the field. Name is case-sensitive.
1488
+
1489
+ Returns
1490
+ -------
1491
+ FieldLayoutEntry
1492
+
1493
+ Examples
1494
+ --------
1495
+ Get the entry via . notation:
1496
+
1497
+ >>> entry = layout.iline
1498
+
1499
+ Raises
1500
+ ------
1501
+ AttributeError
1502
+ If the field by that name does not exist.
1503
+ """
1504
+ entry = self._layout.entry_by_name(name)
1505
+ if entry is None:
1506
+ raise AttributeError(f"No field with name: {name}")
1507
+ return FieldLayoutEntry(self.segyfile, entry, self.traceheader_index)
1508
+
1509
+ def names(self):
1510
+ """
1511
+ List all field names in this traceheader layout.
1512
+
1513
+ Returns
1514
+ -------
1515
+ list of str
1516
+ Names of all fields.
1517
+ """
1518
+ return [entry.name for entry in self._layout]
1519
+
1520
+ def index(self):
1521
+ """
1522
+ Get traceheader index of this layout.
1523
+
1524
+ Returns
1525
+ -------
1526
+ int
1527
+ Layout index.
1528
+ """
1529
+ return self.traceheader_index
1530
+
1531
+ def __repr__(self):
1532
+ return f"HeaderLayoutEntries({self.names()})"
1533
+
1534
+
1535
+ class FieldLayoutEntry():
1536
+ """
1537
+ Represents a single field entry in a trace header layout.
1538
+
1539
+ Notes
1540
+ -----
1541
+ .. versionadded:: 2.0
1542
+ """
1543
+
1544
+ def __init__(self, segyfile, entry, traceheader_index):
1545
+ self.segyfile = segyfile
1546
+ self.entry = entry
1547
+ self.traceheader_index = traceheader_index
1548
+
1549
+ def name(self):
1550
+ """
1551
+ Get the name of the field as is defined in the layout.
1552
+
1553
+ Returns
1554
+ -------
1555
+ str
1556
+ Name of the field.
1557
+ """
1558
+ return self.entry.name
1559
+
1560
+ def offset(self):
1561
+ """
1562
+ Get the byte offset of this field in the trace header.
1563
+
1564
+ Returns
1565
+ -------
1566
+ int
1567
+ Byte offset of the field.
1568
+ """
1569
+ return self.entry.byte
1570
+
1571
+ def type(self):
1572
+ """
1573
+ Get the data type of the field as defined in the layout.
1574
+
1575
+ Returns
1576
+ -------
1577
+ str
1578
+ Data type of the field.
1579
+ """
1580
+ return self.entry.type
1581
+
1582
+
1583
+ def use_only_if_non_zero(self):
1584
+ """
1585
+ Returns the field property 'if-non-zero' stating if this field could be
1586
+ used only when its value does not equal 0.
1587
+
1588
+ Returns
1589
+ -------
1590
+ bool
1591
+ True if requires non-zero value, False otherwise.
1592
+ """
1593
+ return self.entry.requires_nonzero_value
1594
+
1595
+ def __str__(self):
1596
+ name = self.name()
1597
+ offset = self.offset()
1598
+ type = self.type()
1599
+ non_zero = self.use_only_if_non_zero()
1600
+ 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 int, float or 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]