Skip to content

Commit 9beec76

Browse files
committed
[DOC] First pass to mik
1 parent 76d3fd1 commit 9beec76

File tree

3 files changed

+117
-82
lines changed

3 files changed

+117
-82
lines changed

examples/examples/real/mik.py

Lines changed: 93 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
"""
2-
Alesmodel: Plotting Sections and Maps
3-
======================================
2+
Unknown Model: Importing Borehole Data and Building a 3D Geological Model with GemPy
3+
====================================================================================
44
"""
55

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.
1210
#
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.
1413
# We will cover:
1514
#
1615
# - Downloading the borehole data
1716
# - Processing the data using `subsurface`
1817
# - 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.
2324
#
2425
# Let's start by downloading the dataset and inspecting its content.
2526

@@ -46,7 +47,6 @@
4647
# Importing GemPy
4748
import gempy as gp
4849

49-
#
5050
# %% md
5151
# We use `pooch` to download the dataset into a temp file:
5252
# %%
@@ -57,9 +57,11 @@
5757
raw_borehole_data_csv = pooch.retrieve(url, known_hash)
5858

5959
# %% 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
6263

64+
# %%
6365
# Read the collar data from the CSV
6466
collar_df: pd.DataFrame = read_collar(
6567
GenericReaderFilesHelper(
@@ -86,18 +88,24 @@
8688
)
8789

8890
# %%
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.
9196
well_mesh = to_pyvista_points(collars.collar_loc)
9297

9398
# Plot the collar points
9499
pv_plot([well_mesh], image_2d=True)
95100

96101
# %%
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.
100107

108+
# %%
101109
# Create the Survey object
102110
survey_df: pd.DataFrame = read_survey(
103111
GenericReaderFilesHelper(
@@ -112,8 +120,11 @@
112120
survey
113121

114122
# %%
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.
117128

118129
# %%
119130
# Your code here:
@@ -134,10 +145,14 @@
134145

135146
lith
136147
# %%
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.
139153
#
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.
141156

142157
# Combine collar and survey into a BoreholeSet
143158
borehole_set = BoreholeSet(
@@ -147,16 +162,16 @@
147162
)
148163

149164
# %%
150-
151165
# Visualize boreholes with pyvista
166+
import matplotlib.pyplot as plt
167+
152168
well_mesh = to_pyvista_line(
153169
line_set=borehole_set.combined_trajectory,
154170
active_scalar="lith_ids",
155171
radius=40
156172
)
157173

158174
p = init_plotter()
159-
import matplotlib.pyplot as plt
160175

161176
# Set colormap for lithologies
162177
boring_cmap = plt.get_cmap(name="viridis", lut=14)
@@ -174,25 +189,21 @@
174189
bold=True
175190
)
176191

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()
187194

188195
# %% md
189-
# ## Structural Elements from Borehole Set
196+
# Structural Elements from Borehole Set
197+
# """""""""""""""""""""""""""""""""""""
190198
#
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.
192202
#
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.
195205

206+
# %%
196207
# Initialize the color generator for formations
197208
colors_generator = gp.data.ColorsGenerator()
198209

@@ -257,13 +268,16 @@
257268

258269

259270
# %% 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.
263275
#
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.
265278
#
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.
267281

268282
# %%
269283
import gempy_viewer as gpv
@@ -274,16 +288,19 @@
274288
elements=elements,
275289
structural_relation=gp.data.StackRelationType.ERODE
276290
)
291+
277292
# Define the structural frame
278293
structural_frame = gp.data.StructuralFrame(
279294
structural_groups=[group],
280295
color_gen=colors_generator
281296
)
282297

283298
# %% 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.
287304

288305
all_surface_points_coords: gp.data.SurfacePointsTable = structural_frame.surface_points_copy
289306
extent_from_data = all_surface_points_coords.xyz.min(axis=0), all_surface_points_coords.xyz.max(axis=0)
@@ -304,9 +321,10 @@
304321
)
305322

306323
# %% 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.
310328

