Commit Graph
100 Commits
Author SHA1 Message Date
Magne Sjaastad d575ae504b #14669 Fix O(N^2) scaling in RimEnsembleCurveFilter::applyFilter
* 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.
2026-09-03 15:12:34 +02:00
Magne Sjaastad e8f02fd7b0 Increase Windows buildcache size limit to 1.5 GiB
A full Windows build writes ~550 MB of buildcache entries, so the shared 500 MB limit was exceeded before the build finished. buildcache then evicted entries the same build still needed, and the cache never converged: rebuilding an unchanged tree against a cache written by that very tree hit only 44%, leaving the Windows leg at ~70 minutes while Linux, whose objects are half the size, finished in 83 seconds.

Move the limit into the matrix so Windows gets 1.5 GiB and Linux keeps 500 MB.
2026-09-03 14:32:50 +02:00
Magne Sjaastad 6a7301e004 Only add PendingRelease label when issue is closed as completed 2026-09-03 11:51:16 +02:00
Magne Sjaastad 052c1eb550 Fix crash in RimWellIADataAccess::interpolatedResultValue for out-of-grid positions 2026-09-03 11:36:57 +02:00
Magne Sjaastad 88119f9c37 RHEL8: Name CPack package _el8 when building in a container
CMAKE_SYSTEM holds the host kernel release, which on the ubuntu-latest runner says nothing about the RHEL8 userland inside the container, so the el8 regex never matched. CPACK_SYSTEM_NAME is only set for Windows, so the fallback appended a bare underscore and the release asset ended up as ResInsight-2026.09.0_.tar.gz.

Let the caller state the distribution explicitly and skip the suffix entirely when neither name is known.
2026-08-31 22:22:48 +02:00
Magne Sjaastad b16c29b2e8 CI: Build vcpkg ports release-only to cut cold-cache time
vcpkg only reads VCPKG_BUILD_TYPE from the triplet file, so the
-DVCPKG_BUILD_TYPE=release passed to the top-level CMake call never had any
effect and every port was built in both debug and release. On a cold cache
that is roughly half of an 85 minute vcpkg step.

Add release-only overlay triplets for x64-windows and x64-linux and select
them from the workflow matrix. They are separate triplets rather than an
override of the stock ones so local developer builds keep their debug
dependencies. VCPKG_HOST_TRIPLET is set alongside the target triplet,
otherwise the 12 host-tool ports (openssl and protobuf among them) would be
built a second time under the stock triplet.

The triplet is part of the vcpkg cache key since it changes every binary's
ABI hash.
2026-08-31 22:21:52 +02:00
Magne Sjaastad c3c427a01f Bump version to 2026.09.1-dev.01 after release 2026-08-31 20:06:50 +02:00
Magne Sjaastad 197d58a750 Bump to 2026.09.0 2026-08-31 12:45:32 +02:00
Magne Sjaastad 91af3667c8 Add field and object keywords 2026-08-31 12:22:51 +02:00
Magne Sjaastad fd219da50f #14619 ReservoirDataModel: Guard well target mapping against missing or incomplete result data
RigWellTargetMappingTools accessed cellScalarResults() and result vectors without verifying the requested time step or index was within bounds, and without checking for null case data pointers.  Add the missing guards.
2026-08-31 12:20:48 +02:00
Magne Sjaastad cda9551dbb #14222 Well target mapping: Avoid clamp crash with invalid min/max bounds
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.
2026-08-31 12:20:48 +02:00
Magne Sjaastad f8a55787fd #14646 MSW: Number fracture branches by measured depth
The branch number of a fracture followed the order the fractures were created, so appending a second set of fractures to a well put them after the existing ones regardless of where along the well they sit. Sort by the fracture start MD instead. The order also decides which fracture connects a grid cell shared by two of them.
2026-08-31 10:28:41 +02:00
Magne Sjaastad d085f6ce74 #14646 MSW: Report a grid cell only once across COMPSEGS branches
A grid cell has a single COMPDAT connection and must be connected to exactly one segment, but the geometry-based export emitted one COMPSEGS row per branch intersecting the cell. Restore the well-global bookkeeping the removed tree path had: tag each branch with its origin and let the branch carrying most flow claim a shared cell, perforations before fishbones before fractures.
2026-08-31 10:28:41 +02:00
Magne Sjaastad 377751eb68 Histogram: Make selected sub-plot visible in histogram multi plot
Selecting a curve or a plot inside a RimHistogramMultiPlot in the project
tree now scrolls the multi plot view to make the containing histogram plot
visible, mirroring the existing behavior for RimSummaryMultiPlot.
2026-08-31 10:28:10 +02:00
Magne Sjaastad a4e031b734 Grid statistics: Zoom all on histogram plot after creation
When creating a histogram from grid cell results (e.g. PERMX) via
"Create Grid Statistics Plot", zoom the histogram plot to fit the data
right after the curve is populated, instead of leaving it at default axis
ranges.
2026-08-31 10:28:10 +02:00
Magne Sjaastad e5c3e54227 #14538 Well path: Preserve renamed file well name without breaking legacy projects
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.
2026-08-31 10:28:10 +02:00
Magne Sjaastad 5f0c3f8a54 #14632 Guard against out-of-range time step index when loading results
A case in an ensemble can have fewer time steps than the case defining the time step axis of a statistics case. The time step index was used to index m_cellScalarResults directly, reading past the end of the vector in Release where CAF_ASSERT is a no-op.
2026-08-28 07:15:47 +02:00
Magne Sjaastad 6d7959d150 #14542 Summary: Assign unique case ids when importing multiple cases
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.
2026-08-28 07:02:50 +02:00
Magne Sjaastad 5e17f9986d #14627 Grid statistics: Fix NaN deviation for near-constant results
The single-pass sum-of-squares formula subtracts two large and nearly equal numbers. Where a result such as PORO is identical or near-identical across the realizations, the true variance is below the rounding noise floor, the radicand turns negative and sqrt() returns NaN.

Compute the deviation in two passes instead, centering on the exact mean. This also removes the loss of significance the old formula had for values with a large common offset.

Min, max, range, mean and deviation all report HUGE_VAL when the input holds no valid values, but the sum reported the zero it was accumulated from. A caller testing the result with isValidNumber would accept that zero as real data.
2026-08-28 07:01:57 +02:00
Magne Sjaastad ca8c1b8d04 #14626 Add Import Summary Case to the File menu in the main window 2026-08-27 15:02:49 +02:00
Magne Sjaastad 9a5af909f2 #14511 Activate 3D view after importing grid model from summary case 2026-08-27 12:45:28 +02:00
Magne Sjaastad 3508b0a72e #14558 3D filters: Reconnect filter signals after project load
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.
2026-08-27 11:42:50 +02:00
Magne Sjaastad 5edefe4ad7 #14602 Summary: Keep the summary toolbar active for child objects of a multi plot
The toolbar is only shown when the active plot view window is a multi plot. When a child object is selected, the first ancestor view window is the sub plot, so the toolbar was cleared and hidden.

