|
1 | 1 | """
|
2 |
| -Alesmodel: Plotting Sections and Maps |
3 |
| -====================================== |
| 2 | +Unknown Model: Importing Borehole Data and Building a 3D Geological Model with GemPy |
| 3 | +==================================================================================== |
4 | 4 | """
|
5 | 5 |
|
6 |
| -# %% md |
7 |
| -# <img src="https://docs.gempy.org/_static/logos/gempy.png" alt="drawing" width="400"/> |
8 |
| -# |
9 |
| -# # 2.2 From Drillhole Data to GemPy Model |
10 |
| -# |
11 |
| -# In this section, we will explore how to take borehole or drillhole data and convert it into a format compatible with GemPy, creating a 3D geological model. Borehole data is commonly collected in the mining, oil, and gas industries and contains information about subsurface geological formations, including lithologies, stratigraphy, and the geometry of layers or faults. |
| 6 | +# %% |
| 7 | +# In this section, we will explore how to take borehole or drillhole data and convert it into a format compatible with GemPy, |
| 8 | +# creating a 3D geological model. Borehole data is commonly collected in the mining, oil, and gas industries and contains |
| 9 | +# information about subsurface geological formations, including lithologies, stratigraphy, and the geometry of layers or faults. |
12 | 10 | #
|
13 |
| -# For this, we will rely on several helper functions from the `subsurface` package to extract borehole data and translate it into a 3D structure that GemPy can use for modeling. |
| 11 | +# For this, we will rely on several helper functions from the `subsurface` package to extract borehole data and translate it |
| 12 | +# into a 3D structure that GemPy can use for modeling. |
14 | 13 | # We will cover:
|
15 | 14 | #
|
16 | 15 | # - Downloading the borehole data
|
17 | 16 | # - Processing the data using `subsurface`
|
18 | 17 | # - Visualizing borehole locations and lithology data in 3D
|
19 |
| - |
20 |
| -# %% |
21 |
| -# ### Downloading Borehole Data |
22 |
| -# The borehole data is hosted online, and we can use the `pooch` library to download it directly. `pooch` is a library for fetching datasets from the internet. We will download a CSV file that contains the borehole information, including collar positions, survey data, and lithology logs. |
| 18 | +# |
| 19 | +# Downloading Borehole Data |
| 20 | +# """"""""""""""""""""""""" |
| 21 | +# The borehole data is hosted online, and we can use the `pooch` library to download it directly. `pooch` is a library for |
| 22 | +# fetching datasets from the internet. We will download a CSV file that contains the borehole information, including collar |
| 23 | +# positions, survey data, and lithology logs. |
23 | 24 | #
|
24 | 25 | # Let's start by downloading the dataset and inspecting its content.
|
25 | 26 |
|
|
46 | 47 | # Importing GemPy
|
47 | 48 | import gempy as gp
|
48 | 49 |
|
49 |
| -# |
50 | 50 | # %% md
|
51 | 51 | # We use `pooch` to download the dataset into a temp file:
|
52 | 52 | # %%
|
|
57 | 57 | raw_borehole_data_csv = pooch.retrieve(url, known_hash)
|
58 | 58 |
|
59 | 59 | # %% md
|
60 |
| -# Now we can use `subsurface` function to help us reading csv files into pandas dataframes that the package can understand. Since the combination of styles data is provided can highly vary from project to project, `subsurface` provides some *helpers* functions to parse different combination of .csv |
61 |
| -# %% |
| 60 | +# Now we can use `subsurface` function to help us reading csv files into pandas dataframes that the package can understand. |
| 61 | +# Since the combination of styles data is provided can highly vary from project to project, `subsurface` provides some *helpers* |
| 62 | +# functions to parse different combination of .csv |
62 | 63 |
|
| 64 | +# %% |
63 | 65 | # Read the collar data from the CSV
|
64 | 66 | collar_df: pd.DataFrame = read_collar(
|
65 | 67 | GenericReaderFilesHelper(
|
|
86 | 88 | )
|
87 | 89 |
|
88 | 90 | # %%
|
89 |
| -# ### Visualizing the Borehole Collars |
90 |
| -# Once we have the borehole collars data, we can visualize them in 3D using the `pyvista` package. This gives us a good overview of where the boreholes are located in space. In this visualization, each borehole will be represented by a point, and we will label the boreholes using their IDs. |
| 91 | +# Visualizing the Borehole Collars |
| 92 | +# """""""""""""""""""""""""""""""" |
| 93 | +# Once we have the borehole collars data, we can visualize them in 3D using the `pyvista` package. This gives us a good |
| 94 | +# overview of where the boreholes are located in space. In this visualization, each borehole will be represented by a point, |
| 95 | +# and we will label the boreholes using their IDs. |
91 | 96 | well_mesh = to_pyvista_points(collars.collar_loc)
|
92 | 97 |
|
93 | 98 | # Plot the collar points
|
94 | 99 | pv_plot([well_mesh], image_2d=True)
|
95 | 100 |
|
96 | 101 | # %%
|
97 |
| -# ### Reading Borehole Survey Data |
98 |
| -# Borehole surveys give us information about the trajectory of each borehole, including its depth (measured depth). The survey data allows us to compute the full 3D path of each wellbore. We will use the `read_survey` function from `subsurface` to read this data. |
99 |
| -# %% |
| 102 | +# Reading Borehole Survey Data |
| 103 | +# """""""""""""""""""""""""""" |
| 104 | +# Borehole surveys give us information about the trajectory of each borehole, including its depth (measured depth). |
| 105 | +# The survey data allows us to compute the full 3D path of each wellbore. We will use the `read_survey` function from |
| 106 | +# `subsurface` to read this data. |
100 | 107 |
|
| 108 | +# %% |
101 | 109 | # Create the Survey object
|
102 | 110 | survey_df: pd.DataFrame = read_survey(
|
103 | 111 | GenericReaderFilesHelper(
|
|
112 | 120 | survey
|
113 | 121 |
|
114 | 122 | # %%
|
115 |
| -# ### Reading Lithology Data |
116 |
| -# Next, we will read the lithology data. Lithology logs describe the rock type or geological unit encountered at different depths within each borehole. Using `read_lith`, we will extract the lithology data, which includes the top and base depths of each geological formation within the borehole, as well as the formation name. |
| 123 | +# Reading Lithology Data |
| 124 | +# """""""""""""""""""""" |
| 125 | +# Next, we will read the lithology data. Lithology logs describe the rock type or geological unit encountered at different |
| 126 | +# depths within each borehole. Using `read_lith`, we will extract the lithology data, which includes the top and base depths |
| 127 | +# of each geological formation within the borehole, as well as the formation name. |
117 | 128 |
|
118 | 129 | # %%
|
119 | 130 | # Your code here:
|
|
134 | 145 |
|
135 | 146 | lith
|
136 | 147 | # %%
|
137 |
| -# ### Creating a Borehole Set and Visualizing in 3D |
138 |
| -# Now that we have both the collar data and the lithology logs, we can combine them into a `BoreholeSet` object. This object combines the collar, survey, and lithology data and allows us to create a 3D visualization of the borehole trajectories and their associated lithologies. |
| 148 | +# Creating a Borehole Set and Visualizing in 3D |
| 149 | +# """"""""""""""""""""""""""""""""""""""""""""" |
| 150 | +# Now that we have both the collar data and the lithology logs, we can combine them into a `BoreholeSet` object. This object |
| 151 | +# combines the collar, survey, and lithology data and allows us to create a 3D visualization of the borehole trajectories and |
| 152 | +# their associated lithologies. |
139 | 153 | #
|
140 |
| -# We will use `pyvista` to plot the borehole trajectories as lines, and we will color them according to their lithologies. Additionally, we will label the collars for easy identification. |
| 154 | +# We will use `pyvista` to plot the borehole trajectories as lines, and we will color them according to their lithologies. |
| 155 | +# Additionally, we will label the collars for easy identification. |
141 | 156 |
|
142 | 157 | # Combine collar and survey into a BoreholeSet
|
143 | 158 | borehole_set = BoreholeSet(
|
|
147 | 162 | )
|
148 | 163 |
|
149 | 164 | # %%
|
150 |
| - |
151 | 165 | # Visualize boreholes with pyvista
|
| 166 | +import matplotlib.pyplot as plt |
| 167 | + |
152 | 168 | well_mesh = to_pyvista_line(
|
153 | 169 | line_set=borehole_set.combined_trajectory,
|
154 | 170 | active_scalar="lith_ids",
|
155 | 171 | radius=40
|
156 | 172 | )
|
157 | 173 |
|
158 | 174 | p = init_plotter()
|
159 |
| -import matplotlib.pyplot as plt |
160 | 175 |
|
161 | 176 | # Set colormap for lithologies
|
162 | 177 | boring_cmap = plt.get_cmap(name="viridis", lut=14)
|
|
174 | 189 | bold=True
|
175 | 190 | )
|
176 | 191 |
|
177 |
| -# Show 3D plot or save a 2D image depending on the flag |
178 |
| -if plot3D := False: |
179 |
| - p.show() |
180 |
| -else: |
181 |
| - img = p.show(screenshot=True) |
182 |
| - img = p.last_image |
183 |
| - fig = plt.imshow(img) |
184 |
| - plt.axis('off') |
185 |
| - plt.show(block=False) |
186 |
| - p.close() |
| 192 | + |
| 193 | +p.show() |
187 | 194 |
|
188 | 195 | # %% md
|
189 |
| -# ## Structural Elements from Borehole Set |
| 196 | +# Structural Elements from Borehole Set |
| 197 | +# """"""""""""""""""""""""""""""""""""" |
190 | 198 | #
|
191 |
| -# Now that we have successfully imported and visualized the borehole data, we can move on to creating the geological formations (or structural elements) based on the borehole data. Each lithological unit will be associated with a unique identifier and a color, allowing us to distinguish between different formations when we visualize the model. |
| 199 | +# Now that we have successfully imported and visualized the borehole data, we can move on to creating the geological |
| 200 | +# formations (or structural elements) based on the borehole data. Each lithological unit will be associated with a unique |
| 201 | +# identifier and a color, allowing us to distinguish between different formations when we visualize the model. |
192 | 202 | #
|
193 |
| -# GemPy offers the function `gempy.structural_elements_from_borehole_set`, which extracts these structural elements from the borehole data and associates each one with a name, ID, and color. |
194 |
| -# %% |
| 203 | +# GemPy offers the function `gempy.structural_elements_from_borehole_set`, which extracts these structural elements from |
| 204 | +# the borehole data and associates each one with a name, ID, and color. |
195 | 205 |
|
| 206 | +# %% |
196 | 207 | # Initialize the color generator for formations
|
197 | 208 | colors_generator = gp.data.ColorsGenerator()
|
198 | 209 |
|
|
257 | 268 |
|
258 | 269 |
|
259 | 270 | # %% md
|
260 |
| -# ## Initializing the GemPy Model |
261 |
| -# |
262 |
| -# After defining the geological formations, we need to initialize the GemPy model. The first step in this process is to create a `GeoModel` object, which serves as the core container for all data related to the geological model. |
| 271 | +# Initializing the GemPy Model |
| 272 | +# """""""""""""""""""""""""""" |
| 273 | +# After defining the geological formations, we need to initialize the GemPy model. The first step in this process is to |
| 274 | +# create a `GeoModel` object, which serves as the core container for all data related to the geological model. |
263 | 275 | #
|
264 |
| -# We will also define a **regular grid** to interpolate the geological layers. GemPy uses a meshless interpolator to create geological models in 3D space, but grids are convenient for visualization and computation. |
| 276 | +# We will also define a **regular grid** to interpolate the geological layers. GemPy uses a meshless interpolator to |
| 277 | +# create geological models in 3D space, but grids are convenient for visualization and computation. |
265 | 278 | #
|
266 |
| -# GemPy supports various grid types, such as regular grids for visualization, custom grids, topographic grids, and more. For this example, we will use a regular grid with a medium resolution. |
| 279 | +# GemPy supports various grid types, such as regular grids for visualization, custom grids, topographic grids, and more. |
| 280 | +# For this example, we will use a regular grid with a medium resolution. |
267 | 281 |
|
268 | 282 | # %%
|
269 | 283 | import gempy_viewer as gpv
|
|
274 | 288 | elements=elements,
|
275 | 289 | structural_relation=gp.data.StackRelationType.ERODE
|
276 | 290 | )
|
| 291 | + |
277 | 292 | # Define the structural frame
|
278 | 293 | structural_frame = gp.data.StructuralFrame(
|
279 | 294 | structural_groups=[group],
|
280 | 295 | color_gen=colors_generator
|
281 | 296 | )
|
282 | 297 |
|
283 | 298 | # %% md
|
284 |
| -# ### Defining Model Extent and Grid Resolution |
285 |
| -# |
286 |
| -# We now determine the extent of our model based on the surface points provided. This ensures that the grid covers the entire area where the geological data points are located. Additionally, we set a grid resolution of 50x50x50 for a balance between performance and model detail. |
| 299 | +# Defining Model Extent and Grid Resolution |
| 300 | +# """"""""""""""""""""""""""""""""""""""""" |
| 301 | +# We now determine the extent of our model based on the surface points provided. This ensures that the grid covers the |
| 302 | +# entire area where the geological data points are located. Additionally, we set a grid resolution of 50x50x50 for a |
| 303 | +# balance between performance and model detail. |
287 | 304 |
|
288 | 305 | all_surface_points_coords: gp.data.SurfacePointsTable = structural_frame.surface_points_copy
|
289 | 306 | extent_from_data = all_surface_points_coords.xyz.min(axis=0), all_surface_points_coords.xyz.max(axis=0)
|
|
304 | 321 | )
|
305 | 322 |
|
306 | 323 | # %% md
|
307 |
| -# ### 3D Visualization of the Model |
308 |
| -# |
309 |
| -# After initializing the GeoModel, we can proceed to visualize it in 3D using GemPy's `plot_3d` function. This function allows us to see the full 3D geological model with all the defined formations. |
| 324 | +# 3D Visualization of the Model |
| 325 | +# """"""""""""""""""""""""""""" |
| 326 | +# After initializing the GeoModel, we can proceed to visualize it in 3D using GemPy's `plot_3d` function. This function |
| 327 | +# allows us to see the full 3D geological model with all the defined formations. |
310 | 328 |
|
311 | 329 | gempy_plot = gpv.plot_3d(
|
312 | 330 | model=geo_model,
|
|
319 | 337 | )
|
320 | 338 |
|
321 | 339 | # %% md
|
322 |
| -# ### Adding Boreholes and Collars to the Visualization |
323 |
| -# |
324 |
| -# To enhance the 3D model, we can combine the geological formations with the borehole trajectories and collar points that we visualized earlier. This will give us a complete picture of the subsurface, showing both the lithological units and the borehole paths. |
| 340 | +# Adding Boreholes and Collars to the Visualization |
| 341 | +# """"""""""""""""""""""""""""""""""""""""""""""""" |
| 342 | +# To enhance the 3D model, we can combine the geological formations with the borehole trajectories and collar points that we |
| 343 | +# visualized earlier. This will give us a complete picture of the subsurface, showing both the lithological units and the borehole paths. |
325 | 344 |
|
326 | 345 | sp_mesh: pyvista.PolyData = gempy_plot.surface_points_mesh
|
327 | 346 |
|
|
361 | 380 |
|
362 | 381 |
|
363 | 382 | # %% md
|
364 |
| -# ## Step-by-Step Model Building |
365 |
| -# |
366 |
| -# When building a geological model, it's often better to proceed step by step, adding one surface at a time, rather than trying to interpolate all formations at once. This allows for better control over the model and helps avoid potential issues from noisy or irregular data. |
| 383 | +# Step-by-Step Model Building |
| 384 | +# """"""""""""""""""""""""""" |
| 385 | +# When building a geological model, it's often better to proceed step by step, adding one surface at a time, rather |
| 386 | +# than trying to interpolate all formations at once. This allows for better control over the model and helps avoid |
| 387 | +# potential issues from noisy or irregular data. |
367 | 388 | #
|
368 | 389 |
|
369 | 390 | # %% md
|
370 |
| -# ### Surfaces |
371 |
| -# |
372 |
| -# ### Adding Surfaces and Formations |
373 |
| -# |
374 |
| -# In GemPy, surfaces mark the bottom of each geological unit. For our model, we will add the first two formations along with the basement, which always needs to be defined. After this, we can visualize the surfaces in 2D. |
| 391 | +# Surfaces |
| 392 | +# """""""" |
| 393 | +# Adding Surfaces and Formations |
| 394 | +# """"""""""""""""""""""""""""" |
| 395 | +# In GemPy, surfaces mark the bottom of each geological unit. For our model, we will add the first two formations |
| 396 | +# along with the basement, which always needs to be defined. After this, we can visualize the surfaces in 2D. |
375 | 397 |
|
376 | 398 | # %%
|
377 | 399 | group = gp.data.StructuralGroup(
|
|
409 | 431 | # %% md
|
410 | 432 | # ## Model Computation
|
411 | 433 | #
|
412 |
| -# Now that we have the necessary surface points and orientations, we can compute the final geological model. The `compute_model` function will take all the input data and perform the interpolation to generate the 3D subsurface structure. |
| 434 | +# Now that we have the necessary surface points and orientations, we can compute the final geological model. The |
| 435 | +# `compute_model` function will take all the input data and perform the interpolation to generate the 3D subsurface structure. |
413 | 436 |
|
414 | 437 | geo_model.interpolation_options
|
415 | 438 |
|
|
430 | 453 | # -----
|
431 | 454 | # ## Conclusion
|
432 | 455 | #
|
433 |
| -# In this tutorial, we have demonstrated how to take borehole data and create a 3D geological model in GemPy. We explored how to extract structural elements from borehole data, set up a regular grid for interpolation, and visualize the resulting model in both 2D and 3D. |
| 456 | +# In this tutorial, we have demonstrated how to take borehole data and create a 3D geological model in GemPy. We explored |
| 457 | +# how to extract structural elements from borehole data, set up a regular grid for interpolation, and visualize the |
| 458 | +# resulting model in both 2D and 3D. |
434 | 459 | #
|
435 |
| -# GemPy's flexibility allows you to iteratively build models and refine your inputs for more accurate results, and it integrates seamlessly with borehole data for subsurface geological modeling. |
| 460 | +# GemPy's flexibility allows you to iteratively build models and refine your inputs for more accurate results, and it |
| 461 | +# integrates seamlessly with borehole data for subsurface geological modeling. |
436 | 462 | #
|
437 | 463 | # For further reading and resources, check out:
|
438 | 464 |
|
|
0 commit comments