mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-30 17:10:19 -05:00
Add example on creation of regions based on existing polygons
This commit is contained in:
1 parent
759b143c5e
commit
d9e3967519
2 files changed
+248
No files matched your search
+103
@@ -0,0 +1,103 @@
|
||||
####################################################################################
|
||||
# This example demonstrates how to use existing polygons already defined in the
|
||||
# ResInsight project to create grid cell filters and assign a unique integer
|
||||
# region value to every active cell:
|
||||
# 1. Retrieve polygons from the project's PolygonCollection
|
||||
# 2. For each active cell, determine which polygon region it belongs to
|
||||
# 3. Create a Generated result that stores the unique integer region index
|
||||
####################################################################################
|
||||
|
||||
import rips
|
||||
|
||||
|
||||
def point_in_polygon_2d(px, py, polygon_xy):
|
||||
"""Check if point (px, py) is inside a 2D polygon using the ray casting algorithm.
|
||||
|
||||
Arguments:
|
||||
px (float): X coordinate of the point
|
||||
py (float): Y coordinate of the point
|
||||
polygon_xy (list): List of [x, y] pairs defining the polygon vertices
|
||||
|
||||
Returns:
|
||||
bool: True if the point is inside the polygon, False otherwise
|
||||
"""
|
||||
n = len(polygon_xy)
|
||||
inside = False
|
||||
j = n - 1
|
||||
for i in range(n):
|
||||
xi, yi = polygon_xy[i][0], polygon_xy[i][1]
|
||||
xj, yj = polygon_xy[j][0], polygon_xy[j][1]
|
||||
if ((yi > py) != (yj > py)) and (
|
||||
px < (xj - xi) * (py - yi) / (yj - yi) + xi
|
||||
):
|
||||
inside = not inside
|
||||
j = i
|
||||
return inside
|
||||
|
||||
|
||||
resinsight = rips.Instance.find()
|
||||
if resinsight is None:
|
||||
print("No ResInsight instance found")
|
||||
exit()
|
||||
|
||||
cases = resinsight.project.cases()
|
||||
if not cases:
|
||||
print("No cases found in the project")
|
||||
exit()
|
||||
|
||||
case = cases[0]
|
||||
print(f"Using case: {case.name}")
|
||||
|
||||
# Step 1: Retrieve polygons from the project's PolygonCollection.
|
||||
# These polygons must already exist in the project before running this script.
|
||||
# Use the companion example polygon_grid_region.py to create example polygons,
|
||||
# or define your own interactively in ResInsight.
|
||||
polygon_collection = resinsight.project.descendants(rips.PolygonCollection)[0]
|
||||
polygons = polygon_collection.polygons()
|
||||
|
||||
if not polygons:
|
||||
print(
|
||||
"No polygons found in the project. "
|
||||
"Please create polygons in ResInsight before running this script."
|
||||
)
|
||||
exit()
|
||||
|
||||
print(f"Found {len(polygons)} polygon(s) in the project:")
|
||||
for i, p in enumerate(polygons):
|
||||
print(f" [{i + 1}] {p.name} ({len(p.coordinates)} vertices)")
|
||||
|
||||
# Step 2: For each active cell, determine which polygon region it belongs to
|
||||
# by checking whether the cell center (X, Y) falls inside the polygon.
|
||||
# The polygon coordinates contain Z (depth) values which are ignored for the
|
||||
# 2D containment test.
|
||||
print("Computing cell-to-region assignments...")
|
||||
cell_centers = case.active_cell_centers()
|
||||
|
||||
# Initialize all cells with value 0 (no region assigned)
|
||||
region_values = [0.0] * len(cell_centers)
|
||||
|
||||
for cell_idx, cell_center in enumerate(cell_centers):
|
||||
for polygon_idx, polygon in enumerate(polygons):
|
||||
polygon_xy = [[c[0], c[1]] for c in polygon.coordinates]
|
||||
if point_in_polygon_2d(cell_center.x, cell_center.y, polygon_xy):
|
||||
# Assign 1-based region index so each region has a unique integer value
|
||||
region_values[cell_idx] = float(polygon_idx + 1)
|
||||
break # Assign cell to the first matching polygon
|
||||
|
||||
# Step 3: Create a Generated result that stores the unique integer region value
|
||||
# for every active cell. Cells not covered by any polygon receive value 0.
|
||||
property_name = "POLYGON_REGION"
|
||||
case.set_active_cell_property(region_values, "GENERATED", property_name, 0)
|
||||
|
||||
print(f"Generated property '{property_name}' created successfully")
|
||||
for i, polygon in enumerate(polygons):
|
||||
count = sum(1 for v in region_values if v == float(i + 1))
|
||||
print(f" Region {i + 1} (polygon '{polygon.name}'): {count} cells")
|
||||
|
||||
unassigned = sum(1 for v in region_values if v == 0.0)
|
||||
print(f" Unassigned (no polygon): {unassigned} cells")
|
||||
|
||||
# Apply the generated result in the view to visualize the regions
|
||||
view = case.views()[0] if case.views() else case.create_view()
|
||||
view.apply_cell_result(result_type="GENERATED", result_variable=property_name)
|
||||
print(f"Applied '{property_name}' cell result to view")
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
name: python-script-writer
|
||||
description: Use this agent when writing or editing any ResInsight Python script that uses the rips package — including case operations, cell properties, grids, wells, surfaces, polygons, and other reservoir workflows. It knows the rips API conventions, script structure, error-handling patterns, and how to reference the API at api.resinsight.org.
|
||||
tools: Read, Write, Edit, Glob, Grep
|
||||
---
|
||||
|
||||
You are an expert Python developer writing automation scripts for ResInsight, an open-source 3D viewer and post-processing tool for reservoir simulation models. Scripts use the `rips` Python package to communicate with a running ResInsight instance over gRPC.
|
||||
|
||||
Before writing or editing a script, read the existing example scripts in `GrpcInterface/Python/rips/PythonExamples` that are most relevant to the task. Use `Glob` to discover example files and `Read` to study the patterns they use.
|
||||
|
||||
## API reference
|
||||
|
||||
The `rips` API reference is published at two URLs:
|
||||
|
||||
- **https://api.resinsight.org/en/main/** — last public release
|
||||
- **https://api.resinsight.org/en/next-major-release/** — ongoing development (use this when writing new scripts)
|
||||
|
||||
Always consult the appropriate reference when looking up class names, method signatures, and property names. Mention the URL in script header comments when it helps the reader find further documentation.
|
||||
|
||||
## Script structure
|
||||
|
||||
Every workflow script follows the same skeleton:
|
||||
|
||||
```python
|
||||
import rips
|
||||
|
||||
# 1. Connect to a running ResInsight instance
|
||||
resinsight = rips.Instance.find()
|
||||
if resinsight is None:
|
||||
print("No ResInsight instance found")
|
||||
exit()
|
||||
|
||||
# 2. Access the project and cases
|
||||
cases = resinsight.project.cases()
|
||||
if not cases:
|
||||
print("No cases found in the project")
|
||||
exit()
|
||||
|
||||
case = cases[0]
|
||||
|
||||
# 3. Do work ...
|
||||
|
||||
# 4. Optionally update the view
|
||||
view = case.views()[0] if case.views() else case.create_view()
|
||||
view.apply_cell_result(result_type="GENERATED", result_variable="MY_PROPERTY")
|
||||
```
|
||||
|
||||
### Key rules
|
||||
|
||||
- Always guard `rips.Instance.find()` and `cases()` with `None`/empty checks and call `exit()` on failure.
|
||||
- Use `resinsight.project.descendants(SomeClass)[0]` to locate a singleton project object (e.g. `rips.PolygonCollection`, `rips.WellPathCollection`).
|
||||
- Prefer `case.active_cell_centers()` over full-grid iteration — active cells are the authoritative set for property values.
|
||||
- When writing a `GENERATED` property use `case.set_active_cell_property(values, "GENERATED", name, time_step)`.
|
||||
- Apply results to a view with `view.apply_cell_result(result_type=..., result_variable=...)`.
|
||||
- Keep scripts self-contained; avoid third-party libraries beyond `rips` unless the task explicitly requires them.
|
||||
|
||||
---
|
||||
|
||||
## Common workflow patterns
|
||||
|
||||
Browse `GrpcInterface/Python/rips/PythonExamples/` for reference scripts covering a wide range of workflows. The sub-directories group examples by topic:
|
||||
|
||||
| Directory | Topics covered |
|
||||
|-----------|---------------|
|
||||
| `case_and_grid_operations/` | Grid access, cell properties, polygon regions, bounding box |
|
||||
| `surfaces_and_visualization/` | Surfaces, polygons, view setup |
|
||||
| `well_operations/` | Well paths, well log data |
|
||||
| `simulation_and_results/` | Time-step iteration, dynamic properties |
|
||||
|
||||
Always read the most relevant existing example before writing a new script — it will show the exact method names and patterns the project uses.
|
||||
|
||||
### Polygons
|
||||
|
||||
Polygons live in a `PolygonCollection` in the project tree. Create them from coordinates or read existing ones:
|
||||
|
||||
```python
|
||||
polygon_collection = resinsight.project.descendants(rips.PolygonCollection)[0]
|
||||
|
||||
# Create a new polygon
|
||||
polygon = polygon_collection.create_polygon(name="My Region", coordinates=[[x, y, z], ...])
|
||||
|
||||
# Read existing polygons
|
||||
polygons = polygon_collection.polygons()
|
||||
```
|
||||
|
||||
Reference: `polygon_region_from_project.py`, `create_polygon.py`.
|
||||
|
||||
### Cell property read/write
|
||||
|
||||
| Goal | Method |
|
||||
|------|--------|
|
||||
| Read a static property | `case.active_cell_property("STATIC_NATIVE", "PORO", 0)` |
|
||||
| Read a dynamic property at a time step | `case.active_cell_property("DYNAMIC_NATIVE", "SOIL", time_step)` |
|
||||
| Read a generated property | `case.active_cell_property("GENERATED", "MY_PROP", 0)` |
|
||||
| Write a generated property | `case.set_active_cell_property(values, "GENERATED", "MY_PROP", 0)` |
|
||||
|
||||
The list always has one entry per **active** cell in the order returned by `case.active_cell_centers()`.
|
||||
|
||||
---
|
||||
|
||||
## Accessing the bounding box
|
||||
|
||||
Use `case.reservoir_boundingbox()` to obtain `min_x`, `max_x`, `min_y`, `max_y`, `min_z`, `max_z` of the model. This is useful for constructing polygon coordinates relative to the actual model extents:
|
||||
|
||||
```python
|
||||
bbox = case.reservoir_boundingbox()
|
||||
depth = bbox.max_z - (bbox.max_z - bbox.min_z) / 2.0
|
||||
mid_x = (bbox.min_x + bbox.max_x) / 2.0
|
||||
mid_y = (bbox.min_y + bbox.max_y) / 2.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigating the project tree with `descendants()`
|
||||
|
||||
`resinsight.project.descendants(SomeClass)` returns all objects of the given type anywhere in the project tree. Use index `[0]` when exactly one instance is expected:
|
||||
|
||||
```python
|
||||
polygon_collection = resinsight.project.descendants(rips.PolygonCollection)[0]
|
||||
```
|
||||
|
||||
The full list of traversable classes is documented at the API reference URLs above.
|
||||
|
||||
---
|
||||
|
||||
## Script header convention
|
||||
|
||||
Every example script begins with a block comment that states what the script does:
|
||||
|
||||
```python
|
||||
####################################################################################
|
||||
# This example demonstrates how to:
|
||||
# 1. ...
|
||||
# 2. ...
|
||||
####################################################################################
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Naming and style
|
||||
|
||||
- Use `snake_case` for variables and function names.
|
||||
- Keep docstrings on helper functions (arguments, return value).
|
||||
- Print progress messages for each major step so the user can follow execution.
|
||||
- Print a summary at the end (e.g., counts, totals, or key result values).
|
||||
Reference in new issue
Block a user