Use the summary multi plot as active plot view window when any object inside the multi plot is selected.
2026-08-27 10:16:06 +02:00
Magne Sjaastad 61117162da #14602 Contour map: Keep interactive panning when reloading a project
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.
2026-08-27 10:16:06 +02:00
Magne Sjaastad cf3dadacf7 #14602 UI adjustments for summary import and curve calculator
Use a file dialog caption matching the imported file type instead of always showing "Import Grid Model".

Parent the summary vector selection dialog to the active window, so closing it does not raise the 3D main window in front of the plot window when opened from the Summary Curve Calculator.
2026-08-27 10:16:06 +02:00
Magne Sjaastad 6c63c755e7 #14606 Contour Map: Use the cached ensemble statistics when the project is reloaded
The map size of the sample grid was recomputed from the expanded bounding box and the sample spacing stored in the project file. The expanded bounding box is an exact multiple of the sample spacing, but after the limited precision of the project file the division can land just above a whole number of cells, and the map size gets one extra cell. The cache files were then rejected, and the ensemble statistics recomputed.

Store the map size in the project file, and use it directly when the sample grid is restored from the cache.

Identify the input grids of the cache validity key by file path, size and modification time, so that the cache is also invalidated when a grid file is modified in place. Label and count the primary case and the ensemble cases, so that a primary case can not be mistaken for the first ensemble case.
2026-08-27 09:37:31 +02:00
Magne Sjaastad d7ea4351e3 #14600 Keep MdiWindowController as project file keyword
Older versions of ResInsight depend on this keyword to create the window controller for plot windows. 3D views work with both keywords, because Rim3dView creates the controller in its constructor, while plots only get one from the project file. DockWindowController is kept as a read alias so project files written by recent dev builds still load.
2026-08-26 14:42:42 +02:00
Magne Sjaastad 7b11292839 #14596 Skip computing corner coordinates for cells excluded by the K filter
Move the call to cellCornerVertices below the K filter early out, so the corner coordinates are only computed for the cells that are actually tested against the polygon.
2026-08-25 08:22:29 +02:00
Magne Sjaastad 4a6ebedade #14596 Name the remaining unnamed OpenMP critical sections
All unnamed critical regions map to the same implicit name, so a single process wide lock was shared by summary import, geometry generation, grid bounding box computation and NNC merging. Unrelated parallel loops therefore serialized against each other.

Give each region a critical_section_ name describing what it protects. The two regions guarding RifOpmCommonEclipseSummary::sm_createdEsmryFileCount deliberately share one name, as they protect the same counter.
2026-08-25 08:22:29 +02:00
Magne Sjaastad 63f73a2154 #14596 Document OpenMP conventions for agents
Describe the OpenMP 2.0 constraints that follow from building with MSVC /openmp, and why an orphaned work sharing construct can deadlock the application when the master thread is also the Qt GUI thread.

Also document the pattern of collecting results in per thread buffers and merging after the parallel region, the convention of naming critical sections, and the rule that exceptions must not escape a structured block.
2026-08-25 08:22:29 +02:00
Magne Sjaastad eec9a88bd3 #14596 Collect cells per thread in polygon filter instead of using critical sections
All four cell filter loops guarded a push_back into a shared container with an unnamed critical section, taking a process wide lock for every cell matching the polygon. On large grids with a permissive polygon this can be slower than running single threaded, and it made the resulting cell order vary between runs.

Collect the cells in per thread buffers and append them in thread order after the parallel region, following the pattern used elsewhere in the code base. This removes eight critical sections.
2026-08-25 08:22:29 +02:00
Magne Sjaastad 5c325dcc0b #14596 Remove unnecessary critical section in RiaCurveMerger
Each iteration writes to its own element of accumulatedValidValues, which is sized before the loop, so no synchronization is required. The write to curveValues a few lines below uses the same pattern without a critical section.

All unnamed critical regions share a single process wide lock, so this also removes contention against unrelated parallel loops.
2026-08-25 08:22:29 +02:00
Magne Sjaastad ad0f5f1bce #14596 Remove dead ordered clause and per-triangle lock in NNC geometry generation
The loop was declared with an ordered clause, but the body contains no ordered region, so the clause only enabled the ordered scheduling machinery without providing any ordering. The intended deterministic vertex order was not achieved either, because the actual synchronization was a critical section, which does not preserve iteration order.

Collect vertices in per thread buffers and merge them in thread order after the parallel region, following the pattern already used in RivFaultGeometryGenerator. This removes a lock acquisition per triangle in the innermost polygon loop, and makes the generated geometry identical from run to run.
2026-08-25 08:22:29 +02:00
Magne Sjaastad fa85100da7 #14596 Remove dead reserve and redundant barrier in NNC computation
totalNumberOfConnections was declared, listed in the reduction clause and used to size otherConnections, but it was never incremented. The counting was lost when the loop body was moved into extractConnectionsForFace, so the call has always been reserve( size() + 0 ), which RigConnectionContainer::reserve turns into a no-op.

The reserve was also called by every thread on the shared container without synchronization. Removing it eliminates that data race. The explicit barrier is redundant as well, since the omp for construct has no nowait clause and therefore already synchronizes before the merge.
2026-08-25 08:22:29 +02:00
Magne Sjaastad a7684a14d6 #14596 Use #pragma omp parallel for when building corner point grids
The loops were annotated with #pragma omp for, which is a work-sharing construct that does nothing outside an enclosing parallel region. Replace it with #pragma omp parallel for so the cell loops actually run in parallel.
2026-08-25 08:22:29 +02:00
Magne Sjaastad d2862941f6 #14596 Remove stray #pragma omp for in cvfqt::Utils::toTextureImageRegion
The pragma had no enclosing parallel region, so it did not parallelize anything and only added confusion.

This code caused a deadlock on Windows for the following workflow:
1. import a grid model
2. Import a large summary ensemble with no ESMRY that triggers display of a progress dialog. Make sure the progress dialog is displayed on top of the 3D models -> deadlock
2026-08-25 08:22:29 +02:00
Magne Sjaastad c9750f4698 Avoid crash when grid data is missing in shared grid ensemble 2026-08-24 09:10:22 +02:00
Magne Sjaastad a6b903acf3 Avoid terminate on parse error in element property file 2026-08-24 09:10:22 +02:00
Magne Sjaastad d53e5b48b3 Avoid GUI cleanup on a main window being destroyed 2026-08-24 09:10:22 +02:00
Magne Sjaastad 7032832774 Avoid crash when displacements are missing for GeoMech intersections 2026-08-24 09:10:22 +02:00
Magne Sjaastad abefc17f3f CI: Reduce Actions cache pressure in ResInsightWithCache
Only save the buildcache from the default branch. GitHub scopes cache writes to the current ref, so a cache saved from a feature branch or PR is invisible to every other branch while still counting against the 10 GB per-repository quota and evicting the shared caches on the default branch.

