From 4928782cf4eebf24341f7ee8d08c5afdbe4497bc Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 27 Jan 2022 15:31:39 +0100 Subject: [PATCH 1/4] Add Schedule member to the msim class --- examples/msim.cpp | 4 +- msim/include/opm/msim/msim.hpp | 17 +++-- msim/src/msim.cpp | 49 +++++++------- tests/msim/test_msim.cpp | 12 ++-- tests/msim/test_msim_ACTIONX.cpp | 108 +++++++++++++++---------------- tests/msim/test_msim_EXIT.cpp | 11 ++-- 6 files changed, 104 insertions(+), 97 deletions(-) diff --git a/examples/msim.cpp b/examples/msim.cpp index 65e7aa2d8..733c4a510 100644 --- a/examples/msim.cpp +++ b/examples/msim.cpp @@ -51,8 +51,8 @@ int main(int /* argc */, char** argv) { error_guard.terminate(); } - Opm::msim msim(state); + Opm::msim msim(state, schedule); Opm::EclipseIO io(state, state.getInputGrid(), schedule, summary_config); - msim.run(schedule, io, false); + msim.run(io, false); } diff --git a/msim/include/opm/msim/msim.hpp b/msim/include/opm/msim/msim.hpp index a04e25368..ed4074356 100644 --- a/msim/include/opm/msim/msim.hpp +++ b/msim/include/opm/msim/msim.hpp @@ -36,27 +36,32 @@ public: using well_rate_function = double(const EclipseState&, const Schedule&, const SummaryState& st, const data::Solution&, size_t report_step, double seconds_elapsed); using solution_function = void(const EclipseState&, const Schedule&, data::Solution&, size_t report_step, double seconds_elapsed); - msim(const EclipseState& state); + msim(const EclipseState& state, const Schedule& schedule_arg); Opm::UDAValue uda_val(); void well_rate(const std::string& well, data::Rates::opt rate, std::function func); void solution(const std::string& field, std::function func); - void run(Schedule& schedule, EclipseIO& io, bool report_only); - void post_step(Schedule& schedule, SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, const time_point& sim_time); + void run(EclipseIO& io, bool report_only); + void post_step(SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, const time_point& sim_time); Action::State action_state; private: - void run_step(const Schedule& schedule, WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, EclipseIO& io) const; - void run_step(const Schedule& schedule, WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const; + void run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, EclipseIO& io) const; + void run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const; void output(WellTestState& wtest_state, SummaryState& st, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_data, EclipseIO& io) const; - void simulate(const Schedule& schedule, const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double seconds_elapsed, double time_step) const; + void simulate(const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double seconds_elapsed, double time_step) const; EclipseState state; std::map>> well_rates; std::map> solutions; + +public: + Schedule schedule; + Action::State action_state; + SummaryState st; }; } diff --git a/msim/src/msim.cpp b/msim/src/msim.cpp index 8737f1947..651daa177 100644 --- a/msim/src/msim.cpp +++ b/msim/src/msim.cpp @@ -41,15 +41,16 @@ namespace Opm { -msim::msim(const EclipseState& state_arg) : - state(state_arg) +msim::msim(const EclipseState& state_arg, const Schedule& schedule_arg) + : state(state_arg) + , schedule(schedule_arg) {} -void msim::run(Schedule& schedule, EclipseIO& io, bool report_only) { +void msim::run(EclipseIO& io, bool report_only) { const double week = 7 * 86400; data::Solution sol; - SummaryState st(TimeService::from_time_t(schedule.getStartTime())); - UDQState udq_state(schedule.getUDQConfig(0).params().undefinedValue()); + SummaryState st(TimeService::from_time_t(this->schedule.getStartTime())); + UDQState udq_state(this->schedule.getUDQConfig(0).params().undefinedValue()); WellTestState wtest_state; Python python; @@ -58,13 +59,13 @@ void msim::run(Schedule& schedule, EclipseIO& io, bool report_only) { data::Wells well_data; data::GroupAndNetworkValues group_nwrk_data; if (report_only) - run_step(schedule, wtest_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, io); + run_step(wtest_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, io); else { double time_step = std::min(week, 0.5*schedule.stepLength(report_step - 1)); - run_step(schedule, wtest_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, time_step, io); + run_step(wtest_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, time_step, io); } auto sim_time = TimeService::from_time_t( schedule.simTime(report_step) ); - post_step(schedule, st, sol, well_data, group_nwrk_data, report_step, sim_time); + post_step(st, sol, well_data, group_nwrk_data, report_step, sim_time); const auto& exit_status = schedule.exitStatus(); if (exit_status.has_value()) return; @@ -76,33 +77,33 @@ UDAValue msim::uda_val() { } -void msim::post_step(Schedule& schedule, SummaryState& st, data::Solution& /* sol */, data::Wells& /* well_data */, data::GroupAndNetworkValues& /* grp_nwrk_data */, size_t report_step, const time_point& sim_time) { - const auto& actions = schedule[report_step].actions.get(); +void msim::post_step(SummaryState& st, data::Solution& /* sol */, data::Wells& /* well_data */, data::GroupAndNetworkValues& /* grp_nwrk_data */, size_t report_step, const time_point& sim_time) { + const auto& actions = this->schedule[report_step].actions.get(); if (actions.empty()) return; - Action::Context context( st , schedule[report_step].wlist_manager.get()); + Action::Context context( st , this->schedule[report_step].wlist_manager.get()); for (const auto& action : actions.pending(this->action_state, std::chrono::system_clock::to_time_t(sim_time))) { auto result = action->eval(context); if (result) - schedule.applyAction(report_step, *action, result.wells(), {}); + this->schedule.applyAction(report_step, *action, result.wells(), {}); } for (const auto& pyaction : actions.pending_python()) - schedule.runPyAction(report_step, *pyaction, this->state, st); + this->schedule.runPyAction(report_step, *pyaction, this->state, st); } -void msim::run_step(const Schedule& schedule, WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& grp_nwrk_data, size_t report_step, EclipseIO& io) const { - this->run_step(schedule, wtest_state, st, udq_state, sol, well_data, grp_nwrk_data, report_step, schedule.stepLength(report_step - 1), io); +void msim::run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& grp_nwrk_data, size_t report_step, EclipseIO& io) const { + this->run_step(wtest_state, st, udq_state, sol, well_data, grp_nwrk_data, report_step, schedule.stepLength(report_step - 1), io); } -void msim::run_step(const Schedule& schedule, WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const { - double start_time = schedule.seconds(report_step - 1); - double end_time = schedule.seconds(report_step); +void msim::run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const { + double start_time = this->schedule.seconds(report_step - 1); + double end_time = this->schedule.seconds(report_step); double seconds_elapsed = start_time; while (seconds_elapsed < end_time) { @@ -110,7 +111,7 @@ void msim::run_step(const Schedule& schedule, WellTestState& wtest_state, Summar if ((seconds_elapsed + time_step) > end_time) time_step = end_time - seconds_elapsed; - this->simulate(schedule, st, sol, well_data, group_nwrk_data, report_step, seconds_elapsed, time_step); + this->simulate(st, sol, well_data, group_nwrk_data, report_step, seconds_elapsed, time_step); seconds_elapsed += time_step; @@ -124,7 +125,7 @@ void msim::run_step(const Schedule& schedule, WellTestState& wtest_state, Summar {}, {}); - schedule.getUDQConfig( report_step ).eval(report_step, schedule.wellMatcher(report_step), st, udq_state); + this->schedule.getUDQConfig( report_step ).eval(report_step, schedule.wellMatcher(report_step), st, udq_state); this->output(wtest_state, st, @@ -154,15 +155,15 @@ void msim::output(WellTestState& wtest_state, SummaryState& st, const UDQState& } -void msim::simulate(const Schedule& schedule, const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& /* group_nwrk_data */, size_t report_step, double seconds_elapsed, double time_step) const { +void msim::simulate(const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& /* group_nwrk_data */, size_t report_step, double seconds_elapsed, double time_step) const { for (const auto& sol_pair : this->solutions) { auto func = sol_pair.second; - func(this->state, schedule, sol, report_step, seconds_elapsed + time_step); + func(this->state, this->schedule, sol, report_step, seconds_elapsed + time_step); } for (const auto& well_pair : this->well_rates) { const std::string& well_name = well_pair.first; - const auto& sched_well = schedule.getWell(well_name, report_step); + const auto& sched_well = this->schedule.getWell(well_name, report_step); bool well_open = (sched_well.getStatus() == Well::Status::OPEN); data::Well& well = well_data[well_name]; @@ -171,7 +172,7 @@ void msim::simulate(const Schedule& schedule, const SummaryState& st, data::Solu auto func = rate_pair.second; if (well_open) - well.rates.set(rate, func(this->state, schedule, st, sol, report_step, seconds_elapsed + time_step)); + well.rates.set(rate, func(this->state, this->schedule, st, sol, report_step, seconds_elapsed + time_step)); else well.rates.set(rate, 0.0); } diff --git a/tests/msim/test_msim.cpp b/tests/msim/test_msim.cpp index bd23657b2..8bce04351 100644 --- a/tests/msim/test_msim.cpp +++ b/tests/msim/test_msim.cpp @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(RUN) { EclipseState state(deck); Schedule schedule(deck, state, python); SummaryConfig summary_config(deck, schedule, state.fieldProps(), state.aquifer()); - msim msim(state); + msim msim(state, schedule); msim.well_rate("PROD", data::Rates::opt::oil, prod_opr); msim.well_rate("RFTP", data::Rates::opt::oil, prod_rft); @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(RUN) { const WorkArea work_area("test_msim"); EclipseIO io(state, state.getInputGrid(), schedule, summary_config); - msim.run(schedule, io, false); + msim.run(io, false); for (const auto& fname : {"SPE1CASE1.INIT", "SPE1CASE1.UNRST", "SPE1CASE1.EGRID", "SPE1CASE1.SMSPEC", "SPE1CASE1.UNSMRY", "SPE1CASE1.RSM"}) BOOST_CHECK( is_file( fname )); @@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(RUN_SUMTHIN) { EclipseState state(deck); Schedule schedule(deck, state, python); SummaryConfig summary_config(deck, schedule, state.fieldProps(), state.aquifer()); - msim msim(state); + msim msim(state, schedule); msim.well_rate("PROD", data::Rates::opt::oil, prod_opr); msim.well_rate("RFTP", data::Rates::opt::oil, prod_rft); @@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(RUN_SUMTHIN) { EclipseIO io(state, state.getInputGrid(), schedule, summary_config); // TSTEP = N*7 - msim.run(schedule, io, false); + msim.run(io, false); // clang-format off const auto expect_smry_time = std::vector { @@ -245,7 +245,7 @@ BOOST_AUTO_TEST_CASE(RUN_RPTONLY) { Schedule schedule(deck, state, std::make_shared()); const SummaryConfig summary_config(deck, schedule, state.fieldProps(), state.aquifer()); - msim msim(state); + msim msim(state, schedule); msim.well_rate("PROD", data::Rates::opt::oil, prod_opr); msim.well_rate("RFTP", data::Rates::opt::oil, prod_rft); @@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE(RUN_RPTONLY) { EclipseIO io(state, state.getInputGrid(), schedule, summary_config); // TSTEP = N*7 - msim.run(schedule, io, false); + msim.run(io, false); // clang-format off const auto expect_smry_time = std::vector { diff --git a/tests/msim/test_msim_ACTIONX.cpp b/tests/msim/test_msim_ACTIONX.cpp index b6e575344..96cc10e1c 100644 --- a/tests/msim/test_msim_ACTIONX.cpp +++ b/tests/msim/test_msim_ACTIONX.cpp @@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(UDQ_SORTA_EXAMPLE) { #include "actionx2.include" test_data td( actionx ); - msim sim(td.state); + msim sim(td.state, td.schedule); { WorkArea work_area("test_msim"); EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); @@ -184,16 +184,16 @@ BOOST_AUTO_TEST_CASE(UDQ_SORTA_EXAMPLE) { sim.well_rate("P3", data::Rates::opt::oil, prod_opr); sim.well_rate("P4", data::Rates::opt::oil, prod_opr_low); - sim.run(td.schedule, io, false); + sim.run(io, false); { - const auto& w1 = td.schedule.getWell("P1", 1); - const auto& w4 = td.schedule.getWell("P4", 1); + const auto& w1 = sim.schedule.getWell("P1", 1); + const auto& w4 = sim.schedule.getWell("P4", 1); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); BOOST_CHECK(w4.getStatus() == Well::Status::OPEN ); } { - const auto& w1 = td.schedule.getWellatEnd("P1"); - const auto& w4 = td.schedule.getWellatEnd("P4"); + const auto& w1 = sim.schedule.getWellatEnd("P1"); + const auto& w4 = sim.schedule.getWellatEnd("P4"); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); BOOST_CHECK(w4.getStatus() == Well::Status::SHUT ); } @@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(WELL_CLOSE_EXAMPLE) { #include "actionx1.include" test_data td( actionx1 ); - msim sim(td.state); + msim sim(td.state, td.schedule); { WorkArea work_area("test_msim"); EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); @@ -221,10 +221,10 @@ BOOST_AUTO_TEST_CASE(WELL_CLOSE_EXAMPLE) { sim.well_rate("P4", data::Rates::opt::wat, prod_wpr_P4); { - const auto& w1 = td.schedule.getWell("P1", 15); - const auto& w2 = td.schedule.getWell("P2", 15); - const auto& w3 = td.schedule.getWell("P3", 15); - const auto& w4 = td.schedule.getWell("P4", 15); + const auto& w1 = sim.schedule.getWell("P1", 15); + const auto& w2 = sim.schedule.getWell("P2", 15); + const auto& w3 = sim.schedule.getWell("P3", 15); + const auto& w4 = sim.schedule.getWell("P4", 15); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); BOOST_CHECK(w2.getStatus() == Well::Status::OPEN ); @@ -233,19 +233,19 @@ BOOST_AUTO_TEST_CASE(WELL_CLOSE_EXAMPLE) { } - sim.run(td.schedule, io, false); + sim.run(io, false); { - const auto& w1 = td.schedule.getWell("P1", 15); - const auto& w3 = td.schedule.getWell("P3", 15); + const auto& w1 = sim.schedule.getWell("P1", 15); + const auto& w3 = sim.schedule.getWell("P3", 15); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); BOOST_CHECK(w3.getStatus() == Well::Status::OPEN ); } { - const auto& w2_6 = td.schedule.getWell("P2", 6); + const auto& w2_6 = sim.schedule.getWell("P2", 6); BOOST_CHECK(w2_6.getStatus() == Well::Status::SHUT ); } { - const auto& w4_11 = td.schedule.getWell("P4", 11); + const auto& w4_11 = sim.schedule.getWell("P4", 11); BOOST_CHECK(w4_11.getStatus() == Well::Status::SHUT ); } } @@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE(UDQ_ASSIGN) { #include "actionx1.include" test_data td( actionx1 ); - msim sim(td.state); + msim sim(td.state, td.schedule); { WorkArea work_area("test_msim"); EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); @@ -272,7 +272,7 @@ BOOST_AUTO_TEST_CASE(UDQ_ASSIGN) { sim.well_rate("P3", data::Rates::opt::wat, prod_wpr_P3); sim.well_rate("P4", data::Rates::opt::wat, prod_wpr_P4); - sim.run(td.schedule, io, false); + sim.run(io, false); const auto& base_name = td.state.getIOConfig().getBaseName(); @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(UDQ_WUWCT) { #include "actionx1.include" test_data td( actionx1 ); - msim sim(td.state); + msim sim(td.state, td.schedule); { WorkArea work_area("test_msim"); EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); @@ -318,7 +318,7 @@ BOOST_AUTO_TEST_CASE(UDQ_WUWCT) { sim.well_rate("P3", data::Rates::opt::wat, prod_wpr_P3); sim.well_rate("P4", data::Rates::opt::wat, prod_wpr_P4); - sim.run(td.schedule, io, false); + sim.run(io, false); const auto& base_name = td.state.getIOConfig().getBaseName(); const EclIO::ESmry ecl_sum(base_name + ".SMSPEC"); @@ -363,7 +363,7 @@ BOOST_AUTO_TEST_CASE(UDQ_WUWCT) { BOOST_AUTO_TEST_CASE(UDQ_IN_ACTIONX) { #include "udq_in_actionx.include" test_data td( actionx1 ); - msim sim(td.state); + msim sim(td.state, td.schedule); { WorkArea work_area("test_msim"); EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); @@ -384,26 +384,26 @@ BOOST_AUTO_TEST_CASE(UDQ_IN_ACTIONX) { sim.well_rate("P4", data::Rates::opt::gas, prod_gpr); { - const auto& w1 = td.schedule.getWell("P1", 15); + const auto& w1 = sim.schedule.getWell("P1", 15); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); - const auto& udq1 = td.schedule.getUDQConfig(15); + const auto& udq1 = sim.schedule.getUDQConfig(15); BOOST_CHECK(!udq1.has_keyword("FUNEW")); - const auto& udq2 = td.schedule.getUDQConfig(25); + const auto& udq2 = sim.schedule.getUDQConfig(25); BOOST_CHECK(udq2.has_keyword("FUPROD")); } - sim.run(td.schedule, io, false); + sim.run(io, false); { - const auto& w1 = td.schedule.getWell("P1", 15); + const auto& w1 = sim.schedule.getWell("P1", 15); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); - const auto& udq1 = td.schedule.getUDQConfig(15); + const auto& udq1 = sim.schedule.getUDQConfig(15); BOOST_CHECK(udq1.has_keyword("FUNEW")); - const auto& udq2 = td.schedule.getUDQConfig(25); + const auto& udq2 = sim.schedule.getUDQConfig(25); BOOST_CHECK(udq2.has_keyword("FUPROD")); BOOST_CHECK(udq2.has_keyword("FUNEW")); } @@ -421,7 +421,7 @@ BOOST_AUTO_TEST_CASE(UDQ_IN_ACTIONX) { BOOST_AUTO_TEST_CASE(UDA) { #include "uda.include" test_data td( uda_deck ); - msim sim(td.state); + msim sim(td.state, td.schedule); auto eps_lim = sim.uda_val().epsilonLimit(); EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); @@ -434,7 +434,7 @@ BOOST_AUTO_TEST_CASE(UDA) { { WorkArea work_area("uda_sim"); - sim.run(td.schedule, io, true); + sim.run(io, true); const auto& base_name = td.state.getIOConfig().getBaseName(); const EclIO::ESmry ecl_sum(base_name + ".SMSPEC"); @@ -460,7 +460,7 @@ BOOST_AUTO_TEST_CASE(UDA) { BOOST_AUTO_TEST_CASE(COMPDAT) { #include "compdat.include" test_data td( compdat_deck ); - msim sim(td.state); + msim sim(td.state, td.schedule); EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); sim.well_rate("P1", data::Rates::opt::wat, prod_wpr_P1); @@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE(COMPDAT) { { WorkArea work_area("compdat_sim"); - BOOST_CHECK_NO_THROW(sim.run(td.schedule, io, true)); + BOOST_CHECK_NO_THROW(sim.run(io, true)); } } @@ -480,10 +480,10 @@ BOOST_AUTO_TEST_CASE(COMPDAT) { BOOST_AUTO_TEST_CASE(PYTHON_WELL_CLOSE_EXAMPLE) { const auto& deck = Parser().parseFile("msim/MSIM_PYACTION.DATA"); test_data td( deck ); - msim sim(td.state); + msim sim(td.state, td.schedule); { WorkArea work_area("test_msim"); - EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); + EclipseIO io(td.state, td.state.getInputGrid(), sim.schedule, td.summary_config); sim.well_rate("P1", data::Rates::opt::oil, prod_opr); sim.well_rate("P2", data::Rates::opt::oil, prod_opr); @@ -496,10 +496,10 @@ BOOST_AUTO_TEST_CASE(PYTHON_WELL_CLOSE_EXAMPLE) { sim.well_rate("P4", data::Rates::opt::wat, prod_wpr_P4); { - const auto& w1 = td.schedule.getWell("P1", 15); - const auto& w2 = td.schedule.getWell("P2", 15); - const auto& w3 = td.schedule.getWell("P3", 15); - const auto& w4 = td.schedule.getWell("P4", 15); + const auto& w1 = sim.schedule.getWell("P1", 15); + const auto& w2 = sim.schedule.getWell("P2", 15); + const auto& w3 = sim.schedule.getWell("P3", 15); + const auto& w4 = sim.schedule.getWell("P4", 15); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); BOOST_CHECK(w2.getStatus() == Well::Status::OPEN ); @@ -508,19 +508,19 @@ BOOST_AUTO_TEST_CASE(PYTHON_WELL_CLOSE_EXAMPLE) { } - sim.run(td.schedule, io, false); + sim.run(io, false); { - const auto& w1 = td.schedule.getWell("P1", 15); - const auto& w3 = td.schedule.getWell("P3", 15); + const auto& w1 = sim.schedule.getWell("P1", 15); + const auto& w3 = sim.schedule.getWell("P3", 15); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); BOOST_CHECK(w3.getStatus() == Well::Status::OPEN ); } { - const auto& w2_6 = td.schedule.getWell("P2", 6); + const auto& w2_6 = sim.schedule.getWell("P2", 6); BOOST_CHECK(w2_6.getStatus() == Well::Status::SHUT ); } { - const auto& w4_11 = td.schedule.getWell("P4", 11); + const auto& w4_11 = sim.schedule.getWell("P4", 11); BOOST_CHECK(w4_11.getStatus() == Well::Status::SHUT ); } } @@ -529,10 +529,10 @@ BOOST_AUTO_TEST_CASE(PYTHON_WELL_CLOSE_EXAMPLE) { BOOST_AUTO_TEST_CASE(PYTHON_ACTIONX) { const auto& deck = Parser().parseFile("msim/MSIM_PYACTION_ACTIONX.DATA"); test_data td( deck ); - msim sim(td.state); + msim sim(td.state, td.schedule); { WorkArea work_area("test_msim"); - EclipseIO io(td.state, td.state.getInputGrid(), td.schedule, td.summary_config); + EclipseIO io(td.state, td.state.getInputGrid(), sim.schedule, td.summary_config); sim.well_rate("P1", data::Rates::opt::oil, prod_opr); sim.well_rate("P2", data::Rates::opt::oil, prod_opr); @@ -545,10 +545,10 @@ BOOST_AUTO_TEST_CASE(PYTHON_ACTIONX) { sim.well_rate("P4", data::Rates::opt::wat, prod_wpr_P4); { - const auto& w1 = td.schedule.getWell("P1", 0); - const auto& w2 = td.schedule.getWell("P2", 0); - const auto& w3 = td.schedule.getWell("P3", 0); - const auto& w4 = td.schedule.getWell("P4", 0); + const auto& w1 = sim.schedule.getWell("P1", 0); + const auto& w2 = sim.schedule.getWell("P2", 0); + const auto& w3 = sim.schedule.getWell("P3", 0); + const auto& w4 = sim.schedule.getWell("P4", 0); BOOST_CHECK(w1.getStatus() == Well::Status::OPEN ); BOOST_CHECK(w2.getStatus() == Well::Status::OPEN ); @@ -557,13 +557,13 @@ BOOST_AUTO_TEST_CASE(PYTHON_ACTIONX) { } - sim.run(td.schedule, io, false); + sim.run(io, false); { - const auto& w1 = td.schedule.getWell("P1", 1); - const auto& w2 = td.schedule.getWell("P2", 2); - const auto& w3 = td.schedule.getWell("P3", 3); - const auto& w4 = td.schedule.getWell("P4", 4); + const auto& w1 = sim.schedule.getWell("P1", 1); + const auto& w2 = sim.schedule.getWell("P2", 2); + const auto& w3 = sim.schedule.getWell("P3", 3); + const auto& w4 = sim.schedule.getWell("P4", 4); BOOST_CHECK(w1.getStatus() == Well::Status::SHUT ); BOOST_CHECK(w2.getStatus() == Well::Status::SHUT ); BOOST_CHECK(w3.getStatus() == Well::Status::SHUT ); diff --git a/tests/msim/test_msim_EXIT.cpp b/tests/msim/test_msim_EXIT.cpp index b8b9ec988..8cb8a0e89 100644 --- a/tests/msim/test_msim_EXIT.cpp +++ b/tests/msim/test_msim_EXIT.cpp @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(MSIM_EXIT_TEST) { { WorkArea work_area("test_msim"); - Opm::msim msim(state); + Opm::msim msim(state, schedule); Opm::EclipseIO io(state, state.getInputGrid(), schedule, summary_config); msim.well_rate("P1", Opm::data::Rates::opt::oil, Opm::prod_opr); msim.well_rate("P2", Opm::data::Rates::opt::oil, Opm::prod_opr); @@ -99,9 +99,10 @@ BOOST_AUTO_TEST_CASE(MSIM_EXIT_TEST) { msim.well_rate("P2", Opm::data::Rates::opt::wat, Opm::prod_wpr_P2); msim.well_rate("P3", Opm::data::Rates::opt::wat, Opm::prod_wpr_P3); msim.well_rate("P4", Opm::data::Rates::opt::wat, Opm::prod_wpr_P4); - msim.run(schedule, io, false); + msim.run(io, false); + + auto exit_status = msim.schedule.exitStatus(); + BOOST_CHECK( exit_status.has_value() ); + BOOST_CHECK_EQUAL(exit_status.value(), 99); } - auto exit_status = schedule.exitStatus(); - BOOST_CHECK( exit_status.has_value() ); - BOOST_CHECK_EQUAL(exit_status.value(), 99); } From c660b2e4b424f02dc3990792655fd2adbca7eb60 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 27 Jan 2022 15:44:21 +0100 Subject: [PATCH 2/4] Make SummaryState public member in msim class --- msim/include/opm/msim/msim.hpp | 13 ++++++------ msim/src/msim.cpp | 36 ++++++++++++++++------------------ 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/msim/include/opm/msim/msim.hpp b/msim/include/opm/msim/msim.hpp index ed4074356..f19f8cf08 100644 --- a/msim/include/opm/msim/msim.hpp +++ b/msim/include/opm/msim/msim.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -43,16 +44,14 @@ public: void well_rate(const std::string& well, data::Rates::opt rate, std::function func); void solution(const std::string& field, std::function func); void run(EclipseIO& io, bool report_only); - void post_step(SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, const time_point& sim_time); - - Action::State action_state; + void post_step(data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, const time_point& sim_time); private: - void run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, EclipseIO& io) const; - void run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const; - void output(WellTestState& wtest_state, SummaryState& st, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_data, EclipseIO& io) const; - void simulate(const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double seconds_elapsed, double time_step) const; + void run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, EclipseIO& io); + void run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io); + void output(WellTestState& wtest_state, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_data, EclipseIO& io); + void simulate(data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double seconds_elapsed, double time_step); EclipseState state; std::map>> well_rates; diff --git a/msim/src/msim.cpp b/msim/src/msim.cpp index 651daa177..e00cb7728 100644 --- a/msim/src/msim.cpp +++ b/msim/src/msim.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -44,12 +43,12 @@ namespace Opm { msim::msim(const EclipseState& state_arg, const Schedule& schedule_arg) : state(state_arg) , schedule(schedule_arg) + , st(TimeService::from_time_t(this->schedule.getStartTime())) {} void msim::run(EclipseIO& io, bool report_only) { const double week = 7 * 86400; data::Solution sol; - SummaryState st(TimeService::from_time_t(this->schedule.getStartTime())); UDQState udq_state(this->schedule.getUDQConfig(0).params().undefinedValue()); WellTestState wtest_state; Python python; @@ -59,13 +58,13 @@ void msim::run(EclipseIO& io, bool report_only) { data::Wells well_data; data::GroupAndNetworkValues group_nwrk_data; if (report_only) - run_step(wtest_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, io); + run_step(wtest_state, udq_state, sol, well_data, group_nwrk_data, report_step, io); else { double time_step = std::min(week, 0.5*schedule.stepLength(report_step - 1)); - run_step(wtest_state, st, udq_state, sol, well_data, group_nwrk_data, report_step, time_step, io); + run_step(wtest_state, udq_state, sol, well_data, group_nwrk_data, report_step, time_step, io); } auto sim_time = TimeService::from_time_t( schedule.simTime(report_step) ); - post_step(st, sol, well_data, group_nwrk_data, report_step, sim_time); + post_step(sol, well_data, group_nwrk_data, report_step, sim_time); const auto& exit_status = schedule.exitStatus(); if (exit_status.has_value()) return; @@ -77,12 +76,12 @@ UDAValue msim::uda_val() { } -void msim::post_step(SummaryState& st, data::Solution& /* sol */, data::Wells& /* well_data */, data::GroupAndNetworkValues& /* grp_nwrk_data */, size_t report_step, const time_point& sim_time) { +void msim::post_step(data::Solution& /* sol */, data::Wells& /* well_data */, data::GroupAndNetworkValues& /* grp_nwrk_data */, size_t report_step, const time_point& sim_time) { const auto& actions = this->schedule[report_step].actions.get(); if (actions.empty()) return; - Action::Context context( st , this->schedule[report_step].wlist_manager.get()); + Action::Context context( this->st , this->schedule[report_step].wlist_manager.get()); for (const auto& action : actions.pending(this->action_state, std::chrono::system_clock::to_time_t(sim_time))) { auto result = action->eval(context); @@ -91,17 +90,17 @@ void msim::post_step(SummaryState& st, data::Solution& /* sol */, data::Wells& / } for (const auto& pyaction : actions.pending_python()) - this->schedule.runPyAction(report_step, *pyaction, this->state, st); + this->schedule.runPyAction(report_step, *pyaction, this->state, this->st); } -void msim::run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& grp_nwrk_data, size_t report_step, EclipseIO& io) const { - this->run_step(wtest_state, st, udq_state, sol, well_data, grp_nwrk_data, report_step, schedule.stepLength(report_step - 1), io); +void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& grp_nwrk_data, size_t report_step, EclipseIO& io) { + this->run_step(wtest_state, udq_state, sol, well_data, grp_nwrk_data, report_step, schedule.stepLength(report_step - 1), io); } -void msim::run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) const { +void msim::run_step(WellTestState& wtest_state, UDQState& udq_state, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& group_nwrk_data, size_t report_step, double dt, EclipseIO& io) { double start_time = this->schedule.seconds(report_step - 1); double end_time = this->schedule.seconds(report_step); double seconds_elapsed = start_time; @@ -111,11 +110,11 @@ void msim::run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_ if ((seconds_elapsed + time_step) > end_time) time_step = end_time - seconds_elapsed; - this->simulate(st, sol, well_data, group_nwrk_data, report_step, seconds_elapsed, time_step); + this->simulate(sol, well_data, group_nwrk_data, report_step, seconds_elapsed, time_step); seconds_elapsed += time_step; - io.summary().eval(st, + io.summary().eval(this->st, report_step, seconds_elapsed, well_data, @@ -125,10 +124,9 @@ void msim::run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_ {}, {}); - this->schedule.getUDQConfig( report_step ).eval(report_step, schedule.wellMatcher(report_step), st, udq_state); + this->schedule.getUDQConfig( report_step ).eval(report_step, schedule.wellMatcher(report_step), this->st, udq_state); this->output(wtest_state, - st, udq_state, report_step, (seconds_elapsed < end_time), @@ -142,11 +140,11 @@ void msim::run_step(WellTestState& wtest_state, SummaryState& st, UDQState& udq_ -void msim::output(WellTestState& wtest_state, SummaryState& st, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_nwrk_data, EclipseIO& io) const { +void msim::output(WellTestState& wtest_state, const UDQState& udq_state, size_t report_step, bool substep, double seconds_elapsed, const data::Solution& sol, const data::Wells& well_data, const data::GroupAndNetworkValues& group_nwrk_data, EclipseIO& io) { RestartValue value(sol, well_data, group_nwrk_data, {}); io.writeTimeStep(this->action_state, wtest_state, - st, + this->st, udq_state, report_step, substep, @@ -155,7 +153,7 @@ void msim::output(WellTestState& wtest_state, SummaryState& st, const UDQState& } -void msim::simulate(const SummaryState& st, data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& /* group_nwrk_data */, size_t report_step, double seconds_elapsed, double time_step) const { +void msim::simulate(data::Solution& sol, data::Wells& well_data, data::GroupAndNetworkValues& /* group_nwrk_data */, size_t report_step, double seconds_elapsed, double time_step) { for (const auto& sol_pair : this->solutions) { auto func = sol_pair.second; func(this->state, this->schedule, sol, report_step, seconds_elapsed + time_step); @@ -172,7 +170,7 @@ void msim::simulate(const SummaryState& st, data::Solution& sol, data::Wells& we auto func = rate_pair.second; if (well_open) - well.rates.set(rate, func(this->state, this->schedule, st, sol, report_step, seconds_elapsed + time_step)); + well.rates.set(rate, func(this->state, this->schedule, this->st, sol, report_step, seconds_elapsed + time_step)); else well.rates.set(rate, 0.0); } From c37c2b2d49fbe6ebbe93e22cabcf91fc4b27d4f6 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 28 Jan 2022 09:26:01 +0100 Subject: [PATCH 3/4] Make SummaryState::set() available as __setitem__ in Python --- opm/input/eclipse/Schedule/SummaryState.hpp | 6 ++++-- python/cxx/summary_state.cpp | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/opm/input/eclipse/Schedule/SummaryState.hpp b/opm/input/eclipse/Schedule/SummaryState.hpp index 823db401f..06e9885f4 100644 --- a/opm/input/eclipse/Schedule/SummaryState.hpp +++ b/opm/input/eclipse/Schedule/SummaryState.hpp @@ -75,8 +75,10 @@ public: explicit SummaryState(std::time_t sim_start_arg); /* - The set() function has to be retained temporarily to support updating of - cumulatives from restart files. + The canonical way to update the SummaryState is through the update_xxx() + methods which will inspect the variable and either accumulate or just + assign, depending on whether it represents a total or not. The set() + method is low level and unconditionally do an assignment. */ void set(const std::string& key, double value); diff --git a/python/cxx/summary_state.cpp b/python/cxx/summary_state.cpp index e5e95f6cc..04c8f3b0b 100644 --- a/python/cxx/summary_state.cpp +++ b/python/cxx/summary_state.cpp @@ -55,5 +55,6 @@ void python::common::export_SummaryState(py::module& module) { .def("__contains__", &SummaryState::has) .def("has_well_var", py::overload_cast(&SummaryState::has_well_var, py::const_)) .def("has_group_var", py::overload_cast(&SummaryState::has_group_var, py::const_)) + .def("__setitem__", &SummaryState::set) .def("__getitem__", py::overload_cast(&SummaryState::get, py::const_)); } From 343f14dacfec96845078b42eaba2b3bab3a76537 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 28 Jan 2022 09:28:07 +0100 Subject: [PATCH 4/4] Record and act on return value from PYACTION --- CMakeLists_files.cmake | 1 + msim/src/msim.cpp | 4 +-- opm/input/eclipse/Schedule/Action/Actions.hpp | 2 +- .../eclipse/Schedule/Action/PyAction.hpp | 3 +- opm/input/eclipse/Schedule/Action/State.hpp | 6 ++++ opm/input/eclipse/Schedule/Schedule.hpp | 2 +- .../input/eclipse/Schedule/Action/Actions.cpp | 4 +-- .../eclipse/Schedule/Action/PyAction.cpp | 22 ++++++++------ .../input/eclipse/Schedule/Action/State.cpp | 18 +++++++++++- src/opm/input/eclipse/Schedule/Schedule.cpp | 6 ++-- tests/EMBEDDED_PYTHON.DATA | 12 ++++++++ tests/msim/MSIM_PYACTION.DATA | 2 +- tests/msim/action_count.py | 11 +++++++ tests/msim/test_msim_ACTIONX.cpp | 1 + tests/parser/ACTIONX.cpp | 3 +- tests/parser/EmbeddedPython.cpp | 29 ++++++++++++++++++- 16 files changed, 104 insertions(+), 22 deletions(-) create mode 100644 tests/msim/action_count.py diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index cea90529c..ed370bf25 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -551,6 +551,7 @@ if(ENABLE_ECL_OUTPUT) tests/msim/action1.py tests/msim/action2.py tests/msim/action3.py + tests/msim/action_count.py tests/VFP_CASE.DATA) endif() diff --git a/msim/src/msim.cpp b/msim/src/msim.cpp index e00cb7728..d51d8e8b9 100644 --- a/msim/src/msim.cpp +++ b/msim/src/msim.cpp @@ -89,8 +89,8 @@ void msim::post_step(data::Solution& /* sol */, data::Wells& /* well_data */, da this->schedule.applyAction(report_step, *action, result.wells(), {}); } - for (const auto& pyaction : actions.pending_python()) - this->schedule.runPyAction(report_step, *pyaction, this->state, this->st); + for (const auto& pyaction : actions.pending_python(this->action_state)) + this->schedule.runPyAction(report_step, *pyaction, this->action_state, this->state, this->st); } diff --git a/opm/input/eclipse/Schedule/Action/Actions.hpp b/opm/input/eclipse/Schedule/Action/Actions.hpp index a8b4e261b..e7e958ee9 100644 --- a/opm/input/eclipse/Schedule/Action/Actions.hpp +++ b/opm/input/eclipse/Schedule/Action/Actions.hpp @@ -55,7 +55,7 @@ public: const ActionX& operator[](const std::string& name) const; const ActionX& operator[](std::size_t index) const; std::vector pending(const State& state, std::time_t sim_time) const; - std::vector pending_python() const; + std::vector pending_python(const State& state) const; bool has(const std::string& name) const; std::vector::const_iterator begin() const; diff --git a/opm/input/eclipse/Schedule/Action/PyAction.hpp b/opm/input/eclipse/Schedule/Action/PyAction.hpp index cf02d046c..7744da8c7 100644 --- a/opm/input/eclipse/Schedule/Action/PyAction.hpp +++ b/opm/input/eclipse/Schedule/Action/PyAction.hpp @@ -36,6 +36,7 @@ class SummaryState; class PyRunModule; namespace Action { +class State; class PyAction { public: @@ -53,7 +54,7 @@ public: bool run(EclipseState& ecl_state, Schedule& schedule, std::size_t report_step, SummaryState& st, const std::function&)>& actionx_callback) const; const std::string& name() const; - bool active() const; + bool ready(const State& state) const; bool operator==(const PyAction& other) const; template diff --git a/opm/input/eclipse/Schedule/Action/State.hpp b/opm/input/eclipse/Schedule/Action/State.hpp index 9fb247f0c..2c7842388 100644 --- a/opm/input/eclipse/Schedule/Action/State.hpp +++ b/opm/input/eclipse/Schedule/Action/State.hpp @@ -35,6 +35,8 @@ namespace Action { class ActionX; class Actions; +class PyAction; + class State { struct RunState { @@ -79,9 +81,11 @@ struct RunState { public: void add_run(const ActionX& action, std::time_t sim_time, Result result); + void add_run(const PyAction& action, bool result); std::size_t run_count(const ActionX& action) const; std::time_t run_time(const ActionX& action) const; std::optional result(const std::string& action) const; + std::optional python_result(const std::string& action) const; void load_rst(const Actions& action_config, const RestartIO::RstState& rst_state); template @@ -89,6 +93,7 @@ public: { serializer.map(this->run_state); serializer.map(this->last_result); + serializer.template map, false>(this->m_python_result); } @@ -100,6 +105,7 @@ private: static action_id make_id(const ActionX& action); std::map run_state; std::map last_result; + std::map m_python_result; }; diff --git a/opm/input/eclipse/Schedule/Schedule.hpp b/opm/input/eclipse/Schedule/Schedule.hpp index 33558bda3..28125cb8b 100644 --- a/opm/input/eclipse/Schedule/Schedule.hpp +++ b/opm/input/eclipse/Schedule/Schedule.hpp @@ -295,7 +295,7 @@ namespace Opm Python code. he return value from runPyAction() comes from such a internal ACTIONX. */ - SimulatorUpdate runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, EclipseState& ecl_state, SummaryState& summary_state); + SimulatorUpdate runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, Action::State& action_state, EclipseState& ecl_state, SummaryState& summary_state); const GasLiftOpt& glo(std::size_t report_step) const; diff --git a/src/opm/input/eclipse/Schedule/Action/Actions.cpp b/src/opm/input/eclipse/Schedule/Action/Actions.cpp index dfbab48d1..e370d2089 100644 --- a/src/opm/input/eclipse/Schedule/Action/Actions.cpp +++ b/src/opm/input/eclipse/Schedule/Action/Actions.cpp @@ -109,10 +109,10 @@ bool Actions::ready(const State& state, std::time_t sim_time) const { return false; } -std::vector Actions::pending_python() const { +std::vector Actions::pending_python(const State& state) const { std::vector pyaction_vector; for (const auto& pyaction : this->pyactions) { - if (pyaction.active()) + if (pyaction.ready(state)) pyaction_vector.push_back( &pyaction ); } return pyaction_vector; diff --git a/src/opm/input/eclipse/Schedule/Action/PyAction.cpp b/src/opm/input/eclipse/Schedule/Action/PyAction.cpp index e70b8c57d..4c60607ef 100644 --- a/src/opm/input/eclipse/Schedule/Action/PyAction.cpp +++ b/src/opm/input/eclipse/Schedule/Action/PyAction.cpp @@ -31,6 +31,7 @@ namespace py = pybind11; #include #include #include +#include #include @@ -64,8 +65,18 @@ PyAction PyAction::serializeObject() return result; } -bool PyAction::active() const { - return this->m_active; +bool PyAction::ready(const State& state) const { + if (this->m_run_count == RunCount::unlimited) + return true; + + auto last_result = state.python_result(this->m_name); + if (!last_result.has_value()) + return true; + + if (this->m_run_count == RunCount::first_true && last_result.value() == false) + return true; + + return false; } @@ -73,13 +84,6 @@ const std::string& PyAction::name() const { return this->m_name; } -void PyAction::update(bool result) const { - if (this->m_run_count == RunCount::single) - this->m_active = false; - - if (this->m_run_count == RunCount::first_true && result) - this->m_active = false; -} bool PyAction::operator==(const PyAction& other) const { return this->m_name == other.m_name && diff --git a/src/opm/input/eclipse/Schedule/Action/State.cpp b/src/opm/input/eclipse/Schedule/Action/State.cpp index a9b17adce..3087cd5ff 100644 --- a/src/opm/input/eclipse/Schedule/Action/State.cpp +++ b/src/opm/input/eclipse/Schedule/Action/State.cpp @@ -59,6 +59,10 @@ void State::add_run(const ActionX& action, std::time_t run_time, Result result) this->last_result.insert_or_assign(action.name(), std::move(result)); } +void State::add_run(const PyAction& action, bool result) { + this->m_python_result.insert_or_assign( action.name(), result ); +} + std::optional State::result(const std::string& action) const { auto iter = this->last_result.find(action); @@ -69,6 +73,16 @@ std::optional State::result(const std::string& action) const { } +std::optional State::python_result(const std::string& action) const { + auto iter = this->m_python_result.find(action); + if (iter == this->m_python_result.end()) + return std::nullopt; + + return iter->second; +} + + + /* When restoring from restart file we initialize the number of times it has run and the last run time. From the evaluation only the 'true' evaluation is @@ -86,7 +100,8 @@ void State::load_rst(const Actions& action_config, const RestartIO::RstState& rs bool State::operator==(const State& other) const { return this->run_state == other.run_state && - this->last_result == other.last_result; + this->last_result == other.last_result && + this->m_python_result == other.m_python_result; } @@ -94,6 +109,7 @@ State State::serializeObject() { State st; st.run_state.insert(std::make_pair( std::make_pair("ACTION", 100), RunState::serializeObject())); st.last_result.insert( std::make_pair("ACTION", Result::serializeObject())); + st.m_python_result.insert( std::make_pair("PYACTION", false) ); return st; } diff --git a/src/opm/input/eclipse/Schedule/Schedule.cpp b/src/opm/input/eclipse/Schedule/Schedule.cpp index 3b8d97dd9..a601d89fe 100644 --- a/src/opm/input/eclipse/Schedule/Schedule.cpp +++ b/src/opm/input/eclipse/Schedule/Schedule.cpp @@ -58,6 +58,7 @@ #include #include +#include #include #include #include @@ -1428,14 +1429,15 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen */ - SimulatorUpdate Schedule::runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, EclipseState& ecl_state, SummaryState& summary_state) { + SimulatorUpdate Schedule::runPyAction(std::size_t reportStep, const Action::PyAction& pyaction, Action::State& action_state, EclipseState& ecl_state, SummaryState& summary_state) { SimulatorUpdate sim_update; auto apply_action_callback = [&sim_update, &reportStep, this](const std::string& action_name, const std::vector& matching_wells) { sim_update = this->applyAction(reportStep, action_name, matching_wells); }; - pyaction.run(ecl_state, *this, reportStep, summary_state, apply_action_callback); + auto result = pyaction.run(ecl_state, *this, reportStep, summary_state, apply_action_callback); + action_state.add_run(pyaction, result); return sim_update; } diff --git a/tests/EMBEDDED_PYTHON.DATA b/tests/EMBEDDED_PYTHON.DATA index 4e73a873d..75cb818ff 100644 --- a/tests/EMBEDDED_PYTHON.DATA +++ b/tests/EMBEDDED_PYTHON.DATA @@ -34,6 +34,18 @@ PYACTION WCLOSE UNLIMITED / 'wclose.py' / +PYACTION + UNLIMITED UNLIMITED / + 'wclose.py' / + +PYACTION + SINGLE SINGLE / + 'wclose.py' / + +PYACTION + FIRST_TRUE FIRST_TRUE / + 'wclose.py' / + WELSPECS 'PROD1' 'G1' 10 10 8400 'OIL' / 'PROD2' 'G1' 5 5 8400 'OIL' / diff --git a/tests/msim/MSIM_PYACTION.DATA b/tests/msim/MSIM_PYACTION.DATA index fe9e559d8..63941c7f3 100644 --- a/tests/msim/MSIM_PYACTION.DATA +++ b/tests/msim/MSIM_PYACTION.DATA @@ -447,7 +447,7 @@ PYACTION PYACTION ACTION3 FIRST_TRUE / - 'action2.py' / + 'action_count.py' / DATES 1 'JAN' 2015 / diff --git a/tests/msim/action_count.py b/tests/msim/action_count.py new file mode 100644 index 000000000..79cf3246e --- /dev/null +++ b/tests/msim/action_count.py @@ -0,0 +1,11 @@ +import math + +def run(ecl_state, schedule, report_step, summary_state, actionx_callback): + if not "run_count" in summary_state: + summary_state["run_count"] = 0 + summary_state["run_count"] += 1 + + if summary_state.elapsed() > (365 + 15)*24*3600: + return True + + return False diff --git a/tests/msim/test_msim_ACTIONX.cpp b/tests/msim/test_msim_ACTIONX.cpp index 96cc10e1c..8e217f1ed 100644 --- a/tests/msim/test_msim_ACTIONX.cpp +++ b/tests/msim/test_msim_ACTIONX.cpp @@ -524,6 +524,7 @@ BOOST_AUTO_TEST_CASE(PYTHON_WELL_CLOSE_EXAMPLE) { BOOST_CHECK(w4_11.getStatus() == Well::Status::SHUT ); } } + BOOST_CHECK_EQUAL( sim.st.get("run_count"), 13); } BOOST_AUTO_TEST_CASE(PYTHON_ACTIONX) { diff --git a/tests/parser/ACTIONX.cpp b/tests/parser/ACTIONX.cpp index 1b58c184a..bdd7147cd 100644 --- a/tests/parser/ACTIONX.cpp +++ b/tests/parser/ACTIONX.cpp @@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE(TestActions) { BOOST_CHECK(!action2.eval(context)); - const auto& python_actions = config.pending_python(); + const auto& python_actions = config.pending_python(action_state); BOOST_CHECK_EQUAL(python_actions.size(), 2U); } @@ -977,6 +977,7 @@ BOOST_AUTO_TEST_CASE(ActionState) { auto res = st.result("NAME-HIDDEN"); BOOST_CHECK(!res.has_value()); + } BOOST_AUTO_TEST_CASE(MANUAL4_QUOTE) { diff --git a/tests/parser/EmbeddedPython.cpp b/tests/parser/EmbeddedPython.cpp index 2ee33728a..e47c9b746 100644 --- a/tests/parser/EmbeddedPython.cpp +++ b/tests/parser/EmbeddedPython.cpp @@ -32,6 +32,7 @@ #include #include #include +#include using namespace Opm; @@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(PYACTION) { const std::string& fname = pyaction_kw.getRecord(1).getItem(0).get(0); Action::PyAction py_action(python, "WCLOSE", Action::PyAction::RunCount::unlimited, deck.makeDeckPath(fname)); auto actionx_callback = [] (const std::string&, const std::vector&) { ;}; - + Action::State action_state; st.update_well_var("PROD1", "WWCT", 0); py_action.run(ecl_state, schedule, 10, st, actionx_callback); @@ -141,6 +142,32 @@ BOOST_AUTO_TEST_CASE(PYACTION) { BOOST_CHECK( well1.getStatus() == Well::Status::SHUT ); BOOST_CHECK( well2.getStatus() == Well::Status::OPEN ); BOOST_CHECK( st.has("RUN_COUNT") ); + + std::map action_map; + for (const auto * p : schedule[0].actions().pending_python(action_state)) + action_map.emplace( p->name(), *p ); + + const auto& pyaction_unlimited = action_map.at("UNLIMITED"); + const auto& pyaction_single = action_map.at("SINGLE"); + const auto& pyaction_first_true = action_map.at("FIRST_TRUE"); + + auto actions = schedule[0].actions(); + BOOST_CHECK( actions.pending_python(action_state).size() == 4); + + action_state.add_run( py_action, true); + BOOST_CHECK( actions.pending_python(action_state).size() == 4); + + action_state.add_run( pyaction_unlimited, true); + BOOST_CHECK( actions.pending_python(action_state).size() == 4); + + action_state.add_run( pyaction_single, false); + BOOST_CHECK( actions.pending_python(action_state).size() == 3); + + action_state.add_run( pyaction_first_true, false); + BOOST_CHECK( actions.pending_python(action_state).size() == 3); + + action_state.add_run( pyaction_first_true, true); + BOOST_CHECK( actions.pending_python(action_state).size() == 2); }