Skip to content

Commit a002410

Browse files
authored
[META] Added openpyxl as requirenment for docs and enhance example (#1007)
# Description Enhanced the horizontal stratigraphic example with comprehensive documentation and additional functionality. The improvements include: - Added detailed docstrings explaining the purpose of each code section - Implemented functions to extract mesh vertices and normals from the geological model - Added capability to export model components to VTK files - Created functionality to save mesh data to Excel files and convert to XYZ format - Improved code organization with clear section headers - Added openpyxl to docs requirements for Excel file handling Relates to geological model visualization and data export
2 parents e13bc89 + a227afe commit a002410

File tree

2 files changed

+128
-44
lines changed

2 files changed

+128
-44
lines changed
Lines changed: 125 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,39 @@
11
"""
2-
Model 1 - Horizontal stratigraphy
3-
==================================
2+
Model 1 - Horizontal Stratigraphy
3+
===================================
44
5-
A simple example with horizontal layers
5+
This example demonstrates how to build a basic geological model with horizontally stacked layers using GemPy,
6+
an open-source Python library for implicit geological modeling. In this script, we will:
67
7-
This script demonstrates how to create a basic model with horizontally stacked layers using GemPy,
8-
a Python-based, open-source library for implicit geological modeling.
9-
"""
8+
- Set up and compute a simple geological model using input data (orientations and surface points).
9+
- Visualize the model in 2D and 3D.
10+
- Export model components (formations, orientations, surface points, and the regular grid) to VTK files.
11+
- Extract mesh vertices and normals, save them to Excel files, and convert the vertex data to an XYZ file.
1012
13+
Each section includes detailed comments to explain its purpose and functionality.
14+
"""
1115

1216
# %%
1317
# Import necessary libraries
1418
import gempy as gp
1519
import gempy_viewer as gpv
16-
20+
import pyvista as pv
21+
import vtk
22+
import pandas as pd
1723

1824
# sphinx_gallery_thumbnail_number = 2
1925

2026
# %%
21-
# Generate the model
22-
# Define the path to data
27+
# Generate the Geological Model
28+
# -----------------------------
29+
# In this section, we define the data source and create a GeoModel instance.
30+
# The model is defined with a specific spatial extent and resolution (refinement).
31+
# Input data (orientations and surface points) are loaded from a remote repository.
32+
33+
# Define the path to the dataset hosted online
2334
data_path = 'https://raw.githubusercontent.com/cgre-aachen/gempy_data/master/'
24-
# Create a GeoModel instance
35+
36+
# Create a GeoModel instance with the given project name, extent, and refinement level.
2537
data = gp.create_geomodel(
2638
project_name='horizontal',
2739
extent=[0, 1000, 0, 1000, 0, 1000],
@@ -31,104 +43,174 @@
3143
path_to_surface_points=data_path + "/data/input_data/jan_models/model1_surface_points.csv"
3244
)
3345
)
34-
# Map geological series to surfaces
46+
47+
# Map a geological series to the corresponding surfaces.
48+
# Here, the "Strat_Series" is associated with two formations ('rock2' and 'rock1'),
49+
# which defines the stacking order of horizontal layers.
3550
gp.map_stack_to_surfaces(
3651
gempy_model=data,
3752
mapping_object={"Strat_Series": ('rock2', 'rock1')}
3853
)
39-
# Compute the geological model
54+
55+
# Compute the geological model using the provided data and mappings.
4056
gp.compute_model(data)
4157
geo_data = data
4258

4359
# %%
44-
# Plot the initial geological model in the y direction without results
60+
# 2D Visualization of the Geological Model
61+
# -----------------------------------------
62+
# The following plots provide 2D views of the model.
63+
# - The first plot shows the initial model without computed results.
64+
# - The subsequent plots display the computed geological data in the x and y directions.
65+
# Note: The boundaries are hidden in the latter plots for a cleaner visualization.
66+
67+
# Plot the initial model in the y direction (without displaying computed results)
4568
gpv.plot_2d(geo_data, direction=['y'], show_results=False)
4669

47-
# Plot the result of the model in the x and y direction with data and without boundaries
70+
# Plot the computed model results in the x and y directions, including data but excluding boundaries
4871
gpv.plot_2d(geo_data, direction=['x'], show_data=True, show_boundaries=False)
4972
gpv.plot_2d(geo_data, direction=['y'], show_data=True, show_boundaries=False)
5073

5174
# %%
52-
# Export to VTK model
53-
p = gpv.plot_3d(geo_data, show_data=True, show_results=True, show_boundaries=True,image=True)
54-
p.surface_poly['rock1'].save('rock1.vtk') # Save the vtk file for formation 1
55-
p.surface_poly['rock2'].save('rock2.vtk') # Save the vtk file for formation 2
56-
p.orientations_mesh.save('orientations.vtk') # Save the vtk file for the orientations
57-
p.surface_points_mesh.save('surface_points.vtk') # Save the vtk file for the surface points
58-
box = p.regular_grid_actor.GetMapper().GetInput() # Get the vtk file for the regular grid
75+
# 3D Visualization and Export to VTK
76+
# ----------------------------------
77+
# Visualize the model in 3D with data, results, and boundaries.
78+
# The generated 3D plot is also used to export various components of the model to VTK files.
79+
p = gpv.plot_3d(geo_data, show_data=True, show_results=True, show_boundaries=True, image=True)
80+
81+
# Export different components of the model to VTK files:
82+
p.surface_poly['rock1'].save('rock1.vtk') # Save formation 'rock1'
83+
p.surface_poly['rock2'].save('rock2.vtk') # Save formation 'rock2'
84+
p.orientations_mesh.save('orientations.vtk') # Save the orientations mesh
85+
p.surface_points_mesh.save('surface_points.vtk') # Save the surface points mesh
86+
87+
# Retrieve and export the regular grid (volume representation) of the model
88+
box = p.regular_grid_actor.GetMapper().GetInput()
5989
box.save('box.vtk')
6090

6191
# %%
62-
import pyvista as pv
92+
# Display the Exported VTK Files with PyVista
93+
# -------------------------------------------
94+
# Load and plot each exported VTK file to verify the export.
6395
pv.read('rock1.vtk').plot(show_edges=False)
6496
pv.read('rock2.vtk').plot(show_edges=False)
6597
pv.read('orientations.vtk').plot(show_edges=False)
6698
pv.read('surface_points.vtk').plot(show_edges=False)
6799
pv.read('box.vtk').plot(show_edges=False)
68100

101+
69102
# %%
70-
# Export vertices of mesh
71-
from builtins import range
72-
import vtk
73-
import pandas as pd
103+
# Extract and Save Mesh Vertices and Normals
104+
# ------------------------------------------
105+
# The following functions extract vertex coordinates and corresponding normal vectors
106+
# from a mesh (vtkPolyData), and then save this data into Excel files for further analysis.
107+
74108
def generate_normals(polydata):
109+
"""
110+
Generate normals for the given polydata if they are not already computed.
111+
112+
Parameters:
113+
polydata (vtk.vtkPolyData): Input polydata for which normals are to be computed.
114+
115+
Returns:
116+
vtk.vtkPolyData: The polydata with computed point normals.
117+
"""
75118
normal_generator = vtk.vtkPolyDataNormals()
76119
normal_generator.SetInputData(polydata)
77120
normal_generator.ComputePointNormalsOn()
78121
normal_generator.ComputeCellNormalsOff()
79122
normal_generator.Update()
80123
return normal_generator.GetOutput()
81124

125+
82126
def get_vertices_and_normals(mesh):
127+
"""
128+
Extract vertices and normals from a mesh.
129+
130+
Parameters:
131+
mesh: The mesh object (typically from a formation) to extract surface vertices and normals.
83132
133+
Returns:
134+
tuple: Two lists containing vertices and normals respectively.
135+
"""
136+
# Extract the surface of the mesh
84137
surface_mesh = mesh.extract_surface()
85138
polydata = surface_mesh
86139

87-
# Generate normals if not present
140+
# Ensure normals are computed for the polydata
88141
polydata_with_normals = generate_normals(polydata)
89142

90-
# Get points (vertices)
143+
# Extract points (vertices)
91144
points = polydata_with_normals.GetPoints()
92-
vertices = []
93-
for i in range(points.GetNumberOfPoints()):
94-
vertices.append(points.GetPoint(i))
145+
vertices = [points.GetPoint(i) for i in range(points.GetNumberOfPoints())]
95146

96-
# Get normals
147+
# Extract normals associated with the points
97148
normals_array = polydata_with_normals.GetPointData().GetNormals()
98-
normals = []
99-
for i in range(normals_array.GetNumberOfTuples()):
100-
normals.append(normals_array.GetTuple(i))
149+
normals = [normals_array.GetTuple(i) for i in range(normals_array.GetNumberOfTuples())]
101150

102151
return vertices, normals
103152

153+
104154
def save_to_excel(vertices, normals, vertices_file, normals_file):
105-
# Create DataFrames
155+
"""
156+
Save the vertices and normals to separate Excel files.
157+
158+
Parameters:
159+
vertices (list): List of vertex coordinates.
160+
normals (list): List of normal vectors.
161+
vertices_file (str): File name for saving vertices.
162+
normals_file (str): File name for saving normals.
163+
"""
164+
# Create DataFrames from the lists
106165
vertices_df = pd.DataFrame(vertices, columns=['X', 'Y', 'Z'])
107166
normals_df = pd.DataFrame(normals, columns=['x', 'y', 'z'])
108167

109-
# Save to Excel files
168+
# Write the DataFrames to Excel files
110169
vertices_df.to_excel(vertices_file, index=False)
111-
normals_df.to_excel
170+
normals_df.to_excel(normals_file, index=False)
171+
172+
173+
# Extract vertices and normals from the mesh of formation 'rock1'
112174
mesh = p.surface_poly['rock1']
113175
vertices, normals = get_vertices_and_normals(mesh)
176+
177+
# Create DataFrames for later processing or verification
114178
vertices_df = pd.DataFrame(vertices, columns=['X', 'Y', 'Z'])
115179
normals_df = pd.DataFrame(normals, columns=['x', 'y', 'z'])
116-
# Save to Excel filesthe
180+
181+
# Define file names for the Excel outputs
117182
vertices_file = "rock1_vertices.xlsx"
118183
normals_file = "rock1_norms.xlsx"
184+
185+
# Save the extracted data to Excel files
119186
save_to_excel(vertices, normals, vertices_file, normals_file)
187+
188+
# Optionally, read back the Excel files to verify their content
120189
pd.read_excel(vertices_file)
121190
pd.read_excel(normals_file)
122191

192+
123193
# %%
124-
# Convert the DataFrame to an XYZ file
194+
# Convert Vertices DataFrame to an XYZ File
195+
# -----------------------------------------
196+
# This function converts a DataFrame containing vertex coordinates to an XYZ format file,
197+
# which is a simple text file with one point per line.
198+
125199
def dataframe_to_xyz(df, filename):
200+
"""
201+
Write vertex coordinates from a DataFrame to an XYZ file.
202+
203+
Parameters:
204+
df (pandas.DataFrame): DataFrame containing 'X', 'Y', and 'Z' columns.
205+
filename (str): Output filename for the XYZ file.
206+
"""
126207
with open(filename, 'w') as f:
127208
for index, row in df.iterrows():
128209
f.write(f"{row['X']} {row['Y']} {row['Z']}\n")
129210

130-
# Specify the filename
211+
212+
# Specify the output file name for the XYZ file
131213
filename = "output.xyz"
132214

133-
# Call the function to write the DataFrame to an XYZ file
134-
dataframe_to_xyz(vertices_df, filename)
215+
# Convert the vertices DataFrame to an XYZ file
216+
dataframe_to_xyz(vertices_df, filename)

requirements/docs_requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ rasterio
66
sphinx
77
sphinx-gallery
88
sphinx_design
9-
sphinx_automodapi
9+
sphinx_automodapi
10+
11+
openpyxl

0 commit comments

Comments
 (0)