segyio 1.9.13__cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.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.
Potentially problematic release.
This version of segyio might be problematic. Click here for more details.
- segyio/__init__.py +99 -0
- segyio/_segyio.cpython-313-aarch64-linux-gnu.so +0 -0
- segyio/binfield.py +89 -0
- segyio/create.py +257 -0
- segyio/depth.py +180 -0
- segyio/field.py +546 -0
- segyio/gather.py +444 -0
- segyio/line.py +498 -0
- segyio/open.py +192 -0
- segyio/segy.py +1010 -0
- segyio/segysampleformat.py +19 -0
- segyio/su/__init__.py +2 -0
- segyio/su/file.py +118 -0
- segyio/su/words.py +284 -0
- segyio/tools.py +731 -0
- segyio/trace.py +967 -0
- segyio/tracefield.py +195 -0
- segyio/tracesortingformat.py +6 -0
- segyio/utils.py +28 -0
- segyio-1.9.13.dist-info/METADATA +79 -0
- segyio-1.9.13.dist-info/RECORD +23 -0
- segyio-1.9.13.dist-info/WHEEL +6 -0
- segyio-1.9.13.dist-info/top_level.txt +1 -0
segyio/line.py
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from collections.abc import Mapping # noqa
|
|
3
|
+
except ImportError:
|
|
4
|
+
from collections import Mapping # noqa
|
|
5
|
+
|
|
6
|
+
import itertools
|
|
7
|
+
try: from future_builtins import zip
|
|
8
|
+
except ImportError: pass
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from .utils import castarray
|
|
12
|
+
|
|
13
|
+
# in order to support [:end] syntax, we must make sure
|
|
14
|
+
# start has a non-None value. lineno.indices() would set it
|
|
15
|
+
# to 0, but we don't know if that's a reasonable value or
|
|
16
|
+
# not. If start is None we set it to the first line
|
|
17
|
+
def sanitize_slice(s, source):
|
|
18
|
+
if all((s.start, s.stop, s.step)):
|
|
19
|
+
return s
|
|
20
|
+
|
|
21
|
+
start, stop, step = s.start, s.stop, s.step
|
|
22
|
+
increasing = step is None or step > 0
|
|
23
|
+
|
|
24
|
+
if start is None:
|
|
25
|
+
start = min(source) if increasing else max(source)
|
|
26
|
+
|
|
27
|
+
if stop is None:
|
|
28
|
+
stop = max(source) + 1 if increasing else min(source) - 1
|
|
29
|
+
|
|
30
|
+
return slice(start, stop, step)
|
|
31
|
+
|
|
32
|
+
class Line(Mapping):
|
|
33
|
+
"""
|
|
34
|
+
The Line implements the dict interface, with a fixed set of int_like keys,
|
|
35
|
+
the line numbers/labels. Data is read lazily from disk, so iteration does
|
|
36
|
+
not consume much memory, and are returned as numpy.ndarrays.
|
|
37
|
+
|
|
38
|
+
It provides a convenient interface for reading data in a cartesian grid
|
|
39
|
+
system, provided one exists and is detectable by segyio.
|
|
40
|
+
|
|
41
|
+
Lines can be accessed individually or with slices, and writing is done via
|
|
42
|
+
assignment. Note that accessing lines uses the line numbers, not their
|
|
43
|
+
position, so if a files has lines [2400..2500], accessing line [0..100]
|
|
44
|
+
will be an error. Note that since each line is returned as a numpy.ndarray,
|
|
45
|
+
meaning accessing the intersections of the inline and crossline is
|
|
46
|
+
0-indexed - orthogonal labels are not preserved.
|
|
47
|
+
|
|
48
|
+
Additionally, the line has a concept of offsets, which is useful when
|
|
49
|
+
dealing with prestack files. Offsets are accessed via sub indexing, meaning
|
|
50
|
+
iline[10, 4] will give you line 10 at offset 4. Please note that offset,
|
|
51
|
+
like lines, are accessed via their labels, not their indices. If your file
|
|
52
|
+
has the offsets [150, 250, 350, 450] and the lines [2400..2500], you can
|
|
53
|
+
access the third offset with [2403, 350]. Please refer to the examples for
|
|
54
|
+
more details. If no offset is specified, segyio will give you the first.
|
|
55
|
+
|
|
56
|
+
Notes
|
|
57
|
+
-----
|
|
58
|
+
.. versionadded:: 1.1
|
|
59
|
+
|
|
60
|
+
.. versionchanged:: 1.6
|
|
61
|
+
common dict operations (Mapping)
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, filehandle, labels, length, stride, offsets, name):
|
|
65
|
+
self.filehandle = filehandle.xfd
|
|
66
|
+
self.lines = labels
|
|
67
|
+
self.length = length
|
|
68
|
+
self.stride = stride
|
|
69
|
+
self.shape = (length, len(filehandle.samples))
|
|
70
|
+
self.dtype = filehandle.dtype
|
|
71
|
+
|
|
72
|
+
# pre-compute all line beginnings
|
|
73
|
+
from ._segyio import fread_trace0
|
|
74
|
+
self.heads = {
|
|
75
|
+
label: fread_trace0(label,
|
|
76
|
+
length,
|
|
77
|
+
stride,
|
|
78
|
+
len(offsets),
|
|
79
|
+
labels,
|
|
80
|
+
name)
|
|
81
|
+
for label in labels
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
self.offsets = { x: i for i, x in enumerate(offsets) }
|
|
85
|
+
self.default_offset = offsets[0]
|
|
86
|
+
|
|
87
|
+
def ranges(self, index, offset):
|
|
88
|
+
if not isinstance(index, slice):
|
|
89
|
+
index = slice(index, index + 1)
|
|
90
|
+
|
|
91
|
+
if not isinstance(offset, slice):
|
|
92
|
+
offset = slice(offset, offset + 1)
|
|
93
|
+
|
|
94
|
+
index = sanitize_slice(index, self.heads.keys())
|
|
95
|
+
offset = sanitize_slice(offset, self.offsets.keys())
|
|
96
|
+
irange = range(*index.indices(max(self.heads.keys()) + 1))
|
|
97
|
+
orange = range(*offset.indices(max(self.offsets.keys()) + 1))
|
|
98
|
+
irange = filter(self.heads.__contains__, irange)
|
|
99
|
+
orange = filter(self.offsets.__contains__, orange)
|
|
100
|
+
# offset-range is used in inner loops, so make it a list for
|
|
101
|
+
# reusability. offsets are usually few, so no real punishment by using
|
|
102
|
+
# non-generators here
|
|
103
|
+
return irange, list(orange)
|
|
104
|
+
|
|
105
|
+
def __getitem__(self, index):
|
|
106
|
+
"""line[i] or line[i, o]
|
|
107
|
+
|
|
108
|
+
The line `i`, or the line `i` at a specific offset `o`. ``line[i]``
|
|
109
|
+
returns a numpy array, and changes to this array will *not* be
|
|
110
|
+
reflected on disk.
|
|
111
|
+
|
|
112
|
+
The `i` and `o` are *keys*, and should correspond to the line- and
|
|
113
|
+
offset labels in your file, and in the `ilines`, `xlines`, and
|
|
114
|
+
`offsets` attributes.
|
|
115
|
+
|
|
116
|
+
Slices can contain lines and offsets not in the file, and like with
|
|
117
|
+
list slicing, these are handled gracefully and ignored.
|
|
118
|
+
|
|
119
|
+
When `i` or `o` is a slice, a generator of numpy arrays is returned. If
|
|
120
|
+
the slice is defaulted (:), segyio knows enough about the structure to
|
|
121
|
+
give you all of the respective labels.
|
|
122
|
+
|
|
123
|
+
When both `i` and `o` are slices, only one generator is returned, and
|
|
124
|
+
the lines are yielded offsets-first, roughly equivalent to the double
|
|
125
|
+
for loop::
|
|
126
|
+
|
|
127
|
+
>>> for line in lines:
|
|
128
|
+
... for off in offsets:
|
|
129
|
+
... yield line[line, off]
|
|
130
|
+
...
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
i : int or slice
|
|
135
|
+
o : int or slice
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
line : numpy.ndarray of dtype or generator of numpy.ndarray of dtype
|
|
140
|
+
|
|
141
|
+
Raises
|
|
142
|
+
------
|
|
143
|
+
KeyError
|
|
144
|
+
If `i` or `o` don't exist
|
|
145
|
+
|
|
146
|
+
Notes
|
|
147
|
+
-----
|
|
148
|
+
.. versionadded:: 1.1
|
|
149
|
+
|
|
150
|
+
Examples
|
|
151
|
+
--------
|
|
152
|
+
|
|
153
|
+
Read an inline:
|
|
154
|
+
|
|
155
|
+
>>> x = line[2400]
|
|
156
|
+
|
|
157
|
+
Copy every inline into a list:
|
|
158
|
+
|
|
159
|
+
>>> l = [numpy.copy(x) for x in iline[:]]
|
|
160
|
+
|
|
161
|
+
Numpy operations on every other inline:
|
|
162
|
+
|
|
163
|
+
>>> for line in line[::2]:
|
|
164
|
+
... line = line * 2
|
|
165
|
+
... avg = np.average(line)
|
|
166
|
+
|
|
167
|
+
Read lines up to 2430:
|
|
168
|
+
|
|
169
|
+
>>> for line in line[:2430]:
|
|
170
|
+
... line.mean()
|
|
171
|
+
|
|
172
|
+
Copy all lines at all offsets:
|
|
173
|
+
|
|
174
|
+
>>> l = [numpy.copy(x) for x in line[:,:]]
|
|
175
|
+
|
|
176
|
+
Copy all offsets of a line:
|
|
177
|
+
|
|
178
|
+
>>> x = numpy.copy(iline[10,:])
|
|
179
|
+
|
|
180
|
+
Copy all lines at a fixed offset:
|
|
181
|
+
|
|
182
|
+
>>> x = numpy.copy(iline[:, 120])
|
|
183
|
+
|
|
184
|
+
Copy every other line and offset:
|
|
185
|
+
|
|
186
|
+
>>> map(numpy.copy, line[::2, ::2])
|
|
187
|
+
|
|
188
|
+
Copy all offsets [200, 250, 300, 350, ...] in the range [200, 800) for
|
|
189
|
+
all lines [2420,2460):
|
|
190
|
+
|
|
191
|
+
>>> l = [numpy.copy(x) for x in line[2420:2460, 200:800:50]]
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
offset = self.default_offset
|
|
195
|
+
try: index, offset = index
|
|
196
|
+
except TypeError: pass
|
|
197
|
+
|
|
198
|
+
# prioritise the code path that's potentially in loops externally
|
|
199
|
+
if not isinstance(index, slice) and not isinstance(offset, slice):
|
|
200
|
+
head = self.heads[index] + self.offsets[offset]
|
|
201
|
+
return self.filehandle.getline(head,
|
|
202
|
+
self.length,
|
|
203
|
+
self.stride,
|
|
204
|
+
len(self.offsets),
|
|
205
|
+
np.empty(self.shape, dtype=self.dtype),
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# at this point, either offset or index is a slice (or proper
|
|
209
|
+
# type-error), so we're definitely making a generator. make them both
|
|
210
|
+
# slices to unify all code paths
|
|
211
|
+
irange, orange = self.ranges(index, offset)
|
|
212
|
+
|
|
213
|
+
def gen():
|
|
214
|
+
x = np.empty(self.shape, dtype=self.dtype)
|
|
215
|
+
y = np.copy(x)
|
|
216
|
+
|
|
217
|
+
# only fetch lines that exist. the slice can generate both offsets
|
|
218
|
+
# and line numbers that don't exist, so filter out misses before
|
|
219
|
+
# they happen
|
|
220
|
+
for line in irange:
|
|
221
|
+
for off in orange:
|
|
222
|
+
head = self.heads[line] + self.offsets[off]
|
|
223
|
+
self.filehandle.getline(head,
|
|
224
|
+
self.length,
|
|
225
|
+
self.stride,
|
|
226
|
+
len(self.offsets),
|
|
227
|
+
y,
|
|
228
|
+
)
|
|
229
|
+
y, x = x, y
|
|
230
|
+
yield x
|
|
231
|
+
|
|
232
|
+
return gen()
|
|
233
|
+
|
|
234
|
+
def __setitem__(self, index, val):
|
|
235
|
+
"""line[i] = val or line[i, o] = val
|
|
236
|
+
|
|
237
|
+
Follows the same rules for indexing and slicing as ``line[i]``.
|
|
238
|
+
|
|
239
|
+
In either case, if the `val` iterable is exhausted before the line(s),
|
|
240
|
+
assignment stops with whatever is written so far. If `val` is longer
|
|
241
|
+
than an individual line, it's essentially truncated.
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
i : int or slice
|
|
246
|
+
offset : int or slice
|
|
247
|
+
val : array_like
|
|
248
|
+
|
|
249
|
+
Raises
|
|
250
|
+
------
|
|
251
|
+
KeyError
|
|
252
|
+
If `i` or `o` don't exist
|
|
253
|
+
|
|
254
|
+
Notes
|
|
255
|
+
-----
|
|
256
|
+
.. versionadded:: 1.1
|
|
257
|
+
|
|
258
|
+
Examples
|
|
259
|
+
--------
|
|
260
|
+
Copy a full line:
|
|
261
|
+
|
|
262
|
+
>>> line[2400] = other[2834]
|
|
263
|
+
|
|
264
|
+
Copy first half of the inlines from g to f:
|
|
265
|
+
|
|
266
|
+
>>> line[:] = other[:labels[len(labels) / 2]]
|
|
267
|
+
|
|
268
|
+
Copy every other line consecutively:
|
|
269
|
+
|
|
270
|
+
>>> line[:] = other[::2]
|
|
271
|
+
|
|
272
|
+
Copy every third offset:
|
|
273
|
+
|
|
274
|
+
>>> line[:,:] = other[:,::3]
|
|
275
|
+
|
|
276
|
+
Copy a line into a set line and offset:
|
|
277
|
+
|
|
278
|
+
>>> line[12, 200] = other[21]
|
|
279
|
+
"""
|
|
280
|
+
|
|
281
|
+
offset = self.default_offset
|
|
282
|
+
try: index, offset = index
|
|
283
|
+
except TypeError: pass
|
|
284
|
+
|
|
285
|
+
if not isinstance(index, slice) and not isinstance(offset, slice):
|
|
286
|
+
head = self.heads[index] + self.offsets[offset]
|
|
287
|
+
return self.filehandle.putline(head,
|
|
288
|
+
self.length,
|
|
289
|
+
self.stride,
|
|
290
|
+
len(self.offsets),
|
|
291
|
+
index,
|
|
292
|
+
offset,
|
|
293
|
+
castarray(val, dtype = self.dtype),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
irange, orange = self.ranges(index, offset)
|
|
297
|
+
|
|
298
|
+
val = iter(val)
|
|
299
|
+
for line in irange:
|
|
300
|
+
for off in orange:
|
|
301
|
+
head = self.heads[line] + self.offsets[off]
|
|
302
|
+
try: self.filehandle.putline(head,
|
|
303
|
+
self.length,
|
|
304
|
+
self.stride,
|
|
305
|
+
len(self.offsets),
|
|
306
|
+
line,
|
|
307
|
+
off,
|
|
308
|
+
next(val),
|
|
309
|
+
)
|
|
310
|
+
except StopIteration: return
|
|
311
|
+
|
|
312
|
+
# can't rely on most Mapping default implementations of
|
|
313
|
+
# dict-like, because iter() does not yield keys for this class, it gives
|
|
314
|
+
# the lines themselves. that violates some assumptions (but segyio's always
|
|
315
|
+
# worked that way), and it's the more natural behaviour for segyio, so it's
|
|
316
|
+
# acceptible. additionally, the default implementations would be very slow
|
|
317
|
+
# and ineffective because they assume __getitem__ is sufficiently cheap,
|
|
318
|
+
# but it isn't here since it involves a disk operation
|
|
319
|
+
def __len__(self):
|
|
320
|
+
"""x.__len__() <==> len(x)"""
|
|
321
|
+
return len(self.heads)
|
|
322
|
+
|
|
323
|
+
def __iter__(self):
|
|
324
|
+
"""x.__iter__() <==> iter(x)"""
|
|
325
|
+
return self[:]
|
|
326
|
+
|
|
327
|
+
def __contains__(self, key):
|
|
328
|
+
"""x.__contains__(y) <==> y in x"""
|
|
329
|
+
return key in self.heads
|
|
330
|
+
|
|
331
|
+
def keys(self):
|
|
332
|
+
"""D.keys() -> a set-like object providing a view on D's keys"""
|
|
333
|
+
return sorted(self.heads.keys())
|
|
334
|
+
|
|
335
|
+
def values(self):
|
|
336
|
+
"""D.values() -> generator of D's values"""
|
|
337
|
+
return self[:]
|
|
338
|
+
|
|
339
|
+
def items(self):
|
|
340
|
+
"""D.values() -> generator of D's (key,values), as 2-tuples"""
|
|
341
|
+
return zip(self.keys(), self[:])
|
|
342
|
+
|
|
343
|
+
class HeaderLine(Line):
|
|
344
|
+
"""
|
|
345
|
+
The Line implements the dict interface, with a fixed set of int_like keys,
|
|
346
|
+
the line numbers/labels. The values are iterables of Field objects.
|
|
347
|
+
|
|
348
|
+
Notes
|
|
349
|
+
-----
|
|
350
|
+
.. versionadded:: 1.1
|
|
351
|
+
|
|
352
|
+
.. versionchanged:: 1.6
|
|
353
|
+
common dict operations (Mapping)
|
|
354
|
+
"""
|
|
355
|
+
# a lot of implementation details are shared between reading data traces
|
|
356
|
+
# line-by-line and trace headers line-by-line, so (ab)use inheritance for
|
|
357
|
+
# __len__, keys() etc., however, the __getitem__ is way different and is re-implemented
|
|
358
|
+
|
|
359
|
+
def __init__(self, header, base, direction):
|
|
360
|
+
super(HeaderLine, self).__init__(header.segy,
|
|
361
|
+
base.lines,
|
|
362
|
+
base.length,
|
|
363
|
+
base.stride,
|
|
364
|
+
sorted(base.offsets.keys()),
|
|
365
|
+
'header.' + direction,
|
|
366
|
+
)
|
|
367
|
+
self.header = header
|
|
368
|
+
|
|
369
|
+
def __getitem__(self, index):
|
|
370
|
+
"""line[i] or line[i, o]
|
|
371
|
+
|
|
372
|
+
The line `i`, or the line `i` at a specific offset `o`. ``line[i]``
|
|
373
|
+
returns an iterable of `Field` objects, and changes to these *will* be
|
|
374
|
+
reflected on disk.
|
|
375
|
+
|
|
376
|
+
The `i` and `o` are *keys*, and should correspond to the line- and
|
|
377
|
+
offset labels in your file, and in the `ilines`, `xlines`, and
|
|
378
|
+
`offsets` attributes.
|
|
379
|
+
|
|
380
|
+
Slices can contain lines and offsets not in the file, and like with
|
|
381
|
+
list slicing, these are handled gracefully and ignored.
|
|
382
|
+
|
|
383
|
+
When `i` or `o` is a slice, a generator of iterables of headers are
|
|
384
|
+
returned.
|
|
385
|
+
|
|
386
|
+
When both `i` and `o` are slices, one generator is returned for the
|
|
387
|
+
product `i` and `o`, and the lines are yielded offsets-first, roughly
|
|
388
|
+
equivalent to the double for loop::
|
|
389
|
+
|
|
390
|
+
>>> for line in lines:
|
|
391
|
+
... for off in offsets:
|
|
392
|
+
... yield line[line, off]
|
|
393
|
+
...
|
|
394
|
+
|
|
395
|
+
Parameters
|
|
396
|
+
----------
|
|
397
|
+
|
|
398
|
+
i : int or slice
|
|
399
|
+
o : int or slice
|
|
400
|
+
|
|
401
|
+
Returns
|
|
402
|
+
-------
|
|
403
|
+
line : iterable of Field or generator of iterator of Field
|
|
404
|
+
|
|
405
|
+
Raises
|
|
406
|
+
------
|
|
407
|
+
KeyError
|
|
408
|
+
If `i` or `o` don't exist
|
|
409
|
+
|
|
410
|
+
Notes
|
|
411
|
+
-----
|
|
412
|
+
.. versionadded:: 1.1
|
|
413
|
+
|
|
414
|
+
"""
|
|
415
|
+
offset = self.default_offset
|
|
416
|
+
try: index, offset = index
|
|
417
|
+
except TypeError: pass
|
|
418
|
+
|
|
419
|
+
if not isinstance(index, slice) and not isinstance(offset, slice):
|
|
420
|
+
start = self.heads[index] + self.offsets[offset]
|
|
421
|
+
step = self.stride * len(self.offsets)
|
|
422
|
+
stop = start + step * self.length
|
|
423
|
+
return self.header[start:stop:step]
|
|
424
|
+
|
|
425
|
+
def gen():
|
|
426
|
+
irange, orange = self.ranges(index, offset)
|
|
427
|
+
for line in irange:
|
|
428
|
+
for off in orange:
|
|
429
|
+
yield self[line, off]
|
|
430
|
+
|
|
431
|
+
return gen()
|
|
432
|
+
|
|
433
|
+
def __setitem__(self, index, val):
|
|
434
|
+
"""line[i] = val or line[i, o] = val
|
|
435
|
+
|
|
436
|
+
Follows the same rules for indexing and slicing as ``line[i]``. If `i`
|
|
437
|
+
is an int, and `val` is a dict or Field, that value is replicated and
|
|
438
|
+
assigned to every trace header in the line, otherwise it's treated as
|
|
439
|
+
an iterable, and each trace in the line is assigned the ``next()``
|
|
440
|
+
yielded value.
|
|
441
|
+
|
|
442
|
+
If `i` or `o` is a slice, `val` must be an iterable.
|
|
443
|
+
|
|
444
|
+
In either case, if the `val` iterable is exhausted before the line(s),
|
|
445
|
+
assignment stops with whatever is written so far.
|
|
446
|
+
|
|
447
|
+
Parameters
|
|
448
|
+
----------
|
|
449
|
+
i : int or slice
|
|
450
|
+
offset : int or slice
|
|
451
|
+
val : dict_like or iterable of dict_like
|
|
452
|
+
|
|
453
|
+
Raises
|
|
454
|
+
------
|
|
455
|
+
KeyError
|
|
456
|
+
If `i` or `o` don't exist
|
|
457
|
+
|
|
458
|
+
Notes
|
|
459
|
+
-----
|
|
460
|
+
.. versionadded:: 1.1
|
|
461
|
+
|
|
462
|
+
Examples
|
|
463
|
+
--------
|
|
464
|
+
Rename the iline 3 to 4:
|
|
465
|
+
|
|
466
|
+
>>> line[3] = { TraceField.INLINE_3D: 4 }
|
|
467
|
+
>>> # please note that rewriting the header won't update the
|
|
468
|
+
>>> # file's interpretation of the file until you reload it, so
|
|
469
|
+
>>> # the new iline 4 will be considered iline 3 until the file
|
|
470
|
+
>>> # is reloaded
|
|
471
|
+
|
|
472
|
+
Set offset line 3 offset 3 to 5:
|
|
473
|
+
|
|
474
|
+
>>> line[3, 3] = { TraceField.offset: 5 }
|
|
475
|
+
"""
|
|
476
|
+
offset = self.default_offset
|
|
477
|
+
try: index, offset = index
|
|
478
|
+
except TypeError: pass
|
|
479
|
+
|
|
480
|
+
if not isinstance(index, slice) and not isinstance(offset, slice):
|
|
481
|
+
start = self.heads[index] + self.offsets[offset]
|
|
482
|
+
step = self.stride * len(self.offsets)
|
|
483
|
+
stop = start + step * self.length
|
|
484
|
+
self.header[start:stop:step] = val
|
|
485
|
+
return
|
|
486
|
+
|
|
487
|
+
# if this is a dict-like, just repeat it
|
|
488
|
+
if hasattr(val, 'keys'):
|
|
489
|
+
val = itertools.repeat(val)
|
|
490
|
+
|
|
491
|
+
irange, orange = self.ranges(index, offset)
|
|
492
|
+
val = iter(val)
|
|
493
|
+
for line in irange:
|
|
494
|
+
for off in orange:
|
|
495
|
+
try:
|
|
496
|
+
self[line, off] = next(val)
|
|
497
|
+
except StopIteration:
|
|
498
|
+
return
|
segyio/open.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
|
|
3
|
+
import segyio
|
|
4
|
+
|
|
5
|
+
def infer_geometry(f, metrics, iline, xline, strict):
|
|
6
|
+
try:
|
|
7
|
+
cube_metrics = f.xfd.cube_metrics(iline, xline)
|
|
8
|
+
f._sorting = cube_metrics['sorting']
|
|
9
|
+
iline_count = cube_metrics['iline_count']
|
|
10
|
+
xline_count = cube_metrics['xline_count']
|
|
11
|
+
offset_count = cube_metrics['offset_count']
|
|
12
|
+
metrics.update(cube_metrics)
|
|
13
|
+
|
|
14
|
+
ilines = numpy.zeros(iline_count, dtype=numpy.intc)
|
|
15
|
+
xlines = numpy.zeros(xline_count, dtype=numpy.intc)
|
|
16
|
+
offsets = numpy.zeros(offset_count, dtype=numpy.intc)
|
|
17
|
+
|
|
18
|
+
f.xfd.indices(metrics, ilines, xlines, offsets)
|
|
19
|
+
f.interpret(ilines, xlines, offsets, f._sorting)
|
|
20
|
+
|
|
21
|
+
except:
|
|
22
|
+
if not strict:
|
|
23
|
+
f._ilines = None
|
|
24
|
+
f._xlines = None
|
|
25
|
+
f._offsets = None
|
|
26
|
+
else:
|
|
27
|
+
f.close()
|
|
28
|
+
raise
|
|
29
|
+
|
|
30
|
+
return f
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def open(filename, mode="r", iline = 189,
|
|
34
|
+
xline = 193,
|
|
35
|
+
strict = True,
|
|
36
|
+
ignore_geometry = False,
|
|
37
|
+
endian = 'big'):
|
|
38
|
+
"""Open a segy file.
|
|
39
|
+
|
|
40
|
+
Opens a segy file and tries to figure out its sorting, inline numbers,
|
|
41
|
+
crossline numbers, and offsets, and enables reading and writing to this
|
|
42
|
+
file in a simple manner.
|
|
43
|
+
|
|
44
|
+
For reading, the access mode `r` is preferred. All write operations will
|
|
45
|
+
raise an exception. For writing, the mode `r+` is preferred (as `rw` would
|
|
46
|
+
truncate the file). Any mode with `w` will raise an error. The modes used
|
|
47
|
+
are standard C file modes; please refer to that documentation for a
|
|
48
|
+
complete reference.
|
|
49
|
+
|
|
50
|
+
Open should be used together with python's ``with`` statement. Please refer
|
|
51
|
+
to the examples. When the ``with`` statement is used the file will
|
|
52
|
+
automatically be closed when the routine completes or an exception is
|
|
53
|
+
raised.
|
|
54
|
+
|
|
55
|
+
By default, segyio tries to open in ``strict`` mode. This means the file will
|
|
56
|
+
be assumed to represent a geometry with consistent inline, crosslines and
|
|
57
|
+
offsets. If strict is False, segyio will still try to establish a geometry,
|
|
58
|
+
but it won't abort if it fails. When in non-strict mode is opened,
|
|
59
|
+
geometry-dependent modes such as iline will raise an error.
|
|
60
|
+
|
|
61
|
+
If ``ignore_geometry=True``, segyio will *not* try to build iline/xline or
|
|
62
|
+
other geometry related structures, which leads to faster opens. This is
|
|
63
|
+
essentially the same as using ``strict=False`` on a file that has no
|
|
64
|
+
geometry.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
|
|
69
|
+
filename : str
|
|
70
|
+
Path to file to open
|
|
71
|
+
|
|
72
|
+
mode : {'r', 'r+'}
|
|
73
|
+
File access mode, read-only ('r', default) or read-write ('r+')
|
|
74
|
+
|
|
75
|
+
iline : int or segyio.TraceField
|
|
76
|
+
Inline number field in the trace headers. Defaults to 189 as per the
|
|
77
|
+
SEG-Y rev1 specification
|
|
78
|
+
|
|
79
|
+
xline : int or segyio.TraceField
|
|
80
|
+
Crossline number field in the trace headers. Defaults to 193 as per the
|
|
81
|
+
SEG-Y rev1 specification
|
|
82
|
+
|
|
83
|
+
strict : bool, optional
|
|
84
|
+
Abort if a geometry cannot be inferred. Defaults to True.
|
|
85
|
+
|
|
86
|
+
ignore_geometry : bool, optional
|
|
87
|
+
Opt out on building geometry information, useful for e.g. shot
|
|
88
|
+
organised files. Defaults to False.
|
|
89
|
+
|
|
90
|
+
endian : {'big', 'msb', 'little', 'lsb'}
|
|
91
|
+
File endianness, big/msb (default) or little/lsb
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
|
|
96
|
+
file : segyio.SegyFile
|
|
97
|
+
An open segyio file handle
|
|
98
|
+
|
|
99
|
+
Raises
|
|
100
|
+
------
|
|
101
|
+
|
|
102
|
+
ValueError
|
|
103
|
+
If the mode string contains 'w', as it would truncate the file
|
|
104
|
+
|
|
105
|
+
Notes
|
|
106
|
+
-----
|
|
107
|
+
|
|
108
|
+
.. versionadded:: 1.1
|
|
109
|
+
|
|
110
|
+
.. versionchanged:: 1.8
|
|
111
|
+
endian argument
|
|
112
|
+
|
|
113
|
+
When a file is opened non-strict, only raw traces access is allowed, and
|
|
114
|
+
using modes such as ``iline`` raise an error.
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
Examples
|
|
118
|
+
--------
|
|
119
|
+
|
|
120
|
+
Open a file in read-only mode:
|
|
121
|
+
|
|
122
|
+
>>> with segyio.open(path, "r") as f:
|
|
123
|
+
... print(f.ilines)
|
|
124
|
+
...
|
|
125
|
+
[1, 2, 3, 4, 5]
|
|
126
|
+
|
|
127
|
+
Open a file in read-write mode:
|
|
128
|
+
|
|
129
|
+
>>> with segyio.open(path, "r+") as f:
|
|
130
|
+
... f.trace = np.arange(100)
|
|
131
|
+
|
|
132
|
+
Open two files at once:
|
|
133
|
+
|
|
134
|
+
>>> with segyio.open(src_path) as src, segyio.open(dst_path, "r+") as dst:
|
|
135
|
+
... dst.trace = src.trace # copy all traces from src to dst
|
|
136
|
+
|
|
137
|
+
Open a file little-endian file:
|
|
138
|
+
|
|
139
|
+
>>> with segyio.open(path, endian = 'little') as f:
|
|
140
|
+
... f.trace[0]
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
if 'w' in mode:
|
|
145
|
+
problem = 'w in mode would truncate the file'
|
|
146
|
+
solution = 'use r+ to open in read-write'
|
|
147
|
+
raise ValueError(', '.join((problem, solution)))
|
|
148
|
+
|
|
149
|
+
endians = {
|
|
150
|
+
'little': 256, # (1 << 8)
|
|
151
|
+
'lsb': 256,
|
|
152
|
+
'big': 0,
|
|
153
|
+
'msb': 0,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if endian not in endians:
|
|
157
|
+
problem = 'unknown endianness {}, expected one of: '
|
|
158
|
+
opts = ' '.join(endians.keys())
|
|
159
|
+
raise ValueError(problem.format(endian) + opts)
|
|
160
|
+
|
|
161
|
+
from . import _segyio
|
|
162
|
+
fd = _segyio.segyiofd(str(filename), mode, endians[endian])
|
|
163
|
+
fd.segyopen()
|
|
164
|
+
metrics = fd.metrics()
|
|
165
|
+
|
|
166
|
+
f = segyio.SegyFile(fd,
|
|
167
|
+
filename = str(filename),
|
|
168
|
+
mode = mode,
|
|
169
|
+
iline = int(iline),
|
|
170
|
+
xline = int(xline),
|
|
171
|
+
endian = endian,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
delay_scalar = f.header[0][segyio.TraceField.ScalarTraceHeader]
|
|
176
|
+
if delay_scalar == 0:
|
|
177
|
+
delay_scalar = 1
|
|
178
|
+
elif delay_scalar < 0:
|
|
179
|
+
delay_scalar = 1.0 / delay_scalar
|
|
180
|
+
dt = segyio.tools.dt(f, fallback_dt = 4000.0) / 1000.0
|
|
181
|
+
t0 = f.header[0][segyio.TraceField.DelayRecordingTime] * abs(delay_scalar)
|
|
182
|
+
samples = metrics['samplecount']
|
|
183
|
+
f._samples = (numpy.arange(samples) * dt) + t0
|
|
184
|
+
|
|
185
|
+
except:
|
|
186
|
+
f.close()
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
if ignore_geometry:
|
|
190
|
+
return f
|
|
191
|
+
|
|
192
|
+
return infer_geometry(f, metrics, int(iline), int(xline), strict)
|