subsurface-terra 2025.1.0rc15__py3-none-any.whl → 2025.1.0rc17__py3-none-any.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.
Files changed (82) hide show
  1. subsurface/__init__.py +31 -31
  2. subsurface/_version.py +34 -21
  3. subsurface/api/__init__.py +13 -13
  4. subsurface/api/interfaces/__init__.py +3 -3
  5. subsurface/api/interfaces/stream.py +136 -136
  6. subsurface/api/reader/read_wells.py +78 -78
  7. subsurface/core/geological_formats/boreholes/_combine_trajectories.py +117 -117
  8. subsurface/core/geological_formats/boreholes/_map_attrs_to_survey.py +236 -234
  9. subsurface/core/geological_formats/boreholes/_survey_to_unstruct.py +163 -163
  10. subsurface/core/geological_formats/boreholes/boreholes.py +140 -140
  11. subsurface/core/geological_formats/boreholes/collars.py +26 -26
  12. subsurface/core/geological_formats/boreholes/survey.py +86 -86
  13. subsurface/core/geological_formats/fault.py +47 -47
  14. subsurface/core/reader_helpers/reader_unstruct.py +11 -11
  15. subsurface/core/reader_helpers/readers_data.py +130 -130
  16. subsurface/core/reader_helpers/readers_wells.py +13 -13
  17. subsurface/core/structs/__init__.py +3 -3
  18. subsurface/core/structs/base_structures/__init__.py +2 -2
  19. subsurface/core/structs/base_structures/_aux.py +69 -0
  20. subsurface/core/structs/base_structures/_liquid_earth_mesh.py +121 -121
  21. subsurface/core/structs/base_structures/_unstructured_data_constructor.py +70 -70
  22. subsurface/core/structs/base_structures/base_structures_enum.py +6 -6
  23. subsurface/core/structs/base_structures/structured_data.py +282 -282
  24. subsurface/core/structs/base_structures/unstructured_data.py +338 -319
  25. subsurface/core/structs/structured_elements/octree_mesh.py +10 -10
  26. subsurface/core/structs/structured_elements/structured_grid.py +59 -59
  27. subsurface/core/structs/structured_elements/structured_mesh.py +9 -9
  28. subsurface/core/structs/unstructured_elements/__init__.py +3 -3
  29. subsurface/core/structs/unstructured_elements/line_set.py +72 -72
  30. subsurface/core/structs/unstructured_elements/point_set.py +43 -43
  31. subsurface/core/structs/unstructured_elements/tetrahedron_mesh.py +35 -35
  32. subsurface/core/structs/unstructured_elements/triangular_surface.py +62 -62
  33. subsurface/core/utils/utils_core.py +38 -38
  34. subsurface/modules/reader/__init__.py +13 -13
  35. subsurface/modules/reader/faults/faults.py +80 -80
  36. subsurface/modules/reader/from_binary.py +46 -46
  37. subsurface/modules/reader/mesh/_GOCAD_mesh.py +82 -82
  38. subsurface/modules/reader/mesh/_trimesh_reader.py +447 -447
  39. subsurface/modules/reader/mesh/csv_mesh_reader.py +53 -53
  40. subsurface/modules/reader/mesh/dxf_reader.py +177 -177
  41. subsurface/modules/reader/mesh/glb_reader.py +30 -30
  42. subsurface/modules/reader/mesh/mx_reader.py +232 -232
  43. subsurface/modules/reader/mesh/obj_reader.py +53 -53
  44. subsurface/modules/reader/mesh/omf_mesh_reader.py +43 -43
  45. subsurface/modules/reader/mesh/surface_reader.py +56 -56
  46. subsurface/modules/reader/mesh/surfaces_api.py +41 -41
  47. subsurface/modules/reader/profiles/__init__.py +3 -3
  48. subsurface/modules/reader/profiles/profiles_core.py +197 -197
  49. subsurface/modules/reader/read_netcdf.py +38 -38
  50. subsurface/modules/reader/topography/__init__.py +7 -7
  51. subsurface/modules/reader/topography/topo_core.py +100 -100
  52. subsurface/modules/reader/volume/read_grav3d.py +447 -428
  53. subsurface/modules/reader/volume/read_volume.py +327 -230
  54. subsurface/modules/reader/volume/segy_reader.py +105 -105
  55. subsurface/modules/reader/volume/seismic.py +173 -173
  56. subsurface/modules/reader/volume/volume_utils.py +43 -43
  57. subsurface/modules/reader/wells/DEP/__init__.py +43 -43
  58. subsurface/modules/reader/wells/DEP/_well_files_reader.py +167 -167
  59. subsurface/modules/reader/wells/DEP/_wells_api.py +61 -61
  60. subsurface/modules/reader/wells/DEP/_welly_reader.py +180 -180
  61. subsurface/modules/reader/wells/DEP/pandas_to_welly.py +212 -212
  62. subsurface/modules/reader/wells/_read_to_df.py +57 -57
  63. subsurface/modules/reader/wells/read_borehole_interface.py +148 -148
  64. subsurface/modules/reader/wells/wells_utils.py +68 -68
  65. subsurface/modules/tools/mocking_aux.py +104 -104
  66. subsurface/modules/visualization/__init__.py +2 -2
  67. subsurface/modules/visualization/to_pyvista.py +320 -320
  68. subsurface/modules/writer/to_binary.py +12 -12
  69. subsurface/modules/writer/to_rex/common.py +78 -78
  70. subsurface/modules/writer/to_rex/data_struct.py +74 -74
  71. subsurface/modules/writer/to_rex/gempy_to_rexfile.py +791 -791
  72. subsurface/modules/writer/to_rex/material_encoder.py +44 -44
  73. subsurface/modules/writer/to_rex/mesh_encoder.py +152 -152
  74. subsurface/modules/writer/to_rex/to_rex.py +115 -115
  75. subsurface/modules/writer/to_rex/utils.py +15 -15
  76. subsurface/optional_requirements.py +116 -116
  77. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/METADATA +194 -194
  78. subsurface_terra-2025.1.0rc17.dist-info/RECORD +99 -0
  79. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/WHEEL +1 -1
  80. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/licenses/LICENSE +203 -203
  81. subsurface_terra-2025.1.0rc15.dist-info/RECORD +0 -98
  82. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc17.dist-info}/top_level.txt +0 -0