Disable unity build for all configurations. Unity blobs invalidate on any change to the sources they bundle, which defeats buildcache reuse and fills the cache with objects that are unlikely to be hit again.
2026-08-22 16:42:24 +02:00
Magne Sjaastad c6c5ff5808 #14584 Move ResInsight-tests out of the shared build root
ResInsight-tests linked into the build root and copied its full runtime DLL
set there as a POST_BUILD step, the same pattern that made
extract-projectfile-versions race against the generated_classes.py edge. The
set is a superset of the one that failed, covering Qt6Gui, Qt6Widgets and the
rest, and nothing orders it against the code generation edge either. It fires
rarely only because copy_if_different compares content, so windeployqt having
already placed identical DLLs makes it a no-op. On a cold build where the copy
wins that race it genuinely writes.

Give the target its own RUNTIME_OUTPUT_DIRECTORY, and point the companion
PreBuildFileCopyTest target at the same directory so the ODB, OpenVDS and HDF5
runtime files stay next to the executable. That target runs before the link
creates the directory, so create it first.

Update the three workflows that run the test executable by path.
2026-08-22 11:50:55 +02:00
Magne Sjaastad f68c0fe2cb #14584 Fix Windows build race copying Qt DLLs into the build root
extract-projectfile-versions linked into the shared build root and copied
Qt6Core.dll and Qt6Sql.dll next to its executable as a POST_BUILD step. The
generated_classes.py edge runs ResInsight.exe out of that same directory with
those DLLs loaded, and nothing orders the two edges relative to each other, so
Ninja was free to schedule them concurrently. Overwriting a mapped DLL is a
sharing violation on Windows, which failed the copy and took the build down.

Give the target its own RUNTIME_OUTPUT_DIRECTORY. It is a standalone developer
tool with no reason to share the application output directory, so moving it
removes the conflict instead of narrowing it. Also switch the copy to
copy_if_different, matching the equivalent step in the unit test target.
2026-08-22 11:50:55 +02:00
Magne Sjaastad 135ed69369 #14559 Summary Calculator: Update dependent data when the last calculation is deleted
deleteCalculation() removes the calculation before rebuildCaseMetaData() is called, and updateDataDependingOnCalculations() returned early for an empty collection. The addresses created by the last calculation were then left behind in the readers until the next refresh or a reload of the project.

Perform one more update after the last calculation is deleted, and keep the early return for the case where no calculations have been present.
2026-08-21 15:54:28 +02:00
Magne Sjaastad 1778a9637b #14559 Delta Ensemble: Refresh calculated addresses for all realizations
A delta ensemble creates summary addresses for all realizations of the source ensembles. When a calculation was created after this, only the realizations showing tree nodes had their addresses recreated. The other realizations had no calculated address, and RimDeltaSummaryCase::values() discarded the vector as being present in only one of the source ensembles. Only realization 0 was plotted until the project was reloaded.

Add RifSummaryReaderInterface::refreshCalculatedAddresses() to recreate the addresses of the calculated readers without touching the native readers, and call it for all cases in an ensemble when calculations are updated.
2026-08-21 15:54:28 +02:00
Magne Sjaastad 2a35edcca9 #14555 Make sure views in a grid ensemble are updated when a grid calculation is evaluated
A case in a grid ensemble is displayed by views in the view collection of the grid ensemble, but RimEclipseCase::reservoirViews() only inspected the view collection of the case itself and the global view collection. No views were found for such a case, so the display model was never rebuilt after a grid calculation was evaluated. When loading a project file, the view was left with the display model created before the calculation was evaluated, showing no legend and no cell colors.
2026-08-21 14:53:48 +02:00
Magne Sjaastad 6022980d54 #14555 Allow statistical grid cases to be used as source for grid calculator expressions
Statistics cases were filtered out of the case lists used by the grid calculator, both in the variable table and in the Select Result dialog. Statistics cases belonging to a grid ensemble were also missing from RimProject::allGridCases(), as only the source cases of the ensemble were collected. Statistics cases in a grid case group were already included.

A statistics case can be used as source only. A calculated result stored in a statistics case is interpreted as computed statistics, and would block recomputation of the statistics, so a statistics case is never assigned as destination case.

A statistics case with no computed statistics is not necessarily opened. Make sure source cases are opened before grid dimensions are validated and before result data is read.
2026-08-21 14:53:48 +02:00
Magne Sjaastad e2319d0bd3 #14566 OpenTelemetry: stop unbounded log flood on send failures
Send one batched request per event batch instead of one request per event.
Application Insights v2/track accepts an array of envelopes, so a batch of up
to maxBatchSize (512) events was previously issued as 512 concurrent POSTs.
QNetworkAccessManager only opens a handful of connections per host, so the
queued requests were aborted by their own transfer timeout before reaching the
wire, reported as "HTTP 0: Operation canceled".

Honour the circuit breaker in processEvents(). It was only checked when
enqueuing, so the up to 10000 already queued events kept being posted at full
rate after the breaker opened. The crash and shutdown flush bypasses the
breaker so a crash report is still attempted.

Keep at most one request in flight. Failures are only observed when the reply
finishes, which for an unreachable endpoint takes the full transfer timeout,
and the 100 ms timer kept launching requests during that window.

Log the circuit breaker transition once instead of on every subsequent failure.

Only read the reply body when the device is open. An aborted reply has a closed
device, and reading from it emitted a QIODevice warning per request.

Call attemptReconnection() from the process timer. It was dead code, so once
the breaker opened it could only close if a reply happened to succeed.
2026-08-21 14:37:58 +02:00
Magne Sjaastad f13fbbbd87 #14565 Grid model results: Make SGAS for Two-phase Gas/Water models when missing in output
Simulators for two-phase gas/water models may report only SWAT. In this case no SGAS result entry was created, so SGAS was missing from the result list and TERNARY fell back to computing SOIL = 1 - SWAT, presenting the gas saturation as oil saturation.

Add RigSgasResultCalculator that creates a placeholder SGAS entry when the model has gas and water but no oil phase, SWAT is present and SGAS is missing. SGAS is then computed as SGAS = 1 - SWAT. The computation is dispatched from findOrLoadKnownScalarResult() and findOrLoadKnownScalarResultForTimeStep() the same way as SOIL.

The equivalent logic in RigCaseCellResultsData::testAndComputeSgasForTimeStep() was only reachable from RigSoilResultCalculator, which never runs for models without an oil phase. This function is replaced by computeSgasForTimeStep() delegating to the new calculator, and the unreachable call in RigSoilResultCalculator is removed.
2026-08-21 14:36:22 +02:00
Magne Sjaastad 5b6307bc81 #14563 Fix truncated display of axis values for summary data
The 'g' number format interprets the precision as the number of significant digits, not as the number of decimals. Using the number of decimals directly caused tick labels to be rounded to too few digits, so 10.5 was displayed as 11 and the same label could appear twice. Add the number of integer digits to the precision when the automatic number format is used.
2026-08-20 15:20:58 +02:00
Magne Sjaastad fad56ae483 #14562 Summary Plot Editor: Improve default source selection
Select the first ensemble when no top level summary cases are available, and never use a realization of an ensemble as the default source. The default source is now resolved by RiuSummaryVectorSelectionUi::defaultSummarySource(), used both when the editor is opened without a selection and when the editor is populated from a plot without curves. Also guard against a null curve set when matching ensemble curve sets in the preview plot.
2026-08-20 15:20:58 +02:00
Magne Sjaastad 9b268e6d98 #14543 Use time from simulation start in Show Plot Data
When the summary plot time axis is configured to show time from simulation start, the text produced by Show Plot Data and the ASCII export now reports the same values instead of date and time. The time column header becomes "Time [<unit>]", and each row reports the elapsed time relative to the first time step of the first curve, scaled to the display unit selected on the time axis. Plots using the date based time axis are unchanged.

