383 Commits
Author SHA1 Message Date
Kristian Bendiksen 393e52cfdc #14063 Well Event: Emit multi-record keywords (e.g. TUNING) correctly
buildKeyword placed every user-supplied item into a single DeckRecord,
so multi-record keywords such as TUNING (3 records, each terminated by
its own '/') were emitted with only one record/slash and rejected by the
simulator.

Distribute items across the keyword's records using the OPM ParserKeyword
schema: each item is matched to the record that defines it. Every record
is emitted (empty records are written as a bare '/') so the schema's
record structure is preserved, and items unknown to the schema are now
reported via a warning. Single-record keywords keep their previous
behaviour, including appending non-schema items after the canonical
block.
2026-05-29 15:43:30 +02:00
Kristian Bendiksen 9515e6c277 #13273 Well Event: Support completion number on perforation events for COMPLUMP
Add a completion_number parameter to the add_perf_event Python API
(AddPerfEvent scriptable method) and a matching field on
RimWellEventPerf. When a timeline perforation event is materialised into
a RimPerforationInterval, a non-zero completion number is copied across,
so it flows through the existing completion-data path onto
RigCompletionData.

The schedule generator now emits COMPLUMP alongside COMPDAT: COMPDAT
generation is folded into generateCompletionsForWell, which computes the
completion data once and builds both keywords from it via the existing
RimKeywordFactory::complumpKeyword (added in #13273). COMPLUMP only
produces records for perforations that have a completion number and is
merged across wells like COMPDAT.
2026-05-29 15:43:30 +02:00
Kristian Bendiksen fab4a4c472 #14079 Well Event: Keep WELSEGS and COMPSEGS per-well instead of merging
WELSEGS and COMPSEGS carry a per-well header record followed by that
well's segment records, so segments from different wells cannot share a
single header. Commit 6ef024c merged records of the same keyword across
wells, which is invalid for these two keywords.

Accumulate WELSEGS and COMPSEGS as separate per-well blocks in a
dedicated container and serialise each block individually, while the
remaining keywords (WELSPECS, COMPDAT, WSEGVALV, WSEGAICD, well-control
and schedule keywords) continue to merge. Emission preserves the
existing priority ordering.
2026-05-29 15:43:30 +02:00
Kristian Bendiksen 24269b07d5 #14079 Well Event: Export MSW keywords per well via explicit well list
Replace the include_welsegs / include_compsegs boolean flags on the
schedule-generation API with a list of wells (export_msw_for_wells) that
gates the multi-segment-well keywords. WSEGVALV and WSEGAICD are now gated
the same way as WELSEGS and COMPSEGS: all four are emitted only for wells
present in the list. An empty list (the default) suppresses MSW export for
all wells, while COMPDAT and well-control keywords are unaffected.

C++: RicScheduleDataGenerator takes a std::set<const RimWellPath*> mswWells
and generateMswForWell early-returns for wells not in the set.
RimcWellEventTimeline_generateSchedule replaces the two bool fields with a
scriptable PdmPtrArrayField<RimWellPath*>, resolved via
ptrReferencedObjectsByType().

Python: generate_schedule_text / generate_schedule expose
export_msw_for_wells (List[WellPath]) instead of the boolean flags. Updates
the well_event_schedule example and adds regression tests covering the
empty-list default, per-well selection, and WSEGVALV/WSEGAICD gating.
2026-05-29 15:43:30 +02:00
Kristian Bendiksen c74ed10157 Well Event: generate_schedule takes an Eclipse case object directly
Replace the integer eclipse_case_id parameter on
RimcWellEventTimeline_generateSchedule with a
caf::PdmPtrField<RimEclipseCase*>. The execute() method no longer needs
to walk the project tree and look up the case by ID — the gRPC layer
resolves the object pointer for us.

generate_schedule_text passes the Eclipse case object through to the
underlying generate_schedule call, matching how add_perf_event /
add_well_keyword_event already take a WellPath object rather than an
ID.
2026-05-28 08:21:17 +02:00
Kristian Bendiksen 80417b9947 #14064 Well Event: Make WELSEGS and COMPSEGS optional in schedule export
Add include_welsegs and include_compsegs flags (default True, preserving
current behaviour) to the schedule-generation API. Plumbed through
RimcWellEventTimeline_generateSchedule -> RicScheduleDataGenerator ->
generateMswForWell, gating only the WELSEGS / COMPSEGS merge calls.
WSEGVALV and WSEGAICD are unaffected.

Python: generate_schedule_text gains include_welsegs / include_compsegs
keyword arguments that forward to generate_schedule. Callers wanting
control-only / well-header-only schedule updates can now suppress the
verbose MSW table blocks.
2026-05-28 08:21:17 +02:00
Kristian Bendiksen 6ef024cee9 #14063 Well Event: Group records of same keyword across wells
The schedule generator previously concatenated full keyword blocks
(`KEYWORD ... /\n/\n`) — one per well — producing verbose output where
each WELSPECS / COMPDAT / WCONHIST / WELSEGS / COMPSEGS / etc. block
appeared once per contributing well. Eclipse keywords are designed to
hold multiple records under a single header; the per-well repetition is
syntactically valid but bloats the file and obscures intent.

Switch `RicScheduleDataGenerator` to accumulate
`std::map<QString, Opm::DeckKeyword>` instead of raw concatenated text.
Each per-well helper now returns an `Opm::DeckKeyword`, and a new
`mergeKeyword` primitive appends its records into the accumulator entry,
creating the entry from the helper's keyword on first encounter. At
emission time each accumulated keyword is serialised once.

To support this, `RifEventKeywordFormatter` is split: each `format*`
QString function now wraps a corresponding `build*` function that
returns `std::optional<Opm::DeckKeyword>`. `RimWellEventKeyword` and
`RimKeywordEvent` expose a parallel `generateDeckKeyword` accessor.
The text-scraping `extractKeywordName` helper becomes dead code and is
removed.
2026-05-28 08:21:17 +02:00
Kristian Bendiksen d2e7bed6b8 Well Event: Emit RPTRST/RPTSCHED as proper mnemonic lists
Schedule-level keywords whose OPM ParserRecord is a single ALL-sized item
(RPTRST, RPTSCHED, ...) are not positional — they take a free-form list
of mnemonic tokens like `BASIC=2 DEN ROCKC NORST=1`. Previously each
`keyword_data` entry became its own DeckItem, and OPM emitted them
either as truncated positional values or as a quoted, space-collapsed
single string.

Detect that schema shape in `RifEventKeywordFormatter::formatKeyword`
and pack the user's entries into one RawString DeckItem of mnemonic
tokens: `KEY` for FLAG items, `KEY=VALUE` otherwise.

To distinguish flag mnemonics (`DEN`) from `KEY=1`-style entries, add a
new `FLAG` value to `RimWellEventKeywordItem::ItemType` plumbed end to
end (Python -> GRPC -> Rimc -> storage -> formatter). The Python API
now maps `bool True` to FLAG and silently drops `bool False`.

For positional keywords (WCONHIST etc.) FLAG items have no value to
emit and are rendered as default markers ("1*").

A `rawStringItem` helper is added to `RifOpmDeckTools` so the
mnemonic-list path can produce DeckItems whose OPM output is not
quoted.
2026-05-28 08:21:17 +02:00
Kristian Bendiksen 9ada4eed09 #14065 Well Event: Emit keyword items in canonical Eclipse order
`add_well_keyword_event` previously emitted items in `keyword_data` dict
insertion order. Eclipse keywords such as WCONHIST are positional, so a
non-canonical order silently corrupted the simulator input.

`RifEventKeywordFormatter::formatKeyword` now walks the OPM
`ParserRecord` schema and emits items in canonical order, inserting
`1*` defaults for intermediate gaps and dropping trailing unspecified
items. Items that are not part of the keyword schema (e.g. mnemonic-list
keywords like RPTRST/RPTSCHED) are appended after the canonical block in
caller-supplied order to preserve existing behaviour.

Fixes #14065.
2026-05-28 08:21:17 +02:00
Håkon Hægland be3621a66d Make RimMultiPlot column/row count scriptable via gRPC
* Make RimMultiPlot column/row count scriptable via gRPC
* Add pytest for scriptable multi-plot layout
2026-05-26 18:49:56 +02:00
Kristian Bendiksen 01ceb57323 #14001 Python: Drop hand-written PropertyType, use auto-generated PropertyType
Consolidate the hand-written PropertyType / NNCPropertyType enums in
enums.py into the auto-generated StrEnum classes that already exist in
generated_classes.py. enums.py and all its imports are removed.

The auto-generated class for caf::AppEnum<RiaDefines::ResultCatType>
was named "ResultType" because the existing scriptable m_resultType
fields used keyword "ResultType". Switch those fields to
CAF_PDM_InitScriptableFieldWithScriptKeywordNoDefault, keeping the XML
keyword "ResultType" (so project files still load) but exposing the
script keyword as "PropertyType". The generator now emits a single
PropertyType StrEnum that:

- matches the proto PropertyType for setters (FLOAT/INTEGER aside, the
  members callers actually use are identical),
- gains ALLAN_DIAGRAMS that the C++ side already knew about,
- loses the proto-only REMOVED/UNDEFINED sentinels (unused in any
  Python caller).

rips.NNCPropertyType was exported but unreferenced anywhere — dropped.

Updated Python callers:
- rips.ResultType.X -> rips.PropertyType.X across examples and tests.
- result_type= kwarg / .result_type attribute -> property_type /
  .property_type (the script keyword change renamed those everywhere
  the affected PdmObject fields are accessed from Python).
- view.apply_cell_result's hand-written wrapper now takes property_type
  instead of result_type and forwards via cell_result.property_type.

All 271 Python tests pass, ruff and clang-format clean.
2026-05-26 08:23:28 +02:00
Kristian Bendiksen a6c3ac2d1a #14001 Python: Demonstrate discrete property getters in active-cell example
Show how property_data_type, discrete_property_category_names, and
discrete_property_category_colors round-trip the values just written by
set_active_cell_property + set_discrete_property_category_names.
2026-05-26 08:23:28 +02:00
Kristian Bendiksen 8971ebfc9c #14001 Python: Auto-generate StrEnum return types for scriptable methods
Replace the hand-written PropertyDataType wrapper with first-class enum
return support in the code generator. Scriptable methods that derive
from the new caf::PdmEnumObjectMethod<T> template return a typed enum
value across the gRPC boundary; the Python generator emits a StrEnum
class with the texts pulled from the underlying caf::AppEnum<T> and
wraps the call site so callers receive `EnumClass(value)` directly.

caf::PdmDataContainerEnum is the small PdmObject the framework uses to
carry the enum text on the wire (single scriptable QString field).
RimcEclipseCase_propertyDataType becomes declarative: implement
executeEnum() returning the typed RiaDefines::ResultDataType and pick a
returnEnumScriptName() — the base handles serialization and reports
the four AppEnum members (UNKNOWN, FLOAT, DOUBLE, INTEGER) to the
generator.

Drop the hand-written PropertyDataType in enums.py — the generated one
in resinsight_classes is wire-equivalent and now the single source of
truth.
2026-05-26 08:23:28 +02:00
Kristian Bendiksen 740b59702b #14001 Python: Wrap property_data_type to return PropertyDataType enum
The auto-generated stub returns a DataContainerString, forcing call
sites to use .values[0]. Add a hand-written wrapper on Reservoir
(which both EclipseCase and the other reservoir types inherit from)
that unwraps the single-value container and returns the typed
PropertyDataType so a caller can simply write

    case.property_data_type(...) == rips.PropertyDataType.INTEGER

Update the round-trip tests to use the cleaner form.
2026-05-26 08:23:28 +02:00
Kristian Bendiksen c33c63e8d0 #14001 Python: Add getters for discrete grid property data type and category mapping
Add the inverse APIs to the existing discrete-property setters:

- case.property_data_type(property_type, property_name) returns "FLOAT" or
  "INTEGER", matching the data_type flag passed to set_active_cell_property
  / set_grid_property.
- case.discrete_property_category_names(property_name) and
  case.discrete_property_category_colors(property_name) return the
  {int: str} mappings previously bound via
  set_discrete_property_category_names, or empty dicts when nothing is bound.

Implemented as PdmObjectMethods on RimEclipseCase and
RimColorLegendCollection so the bindings surface automatically through
generated_classes.py.
2026-05-26 08:23:28 +02:00
Kristian Bendiksen 17b024e876 #4469 Python: Add async tests for Grid cell_centers and cell_corners
Fill the async-variant gaps in the Grid class. Both sync forms are
already exercised by test_10k; the new tests iterate the streaming
variants, accumulate the chunks, and assert length and a spot-check
cell against the sync result.

Fixes #4469.
2026-05-22 14:21:46 +02:00
Kristian Bendiksen f7e05d041a #4465 Python: Add async tests for remaining Properties commands
Fill the async-variant gaps in Properties / NNCProperties coverage that
already have sync tests: selected_cells_async, selected_cell_property_async,
nnc_connections_static_values_async, nnc_connections_generated_values_async.
Each test accumulates the streamed chunks and asserts equality with the
corresponding synchronous call.

Fixes #4465.
2026-05-22 14:21:46 +02:00
Kristian Bendiksen 8663cd5d8f #4468 Python: Add tests for Project plot, export_well_paths, fracture template, summary_cases
Cover four more Project APIs. test_plot_lookup mirrors the existing
views/view test using the WellLogPlot collection in 10KWithWellLog.rsp.
test_export_well_paths exercises the WELLPATHS export-folder path.
test_scale_fracture_template_and_set_containment opens
small-completion-export-fractures.rsp, asserts the scale factors round
trip via PDM descendants, then verifies set_fracture_containment by
saving the project and inspecting the .rsp XML (RimFractureContainment
fields are not scriptable). test_summary_cases uses
SIMPLE_SUMMARY2.SMSPEC and asserts summary_cases() reflects
import_summary_case.
2026-05-22 14:21:46 +02:00
Kristian Bendiksen 14a701773e #4468 Python: Add tests for Project import_formation_names and import_well_log_files
Verify import_formation_names via the FORMATION_NAMES branch of
available_properties on the target case. Verify import_well_log_files
via the well_log_folder argument, writing a minimal synthetic LAS file
into a tempdir and checking the returned well-path names contain the
LAS WELL header value.

Fixes #4468.
2026-05-22 14:21:46 +02:00
Kristian Bendiksen ac43c9ac71 #4468 Python: Add tests for Project save, views, well_path_by_name, grid_case_groups, key_values_async
Cover the easily-reachable gaps in Project API test coverage: save() round
trip, views()/view() lookup, well_path_by_name() hit and miss, the
grid_case_groups()/grid_case_group(id) lookup pair, and the async
key_values iterator.
2026-05-22 14:21:46 +02:00
Kristian Bendiksen ce3f2935d9 #4467 Python: Add async + classification tests for Case property/NNC APIs
Cover the remaining gaps in the Case Properties + NNC test batch:
grid_property_async, nnc_connections_async, and available_properties for
DYNAMIC_NATIVE and GENERATED branches.

Closes #4467.
2026-05-22 14:21:46 +02:00
Kristian Bendiksen ab577949bd #13842 Python: Do not check in generated _version.py
Remove the committed rips/_version.py and add it to .gitignore. The file
is generated by CMake from rips/_version.py.cmake at configure time, so
checking it in caused the source tree to carry stale/duplicated state.

Addresses review feedback on PR #13847.
2026-05-21 08:32:45 +02:00
Kristian Bendiksen 5486c615a1 #13842 Python: Migrate from setup.py to pyproject.toml
Replace legacy setup.py packaging with modern pyproject.toml (PEP 517/518/621).
Version is dynamically resolved via setuptools attr: directive from rips/_version.py,
which CMake generates.

Consolidate requirements.txt, dev-requirements.txt, and build-requirements.txt
into pyproject.toml dependencies and [dev] optional-dependencies.
Consolidate rips/mypy.ini into [tool.mypy] section of pyproject.toml.
Update GitHub Actions workflow and CMakeLists.txt accordingly.
2026-05-21 08:32:45 +02:00
Kristian Bendiksen ddcfef736b Python: Add filtered_cells API for evaluating a data filter on a case
Adds case.filtered_cells(filter, time_step, grid_index) which applies any
RimCellFilter to an Eclipse case and returns a per-cell 0/1 mask. The mask
length and ordering match case.grid_property(..., grid_index), so callers
can combine the two vectors element-wise.

Implemented as a scriptable Rimc method (PdmVoidObjectMethod) on
RimEclipseCase that writes the mask via the existing key-value store, with
a thin Python wrapper mirroring the visible_cells() pattern. No proto or
gRPC service changes.
2026-05-15 08:37:46 +02:00
Kristian Bendiksen c9456bba62 Python: Add cell filter reference to perforation intervals
Lets a script attach a cell filter to a RimPerforationInterval as a
typed reference so subsequent work can gate completion-export
connections by the filter's cell mask. Pure metadata in this commit:
no consumer of RimPerforationInterval is modified, the field exists
purely as an API surface.

Add a non-scriptable caf::PdmPtrField<RimCellFilter*> m_cellFilter to
RimPerforationInterval with public cellFilter()/setCellFilter()
accessors and a "Cell Filter" group in the property panel between the
existing geometry/flow group and the Date Settings group. The field is
deliberately non-scriptable: a scriptable PdmPtrField round-trips in
Python as an opaque "ClassName:0xADDR" string rather than a resolved
object, defeating the point. Two CAF object methods on
RimcPerforationInterval expose the relationship:

- AddFilter (perf.add_filter(filter)) sets the pointer; replaces any
  existing filter (single filter per perforation).
- cell_filter (perf.cell_filter()) resolves to the referenced
  RimCellFilter object on read, mirroring the valve.template() pattern.

Composition stays in RimCombinedFilter: a script that wants AND/OR
across multiple filters attaches a combined filter as the single
filter on the perforation.

The Python example in PythonExamples builds a case-level AND combined
filter (PERMX 100..20000 AND K=5..10), creates a modeled well path
with two perforation intervals, and shares the same filter between
them. test_perforation_filter.py covers single-set, replace-on-second-
add, and shared-pointer round-trips.
2026-05-15 08:37:46 +02:00
Kristian Bendiksen 3a1ff7a1e3 CellFilters: Auto-derive combined-filter name from children
Combined filters used to always show whatever literal string the user
passed in (or the default "Combined Filter"). For programmatic creation
without a name, that string never reflected the children, leaving the
case-level "Data Filters" tree row stale when child bounds changed.

Add an m_autoDeriveName flag (default true) to RimCombinedFilter. When
on, the display name is recomputed on every relevant change — addFilter,
removeFilter, onChildAdded, onChildFilterChanged, and combineMode
toggle — by joining children's name() with " AND " / " OR ". The flag
flips to false the first time the user renames the filter via the UI
property panel or via Python (combined.user_description = "..."),
preserving any explicitly typed name verbatim from then on.

Two child-creation paths needed an extra triggerFilterChanged() so the
combined parent recomputes after the child's final name is set:
addPropertyFilterToCombinedFilter (setDefaults updates name without a
signal) and RimcCombinedFilter_addRangeFilter (Rimc applies user
params after addNewFilter).

The Rimc factory passes setAutoDeriveName(false) only when a name is
supplied. The Python example demonstrates both modes side by side, and
test_combined_filter.py adds three cases: named filter preserves its
name, auto-derived filter tracks bound mutations, and Python rename
locks the auto-derived filter.
2026-05-15 08:37:46 +02:00
Kristian Bendiksen eb70d32eb1 Python: Add scriptable Data Filters API for combined filters
Expose the case-level RimDataFilterCollection (introduced for
case-shared cell filters) to Python so scripts can build combined
filters with property-filter and IJK-range children without going
through a view.

Scriptable surface:
- RimEclipseCase.m_dataFilterCollection -> case.data_filter_collection()
- RimDataFilterCollection (object + Filters child array) -> filters()
- RimEclipsePropertyFilter (object + m_lowerBound + m_upperBound) ->
  pf.lower_bound, pf.upper_bound
- RimCellRangeFilter (object + start_index_{i,j,k}, cell_count_{i,j,k})

New CAF object methods under ProjectDataModelCommands/:
- RimcDataFilterCollection: add_combined_filter(name, combine_mode)
- RimcCombinedFilter: add_property_filter(result_variable, result_type)
  and add_range_filter(name, start_{i,j,k}, cell_count_{i,j,k}).

Bounds are not part of add_property_filter because the Python class
generator emits a bare nan literal as the default for double fields
initialised to quiet_NaN, which fails to import. Bounds are set via
the now-scriptable lower_bound/upper_bound fields after creation,
matching the update() pattern used elsewhere in the API.

RicEclipsePropertyFilterFeatureImpl::addPropertyFilterToCombinedFilter
now returns the new RimEclipsePropertyFilter* so the CAF method can
hand it back to Python. setDefaults is relaxed to be view-tolerant:
case-level filters have no Rim3dView ancestor and the eclipse case is
already wired by RimEclipsePropertyFilter::setCase via the owning
collection's onItemsChanged hook.

End-to-end coverage in tests/test_combined_filter.py: round-trip
create, mode toggle, property-filter add/mutate, range-filter
add/mutate. Example script in PythonExamples builds an AND combined
filter that gates PERMX between 100 and 20000 against a K=5..10
slice.
2026-05-15 08:37:46 +02:00
Magne Sjaastad dc20a016f7 #13987 PDM: Migrate Surface and Polygon collections to PdmNestedCollection
Port RimSurfaceCollection, RimPolygonCollection and the polygon-in-view
side onto the new caf::PdmNestedCollection framework, replacing the
per-class folder plumbing (m_subCollections field, topmost-folder flag,
add/remove/find sub-collection, factory, "new folder" command) with the
shared base.

- RimSurfaceCollection and RimPolygonCollection now derive from
  caf::PdmNestedCollection. Each constructor uses the generic folder
  icon and exposes a static createTopmost() that marks the top instance
  and applies the branded domain icon; RimOilField calls the factory.
- RimPolygonInViewCollection uses the new RimNestedMirrorCollectionInView
  helper, and a new RimPolygonContainer carries the source-side polygon
  link.
- RicNewSurfaceCollectionFeature is removed; the generic
  RicNewNestedCollectionFeature drives the "Add Folder" action against
  both collections.
- Update callers (RimAnnotationCollection, RimEnsembleSurface, polygon
  command features) and the scripting wrapper (RimcSurfaceCollection),
  plus the Python add-folder binding and polygon test.
2026-05-13 14:53:00 +02:00
Kristian Bendiksen b775f42057 #13982 Python: Demonstrate string form alongside StrEnum in examples and tests
The property/porosity/data-type arguments accept either a typed StrEnum
(rips.PropertyType.DYNAMIC_NATIVE) or a plain string ("DYNAMIC_NATIVE").
Keep both forms visible to users:

- soil_average_sync.py and input_prop_test_sync.py now use string
  arguments and point at their _async counterparts for the typed-enum
  form. The sync/async examples otherwise mirror each other, so each
  pair documents one form.

- Add test_10k_property_strings to exercise available_properties,
  active_cell_property, grid_property, and set_active_cell_property
  (including data_type="INTEGER") with strings only, so a future
  refactor that narrows the accepted types would fail the suite.
2026-05-13 13:47:40 +02:00
Kristian Bendiksen e7608536cf #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.
2026-05-13 13:47:40 +02:00
Kristian Bendiksen 1424a6dc53 #13982 Python: Add type hints to remaining methods in case.py
Extend type annotations across the rest of rips.case beyond the property
and enum methods typed in the previous commit. Adds parameter and return
types to the command wrappers (replace, export_*, create_*, import_*),
the simple getters (time_steps, reservoir_boundingbox,
distance_to_closest_fault, days_since_start, view, views, create_view,
simulation_wells, coarsening_info), the selected-cells and NNC accessors
(selected_cells*, nnc_connections*, nnc_connections_{static,dynamic,
generated}_values*), and the private grpc helpers
(__custom_init__, __request, __generate_property_input_iterator,
__generate_property_input_chunks, __nnc_connections_values_async,
__nnc_values_generator_to_list, __generate_nnc_property_input_chunks).

Uses Iterator[X] for grpc streaming responses, List[X] for collected
sequences, the proto message types from Case_pb2 / Properties_pb2 /
NNCProperties_pb2 / Definitions_pb2 for typed payloads, Optional and
Union[str, List[str]] for nullable or coerced-list parameters, and Any
for the heterogeneous _execute_command return values. No runtime
behavior changes; mypy --strict is clean on case.py.
2026-05-13 13:47:40 +02:00
Kristian Bendiksen 79914f2a00 #13982 Python: Type case API with StrEnum classes for proto enums
The previous commit auto-generated StrEnum classes for caf::AppEnum scriptable
fields, but the hand-written gRPC API in rips.case still accepted bare strings
for proto-backed enums (PropertyType, PropertyDataType, NNCPropertyType) and
the AppEnum-backed PorosityModelType.

Add a new rips.enums module with hand-written StrEnum classes for the three
proto-only enums and reuse the auto-generated PorosityModelType from
resinsight_classes. Re-export the new enums from the rips package and narrow
the parameter type hints across cell_count, cell_info_for_active_cells,
available_properties, active_cell_property, selected_cell_property,
grid_property, set_active_cell_property, set_grid_property,
active_cell_centers, active_cell_corners, set_nnc_connections_values, and
grid_property_for_positions (sync and async variants).

Existing string arguments keep working because StrEnum members are strings
and protobuf's EnumTypeWrapper.Value accepts both. A new
test_10k_grid_property_strenum asserts string and enum forms return the same
data.
2026-05-13 13:47:40 +02:00
Kristian Bendiksen 21f1330232 #13982 Python: Generate StrEnum classes for AppEnum-typed fields
Auto-generate enum.StrEnum classes from cafPdmPythonGenerator for every
caf::AppEnum-backed scriptable field, deduplicated by C++ AppEnum type.
Generated PdmObject attributes and method arguments now reference the
typed enum (e.g. self.depth_type: DepthType = DepthType.MEASURED_DEPTH)
instead of bare str. Member names are sanitized against Python keywords
and identifier rules (None -> None_, R-G -> R_G).

Existing scripts that pass plain strings keep working because StrEnum
members are strings; the wire format is unchanged.

Bumps python_requires to >=3.11 (StrEnum). Adds an Enum branch in
PdmObjectBase.__convert_to_grpc_value so future IntEnum use is also
handled.
2026-05-13 13:47:40 +02:00
Kristian Bendiksen 96dc4df316 #9336 Python: Tolerate transient heartbeat failures
The heartbeat introduced in db871296c closed the gRPC channel after a
single missed ping (2s deadline). On Windows CI under load this fired
during normal operations, leaving every subsequent test stuck on a
dead channel.

Track consecutive failures inside the heartbeat loop and only declare
the connection lost after failure_threshold (default 3) consecutive
failures. A successful ping resets the counter. Per-ping deadline
raised from 2s to 5s, and below-threshold failures log a warning so
transient slowness is still visible.
2026-05-04 20:03:39 +02:00
Kristian Bendiksen 85fc0220c7 #9336 Python: Translate closed-channel errors and stop downgrading RipsError
After the heartbeat closes a dead-server channel, gRPC raises
ValueError("Cannot invoke RPC on closed channel!") rather than a
grpc.RpcError, which slipped past the existing pdmobject wrappers and
surfaced to the user as a bare ValueError nested in a traceback.

- pdmobject add_method decorator and _call_pdm_method_void /
  _return_value / _return_optional_value now also catch the
  closed-channel ValueError and translate it to RipsError.
- well_path.add_well_log no longer rewraps every exception as
  RuntimeError. Cleanup of temporary key-value entries still runs, but
  the original typed exception (e.g. RipsError with code/details) is
  re-raised so callers can inspect it.
2026-05-04 20:03:39 +02:00
Kristian Bendiksen f8d180f48d #9336 Python: Improve error reporting from gRPC to client
Surface gRPC failure context to Python script authors instead of
silently returning sentinel values or raising bare gRPC errors.

- RipsError now carries code/details/location and a from_rpc_error()
  helper. Existing single-arg construction stays valid.
- Stop swallowing grpc.RpcError in case.__grid_count for non-NOT_FOUND
  failures and in instance._check_connection_and_version, so the
  underlying status is propagated.
- pdmobject add_method decorator and _call_pdm_method_* attach
  code/details to the raised RipsError; existing message text is
  preserved so pytest match=... assertions keep working.
- Instance.start_heartbeat / stop_heartbeat / check_alive provide an
  opt-in background ping that flips a sticky lost-connection flag and
  raises a readable RipsError from check_alive() if the server dies.
- Server side: GetPdmObject in RiaGrpcAppService, RiaGrpcCaseService
  and RiaGrpcProjectService no longer return Status::OK with an empty
  reply when the underlying object is missing; they return NOT_FOUND
  / INTERNAL with a descriptive message. Other RiaGrpc*Service files
  audited and already return non-OK with text on error paths.
2026-05-04 20:03:39 +02:00
Kristian Bendiksen 2d88be506d #13908 Python: Add API to read well log information
Expose well logs attached to a well path through the Python API. A new
`well_path.well_logs()` returns the list of well logs, and each
`WellLog` gains `channel_names()` and `well_log_data()` for reading
channel names, measured depth, optional TVD MSL/RKB, and channel
values.

The read methods are registered on the abstract `RimWellLog` base
class and use the common `RigWellLogData` interface so they work for
imported, LAS and OSDU well logs alike.
2026-04-23 19:50:05 +02:00
Magne Sjaastad 03409aed70 #13806 Python: Add category mapping API for discrete properties
Introduce set_discrete_property_category_names on Case to bind integer
result values to text labels and optional colors. Internally it creates a
custom ColorLegend, appends one item per value (with an auto-assigned
palette color when the caller does not provide one) and registers the
legend as the default for the (case, resultName) pair.

Also add Project.color_legend_collection() as a dedicated accessor for
the project's ColorLegendCollection, pytest coverage for legend creation
and the default-legend-for-result binding, and two PythonExamples
demonstrating the end-to-end discrete property + category label flow.

#13806 Python: Silence mypy warnings for category_mapping module
2026-04-20 18:55:36 +02:00
Magne Sjaastad 75c1cedaec #13806 Python: Add API for creating discrete grid properties
Add a data_type parameter to set_active_cell_property, set_active_cell_property_async
and set_grid_property so callers can upload INTEGER-typed discrete properties in
addition to the existing FLOAT properties. Previously a discrete property required
the result name to end with "NUM"; the explicit data type makes the intent
independent of the name.

The gRPC service now forwards the selected data type to
RigCaseCellResultsData::createCategoryResult, and the Python tests cover both
float and integer property uploads.
2026-04-20 18:55:36 +02:00
Kristian Bendiksen b707c4588b #13841 Python: Add available_properties API for regular surfaces 2026-04-13 10:53:06 +02:00
Kristian Bendiksen 68b193cd56 #13841 Python: Add get_property API for regular surfaces 2026-04-09 07:59:34 +02:00
Copilot 9701f695d8 Python: Expose port number in the Instance class 2026-03-23 08:59:52 +01:00
Kristian Bendiksen 614acc664a #13682 Python: Expose polygon appearance properties to Python API
Make RimPolygonAppearance scriptable so polygon visual properties
(colors, line thickness, spheres, closed state, plane lock) can be
controlled from the Python API.
2026-03-12 08:30:52 +01:00
Kristian Bendiksen 576c2179da #13682 Python: Add API for well path color
Make m_wellPathColor scriptable so it is accessible via the Python API
as well_path_color (hex string). Add a test and update the
import_well_paths_and_logs example to demonstrate setting colors.
2026-03-12 08:30:52 +01:00
Kristian Bendiksen 00b4f180a2 #13515 Fix comma-aware parsing of gRPC string values in Python
Replace naive split(", ") in __makelist() with a quote-aware state
machine that respects quoted strings and nested brackets. Also add
__unescape_string() to reverse C++ escaping, and fix strip('"') to
only remove the outer quote pair.
2026-03-06 20:56:48 +01:00
JJ 24be78f105 Tie-in valve improvements (#13661)
Support (bool, something) pairs as tuples on python side.
Support custom outlet valve md
Add python test for custom outlet valve md
Use tie-in depth as default for custom valve md
Standalone valve creation and display updates
Update MSW valve export
Add valve open/shut flag.
Add python examples
2026-03-06 12:01:14 +01:00
kriben fcff011200 Python code linting changes detected by ruff 2026-03-06 08:52:04 +01:00
Kristian Bendiksen 27042e4bab Python: Fix ruff E402 lint errors in __init__.py
Move typing import to top of file and add noqa: E402 comments
to imports that must follow the sys.path.insert call.
2026-03-06 08:52:04 +01:00
Magne Sjaastad d9e3967519 Add example on creation of regions based on existing polygons 2026-03-06 08:22:16 +01:00
Kristian Bendiksen 75bc84e146 #12452 Python: Replace logger.error with raise RipsError in instance.py
Error conditions in launch() and find() now raise RipsError instead of
logging and returning None, making failures explicit. Updated test to
expect the exception.
2026-03-05 15:58:25 +01:00