#13982 Python: Tighten Instance.launch/find return type and drop dead None checks

Instance.launch and Instance.find now always either return a connected
Instance or raise RipsError, so their return type narrows from
Optional[Instance] to Instance. In launch(), the unreachable `if pid:`
guard around the Popen result and the trailing `return None` are
removed; the only failure paths raise RipsError.

Update the PythonExamples that previously branched on a None return:
- Drop `if resinsight is None: exit(1)` style guards and `if resinsight
  is not None:` wrappers; dedent the body where applicable.
- export_corner_point_grid.py used the None check as a fallback to
  launch ResInsight; rewrite as try/except rips.RipsError so the
  fallback still works.
- instance_example.py is repurposed as a try/except example for
  reporting connection failure.
This commit is contained in:
Kristian Bendiksen
2026-05-13 13:47:40 +02:00
parent 1424a6dc53
commit e7608536cf
18 changed files with 262 additions and 285 deletions
@@ -8,32 +8,32 @@ import rips
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is not None:
# Get a list of all cases
cases = resinsight.project.cases()
print("Got " + str(len(cases)) + " cases: ")
for case in cases:
print("Case id: " + str(case.id))
print("Case name: " + case.name)
print("Case type: " + case.__class__.__name__)
print("Case file name: " + case.file_path)
print("Case reservoir bounding box:", case.reservoir_boundingbox())
# Get a list of all cases
cases = resinsight.project.cases()
timesteps = case.time_steps()
for t in timesteps:
print("Year: " + str(t.year))
print("Month: " + str(t.month))
print("Got " + str(len(cases)) + " cases: ")
for case in cases:
print("Case id: " + str(case.id))
print("Case name: " + case.name)
print("Case type: " + case.__class__.__name__)
print("Case file name: " + case.file_path)
print("Case reservoir bounding box:", case.reservoir_boundingbox())
if isinstance(case, rips.EclipseCase):
print("Getting coarsening info for case: ", case.name, case.id)
coarsening_info = case.coarsening_info()
if coarsening_info:
print("Coarsening information:")
timesteps = case.time_steps()
for t in timesteps:
print("Year: " + str(t.year))
print("Month: " + str(t.month))
for c in coarsening_info:
print(
"[{}, {}, {}] - [{}, {}, {}]".format(
c.min.x, c.min.y, c.min.z, c.max.x, c.max.y, c.max.z
)
if isinstance(case, rips.EclipseCase):
print("Getting coarsening info for case: ", case.name, case.id)
coarsening_info = case.coarsening_info()
if coarsening_info:
print("Coarsening information:")
for c in coarsening_info:
print(
"[{}, {}, {}] - [{}, {}, {}]".format(
c.min.x, c.min.y, c.min.z, c.max.x, c.max.y, c.max.z
)
)
@@ -3,9 +3,8 @@
#######################################
import rips
resinsight = rips.Instance.find()
if resinsight is None:
print("ERROR: could not find ResInsight")
else:
try:
resinsight = rips.Instance.find()
print("Successfully connected to ResInsight")
except rips.RipsError as e:
print(f"ERROR: could not find ResInsight: {e}")
@@ -8,11 +8,10 @@
import rips
resinsight = rips.Instance.find()
if resinsight is not None:
cases = resinsight.project.selected_cases()
cases = resinsight.project.selected_cases()
print("Got " + str(len(cases)) + " cases: ")
for case in cases:
print(case.name)
for property in case.available_properties(rips.PropertyType.DYNAMIC_NATIVE):
print(property)
print("Got " + str(len(cases)) + " cases: ")
for case in cases:
print(case.name)
for property in case.available_properties(rips.PropertyType.DYNAMIC_NATIVE):
print(property)
@@ -34,9 +34,10 @@ def validate_grid_dimensions(coord_array, zcorn_array, actnum_array, nx, ny, nz)
def main():
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is None:
# Connect to ResInsight, launching it if no existing instance is found.
try:
resinsight = rips.Instance.find()
except rips.RipsError:
print("Starting ResInsight...")
resinsight = rips.Instance.launch(console=True)
@@ -5,8 +5,6 @@
import rips
resinsight = rips.Instance.find()
if resinsight is None:
exit(1)
cases = resinsight.project.cases()
if len(cases) == 0:
@@ -34,9 +34,6 @@ def point_in_polygon_2d(px, py, polygon_xy):
resinsight = rips.Instance.find()
if resinsight is None:
print("No ResInsight instance found")
exit()
cases = resinsight.project.cases()
if not cases:
@@ -6,63 +6,62 @@
import rips
resinsight = rips.Instance.find()
if resinsight is not None:
cases = resinsight.project.cases()
cases = resinsight.project.cases()
print("Got " + str(len(cases)) + " cases: ")
for case in cases:
print(case.name)
cells = case.selected_cells()
print("Found " + str(len(cells)) + " selected cells")
print("Got " + str(len(cases)) + " cases: ")
for case in cases:
print(case.name)
cells = case.selected_cells()
print("Found " + str(len(cells)) + " selected cells")
time_step_info = case.time_steps()
time_step_info = case.time_steps()
for idx, cell in enumerate(cells):
for idx, cell in enumerate(cells):
print(
"Selected cell: [{}, {}, {}] grid: {}".format(
cell.ijk.i + 1, cell.ijk.j + 1, cell.ijk.k + 1, cell.grid_index
)
)
# Get the grid and dimensions
grid = case.grids()[cell.grid_index]
dimensions = grid.dimensions()
# Map ijk to cell index
cell_index = (
dimensions.i * dimensions.j * cell.ijk.k
+ dimensions.i * cell.ijk.j
+ cell.ijk.i
)
# Print the cell center
cell_centers = grid.cell_centers()
cell_center = cell_centers[cell_index]
print(
"Cell center: [{}, {}, {}]".format(
cell_center.x, cell_center.y, cell_center.z
)
)
# Print the cell corners
cell_corners = grid.cell_corners()[cell_index]
print("Cell corners:")
print("c0:\n" + str(cell_corners.c0))
print("c1:\n" + str(cell_corners.c1))
print("c2:\n" + str(cell_corners.c2))
print("c3:\n" + str(cell_corners.c3))
print("c4:\n" + str(cell_corners.c4))
print("c5:\n" + str(cell_corners.c5))
print("c6:\n" + str(cell_corners.c6))
print("c7:\n" + str(cell_corners.c7))
for tidx, timestep in enumerate(time_step_info):
# Read the full SOIL result for time step
soil_results = case.selected_cell_property(
rips.PropertyType.DYNAMIC_NATIVE, "SOIL", tidx
)
print(
"Selected cell: [{}, {}, {}] grid: {}".format(
cell.ijk.i + 1, cell.ijk.j + 1, cell.ijk.k + 1, cell.grid_index
"SOIL: {} ({}.{}.{})".format(
soil_results[idx], timestep.year, timestep.month, timestep.day
)
)
# Get the grid and dimensions
grid = case.grids()[cell.grid_index]
dimensions = grid.dimensions()
# Map ijk to cell index
cell_index = (
dimensions.i * dimensions.j * cell.ijk.k
+ dimensions.i * cell.ijk.j
+ cell.ijk.i
)
# Print the cell center
cell_centers = grid.cell_centers()
cell_center = cell_centers[cell_index]
print(
"Cell center: [{}, {}, {}]".format(
cell_center.x, cell_center.y, cell_center.z
)
)
# Print the cell corners
cell_corners = grid.cell_corners()[cell_index]
print("Cell corners:")
print("c0:\n" + str(cell_corners.c0))
print("c1:\n" + str(cell_corners.c1))
print("c2:\n" + str(cell_corners.c2))
print("c3:\n" + str(cell_corners.c3))
print("c4:\n" + str(cell_corners.c4))
print("c5:\n" + str(cell_corners.c5))
print("c6:\n" + str(cell_corners.c6))
print("c7:\n" + str(cell_corners.c7))
for tidx, timestep in enumerate(time_step_info):
# Read the full SOIL result for time step
soil_results = case.selected_cell_property(
rips.PropertyType.DYNAMIC_NATIVE, "SOIL", tidx
)
print(
"SOIL: {} ({}.{}.{})".format(
soil_results[idx], timestep.year, timestep.month, timestep.day
)
)
@@ -8,42 +8,42 @@ import rips
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is not None:
# Get a list of all cases
cases = resinsight.project.cases()
for c in cases:
print("Case name: " + c.name)
# Get a list of all cases
cases = resinsight.project.cases()
# create a polygon which is same a the bounding box in x and y.
# depth is set to middle of the bounding box
bbox = c.reservoir_boundingbox()
depth = bbox.max_z - ((bbox.max_z - bbox.min_z) / 2.0)
for c in cases:
print("Case name: " + c.name)
coordinates = []
coordinates.append([bbox.min_x, bbox.min_y, depth])
coordinates.append([bbox.max_x, bbox.min_y, depth])
coordinates.append([bbox.max_x, bbox.max_y, depth])
coordinates.append([bbox.min_x, bbox.max_y, depth])
# create a polygon which is same a the bounding box in x and y.
# depth is set to middle of the bounding box
bbox = c.reservoir_boundingbox()
depth = bbox.max_z - ((bbox.max_z - bbox.min_z) / 2.0)
polygon_collection = resinsight.project.descendants(rips.PolygonCollection)[0]
p = polygon_collection.create_polygon(
name="{} bounding box".format(c.name), coordinates=coordinates
)
print("Coordinates for {}:".format(p.name))
for coord in p.coordinates:
print(coord)
coordinates = []
coordinates.append([bbox.min_x, bbox.min_y, depth])
coordinates.append([bbox.max_x, bbox.min_y, depth])
coordinates.append([bbox.max_x, bbox.max_y, depth])
coordinates.append([bbox.min_x, bbox.max_y, depth])
# Customize appearance
appearance = p.appearance()
if appearance is not None:
appearance.line_color = "#ff0000"
appearance.line_thickness = 5
appearance.show_spheres = True
appearance.sphere_color = "#0000ff"
appearance.update()
print(
"Appearance updated: line_color={}, line_thickness={}".format(
appearance.line_color, appearance.line_thickness
)
polygon_collection = resinsight.project.descendants(rips.PolygonCollection)[0]
p = polygon_collection.create_polygon(
name="{} bounding box".format(c.name), coordinates=coordinates
)
print("Coordinates for {}:".format(p.name))
for coord in p.coordinates:
print(coord)
# Customize appearance
appearance = p.appearance()
if appearance is not None:
appearance.line_color = "#ff0000"
appearance.line_thickness = 5
appearance.show_spheres = True
appearance.sphere_color = "#0000ff"
appearance.update()
print(
"Appearance updated: line_color={}, line_thickness={}".format(
appearance.line_color, appearance.line_thickness
)
)
@@ -34,58 +34,56 @@ resinsight = rips.Instance.find()
project = resinsight.project
# Get a list of all cases
cases = resinsight.project.cases()
if resinsight is not None:
# Get a list of all cases
cases = resinsight.project.cases()
for c in cases:
bbox = c.reservoir_boundingbox()
depth = bbox.max_z - ((bbox.max_z - bbox.min_z) / 2.0)
for c in cases:
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
origin_x = bbox.min_x
origin_y = bbox.min_y
name = "{} surface".format(c.name)
name = "{} surface".format(c.name)
nx = 200
ny = 100
nx = 200
ny = 100
increment_x = (bbox.max_x - bbox.min_x) / float(nx)
increment_y = (bbox.max_y - bbox.min_y) / float(ny)
increment_x = (bbox.max_x - bbox.min_x) / float(nx)
increment_y = (bbox.max_y - bbox.min_y) / float(ny)
surface_collection = resinsight.project.descendants(rips.SurfaceCollection)[0]
surface_collection = resinsight.project.descendants(rips.SurfaceCollection)[0]
# Create a surface at a given depth
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,
)
# Create a surface at a given depth
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,
)
# Rotate the resulting surface
s.rotation = 45.0
s.update()
# Rotate the resulting surface
s.rotation = 45.0
s.update()
# Add one property
s.set_property("first_property", create_x_surface(nx, ny))
# Add one property
s.set_property("first_property", create_x_surface(nx, ny))
# Add a wave surface
s.set_property("wave", create_wave_surface(nx, ny))
# Add a wave surface
s.set_property("wave", create_wave_surface(nx, ny))
wave_values = s.get_property("wave")
print(
f"Retrieved {len(wave_values)} wave property values, min={min(wave_values):.2f}, max={max(wave_values):.2f}"
)
wave_values = s.get_property("wave")
print(
f"Retrieved {len(wave_values)} wave property values, min={min(wave_values):.2f}, max={max(wave_values):.2f}"
)
# List available properties
props = s.available_properties()
print(f"Available properties: {props}")
# List available properties
props = s.available_properties()
print(f"Available properties: {props}")
# Use the wave as depth for the surface
s.set_property_as_depth("wave")
# Use the wave as depth for the surface
s.set_property_as_depth("wave")
@@ -8,23 +8,21 @@ import rips
# Connect to ResInsight instance
resinsight = rips.Instance.find()
# Check if connection worked
if resinsight is not None:
# Get a list of all cases
cases = resinsight.project.cases()
for case in cases:
# Get a list of all views
views = case.views()
for view in views:
# Set some parameters for the view
view.show_grid_box = not view.show_grid_box
view.background_color = "#3388AA"
# Update the view in ResInsight
view.update()
# Clone the first view
new_view = views[0].clone()
new_view.background_color = "#FFAA33"
new_view.update()
view.show_grid_box = False
view.set_visible(False)
# Get a list of all cases
cases = resinsight.project.cases()
for case in cases:
# Get a list of all views
views = case.views()
for view in views:
# Set some parameters for the view
view.show_grid_box = not view.show_grid_box
view.background_color = "#3388AA"
# Update the view in ResInsight
view.update()
# Clone the first view
new_view = views[0].clone()
new_view.background_color = "#FFAA33"
new_view.update()
view.show_grid_box = False
view.set_visible(False)
view.update()
@@ -11,8 +11,6 @@ import sys
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is None:
sys.exit("ResInsight is not running. Please start ResInsight and try again.")
# Get a list of all wells
cases = resinsight.project.cases()
@@ -8,29 +8,29 @@ import rips
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is not None:
# Get a list of all wells
cases = resinsight.project.cases()
for case in cases:
print("Case id: " + str(case.id))
print("Case name: " + case.name)
# Get a list of all wells
cases = resinsight.project.cases()
timesteps = case.time_steps()
sim_wells = case.simulation_wells()
for sim_well in sim_wells:
print("Simulation well: " + sim_well.name)
for case in cases:
print("Case id: " + str(case.id))
print("Case name: " + case.name)
for tidx, timestep in enumerate(timesteps):
status = sim_well.status(tidx)
cells = sim_well.cells(tidx)
print(
"timestep: "
+ str(tidx)
+ " type: "
+ status.well_type
+ " open: "
+ str(status.is_open)
+ " cells:"
+ str(len(cells))
)
timesteps = case.time_steps()
sim_wells = case.simulation_wells()
for sim_well in sim_wells:
print("Simulation well: " + sim_well.name)
for tidx, timestep in enumerate(timesteps):
status = sim_well.status(tidx)
cells = sim_well.cells(tidx)
print(
"timestep: "
+ str(tidx)
+ " type: "
+ status.well_type
+ " open: "
+ str(status.is_open)
+ " cells:"
+ str(len(cells))
)
@@ -8,10 +8,10 @@ import rips
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is not None:
# Get a list of all wells
wells = resinsight.project.well_paths()
print("Got " + str(len(wells)) + " wells: ")
for well in wells:
print("Well name: " + well.name)
# Get a list of all wells
wells = resinsight.project.well_paths()
print("Got " + str(len(wells)) + " wells: ")
for well in wells:
print("Well name: " + well.name)
@@ -1,8 +1,6 @@
import rips
resinsight = rips.Instance.find()
if resinsight is None:
raise RuntimeError("No running ResInsight instance found on the expected ports.")
# Create a modeled well path and add well path targets
# The coordinates are based on the Norne case
@@ -17,8 +17,6 @@ def fieldValueOrDefaultText(grpc_object, optional_field_name: str):
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is None:
exit(1)
# Get a list of all wells
wells = resinsight.project.well_paths()
@@ -9,14 +9,14 @@ import rips
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is not None:
# Get a list of all wells
wells = resinsight.project.well_paths()
print("Got " + str(len(wells)) + " wells: ")
for well in wells:
print("Well name: " + well.name)
# Get a list of all wells
wells = resinsight.project.well_paths()
# will only work for editable well paths (ModeledWellPath)
new_well = well.duplicate()
print("New Well name: " + new_well.name)
print("Got " + str(len(wells)) + " wells: ")
for well in wells:
print("Well name: " + well.name)
# will only work for editable well paths (ModeledWellPath)
new_well = well.duplicate()
print("New Well name: " + new_well.name)
@@ -23,39 +23,39 @@ def print_dictionary(title, data):
# Connect to ResInsight
resinsight = rips.Instance.find()
if resinsight is not None:
# Get a list of all wells
wells = resinsight.project.well_paths()
# Find the first case
cases = resinsight.project.cases()
c = cases[0] if len(cases) else None
# Get a list of all wells
wells = resinsight.project.well_paths()
for well in wells:
result = well.trajectory_properties(resampling_interval=10.0)
# Find the first case
cases = resinsight.project.cases()
c = cases[0] if len(cases) else None
if c:
# Convert the result data into points
positions = [
list(coord)
for coord in zip(
result["coordinate_x"],
result["coordinate_y"],
result["coordinate_z"],
)
]
for well in wells:
result = well.trajectory_properties(resampling_interval=10.0)
# Extract some properties
properties = [
(rips.PropertyType.DYNAMIC_NATIVE, "PRESSURE", 0),
(rips.PropertyType.STATIC_NATIVE, "FAULTDIST", 0),
]
if c:
# Convert the result data into points
positions = [
list(coord)
for coord in zip(
result["coordinate_x"],
result["coordinate_y"],
result["coordinate_z"],
)
]
for property_type, property_name, time_step in properties:
porosity_model = rips.PorosityModelType.MATRIX_MODEL
result[property_name] = c.grid_property_for_positions(
positions, property_type, property_name, time_step, porosity_model
)
# Extract some properties
properties = [
(rips.PropertyType.DYNAMIC_NATIVE, "PRESSURE", 0),
(rips.PropertyType.STATIC_NATIVE, "FAULTDIST", 0),
]
title = "Well name: " + well.name
print_dictionary(title, result)
for property_type, property_name, time_step in properties:
porosity_model = rips.PorosityModelType.MATRIX_MODEL
result[property_name] = c.grid_property_for_positions(
positions, property_type, property_name, time_step, porosity_model
)
title = "Well name: " + well.name
print_dictionary(title, result)
+13 -19
View File
@@ -116,7 +116,7 @@ class Instance:
init_timeout: int = 300,
command_line_parameters: List[str] = [],
enable_heartbeat: bool = True,
) -> Optional[Instance]:
) -> Instance:
"""Launch a new Instance of ResInsight. This requires the environment variable
RESINSIGHT_EXECUTABLE to be set or the parameter resinsight_executable to be provided.
The RESINSIGHT_GRPC_PORT environment variable can be set to an alternative port number.
@@ -135,7 +135,7 @@ class Instance:
server periodically and aborts pending RPCs if it dies. Disable on
slow boxes where false positives matter (long GC pauses, debugger).
Returns:
Instance: an instance object if it worked. None if not.
Instance: a connected instance object. Raises :class:`RipsError` on failure.
"""
requested_port: int = 50051
@@ -193,29 +193,23 @@ class Instance:
process = subprocess.Popen(parameters)
pid = process.pid
if pid:
port = Instance.__read_port_number_from_file(
port_number_file, init_timeout
)
if port == -1:
# Need to kill the process using PID since there is no GRPC connection to use.
Instance.__kill_process(pid)
raise RipsError("Unable to read port number. Launch failed.")
else:
instance = Instance(
port=port,
launched=True,
enable_heartbeat=enable_heartbeat,
)
return instance
return None
port = Instance.__read_port_number_from_file(port_number_file, init_timeout)
if port == -1:
# Need to kill the process using PID since there is no GRPC connection to use.
Instance.__kill_process(pid)
raise RipsError("Unable to read port number. Launch failed.")
return Instance(
port=port,
launched=True,
enable_heartbeat=enable_heartbeat,
)
@staticmethod
def find(
start_port: int = 50051,
end_port: int = 50071,
enable_heartbeat: bool = True,
) -> Optional[Instance]:
) -> Instance:
"""Search for an existing Instance of ResInsight by testing ports.
By default we search from port 50051 to 50071 or if the environment