-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGemPy_CrossSection.py
More file actions
323 lines (213 loc) · 6.03 KB
/
Copy pathGemPy_CrossSection.py
File metadata and controls
323 lines (213 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#!/usr/bin/env python
# coding: utf-8
# In[47]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# In[48]:
wells = pd.read_csv(r"C:\Projects\pythonscripts\welldata.csv") # Change path/file name to match where your CSV actually lives
#Example Data - ID, well_name, x, y, ground_elevation, formation_name, rock_type, top_depth, bottom_depth, description
# In[49]:
print(wells.head())
print()
print(wells.columns.tolist())
print()
print(f"Number of rows: {len(wells)}")
print(f"Number of wells: {wells['well_name'].nunique()}")
print(f"Formations: {wells['formation_name'].unique()}")
# In[50]:
wells["top_elevation"] = wells["ground_elevation"] - wells["top_depth"]
wells["bottom_elevation"] = wells["ground_elevation"] - wells["bottom_depth"]
# In[53]:
print("Rock types:")
print(wells["rock_type"].unique())
print("\nNumber of intervals by rock type:")
print(wells["rock_type"].value_counts())
# In[54]:
units = wells["rock_type"].unique()
print("Geological units:")
for i, unit in enumerate(units):
print(i, unit)
# In[55]:
unit_map = {unit: i for i, unit in enumerate(units)}
wells["unit_id"] = wells["rock_type"].map(unit_map)
print(wells[["rock_type", "unit_id"]].drop_duplicates().sort_values("unit_id"))
# In[56]:
# Sort the data by well and depth
wells = wells.sort_values(["well_name", "top_depth"]).reset_index(drop=True)
# Identify where the rock type changes within each well
wells["rock_type_change"] = (
wells["rock_type"] != wells.groupby("well_name")["rock_type"].shift()
)
# Show only the rows where a new rock type begins
contacts = wells[wells["rock_type_change"]].copy()
print(contacts[
[
"well_name",
"top_depth",
"bottom_depth",
"rock_type",
"top_elevation"
]
].to_string(index=False))
# In[58]:
# Keep only rows where the rock type changes
# and exclude the first logged interval in each well
contacts = wells[
wells["rock_type_change"] &
(wells.groupby("well_name").cumcount() > 0)
].copy()
# The top of the new interval is the contact elevation
contacts["contact_elevation"] = contacts["top_elevation"]
# Keep only the information we need
contacts = contacts[
[
"well_name",
"x",
"y",
"contact_elevation",
"rock_type"
]
].reset_index(drop=True)
print(contacts.to_string(index=False))
# In[59]:
# Start with the complete well data
contact_data = wells.copy()
# Identify the rock type immediately above each interval
contact_data["rock_type_above"] = (
contact_data.groupby("well_name")["rock_type"].shift()
)
# A contact occurs when the rock type changes
contact_data["is_contact"] = (
contact_data["rock_type"] != contact_data["rock_type_above"]
)
# Exclude the first interval of each well
contact_data = contact_data[
contact_data["rock_type_above"].notna() &
contact_data["is_contact"]
].copy()
# The top of the new interval is the contact elevation
contact_data["contact_elevation"] = contact_data["top_elevation"]
# Keep the useful information
contacts = contact_data[
[
"well_name",
"x",
"y",
"contact_elevation",
"rock_type_above",
"rock_type"
]
].copy()
# Rename the current rock type to make the meaning explicit
contacts = contacts.rename(
columns={"rock_type": "rock_type_below"}
)
print(contacts.to_string(index=False))
# In[60]:
# Create a clean table of geological contacts
contact_points = contacts[
[
"x",
"y",
"contact_elevation",
"rock_type_below"
]
].copy()
# Rename columns for clarity
contact_points = contact_points.rename(
columns={
"contact_elevation": "z",
"rock_type_below": "rock_type"
}
)
print(contact_points.head(20).to_string(index=False))
# In[61]:
print(
contact_points["rock_type"].value_counts()
)
# In[62]:
import gempy
print(gempy.__version__)
# In[63]:
import gempy as gp
import gempy_viewer as gpv
# In[64]:
surface_points = contact_points.copy()
surface_points = surface_points.rename(
columns={
"rock_type": "group",
"z": "Z"
}
)
surface_points.head()
# In[70]:
import matplotlib.patches as mpatches
fig, ax = plt.subplots(figsize=(10, 8))
# Colors for each rock type
rock_colors = {
"Bedrock": "grey",
"Metamorphic": "darkblue",
"Sand": "gold",
"Clay": "brown",
"Gravel": "orange",
"Silt": "lightsteelblue"
}
# Give each well a position along the x-axis
well_names = wells["well_name"].unique()
well_positions = {well: i for i, well in enumerate(well_names)}
# Width of each well log
bar_width = 0.25
# Plot each logged interval
for well_name, well_data in wells.groupby("well_name"):
x_pos = well_positions[well_name]
for _, row in well_data.iterrows():
top = row["top_elevation"]
bottom = row["bottom_elevation"]
color = rock_colors.get(row["rock_type"], "black")
rect = plt.Rectangle(
(x_pos - bar_width / 2, bottom),
bar_width,
top - bottom,
facecolor=color,
edgecolor="black",
linewidth=0.5
)
ax.add_patch(rect)
# Set X axis
ax.set_xlim(-0.5, len(well_names) - 0.5)
ax.set_xticks(range(len(well_names)))
ax.set_xticklabels(well_names, rotation=45, ha="right")
# IMPORTANT: explicitly set Y limits from the data
y_min = wells["bottom_elevation"].min()
y_max = wells["top_elevation"].max()
padding = (y_max - y_min) * 0.05
ax.set_ylim(
y_min - padding,
y_max + padding
)
ax.set_ylabel("Elevation (ft)")
ax.set_xlabel("Well")
ax.set_title("Well Lithology")
# Legend
legend_patches = [
mpatches.Patch(color=color, label=rock_type)
for rock_type, color in rock_colors.items()
if rock_type in wells["rock_type"].unique()
]
ax.legend(
handles=legend_patches,
title="Legend",
title_fontsize=14,
fontsize=12,
loc="center left",
bbox_to_anchor=(1.02, 0.5),
handlelength=2.0,
handleheight=1.2,
handletextpad=1.0,
labelspacing=1.0,
borderpad=1.0
)
plt.tight_layout()
plt.show()
# In[ ]: