Skip to content

Commit 4f51677

Browse files
authored
Add benchmarking tests and examples generator for performance analysis (#8)
# Test speed [ENH] Add example generator and initial speed test Introduce `examples_generator.py` to create sample geological models, including a "two wells" example. Add `test_speed_I` for benchmarking model computation and validation of interpolation options. [ENH][TEST] Add custom grid setups and profiling for performance Introduce custom grid configurations to improve control over model evaluations. Add profiling in test_benchmark to measure and analyze computation speeds and iterations. Update tests to track interpolation counts and backend changes.
2 parents 7812a39 + 5d32fda commit 4f51677

File tree

7 files changed

+116
-5
lines changed

7 files changed

+116
-5
lines changed

docs/dev_logs/2025_May.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
- [ ] Keep extracting code into modules and functions
66
- [ ] Posterior analysis for gempy
77
- [ ] Investigate speed
8-
- [ ] Investigate segmentation function
8+
- [ ] Investigate segmentation function
9+
https://chatgpt.com/share/68172f53-4f7c-8000-8f42-205a033ed899
910
- [ ] Create a robust api to make sure we do not mess up assigning priors to gempy parameters
1011
- [ ] do we need to use index_put or just normal indexing?
1112
--- Vector examples
@@ -18,9 +19,13 @@
1819
### Doing:
1920
- [x] Compressing code
2021
- [x] GemPy posterior visualization
21-
- [ ] Add proper thickness likelihood
22+
- [x] Add proper thickness likelihood
2223
- [ ] Speed
23-
24+
- [ ] Time test for torch CPU
25+
- [ ] Time test for keops CPU
26+
- [ ] Time test for keops GPU
27+
- [ ] Options and config presets
28+
2429
### Possible Design:
2530
- Geological Model
2631
- Priors

gempy_probability/modules/model_definition/model_examples.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ def model(geo_model: gempy.core.data.GeoModel, normal, y_obs_list):
4343
# endregion
4444

4545
# region Forward model computation
46+
47+
geo_model.counter +=1
4648

4749
# * Compute the geological model
4850
geo_model.solutions = gempy_engine.compute_model(
@@ -51,7 +53,9 @@ def model(geo_model: gempy.core.data.GeoModel, normal, y_obs_list):
5153
data_descriptor=geo_model.input_data_descriptor,
5254
geophysics_input=geo_model.geophysics_input,
5355
)
54-
56+
# if i does not exist init
57+
58+
5559
# Compute and observe the thickness of the geological layer
5660
model_solutions: gp.data.Solutions = geo_model.solutions
5761
thickness = apparent_thickness_likelihood(model_solutions)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from enum import Enum, auto
2+
import gempy as gp
3+
import os
4+
from gempy_engine.core.backend_tensor import BackendTensor
5+
6+
7+
class ExampleProbModel(Enum):
8+
TWO_WELLS = auto()
9+
10+
11+
def generate_example_model(example_model: ExampleProbModel, compute_model: bool = True) -> gp.data.GeoModel:
12+
match example_model:
13+
case ExampleProbModel.TWO_WELLS:
14+
return _generate_two_wells_model(compute_model)
15+
case _:
16+
raise ValueError(f"Example model {example_model} not found.")
17+
18+
19+
def _generate_two_wells_model(compute_model: bool) -> gp.data.GeoModel:
20+
current_dir = os.path.dirname(os.path.abspath(__file__))
21+
data_path = os.path.abspath(os.path.join(current_dir, '..', '..', 'examples', 'tutorials', 'data'))
22+
geo_model = gp.create_geomodel(
23+
project_name='Wells',
24+
extent=[0, 12000, -500, 500, 0, 4000],
25+
refinement=4,
26+
importer_helper=gp.data.ImporterHelper(
27+
path_to_orientations=os.path.join(data_path, "2-layers", "2-layers_orientations.csv"),
28+
path_to_surface_points=os.path.join(data_path, "2-layers", "2-layers_surface_points.csv")
29+
)
30+
)
31+
32+
BackendTensor.change_backend_gempy(engine_backend=gp.data.AvailableBackends.PYTORCH)
33+
34+
geo_model.interpolation_options.uni_degree = 0
35+
geo_model.interpolation_options.mesh_extraction = False
36+
37+
if compute_model:
38+
# Compute the geological model
39+
gp.compute_model(geo_model)
40+
41+
return geo_model

tests/test_benchmark/__init__.py

Whitespace-only changes.

tests/test_benchmark/notes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
- Running 250 iterations took 28 sec. This is 8.81 it/s
2+
- The number of gempy evaluations are:
3+
- 53 evals/s or 18 ms per eval
4+
- 6 evals per iteration
5+
- More than 53 gempy evaluations per second seem hard to improve
6+
- Wait till I have a likelihood with a bigger model and a likelihood function that needs
7+
big grids (i.e.) gravity

tests/test_benchmark/test_speed_I.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import pytest
2+
3+
import gempy as gp
4+
import gempy_viewer as gpv
5+
import numpy as np
6+
7+
from gempy_probability.plugins.examples_generator import generate_example_model, ExampleProbModel
8+
9+
10+
def test_speed_I():
11+
12+
two_wells: gp.data.GeoModel = generate_example_model(
13+
example_model=ExampleProbModel.TWO_WELLS,
14+
compute_model=False
15+
)
16+
17+
assert two_wells.interpolation_options.number_octree_levels == 4, "Number of octrees should be 4"
18+
19+
# region Minimal grid for the specific likelihood function
20+
x_loc = 6000
21+
y_loc = 0
22+
z_loc = np.linspace(0, 4000, 100)
23+
xyz_coord = np.array([[x_loc, y_loc, z] for z in z_loc])
24+
gp.set_custom_grid(two_wells.grid, xyz_coord=xyz_coord)
25+
# endregion
26+
27+
two_wells.grid.active_grids = gp.data.Grid.GridTypes.CUSTOM
28+
29+
profiler = cProfile.Profile()
30+
profiler.enable()
31+
iterations = 100
32+
for _ in range(iterations):
33+
gp.compute_model(
34+
gempy_model=two_wells,
35+
engine_config=gp.data.GemPyEngineConfig(
36+
backend=gp.data.AvailableBackends.numpy
37+
)
38+
)
39+
profiler.disable()
40+
stats = pstats.Stats(profiler).sort_stats('cumtime')
41+
stats.print_stats(10)
42+
43+
if PLOT := False:
44+
gpv.plot_2d(two_wells, show_scalar=False)
45+
46+
47+
@pytest.mark.skip(reason="Not implemented yet")
48+
def test_speed_on_gravity_likelihood():
49+
pass

tests/test_prob_model/test_prob_I.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ def test_basic_gempy_I() -> None:
3838

3939
geo_model.grid.active_grids = gp.data.Grid.GridTypes.CUSTOM
4040
assert geo_model.grid.values.shape[0] == 100, "Custom grid should have 100 cells"
41+
geo_model.counter = 0
4142
gp.compute_model(gempy_model=geo_model)
42-
43+
BackendTensor.change_backend_gempy(engine_backend=gp.data.AvailableBackends.PYTORCH)
4344

4445
normal = dist.Normal(
4546
loc=(geo_model.surface_points_copy_transformed.xyz[0, 2]),
@@ -73,6 +74,7 @@ def _prob_run(geo_model: gp.data.GeoModel, prob_model: callable,
7374
num_samples=50
7475
)
7576
prior = predictive(geo_model, normal, y_obs_list)
77+
print("Number of interpolations: ", geo_model.counter)
7678

7779
data = az.from_pyro(prior=prior)
7880
az.plot_trace(data.prior)
@@ -103,9 +105,12 @@ def _prob_run(geo_model: gp.data.GeoModel, prob_model: callable,
103105
posterior_samples=posterior_samples
104106
)
105107
posterior_predictive = posterior_predictive_fn(geo_model, normal, y_obs_list)
108+
106109
data = az.from_pyro(posterior=mcmc, prior=prior, posterior_predictive=posterior_predictive)
107110
# endregion
108111

112+
print("Number of interpolations: ", geo_model.counter)
113+
109114
if True: # * Save the arviz data
110115
data.to_netcdf("arviz_data.nc")
111116

0 commit comments

Comments
 (0)