subsurface-terra 2025.1.0rc15__py3-none-any.whl → 2025.1.0rc16__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 (81) 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/_liquid_earth_mesh.py +121 -121
  20. subsurface/core/structs/base_structures/_unstructured_data_constructor.py +70 -70
  21. subsurface/core/structs/base_structures/base_structures_enum.py +6 -6
  22. subsurface/core/structs/base_structures/structured_data.py +282 -282
  23. subsurface/core/structs/base_structures/unstructured_data.py +319 -319
  24. subsurface/core/structs/structured_elements/octree_mesh.py +10 -10
  25. subsurface/core/structs/structured_elements/structured_grid.py +59 -59
  26. subsurface/core/structs/structured_elements/structured_mesh.py +9 -9
  27. subsurface/core/structs/unstructured_elements/__init__.py +3 -3
  28. subsurface/core/structs/unstructured_elements/line_set.py +72 -72
  29. subsurface/core/structs/unstructured_elements/point_set.py +43 -43
  30. subsurface/core/structs/unstructured_elements/tetrahedron_mesh.py +35 -35
  31. subsurface/core/structs/unstructured_elements/triangular_surface.py +62 -62
  32. subsurface/core/utils/utils_core.py +38 -38
  33. subsurface/modules/reader/__init__.py +13 -13
  34. subsurface/modules/reader/faults/faults.py +80 -80
  35. subsurface/modules/reader/from_binary.py +46 -46
  36. subsurface/modules/reader/mesh/_GOCAD_mesh.py +82 -82
  37. subsurface/modules/reader/mesh/_trimesh_reader.py +447 -447
  38. subsurface/modules/reader/mesh/csv_mesh_reader.py +53 -53
  39. subsurface/modules/reader/mesh/dxf_reader.py +177 -177
  40. subsurface/modules/reader/mesh/glb_reader.py +30 -30
  41. subsurface/modules/reader/mesh/mx_reader.py +232 -232
  42. subsurface/modules/reader/mesh/obj_reader.py +53 -53
  43. subsurface/modules/reader/mesh/omf_mesh_reader.py +43 -43
  44. subsurface/modules/reader/mesh/surface_reader.py +56 -56
  45. subsurface/modules/reader/mesh/surfaces_api.py +41 -41
  46. subsurface/modules/reader/profiles/__init__.py +3 -3
  47. subsurface/modules/reader/profiles/profiles_core.py +197 -197
  48. subsurface/modules/reader/read_netcdf.py +38 -38
  49. subsurface/modules/reader/topography/__init__.py +7 -7
  50. subsurface/modules/reader/topography/topo_core.py +100 -100
  51. subsurface/modules/reader/volume/read_grav3d.py +478 -428
  52. subsurface/modules/reader/volume/read_volume.py +327 -230
  53. subsurface/modules/reader/volume/segy_reader.py +105 -105
  54. subsurface/modules/reader/volume/seismic.py +173 -173
  55. subsurface/modules/reader/volume/volume_utils.py +43 -43
  56. subsurface/modules/reader/wells/DEP/__init__.py +43 -43
  57. subsurface/modules/reader/wells/DEP/_well_files_reader.py +167 -167
  58. subsurface/modules/reader/wells/DEP/_wells_api.py +61 -61
  59. subsurface/modules/reader/wells/DEP/_welly_reader.py +180 -180
  60. subsurface/modules/reader/wells/DEP/pandas_to_welly.py +212 -212
  61. subsurface/modules/reader/wells/_read_to_df.py +57 -57
  62. subsurface/modules/reader/wells/read_borehole_interface.py +148 -148
  63. subsurface/modules/reader/wells/wells_utils.py +68 -68
  64. subsurface/modules/tools/mocking_aux.py +104 -104
  65. subsurface/modules/visualization/__init__.py +2 -2
  66. subsurface/modules/visualization/to_pyvista.py +320 -320
  67. subsurface/modules/writer/to_binary.py +12 -12
  68. subsurface/modules/writer/to_rex/common.py +78 -78
  69. subsurface/modules/writer/to_rex/data_struct.py +74 -74
  70. subsurface/modules/writer/to_rex/gempy_to_rexfile.py +791 -791
  71. subsurface/modules/writer/to_rex/material_encoder.py +44 -44
  72. subsurface/modules/writer/to_rex/mesh_encoder.py +152 -152
  73. subsurface/modules/writer/to_rex/to_rex.py +115 -115
  74. subsurface/modules/writer/to_rex/utils.py +15 -15
  75. subsurface/optional_requirements.py +116 -116
  76. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/METADATA +194 -194
  77. subsurface_terra-2025.1.0rc16.dist-info/RECORD +98 -0
  78. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/WHEEL +1 -1
  79. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc16.dist-info}/licenses/LICENSE +203 -203
  80. subsurface_terra-2025.1.0rc15.dist-info/RECORD +0 -98
  81. {subsurface_terra-2025.1.0rc15.dist-info → subsurface_terra-2025.1.0rc16.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