From 446b77dd89629e3693a42e4b77ba55b930e4d014 Mon Sep 17 00:00:00 2001 From: hnil Date: Mon, 4 May 2026 13:37:04 +0200 Subject: [PATCH 1/9] comparing hardcoded timestep --- opm/models/utils/simulator.hh | 2 +- regressionTests.cmake | 30 ++++ tests/run-timestep-replay-regressionTest.sh | 168 ++++++++++++++++++++ 3 files changed, 199 insertions(+), 1 deletion(-) create mode 100755 tests/run-timestep-replay-regressionTest.sh diff --git a/opm/models/utils/simulator.hh b/opm/models/utils/simulator.hh index 4876bb00adc..587cb7c2e87 100644 --- a/opm/models/utils/simulator.hh +++ b/opm/models/utils/simulator.hh @@ -395,7 +395,7 @@ public: * \param value The new value for the time step size \f$\mathrm{[s]}\f$ */ void setTimeStepSize(Scalar value) - { timeStepSize_ = value; } + { timeStepSize_ = float(value); } /*! * \brief Set the current time step index to a given value. diff --git a/regressionTests.cmake b/regressionTests.cmake index 19df609ca39..b08754f6c11 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -5,6 +5,7 @@ opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-regressionTest.sh "") set(abs_tol 2e-2) set(rel_tol 1e-5) set(coarse_rel_tol 1e-2) +set(timestep_replay_rel_tol 2e-4) add_test_compareECLFiles(CASENAME spe1flowexp FILENAME SPE1CASE2 @@ -52,6 +53,35 @@ add_multiple_tests( DIR spe1 ) +opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-timestep-replay-regressionTest.sh "") + +add_test_compareECLFiles(CASENAME spe1_timestep_replay + FILENAME SPE1CASE1 + SIMULATOR flow + PREFIX compareTimestepReplay + ABS_TOL ${abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR spe1) + +add_test_compareECLFiles(CASENAME spe9_timestep_replay + FILENAME SPE9_CP_SHORT + SIMULATOR flow + PREFIX compareTimestepReplay + ABS_TOL ${abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR spe9) + +add_test_compareECLFiles(CASENAME base_model + FILENAME 0_BASE_MODEL6 + SIMULATOR flow + PREFIX compareTimestepReplay + ABS_TOL ${abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model6 + TEST_ARGS --full-time-step-initially=true) + +opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-regressionTest.sh "") + set(_spe1_coarse_tests SPE1CASE2_GASWATER SPE1CASE2_GASWATER_MSW diff --git a/tests/run-timestep-replay-regressionTest.sh b/tests/run-timestep-replay-regressionTest.sh new file mode 100755 index 00000000000..b1d46c5ddd3 --- /dev/null +++ b/tests/run-timestep-replay-regressionTest.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +# This runs a simulator, extracts the accepted substep end times from the +# generated INFOSTEP file, reruns the same case using the hardcoded timestep +# controller, and compares the two outputs. + +set -u + +if test $# -eq 0 +then + echo -e "Usage:\t$0 -- [additional simulator options]" + echo -e "\tMandatory options:" + echo -e "\t\t -i Path to read deck from" + echo -e "\t\t -r Path to store results in" + echo -e "\t\t -b Path to simulator binary" + echo -e "\t\t -f Deck file name" + echo -e "\t\t -a Absolute tolerance in comparison" + echo -e "\t\t -t Relative tolerance in comparison" + echo -e "\t\t -c Path to comparison tool" + echo -e "\t\t -e Simulator binary to use" + echo -e "\tOptional options:" + echo -e "\t\t -d Unused, accepted for compatibility with other drivers" + exit 1 +fi + +OPTIND=1 +while getopts "i:r:b:f:a:t:c:d:e:" OPT +do + case "${OPT}" in + i) INPUT_DATA_PATH=${OPTARG} ;; + r) RESULT_PATH=${OPTARG} ;; + b) BINPATH=${OPTARG} ;; + f) FILENAME=${OPTARG} ;; + a) ABS_TOL=${OPTARG} ;; + t) REL_TOL=${OPTARG} ;; + c) COMPARE_ECL_COMMAND=${OPTARG} ;; + d) : ;; + e) EXE_NAME=${OPTARG} ;; + esac +done +shift $(($OPTIND-1)) +TEST_ARGS="$@" + +BASELINE_PATH=${RESULT_PATH}/baseline +REPLAY_PATH=${RESULT_PATH}/replay +TIMESTEP_FILE=${RESULT_PATH}/${FILENAME}.timesteps +BASELINE_LOG=${RESULT_PATH}/${FILENAME}.baseline.log +REPLAY_LOG=${RESULT_PATH}/${FILENAME}.replay.log +BASELINE_INFOSTEP=${BASELINE_PATH}/${FILENAME}.INFOSTEP + +resolve_simulator_binary() { + local candidate="${BINPATH}/${EXE_NAME}" + if [ -x "${candidate}" ]; then + printf '%s\n' "${candidate}" + return 0 + fi + + if [ "${EXE_NAME}" = "flow" ] && [ -x "${BINPATH}/flow_blackoil" ]; then + printf '%s\n' "${BINPATH}/flow_blackoil" + return 0 + fi + + echo "ERROR: Simulator binary not found: ${candidate}" >&2 + return 1 +} + +run_simulation() { + local output_path=$1 + local log_path=$2 + shift 2 + local simulator_binary + + simulator_binary=$(resolve_simulator_binary) || return 1 + + mkdir -p "${output_path}" + "${simulator_binary}" "${INPUT_DATA_PATH}/${FILENAME}" "$@" --output-dir="${output_path}" > "${log_path}" 2>&1 + local status=$? + if [ ${status} -ne 0 ]; then + cat "${log_path}" + return ${status} + fi + + return 0 +} + +extract_timesteps() { + local infostep_path=$1 + local log_path=$2 + local output_file=$3 + + python3 - "$infostep_path" "$log_path" "$output_file" <<'PY' +from pathlib import Path +import re +import sys + +infostep = Path(sys.argv[1]) +log_path = Path(sys.argv[2]) +output = Path(sys.argv[3]) + +if not infostep.exists(): + raise FileNotFoundError(f"INFOSTEP file not found: {infostep}") +if not log_path.exists(): + raise FileNotFoundError(f"Simulation log not found: {log_path}") + +lines = [line.strip() for line in infostep.read_text().splitlines() if line.strip()] +header_idx = next((i for i, line in enumerate(lines) if "Time(day)" in line and "Conv" in line), None) +if header_idx is None: + raise ValueError(f"Unable to locate INFOSTEP header in {infostep}") + +header = lines[header_idx].split() +time_idx = header.index("Time(day)") +conv_idx = header.index("Conv") + +accepted_times = [] +for row in lines[header_idx + 1:]: + cols = row.split() + if len(cols) <= max(time_idx, conv_idx): + continue + if cols[conv_idx] not in {"1", "1.0", "true", "True"}: + continue + accepted_times.append(cols[time_idx]) + +if not accepted_times: + raise ValueError(f"No accepted timesteps found in {infostep}") + +# INFOSTEP currently omits the last accepted endpoint of the full simulation, +# so append the total simulation time from the baseline log if needed. +final_time = None +for line in reversed(log_path.read_text().splitlines()): + match = re.search(r"day\s+[^/]+/(\S+)", line) + if match: + final_time = match.group(1) + break + +if final_time is None: + raise ValueError(f"Unable to determine final simulation time from {log_path}") + +if accepted_times[-1] != final_time: + accepted_times.append(final_time) + +output.write_text("\n".join(accepted_times) + "\n") +PY +} + +rm -rf "${RESULT_PATH}" +mkdir -p "${RESULT_PATH}" + +# Generate precise accepted substep end times from INFOSTEP. +run_simulation "${BASELINE_PATH}" "${BASELINE_LOG}" ${TEST_ARGS} --output-extra-convergence-info=steps +test $? -eq 0 || exit 1 + +extract_timesteps "${BASELINE_INFOSTEP}" "${BASELINE_LOG}" "${TIMESTEP_FILE}" +test $? -eq 0 || exit 1 + +#run_simulation "${REPLAY_PATH}" "${REPLAY_LOG}" ${TEST_ARGS_REPLAY} --output-extra-convergence-info=steps --time-step-control=hardcoded --time-step-control-file-name="${TIMESTEP_FILE}" +run_simulation "${REPLAY_PATH}" "${REPLAY_LOG}" --initial-time-step-in-days=11111111 --output-extra-convergence-info=steps --time-step-control=hardcoded --time-step-control-file-name="${TIMESTEP_FILE}" +test $? -eq 0 || exit 1 + +ecode=0 +echo "=== Executing comparison for EGRID, INIT, UNRST, UNSMRY and RFT files ===" +"${COMPARE_ECL_COMMAND}" "${BASELINE_PATH}/${FILENAME}" "${REPLAY_PATH}/${FILENAME}" "${ABS_TOL}" "${REL_TOL}" +if [ $? -ne 0 ] +then + ecode=1 + "${COMPARE_ECL_COMMAND}" -a "${BASELINE_PATH}/${FILENAME}" "${REPLAY_PATH}/${FILENAME}" "${ABS_TOL}" "${REL_TOL}" +fi + +exit ${ecode} \ No newline at end of file From 4cb6c5aedf22d604e5ff638fedf4b9573772d646 Mon Sep 17 00:00:00 2001 From: hnil Date: Tue, 5 May 2026 09:59:26 +0200 Subject: [PATCH 2/9] Tests on replay. The last to tests fails with the given tolerance 1e-20, but differs in pression of 1e6 due to one failer in a step. --- compareECLFiles.cmake | 5 ++- .../AdaptiveTimeStepping_impl.hpp | 28 ++++++++++++ .../timestepping/SimulatorReport.cpp | 2 +- .../timestepping/TimeStepControl.cpp | 31 ++++++++++--- regressionTests.cmake | 45 ++++++++++++++----- tests/run-timestep-replay-regressionTest.sh | 15 ++++--- 6 files changed, 101 insertions(+), 25 deletions(-) diff --git a/compareECLFiles.cmake b/compareECLFiles.cmake index b529c0ab7ed..5a9508a135f 100644 --- a/compareECLFiles.cmake +++ b/compareECLFiles.cmake @@ -60,7 +60,7 @@ endfunction() # - This test class compares output from a simulation to reference files. function(add_test_compareECLFiles) set(oneValueArgs CASENAME FILENAME SIMULATOR ABS_TOL REL_TOL DIR DIR_PREFIX PREFIX RESTART_STEP RESTART_SCHED) - set(multiValueArgs TEST_ARGS) + set(multiValueArgs TEST_ARGS TEST_ARGS_REPLAY) cmake_parse_arguments(PARAM "$" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) if(NOT PARAM_DIR) set(PARAM_DIR ${PARAM_CASENAME}) @@ -84,6 +84,9 @@ function(add_test_compareECLFiles) if(PARAM_RESTART_SCHED STREQUAL "false" OR PARAM_RESTART_SCHED STREQUAL "true") list(APPEND DRIVER_ARGS -h ${PARAM_RESTART_SCHED}) endif() + foreach(arg IN LISTS PARAM_TEST_ARGS_REPLAY) + list(APPEND DRIVER_ARGS -y ${arg}) + endforeach() opm_add_test(${PARAM_PREFIX}_${PARAM_SIMULATOR}+${PARAM_FILENAME} NO_COMPILE EXE_NAME ${PARAM_SIMULATOR} DRIVER_ARGS ${DRIVER_ARGS} diff --git a/opm/simulators/timestepping/AdaptiveTimeStepping_impl.hpp b/opm/simulators/timestepping/AdaptiveTimeStepping_impl.hpp index f0507aaf80e..64ab0b3c4e7 100644 --- a/opm/simulators/timestepping/AdaptiveTimeStepping_impl.hpp +++ b/opm/simulators/timestepping/AdaptiveTimeStepping_impl.hpp @@ -522,6 +522,34 @@ maybeModifySuggestedTimeStepAtBeginningOfReportStep_(const double original_time_ this->adaptive_time_stepping_.maybeModifySuggestedTimeStepAtBeginningOfReportStep_( original_time_step, this->is_event_ ); + + if (this->adaptive_time_stepping_.time_step_control_type_ != TimeStepControlType::HardCodedTimeStep) { + return; + } + + struct ZeroRelativeChange final : RelativeChangeInterface { + double relativeChange() const override { return 0.0; } + } zero_relative_change; + + const auto* hardcoded_control = static_cast( + this->adaptive_time_stepping_.time_step_control_.get()); + AdaptiveSimulatorTimer report_step_timer{ + this->simulator_timer_.startDateTime(), + original_time_step, + this->simulator_timer_.simulationTimeElapsed(), + original_time_step, + this->simulator_timer_.reportStepNum(), + maxTimeStep_() + }; + + const double hardcoded_initial_step = hardcoded_control->computeTimeStepSize( + original_time_step, + 0, + zero_relative_change, + report_step_timer); + if (std::isfinite(hardcoded_initial_step) && hardcoded_initial_step > 0.0) { + this->adaptive_time_stepping_.setSuggestedNextStep(hardcoded_initial_step); + } } // The maybeUpdateTuning_() lambda callback is defined in SimulatorFullyImplicitBlackoil::runStep() diff --git a/opm/simulators/timestepping/SimulatorReport.cpp b/opm/simulators/timestepping/SimulatorReport.cpp index 64ce8667b52..4dce35e4fd9 100644 --- a/opm/simulators/timestepping/SimulatorReport.cpp +++ b/opm/simulators/timestepping/SimulatorReport.cpp @@ -395,7 +395,7 @@ namespace Opm os << " Time(day) TStep(day) Assembly LSetup LSolve LocSol Update Output WellIt Lins NewtIt LinIt Conv\n"; for (std::size_t i = 0; i < this->stepreports.size(); ++i) { const SimulatorReportSingle& sr = this->stepreports[i]; - os.precision(10); + os.precision(20); os << std::defaultfloat; os << std::setw(11) << unit::convert::to(sr.global_time, unit::day) << " "; os << std::setw(11) << unit::convert::to(sr.timestep_length, unit::day) << " "; diff --git a/opm/simulators/timestepping/TimeStepControl.cpp b/opm/simulators/timestepping/TimeStepControl.cpp index c5dbf4fdf10..ce507a0b60a 100644 --- a/opm/simulators/timestepping/TimeStepControl.cpp +++ b/opm/simulators/timestepping/TimeStepControl.cpp @@ -41,6 +41,14 @@ namespace Opm { + namespace { + double hardcodedTimeTolerance(const double a, const double b, const double c = 0.0) + { + const double scale = std::max({1.0, std::abs(a), std::abs(b), std::abs(c)}); + return 64.0 * std::numeric_limits::epsilon() * scale; + } + } + //////////////////////////////////////////////////////// // // InterationCountTimeStepControl Implementation @@ -128,12 +136,14 @@ namespace Opm std::string::size_type sz; std::string line; while ( std::getline(infile, line)) { - if( line[0] != '-') { // ignore lines starting with '-' + if (!line.empty() && line[0] != '-') { // ignore lines starting with '-' const double time = std::stod(line,&sz); // read the first number i.e. the actual substep time subStepTime_.push_back( time * unit::day ); } } + + std::sort(subStepTime_.begin(), subStepTime_.end()); } HardcodedTimeStepControl HardcodedTimeStepControl::serializationTestObject() @@ -145,14 +155,25 @@ namespace Opm } double - HardcodedTimeStepControl::computeTimeStepSize(const double /*dt */, + HardcodedTimeStepControl::computeTimeStepSize(const double dt, const int /*iterations */, const RelativeChangeInterface& /* relativeChange */, const AdaptiveSimulatorTimer& substepTimer) const { - auto nextTime - = std::upper_bound(subStepTime_.begin(), subStepTime_.end(), substepTimer.simulationTimeElapsed()); - return (*nextTime - substepTimer.simulationTimeElapsed()); + const double currentTime = substepTimer.simulationTimeElapsed(); + const double remaining = substepTimer.totalTime() - currentTime; + const double tol = hardcodedTimeTolerance(currentTime, substepTimer.totalTime(), dt); + + auto nextTime = std::upper_bound(subStepTime_.begin(), subStepTime_.end(), currentTime + tol); + while (nextTime != subStepTime_.end() && (*nextTime - currentTime) <= tol) { + ++nextTime; + } + + if (nextTime == subStepTime_.end()) { + return remaining > tol ? remaining : std::max(dt, tol); + } + + return std::max(*nextTime - currentTime, tol); } bool HardcodedTimeStepControl::operator==(const HardcodedTimeStepControl& ctrl) const diff --git a/regressionTests.cmake b/regressionTests.cmake index b08754f6c11..016fb443d81 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -5,7 +5,8 @@ opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-regressionTest.sh "") set(abs_tol 2e-2) set(rel_tol 1e-5) set(coarse_rel_tol 1e-2) -set(timestep_replay_rel_tol 2e-4) +set(timestep_replay_abs_tol 2e-20) +set(timestep_replay_rel_tol 2e-20) add_test_compareECLFiles(CASENAME spe1flowexp FILENAME SPE1CASE2 @@ -57,28 +58,50 @@ opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-timestep-replay-regressionTe add_test_compareECLFiles(CASENAME spe1_timestep_replay FILENAME SPE1CASE1 - SIMULATOR flow + SIMULATOR flow_blackoil PREFIX compareTimestepReplay - ABS_TOL ${abs_tol} + ABS_TOL ${timestep_replay_abs_tol} REL_TOL ${timestep_replay_rel_tol} - DIR spe1) + DIR spe1 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) add_test_compareECLFiles(CASENAME spe9_timestep_replay FILENAME SPE9_CP_SHORT - SIMULATOR flow + SIMULATOR flow_blackoil PREFIX compareTimestepReplay - ABS_TOL ${abs_tol} + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR spe9 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME spe9_timestep_replay_all + FILENAME SPE9 + SIMULATOR flow_blackoil + PREFIX compareTimestepReplay + ABS_TOL ${timestep_replay_abs_tol} REL_TOL ${timestep_replay_rel_tol} - DIR spe9) + DIR spe9 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) -add_test_compareECLFiles(CASENAME base_model +add_test_compareECLFiles(CASENAME base_model_large_timestep_replay FILENAME 0_BASE_MODEL6 - SIMULATOR flow + SIMULATOR flow_oilwater PREFIX compareTimestepReplay - ABS_TOL ${abs_tol} + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model6 + TEST_ARGS --full-time-step-initially=false --solver-max-time-step-in-days=10 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME base_model_short_timestep_replay + FILENAME 0_BASE_MODEL6 + SIMULATOR flow_oilwater + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} REL_TOL ${timestep_replay_rel_tol} DIR model6 - TEST_ARGS --full-time-step-initially=true) + TEST_ARGS --full-time-step-initially=true + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-regressionTest.sh "") diff --git a/tests/run-timestep-replay-regressionTest.sh b/tests/run-timestep-replay-regressionTest.sh index b1d46c5ddd3..350b2ad24f4 100755 --- a/tests/run-timestep-replay-regressionTest.sh +++ b/tests/run-timestep-replay-regressionTest.sh @@ -24,7 +24,8 @@ then fi OPTIND=1 -while getopts "i:r:b:f:a:t:c:d:e:" OPT +declare -a TEST_ARGS_REPLAY=() +while getopts "i:r:b:f:a:t:c:d:e:y:" OPT do case "${OPT}" in i) INPUT_DATA_PATH=${OPTARG} ;; @@ -36,10 +37,11 @@ do c) COMPARE_ECL_COMMAND=${OPTARG} ;; d) : ;; e) EXE_NAME=${OPTARG} ;; + y) TEST_ARGS_REPLAY+=("${OPTARG}") ;; esac done shift $(($OPTIND-1)) -TEST_ARGS="$@" +declare -a TEST_ARGS=("$@") BASELINE_PATH=${RESULT_PATH}/baseline REPLAY_PATH=${RESULT_PATH}/replay @@ -73,7 +75,7 @@ run_simulation() { simulator_binary=$(resolve_simulator_binary) || return 1 mkdir -p "${output_path}" - "${simulator_binary}" "${INPUT_DATA_PATH}/${FILENAME}" "$@" --output-dir="${output_path}" > "${log_path}" 2>&1 + "${simulator_binary}" "$@" --output-dir="${output_path}" "${INPUT_DATA_PATH}/${FILENAME}" > "${log_path}" 2>&1 local status=$? if [ ${status} -ne 0 ]; then cat "${log_path}" @@ -129,7 +131,7 @@ final_time = None for line in reversed(log_path.read_text().splitlines()): match = re.search(r"day\s+[^/]+/(\S+)", line) if match: - final_time = match.group(1) + final_time = match.group(1).rstrip(",;") break if final_time is None: @@ -146,14 +148,13 @@ rm -rf "${RESULT_PATH}" mkdir -p "${RESULT_PATH}" # Generate precise accepted substep end times from INFOSTEP. -run_simulation "${BASELINE_PATH}" "${BASELINE_LOG}" ${TEST_ARGS} --output-extra-convergence-info=steps +run_simulation "${BASELINE_PATH}" "${BASELINE_LOG}" ${TEST_ARGS[@]+"${TEST_ARGS[@]}"} --output-extra-convergence-info=steps test $? -eq 0 || exit 1 extract_timesteps "${BASELINE_INFOSTEP}" "${BASELINE_LOG}" "${TIMESTEP_FILE}" test $? -eq 0 || exit 1 -#run_simulation "${REPLAY_PATH}" "${REPLAY_LOG}" ${TEST_ARGS_REPLAY} --output-extra-convergence-info=steps --time-step-control=hardcoded --time-step-control-file-name="${TIMESTEP_FILE}" -run_simulation "${REPLAY_PATH}" "${REPLAY_LOG}" --initial-time-step-in-days=11111111 --output-extra-convergence-info=steps --time-step-control=hardcoded --time-step-control-file-name="${TIMESTEP_FILE}" +run_simulation "${REPLAY_PATH}" "${REPLAY_LOG}" ${TEST_ARGS[@]+"${TEST_ARGS[@]}"} ${TEST_ARGS_REPLAY[@]+"${TEST_ARGS_REPLAY[@]}"} --output-extra-convergence-info=steps --time-step-control=hardcoded --time-step-control-file-name="${TIMESTEP_FILE}" test $? -eq 0 || exit 1 ecode=0 From 2b356c0742c5b864bb13315bda8d10619a6f5715 Mon Sep 17 00:00:00 2001 From: hnil Date: Mon, 27 Apr 2026 11:59:37 +0200 Subject: [PATCH 3/9] moving updateFailed to problem --- opm/simulators/flow/BlackoilModel_impl.hpp | 2 +- opm/simulators/flow/FlowProblem.hpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/opm/simulators/flow/BlackoilModel_impl.hpp b/opm/simulators/flow/BlackoilModel_impl.hpp index bf0945a09a0..2e72b367d57 100644 --- a/opm/simulators/flow/BlackoilModel_impl.hpp +++ b/opm/simulators/flow/BlackoilModel_impl.hpp @@ -122,7 +122,7 @@ prepareStep(const SimulatorTimerInterface& timer) "- the previous step succeeded on some ranks but failed on others."); } if (lastStepFailed) { - simulator_.model().updateFailed(); + simulator_.problem().updateFailed(); } else { simulator_.model().advanceTimeLevel(); diff --git a/opm/simulators/flow/FlowProblem.hpp b/opm/simulators/flow/FlowProblem.hpp index 73ff334ffd9..5192c4f07e4 100644 --- a/opm/simulators/flow/FlowProblem.hpp +++ b/opm/simulators/flow/FlowProblem.hpp @@ -392,6 +392,11 @@ class FlowProblem : public GetPropType } + void updateFailed() + { + this->model().updateFailed(); + } + /*! * \brief Called by the simulator before each Newton-Raphson iteration. */ From f089db5a4bbe31fc4ebdb91c52e1a2c2a6850fe6 Mon Sep 17 00:00:00 2001 From: hnil Date: Mon, 27 Apr 2026 12:09:52 +0200 Subject: [PATCH 4/9] also moved advanceTimelevel --- opm/simulators/flow/BlackoilModel_impl.hpp | 2 +- opm/simulators/flow/FlowProblem.hpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/opm/simulators/flow/BlackoilModel_impl.hpp b/opm/simulators/flow/BlackoilModel_impl.hpp index 2e72b367d57..dd0c5680ca4 100644 --- a/opm/simulators/flow/BlackoilModel_impl.hpp +++ b/opm/simulators/flow/BlackoilModel_impl.hpp @@ -125,7 +125,7 @@ prepareStep(const SimulatorTimerInterface& timer) simulator_.problem().updateFailed(); } else { - simulator_.model().advanceTimeLevel(); + simulator_.problem().advanceTimeLevel(); } // Set the timestep size and episode index for the model explicitly. diff --git a/opm/simulators/flow/FlowProblem.hpp b/opm/simulators/flow/FlowProblem.hpp index 5192c4f07e4..2850a0a07f7 100644 --- a/opm/simulators/flow/FlowProblem.hpp +++ b/opm/simulators/flow/FlowProblem.hpp @@ -397,6 +397,11 @@ class FlowProblem : public GetPropType this->model().updateFailed(); } + void advanceTimeLevel() + { + this->model().advanceTimeLevel(); + } + /*! * \brief Called by the simulator before each Newton-Raphson iteration. */ From d3327c5e8f7645bca39f9518e236bbb39213c132 Mon Sep 17 00:00:00 2001 From: hnil Date: Mon, 27 Apr 2026 12:34:16 +0200 Subject: [PATCH 5/9] added start on proper update functions --- opm/simulators/flow/FlowProblem.hpp | 2 ++ opm/simulators/wells/BlackoilWellModel.hpp | 4 ++++ .../wells/BlackoilWellModelGeneric.cpp | 3 +++ .../wells/BlackoilWellModelGeneric.hpp | 21 +++++++++++++++++++ .../wells/BlackoilWellModel_impl.hpp | 16 ++++++++++++++ 5 files changed, 46 insertions(+) diff --git a/opm/simulators/flow/FlowProblem.hpp b/opm/simulators/flow/FlowProblem.hpp index 2850a0a07f7..566ac17e0dc 100644 --- a/opm/simulators/flow/FlowProblem.hpp +++ b/opm/simulators/flow/FlowProblem.hpp @@ -394,12 +394,14 @@ class FlowProblem : public GetPropType void updateFailed() { + wellModel_.updateFailed(); this->model().updateFailed(); } void advanceTimeLevel() { this->model().advanceTimeLevel(); + wellModel_.advanceTimeLevel(); } /*! diff --git a/opm/simulators/wells/BlackoilWellModel.hpp b/opm/simulators/wells/BlackoilWellModel.hpp index f4792403ef7..481f9b4ec51 100644 --- a/opm/simulators/wells/BlackoilWellModel.hpp +++ b/opm/simulators/wells/BlackoilWellModel.hpp @@ -154,6 +154,10 @@ template class WellContributions; void beginTimeStep(); + void updateFailed(); + + void advanceTimeLevel(); + void beginIteration() { OPM_TIMEBLOCK(beginIteration); diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.cpp b/opm/simulators/wells/BlackoilWellModelGeneric.cpp index e4969aad544..5d7d2ccba56 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.cpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.cpp @@ -110,6 +110,7 @@ BlackoilWellModelGeneric(Schedule& schedule, , guideRate_(schedule) , active_wgstate_(pu) , last_valid_wgstate_(pu) + , prev_timestep_wgstate_(pu) , nupcol_wgstate_(pu) , group_state_helper_(this->wellState(), this->groupState(), @@ -1964,10 +1965,12 @@ operator==(const BlackoilWellModelGeneric& rhs) const && this->last_run_wellpi_ == rhs.last_run_wellpi_ && this->local_shut_wells_ == rhs.local_shut_wells_ && this->closed_this_step_ == rhs.closed_this_step_ + && this->prev_timestep_closed_this_step_ == rhs.prev_timestep_closed_this_step_ && this->genNetwork_ == rhs.genNetwork_ && this->prev_inj_multipliers_ == rhs.prev_inj_multipliers_ && this->active_wgstate_ == rhs.active_wgstate_ && this->last_valid_wgstate_ == rhs.last_valid_wgstate_ + && this->prev_timestep_wgstate_ == rhs.prev_timestep_wgstate_ && this->nupcol_wgstate_ == rhs.nupcol_wgstate_ && this->switched_prod_groups_ == rhs.switched_prod_groups_ && this->switched_inj_groups_ == rhs.switched_inj_groups_ diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index b8a0e08c195..a111676ac20 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -212,6 +212,23 @@ class BlackoilWellModelGeneric data::GroupAndNetworkValues groupAndNetworkData(const int reportStepIdx) const; + // Snapshot dynamic state at start of timestep for failure recovery. + void advanceTimeLevel() + { + this->prev_timestep_wgstate_ = this->active_wgstate_; + this->prev_timestep_closed_this_step_ = this->closed_this_step_; + this->genNetwork_.commitState(); + } + + // Restore dynamic state captured at the start of the failing timestep. + void updateFailed() + { + this->active_wgstate_ = this->prev_timestep_wgstate_; + this->closed_this_step_ = this->prev_timestep_closed_this_step_; + this->genNetwork_.resetState(); + this->group_state_helper_.updateState(this->wellState(), this->groupState()); + } + /// Shut down any single well /// Returns true if the well was actually found and shut. bool forceShutWellByName(const std::string& wellname, @@ -254,11 +271,13 @@ class BlackoilWellModelGeneric serializer(last_run_wellpi_); serializer(local_shut_wells_); serializer(closed_this_step_); + serializer(prev_timestep_closed_this_step_); serializer(guideRate_); serializer(genNetwork_); serializer(prev_inj_multipliers_); serializer(active_wgstate_); serializer(last_valid_wgstate_); + serializer(prev_timestep_wgstate_); serializer(nupcol_wgstate_); serializer(switched_prod_groups_); serializer(switched_inj_groups_); @@ -550,6 +569,7 @@ class BlackoilWellModelGeneric std::vector pvt_region_idx_; mutable std::unordered_set closed_this_step_; + std::unordered_set prev_timestep_closed_this_step_; GuideRate guideRate_; std::unique_ptr> vfp_properties_{}; @@ -568,6 +588,7 @@ class BlackoilWellModelGeneric */ WGState active_wgstate_; WGState last_valid_wgstate_; + WGState prev_timestep_wgstate_; WGState nupcol_wgstate_; GroupStateHelperType group_state_helper_; WellGroupEvents report_step_start_events_; //!< Well group events at start of report step diff --git a/opm/simulators/wells/BlackoilWellModel_impl.hpp b/opm/simulators/wells/BlackoilWellModel_impl.hpp index 46dfdd352cf..bfc77f9cc78 100644 --- a/opm/simulators/wells/BlackoilWellModel_impl.hpp +++ b/opm/simulators/wells/BlackoilWellModel_impl.hpp @@ -569,6 +569,22 @@ namespace Opm { } + template + void + BlackoilWellModel:: + updateFailed() + { + this->BlackoilWellModelGeneric::updateFailed(); + } + + template + void + BlackoilWellModel:: + advanceTimeLevel() + { + this->BlackoilWellModelGeneric::advanceTimeLevel(); + } + #ifdef RESERVOIR_COUPLING_ENABLED // Automatically manages the lifecycle of the DeferredLogger pointer // in the reservoir coupling logger. Ensures the logger is properly From 40dbaf5dd79eacae6ca1d021febc470281c9d0cf Mon Sep 17 00:00:00 2001 From: hnil Date: Mon, 27 Apr 2026 12:36:39 +0200 Subject: [PATCH 6/9] made dummy updates for compositional wells --- flowexperimental/comp/wells/CompWellModel.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/flowexperimental/comp/wells/CompWellModel.hpp b/flowexperimental/comp/wells/CompWellModel.hpp index 52daa76fb5f..dabec5a4866 100644 --- a/flowexperimental/comp/wells/CompWellModel.hpp +++ b/flowexperimental/comp/wells/CompWellModel.hpp @@ -96,6 +96,11 @@ class CompWellModel : WellConnectionAuxiliaryModule Date: Tue, 5 May 2026 14:25:01 +0200 Subject: [PATCH 7/9] Near perfect replay. Still some small issues. --- opm/models/utils/simulator.hh | 4 +- opm/simulators/flow/BlackoilModel_impl.hpp | 1 + opm/simulators/flow/FlowProblem.hpp | 30 ++++++ opm/simulators/flow/FlowProblemBlackoil.hpp | 14 +++ opm/simulators/linalg/ISTLSolver.hpp | 11 ++- opm/simulators/linalg/ISTLSolverTPSA.hpp | 10 +- .../linalg/gpuistl/ISTLSolverGPUISTL.hpp | 15 ++- .../wells/BlackoilWellModelGeneric.cpp | 2 + .../wells/BlackoilWellModelGeneric.hpp | 12 +++ regressionTests.cmake | 98 ++++++++++++++++--- 10 files changed, 179 insertions(+), 18 deletions(-) diff --git a/opm/models/utils/simulator.hh b/opm/models/utils/simulator.hh index 587cb7c2e87..dd6ba01bb78 100644 --- a/opm/models/utils/simulator.hh +++ b/opm/models/utils/simulator.hh @@ -395,7 +395,9 @@ public: * \param value The new value for the time step size \f$\mathrm{[s]}\f$ */ void setTimeStepSize(Scalar value) - { timeStepSize_ = float(value); } + { + timeStepSize_ = float(value); + } /*! * \brief Set the current time step index to a given value. diff --git a/opm/simulators/flow/BlackoilModel_impl.hpp b/opm/simulators/flow/BlackoilModel_impl.hpp index dd0c5680ca4..85d524e1257 100644 --- a/opm/simulators/flow/BlackoilModel_impl.hpp +++ b/opm/simulators/flow/BlackoilModel_impl.hpp @@ -123,6 +123,7 @@ prepareStep(const SimulatorTimerInterface& timer) } if (lastStepFailed) { simulator_.problem().updateFailed(); + simulator_.model().newtonMethod().eraseMatrix(); } else { simulator_.problem().advanceTimeLevel(); diff --git a/opm/simulators/flow/FlowProblem.hpp b/opm/simulators/flow/FlowProblem.hpp index 566ac17e0dc..2cae05cc2db 100644 --- a/opm/simulators/flow/FlowProblem.hpp +++ b/opm/simulators/flow/FlowProblem.hpp @@ -367,6 +367,8 @@ class FlowProblem : public GetPropType const int episodeIdx = this->episodeIndex(); const int timeStepSize = this->simulator().timeStepSize(); + this->captureBeginTimeStepState_(); + this->beginTimeStep_(enableExperiments, episodeIdx, this->simulator().timeStepIndex(), @@ -394,6 +396,7 @@ class FlowProblem : public GetPropType void updateFailed() { + this->restoreBeginTimeStepState_(); wellModel_.updateFailed(); this->model().updateFailed(); } @@ -1283,6 +1286,26 @@ class FlowProblem : public GetPropType { return *static_cast(this); } protected: + virtual void captureBeginTimeStepState_() + { + prev_timestep_first_step_ = first_step_; + prev_timestep_max_polymer_adsorption_ = this->polymer_.maxAdsorption; + prev_timestep_max_oil_saturation_ = this->maxOilSaturation_; + prev_timestep_max_water_saturation_ = this->maxWaterSaturation_; + prev_timestep_min_ref_pressure_ = this->minRefPressure_; + prev_timestep_rock_comp_trans_mult_val_ = this->rockCompTransMultVal_; + } + + virtual void restoreBeginTimeStepState_() + { + first_step_ = prev_timestep_first_step_; + this->polymer_.maxAdsorption = prev_timestep_max_polymer_adsorption_; + this->maxOilSaturation_ = prev_timestep_max_oil_saturation_; + this->maxWaterSaturation_ = prev_timestep_max_water_saturation_; + this->minRefPressure_ = prev_timestep_min_ref_pressure_; + this->rockCompTransMultVal_ = prev_timestep_rock_comp_trans_mult_val_; + } + template void updateProperty_(const std::string& failureMsg, UpdateFunc func) @@ -1869,6 +1892,13 @@ class FlowProblem : public GetPropType BCData bcindex_; bool nonTrivialBoundaryConditions_ = false; bool first_step_ = true; + bool prev_timestep_first_step_ = true; + + std::vector prev_timestep_max_polymer_adsorption_; + std::vector prev_timestep_max_oil_saturation_; + std::vector prev_timestep_max_water_saturation_; + std::vector prev_timestep_min_ref_pressure_; + std::vector prev_timestep_rock_comp_trans_mult_val_; /// Whether or not the current episode will end at the end of the /// current time step. diff --git a/opm/simulators/flow/FlowProblemBlackoil.hpp b/opm/simulators/flow/FlowProblemBlackoil.hpp index e240505fbcc..8d583547432 100644 --- a/opm/simulators/flow/FlowProblemBlackoil.hpp +++ b/opm/simulators/flow/FlowProblemBlackoil.hpp @@ -188,6 +188,7 @@ class FlowProblemBlackoil : public FlowProblem : FlowProblemType(simulator) , thresholdPressures_(simulator) , mixControls_(simulator.vanguard().schedule()) + , prev_timestep_mixControls_(simulator.vanguard().schedule()) , actionHandler_(simulator.vanguard().eclState(), simulator.vanguard().schedule(), simulator.vanguard().actionState(), @@ -1173,6 +1174,18 @@ class FlowProblemBlackoil : public FlowProblem } protected: + void captureBeginTimeStepState_() override + { + FlowProblemType::captureBeginTimeStepState_(); + prev_timestep_mixControls_ = mixControls_; + } + + void restoreBeginTimeStepState_() override + { + FlowProblemType::restoreBeginTimeStepState_(); + mixControls_ = prev_timestep_mixControls_; + } + void updateExplicitQuantities_(int episodeIdx, int timeStepSize, const bool first_step_after_restart) override { this->updateExplicitQuantities_(first_step_after_restart); @@ -1734,6 +1747,7 @@ class FlowProblemBlackoil : public FlowProblem std::unique_ptr damarisWriter_; #endif MixingRateControls mixControls_; + MixingRateControls prev_timestep_mixControls_; ActionHandler actionHandler_; diff --git a/opm/simulators/linalg/ISTLSolver.hpp b/opm/simulators/linalg/ISTLSolver.hpp index 8ecb0e4be1f..b9eac998108 100644 --- a/opm/simulators/linalg/ISTLSolver.hpp +++ b/opm/simulators/linalg/ISTLSolver.hpp @@ -313,9 +313,18 @@ std::unique_ptr blockJacobiAdjacency(const Grid& grid, element_chunks_ = std::make_unique(simulator_.vanguard().gridView(), Dune::Partitions::all, ThreadManager::maxThreads()); } - // nothing to clean here void eraseMatrix() override { + matrix_ = nullptr; + rhs_ = nullptr; + iterations_ = 0; + solveCount_ = 0; + + for (auto& solverInfo : flexibleSolver_) { + solverInfo.pre_ = nullptr; + solverInfo.solver_.reset(); + solverInfo.op_.reset(); + } } void setActiveSolver(const int num) override diff --git a/opm/simulators/linalg/ISTLSolverTPSA.hpp b/opm/simulators/linalg/ISTLSolverTPSA.hpp index e3e86094a0b..531781168a8 100644 --- a/opm/simulators/linalg/ISTLSolverTPSA.hpp +++ b/opm/simulators/linalg/ISTLSolverTPSA.hpp @@ -274,7 +274,15 @@ class ISTLSolverTPSA : public AbstractISTLSolver()) , guideRate_(schedule) + , prev_timestep_guideRate_(schedule) , active_wgstate_(pu) , last_valid_wgstate_(pu) , prev_timestep_wgstate_(pu) , nupcol_wgstate_(pu) + , prev_timestep_nupcol_wgstate_(pu) , group_state_helper_(this->wellState(), this->groupState(), this->schedule(), diff --git a/opm/simulators/wells/BlackoilWellModelGeneric.hpp b/opm/simulators/wells/BlackoilWellModelGeneric.hpp index a111676ac20..c37b24bfbe1 100644 --- a/opm/simulators/wells/BlackoilWellModelGeneric.hpp +++ b/opm/simulators/wells/BlackoilWellModelGeneric.hpp @@ -216,7 +216,11 @@ class BlackoilWellModelGeneric void advanceTimeLevel() { this->prev_timestep_wgstate_ = this->active_wgstate_; + this->prev_timestep_nupcol_wgstate_ = this->nupcol_wgstate_; this->prev_timestep_closed_this_step_ = this->closed_this_step_; + this->prev_timestep_guideRate_ = this->guideRate_; + this->prev_timestep_well_open_times_ = this->well_open_times_; + this->prev_timestep_well_close_times_ = this->well_close_times_; this->genNetwork_.commitState(); } @@ -224,7 +228,11 @@ class BlackoilWellModelGeneric void updateFailed() { this->active_wgstate_ = this->prev_timestep_wgstate_; + this->nupcol_wgstate_ = this->prev_timestep_nupcol_wgstate_; this->closed_this_step_ = this->prev_timestep_closed_this_step_; + this->guideRate_ = this->prev_timestep_guideRate_; + this->well_open_times_ = this->prev_timestep_well_open_times_; + this->well_close_times_ = this->prev_timestep_well_close_times_; this->genNetwork_.resetState(); this->group_state_helper_.updateState(this->wellState(), this->groupState()); } @@ -549,9 +557,11 @@ class BlackoilWellModelGeneric // Times at which wells were opened (for WCYCLE) std::map well_open_times_; + std::map prev_timestep_well_open_times_; // Times at which wells were shut (for WCYCLE) std::map well_close_times_; + std::map prev_timestep_well_close_times_; std::vector conn_idx_map_{}; std::function not_on_process_{}; @@ -572,6 +582,7 @@ class BlackoilWellModelGeneric std::unordered_set prev_timestep_closed_this_step_; GuideRate guideRate_; + GuideRate prev_timestep_guideRate_; std::unique_ptr> vfp_properties_{}; // previous injection multiplier, it is used in the injection multiplier calculation for WINJMULT keyword @@ -590,6 +601,7 @@ class BlackoilWellModelGeneric WGState last_valid_wgstate_; WGState prev_timestep_wgstate_; WGState nupcol_wgstate_; + WGState prev_timestep_nupcol_wgstate_; GroupStateHelperType group_state_helper_; WellGroupEvents report_step_start_events_; //!< Well group events at start of report step diff --git a/regressionTests.cmake b/regressionTests.cmake index 016fb443d81..5d500672bf9 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -5,8 +5,8 @@ opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-regressionTest.sh "") set(abs_tol 2e-2) set(rel_tol 1e-5) set(coarse_rel_tol 1e-2) -set(timestep_replay_abs_tol 2e-20) -set(timestep_replay_rel_tol 2e-20) +set(timestep_replay_abs_tol 2e-14) +set(timestep_replay_rel_tol 2e-14) add_test_compareECLFiles(CASENAME spe1flowexp FILENAME SPE1CASE2 @@ -74,34 +74,108 @@ add_test_compareECLFiles(CASENAME spe9_timestep_replay DIR spe9 TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) -add_test_compareECLFiles(CASENAME spe9_timestep_replay_all - FILENAME SPE9 - SIMULATOR flow_blackoil +# add_test_compareECLFiles(CASENAME spe9_timestep_replay_all +# FILENAME SPE9 +# SIMULATOR flow_blackoil +# PREFIX compareTimestepReplay +# ABS_TOL ${timestep_replay_abs_tol} +# REL_TOL ${timestep_replay_rel_tol} +# DIR spe9 +# TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME base_model_small_timestep_replay + FILENAME 0_BASE_MODEL6 + SIMULATOR flow_oilwater PREFIX compareTimestepReplay ABS_TOL ${timestep_replay_abs_tol} REL_TOL ${timestep_replay_rel_tol} - DIR spe9 + DIR model6 + TEST_ARGS --full-time-step-initially=false --solver-max-time-step-in-days=10 TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) add_test_compareECLFiles(CASENAME base_model_large_timestep_replay FILENAME 0_BASE_MODEL6 SIMULATOR flow_oilwater - PREFIX compareTimestepReplay + PREFIX compareTimestepReplayFail ABS_TOL ${timestep_replay_abs_tol} REL_TOL ${timestep_replay_rel_tol} DIR model6 - TEST_ARGS --full-time-step-initially=false --solver-max-time-step-in-days=10 - TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) -add_test_compareECLFiles(CASENAME base_model_short_timestep_replay - FILENAME 0_BASE_MODEL6 +add_test_compareECLFiles(CASENAME msw_model6_large_timestep_replay + FILENAME 1_MSW_MODEL6 SIMULATOR flow_oilwater PREFIX compareTimestepReplayFail ABS_TOL ${timestep_replay_abs_tol} REL_TOL ${timestep_replay_rel_tol} DIR model6 - TEST_ARGS --full-time-step-initially=true + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME aq_model6_large_timestep_replay + FILENAME 0A_AQUCT_MODEL6 + SIMULATOR flow_oilwater + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model6 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME model5_large_timestep_replay + #FILENAME 0_BASE_MODEL5 + #FILENAME 5_NETWORK_MODEL5_MSW + FILENAME 4_GLIFT_MODEL5 + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model5 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME grupcntl_large_timestep_replay + FILENAME GRUPCNTL-36 + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR grupcntl + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME model2_fail_large_timestep_replay + FILENAME 9_2A_DEPL_GCONPROD_1L_MSW + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model2 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME model2_large_timestep_replay + FILENAME 0_BASE_MODEL2 + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model2 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME model2_large_timestep_replay + #FILENAME 7_HYSTERESIS_MODEL2 #ok + #FILENAME 9_4E_WINJ_GINJ_GUIDERATE_MSW #ok + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model2 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-regressionTest.sh "") From f8cc7d379125cd047a76300acee9bdc17cf9c185 Mon Sep 17 00:00:00 2001 From: hnil Date: Thu, 7 May 2026 09:54:16 +0200 Subject: [PATCH 8/9] This make all replay work on model2 models exept small millisecond difference in inthead --- opm/models/blackoil/blackoilnewtonmethod.hpp | 6 ++++++ opm/simulators/flow/BlackoilModel_impl.hpp | 1 + regressionTests.cmake | 22 +++++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/opm/models/blackoil/blackoilnewtonmethod.hpp b/opm/models/blackoil/blackoilnewtonmethod.hpp index fd7ba75d6a1..6e8a1373cd7 100644 --- a/opm/models/blackoil/blackoilnewtonmethod.hpp +++ b/opm/models/blackoil/blackoilnewtonmethod.hpp @@ -90,6 +90,12 @@ class BlackOilNewtonMethod : public GetPropTypemodel().numTotalDof(), false); } + void resetPrimaryVariableSwitches() + { + numPriVarsSwitched_ = 0; + std::fill(wasSwitched_.begin(), wasSwitched_.end(), false); + } + /*! * \brief Register all run-time parameters for the blackoil newton method. */ diff --git a/opm/simulators/flow/BlackoilModel_impl.hpp b/opm/simulators/flow/BlackoilModel_impl.hpp index 85d524e1257..50073042101 100644 --- a/opm/simulators/flow/BlackoilModel_impl.hpp +++ b/opm/simulators/flow/BlackoilModel_impl.hpp @@ -143,6 +143,7 @@ prepareStep(const SimulatorTimerInterface& timer) unsigned numDof = simulator_.model().numGridDof(); wasSwitched_.resize(numDof); std::fill(wasSwitched_.begin(), wasSwitched_.end(), false); + simulator_.model().newtonMethod().resetPrimaryVariableSwitches(); if (param_.update_equations_scaling_) { OpmLog::error("Equation scaling not supported"); diff --git a/regressionTests.cmake b/regressionTests.cmake index 5d500672bf9..6dbb36dc1fd 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -167,7 +167,7 @@ add_test_compareECLFiles(CASENAME model2_large_timestep_replay add_test_compareECLFiles(CASENAME model2_large_timestep_replay #FILENAME 7_HYSTERESIS_MODEL2 #ok - #FILENAME 9_4E_WINJ_GINJ_GUIDERATE_MSW #ok + FILENAME 9_4E_WINJ_GINJ_GUIDERATE_MSW #ok SIMULATOR flow_blackoil PREFIX compareTimestepReplayFail ABS_TOL ${timestep_replay_abs_tol} @@ -176,6 +176,26 @@ add_test_compareECLFiles(CASENAME model2_large_timestep_replay TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) +add_test_compareECLFiles(CASENAME model2_large_timestep_replay + FILENAME 0_BASE_MODEL2_LET + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model2 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME model2_large_timestep_replay + FILENAME 0A4_GRCTRL_LRAT_LRAT_GGR_BASE_MODEL2_MSW + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL ${timestep_replay_rel_tol} + DIR model2 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + opm_set_test_driver(${PROJECT_SOURCE_DIR}/tests/run-regressionTest.sh "") From 2c33cf18204112e250c79e8fe4e9ba023127aee5 Mon Sep 17 00:00:00 2001 From: hnil Date: Thu, 7 May 2026 11:28:28 +0200 Subject: [PATCH 9/9] relaxed tolarnce to avoid failure due to very small differenc in historic total --- regressionTests.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/regressionTests.cmake b/regressionTests.cmake index 6dbb36dc1fd..99a2d652db8 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -154,6 +154,16 @@ add_test_compareECLFiles(CASENAME model2_fail_large_timestep_replay DIR model2 TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) + +add_test_compareECLFiles(CASENAME model2_fail_large_timestep_replay + FILENAME 9_2B_DEPL_GCONPROD_2L_MSW + SIMULATOR flow_blackoil + PREFIX compareTimestepReplayFail + ABS_TOL ${timestep_replay_abs_tol} + REL_TOL 2e-7 + DIR model2 + TEST_ARGS --full-time-step-initially=true --cpr-reuse-setup=0 --newton-max-iterations=8 --tolerance-cnv-relaxed=1e-3 + TEST_ARGS_REPLAY --initial-time-step-in-days=11111111) add_test_compareECLFiles(CASENAME model2_large_timestep_replay FILENAME 0_BASE_MODEL2