Time in months and years is computed using calendar arithmetic instead of a fixed number of seconds per unit, so a time step exactly N calendar months or years after the simulation start reports exactly N. This makes resampled data report whole numbers also when a leap year is part of the interval. The calendar arithmetic is available as RiaQDateTimeTools::calendarYearsBetween() and calendarMonthsBetween(), and is used both when plotting the curves and when reporting the time column.
2026-08-20 11:26:56 +02:00
Magne Sjaastad 508804d6ab #14544 Make mime data hasFormat() consistent with formats()
MimeDataWithIndexes and MimeDataWithReferences advertised every stored format in formats() but denied all of them except their own format name in hasFormat(). The object reference paths that the tree view stores under the ObjectReferenceList mime type were therefore reported as absent, and only worked because QMimeData::data() does not consult hasFormat().

Fall back to QMimeData::hasFormat() so the two functions agree.
2026-08-19 10:32:17 +02:00
Magne Sjaastad 5b62a84a12 Log error when a result fails to load
findOrLoadKnownScalarResultForTimeStep asserted when the reader failed to
load a result. A result listed in the meta data that cannot be read from
file is a run-time condition, not a programming error, and aborted the
application in builds with asserts enabled.

Report the failing result name and time step through RiaLogging instead.
2026-08-19 07:26:27 +02:00
Magne Sjaastad 71090bdc18 Skip texture binding when texture setup has failed
RenderStateTextureBindings::setupTextures() ignores the return value of
Texture::setupTexture(). When setup fails, the texture is deleted and left
without a valid OpenGL id, but applyOpenGL() still calls Texture::bind(),
where CVF_ASSERT on the id aborts the application.

Skip bindings without a valid texture id and log a render error instead.
2026-08-19 07:26:27 +02:00
Magne Sjaastad 43a925c3f0 #14527 Scale MAPAXES to grid units in the opm-common grid reader
opm-common scales MAPAXES to meter based on the MAPUNITS keyword, while COORD and ZCORN are left in the units given by GRIDUNIT. The map axis transform uses normalized axes plus an origin translation, so the origin must be in grid units to match the node coordinates. Scale the map axes accordingly, and use with_mapaxes() as the guard, as get_mapaxes() returns a fixed size array that is only assigned when the MAPAXES keyword is present. The duplicated map axes handling in the two readers is moved into a shared function.

RivSingleCellPartGenerator-Test.cpp and RivIjkIntersectionGeometryGenerator-Test.cpp both define buildBoxGrid in an anonymous namespace. The definitions do not collide as long as the two files end up in different unity build chunks, but adding a test file repartitions the chunks and the build then fails with a redefinition error.
2026-08-19 07:25:41 +02:00
Magne Sjaastad d6c0df228d Fix RHEL8 vcpkg build broken by the toolchain cross-compile check
The RHEL8 job pins its own older ports baseline in
vcpkg-configuration-rhel8.json for Rocky Linux 8 compatibility. At that
baseline arrow 18.1.0 passes -DCMAKE_SYSTEM_PROCESSOR=x64, which the
updated linux toolchain reads as a cross build and answers with a
nonexistent x64-linux-gnu-gcc. The January 2025 toolchain had no such
inference, so the mismatch used to be harmless.

Bump the vcpkg submodule to normalize the vcpkg triplet architecture to
the GNU processor name before the cross-compilation check.

The default baseline is unaffected: arrow 21.0.0 there no longer passes
the flag, upstream having removed it in microsoft/vcpkg#47958.
2026-08-14 12:51:26 +02:00
Magne Sjaastad fa0c45a24f Update vcpkg to the 2026.07.29 release
Rebase the fork-local patches onto microsoft/vcpkg tag 2026.07.29
(9e593bb18ea69cc5095e012465dcd675a822ed0d): drop /Z7 from the vcpkg
Windows toolchain, and set CMAKE_POLICY_VERSION_MINIMUM for the
x64-linux and x64-windows triplets.

The previous submodule commit was based on upstream from January 2025,
which pinned msys2-runtime-3.5.4-2. That distfile has been pruned from
all MSYS2 mirrors, so any Windows build with a cold vcpkg binary cache
failed with a 404 while rapidjson acquired pkgconf. The new base pins
msys2-runtime-3.6.5-1, which is still hosted.

The ports baseline in vcpkg-configuration.json is left unchanged.
2026-08-14 12:51:26 +02:00
Magne Sjaastad 0af06ad965 #14423 Fail a unit test leaving data behind in the shared project
The project is a global object shared by all tests, and the MSW export tests loaded a project without closing it. The two summary cases of that project stayed in the summary case main collection, and made RimSummaryCaseMainCollection.RemoveCases_NoDanglingInCallerVector fail on Windows only. Google Test runs the test suites in link order, and the MSW suite runs before the summary suite on Windows and after it on Linux.

Add a test event listener reporting a failure for the test leaving cases, ensembles, well paths or views behind in the project. The project is closed as well, so the tests running after the offending one are unaffected.
2026-08-14 10:23:34 +02:00
Magne Sjaastad db594490fc #14517 Share the mock case factory between the summary tests
RimDeltaSummaryEnsemble-Test.cpp and RimSummaryCaseMainCollection-Test.cpp each defined an identical createMockCase() in an anonymous namespace. A unity build concatenates the two translation units, which merges the two anonymous namespaces into one and makes the second definition a redefinition, breaking the build with C2084.

Move the factory to RimMockSummaryCase.h, the header both tests already include for the mock case itself, and drop both local copies.
2026-08-14 10:23:34 +02:00
Magne Sjaastad 8ac2255832 #14423 Defer destruction of orphaned derived cases to a batch flush
Closing all summary cases while a delta ensemble is present crashed with a use-after-free. RicCloseSummaryCaseFeature::deleteSummaryCases holds a case list across removeCases and deletes it afterwards, while removeCases made the delta ensemble rebuild and destroy derived cases that are themselves part of that list. The previous fix rewrote the caller list with the surviving cases, which stopped the crash but left the hazard in place for any other caller holding a case list across a removal.

Add RimSummaryCaseUpdateBatch, a plain scope object that is ambient for the duration of its scope. Removal now only detaches, and hands the detached cases to the batch. The outermost scope flushes, regenerating the dirty delta ensembles in dependency order first so a chained delta ensemble sees the final state of its source, then destroying the orphans with caf::PdmObjectHandleTools::deleteObjects. A nested batch contributes to the outermost one and never flushes. Orphans are held as guarded pointers, so a case the caller destroyed itself is skipped instead of being destroyed twice. Both contribution points fall back to immediate execution when no batch is active, so call sites that do not open one keep behaving as before.

