Requests are queued by the completion queue thread and processed from the main
thread. A long running request, or an open progress dialog in the GUI, therefore
left every request unanswered for as long as that operation lasted, including
the liveness checks a client uses to decide whether ResInsight is still there.
Let a callback declare that it touches no application state, and serve those
directly on the completion queue thread instead of queueing them. GetVersion is
the only such callback, since it reads compile-time constants only. Everything
that touches the project or the data model is still processed on the main
thread, so no request can observe or modify the data model while another one is
running.
The allocated callback list is now used from both threads and is guarded by a
mutex. Shutdown takes a lock before flagging that it is in progress, so that it
waits for any dispatch already running on the completion queue thread and no new
request handler can be registered on a queue that is about to be shut down.
During a blocking call of 3.25 s, 65 pings were answered with a maximum latency
of 6.2 ms. Before this change none of them were answered until the call
completed.
A schedule generated from a full production history, or a large raw text block
imported into the event timeline, easily exceeds the gRPC default limit of 4 MB.
Such a call failed with "Received message larger than max (5119402 vs 4194304)"
even though both sides were healthy.
Raise the limit to 128 MB on the server and use a matching limit for the client
channels. A schedule of 19.5 MB is now returned without error.
A call that failed because the connection went away surfaced the raw gRPC text,
typically "Channel closed!", which says nothing about what happened. Translate
such failures through a single helper that explains that the connection to
ResInsight was lost while the call was running, and that names the message size
limit when that is the cause. The gRPC code and details are still carried on the
RipsError for callers that branch on them.
ResInsight serves gRPC requests from its main thread, so a long running call
leaves the heartbeat pings unanswered for as long as that call runs. The
heartbeat counted those timeouts as connection failures and closed the channel,
which aborted the very call the user was waiting for. The client reported
"RipsError: Channel closed!" while the ResInsight process was alive and well.
Classify a ping that fails with DEADLINE_EXCEEDED or RESOURCE_EXHAUSTED as a
busy server and never count it towards the failure threshold. Only failures
that indicate a broken connection declare the connection lost. A new
busy_warning_sec argument controls when a warning is logged for a server that
has been unresponsive for a while, so long operations are visible without being
killed.
Keep the process handle from Instance.launch so the heartbeat can check whether
ResInsight actually exited. A dead process is now detected immediately and
reported with its exit code instead of a bare channel error, and check_alive()
repeats that diagnosis.
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.
RicNewSummaryEnsembleCurveSetFeature::createPlotForCurveSetsAndUpdate() always
called RiaGuiApplication::instance(), which is null when running as a console
application. Calling new_summary_plot() with an ensemble and no address string
from Python did therefore terminate ResInsight. Only access the plot main
window when the GUI is running.
new_summary_plot() with an ensemble and an address string created an empty
plot. The address filters were applied to an empty list of summary cases, and
no curve sets were created. Use the summary cases of the ensemble, and make
sure the summary addresses are created before the address filters are applied.
Add example script showing how to create summary plots for an ensemble.
* 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.
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.
A term operand had to be an unsigned integer or a DURATION variable, so
"DATE STARTUP = 2026-08-28 + -2" was rejected as a malformed declaration
instead of subtracting two days.
Accept an optional sign on the operand in date and duration expressions, and
share one pattern between the terms of a date expression and the UNTIL terms
of INSERT_DATE so the two cannot drift apart.
Fractional day offsets are still unsupported.
COMMENT is accepted on all keywords and is stored on the event instead of
being passed as keyword data, so it is not part of the item names reported
by opm-common. The validation error listed only the opm-common items, giving
no hint that COMMENT is allowed when a user mistyped it as COMMENTS.
Append COMMENT to the list of valid item names in the error message. Only the
diagnostic is affected, COMMENT is still handled as event metadata.
The visualization framework requires desktop OpenGL with the fixed-function
pipeline. Qt may otherwise create an OpenGL ES or core profile context on
some platforms (e.g. Wayland/EGL on Ubuntu 26.04), causing all 3D rendering
to fail with shader compile errors.
GCC's -Wfree-nonheap-object false-positives on inlined std::vector copies
(GCC PR 104475), breaking -Werror builds with GCC 15 on Ubuntu 26.04. Keep
the warning visible but do not let it fail the build.
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.
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.
Include files containing only INCLUDE statements, such as a wrapper
collecting many lift curve (.ecl/.vfp) files, were dropped during
sector model export, and the includes for all leaf files were written
directly into the exported .DATA file.
Update the custom-opm-common submodule so FileDeck recreates
include-only files in the output directory and preserves the include
hierarchy at any nesting depth. Add a regression test exporting a deck
with a nested include-only wrapper and reloading the result.
Table keywords such as PVTO and SGOF were rewritten by opm-common as one
unbounded line when saving the deck, exceeding the 132 character line
limit of Eclipse 100 and making exported sector models unusable there.
Update the custom-opm-common submodule so DeckOutput tracks the current
line width and wraps records at item boundaries before a line exceeds
132 characters. Quoted values are never split. Add a regression test
that round-trips a deck with a long rel. perm. table and a record of
quoted mnemonics.
Sector model export removed all ACTNUM and FAULTS keywords from the deck
and re-inserted the sector-specific replacements at the end of the GRID
section, which always lands in the main DATA file. Decks that keep these
keywords in separate include files lost their file organization, leaving
the original include files empty while the data was written inline.
Collapse duplicates to a single occurrence instead of removing all
occurrences, so the subsequent in-place replacement keeps the keyword in
its original file when the deck is saved with OutputMode::COPY. ACTNUM
keeps the last occurrence since a later ACTNUM overrides an earlier one;
FAULTS keeps the first so fault definitions do not move past keywords
referencing them.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.