diff --git a/opm/input/eclipse/Schedule/Tuning.hpp b/opm/input/eclipse/Schedule/Tuning.hpp index dd57b2cf8..0d4a13b73 100644 --- a/opm/input/eclipse/Schedule/Tuning.hpp +++ b/opm/input/eclipse/Schedule/Tuning.hpp @@ -20,6 +20,8 @@ #ifndef OPM_TUNING_HPP #define OPM_TUNING_HPP +#include + namespace Opm { class NextStep { @@ -49,7 +51,7 @@ namespace Opm { static Tuning serializationTestObject(); // Record1 - double TSINIT; + std::optional TSINIT; double TSMAXZ; double TSMINZ; double TSMCHP; diff --git a/opm/output/eclipse/VectorItems/doubhead.hpp b/opm/output/eclipse/VectorItems/doubhead.hpp index bed769a45..2ed1c4cb5 100644 --- a/opm/output/eclipse/VectorItems/doubhead.hpp +++ b/opm/output/eclipse/VectorItems/doubhead.hpp @@ -21,6 +21,7 @@ #define OPM_OUTPUT_ECLIPSE_VECTOR_DOUBHEAD_HPP #include +#include namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems { @@ -90,6 +91,12 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems // Default => Use TSMINZ from TUNING constexpr auto NetBalMinTSDefault = 0.0; + + // Default => Let Simulator choose TSINIT + constexpr auto TSINITNoValue = 1.00000011; + inline bool TSINITHasNoValue(const double value) { + return std::abs(value - TSINITNoValue) < 1.0e-7; + } } }}}} // Opm::RestartIO::Helpers::VectorItems diff --git a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp index 189be4be8..5c211439b 100644 --- a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp +++ b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp @@ -1028,11 +1028,18 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno) const auto& deck_item = rec.getItem(item_name); return deck_item.defaultApplied(0) ? previous_value : rec.getItem(item_name).getSIDouble(0); }; + + // \Note No TSTINIT value should not be used unless explicitly non-defaulted, hence removing value by default + // \Note (exception is the first time step, which is handled by the Tuning constructor) + tuning.TSINIT = std::nullopt; if (numrecords > 0) { const auto& record1 = handlerContext.keyword.getRecord(0); - tuning.TSINIT = nondefault_or_previous_sidouble(record1, "TSINIT", tuning.TSINIT); + // \Note A value indicates TSINIT was set in this record + if (const auto& deck_item = record1.getItem("TSINIT"); !deck_item.defaultApplied(0)) + tuning.TSINIT = std::optional{ record1.getItem("TSINIT").getSIDouble(0) }; + tuning.TSMAXZ = nondefault_or_previous_sidouble(record1, "TSMAXZ", tuning.TSMAXZ); tuning.TSMINZ = nondefault_or_previous_sidouble(record1, "TSMINZ", tuning.TSMINZ); tuning.TSMCHP = nondefault_or_previous_sidouble(record1, "TSMCHP", tuning.TSMCHP); diff --git a/src/opm/input/eclipse/Schedule/ScheduleState.cpp b/src/opm/input/eclipse/Schedule/ScheduleState.cpp index 30b464a55..16d06330a 100644 --- a/src/opm/input/eclipse/Schedule/ScheduleState.cpp +++ b/src/opm/input/eclipse/Schedule/ScheduleState.cpp @@ -131,8 +131,9 @@ ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_t if (this->next_tstep.has_value()) { if (!this->next_tstep->every_report()) { this->next_tstep = std::nullopt; - this->events().addEvent(ScheduleEvents::TUNING_CHANGE); } + // Need to signal an event also for the persistance to take effect + this->events().addEvent(ScheduleEvents::TUNING_CHANGE); } } @@ -347,12 +348,10 @@ Tuning& ScheduleState::tuning() { return this->m_tuning; } +// Returns -1 if there is no active limit on next step (from TUNING or NEXT[STEP]) double ScheduleState::max_next_tstep() const { - auto tuning_value = this->m_tuning.TSINIT; - if (!this->next_tstep.has_value()) - return tuning_value; - - auto next_value = this->next_tstep->value(); + double tuning_value = this->m_tuning.TSINIT.has_value() ? this->m_tuning.TSINIT.value() : -1.0; + double next_value = this->next_tstep.has_value() ? this->next_tstep->value() : -1.0; return std::max(next_value, tuning_value); } diff --git a/src/opm/input/eclipse/Schedule/Tuning.cpp b/src/opm/input/eclipse/Schedule/Tuning.cpp index b5e2bd093..8953ab31c 100644 --- a/src/opm/input/eclipse/Schedule/Tuning.cpp +++ b/src/opm/input/eclipse/Schedule/Tuning.cpp @@ -53,7 +53,7 @@ Tuning::Tuning() { using WsegIterKW = ParserKeywords::WSEGITER; // Record1 - TSINIT = TuningKw::TSINIT::defaultValue * Metric::Time; + TSINIT = std::nullopt; // Let simulator choose initial step if not specified TSMAXZ = TuningKw::TSMAXZ::defaultValue * Metric::Time; TSMINZ = TuningKw::TSMINZ::defaultValue * Metric::Time; TSMCHP = TuningKw::TSMCHP::defaultValue * Metric::Time; @@ -97,7 +97,7 @@ Tuning::Tuning() { Tuning Tuning::serializationTestObject() { Tuning result; - result.TSINIT = 1.0; + result.TSINIT = std::optional{1.0}; result.TSMAXZ = 2.0; result.TSMINZ = 3.0; result.TSMCHP = 4.0; diff --git a/src/opm/io/eclipse/rst/state.cpp b/src/opm/io/eclipse/rst/state.cpp index 68fd33734..27549704c 100644 --- a/src/opm/io/eclipse/rst/state.cpp +++ b/src/opm/io/eclipse/rst/state.cpp @@ -105,7 +105,9 @@ void RstState::load_tuning(const std::vector& intehead, this->tuning.MXWSIT = intehead[ VI::intehead::MXWSIT ]; this->tuning.MXWPIT = intehead[ VI::intehead::MXWPIT ]; - tuning.TSINIT = this->unit_system.to_si(M::time, doubhead[VI::doubhead::TsInit]); + double tsinit = this->unit_system.to_si(M::time, doubhead[VI::doubhead::TsInit]); + //tuning.TSINIT = tsinit > 0 ? std::optional{ tsinit } : std::nullopt; + tuning.TSINIT = VI::DoubHeadValue::TSINITHasNoValue(tsinit) ? std::nullopt : std::optional{ tsinit }; tuning.TSMAXZ = this->unit_system.to_si(M::time, doubhead[VI::doubhead::TsMaxz]); tuning.TSMINZ = this->unit_system.to_si(M::time, doubhead[VI::doubhead::TsMinz]); tuning.TSMCHP = this->unit_system.to_si(M::time, doubhead[VI::doubhead::TsMchp]); diff --git a/src/opm/output/eclipse/DoubHEAD.cpp b/src/opm/output/eclipse/DoubHEAD.cpp index 623dc0e67..dace3f8d0 100644 --- a/src/opm/output/eclipse/DoubHEAD.cpp +++ b/src/opm/output/eclipse/DoubHEAD.cpp @@ -567,7 +567,7 @@ Opm::RestartIO::DoubHEAD::tuningParameters(const Tuning& tuning, const double cnvT) { // Record 1 - this->data_[Index::TsInit] = tuning.TSINIT / cnvT; + this->data_[Index::TsInit] = tuning.TSINIT.has_value() ? tuning.TSINIT.value() / cnvT : VI::DoubHeadValue::TSINITNoValue ; this->data_[Index::TsMaxz] = tuning.TSMAXZ / cnvT; this->data_[Index::TsMinz] = tuning.TSMINZ / cnvT; this->data_[Index::TsMchp] = tuning.TSMCHP / cnvT; diff --git a/tests/parser/TuningTests.cpp b/tests/parser/TuningTests.cpp index cd583cffd..f88c05a48 100644 --- a/tests/parser/TuningTests.cpp +++ b/tests/parser/TuningTests.cpp @@ -20,6 +20,8 @@ #define BOOST_TEST_MODULE TuningTests +#include + #include #include @@ -99,11 +101,13 @@ BOOST_AUTO_TEST_CASE(TuningTest) { { size_t timestep = 4; const auto& event = schedule[timestep].events(); - BOOST_CHECK(!event.hasEvent(ScheduleEvents::TUNING_CHANGE)); + // Because NEXTSTEP is persistent a tuning event is triggered at each report step + BOOST_CHECK(event.hasEvent(ScheduleEvents::TUNING_CHANGE)); + + const auto& tuning = schedule[timestep].tuning(); + std::optional TSINIT_default = tuning.TSINIT; + BOOST_CHECK(TSINIT_default == std::nullopt); - const auto& tuning = schedule[4].tuning(); - double TSINIT_default = tuning.TSINIT; - BOOST_CHECK_CLOSE(TSINIT_default, 1 * Metric::Time, diff); BOOST_CHECK_CLOSE(schedule[timestep].max_next_tstep(), 5*Metric::Time, diff); double TSMAXZ_default = tuning.TSMAXZ; @@ -223,8 +227,9 @@ BOOST_AUTO_TEST_CASE(TuningTest) { const auto& tuning = schedule[timeStep].tuning(); BOOST_CHECK(event.hasEvent(ScheduleEvents::TUNING_CHANGE)); - double TSINIT = tuning.TSINIT; - BOOST_CHECK_CLOSE(TSINIT, 2 * Metric::Time, diff); + std::optional TSINIT = tuning.TSINIT; + BOOST_CHECK_CLOSE(TSINIT.value(), 2 * Metric::Time, diff); + BOOST_CHECK_CLOSE(schedule[timeStep].max_next_tstep(), 5*Metric::Time, diff); double TSMAXZ = tuning.TSMAXZ; @@ -330,7 +335,8 @@ BOOST_AUTO_TEST_CASE(TuningTest) { { std::size_t timestep = 7; const auto& event = schedule[timestep].events(); - BOOST_CHECK(!event.hasEvent(ScheduleEvents::TUNING_CHANGE)); + // Because NEXTSTEP is persistent a tuning event is triggered at each report step + BOOST_CHECK(event.hasEvent(ScheduleEvents::TUNING_CHANGE)); } /*** TIMESTEP 8 ***/ @@ -362,5 +368,13 @@ BOOST_AUTO_TEST_CASE(TuningTest) { BOOST_CHECK_CLOSE(tuning.TMAXWC, 10.0 * Metric::Time, diff); BOOST_CHECK_EQUAL(tuning.MXWSIT, ParserKeywords::WSEGITER::MAX_WELL_ITERATIONS::defaultValue); + + /********* Record 2 [and 3] (should be unchanged) ***********/ + double TRGTTE = tuning.TRGTTE; + BOOST_CHECK_CLOSE(TRGTTE, 0.2, diff); + + int NEWTMX = tuning.NEWTMX; + BOOST_CHECK_EQUAL(NEWTMX, 13); + } }