311329
gempy_plot = gpv.plot_3d(
312330
model=geo_model,
@@ -319,9 +337,10 @@
319337
)
320338

321339
# %% 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.
325344

326345
sp_mesh: pyvista.PolyData = gempy_plot.surface_points_mesh
327346

@@ -361,17 +380,20 @@
361380

362381

363382
# %% 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.
367388
#
368389

369390
# %% 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.
375397

376398
# %%
377399
group = gp.data.StructuralGroup(
@@ -409,7 +431,8 @@
409431
# %% md
410432
# ## Model Computation
411433
#
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.
413436

414437
geo_model.interpolation_options
415438

@@ -430,9 +453,12 @@
430453
# -----
431454
# ## Conclusion
432455
#
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.
434459
#
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.
436462
#
437463
# For further reading and resources, check out:
438464

examples/tutorials/a_getting_started/get_started.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
===============
44
55
"""
6+
67
# %%
78
# Welcome to our introductory notebook on GemPy! Here, we will cover the essentials of GemPy, introducing you to the core concepts of
89
# geomodeling and demonstrating how you can leverage these to create your own geological models. We will guide you through building a
910
# model from scratch, based on a conceptual 2D cross-section with boreholes. This simple example will highlight key workflow steps
1011
# and structural features that GemPy can model.
11-
12+
#
1213
# Installation
1314
# """"""""""""
1415
#
@@ -36,6 +37,8 @@
3637
import matplotlib.pyplot as plt
3738
import matplotlib.image as mpimg
3839

40+
# sphinx_gallery_thumbnail_number = 11
41+
3942
# %% md
4043
# Main Classes and Objects in GemPy
4144
# """""""""""""""""""""""""""""""""
@@ -75,20 +78,25 @@
7578
# This object will contain all other data structures and necessary functionality. Here’s what we will do:
7679
#
7780
# 1. **Name the Model**: Assign a name to our model.
81+
#
7882
# 2. **Define Extent**: Specify the extent in x, y, and z. The extent should make sense depending on your use case and should enclose all
79-
# relevant data in a representative space. For this example, we align the extent with the cross-section we imported:
83+
# relevant data in a representative space. For this example, we align the extent with the cross-section we imported:
84+
#
8085
# - **X** is parallel to the section.
81-
# - **Y** is perpendicular. Since we have no data along y, a narrow extent makes sense. We choose an extent of 400, defining it as
82-
# -200 to 200, placing the cross-section at y=0 (in the middle).
83-
# - **Z**, representing depth, takes a negative value since we are modeling the subsurface.
86+
# - **Y** is perpendicular. Since we have no data along y, a narrow extent makes sense. We choose an extent of 400, defining it as -200 to 200,
87+
# placing the cross-section at y=0 (in the middle).
88+
# - **Z** representing depth, takes a negative value since we are modeling the subsurface.
89+
#
8490
# 3. **Initialize Structural Framework**: Set up a default structural framework.
91+
#
8592
# 4. **Define either resolution or refinement**: In GemPy 3, you can use either regular grids or octrees.
8693
# - **Regular grids**: Define a resolution (and refinement=None). A medium resolution of 50x50x50, for example, results in 125,000 voxels.
87-
# Model voxels are prisms, not cubes, so resolution can differ from extent. Avoid exceeding 100 cells in each direction (1,000,000 voxels)
88-
# to prevent high computational costs.
94+
# Model voxels are prisms, not cubes, so resolution can differ from extent. Avoid exceeding 100 cells in each direction (1,000,000 voxels)
95+
# to prevent high computational costs.
8996
# - **Octrees**: Define a level of refinement (and resolution=None). Higher refinement levels increase computational costs.
90-
97+
#
9198
# .. admonition:: Note on choice of modeling grids
99+
#
92100
# Which type of grid is used depends on the use case. Note that as of the current version of GemPy 3,
93101
# the rendering of surfaces uses dual-contouring, which is based on octrees. So even if you choose regular grids, octree-based computing will
94102
# be executed additionally in order to render the surfaces in 3D.

0 commit comments

Comments
 (0)