Files
ResInsight/GrpcInterface/Python/rips/tests/test_surfaces.py
T
Kristian Bendiksen 4977941896 #14821 Python: Report specific errors when import_surface fails
- Return std::expected from RimSurfaceCollection::importSurfacesFromFiles
  with a message naming the files that could not be imported
- Return specific errors from ImportSurface and NewSurface scripting
  methods instead of nullptr
- Import before deleting the surface to overwrite, so a failed overwrite
  keeps the existing surface
- Catch surfio exceptions in RifSurfio to avoid a crash on corrupt files
- Include the method name in the generic "no result" gRPC error
- Add Python tests for import_surface error handling
2026-10-05 08:30:33 +02:00

286 lines
11 KiB
Python

import sys
import os
import tempfile
import pytest
from pathlib import Path
sys.path.insert(1, os.path.join(sys.path[0], "../../"))
import rips
import dataroot
def test_create_and_export_surface(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
case = rips_instance.project.load_case(path=case_path)
assert len(case.grids()) == 1
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
surface = surface_collection.new_surface(case, 5)
assert surface
with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname:
path = Path(tmpdirname, "mysurface.ts")
print("Temporary folder: ", path.as_posix())
surface.export_to_file(path.as_posix())
assert path.exists()
def test_create_regular_surface(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
c = rips_instance.project.load_case(path=case_path)
assert len(c.grids()) == 1
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
bbox = c.reservoir_boundingbox()
depth = bbox.max_z - ((bbox.max_z - bbox.min_z) / 2.0)
origin_x = bbox.min_x
origin_y = bbox.min_y
name = "test surface name"
nx = 20
ny = 10
increment_x = (bbox.max_x - bbox.min_x) / float(nx)
increment_y = (bbox.max_y - bbox.min_y) / float(ny)
rotation = 23.4
s = surface_collection.new_regular_surface(
name=name,
origin_x=origin_x,
origin_y=origin_y,
depth=-depth,
nx=nx,
ny=ny,
increment_x=increment_x,
increment_y=increment_y,
rotation=rotation,
)
assert s.origin_x == origin_x
assert s.origin_y == origin_y
assert s.increment_x == increment_x
assert s.increment_y == increment_y
assert s.rotation == rotation
assert s.nx == nx
assert s.ny == ny
with pytest.raises(
rips.RipsError, match="Failed to set property: incorrect dimensions."
):
s.set_property("property_too_big", [i for i in range(nx * ny * 2)])
# Successfully set the depth property
s.set_property("depth", [i for i in range(nx * ny)])
s.set_property_as_depth("depth")
# Change the dimensions of the surface: should invalidate the properties
s.nx = 30
s.update()
def test_surface_settings_in_view(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
case = rips_instance.project.load_case(path=case_path)
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
folder = surface_collection.add_folder(folder_name="View surfaces")
surface = folder.new_regular_surface(name="View surface", nx=2, ny=2)
surface.set_property("Property A", [1.0, 2.0, 3.0, 4.0])
surface.set_property("Property B", [5.0, 6.0, 7.0, 8.0])
first_view = case.create_view()
second_view = case.create_view()
assert first_view.set_surface_visible(surface=surface, visible=False) is None
assert second_view.set_surface_visible(surface=surface, visible=True) is None
assert (
first_view.set_surface_property(surface=surface, property_name="Property B")
is None
)
with pytest.raises(rips.RipsError, match="not available for surface"):
first_view.set_surface_property(surface=surface, property_name="Missing")
with pytest.raises(rips.RipsError, match="Surface is null"):
first_view.set_surface_visible(surface=None, visible=True)
def test_get_property(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
c = rips_instance.project.load_case(path=case_path)
assert len(c.grids()) == 1
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
nx = 5
ny = 4
s = surface_collection.new_regular_surface(nx=nx, ny=ny)
input_values = [float(i) for i in range(nx * ny)]
s.set_property("test_prop", input_values)
output_values = s.get_property("test_prop")
assert len(output_values) == nx * ny
for i in range(len(input_values)):
assert abs(output_values[i] - input_values[i]) < 1e-6
# Getting a non-existent property should raise an error
with pytest.raises(rips.RipsError, match="not found"):
s.get_property("non_existent_property")
def test_available_properties(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
c = rips_instance.project.load_case(path=case_path)
assert len(c.grids()) == 1
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
nx = 5
ny = 4
s = surface_collection.new_regular_surface(nx=nx, ny=ny)
# Empty property list initially
props = s.available_properties()
assert props == []
# Property names after setting one property
s.set_property("prop_a", [float(i) for i in range(nx * ny)])
props = s.available_properties()
assert "prop_a" in props
assert len(props) == 1
# Property names persist after setting multiple properties
s.set_property("prop_b", [float(i) for i in range(nx * ny)])
props = s.available_properties()
assert len(props) == 2
assert "prop_a" in props
assert "prop_b" in props
def test_create_regular_surface_invalid_values(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
c = rips_instance.project.load_case(path=case_path)
assert len(c.grids()) == 1
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
with pytest.raises(rips.RipsError, match="Invalid nx"):
surface_collection.new_regular_surface(nx=-1)
with pytest.raises(rips.RipsError, match="Invalid ny"):
surface_collection.new_regular_surface(ny=-1)
with pytest.raises(rips.RipsError, match="Invalid increment X. Must be positive."):
surface_collection.new_regular_surface(increment_x=-1.0)
with pytest.raises(rips.RipsError, match="Invalid increment Y. Must be positive."):
surface_collection.new_regular_surface(increment_y=-1.0)
with pytest.raises(rips.RipsError, match="Invalid rotation."):
surface_collection.new_regular_surface(rotation=-1.0)
def test_surface_name_uniqueness(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
rips_instance.project.load_case(path=case_path)
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
surface_collection.new_regular_surface(name="Top")
# The default policy is to fail on a name already used in the same folder
with pytest.raises(rips.RipsError, match="already exists"):
surface_collection.new_regular_surface(name="Top")
renamed = surface_collection.new_regular_surface(
name="Top", on_name_conflict=rips.NameConflictPolicy.AUTO_RENAME
)
assert renamed.surface_user_description == "Top_1"
# Names are case sensitive, so this is not a conflict
other_case = surface_collection.new_regular_surface(name="top")
assert other_case.surface_user_description == "top"
overwritten = surface_collection.new_regular_surface(
name="Top", on_name_conflict=rips.NameConflictPolicy.OVERWRITE
)
assert overwritten.surface_user_description == "Top"
names = [s.surface_user_description for s in surface_collection.surfaces_field()]
assert names.count("Top") == 1
# A different folder is a separate namespace
folder = surface_collection.add_folder(folder_name="Folder A")
in_folder = folder.new_regular_surface(name="Top")
assert in_folder.surface_user_description == "Top"
def test_import_surface_errors(rips_instance, initialize_test):
case_path = dataroot.PATH + "/Case_with_10_timesteps/Real0/BRUGGE_0000.EGRID"
case = rips_instance.project.load_case(path=case_path)
surface_collection = rips_instance.project.descendants(rips.SurfaceCollection)[0]
with pytest.raises(rips.RipsError, match="File name is empty"):
surface_collection.import_surface(file_name="")
with tempfile.TemporaryDirectory(prefix="rips") as tmpdirname:
missing_path = Path(tmpdirname, "missing.irap").as_posix()
with pytest.raises(rips.RipsError, match="missing.irap"):
surface_collection.import_surface(file_name=missing_path)
corrupt_path = Path(tmpdirname, "corrupt.irap")
corrupt_path.write_text("this is not a surface file")
with pytest.raises(
rips.RipsError, match="Could not import surface from file .*corrupt.irap"
):
surface_collection.import_surface(file_name=corrupt_path.as_posix())
surface_path = Path(tmpdirname, "mysurface.ts")
surface_collection.new_surface(case, 5).export_to_file(surface_path.as_posix())
valid_content = surface_path.read_bytes()
imported = surface_collection.import_surface(file_name=surface_path.as_posix())
assert imported.surface_user_description == "mysurface.ts"
def surface_names():
return [
s.surface_user_description for s in surface_collection.surfaces_field()
]
# A failed overwrite must keep the existing surface
surface_path.unlink()
with pytest.raises(rips.RipsError, match="mysurface.ts"):
surface_collection.import_surface(
file_name=surface_path.as_posix(),
on_name_conflict=rips.NameConflictPolicy.OVERWRITE,
)
assert surface_names().count("mysurface.ts") == 1
surface_path.write_text("this is not a surface file")
with pytest.raises(rips.RipsError, match="mysurface.ts"):
surface_collection.import_surface(
file_name=surface_path.as_posix(),
on_name_conflict=rips.NameConflictPolicy.OVERWRITE,
)
assert surface_names().count("mysurface.ts") == 1
# A successful overwrite replaces the surface and keeps the name
surface_path.write_bytes(valid_content)
overwritten = surface_collection.import_surface(
file_name=surface_path.as_posix(),
on_name_conflict=rips.NameConflictPolicy.OVERWRITE,
)
assert overwritten.surface_user_description == "mysurface.ts"
assert surface_names().count("mysurface.ts") == 1
renamed = surface_collection.import_surface(
file_name=surface_path.as_posix(),
on_name_conflict=rips.NameConflictPolicy.AUTO_RENAME,
)
assert renamed.surface_user_description == "mysurface.ts_1"