Open a batch in RimSummaryCaseMainCollection::removeCases and in RicCloseSummaryCaseFeature::deleteSummaryCases, which is the outermost of the two and therefore keeps the detached cases alive across its own deleteObjects call. removeCases no longer rewrites the caller list, so it now takes it by const reference.
2026-08-14 10:23:34 +02:00
Magne Sjaastad 6c0cc9c363 #14423 Rebuild delta ensemble derived cases declaratively
Derived cases were pooled through an m_inUse flag on RimDeltaSummaryCase. Every regeneration marked all cases not in use, which also severed their source references and cleared their caches, then handed them back out one by one and deleted whatever was left over. The flag conflated pool bookkeeping with owning the source references, allSummaryCases() hid the not-in-use cases from the rest of the project, and cases taken from the pool were pushed straight into m_cases without connecting nameChanged, so a renamed source case never propagated to the derived case.

Replace the pooling with desiredSourceCasePairs(), a pure computation of the source case pairs the ensemble should have, and rebuildDerivedCases(), which diffs that against the existing derived cases keyed on the source case pointer pair. Matching cases are reused, missing ones are created, and surplus ones are detached and returned to the caller instead of being deleted in place. Keying on the pointer pair makes the rebuild idempotent, also right after project load where the derived cases arrive from XML with their sources already resolved.

Add the protected RimSummaryEnsemble::addCaseWithoutDependencyUpdate(), used both by addCase() and by the rebuild, so a derived case gets nameChanged connected without triggering the dependent-ensemble notification that the rebuild is already performing itself.

Remove setAllCasesNotInUse(), firstCaseNotInUse(), deleteCasesNoInUse(), RimDeltaSummaryCase::setInUse()/isInUse() and the m_inUse field, and add clearSourceCases() for the one thing setInUse(false) was actually needed for. Old project files keep loading, unknown XML keywords are skipped. The activeOnly parameter of allDerivedCases() is gone and RimDeltaSummaryEnsemble no longer overrides allSummaryCases().
2026-08-14 10:23:34 +02:00
Magne Sjaastad f0d6081448 #14423 Add cycle-safe delta ensemble dependency traversal
Delta ensembles were located by scanning the summary case main collection for objects referring to a given ensemble, and the dependent ensembles were visited by unguarded recursion. A dependency cycle, which is constructible through the UI, made that recursion run forever, and an ensemble used as both source 1 and source 2 was reported twice.

Add dependentDeltaEnsembles(), deltaEnsemblesInUpdateOrder() and wouldCreateDependencyCycle() to RimSummaryEnsembleTools. The traversal uses the PDM back references, deduplicates, is iterative with visited and on-path sets, and returns the delta ensembles in topological order so a delta ensemble is always visited before the delta ensembles using it as a source. Back edges are logged instead of traversed.

Reimplement updateDependentDeltaEnsembles on top of the new traversal and replace RimDeltaSummaryEnsemble::findReferringEnsembles() with dependentDeltaEnsembles() at its four call sites. Back references also find a delta ensemble that is detached from the project tree, which the previous ancestor scan did not.
2026-08-14 10:23:34 +02:00
Magne Sjaastad b97a52005e #14423 Fix crash when closing summary cases referred by a delta ensemble
Removing a source case makes a delta ensemble recreate and delete its derived cases. Those cases are part of the list being removed, leaving dangling pointers that crash in PdmObjectHandle::prepareForDelete(). Use guarded pointers and return only the surviving cases.
2026-08-14 10:23:34 +02:00
Magne Sjaastad acbc041f13 Remove Copilot coding agent setup workflow
The copilot-setup-steps job ran before every Copilot task and spent up to
an hour on submodule checkout, Qt, vcpkg and a full build, which is wasted
work for documentation and other text-only tasks. Remove it so cloud tasks
start from a plain checkout.
2026-08-13 14:49:10 +02:00
Magne Sjaastad 3371141a60 #14319 Guard against malformed statistics cache files when restoring contour map results 2026-08-13 07:22:30 +02:00
Magne Sjaastad 0049d6ee72 #14319 Store ensemble contour map statistics cache as GRI surface files 2026-08-13 07:22:30 +02:00
Magne Sjaastad 4d789634f4 #14319 Cache computed ensemble contour map statistics as ROFF files next to the project 2026-08-13 07:22:30 +02:00
Magne Sjaastad 88832908a4 #14319 Add binary ROFF writer with round-trip unit tests 2026-08-13 07:22:30 +02:00
Magne Sjaastad c8b1ebc266 #14505 caf: Defer group box deletion in PdmUiFormLayoutObjectEditor
The group boxes were destroyed with a raw delete, which destroys the child widgets immediately and defeats the deleteLater() used by PdmUiFieldEditorHandle to keep field editor widgets alive. When the rebuild is triggered from fieldChangedByUi(), Qt is still using those widgets further up the stack, giving a use-after-free.

Hide and detach the group box before scheduling a deferred delete, so it leaves the layout, the visual tree and the focus chain immediately. Detaching first is what plain deleteLater() did not do in issue 9719.
2026-08-11 08:36:37 +02:00
Magne Sjaastad d5a7e79d50 #14505 caf: Guard null auto value tool button in PdmUiLineEditor
The tool button and the layout are owned by m_placeholder, which is not returned as the editor widget when auto value is not supported. Nothing tracks m_placeholder in that case, so it can be destroyed together with a parent widget while the reparented m_lineEdit survives. A later configureAndUpdateUi() then found a non-null m_lineEdit and a null m_autoValueToolButton, and crashed on hide().

Guard both pointers before use, matching how m_label and m_lineEdit are already guarded in the same function. This is a separate defect from the use-after-free in issue 14505, found while reproducing it in cafTestApplication.
2026-08-11 08:36:37 +02:00
Magne Sjaastad 702e7e3173 #14505 caf: Add cafTestApplication repro for reentrant property editor rebuild
Adds a ReentrantEditorRebuild demo object that rebuilds all property editors from inside fieldChangedByUi(), while Qt is still executing QLineEdit::focusOutEvent. Type a value into Realization Filter and press Tab to trigger. The change handler drops both the line edit field and its parent group from the ui ordering, so PdmUiFormLayoutObjectEditor::configureAndUpdateUi() tears down widgets that are still in use further up the stack. Uncheck Rebuild Layout On Change to run the same edit without the reentrant rebuild.
2026-08-11 08:36:37 +02:00
Magne Sjaastad c9d118f2de Copy Qt pdb files to the build folder when present
Qt is distributed both with and without pdb files. When they are present,
copy them next to the Qt dlls so symbols are available when debugging.

Pass --pdb to windeployqt for the configurations built with debug
information, and copy the matching pdb file in the AppFwk test
applications and unit tests using a helper script that skips missing
files.
2026-08-10 13:48:34 +02:00
Magne Sjaastad 1660e333ff #14493 Delete curves for all cases in one pass when deleting a summary ensemble
deleteSummaryCaseCollection() looped over every case in the ensemble, and for each case scanned every summary plot and called updateConnectedEditors() on every multi plot. For an ensemble with many realizations this scanned each plot once per realization, and updated the connected editors once per realization and multi plot. This showed up as a hot spot in the profiler.

