- Move depthTypeAbbreviation(), computePressureSamplesPerCase() and
computeMeanPressurePerCase() out of RimParameterRftCrossPlot into a new
standalone RimRftCrossPlotTools namespace, shared by RimParameterRftCrossPlot
and RimRftTornadoPlot (removing duplicated code in the latter).
- Extract pure, reader-independent helpers filterPressuresByDepthRange() and
computeMean() so the depth filtering and averaging logic can be unit tested
without RFT reader/ensemble mocks.
- Add RimRftCrossPlotTools-Test.cpp with gtest cases covering depth range
filtering (disabled, inclusive bounds, out-of-range, mismatched sizes) and
mean computation (empty/normal), plus depth type abbreviation.
Move RimWellLogRftCurve::rftCurveDepthValues into RimRftCrossPlotTools
This static helper's only caller was already RimRftCrossPlotTools::computePressureSamplesPerCase, so move it alongside the other RFT cross plot/tornado plot helpers for consistency.
Recognizes Vec3-shaped object schemas ({x, y, z} number properties)
structurally in RimWorkflowDescribeTools and dispatches them to a new
RimWorkflowVec3Binding in RimWorkflowTaskInput::createBinding(). Stores
the value as a cvf::Vec3d field, parses JSON object defaults, and emits
a YAML flow-mapping ({x: .., y: .., z: ..}) matching the taskmaestro
Vec3 pydantic model shape.
Replace the rips.taskmaestro_helper introspect subcommand with
`python -m taskmaestro workflow describe --json`. RimWorkflowDescribeTools
converts the describe output (JSON schemas, depends_on, config_values)
into the workflow graph used by the Workflow UI. This also adds support
for collect dependencies and maps rips.Reservoir to EclipseCase pickers.
- Camera::fitView/computeFitViewEyePosition: derive one corrected, non-parallel up vector and use it consistently for right, upNorm, planeTop and the final setFromLookAt(), instead of only repairing the right vector. Previously the degenerate (parallel dir/up) case still produced a singular view matrix because up itself was never corrected. Added a regression test exercising fitView() end-to-end (not just computeFitViewEyePosition()) that checks the resulting direction/up/right form a valid orthonormal basis.
- RimViewController::updateCameraLink/updateTimeStepLink: guard against ownerViewLinker()->masterView() being null (RimViewLinker::setMasterView(nullptr) is a supported state), not only against a null view linker.
- RicHistogramPlotTools: use RiaGuiApplication::isRunning() before calling RiaGuiApplication::instance(), since instance() itself asserts when there is no running GUI application.
- RimDockWindowController::updateViewerWidget: create the view widget before the dock widget, so a failed view-widget creation does not leave a dock widget allocated without a dock manager (which deleteDockWidget() cannot clean up).
- RimStimPlanModelCalculator::calculateFormation: avoid continue via a small lambda, and reject double values outside the range of int before the static_cast to avoid undefined behavior.
dir^up becomes a zero vector when the view direction and up vector are parallel, producing an invalid zero-normal plane. Plane::distanceToOrigin() then asserts/aborts (CVF_ASSERT, always active). Fall back to an arbitrary axis to compute a valid right vector in this degenerate case.
RimViewController::masterView() dereferenced ownerViewLinker() without a
null check, causing a crash when the controller has been detached from
its RimViewLinker parent (e.g. mid-destruction, after the owning
child-array field has already erased it but before delete runs).
The same unguarded ownerViewLinker() dereference existed in several other
methods: isActive(), isCameraLinked(), isTimeStepLinked(),
isResultColorControlled(), isLegendDefinitionsControlled(),
isPropertyFilterOveridden(), updateOverrides(),
updateDuplicatedPropertyFilters(), updateCameraLink(),
updateTimeStepLink(), updateResultColorsControl(),
updateLegendDefinitions() and applyCellFilterCollectionByUserChoice().
All now store ownerViewLinker() in a local and null-check it before use,
matching the pattern already used in isCellFiltersControlled().
Guarded two unconditional null derefs: dockWidget->setWidget(viewWidget) when viewWidget creation fails, and viewPdmObject() in handleViewerDeletion() when no view has ever been assigned to control. Added regression test for the handleViewerDeletion() case; the setWidget() path requires a running GUI/main window and isn't reachable from the headless unit test harness.
synchronize2dIntersectionViews() could crash in two ways: when the view's ownerCase() is null, and when the collection has no Rim3dView ancestor at all (e.g. mid-destruction after detaching from the PDM tree, where firstAncestorOrThisOfTypeAsserted<Rim3dView>() returns null since CAF_ASSERT is compiled out in release). Both cases are now guarded.
A system HDF5 reports /usr/include. CMake filters implicit dirs from
target include directories but not from the source-file
INCLUDE_DIRECTORIES property, so -I/usr/include reached the command line
and broke #include_next <math.h> in <cmath> on gcc-toolset.
* #14806 Faults: Compute unnamed fault faces in parallel and only from positive faces
- A negative I or J face is the positive face of the neighbor cell with a lower
index, and is already handled when that neighbor is processed. Only visit the
positive faces.
- Each cell only writes the fault index of its own positive faces and the
opposite faces of its neighbors, so process the cells in parallel. The
thread results are appended in thread order, so the fault faces keep the
same order as before.
* #14806 Faults: Add unit tests for parallel unnamed fault face computation
Build a small in-memory grid (no file, no view) with geometric faults at
every I interface, and verify that calculateFaults() finds all unnamed
fault faces, keeps them ordered by native cell index (the per-thread
results are appended in thread order), and separates faces touching an
inactive cell into the dedicated inactive fault face list.
Building the cell search tree was dominated by one small std::vector<int>
allocation per leaf, and a tree with one cell per leaf becomes very deep for
large grids.
- BoundingBoxTree: Store leaf ids in flat arrays, and move the input data
instead of copying it. The tree shape is unchanged. Queries no longer copy
the ids of each leaf.
- RigMainGrid: Always group at least 8 cells along K in each leaf.
findIntersectingCells() removes cells not intersecting the query bounding
box, so the result is the same set of cells as for a tree with one cell per
leaf. This also applies to very large grids, which previously got the
cells of the whole leaf.
- Include cells of nested LGRs and LGR cells in invalid main grid cells in
the grouped tree, as the previous tree with one cell per leaf did.
Fixes a set of related bugs affecting ensemble contour maps that use a GENERATED (grid calculation) result, plus a related bug where switching a view's case lost generated results.
- Remove unused SOURCE_GROUP_HEADER_FILES from GrpcInterface CMakeLists
- Scope the following defines to the files that need it ENABLE_GRPC, USE_ODB_API , USE_OPENVDS, USE_HDF5
- RifOdbReader.cpp copied an odb_InstanceRepository by value, which
fails to compile under MSVC's strict conformance mode due to an
ambiguous implicit copy constructor in the vendor headers (diamond
inheritance). Use a reference instead, since it was only ever read
from.
Union-find sweep from highest to lowest value computes the prominence of each peak. Peaks are filtered on prominence and edge contact, ranked by prominence or value, and thinned by minimum distance. Flat shoulders and plateaus are rejected, and prominence is measured against the minimum of each connected data region.
A static property with an active dynamic filter now produces a contour map per time step. Adds a synthetic ensemble fixture with unified restart files, as the OPM reader used for statistics does not support per-step restart files.
Replace the built-in formation filter with ensemble data filters. Load ensemble data filters after project read, and parent new views before assigning the case so the ensemble filter collection is resolved correctly.
The opm-common reader only reads unified restart files (UNRST). When the
opm-common reader was selected in preferences, cases with one restart file per
time step (X0000, X0001, ...) were opened without dynamic results.
- Fall back to the resdata reader when there is no unified restart file but
non-unified restart files are present.
- Unit tests using dynamic results check that the result and time step exist
before accessing them, so a missing result fails the test instead of
crashing the test executable.
Add a test covering a flat 'realization-N/CASE.EXT' folder structure
(no iter-N level) grouped with EnsembleGroupingMode::NONE, verifying
all files end up in a single best-effort ensemble. Also add a test
confirming empty input produces no bogus ensemble entry.
* Skip deleted plot widgets in RiuMultiPlotBook::visiblePlotWidgets
* Guard missing calculation in detectCyclicCalculation
* Guard invalid node index in RigFemPart node lookups
* Guard null well path in RimWellLogExtractionCurve::extractGeomData
* Guard unopened case in RimGridCalculation::removeDependentObjects
* Guard empty subset in createLateralAtMeasuredDepth
RigStatisticsCalculator::mobileVolumeWeightedMean() accumulated into a sum
from an out-parameter that callees were not required to write. Every
implementation of the per time step overload has early-return paths that
leave the value untouched, as does RigWeightedMeanCalc::weightedMeanOverCells,
so indeterminate stack memory could be summed and presented in the UI.
Return std::optional<double> from the computation layer instead of using an
out-parameter, making "value not available" explicit and impossible to ignore:
- RigWeightedMeanCalc::weightedMeanOverCells
- RigStatisticsCalculator::mobileVolumeWeightedMean and all overrides
Time steps without a computable mean no longer contribute to the accumulated
sum, and are excluded from the divisor. Previously they would either add
garbage or, with a naive zero initialisation, bias the average towards zero.
RigStatisticsDataCache keeps the existing HUGE_VAL marker on its public API
via value_or(), so behaviour seen by RigHistogramData and the overlay info
is unchanged apart from the fix.
Also:
- Set m_isVolumeWeightedMeanCalculated in the per time step cache, which was
never assigned, so the cached value was recomputed on every call.
- Remove a duplicated ensureKnownResultLoaded() call in
RigEclipseNativeVisibleCellsStatCalc.
Fixes#14766
Extends the existing single-curve auto axis assignment (unit and summary vector matching, then alternating left/right) to also cover RimEnsembleCurveSet. Previously ensemble curve sets were always placed on the default left axis, so e.g. FOPT and FOPR ensembles ended up on the same axis despite differing units.
- Add RimEnsembleCurveSet::unitNameY(), looked up from the first available summary case reader in the ensemble.
- Move X and Y axis assignment from RimSummaryPlot to new RimSummaryPlotTools. Y-axis assignment takes a RiaSummaryAddressModifier::CurveAddressProvider, and compares against both curves and curve sets in the plot.
- RimEnsembleCurveSetCollection::addCurveSet gains an autoAssignPlotAxis flag (default true, matching addCurveAndUpdate for single curves).
Also fixes a bug in the summary plot editor where ensemble curve sets manually assigned to different Y-axes collapsed onto the same axis when clicking OK. The PdmPtrField axis reference does not resolve across the cloned preview and target plots. Add RimEnsembleCurveSet::findOrAssignLeftOrRightAxisY() to capture and re-apply the axis explicitly.
Add RimSummaryPlot-Test.cpp with unit tests for axis auto-assignment of curves and ensemble curve sets.
Fixes#14728
QFileInfo::baseName() truncates at the first dot, so the ESMRY and h5 files were not found for realizations like CASE.V2-0.SMSPEC. Without an UNSMRY file the realization was opened as an empty summary case. Use completeBaseName() to strip only the extension.
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 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.
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
Add the ResInsight-featuretests executable: a headless GUI test harness (RiaFeatureTestModelBuilder, RiaFeatureTestTreeView), a runner that triggers Ric*Feature commands through their QAction (RicFeatureExecutionRunner), a generic execution sweep over every registered feature (RicFeatureSweep-Test, RicFeatureExecutionSweep-Test, RicFeatureSubprocessSweep-Test, RicFeatureSweepDenylist), and supporting CMake wiring. Adds RiaRegressionTestRunner::setRunningRegressionTests() so tests can suppress modal dialogs, and shares RimMockSummaryCase as a common test model under TestModels.
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 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.