@@ -1,105 +1,105 @@
1
- from typing import Union
2
-
3
- from subsurface import optional_requirements
4
- from ....core.structs.base_structures import StructuredData
5
- import numpy as np
6
-
7
-
8
- def read_in_segy(filepath: str, ignore_geometry: bool = True, flip_y_axis: bool = True) -> StructuredData:
9
- """
10
- Reads a SEG-Y file and processes its data into a structured format.
11
-
12
- This function opens a SEG-Y file using the `segyio` library and extracts
13
- the trace data. The data is optionally flipped along the y-axis and its
14
- axes are rearranged before being converted into a `StructuredData` object.
15
- The `ignore_geometry` option determines whether SEG-Y header geometry
16
- information is considered during file read.
17
-
18
- Args:
19
- filepath (str): The path to the SEG-Y file to be read.
20
- ignore_geometry (bool): Whether to ignore the geometry information
21
- from the SEG-Y header. Defaults to True.
22
- flip_y_axis (bool): Whether to flip the data vertically (on the y-axis).
23
- Defaults to True.
24
-
25
- Returns:
26
- StructuredData: An instance of `StructuredData` containing the
27
- processed trace data from the SEG-Y file.
28
-
29
- Raises:
30
- IOError: If there is an error in opening or reading the SEG-Y file.
31
- ValueError: If the data cannot be processed into the expected format.
32
- """
33
- segyio = optional_requirements.require_segyio()
34
- segyfile = segyio.open(filepath, ignore_geometry=ignore_geometry)
35
-
36
- data = np.asarray([np.copy(tr) for tr in segyfile.trace[:]])
37
-
38
- if flip_y_axis:
39
- data = np.flip(data, axis=1)
40
-
41
- data = np.swapaxes(data, 0, 1)
42
-
43
- sd = StructuredData.from_numpy(data) # data holds traces * (samples per trace) values
44
- segyfile.close()
45
- return sd
46
-
47
-
48
- def create_mesh_from_coords(coords: dict, zmin: Union[float, int], zmax: Union[float, int] = 0.0):
49
- """Creates a mesh for plotting StructuredData
50
-
51
- Args:
52
- coords (Union[dict, LineString]): the x and y, i.e. latitude and longitude, location of the traces of the seismic profile
53
- zmax (float): the maximum elevation of the seismic profile, by default 0.0
54
- zmin (float): the location in z where the lowest sample was taken
55
-
56
- Returns: vertices and faces for creating an UnstructuredData object
57
-
58
- """
59
- n = len(coords['x'])
60
- coords = np.array([coords['x'], coords['y']]).T
61
- # duplicating the line, once with z=lower and another with z=upper values
62
- vertices = np.zeros((2 * n, 3))
63
- vertices[:n, :2] = coords
64
- vertices[:n, 2] = zmin
65
- vertices[n:, :2] = coords
66
- vertices[n:, 2] = zmax
67
- # i+n --- i+n+1
68
- # |\ |
69
- # | \ |
70
- # | \ |
71
- # | \ |
72
- # i --- i+1
73
-
74
- scipy = optional_requirements.require_scipy()
75
- tri = scipy.spatial.qhull.Delaunay(vertices[:, [0, 2]])
76
- faces = tri.simplices
77
- return vertices, faces
78
-
79
-
80
- def apply_colormap_to_texture(texture: StructuredData, cmap_name="bwr"):
81
- """
82
- Convert a single-channel seismic texture.data array into RGB
83
- using a Matplotlib colormap (e.g., 'bwr', 'jet', 'RdBu_r', etc.).
84
- """
85
- # 'texture.data' should be a 2D array of amplitudes: shape = (height, width)
86
- import matplotlib.colors as colors
87
- import matplotlib.pyplot as plt
88
- data = texture.values
89
- # 1. Normalize data to [0,1] range based on its min/max
90
- min_val, max_val = data.min(), data.max()
91
- norm = colors.Normalize(vmin=min_val, vmax=max_val)
92
-
93
- norm = colors.Normalize(vmin=-6, vmax=6)
94
-
95
- # 2. Get a Matplotlib colormap
96
- cmap = plt.get_cmap(cmap_name)
97
-
98
- # 3. Map normalized data -> RGBA, shape becomes (height, width, 4)
99
- rgba_data = cmap(norm(data))
100
-
101
- # 4. Convert from float in [0,1] to uint8 in [0,255], and drop alpha channel
102
- rgb_data = (rgba_data[..., :3] * 255).astype(np.uint8)
103
-
104
- texture = StructuredData.from_numpy(rgb_data)
105
- return texture
1
+ from typing import Union
2
+
3
+ from subsurface import optional_requirements
4
+ from ....core.structs.base_structures import StructuredData
5
+ import numpy as np
6
+
7
+
8
+ def read_in_segy(filepath: str, ignore_geometry: bool = True, flip_y_axis: bool = True) -> StructuredData:
9
+ """
10
+ Reads a SEG-Y file and processes its data into a structured format.
11
+
12
+ This function opens a SEG-Y file using the `segyio` library and extracts
13
+ the trace data. The data is optionally flipped along the y-axis and its
14
+ axes are rearranged before being converted into a `StructuredData` object.
15
+ The `ignore_geometry` option determines whether SEG-Y header geometry
16
+ information is considered during file read.
17
+
18
+ Args:
19
+ filepath (str): The path to the SEG-Y file to be read.
20
+ ignore_geometry (bool): Whether to ignore the geometry information
21
+ from the SEG-Y header. Defaults to True.
22
+ flip_y_axis (bool): Whether to flip the data vertically (on the y-axis).
23
+ Defaults to True.
24
+
25
+ Returns:
26
+ StructuredData: An instance of `StructuredData` containing the
27
+ processed trace data from the SEG-Y file.
28
+
29
+ Raises:
30
+ IOError: If there is an error in opening or reading the SEG-Y file.
31
+ ValueError: If the data cannot be processed into the expected format.
32
+ """
33
+ segyio = optional_requirements.require_segyio()
34
+ segyfile = segyio.open(filepath, ignore_geometry=ignore_geometry)
35
+
36
+ data = np.asarray([np.copy(tr) for tr in segyfile.trace[:]])
37
+
38
+ if flip_y_axis:
39
+ data = np.flip(data, axis=1)
40
+
41
+ data = np.swapaxes(data, 0, 1)
42
+
43
+ sd = StructuredData.from_numpy(data) # data holds traces * (samples per trace) values
44
+ segyfile.close()
45
+ return sd
46
+
47
+
48
+ def create_mesh_from_coords(coords: dict, zmin: Union[float, int], zmax: Union[float, int] = 0.0):
49
+ """Creates a mesh for plotting StructuredData
50
+
51
+ Args:
52
+ coords (Union[dict, LineString]): the x and y, i.e. latitude and longitude, location of the traces of the seismic profile
53
+ zmax (float): the maximum elevation of the seismic profile, by default 0.0
54
+ zmin (float): the location in z where the lowest sample was taken
55
+
56
+ Returns: vertices and faces for creating an UnstructuredData object
57
+
58
+ """
59
+ n = len(coords['x'])
60
+ coords = np.array([coords['x'], coords['y']]).T
61
+ # duplicating the line, once with z=lower and another with z=upper values
62
+ vertices = np.zeros((2 * n, 3))
63
+ vertices[:n, :2] = coords
64
+ vertices[:n, 2] = zmin
65
+ vertices[n:, :2] = coords
66
+ vertices[n:, 2] = zmax
67
+ # i+n --- i+n+1
68
+ # |\ |
69
+ # | \ |
70
+ # | \ |
71
+ # | \ |
72
+ # i --- i+1
73
+
74
+ scipy = optional_requirements.require_scipy()
75
+ tri = scipy.spatial.qhull.Delaunay(vertices[:, [0, 2]])
76
+ faces = tri.simplices
77
+ return vertices, faces
78
+
79
+
80
+ def apply_colormap_to_texture(texture: StructuredData, cmap_name="bwr"):
81
+ """
82
+ Convert a single-channel seismic texture.data array into RGB
83
+ using a Matplotlib colormap (e.g., 'bwr', 'jet', 'RdBu_r', etc.).
84
+ """
85
+ # 'texture.data' should be a 2D array of amplitudes: shape = (height, width)
86
+ import matplotlib.colors as colors
87
+ import matplotlib.pyplot as plt
88
+ data = texture.values
89
+ # 1. Normalize data to [0,1] range based on its min/max
90
+ min_val, max_val = data.min(), data.max()
91
+ norm = colors.Normalize(vmin=min_val, vmax=max_val)
92
+
93
+ norm = colors.Normalize(vmin=-6, vmax=6)
94
+
95
+ # 2. Get a Matplotlib colormap
96
+ cmap = plt.get_cmap(cmap_name)
97
+
98
+ # 3. Map normalized data -> RGBA, shape becomes (height, width, 4)
99
+ rgba_data = cmap(norm(data))
100
+
101
+ # 4. Convert from float in [0,1] to uint8 in [0,255], and drop alpha channel
102
+ rgb_data = (rgba_data[..., :3] * 255).astype(np.uint8)
103
+
104
+ texture = StructuredData.from_numpy(rgb_data)
105
+ return texture
@@ -1,173 +1,173 @@
1
- """
2
- TODO: This is legacy code waiting to be updated to the new ideas
3
-
4
- """
5
-
6
- import xarray as xr
7
- import numpy as np
8
-
9
- from subsurface import optional_requirements
10
-
11
-
12
- class Seismic:
13
- def __init__(self, data: np.ndarray, *args, **kwargs):
14
- """Seismic data object based on xarray.DataArray.
15
-
16
- Args:
17
- data (Array): np.ndarray of the seismic cube / section.
18
- """
19
- self.n_shp = len(data.shape)
20
- self._xarray = xr.DataArray(self._flip(data), *args, **kwargs)
21
-
22
- def __getattr__(self, attr):
23
- if attr in self.__dict__:
24
- return getattr(self, attr)
25
- return getattr(self._xarray, attr)
26
-
27
- def __getitem__(self, item):
28
- if isinstance(item, str):
29
- return self._xarray._getitem_coord(item)
30
-
31
- # preserve coordinates
32
- cp = list(self._xarray.coords.items()) # parent coordinates
33
- coords = [(cp[i]) for i, it in enumerate(item) if not type(it) == int]
34
- return Seismic(self._xarray[item].data, coords=coords)
35
-
36
- def __repr__(self):
37
- return self._xarray.__repr__()
38
-
39
- def __str__(self):
40
- return "Seismic"
41
-
42
- def _flip(self, data: np.ndarray) -> np.ndarray:
43
- """Flip SEGY data to fit with numpy array orientation.
44
-
45
- Args:
46
- data (np.ndarray): Seismic data.
47
-
48
- Returns:
49
- (np.ndarray) Flipped seismic data.
50
- """
51
- if self.n_shp == 1:
52
- return data
53
- if self.n_shp == 2:
54
- return data
55
- if self.n_shp == 3:
56
- return np.flip(data, axis=2)
57
-
58
- def add_coords(self):
59
- """Ability to easily add physical coordinates."""
60
- raise NotImplementedError
61
-
62
- def to_segy(self, filepath: str) -> None:
63
- """Write given Seismic to SEGY file using segyio.tools.from_array().
64
-
65
- Args:
66
- filepath (str): Filepath for SEGY file.
67
- """
68
- segyio = optional_requirements.require_segyio()
69
- segyio.tools.from_array(filepath, self._xarray.data)
70
-
71
- def plot_(self):
72
- return xr.plot.plot._PlotMethods(self)
73
-
74
- def plot(self):
75
- if self.n_shp == 1:
76
- return _plot_1d(self)
77
- elif self.n_shp == 2:
78
- pass
79
- # TODO: plot seismic section using imshow for correct orientation
80
- elif self.n_shp >= 3:
81
- _plot_3d(self)
82
-
83
- def create_pyvista_grid(self) -> 'pyvista.grid.UniformGrid':
84
- """Generate UniformGrid object for 3D plotting of the seismic.
85
-
86
- Args:
87
- seismic (Seismic): Seismic object.
88
-
89
- Returns:
90
- (pv.grid.UniformGrid)
91
- """
92
- pv = optional_requirements.require_pyvista()
93
- grid = pv.UniformGrid()
94
- grid.spacing = (1, 1, 1) # TODO: cell sizes? vertical exaggeration etc
95
- grid.dimensions = np.array(self.data.shape) + 1
96
- grid.cell_data["values"] = self.data.flatten(order="F")
97
- # TODO: correct orientation of cube
98
- return grid
99
-
100
- def plot_3d_slices(self):
101
- pv = optional_requirements.require_pyvista()
102
- if self.n_shp != 3:
103
- raise AssertionError("Seismic data needs to be a 3-D volume.")
104
- # TODO: cmap, kwarg passthrough
105
- pv.OrthogonalSlicer(self.grid)
106
-
107
-
108
- def _plot_1d(seismic: Seismic, linekwargs={}, fillkwargs={}):
109
- import matplotlib.pyplot as plt
110
- fig, ax = plt.subplots(figsize=(2,8))
111
-
112
- lkwargs = dict(
113
- color="black",
114
- linewidth=1
115
- )
116
- lkwargs.update(linekwargs)
117
-
118
- fkwargs = dict(
119
- color="grey"
120
- )
121
- fkwargs.update(fillkwargs)
122
-
123
- y = np.arange(0, *seismic.data.shape)
124
- ax.plot(seismic.data, y, **lkwargs)
125
- x1 = seismic.data.copy()
126
- x1[x1<=0] = 0
127
- ax.fill_betweenx(y, x1=x1, **fkwargs)
128
- return ax
129
-
130
-
131
- def _plot_3d(seismic: Seismic):
132
- if not hasattr(seismic, "grid"):
133
- seismic.grid = seismic.create_pyvista_grid()
134
-
135
- seismic.grid.plot(cmap="seismic")
136
-
137
-
138
- def _plot_2d(seismic: Seismic):
139
- pass
140
-
141
-
142
- def _plot_hist(seismic: Seismic):
143
- pass
144
-
145
-
146
- def from_segy(filepath:str, coords=None) -> Seismic:
147
- """Create a Seismic data object from a SEGY file.
148
-
149
- Args:
150
- filepath (str): Filepath to the SEGY file.
151
-
152
- Returns:
153
- Seismic: Seismic data object based on xarray.DataArray.
154
- """
155
- segyio = optional_requirements.require_segyio()
156
- with segyio.open(filepath) as sf:
157
- sf.mmap() # memory mapping
158
- xlines = sf.xlines
159
- ilines = sf.ilines
160
- samples = sf.samples
161
- header = sf.bin
162
-
163
- if not coords:
164
- coords = [
165
- ("ilines", ilines),
166
- ("xlines", xlines),
167
- ("samples", samples)
168
- ]
169
-
170
- cube = segyio.tools.cube(filepath)
171
- seismic = Seismic(cube, coords=coords)
172
- seismic.header = header
173
- return seismic
1
+ """
2
+ TODO: This is legacy code waiting to be updated to the new ideas
3
+
4
+ """
5
+
6
+ import xarray as xr
7
+ import numpy as np
8
+
9
+ from subsurface import optional_requirements
10
+
11
+
12
+ class Seismic:
13
+ def __init__(self, data: np.ndarray, *args, **kwargs):
14
+ """Seismic data object based on xarray.DataArray.
15
+
16
+ Args:
17
+ data (Array): np.ndarray of the seismic cube / section.
18
+ """
19
+ self.n_shp = len(data.shape)
20
+ self._xarray = xr.DataArray(self._flip(data), *args, **kwargs)
21
+
22
+ def __getattr__(self, attr):
23
+ if attr in self.__dict__:
24
+ return getattr(self, attr)
25
+ return getattr(self._xarray, attr)
26
+
27
+ def __getitem__(self, item):
28
+ if isinstance(item, str):
29
+ return self._xarray._getitem_coord(item)
30
+
31
+ # preserve coordinates
32
+ cp = list(self._xarray.coords.items()) # parent coordinates
33
+ coords = [(cp[i]) for i, it in enumerate(item) if not type(it) == int]
34
+ return Seismic(self._xarray[item].data, coords=coords)
35
+
36
+ def __repr__(self):
37
+ return self._xarray.__repr__()
38
+
39
+ def __str__(self):
40
+ return "Seismic"
41
+
42
+ def _flip(self, data: np.ndarray) -> np.ndarray:
43
+ """Flip SEGY data to fit with numpy array orientation.
44
+
45
+ Args:
46
+ data (np.ndarray): Seismic data.
47
+
48
+ Returns:
49
+ (np.ndarray) Flipped seismic data.
50
+ """
51
+ if self.n_shp == 1:
52
+ return data
53
+ if self.n_shp == 2:
54
+ return data
55
+ if self.n_shp == 3:
56
+ return np.flip(data, axis=2)
57
+
58
+ def add_coords(self):
59
+ """Ability to easily add physical coordinates."""
60
+ raise NotImplementedError
61
+
62
+ def to_segy(self, filepath: str) -> None:
63
+ """Write given Seismic to SEGY file using segyio.tools.from_array().
64
+
65
+ Args:
66
+ filepath (str): Filepath for SEGY file.
67
+ """
68
+ segyio = optional_requirements.require_segyio()
69
+ segyio.tools.from_array(filepath, self._xarray.data)
70
+
71
+ def plot_(self):
72
+ return xr.plot.plot._PlotMethods(self)
73
+
74
+ def plot(self):
75
+ if self.n_shp == 1:
76
+ return _plot_1d(self)
77
+ elif self.n_shp == 2:
78
+ pass
79
+ # TODO: plot seismic section using imshow for correct orientation
80
+ elif self.n_shp >= 3:
81
+ _plot_3d(self)
82
+
83
+ def create_pyvista_grid(self) -> 'pyvista.grid.UniformGrid':
84
+ """Generate UniformGrid object for 3D plotting of the seismic.
85
+
86
+ Args:
87
+ seismic (Seismic): Seismic object.
88
+
89
+ Returns:
90
+ (pv.grid.UniformGrid)
91
+ """
92
+ pv = optional_requirements.require_pyvista()
93
+ grid = pv.UniformGrid()
94
+ grid.spacing = (1, 1, 1) # TODO: cell sizes? vertical exaggeration etc
95
+ grid.dimensions = np.array(self.data.shape) + 1
96
+ grid.cell_data["values"] = self.data.flatten(order="F")
97
+ # TODO: correct orientation of cube
98
+ return grid
99
+
100
+ def plot_3d_slices(self):
101
+ pv = optional_requirements.require_pyvista()
102
+ if self.n_shp != 3:
103
+ raise AssertionError("Seismic data needs to be a 3-D volume.")
104
+ # TODO: cmap, kwarg passthrough
105
+ pv.OrthogonalSlicer(self.grid)
106
+
107
+
108
+ def _plot_1d(seismic: Seismic, linekwargs={}, fillkwargs={}):
109
+ import matplotlib.pyplot as plt
110
+ fig, ax = plt.subplots(figsize=(2,8))
111
+
112
+ lkwargs = dict(
113
+ color="black",
114
+ linewidth=1
115
+ )
116
+ lkwargs.update(linekwargs)
117
+
118
+ fkwargs = dict(
119
+ color="grey"
120
+ )
121
+ fkwargs.update(fillkwargs)
122
+
123
+ y = np.arange(0, *seismic.data.shape)
124
+ ax.plot(seismic.data, y, **lkwargs)
125
+ x1 = seismic.data.copy()
126
+ x1[x1<=0] = 0
127
+ ax.fill_betweenx(y, x1=x1, **fkwargs)
128
+ return ax
129
+
130
+
131
+ def _plot_3d(seismic: Seismic):
132
+ if not hasattr(seismic, "grid"):
133
+ seismic.grid = seismic.create_pyvista_grid()
134
+
135
+ seismic.grid.plot(cmap="seismic")
136
+
137
+
138
+ def _plot_2d(seismic: Seismic):
139
+ pass
140
+
141
+
142
+ def _plot_hist(seismic: Seismic):
143
+ pass
144
+
145
+
146
+ def from_segy(filepath:str, coords=None) -> Seismic:
147
+ """Create a Seismic data object from a SEGY file.
148
+
149
+ Args:
150
+ filepath (str): Filepath to the SEGY file.
151
+
152
+ Returns:
153
+ Seismic: Seismic data object based on xarray.DataArray.
154
+ """
155
+ segyio = optional_requirements.require_segyio()
156
+ with segyio.open(filepath) as sf:
157
+ sf.mmap() # memory mapping
158
+ xlines = sf.xlines
159
+ ilines = sf.ilines
160
+ samples = sf.samples
161
+ header = sf.bin
162
+
163
+ if not coords:
164
+ coords = [
165
+ ("ilines", ilines),
166
+ ("xlines", xlines),
167
+ ("samples", samples)
168
+ ]
169
+
170
+ cube = segyio.tools.cube(filepath)
171
+ seismic = Seismic(cube, coords=coords)
172
+ seismic.header = header
173
+ return seismic
@@ -1,43 +1,43 @@
1
- import enum
2
- from typing import List
3
- import numpy as np
4
- from subsurface.optional_requirements import require_scipy
5
-
6
- from ....core.structs import UnstructuredData, StructuredData
7
-
8
- __all__ = ['interpolate_unstructured_data_to_structured_data', ]
9
-
10
-
11
- class InterpolationMethod(enum.Enum):
12
- linear = "linear"
13
- nearest = "nearest"
14
-
15
-
16
- def interpolate_unstructured_data_to_structured_data(
17
- ud: UnstructuredData, attr_name: str,
18
- resolution: List[int] = None,
19
- interpolation_method: InterpolationMethod = InterpolationMethod.nearest) -> StructuredData:
20
- if resolution is None:
21
- resolution = [50, 50, 50]
22
- boundaries_max = ud.vertex.max(axis=0)
23
- boundaries_min = ud.vertex.min(axis=0)
24
- coords = dict()
25
- dims = ['x', 'y', 'z']
26
- for e, i in enumerate(dims):
27
- coords[i] = np.linspace(boundaries_min[e], boundaries_max[e], resolution[e], endpoint=False)
28
-
29
- grid = np.meshgrid(*coords.values())
30
- scipy = require_scipy()
31
- interpolated_attributes = scipy.interpolate.griddata(
32
- points=ud.vertex,
33
- values=ud.attributes.loc[:, attr_name],
34
- xi=tuple(grid),
35
- method=interpolation_method.value
36
- )
37
-
38
- sd = StructuredData.from_numpy(
39
- array=interpolated_attributes,
40
- data_array_name=attr_name,
41
- coords=coords
42
- )
43
- return sd
1
+ import enum
2
+ from typing import List
3
+ import numpy as np
4
+ from subsurface.optional_requirements import require_scipy
5
+
6
+ from ....core.structs import UnstructuredData, StructuredData
7
+
8
+ __all__ = ['interpolate_unstructured_data_to_structured_data', ]
9
+
10
+
11
+ class InterpolationMethod(enum.Enum):
12
+ linear = "linear"
13
+ nearest = "nearest"
14
+
15
+
16
+ def interpolate_unstructured_data_to_structured_data(
17
+ ud: UnstructuredData, attr_name: str,
18
+ resolution: List[int] = None,
19
+ interpolation_method: InterpolationMethod = InterpolationMethod.nearest) -> StructuredData:
20
+ if resolution is None:
21
+ resolution = [50, 50, 50]
22
+ boundaries_max = ud.vertex.max(axis=0)
23
+ boundaries_min = ud.vertex.min(axis=0)
24
+ coords = dict()
25
+ dims = ['x', 'y', 'z']
26
+ for e, i in enumerate(dims):
27
+ coords[i] = np.linspace(boundaries_min[e], boundaries_max[e], resolution[e], endpoint=False)
28
+
29
+ grid = np.meshgrid(*coords.values())
30
+ scipy = require_scipy()
31
+ interpolated_attributes = scipy.interpolate.griddata(
32
+ points=ud.vertex,
33
+ values=ud.attributes.loc[:, attr_name],
34
+ xi=tuple(grid),
35
+ method=interpolation_method.value
36
+ )
37
+
38
+ sd = StructuredData.from_numpy(
39
+ array=interpolated_attributes,
40
+ data_array_name=attr_name,
41
+ coords=coords
42
+ )
43
+ return sd