Replace deleteCurvesAssosiatedWithCase() with deleteCurvesAssosiatedWithCases(), which takes a set of cases and returns whether anything was deleted. Each plot is now scanned once, and the connected editors of a multi plot are updated only when that multi plot actually lost a curve.

RicCloseSummaryCaseFeature and RicCloseObservedDataFeature are updated to the new signature. RicCloseSummaryCaseFeature also scans each plot once instead of once per case.
2026-08-10 12:38:41 +02:00
Magne Sjaastad 1d4a15fd89 #14493 Clear referring curve sets directly when an ensemble is deleted
The destructor called updateReferringCurveSets(), which runs loadDataAndUpdate() on every referring curve set. At that point the summary cases are already deleted, so reloading the curve data has nothing to read. The reload also syncs UI fields, reloads the curve filters, rebuilds the address list, recomputes statistics over an empty case set and updates three legends, and it calls updateAll() on the parent plot once per curve set instead of once per plot.

Add clearReferringCurveSets(), which deletes the ensemble and statistics curves directly and updates each affected plot once. For an ensemble with many realizations spread over several curve sets in the same plot this removes most of the work done while deleting.

Query the referring objects as RimEnsembleCurveSet instead of walking all referring objects and casting. The previous loop discarded everything that was not a curve set, so the behavior is unchanged.
2026-08-10 12:38:41 +02:00
Magne Sjaastad 6bdccdf074 #14486 Legend: Never use zero as lower limit for a logarithmic color range
The lower limit of a logarithmic color range was computed from the value closest to zero, which is not always available. When this value was zero, log10() returned -inf and the lower limit ended up as zero. Use the closest power of ten below the lowest value instead, and use the value closest to zero only when the lowest value is zero or negative. Guard computeTenExponentCeil() and computeTenExponentFloor() against zero, and make sure the range always spans at least one decade.
2026-08-10 12:38:21 +02:00
Magne Sjaastad 2237f578c8 #14486 Contour Map: Always use an available cell result as data source
A new contour map created from the right-click menu on a case used a hardcoded SOIL result, resulting in an empty view when SOIL was not present in the case. Use the cell result of an existing view for the same case if one is available, and otherwise fall back to RigCaseCellResultsData::defaultResult(), which verifies that the result exists before selecting it. Completion Type is a category result and is now excluded as a default result for both 3d views and contour maps.
2026-08-10 12:38:21 +02:00
Magne Sjaastad b851801cfb #14488 Surface and intersection improvements
Use I as the default direction when creating an IJK intersection, and center the fixed index on the I axis.

Allow multiselect of surfaces for a Surface Curve. The field keyword is changed from Surface1 to Surfaces, with a keyword alias so existing project files are read as before. One curve is drawn per selected surface, each with its own label and color.

Use the color defined in the Surfaces collection for Surface Band and Surface Curve. The color is shown as read only, and a Custom Color option allows the user to specify a color. Bands created from an ensemble surface keep the structural uncertainty colors, as the statistics surfaces share the same color.
2026-08-10 12:38:02 +02:00
Magne Sjaastad ff7bf6c7c9 Do not terminate when a grid calculation reads a cell index out of range
RigAllGridCellsResultAccessor bounds checked the cell index with an assert that is compiled out in release builds, and then called std::vector::at(), which throws. RimGridCalculation::getActiveCellValues() calls the accessor from an OpenMP loop, using active cell indices from the destination case to read results from the source case, so an index beyond the source result vector threw an exception that could not escape the parallel region and called std::terminate. Return undefined for an out of range index instead, and guard against a null result accessor.
2026-08-10 10:07:32 +02:00
Magne Sjaastad dd63083c13 Guard against undefined permeability index in riTRANS computation
computeRiTransComponent() checks the transmissibility index and the neighbor
cell permeability index for cvf::UNDEFINED_SIZE_T, but not the permeability
index of the cell itself. The transmissibility and permeability results are
indexed independently, so a defined transmissibility index does not imply a
defined permeability index. For a case where transmissibility is stored per
cell and permeability per active cell, an inactive cell passes the
transmissibility check and then indexes the permeability vector with
UNDEFINED_SIZE_T.

Check the permeability index of the cell before use, matching the guard in
computeNncCombRiTrans().
2026-08-10 10:07:32 +02:00
Magne Sjaastad abfec02080 Skip source cases without case data when computing union of active cells
loadMainCaseAndActiveCellInfo() calls openAndReadActiveCellData() for each
source case in a grid case group. That function returns false before
setReservoirData() when the grid file is missing, and the loop only continues,
so the case stays in the collection with a null eclipseCaseData().
computeUnionOfActiveCells() then iterates every reservoir and calls
eclipseCaseData()->activeCellInfo() on it, which reads m_activeCellInfo off a
null case data and crashes.

Collect the source cases that have case data once, before the cell loops, and
iterate those. This also removes the per-cell case lookup from the innermost
loop.
2026-08-10 10:07:32 +02:00
Magne Sjaastad 55fb8105e2 Do not read NNC geometry out of bounds on INIT and grid file mismatch
transferStaticNNCData() takes the connection count from the INIT file using
ecl_nnc_data_get_size(), but iterates the geometry from the grid file using
ecl_nnc_geometry_iget(). The two counts were only compared in an assert, which
is compiled out in release builds, so an INIT file out of sync with the grid
reads past the end of the geometry vector inside resdata.

Compare the counts at run time, log a warning and iterate the smaller of the
two. Both nncConnections and transmissibilityValuesTemp are filled in the same
loop and stay aligned.
2026-08-10 10:07:32 +02:00
Magne Sjaastad 240df658ee Guard against missing RFT reader when creating an RFT curve
addRftCurve() selects the first Eclipse result case in the project without
checking that it has an RFT reader, and then dereferences rftReader(). The
command is enabled by hasRftData(), which returns true if any case has a
reader, so with several Eclipse cases loaded an enabled menu entry can select
a case with no RFT data and crash.

Null check the reader before use, matching the guard already used by
hasRftData() and hasRftDataForWell().
2026-08-10 10:07:32 +02:00
Magne Sjaastad c0ebe1a331 #14496 MSW export: Order COMPSEGS by branch number
The COMPSEGS rows were emitted in the order the branches are listed in, so they followed the WELSEGS order where the completion branches come before the laterals with a lower branch number.

The rows are now collected while the segments are visited and sorted by branch number before they are added. The sort is stable, so the rows of a branch keep their order along the well path.
2026-08-10 09:42:17 +02:00
Magne Sjaastad f796801a7a #14496 MSW export: Assign branch ID to well path laterals before completions
Branch numbers are handed out from two counters. The well path laterals are numbered first, so that the main bore and the laterals occupy the lowest branch numbers. The completion branches (valves, fractures and fishbones) are numbered after all laterals, and are still listed immediately after the lateral they are connected to.

