[segyio] 231/376: Do not throw on files without good geometry

Jørgen Kvalsvik jokva-guest at moszumanska.debian.org
Wed Sep 20 08:04:38 UTC 2017


This is an automated email from the git hooks/post-receive script.

jokva-guest pushed a commit to branch debian
in repository segyio.

commit fe01eb0b30109ae5f54b6b9d3f0bf6064461cb33
Author: jokva <jokva at statoil.com>
Date:   Thu Mar 9 17:53:25 2017 +0100

    Do not throw on files without good geometry
    
    Support opening files without a reasonable geometry, or that are in some
    other way unsorted. Opens up a use case where segyio is used to read
    files not yet stacked, organised by lines, but still has useful
    properties or interesting data.
    
    The default behaviour is to assume a file is sorted, and raise an
    exception if this cannot be figured out.
---
 python/segyio/_segyio.c | 61 ++++++++++++++++++++++++++++++++--------------
 python/segyio/open.py   | 65 ++++++++++++++++++++++++++++++++++++-------------
 python/segyio/segy.py   | 32 +++++++++++++++++++++---
 python/test/segy.py     | 31 ++++++++++++++++++++++-
 python/test/segyio_c.py | 49 +++++++++++++++++++++++++++----------
 5 files changed, 185 insertions(+), 53 deletions(-)

diff --git a/python/segyio/_segyio.c b/python/segyio/_segyio.c
index fbec53a..53d3952 100644
--- a/python/segyio/_segyio.c
+++ b/python/segyio/_segyio.c
@@ -665,10 +665,8 @@ static PyObject *py_init_metrics(PyObject *self, PyObject *args) {
     errno = 0;
     PyObject *file_capsule = NULL;
     PyObject *binary_header_capsule = NULL;
-    int il_field;
-    int xl_field;
 
-    PyArg_ParseTuple(args, "OOii", &file_capsule, &binary_header_capsule, &il_field, &xl_field);
+    PyArg_ParseTuple(args, "OO", &file_capsule, &binary_header_capsule);
 
     segy_file *p_FILE = get_FILE_pointer_from_capsule(file_capsule);
 
@@ -683,18 +681,49 @@ static PyObject *py_init_metrics(PyObject *self, PyObject *args) {
     int format = segy_format(binary_header);
     int trace_bsize = segy_trace_bsize(sample_count);
 
-    int sorting;
-    int error = segy_sorting(p_FILE, il_field, xl_field, &sorting, trace0, trace_bsize);
+    int trace_count;
+    int error = segy_traces(p_FILE, &trace_count, trace0, trace_bsize);
 
     if (error != 0) {
-        return py_handle_segy_error_with_fields(error, errno, il_field, xl_field, 2);
+        return py_handle_segy_error(error, errno);
     }
 
+    PyObject *dict = PyDict_New();
+    PyDict_SetItemString(dict, "trace0", Py_BuildValue("l", trace0));
+    PyDict_SetItemString(dict, "sample_count", Py_BuildValue("i", sample_count));
+    PyDict_SetItemString(dict, "format", Py_BuildValue("i", format));
+    PyDict_SetItemString(dict, "trace_bsize", Py_BuildValue("i", trace_bsize));
+    PyDict_SetItemString(dict, "trace_count", Py_BuildValue("i", trace_count));
+
+    return Py_BuildValue("O", dict);
+}
+
+static PyObject *py_init_cube_metrics(PyObject *self, PyObject *args) {
+    errno = 0;
+
+    PyObject *file_capsule = NULL;
+    int il_field;
+    int xl_field;
     int trace_count;
-    error = segy_traces(p_FILE, &trace_count, trace0, trace_bsize);
+    long trace0;
+    int trace_bsize;
+
+    PyArg_ParseTuple(args, "Oiiili", &file_capsule,
+                                     &il_field,
+                                     &xl_field,
+                                     &trace_count,
+                                     &trace0,
+                                     &trace_bsize);
+
+    segy_file *p_FILE = get_FILE_pointer_from_capsule(file_capsule);
+
+    if (PyErr_Occurred()) { return NULL; }
+
+    int sorting;
+    int error = segy_sorting(p_FILE, il_field, xl_field, &sorting, trace0, trace_bsize);
 
     if (error != 0) {
-        return py_handle_segy_error(error, errno);
+        return py_handle_segy_error_with_fields(error, errno, il_field, xl_field, 2);
     }
 
     int offset_count;
@@ -733,18 +762,13 @@ static PyObject *py_init_metrics(PyObject *self, PyObject *args) {
     }
 
     PyObject *dict = PyDict_New();
-    PyDict_SetItemString(dict, "iline_field", Py_BuildValue("i", il_field));
-    PyDict_SetItemString(dict, "xline_field", Py_BuildValue("i", xl_field));
+    PyDict_SetItemString(dict, "sorting",      Py_BuildValue("i", sorting));
+    PyDict_SetItemString(dict, "iline_field",  Py_BuildValue("i", il_field));
+    PyDict_SetItemString(dict, "xline_field",  Py_BuildValue("i", xl_field));
     PyDict_SetItemString(dict, "offset_field", Py_BuildValue("i", 37));
-    PyDict_SetItemString(dict, "trace0", Py_BuildValue("l", trace0));
-    PyDict_SetItemString(dict, "sample_count", Py_BuildValue("i", sample_count));
-    PyDict_SetItemString(dict, "format", Py_BuildValue("i", format));
-    PyDict_SetItemString(dict, "trace_bsize", Py_BuildValue("i", trace_bsize));
-    PyDict_SetItemString(dict, "sorting", Py_BuildValue("i", sorting));
-    PyDict_SetItemString(dict, "trace_count", Py_BuildValue("i", trace_count));
     PyDict_SetItemString(dict, "offset_count", Py_BuildValue("i", offset_count));
-    PyDict_SetItemString(dict, "iline_count", Py_BuildValue("i", il_count));
-    PyDict_SetItemString(dict, "xline_count", Py_BuildValue("i", xl_count));
+    PyDict_SetItemString(dict, "iline_count",  Py_BuildValue("i", il_count));
+    PyDict_SetItemString(dict, "xline_count",  Py_BuildValue("i", xl_count));
 
     return Py_BuildValue("O", dict);
 }
@@ -1183,6 +1207,7 @@ static PyMethodDef SegyMethods[] = {
         {"set_field",          (PyCFunction) py_set_field,          METH_VARARGS, "Set a header field."},
 
         {"init_line_metrics",  (PyCFunction) py_init_line_metrics,  METH_VARARGS, "Find the length and stride of inline and crossline."},
+        {"init_cube_metrics",  (PyCFunction) py_init_cube_metrics,  METH_VARARGS, "Find the cube properties sorting, number of ilines, crosslines and offsets."},
         {"init_metrics",       (PyCFunction) py_init_metrics,       METH_VARARGS, "Find most metrics for a segy file."},
         {"init_indices",       (PyCFunction) py_init_indices,       METH_VARARGS, "Find the indices for inline, crossline and offsets."},
         {"fread_trace0",       (PyCFunction) py_fread_trace0,       METH_VARARGS, "Find trace0 of a line."},
diff --git a/python/segyio/open.py b/python/segyio/open.py
index 0871ce0..e340793 100644
--- a/python/segyio/open.py
+++ b/python/segyio/open.py
@@ -3,7 +3,7 @@ import numpy
 import segyio
 
 
-def open(filename, mode="r", iline=189, xline=193):
+def open(filename, mode="r", iline=189, xline=193, strict = True):
     """Open a segy file.
 
     Opens a segy file and tries to figure out its sorting, inline numbers,
@@ -20,6 +20,12 @@ def open(filename, mode="r", iline=189, xline=193):
     automatically be closed when the routine completes or an exception is
     raised.
 
+    By default, segyio tries to open in 'strict' mode. This means the file will
+    be assumed to represent a geometry with consistent inline, crosslines and
+    offsets. If strict is False, segyio will still try to establish a geometry,
+    but it won't abort if it fails. When in non-strict mode is opened,
+    geometry-dependent modes such as iline will raise an error.
+
     Args:
         filename (str): Path to file to open.
         mode (str, optional): File access mode, defaults to "r".
@@ -27,6 +33,8 @@ def open(filename, mode="r", iline=189, xline=193):
                             to 189 as per the SEGY specification.
         xline (TraceField): Crossline number field in the trace headers.
                             Defaults to 193 as per the SEGY specification.
+        strict (bool, optional): Abort if a geometry cannot be inferred.
+                                 Defaults to True.
 
     Examples:
         Open a file in read-only mode::
@@ -48,38 +56,61 @@ def open(filename, mode="r", iline=189, xline=193):
     f = segyio.SegyFile(filename, mode, iline, xline)
 
     try:
-        header = f.bin.buf
-        metrics = segyio._segyio.init_metrics(f.xfd, header, iline, xline)
+        metrics = segyio._segyio.init_metrics(f.xfd, f.bin.buf)
 
         f._samples = metrics['sample_count']
         f._tr0 = metrics['trace0']
         f._fmt = metrics['format']
         f._bsz = metrics['trace_bsize']
         f._ext_headers = (f._tr0 - 3600) // 3200  # should probably be from C
-
         f._tracecount = metrics['trace_count']
 
-        f._sorting = metrics['sorting']
+    except:
+        f.close()
+        raise
+    try:
+        cube_metrics = segyio._segyio.init_cube_metrics(f.xfd,
+                                                        iline,
+                                                        xline,
+                                                        f.tracecount,
+                                                        f._tr0,
+                                                        f._bsz)
+        f._sorting   = cube_metrics['sorting']
+        iline_count  = cube_metrics['iline_count']
+        xline_count  = cube_metrics['xline_count']
+        offset_count = cube_metrics['offset_count']
+        metrics.update(cube_metrics)
+
+        line_metrics = segyio._segyio.init_line_metrics(f.sorting,
+                                                        f.tracecount,
+                                                        iline_count,
+                                                        xline_count,
+                                                        offset_count)
 
-        iline_count, xline_count = metrics['iline_count'], metrics['xline_count']
-        offset_count = metrics['offset_count']
+        f._iline_length = line_metrics['iline_length']
+        f._iline_stride = line_metrics['iline_stride']
 
-        line_metrics = segyio._segyio.init_line_metrics(f.sorting, f.tracecount,
-                                                        iline_count, xline_count, offset_count)
+        f._xline_length = line_metrics['xline_length']
+        f._xline_stride = line_metrics['xline_stride']
 
-        f._ilines  = numpy.zeros(iline_count, dtype=numpy.intc)
-        f._xlines  = numpy.zeros(xline_count, dtype=numpy.intc)
+        f._ilines  = numpy.zeros(iline_count,  dtype = numpy.intc)
+        f._xlines  = numpy.zeros(xline_count,  dtype = numpy.intc)
         f._offsets = numpy.zeros(offset_count, dtype = numpy.intc)
         segyio._segyio.init_indices(f.xfd, metrics, f.ilines, f.xlines, f.offsets)
 
-        f._iline_length = line_metrics['iline_length']
-        f._iline_stride = line_metrics['iline_stride']
+        if numpy.unique(f.ilines).size != f.ilines.size:
+            raise ValueError( "Inlines inconsistent - expect all inlines to be unique")
 
-        f._xline_length = line_metrics['xline_length']
-        f._xline_stride = line_metrics['xline_stride']
+        if numpy.unique(f.xlines).size != f.xlines.size:
+            raise ValueError( "Crosslines inconsistent - expect all crosslines to be unique")
 
     except:
-        f.close()
-        raise
+        if not strict:
+            f._ilines  = None
+            f._xlines  = None
+            f._offsets = None
+        else:
+            f.close()
+            raise
 
     return f
diff --git a/python/segyio/segy.py b/python/segyio/segy.py
index 7bf4ddc..23ca5c6 100644
--- a/python/segyio/segy.py
+++ b/python/segyio/segy.py
@@ -36,6 +36,8 @@ else: range = xrange
 
 class SegyFile(object):
 
+    _unstructured_errmsg = "File opened in unstructured mode."
+
     def __init__(self, filename, mode, iline=189, xline=193):
         """
         Constructor, internal.
@@ -70,16 +72,23 @@ class SegyFile(object):
 
     def __str__(self):
         f = "SegyFile {}:".format(self._filename)
-        il =  "  inlines: {} [{}, {}]".format(len(self.ilines), self.ilines[0], self.ilines[-1])
-        xl =  "  crosslines: {} [{}, {}]".format(len(self.xlines), self.xlines[0], self.xlines[-1])
+
+        if self.unstructured:
+            il =  "  inlines: None"
+            xl =  "  crosslines: None"
+            of =  "  offsets: None"
+        else:
+            il =  "  inlines: {} [{}, {}]".format(len(self.ilines), self.ilines[0], self.ilines[-1])
+            xl =  "  crosslines: {} [{}, {}]".format(len(self.xlines), self.xlines[0], self.xlines[-1])
+            of =  "  offsets: {} [{}, {}]".format(len(self.offsets), self.offsets[0], self.offsets[-1])
+
         tr =  "  traces: {}".format(self.tracecount)
         sm =  "  samples: {}".format(self.samples)
-        of =  "  offsets: {} [{}, {}]".format(len(self.offsets), self.offsets[0], self.offsets[-1])
         fmt = "  float representation: {}".format(self.format)
 
         props = [f, il, xl, tr, sm]
 
-        if len(self.offsets) > 1:
+        if self.offsets is not None and len(self.offsets) > 1:
             props.append(of)
 
         props.append(fmt)
@@ -181,6 +190,9 @@ class SegyFile(object):
         """ :rtype: int """
         return self._ext_headers
 
+    @property
+    def unstructured(self):
+        return self.ilines is None
 
     @property
     def header(self):
@@ -582,6 +594,10 @@ class SegyFile(object):
             Copy an iline from f to g at g's offset 200::
                 >>> g.iline[12, 200] = f.iline[21]
         """
+
+        if self.unstructured:
+            raise ValueError(self._unstructured_errmsg)
+
         il_len, il_stride = self._iline_length, self._iline_stride
         lines = self.ilines
         other_lines = self.xlines
@@ -711,6 +727,10 @@ class SegyFile(object):
             Copy an xline from f to g at g's offset 200::
                 >>> g.xline[12, 200] = f.xline[21]
         """
+
+        if self.unstructured:
+            raise ValueError(self._unstructured_errmsg)
+
         xl_len, xl_stride = self._xline_length, self._xline_stride
         lines = self.xlines
         other_lines = self.ilines
@@ -832,6 +852,10 @@ class SegyFile(object):
             Copy every other depth slices from a different file::
                 >>> f.depth_slice = g.depth_slice[::2]
         """
+
+        if self.unstructured:
+            raise ValueError(self._unstructured_errmsg)
+
         indices = np.asarray(list(range(self.samples)), dtype=np.uintc)
         other_indices = np.asarray([0], dtype=np.uintc)
         buffn = self._depth_buffer
diff --git a/python/test/segy.py b/python/test/segy.py
index 88ae144..643ab2b 100644
--- a/python/test/segy.py
+++ b/python/test/segy.py
@@ -171,6 +171,10 @@ class TestSegy(TestCase):
             self.assertEqual(len(f.trace), f.tracecount)
             self.assertEqual(50, f.samples)
 
+    def test_open_nostrict(self):
+        with segyio.open(self.filename, strict = False):
+            pass
+
     def test_traces_slicing(self):
         with segyio.open(self.filename, "r") as f:
 
@@ -223,7 +227,7 @@ class TestSegy(TestCase):
                 f.header.iline[1,2] = { il: 11 }
                 f.header.iline[1,2] = { xl: 13 }
 
-            with segyio.open("small-ps.sgy", "r") as f:
+            with segyio.open("small-ps.sgy", "r", strict = False) as f:
                 self.assertEqual(f.header[0][il], 1)
                 self.assertEqual(f.header[1][il], 11)
                 self.assertEqual(f.header[2][il], 1)
@@ -517,11 +521,17 @@ class TestSegy(TestCase):
             with segyio.open(self.filename, "r", 2) as f:
                 pass
 
+        with segyio.open(self.filename, "r", 2, strict = False) as f:
+            pass
+
     def test_open_wrong_crossline(self):
         with self.assertRaises(IndexError):
             with segyio.open(self.filename, "r", 189, 2) as f:
                 pass
 
+        with segyio.open(self.filename, "r", 189, 2, strict = False) as f:
+            pass
+
     def test_wonky_dimensions(self):
         with segyio.open(self.fileMx1) as f:
             pass
@@ -532,6 +542,25 @@ class TestSegy(TestCase):
         with segyio.open(self.file1x1) as f:
             pass
 
+    def test_open_fails_unstructured(self):
+        with segyio.open(self.filename, "r", 37, strict = False) as f:
+            with self.assertRaises(ValueError):
+                f.iline[10]
+
+            with self.assertRaises(ValueError):
+                f.iline[:,:]
+
+            with self.assertRaises(ValueError):
+                f.xline[:,:]
+
+            with self.assertRaises(ValueError):
+                f.depth_slice[2]
+
+            # operations that don't rely on geometry still works
+            self.assertEqual(f.header[2][189], 1)
+            self.assertListEqual(list(f.attributes(189)[:]),
+                                 [(i // 5) + 1 for i in range(len(f.trace))])
+
     def test_create_sgy(self):
         with TestContext("create_sgy") as context:
             context.copy_file(self.filename)
diff --git a/python/test/segyio_c.py b/python/test/segyio_c.py
index a573586..7bd8188 100644
--- a/python/test/segyio_c.py
+++ b/python/test/segyio_c.py
@@ -124,7 +124,11 @@ class _segyioTests(TestCase):
         binary_header = _segyio.read_binaryheader(f)
         ilb = 189
         xlb = 193
-        metrics = _segyio.init_metrics(f, binary_header, ilb, xlb)
+        metrics = _segyio.init_metrics(f, binary_header)
+        metrics.update(_segyio.init_cube_metrics(f, ilb, xlb,
+                                                 metrics['trace_count'],
+                                                 metrics['trace0'],
+                                                 metrics['trace_bsize']))
         _segyio.close(f)
 
         sorting = metrics['sorting']
@@ -162,18 +166,24 @@ class _segyioTests(TestCase):
         xlb = 193
 
         with self.assertRaises(TypeError):
-            metrics = _segyio.init_metrics("?", binary_header, ilb, xlb)
+            metrics = _segyio.init_metrics("?", binary_header)
 
         with self.assertRaises(TypeError):
-            metrics = _segyio.init_metrics(f, "?", ilb, xlb)
+            metrics = _segyio.init_metrics(f, "?")
 
         with self.assertRaises(IndexError):
-            metrics = _segyio.init_metrics(f, binary_header, ilb + 1, xlb)
+            metrics = _segyio.init_metrics(f, binary_header)
+            metrics.update(_segyio.init_cube_metrics(f, ilb + 1, xlb,
+                                                     metrics['trace_count'],
+                                                     metrics['trace0'],
+                                                     metrics['trace_bsize']))
+
+        metrics = _segyio.init_metrics(f, binary_header)
+        metrics.update(_segyio.init_cube_metrics(f, ilb, xlb,
+                                                 metrics['trace_count'],
+                                                 metrics['trace0'],
+                                                 metrics['trace_bsize']))
 
-        metrics = _segyio.init_metrics(f, binary_header, ilb, xlb)
-
-        self.assertEqual(metrics['iline_field'], ilb)
-        self.assertEqual(metrics['xline_field'], xlb)
         self.assertEqual(metrics['trace0'], _segyio.textheader_size() + _segyio.binheader_size())
         self.assertEqual(metrics['sample_count'], 50)
         self.assertEqual(metrics['format'], 1)
@@ -187,7 +197,7 @@ class _segyioTests(TestCase):
         _segyio.close(f)
 
         with self.assertRaises(IOError):
-            metrics = _segyio.init_metrics(f, binary_header, ilb, xlb)
+            metrics = _segyio.init_metrics(f, binary_header)
 
     def test_indices(self):
         f = _segyio.open(self.filename, "r")
@@ -195,7 +205,7 @@ class _segyioTests(TestCase):
         binary_header = _segyio.read_binaryheader(f)
         ilb = 189
         xlb = 193
-        metrics = _segyio.init_metrics(f, binary_header, ilb, xlb)
+        metrics = _segyio.init_metrics(f, binary_header)
         dmy = numpy.zeros(2, dtype=numpy.intc)
 
         dummy_metrics = {'xline_count':   2,
@@ -233,6 +243,11 @@ class _segyioTests(TestCase):
         with self.assertRaises(ValueError):
             _segyio.init_indices(f, dummy_metrics, two, one, off)
 
+        metrics.update(_segyio.init_cube_metrics(f, ilb, xlb,
+                                                 metrics['trace_count'],
+                                                 metrics['trace0'],
+                                                 metrics['trace_bsize']))
+
         # Happy Path
         iline_indexes = numpy.zeros(metrics['iline_count'], dtype=numpy.intc)
         xline_indexes = numpy.zeros(metrics['xline_count'], dtype=numpy.intc)
@@ -252,7 +267,11 @@ class _segyioTests(TestCase):
         ilb = 189
         xlb = 193
 
-        metrics = _segyio.init_metrics(f, binary_header, ilb, xlb)
+        metrics = _segyio.init_metrics(f, binary_header)
+        metrics.update(_segyio.init_cube_metrics(f, ilb, xlb,
+                                                 metrics['trace_count'],
+                                                 metrics['trace0'],
+                                                 metrics['trace_bsize']))
 
         sorting = metrics['sorting']
         trace_count = metrics['trace_count']
@@ -318,7 +337,7 @@ class _segyioTests(TestCase):
             binary_header = _segyio.read_binaryheader(f)
             ilb = 189
             xlb = 193
-            metrics = _segyio.init_metrics(f, binary_header, ilb, xlb)
+            metrics = _segyio.init_metrics(f, binary_header)
 
             empty = _segyio.empty_traceheader()
 
@@ -384,7 +403,11 @@ class _segyioTests(TestCase):
         ilb = 189
         xlb = 193
 
-        metrics = _segyio.init_metrics(f, binary_header, ilb, xlb)
+        metrics = _segyio.init_metrics(f, binary_header)
+        metrics.update(_segyio.init_cube_metrics(f, ilb, xlb,
+                                                 metrics['trace_count'],
+                                                 metrics['trace0'],
+                                                 metrics['trace_bsize']))
 
         sorting = metrics['sorting']
         trace_count = metrics['trace_count']

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/segyio.git



More information about the debian-science-commits mailing list