add new methods for geulen#556
Conversation
|
This PR is ready for review. Although I think it will be pretty hard to review. I tried to take into account all possible scenarios for this tool, but still I added a warning because I am not sure the method works for all edge cases (there are so many!). There are a few assumptions in the code:
In the PR there are now 2 functions The 15_geotop.ipynb has code to show the result from both methods. This might be the easiest way to check the results. I will remove one of the methods and clean up the notebook once we choose. |
There was a problem hiding this comment.
Pull request overview
Adds support for converting GeoTOP stratigraphy with “geulen” (paleochannels) into a consistent layered model by optionally splitting layers, and introduces a new plotting helper to visualize GeoTOP stratigraphy in cross-sections.
Changes:
- Add two new geul-handling algorithms (
split_layers_on_geulandsplit_layers_on_geul_optimal) and integrate them intoto_model_layers(method_geulen="split_layers"). - Generalize GeoTOP cross-section plotting to a variable-based helper and add
geotop_strat_in_cross_section. - Update GeoTOP documentation notebook to demonstrate the new stratigraphy cross-section plot and the new
split_layersmethod.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
nlmod/read/geotop.py |
Adds layer-splitting logic for geulen and wires it into to_model_layers. |
nlmod/plot/plot.py |
Adds stratigraphy cross-section plotting and generalizes legend/cross-section helpers. |
nlmod/plot/__init__.py |
Exposes geotop_strat_in_cross_section via the public plotting API. |
docs/data_sources/15_geotop.ipynb |
Demonstrates new plotting and experimental split_layers geul handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@dbrakenhoff and @rubencalje, can you wait with the review? Copilot has some good points that I want to solve first. |
|
@rubencalje & @dbrakenhoff this PR is finally ready for review. Failing test not related to this PR. The PR adds the function Note: there was already an option "add_as_layer" in the
|
|
Thanks a lot! I was struggling with the tests myself, your approach is very clean. I added the tests and made some improvements for the edge cases. I expanded your script and pasted it below in case we ever need to debug this. import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
from nlmod.read.geotop import split_layers_on_geul
def plot_stratigraphy(strat_original, strat_modified):
unique_values = np.unique(np.concatenate([strat_original, strat_modified]))
boundaries = np.append(unique_values, unique_values[-1] + 1)
norm = mpl.colors.BoundaryNorm(boundaries, len(unique_values))
cmap = plt.get_cmap("Dark2", len(unique_values))
fig, (axl, axr) = plt.subplots(1, 2, sharex=True, sharey=True)
axl.matshow(strat.squeeze(), norm=norm, cmap=cmap)
axr.matshow(strat_modified.squeeze(), norm=norm, cmap=cmap)
axl.set_title("Original Stratigraphy")
axr.set_title("Modified Stratigraphy")
# Loop through the array to add text
for istrat, iax in zip([strat_original, strat_modified], (axl, axr)):
rows, _, cols = strat_original.shape
for i in range(rows):
for j in range(cols):
val = istrat[i, 0, j]
# Use white text for dark colors, black for light colors
color = "white"
iax.text(
j,
i,
str(val.item()),
va="center",
ha="center",
color=color,
fontweight="bold",
)
return
# %% example 2 geulen
strat = xr.DataArray(
np.array([[0, 10, 0], [1, 20, 1], [2, 2, 2]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1, 2]
geulen = [10, 20]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example geul at top
strat = xr.DataArray(
np.array([[0, 10, 10], [1, 1, 1], [2, 2, 2]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1, 2]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example geul between and below
strat = xr.DataArray(
np.array([[0, 0, 0], [10, 1, 1], [0,10, 10]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example split single layer
strat = xr.DataArray(
np.array([[0, 0, 0], [0, 10, 10], [0, 0, 0]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example geul at bottom
strat = xr.DataArray(
np.array([[0, 0, 0], [1, 1, 1], [2, 10, 10]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1, 2]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example simple add between layers
strat = xr.DataArray(
np.array([[0, 0, 0], [0, 10, 10], [1, 1, 1]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example add between layers with geul at top and bottom
strat = xr.DataArray(
np.array([[0, 0, 10], [10, 10, 10], [10, 1, 1]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example geul between and below
strat = xr.DataArray(
np.array([[0, 0, 0], [10, 1, 1], [0,10, 10], [0,2, 2]])[:, None, :],
coords={"z": [0, -0.5, -1.0, -1.5], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1,2]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order)
# %% example geul between and below
strat = xr.DataArray(
np.array([[0, 0, 0], [10, 1, 1], [0,10, 10]])[:, None, :],
coords={"z": [0, -0.5, -1.0], "y": [100], "x": [100, 200, 300]},
dims=("z", "y", "x"),
)
units_no_geul = [0, 1]
geulen = [10]
strat_modified, new_unit_order = split_layers_on_geul(strat, units_no_geul, geulen)
plot_stratigraphy(strat, strat_modified)
print(new_unit_order) |
|
Yes, the order is correct and the naming is definitely odd. It is not easy to change this in the |
|
Fixed it in the |



Add two new methods for merging geotop geulen into a geotop stratigraphic model.