The multiple_laterals test project is reduced to a single Eclipse case, and is used by two new unit tests covering the branch numbers and the order the branches are listed in.
2026-08-10 09:42:17 +02:00
Magne Sjaastad aa01d5e4dc #12810 Guard curve mergers against mismatching X and Y sample counts
RiaCurveMerger::addCurveData and RiaWellLogCurveMerger::addCurveData only
validated that the X and Y vectors have the same size with CAF_ASSERT, which is
compiled out in optimized builds. Mismatching sizes are a legitimate run-time
condition for data read from file, so the check has to hold in release builds as
well.

In RiaCurveMerger the shared-X fast path in computeInterpolatedValues() indexes
the Y vector of every curve using the sample count of the first curve. A curve
with identical X values but fewer Y values therefore read past the end of its
heap buffer inside the OpenMP loop. RimEnsembleStatisticsCase passes time steps
and values straight from the summary reader without reconciling the sizes, and
realizations in an ensemble normally share time steps, so this was reachable for
an ensemble containing an ongoing simulation.

Both mergers now truncate the incoming data to the common sample count instead.
For RiaWellLogCurveMerger this is also an improvement in behaviour, since
lookupYValue() used to discard the whole curve when the sizes differed.

Add unit tests covering a single curve with fewer values than time steps and
curves with shared time steps where one curve has fewer values.
2026-08-10 09:41:56 +02:00
Magne Sjaastad c8e11da3d5 #14252 MSW: Report a grid cell only once in COMPSEGS
When several perforation intervals overlap the same grid cell, the main bore COMPSEGS table emitted one row per interval. Two perforations placed back to back therefore reported the same IJK twice, while COMPDAT correctly reported a single connection for that cell.

The candidate intersections collected for one cell all describe the same IJK, so they are now merged into a single COMPSEGS row spanning the union of the overlapping measured depth ranges.

Add TestModels/msw-export/project-files/perf_two_back_to_back.rsp covering the case.
2026-08-10 09:41:41 +02:00
Magne Sjaastad 94da54c4f5 Remove AsyncPdmObjectVectorDeleter and delete PDM objects synchronously
Deleting PDM objects on a worker thread is not safe. PdmObjectHandle::prepareForDelete()
mutates state owned by other objects, it nulls the guarded pointers held by other objects
and clears m_pointersReferencingMe, an unsynchronised std::set that every PdmPointer
construction and destruction touches. Destroying an object off the main thread therefore
races with the main thread on the shared object graph. See issue 14491.

Profiling a summary ensemble teardown shows the mechanism does not pay for itself. Releasing
397 Drogon realizations takes 0.24 s sequentially and 0.24 s in parallel, and for a heavy
case the parallel release is slower than the sequential one, because free() is serialised
inside the allocator. The PDM bookkeeping itself is 0.4 to 2.8 percent of the teardown.

Remove the class and deleteChildrenAsync(), and delete synchronously instead. The call sites
that used clearWithoutDelete() and a manual delete loop to work around the race can now call
deleteChildren() directly. Add caf::PdmObjectHandleTools::deleteObjects() for the case where
the objects are no longer owned by a child array field.

The observer disconnection from issue 12262 does not depend on clearWithoutDelete(). ~Signal()
unregisters itself from every observer, so a deleted child detaches itself. That fix addressed
the async race, where ~Signal() mutated the observer list from a worker thread. The unit test
is updated to assert the observed signal count directly instead of relying on a crash.
2026-08-10 09:40:33 +02:00
Magne Sjaastad 2322d9e97f #14476 CI: Enable CAF_ASSERT in the clang Release build 2026-08-07 15:05:57 +02:00
Magne Sjaastad 1c4c304064 #14476 ApplicationLibCode: Remove unused cvf includes
Remove 95 cvf include lines that the including file does not use. Found by
extracting the symbols declared by every VizFwk header, including symbols they
re-export, and flagging includes where none of those symbols appear anywhere in
the file. Each removal is verified by a full build.

Most of them are cvfVector3.h and cvfObject.h, left behind as the files they
were once needed by changed.

pch.h is left untouched. It includes cvfObject.h and cvfVector3.h on purpose, so
that the files using the precompiled header do not have to.

Note that a clean build after removing an include does not prove the include was
unnecessary, only that the declarations still arrive some other way. That path
can differ between platforms and with RESINSIGHT_ENABLE_UNITY_BUILD, so this
needs a CI round on Linux as well.
2026-08-07 15:05:57 +02:00
Magne Sjaastad 0a4bebcd41 #14476 ApplicationLibCode: Remove includes of cvfBase.h
cvfBase.h includes cvfAssert.h, so these six files were the last indirect route
into the assert header that the previous commit removed from ApplicationLibCode.

Five files dropped the include with no further change. RimSeismicAlphaMapper
declared alphaValue as returning cvf::ubyte, a typedef defined in cvfBase.h
itself, so no smaller VizFwk header provides it. Since cvf::ubyte is
unsigned char, use that directly in the declaration, the definition and the
cast. The type is identical, so callers are unaffected, and the class no longer
depends on VizFwk at all.
2026-08-07 15:05:57 +02:00
Magne Sjaastad 56e98ba1c4 #14476 ApplicationLibCode: Replace CVF_ASSERT with CAF_ASSERT
Migrate all assert macros in ApplicationLibCode to CAF_ASSERT and remove every
use of cvfAssert.h.

CVF_ASSERT is replaced one to one. CVF_TIGHT_ASSERT is also replaced by
CAF_ASSERT, which is semantically exact: CVF_ENABLE_TIGHT_ASSERTS is 1 only
under _DEBUG, and that is what CAF_ASSERT now does. The two CVF_FAIL_MSG sites
become CAF_ASSERT( false && "message" ), preserving the message with the idiom
already used elsewhere in the code base.

Counts before and after: CVF_ASSERT 1044 to 0, CVF_TIGHT_ASSERT 66 to 0,
CVF_FAIL_MSG 2 to 0, cvfAssert.h references 154 to 0.

Include handling: files that included cvfAssert.h directly now include
cafAssert.h instead, includes left dead by the migration are removed, and files
that were relying on cvfAssert.h transitively get an explicit cafAssert.h. Files
that reach cafAssert.h through another caf header are left unchanged; a missing
include here is a compile error, not a silently disabled assert.

ResultStatisticsCache links only LibCore and therefore had no path to
cafAssert.h. Add the cafPdmCore directory as a private include path rather than
linking the library, since cafAssert.h is header only.

Note that this stops these asserts from firing in Release and RelWithDebInfo,
where CVF_ASSERT was previously active.
2026-08-07 15:05:57 +02:00
Magne Sjaastad d0e9f45f32 #14397 Make cvfAssert.h self-contained
cvfAssert.h tests CVF_ENABLE_ASSERTS but did not include cvfConfigCore.h, which
is where that macro gets its default value of 1. A translation unit processing
cvfAssert.h before cvfConfigCore.h evaluated the undefined identifier as 0 and
silently compiled every CVF_ASSERT, CVF_ASSERT_MSG and CVF_FAIL_MSG in that unit
into a no-op, without any warning and without evaluating the expressions.

