The grid calculation data filter evaluated index filters (polygon, user
defined) directly on every grid. An INDEX_K polygon evaluated on a fine
LGR can select no cells, so refined cells whose parent host cell is
inside the polygon were dropped from the aggregation. This gave a lower
sum than the histogram of visible cells in the 3d view, which renders
LGR cells following their parent grid cell.
Hoist the parent grid visibility propagation out of the range filter
branch so it also runs for index filters. Property filters are evaluated
per cell against their own result values and are left untouched.
The cell filter view filter type was changed to use the cell filter
geometry to include cells inside the filters that are inactive in the
view's case, at the cost of ignoring property filters in the view. That
use case is now covered by the data filter type, which evaluates the
filters per calculation case. Restore the original visible cells
semantics for the cell filter view filter type, so property filters in
the view affect the calculation again.
Add a data filter collection to RimReservoirGridEnsemble, so filters
can be defined once for the ensemble instead of per realization. The
filters are bound to the main case of the ensemble for configuration,
and evaluated per case as elsewhere.
The filter creation commands are available from a Data Filters submenu
on the ensemble node, and the collection node is hidden while empty,
matching the case-level behavior. The grid calculation data filter
options include the filters of the ensemble the destination case
belongs to.
Add a filter type option to the grid calculation: None, Cell Filter
View or Data Filter. In data filter mode a single filter from the
destination case's Data Filters collection is selected, and the filter
is evaluated per calculation case and time step using each case's own
active cells and result values. Property based filtering of ensemble
calculations then uses each realization's own property values, which is
not possible with view based filtering.
Old projects with a cell filter view are migrated to the cell filter
view mode. Complex filtering is composed with a combined filter (AND/OR)
in the Data Filters collection.
Add a sourceCaseOverride parameter to RimCellFilter::applyToCellVisibility.
When set, property filters evaluate against the override case's result
values instead of the case the filter is bound to, allowing the same
filter definition to be evaluated against each case in an ensemble.
Combined filters forward the override to their children, and geometry
based filters ignore it.
Add RimCellFilterTools::computeReservoirCellVisibility, which evaluates
a cell filter against a given case for all grids and returns the
visibility indexed by reservoir cell index. Range filters propagate the
parent grid visibility into LGR cells, matching the filtered geometry
of a 3d view.
Also add RimCellFilter::setFilterMode.
The visibility mask for 'filter by view' was taken from the view's
visible cells, which are limited to the active cells of the view's
case. When applying the calculation to additional cases with different
ACTNUM, cells inside the filter geometry that are inactive in the
view's case were excluded from the aggregation for cases where they are
active, giving too low sums.
Build the mask from the cell filter geometry (RANGE_FILTERED +
RANGE_FILTERED_INACTIVE) instead, so each calculation case contributes
its own active cells inside the filters. Property filters in the view
no longer affect the calculation. The result filtering now uses the
same mask as the input filtering, also when the visibility filter is
provided by the caller.
Log at debug level, for each case and time step, how many input values
contribute to a grid calculation aggregation and how many were replaced
with the default value (non-visible cells and undefined values). Also
log the sum and number of values used when computing visible cells
statistics, to allow comparison with the histogram sum in the 3d view.
Aggregation functions like sum() in the expression parser include all
values in the input vector. Undefined values (HUGE_VAL) could leak into
the aggregation input when a grid case group member has fewer time steps
on disk than the main case it inherits result meta data from, or when a
cell is active in the calculation case but has no value in the source
data. This produced infinite sums for some realizations, while the
histogram statistics ignore undefined values.
Replace undefined values in the aggregation input with the default value
0.0, matching the histogram semantics, and log a warning per case and
variable when values were replaced.
* #14760 Move time-step control and grid-only hooks from Rim3dView to RimGridView
* #14760 Store named 3D visualization models in a shared collection instead of per-model fields
RimGridCalculation::AdditionalCasesType::ALL_CASES and
RimIntersectionFilterEnum::INTERSECT_FILTER_BETWEEN both reused the
serialization text of a sibling enum value (NONE and
INTERSECT_SHOW_BELOW respectively), so they would silently round-trip
as the wrong value when read back from a project file.
AppEnumMapperBase::addItem only asserted alias uniqueness, not text
uniqueness, so this class of bug went undetected. Add a CAF_ASSERT
checking that a new enum value's serialization text does not match an
existing value's text or aliases.
Found while investigating the AppEnum alias-duplication note in #14654.
RimEnsembleCurveFilter::applyFilter() (SUMMARY_VALUE mode) only compared
the value at the last time step inside the selected time range against
the filter's value range, discarding the result of every earlier time
step. For monotonically increasing cumulative vectors (e.g. FOPT) this
coincidentally matched user expectations, but for rate vectors (e.g.
FOPR) that oscillate at high frequency between zero and a non-zero
value, the outcome depended arbitrarily on whether the last time step
happened to land on zero or not.
The filter now excludes a realization if any time step inside the
selected time range has a value outside the selected value range, i.e.
all time steps in range must be inside the value range for the
realization to be kept. This makes the behavior consistent between
FOPT and FOPR style vectors.
Added a regression test reproducing the oscillating-rate scenario, and
updated the existing performance test to describe and exercise the new
semantics.
RimEclipseStatisticsCaseEvaluator::addNamedResult() sized the destination
statistics containers (MEAN/P10/P50/P90/etc.) using the time step count of
an arbitrary first result found in the source case (existingResults()[0]).
Results computed by ResInsight, such as riPORV*SOIL and riPORV*SGAS, do not
have their own time step info populated until they are actually computed,
so existingResults()[0] can end up being an unrelated result, including a
static one (e.g. PORV) with only a single time step. Using its size to
size the destination containers collapsed the statistics for all dynamic
results to a single time step, silently dropping the results for every
other time step, i.e. no results were shown for riPORV*SOIL / riPORV*SGAS.
Fix by using RigCaseCellResultsData::maxTimeStepCount() to find the source
result with the most time steps, and use its time step info both for the
destination time step metadata and for sizing the destination containers,
instead of depending on an arbitrary source result.
Added a unit test reproducing the ensemble statistics computation for
riPORV*SOIL across two source cases.
Add caf::PdmFieldReorderCapability to the child array fields holding
RimCase objects:
- RimEclipseCaseCollection::cases (grid models in Project Tree)
- RimCaseCollection::reservoirs (statistics case groups)
- RimGeoMechModels::m_cases (geomechanical models)
This enables the built-in move up/down tree item controls for
reordering cases, matching the pattern already used for e.g.
RimSurfaceCollection and RimMultiPlot.
RicCloseCaseFeature::isCommandEnabled() was updated to require
RimCase::isDeletable() to be true, but no case type ever set this
flag to true by default. As a result, the 'Close' context menu item
disappeared for all grid models.
Set setDeletable( true ) as the default in the RimCase constructor.
Special cases that should not be closed directly by the user (e.g.
the generated ensemble grid case in RimWellTargetMapping) already
opt out by calling setDeletable( false ) explicitly.
Add grid data from Sumo using ri-cloud-api
Load Eclipse grid data (roff) from Sumo as RimReservoirGridEnsemble, one case per realization, with async fetch of grid properties per timestep.
- New: RiaSumoGrid (transport), RifReaderSumoGridProperty (lazy property reader), RimReservoirGridEnsembleSumo, RimRoffCaseSumo, and a "Create Reservoir Grid Ensemble" command.
- Shared grid: IJK dimensions compared before download; one RigMainGrid is reused across the ensemble when they match, each realization still parses its own ACTNUM before releasing geometry.
- Rename folders: Summary/Sumo -> Summary/Cloud, RimSumoDataSource -> Cloud/ (PDM keywords unchanged, old project files still load).
- UI: cloud data sources also shown in the 3D window's Data Sources dock.
- Fix: realizations no longer lost on project reload; realizations missing data are now reported instead of silently skipped.
Axis range and auto-zoom setters now schedule the editor update instead of
performing it immediately. Every immediate update rebuilds the tags of the whole
project tree, and zoom-all releases the range and re-enables auto zoom for every
axis of every subplot. Scheduling also avoids deleting the tag whose clicked
signal is still being emitted from onPinnedTagClicked().
Remove the m_isAutoZoom branches in fieldChangedByUi(). The field is not part of
any UI ordering, and both orderings end with skipRemainingFields(), so the
branches were unreachable. Comments referring to a "Set Range Automatically"
checkbox are updated accordingly.
The pinned tag no longer reuses pinned.svg, which is also the Quick Access dock
icon and must keep following the widget palette. The gray tag variant lives in
PinnedAxisRange.svg.
Zoom All is an explicit request for automatic ranges, but only summary plots
released the user defined range. Histogram, grid cross, VFP and analysis plots
share RimPlotAxisProperties and set the range as user defined when a min or max
is typed, so Zoom All left them with a stale pinned tag and permanently blocked
enableAutoValueMinMax().
Release the range for every axis owned by the window in RimViewWindow instead,
which makes the summary specific overrides redundant.
Replace the chain/padlock icon with a simple two-headed-arrow icon
(SharedAxis.svg) for linked time/property axes.
Add a pinned-range project-tree tag shown when an axis range is
user-defined, using the standard gray pinned.svg icon. The tag
refreshes immediately regardless of the code path that changes the
range/auto-zoom state (Zoom All, data source changes, axis linking,
UI edits), and clicking the tag releases the user-defined range so
the axis auto-zooms again.
Add RicToggleAxisAutoZoomFeature, a checkable context-menu command
on Y-axis and time-axis objects that toggles auto-zoom and keeps the
pinned/user-defined-range tag in sync in both directions.
Move axis-specific context-menu entries out of RimContextCommandBuilder
into appendMenuItems() overrides on RimPlotAxisProperties and
RimSummaryTimeAxisProperties, per the project convention of keeping
object-specific menu logic in the object.
Fix RimSummaryPlot::timeAxisSettingsChanged() to actually re-apply
auto-scale to the plot widget when auto-zoom is re-enabled on the
time axis; previously only the property was updated, and no
recompute of the plotted range was triggered.
An axis range defined by the user is now kept when a summary case or ensemble is added, reloaded or replaced. The range is marked as user defined when the user edits the min/max values, unchecks "Set Range Automatically" or zooms interactively in the plot.
The user defined range is released by "Zoom All", by double click in a plot, by checking "Set Range Automatically" or by changing the Y axis range aggregation of a summary multi plot.
Realizations whose calculated summary vector value is NaN or Inf at
the selected timestep are now excluded from the Pearson correlation
computation instead of propagating NaN through the whole result,
which corrupted the correlation tornado plot. Also guard the final
correlation value against NaN, since the previous check only compared
against +Infinity.
RigEnsembleParameter::stdDeviation() sums squared parameter values,
which can overflow to +Inf for extreme (but finite) magnitudes,
turning normalizedStdDeviation() into NaN. Since NaN compares false
against the variation epsilon, such parameters were silently treated
as having no variation and dropped from parameterCorrelations(),
producing an empty tornado plot for that parameter and calculated
vector combination. Fall back to a range-based variation estimate
when the standard deviation calculation overflows, so the parameter
is retained and the Pearson correlation guard above can take over
instead.
Use the ensemble data filter collection as the source for views owned by a reservoir grid ensemble. Apply the source for newly inserted views, case changes, and project-load initialization while preserving case-level sources for ordinary case views.
Add a regression test verifying that an ensemble view switches from its case collection to the ensemble collection.
Rebind the ensemble data filter collection after file-set cases are recreated. This ensures the collection uses the new main case and clears its source when no replacement cases are found.
The data filter collection node is hidden in the project tree while the
collection is empty, and the owner must be refreshed to re-run its tree
ordering when the first filter appears. The refresh targeted the source
case, which for a grid ensemble collection is the main case of the
ensemble, not the ensemble owning the tree node. Newly created ensemble
data filters were therefore not visible in the tree.
Refresh the PDM parent object instead, which is the case or the grid
ensemble. Also refresh from the programmatic removeFilter path.
Add a data filter collection to RimReservoirGridEnsemble, so filters can be defined once for the ensemble. The filters are bound to the main case of the ensemble for configuration.
Expose filter creation commands from a Data Filters submenu on the ensemble node, and hide the collection node while empty, matching the case-level behavior. Grid calculation integration is intentionally left for the dependent calculation-filtering change.
RicNewPolygonFilterFeature already supports adding polygon filters to
the case-level data filter collection, but the collection menu did not
offer it. Add the polygon filter entries to the menu, matching the cell
filter collection menu in a view. The polygon filter computes its cells
lazily from the collection's source case, so no view is required.
Guard against calling points.back() on an empty vector when all well
path points are filtered out (e.g. due to the zmin check), which
previously caused undefined behavior/crash.
Constructing an Opm::Parser populates the complete keyword database (2774
keywords, measured at 2.4 ms per construction) and this was done once per
keyword event in RifEventKeywordFormatter::buildKeyword, and twice more per
keyword in the column-aligned serialisation in RimKeywordFactory. Schedule
generation was therefore completely dominated by parser construction and grew
directly with the number of events on the timeline. Use a single shared parser
instance instead, since the parser is stateless once constructed.
Also compute the events for a date once in generateDateSection and pass them to
the per-well helpers, instead of calling RimWellEventTimeline::getEventsAtDate
four times per well and date, which scaled as dates x wells x events.
Generating a schedule for a production data set with 1737 events over 490 dates
goes from 4.2 s to 0.1 s, and 21000 events over 14 wells now complete in 0.5 s.
RimMultiPlot::recreatePlotWidgets() dereferenced the viewer unconditionally. The viewer does not exist in console mode, so deleting a summary plot from Python through the generic PdmObject delete() crashed ResInsight in onChildDeleted(). Return early when there is no viewer, matching the other viewer accesses in RimMultiPlot.
Add Python regression tests for deleting a single summary plot inside a multi plot and for deleting a whole multi plot, and an example showing how project tree objects are removed with delete().
Three related fixes found while reviewing the feature:
- Clicking the contour map surface inside a regular 3d view selected the internal RimContourMapProjection in the project tree instead of the RimContourMapInView object. Resolve the actual view from the pick event rather than climbing the projection's own ancestor chain, which pointed to a hidden internal contour map view, and select the owning RimContourMapInView when found.
- RimContourMapInView::name() returned only the raw custom name of the underlying contour map view. RimStatisticsContourMapView leaves its custom name empty and builds the displayed name entirely from generated tags, so the name shown for a statistics contour map in a 3d view was empty. Use the view's auto-generated name instead.
- RimContourMapInView is a presentation wrapper around a hidden RimEclipseContourMapView (or RimStatisticsContourMapView) that owns the actual contour map data. Add a Go to Contour Map button, following the existing pattern used by RimPolygonInView and RimDataFilterInView, so the underlying source object can be quickly selected in the project tree.
Give RimGridView a RimContourMapInViewCollection and let RimEclipseView and RimGeoMechView build their 3d model from it. A dedicated contour map view (RimEclipseContourMapView/RimStatisticsContourMapView) shows its own contour map and returns no collection of its own, so it is unaffected. Adjust RimSurfaceInViewCollection so surfaces and contour maps coexist in the same 3d view without z-fighting.
Add the data model for showing contour maps inside a regular 3d view. RimContourMapInView is a presentation wrapper that decides where a contour map is placed vertically in the 3d scene and what parts of it are drawn; the contour map itself, including the result, the aggregation and the computed geometry, remains owned by the contour map view it points at. RimContourMapInViewCollection owns the set of contour maps available in a 3d view. Add a CONTOUR_MAP entry to RiaDefines::ItemIn3dView so the project tree can be refreshed for just the contour map items.
* Add performance unit test for RimEnsembleCurveFilter::applyFilter
* Fix O(N^2) scaling in RimEnsembleCurveFilter::applyFilter
RimEnsembleCurveSet::objectiveFunctionTimeConfig() was called once per
summary case inside the applyFilter() loop, for both the OBJECTIVE_FUNCTION
and SUMMARY_VALUE filter modes. Internally it calls allAvailableTimeSteps(),
which scans the full time series of every case in the ensemble, so
recomputing it per case turned applyFilter() into an O(caseCount^2) operation.
* Avoid redundant allAvailableTimeSteps() calls in fullTimeStepRange()
fullTimeStepRange() called allAvailableTimeSteps() three times (once for the
empty check, once for min, once for max), each re-scanning the full time
series of every case in the ensemble. Compute it once and reuse the result.
Also simplify allAvailableTimeSteps() to insert time steps directly into the
result set instead of collecting them into an intermediate vector first.
std::clamp() invokes undefined behavior when the low bound is greater
than the high bound. In resetMinimumCellValuesToDefault(), the
minimum/maximum bounds for saturation, pressure, permeability and
transmissibility are derived from case data and can end up inverted
when no data is available, which crashed or produced garbage values.
Reverts the unconditional change from commit 74ffc822f2 and replaces it with
a safer fix: only skip re-initializing the well path name from the source
file on reload when a name has already been set. Older projects that never
serialized the well path Name field (e.g. TestCase_RFT_PLT/RegressionTest.rsp)
still get their name populated from the file on load, while renamed well
paths in newer projects keep their custom name across reloads.
Restores the RMS well round-trip test that verifies a renamed well path
name persists after save/reopen.
Add RicImportOrionEventsFeature to File -> Import -> Well Data and the well
path collection context menu. The feature opens a file dialog and launches
the Python interpreter asynchronously with the new --apply mode of the
rips.orion_events CLI, which connects back to the running instance through
RESINSIGHT_GRPC_PORT and applies the events to the well event timeline.
Output is shown in the Process Monitor panel.
Summary cases are created and given a case id before they are added to the
project. The id search only sees cases already in the project, so every case
in a batch import was given the same id.
The Data Sources tree stores the owning case id in each summary address, and
all lookups resolve to the first case with that id. Selecting the same vector
from several cases therefore produced a single curve, and dropping a vector
from one of the other cases hit the duplicate check and did nothing.
Reading a caf::PdmPtrField sets the pointer to null before the reference string is resolved, and clearing a ptr field disconnects all signals between the referenced object and the field owner. The signal connections established by RimFilterInViewCollection::setSourceCollections() in the view constructor were therefore lost when a project was loaded, and adding a cell filter, property filter or data filter no longer refreshed the Filters node in the project tree. Reconnect in initAfterRead, which runs after the references have been resolved.
Viewer::setView() moves the camera to look straight at the point of interest. Panning does not move the point of interest, so this discarded the camera position restored from the project file.
Only reset the view direction if the camera is not already oriented top-down, which is the case when a contour map has been rotated by a linked 3D view.