Because the header uses pragma once, including cvfConfigCore.h later in the same
unit did not recover the macros. Alphabetical include sorting actively produces
the broken order, since cvfAssert.h sorts before cvfBase.h.

Include cvfConfigCore.h before the macro definitions. It only defines macros, has
pragma once and no dependencies, so this is immune to future include reordering.

This turns the asserts back on in the affected translation units, in both debug
and release builds, and immediately exposed one assert that had never compiled:
in RigFemPartResultCalculatorNormalized::calculate, isNormalizableResult was
called unqualified even though it is a static member of
RigFemPartResultsCollection. Qualify it, matching isMatching in the same file.
2026-08-07 15:05:57 +02:00
Magne Sjaastad 21694b11e7 #14476 Align CAF_ASSERT with standard assert semantics
CAF_ASSERT was unconditionally active in every build configuration. Make it
follow the semantics of the standard assert(): active in Debug, compiled out in
optimized builds (NDEBUG).

Add the CMake option RESINSIGHT_ENABLE_ASSERTS_IN_RELEASE (default OFF) for
developers who want the asserts to stay active in an optimized build, which is
useful when reproducing a problem in RelWithDebInfo with a debugger attached.

When compiled out, the expression is kept inside an unevaluated sizeof rather
than discarded. It is not evaluated, so there is no run-time cost and no side
effects, but it is still type checked and any variable used only by the assert
still counts as referenced, avoiding a wave of unused-variable warnings.

CAF_ENABLE_ASSERTS is given a default in cafAssert.h so the header stays
self-contained and include order can never silently switch the asserts off.

Document in docs/agents/coding-style.md that CAF_ASSERT is the assert to use,
that CVF_ASSERT is legacy, and that asserts are for broken invariants rather
than for run-time conditions that need real error handling.
2026-08-07 15:05:57 +02:00
Magne Sjaastad 827fa64deb #14252 MSW: Name the lateral in the WELSEGS comment line
The first segment of a lateral had its comment cleared when the lateral had a tie-in valve, leaving the rows unlabelled. Write the well path name instead, so the exported segments can be traced back to the lateral they belong to.
2026-08-07 13:29:32 +02:00
Magne Sjaastad 74a6c0827b #14477 MSW: Apply max segment length to fishbones laterals
The geometry path emitted one WELSEGS row per cell intersection of a fishbones lateral, ignoring the max segment length and the custom segment intervals. The tree path split them like every other completion, in collectCompletionWelsegsSegments.

Split each cell intersection with createSubSegmentMDPairs and chain the resulting rows. The lateral geometry is not part of the well path, so the TVD of a sub-segment cannot be interpolated along the well path. Interpolate linearly between the start and end TVD of the intersection instead, as the tree path did for fishbones. The cell is connected by the first sub-segment only, leaving COMPSEGS unchanged, and the lateral label stays on the first row.

An intersection now covers several segment numbers that share one effective diameter, and must still contribute once to the sum for its cell. FishbonesLateralSegment therefore holds the segment numbers of an intersection rather than a single one, and the intersections are grouped per lateral in FishbonesLateral. This replaces the separate list of first and second segment numbers, since the reduction rule is now expressed on the intersections of a lateral.

Verified with a copy of fishbones.rsp with max segment length enforced at 4 m: the three laterals are split into three or four rows each, depths are evenly interpolated, effective diameters are unchanged at 0.01664 and 0.00960, and COMPSEGS is identical. The sweep against TestModels/msw-export is unchanged, as none of the projects enforce a max segment length.
2026-08-07 13:29:32 +02:00
Magne Sjaastad 32850cac9e #14477 MSW: Make the ICD area summation order independent
The summation reproduced the tree implementation, where an ICD sub connected to several cells took part in the sum of each of them, reading areas that earlier cells had already replaced. The result depended on the order the cells were visited in, and an ICD sub could contribute an already combined area to the next cell.

Compute every cell sum from the original areas instead. An ICD sub spans a 0.1 m valve segment and normally connects to a single cell, and when it reaches into more than one it now reports the largest of the sums it takes part in.

Behaviour is unchanged for the common case of one ICD sub per cell, and the sweep against TestModels/msw-export is unaffected. Added tests for the multi-cell case and for independence of the recording order.
2026-08-07 13:29:32 +02:00
Magne Sjaastad cd315870f2 #14477 MSW: Sum WSEGVALV area for fishbones ICD subs sharing a grid cell
The legacy tree path gave every fishbones ICD sub connected to the same grid cell the sum of their areas, so the cell sees the total flow area of the ICDs completing it. That was part of updateDataForMultipleItemsInSameGridCell and disappeared with the rest of the tree code, leaving each ICD sub with its own area.

Record the grid cells each ICD sub connects to while the branches are built, and apply the summation together with the effective diameters. FishbonesDiameterContext now carries both, and is renamed to FishbonesExportContext.

An ICD sub connected to several cells takes part in the sum of each of them and ends up with the value of the last cell in ascending cell order. This is order dependent, and is kept because the tree implementation behaved the same way.

None of the projects in TestModels/msw-export has more than one ICD sub per cell, so the sweep against the stored reference output is unchanged. Added unit tests for applyIcdAreaPerCell and applyEffectiveDiameters instead, since neither function had coverage.
2026-08-07 13:29:32 +02:00
Magne Sjaastad a4713ed43b #14477 MSW: Compute effective diameter for fishbones laterals
The geometry path exported the raw equivalent diameter for every fishbones lateral segment. The legacy tree path replaced it with an effective diameter, computed in updateDataForMultipleItemsInSameGridCell, which was removed together with the rest of the tree code.

Reinstate both rules. Laterals sharing a grid cell get Deff = sqrt(d1^2 + d2^2 + ..) over the lateral segments in that cell (#7686). The first segment of a lateral shares its cell with the main bore and with the first segment of every other lateral on the same sub, so it inherits the effective diameter of the second segment of the same lateral (#7731). A lateral contained in a single grid cell has no second segment and keeps the combined value, which is where the difference from the tree path was most visible.

Lateral segments are recorded while the branches are built, since the grid cell of a segment is not recoverable afterwards. COMPSEGS deduplication clears the cell intersections of segments in an already connected cell. The context is shared by the main bore and all tie-in laterals, and applied once the branches are assembled.

Verified against the twelve projects in TestModels/msw-export: every well with stored reference output now matches the legacy values, including the fishbones laterals that previously differed.
2026-08-07 13:29:32 +02:00
Magne Sjaastad c3801b268e #14477 MSW: Restore diameter and roughness for fishbones ICD subs
The fishbones ICD sub segment left RigMswSegment::diameter and roughness unset, and the formatter renders an empty optional as the 1* default marker. The legacy tree path emitted 0.15 and 5.0e-5 for these rows, taken from the RicMswSegment constructor defaults. RigMswSegment has never had such defaults, so the comment claiming the tree behaviour was matched was wrong.

Set both values explicitly, as buildFractureBranches already does. Verified against TestModels/msw-export/project-files/fishbones.rsp: the ICD sub WELSEGS row again exports 0.15000 and 0.0000500, matching the stored legacy reference output.
2026-08-07 13:29:32 +02:00