diff --git a/CMakeLists.txt b/CMakeLists.txt index cfca587b79..913a3a03f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -873,6 +873,64 @@ macro(opm-simulators_targets_hook) OPM_COMPILE_COMPONENTS_TEMPLATE_LIST=${OPM_COMPILE_COMPONENTS_TEMPLATE_LIST} ) + opm_add_executable( + TARGET + flowexp_comp2_2p + SOURCES + flowexperimental/comp/flowexp_comp2_2p_main.cpp + $ + LIBRARIES + opmsimulators + ) + + opm_add_executable( + TARGET + flowexp_comp3_2p + SOURCES + flowexperimental/comp/flowexp_comp3_2p_main.cpp + $ + LIBRARIES + opmsimulators + ) + + opm_add_executable( + TARGET + flow_comp + ONLY_COMPILE + ALWAYS_ENABLE + DEPENDS + opmsimulators + LIBRARIES + opmsimulators + SOURCES + flowexperimental/comp/flow_comp.cpp + $ + ) + target_compile_definitions(flow_comp + PRIVATE + OPM_COMPILE_COMPONENTS_TEMPLATE_LIST=${OPM_COMPILE_COMPONENTS_TEMPLATE_LIST} + ) + + opm_add_executable( + TARGET + flow_comp2_2p + SOURCES + flowexperimental/comp/flow_comp2_2p.cpp + $ + LIBRARIES + opmsimulators + ) + + opm_add_executable( + TARGET + flow_comp3_2p + SOURCES + flowexperimental/comp/flow_comp3_2p.cpp + $ + LIBRARIES + opmsimulators + ) + if(BUILD_FLOW_FLOAT_VARIANTS) opm_add_executable( TARGET diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 5d95baed56..72448187d7 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -73,6 +73,7 @@ list (APPEND MAIN_SOURCE_FILES flowexperimental/BlackOilEnergyIntensiveQuantitiesGlobalIndex.hpp flowexperimental/BlackOilIntensiveQuantitiesGlobalIndex.hpp flowexperimental/comp/EmptyModel.hpp + flowexperimental/comp/flow_comp.hpp flowexperimental/comp/flowexp_comp.hpp flowexperimental/comp/wells/CompWellModel.hpp flowexperimental/comp/wells/CompWellModel_impl.hpp @@ -156,6 +157,8 @@ list (APPEND MAIN_SOURCE_FILES opm/simulators/flow/MixingRateControls.cpp opm/simulators/flow/NlddReporting.cpp opm/simulators/flow/NonlinearSolver.cpp + opm/simulators/flow/NonlinearSystemCompositional.hpp + opm/simulators/flow/NonlinearSystemCompositional_impl.hpp opm/simulators/flow/partitionCells.cpp opm/simulators/flow/RFTContainer.cpp opm/simulators/flow/RSTConv.cpp @@ -203,6 +206,7 @@ list (APPEND MAIN_SOURCE_FILES opm/simulators/timestepping/TimeStepControl.cpp opm/simulators/timestepping/gatherConvergenceReport.cpp opm/simulators/utils/ComponentName.cpp + opm/simulators/utils/ComponentNameFlowExp.cpp opm/simulators/utils/DeferredLogger.cpp opm/simulators/utils/FullySupportedFlowKeywords.cpp opm/simulators/utils/ParallelFileMerger.cpp @@ -993,6 +997,8 @@ list (APPEND PUBLIC_HEADER_FILES opm/simulators/flow/NonlinearSystemBlackOilReservoirTPSA.hpp opm/simulators/flow/NonlinearSystem.hpp opm/simulators/flow/NonlinearSystem_impl.hpp + opm/simulators/flow/NonlinearSystemCompositional.hpp + opm/simulators/flow/NonlinearSystemCompositional_impl.hpp opm/simulators/flow/CO2H2Container.hpp opm/simulators/flow/CollectDataOnIORank.hpp opm/simulators/flow/CollectDataOnIORank_impl.hpp diff --git a/compareECLFiles.cmake b/compareECLFiles.cmake index 0f624e104b..9e879f053a 100644 --- a/compareECLFiles.cmake +++ b/compareECLFiles.cmake @@ -81,6 +81,7 @@ function(add_test_compareECLFiles) FILENAME DEV_SIMULATOR SIMULATOR + REFERENCE_SIMULATOR ABS_TOL REL_TOL DIR @@ -112,7 +113,11 @@ 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() - list(APPEND DRIVER_ARGS -u ${PARAM_SIMULATOR}) + set(reference_simulator ${PARAM_SIMULATOR}) + if(PARAM_REFERENCE_SIMULATOR) + set(reference_simulator ${PARAM_REFERENCE_SIMULATOR}) + endif() + list(APPEND DRIVER_ARGS -u ${reference_simulator}) if(USE_DEV_SIMULATOR_IN_TESTS AND PARAM_DEV_SIMULATOR) set(PARAM_SIMULATOR ${PARAM_DEV_SIMULATOR}) endif() @@ -151,6 +156,7 @@ function(add_test_compareSeparateECLFiles) DIR2 DEV_SIMULATOR SIMULATOR + REFERENCE_SIMULATOR ABS_TOL REL_TOL IGNORE_EXTRA_KW @@ -187,7 +193,10 @@ function(add_test_compareSeparateECLFiles) if(PARAM_IGNORE_EXTRA_KW) list(APPEND DRIVER_ARGS -y ${PARAM_IGNORE_EXTRA_KW}) endif() - opm_add_test(${PARAM_PREFIX}_${PARAM_SIMULATOR}+${PARAM_CASENAME} + if(PARAM_REFERENCE_SIMULATOR) + list(APPEND DRIVER_ARGS -l ${PARAM_REFERENCE_SIMULATOR}) + endif() + opm_add_test(${PARAM_PREFIX}_${PARAM_SIMULATOR}+${PARAM_CASENAME} NO_COMPILE EXE_TARGET ${PARAM_SIMULATOR} DRIVER_ARGS @@ -676,6 +685,16 @@ add_test_runSimulator( --enable-vtk-output=true ) +add_test_runSimulator(CASENAME 1dcompositional_flowexp_comp3_2p + FILENAME 1D_COMP + SIMULATOR flowexp_comp3_2p + DIR compositional) + +add_test_runSimulator(CASENAME 1dcompositional_flow_comp3_2p + FILENAME 1D_COMP + SIMULATOR flow_comp3_2p + DIR compositional) + # Tests that are run based on simulator results, but not necessarily direct comparison to reference results add_test_runSimulator( CASENAME diff --git a/flowexperimental/comp/flow_comp.cpp b/flowexperimental/comp/flow_comp.cpp new file mode 100644 index 0000000000..1a334da446 --- /dev/null +++ b/flowexperimental/comp/flow_comp.cpp @@ -0,0 +1,119 @@ +/* + Copyright 2024, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ +#include "config.h" + +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include "flow_comp.hpp" + +template +std::tuple +runComponent(int runtimeComponent, bool water, int argc, char** argv) +{ + if (runtimeComponent == compileTimeComponent) { + if (water) + return std::make_tuple(true, Opm::dispatchFlowComp(argc, argv)); + else + return std::make_tuple(true, Opm::dispatchFlowComp(argc, argv)); + } + return std::make_tuple(false, EXIT_FAILURE); +} + + +/** + * @brief Runs a specified runtime component. + * + * This function checks if the provided runtime component matches the current compile-time component. + * If they match, it dispatches the simulator for the number of components and returns + * a tuple where the second element is the result of the execution. + * + * Otherwise, it recursively calls itself with the next component in the list. + * + * @param runtimecomponent The runtime component identifier to be executed. + * @param argc The number of command-line arguments. + * @param argv The array of command-line arguments. + * @return A tuple containing a boolean indicating if the component was executed and an integer result of the execution. + * + * @note We have two non-variadic templates to be able to properly overload for the base case. + */ +template +std::tuple +runComponent(int runtimecomponent, bool water, int argc, char** argv) +{ + if (currentCompileTimeComponent == runtimecomponent) { + if (water) + return std::make_tuple(true, Opm::dispatchFlowComp(argc, argv)); + else + return std::make_tuple(true, Opm::dispatchFlowComp(argc, argv)); + } + return runComponent(runtimecomponent, water, argc, argv); +} + +int +main(int argc, char** argv) +{ + using TypeTag = Opm::Properties::TTag::FlowCompProblem<0, true>; + Opm::registerEclTimeSteppingParameters(); + + // At the moment, this is probably as optimal as can be. + // We only read the RUNSPEC of the Deck file to get the numComp, + // and for this we need to first read the CLI arguments. + Opm::setupParameters_(argc, const_cast(argv), true, false, true, 0); + + auto inputFilename + = Opm::FlowGenericVanguard::canonicalDeckPath(Opm::Parameters::Get()); + + // Only read the RUNSPEC section of the deck + const auto deck + = Opm::Parser {}.parseFile(inputFilename, Opm::ParseContext {}, std::vector {Opm::Ecl::SectionType::RUNSPEC}); + const auto runspec = Opm::Runspec(deck); + const auto numComps = runspec.numComps(); + const auto& phases = runspec.phases(); + const auto wat = phases.active(Opm::Phase::WATER); + + Opm::Parameters::reset(); + + auto [componentSupported, executionStatus] + = runComponent(numComps, wat, argc, argv); + + if (!componentSupported) { + fmt::print("Deck has {} components, not supported. In this build of the simulator, we support the " + "following number of components:\n\n\t{}\n\nNote that the supported components can be changed " + "when configuring CMake through the OPM_COMPILE_COMPONENTS option. That is, run cmake as " + "eg\n\n\tcmake ... -DOPM_COMPILE_COMPONENTS=\"4;7\"\n\nto compile only components 4 and 7, " + "or\n\n\tcmake ... -DOPM_COMPILE_COMPONENTS=3\n\nto compile only component 3, then " + "recompile.\n\nExiting.\n", + numComps, + fmt::join(std::array {OPM_COMPILE_COMPONENTS_TEMPLATE_LIST}, ", ")); + } + return executionStatus; +} diff --git a/flowexperimental/comp/flow_comp.hpp b/flowexperimental/comp/flow_comp.hpp new file mode 100644 index 0000000000..903cf78695 --- /dev/null +++ b/flowexperimental/comp/flow_comp.hpp @@ -0,0 +1,301 @@ +/* + Copyright 2024, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ +#ifndef FLOW_COMP_HPP +#define FLOW_COMP_HPP + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +// // the current code use eclnewtonmethod adding other conditions to proceed_ should do the trick for KA +// // adding linearshe sould be chaning the update_ function in the same class with condition that the error is reduced. +// the trick is to be able to recalculate the residual from here. +// unsure where the timestepping is done from suggestedtime?? +// suggestTimeStep is taken from newton solver in problem.limitTimestep +namespace Opm { + +template +int dispatchFlowComp(int argc, char** argv); + +} // namespace Opm + +namespace Opm::Properties { + +template +struct DiscNewtonMethod; + +template +struct WellModel; + +template +struct TracerModel; + +namespace TTag { + +template +struct FlowCompProblem { + using InheritsFrom = std::tuple; +}; + +} + +template +struct SparseMatrixAdapter> +{ +private: + using Scalar = GetPropType; + enum { numEq = getPropValue() }; + using Block = MatrixBlock; + +public: + using type = typename Linear::IstlSparseMatrixAdapter; +}; + +// NOTE: Do NOT override NewtonMethod here. +// The inherited model configuration decides the solver implementation. + +#if 0 +template +struct SolidEnergyLaw +{ +private: + using Scalar = GetPropType; + using FluidSystem = GetPropType; + +public: + using EclThermalLawManager = ::Opm::EclThermalLawManager; + + using type = typename EclThermalLawManager::SolidEnergyLaw; +}; + +// Set the material law for thermal conduction +template +struct ThermalConductionLaw +{ +private: + using Scalar = GetPropType; + using FluidSystem = GetPropType; + +public: + using EclThermalLawManager = ::Opm::EclThermalLawManager; + + using type = typename EclThermalLawManager::ThermalConductionLaw; +}; + + +template +struct SpatialDiscretizationSplice +{ + using type = TTag::EcfvDiscretization; +}; + +template +struct LocalLinearizerSplice +{ + using type = TTag::AutoDiffLocalLinearizer; +}; +#endif + +// Set the problem property +template +struct Problem> +{ + using type = FlowProblemComp; +}; + +template +struct AquiferModel> { + using type = EmptyModel; +}; + +template +struct WellModel> { + using type = CompWellModel; +}; + +template +struct TracerModel> { + using type = EmptyModel; +}; + + +template +struct FlashSolver> { +private: + using Scalar = GetPropType; + using FluidSystem = GetPropType; + using Evaluation = GetPropType; + +public: + using type = Opm::PTFlash; +}; + + +template +struct NumComp { using type = UndefinedProperty; }; + +// TODO: this is unfortunate, have to check why we need to hard-code it +template +struct NumComp> { + static constexpr int value = NumComp_; +}; + +template +struct EnableDummyWater { using type = UndefinedProperty; }; + +template +struct EnableDummyWater> { + static constexpr bool value = EnableWater_; +}; +#if 0 +struct Temperature { using type = UndefinedProperty; }; + + template + struct Temperature { + using type = GetPropType; + static constexpr type value = 423.25; + }; +#endif + +template +struct FluidSystem> +{ +private: + using Scalar = GetPropType; + static constexpr int num_comp = getPropValue(); + static constexpr bool enable_water = getPropValue(); + +public: + using type = Opm::GenericOilGasWaterFluidSystem; +}; +template +struct EnableMech> { + static constexpr bool value = false; +}; + +template +struct EnableDisgasInWater> { + static constexpr bool value = false; +}; + +template +struct Stencil> +{ +private: + using Scalar = GetPropType; + using GridView = GetPropType; + +public: + using type = EcfvStencil; +}; + +template +struct EnableApiTracking> { + static constexpr bool value = false; +}; + +template +struct EnableSaltPrecipitation> { + static constexpr bool value = false; +}; +template +struct EnablePolymerMW> { + static constexpr bool value = false; +}; + +template +struct EnablePolymer> { + static constexpr bool value = false; +}; + +template +struct EnableDispersion> { + static constexpr bool value = false; +}; + +template +struct EnableBrine> { + static constexpr bool value = false; +}; +template +struct EnableVapwat> { + static constexpr bool value = false; +}; + +template +struct EnableSolvent> { + static constexpr bool value = false; +}; +template +struct EnableFoam> { + static constexpr bool value = false; +}; +template +struct EnableExtbo> { + static constexpr bool value = false; +}; +template +struct EnableBioeffects> { + static constexpr bool value = false; +}; + +template +struct EnableDebuggingChecks>{ + static constexpr bool value = false; +}; + +// disable thermal flux boundaries by default +#if 0 +template +struct EnableThermalFluxBoundaries { + static constexpr bool value = false; +}; +#endif + +} // namespace Opm::Properties + +namespace Opm { + +template +int dispatchFlowComp(int argc, char** argv) +{ + using TypeTag = Properties::TTag::FlowCompProblem; + auto mainObject = std::make_unique(argc, argv); + const auto ret = mainObject->runStatic(); + mainObject.reset(); + return ret; +} + +} // namespace Opm + +#endif diff --git a/flowexperimental/comp/flow_comp2_2p.cpp b/flowexperimental/comp/flow_comp2_2p.cpp new file mode 100644 index 0000000000..a819b1a54b --- /dev/null +++ b/flowexperimental/comp/flow_comp2_2p.cpp @@ -0,0 +1,29 @@ +/* + Copyright 2024, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include "config.h" + +#include + +#include "flow_comp.hpp" + +int main(int argc, char** argv) +{ + return Opm::dispatchFlowComp<2, false>(argc, argv); +} diff --git a/flowexperimental/comp/flow_comp3_2p.cpp b/flowexperimental/comp/flow_comp3_2p.cpp new file mode 100644 index 0000000000..3c240636db --- /dev/null +++ b/flowexperimental/comp/flow_comp3_2p.cpp @@ -0,0 +1,29 @@ +/* + Copyright 2024, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include "config.h" + +#include + +#include "flow_comp.hpp" + +int main(int argc, char** argv) +{ + return Opm::dispatchFlowComp<3, false>(argc, argv); +} diff --git a/flowexperimental/comp/flowexp_comp2_2p_main.cpp b/flowexperimental/comp/flowexp_comp2_2p_main.cpp new file mode 100644 index 0000000000..7a9be82c39 --- /dev/null +++ b/flowexperimental/comp/flowexp_comp2_2p_main.cpp @@ -0,0 +1,36 @@ +/* + Copyright 2024, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include "config.h" + +#include + +#include + +#include "flowexp_comp.hpp" + +int main(int argc, char** argv) +{ + using TypeTag = Opm::Properties::TTag::FlowExpCompProblem<2, false>; + + Opm::registerEclTimeSteppingParameters(); + Opm::setupParameters_(argc, const_cast(argv), true, false, true, 0); + + return Opm::start(argc, argv, false); +} diff --git a/flowexperimental/comp/flowexp_comp3_2p_main.cpp b/flowexperimental/comp/flowexp_comp3_2p_main.cpp new file mode 100644 index 0000000000..49dc4101fb --- /dev/null +++ b/flowexperimental/comp/flowexp_comp3_2p_main.cpp @@ -0,0 +1,39 @@ +/* + Copyright 2024, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include "config.h" + +#include + +#include +#include + +#include "flowexp_comp.hpp" + +int main(int argc, char** argv) +{ + using TypeTag = Opm::Properties::TTag::FlowExpCompProblem<3, false>; + + Opm::Parameters::Register + ("Use adaptive time stepping between report steps"); + Opm::registerEclTimeSteppingParameters(); + Opm::setupParameters_(argc, const_cast(argv), true, false, true, 0); + + return Opm::start(argc, argv, false); +} diff --git a/flowexperimental/comp/wells/CompWellModel.hpp b/flowexperimental/comp/wells/CompWellModel.hpp index 5bf9d82be0..199d23f8ca 100644 --- a/flowexperimental/comp/wells/CompWellModel.hpp +++ b/flowexperimental/comp/wells/CompWellModel.hpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -35,7 +36,10 @@ #include +#include + +#include #include namespace Opm { @@ -44,7 +48,7 @@ class Schedule; struct NewtonIterationContext; template -class CompWellModel : WellConnectionAuxiliaryModule> +class CompWellModel : public WellConnectionAuxiliaryModule> { using ElementContext = GetPropType; using FluidSystem = GetPropType; @@ -96,10 +100,11 @@ public: void beginReportStep(unsigned report_step); void beginTimeStep(); void beginIteration(); + void restoreLastValidState(); void init(); void endIteration() const {} - void endTimeStep() {} + void endTimeStep(); void endEpisode() {} void computeTotalRatesForDof(RateVector& /*rate*/, unsigned /*globalIdx*/) const; @@ -126,6 +131,19 @@ public: auto begin() const { return well_container_.begin(); } auto end() const { return well_container_.end(); } + const SimulatorReportSingle& lastReport() const { return last_report_; } + void prepareDeserialize(const int) {} + const std::map& wellOpenTimes() const { return well_open_times_; } + const std::map& wellCloseTimes() const { return well_close_times_; } + const WellGroupEvents& reportStepStartEvents() const { return report_step_start_events_; } + bool forceShutWellByName(const std::string& well_name, double simulation_time, bool dont_shut_grup_wells); + + template + void setReservoirCouplingSlave(ReservoirCouplingSlave*) {} + + template + void setReservoirCouplingMaster(ReservoirCouplingMaster*) {} + bool getWellConvergence() const; // the following functions are not used while added to avoid modifying WellConnectionAuxiliaryModule.hpp @@ -146,6 +164,9 @@ private: // we will need two to handle the changes between time stepping CompWellState comp_well_states_; + // saved state at beginning of report step, used to restore on failed timestep + CompWellState last_valid_comp_well_states_; + // this is needed for parallel running, not all the wells will be in the same process std::vector wells_ecl_; std::vector > well_connection_data_; @@ -155,6 +176,10 @@ private: mutable BVector x_local_; std::size_t local_num_cells_{0}; + SimulatorReportSingle last_report_{}; + std::map well_open_times_; + std::map well_close_times_; + WellGroupEvents report_step_start_events_{}; void createWellContainer(); void initWellContainer(); diff --git a/flowexperimental/comp/wells/CompWellModel_impl.hpp b/flowexperimental/comp/wells/CompWellModel_impl.hpp index 6090e5121b..3299c37969 100644 --- a/flowexperimental/comp/wells/CompWellModel_impl.hpp +++ b/flowexperimental/comp/wells/CompWellModel_impl.hpp @@ -26,6 +26,8 @@ #include #endif +#include + #include #include #include @@ -42,6 +44,7 @@ CompWellModel::CompWellModel(Simulator& simulator, const NewtonIteratio , comm_(simulator.gridView().comm()) , comp_config_(ecl_state_.compositionalConfig()) , comp_well_states_(comp_config_) + , last_valid_comp_well_states_(comp_config_) { local_num_cells_ = simulator.gridView().size(0); } @@ -52,9 +55,35 @@ CompWellModel:: beginReportStep(unsigned report_step) { // TODO: not considering the parallel running yet + report_step_start_events_ = schedule_[report_step].wellgroup_events(); wells_ecl_ = schedule_.getWells(report_step); + + constexpr auto events_mask = ScheduleEvents::WELL_STATUS_CHANGE | + ScheduleEvents::REQUEST_OPEN_WELL | + ScheduleEvents::REQUEST_SHUT_WELL; + for (const auto& well_ecl : wells_ecl_) { + if (!well_ecl.hasConnections()) { + continue; + } + + if (!report_step_start_events_.hasEvent(well_ecl.name(), events_mask)) { + continue; + } + + if (well_ecl.getStatus() == WellStatus::OPEN) { + well_open_times_.insert_or_assign(well_ecl.name(), simulator_.time()); + well_close_times_.erase(well_ecl.name()); + } + else if (well_ecl.getStatus() == WellStatus::SHUT) { + well_close_times_.insert_or_assign(well_ecl.name(), simulator_.time()); + well_open_times_.erase(well_ecl.name()); + } + } + initWellConnectionData(); initWellState(); + // Save the initial accepted state for this report step. + last_valid_comp_well_states_.copyDynamicStateFrom(comp_well_states_); } template @@ -66,6 +95,24 @@ beginTimeStep() initWellContainer(); } +template +void +CompWellModel:: +restoreLastValidState() +{ + comp_well_states_.copyDynamicStateFrom(last_valid_comp_well_states_); +} + +template +void +CompWellModel:: +endTimeStep() +{ + // Persist the accepted well state so failed retries restart from the last + // successful timestep rather than from the beginning of the report step. + last_valid_comp_well_states_.copyDynamicStateFrom(comp_well_states_); +} + template void CompWellModel:: @@ -83,6 +130,12 @@ createWellContainer() const auto nw = wells_ecl_.size(); // not considering the parallel running yet well_container_.clear(); for (auto w = 0 * nw; w < nw; ++w) { + const auto& well_name = wells_ecl_[w].name(); + if (comp_well_states_.has(well_name) + && comp_well_states_[well_name].status == WellStatus::SHUT) { + continue; + } + well_container_.emplace_back(std::make_shared>(wells_ecl_[w], w, well_connection_data_[w])); } } @@ -180,11 +233,13 @@ initWellState() OPM_END_PARALLEL_TRY_CATCH("ComposotionalWellModel::initializeWellState() failed: ", this->simulator_.vanguard().grid().comm()); - /* TODO: no prev well state for now */ - // \Note:: we are not supporting dynamic temperature, so the temperature is constant, so we just pass in a single value + // Start each report step from freshly initialized schedule state. Retry + // recovery still comes from last_valid_comp_well_states_ via + // restoreLastValidState()/endTimeStep(). this->comp_well_states_.init(this->wells_ecl_, cell_pressure, cell_temperature[0], cell_mole_fractions, this->well_connection_data_, - this->summary_state_); + this->summary_state_, + nullptr);//better but change result &this->last_valid_comp_well_states_); } @@ -293,6 +348,38 @@ recoverWellSolutionAndUpdateWellState(const BVector& x) } } +template +bool +CompWellModel:: +forceShutWellByName(const std::string& well_name, + double simulation_time, + bool) +{ + int well_was_shut = 0; + + if (comp_well_states_.has(well_name)) { + auto& well_state = comp_well_states_[well_name]; + if (well_state.status != WellStatus::SHUT) { + well_state.status = WellStatus::SHUT; + well_close_times_.insert_or_assign(well_name, simulation_time); + well_open_times_.erase(well_name); + + if (last_valid_comp_well_states_.has(well_name)) { + last_valid_comp_well_states_[well_name].status = WellStatus::SHUT; + } + + std::erase_if(well_container_, + [&well_name](const auto& well) + { return well->name() == well_name; }); + + well_was_shut = 1; + } + } + + well_was_shut = comm_.max(well_was_shut); + return well_was_shut == 1; +} + template bool CompWellModel:: diff --git a/flowexperimental/comp/wells/CompWellState.hpp b/flowexperimental/comp/wells/CompWellState.hpp index bcd705ef8a..4ac55539a8 100644 --- a/flowexperimental/comp/wells/CompWellState.hpp +++ b/flowexperimental/comp/wells/CompWellState.hpp @@ -57,8 +57,17 @@ public: SingleWellState& operator[](const std::string& well_name); + bool has(const std::string& well_name) const + { return wells_.has(well_name); } + data::Wells report() const; + // Copy dynamic well variables while keeping each instance's own config reference. + void copyDynamicStateFrom(const CompWellState& other) + { + wells_ = other.wells_; + } + private: WellContainer wells_; diff --git a/flowexperimental/comp/wells/CompWellState_impl.hpp b/flowexperimental/comp/wells/CompWellState_impl.hpp index c2231c108e..ac1082ea8a 100644 --- a/flowexperimental/comp/wells/CompWellState_impl.hpp +++ b/flowexperimental/comp/wells/CompWellState_impl.hpp @@ -35,12 +35,19 @@ init(const std::vector& wells_ecl, const std::vector>& cell_mole_fractions, const std::vector >& well_connection_data, const SummaryState& summary_state, - const CompWellState* /*prev_well_state*/) + const CompWellState* prev_well_state) { this->base_init(wells_ecl, cell_pressures, temperature, cell_mole_fractions, well_connection_data, summary_state); - // TODO: for the simple case we have, I think we can just copy from the prev_well_state - // let us see how we gonna use it though + if (!prev_well_state) { + return; + } + + for (const auto& well_name : this->wells_.wells()) { + if (prev_well_state->wells_.has(well_name)) { + this->wells_[well_name].copyRuntimeStateFrom(prev_well_state->wells_[well_name]); + } + } } template diff --git a/flowexperimental/comp/wells/CompWell_impl.hpp b/flowexperimental/comp/wells/CompWell_impl.hpp index a699d2d5c9..a2e9610314 100644 --- a/flowexperimental/comp/wells/CompWell_impl.hpp +++ b/flowexperimental/comp/wells/CompWell_impl.hpp @@ -17,6 +17,9 @@ along with OPM. If not, see . */ +#include +#include + #include #include @@ -563,6 +566,8 @@ updateWellControl(const SummaryState& summary_state, if (injection_controls.hasControl(Well::InjectorCMode::BHP) && current_control != WellInjectorCMode::BHP) { const Scalar bhp_limit = injection_controls.bhp_limit; const Scalar current_bhp = well_state.bhp; + OpmLog::debug(fmt::format("Well {} BHP control check: current_bhp={:.6e}, bhp_limit={:.6e}, exceeds_limit={}", + this->well_ecl_.name(), current_bhp, bhp_limit, (current_bhp > bhp_limit))); if (current_bhp > bhp_limit) { well_state.bhp = bhp_limit; well_state.injection_cmode = WellInjectorCMode::BHP; @@ -575,7 +580,12 @@ updateWellControl(const SummaryState& summary_state, // TODO: hack to get the injection rate const Scalar current_rate = std::accumulate(well_state.surface_phase_rates.begin(), well_state.surface_phase_rates.end(), 0.0); + OpmLog::debug(fmt::format("Well {} RATE control check: current_rate={:.6e}, rate_limit={:.6e}, bhp={:.6e}, phase_rates=[{}]", + this->well_ecl_.name(), current_rate, rate_limit, well_state.bhp, + fmt::join(well_state.surface_phase_rates, ", "))); if (current_rate > rate_limit) { + OpmLog::info(fmt::format("Well {} RATE control TRIGGERED: current_rate={:.6e} > rate_limit={:.6e}", + this->well_ecl_.name(), current_rate, rate_limit)); well_state.injection_cmode = WellInjectorCMode::RATE; changed = true; } diff --git a/flowexperimental/comp/wells/SingleCompWellState.hpp b/flowexperimental/comp/wells/SingleCompWellState.hpp index 093fceb84f..e802bc452f 100644 --- a/flowexperimental/comp/wells/SingleCompWellState.hpp +++ b/flowexperimental/comp/wells/SingleCompWellState.hpp @@ -94,6 +94,8 @@ public: void update_injector_targets(const Well& well, const SummaryState& st); + void copyRuntimeStateFrom(const SingleCompWellState& other); + Scalar get_total_surface_rate() const; }; diff --git a/flowexperimental/comp/wells/SingleCompWellState_impl.hpp b/flowexperimental/comp/wells/SingleCompWellState_impl.hpp index acf8bcba0d..2599f9c7ac 100644 --- a/flowexperimental/comp/wells/SingleCompWellState_impl.hpp +++ b/flowexperimental/comp/wells/SingleCompWellState_impl.hpp @@ -145,6 +145,22 @@ update_producer_targets(const Well& well, } } +template +void SingleCompWellState:: +copyRuntimeStateFrom(const SingleCompWellState& other) +{ + // Keep the freshly initialized schedule-derived status, controls, and + // injector targets from base_init(); only reuse the dynamic state. + bhp = other.bhp; + surface_phase_rates = other.surface_phase_rates; + phase_fractions = other.phase_fractions; + reservoir_phase_rates = other.reservoir_phase_rates; + if (producer) { + total_molar_fractions = other.total_molar_fractions; + phase_molar_fractions = other.phase_molar_fractions; + } +} + template typename SingleCompWellState::Scalar SingleCompWellState:: diff --git a/opm/models/utils/parametersystem.cpp b/opm/models/utils/parametersystem.cpp index 7730d7bf5e..784a456377 100644 --- a/opm/models/utils/parametersystem.cpp +++ b/opm/models/utils/parametersystem.cpp @@ -453,6 +453,23 @@ void Hide_(const std::string& paramName) paramInfo.isHidden = true; } +bool HideIfRegistered_(const std::string& paramName) +{ + if (!MetaData::registrationOpen()) { + throw std::logic_error("Parameter '" + paramName + "' declared as hidden" + " when parameter registration was already closed."); + } + + auto paramInfoIt = MetaData::mutableRegistry().find(paramName); + if (paramInfoIt == MetaData::mutableRegistry().end()) { + return false; + } + + auto& paramInfo = paramInfoIt->second; + paramInfo.isHidden = true; + return true; +} + bool IsSet_(const std::string& paramName, bool errorIfNotRegistered) { if (errorIfNotRegistered) { diff --git a/opm/models/utils/parametersystem.hpp b/opm/models/utils/parametersystem.hpp index 1e4a4ddfc5..ca71902efa 100644 --- a/opm/models/utils/parametersystem.hpp +++ b/opm/models/utils/parametersystem.hpp @@ -78,6 +78,9 @@ ParamType Get_(const std::string& paramName, ParamType defaultValue, //! \brief Private implementation. void Hide_(const std::string& paramName); +//! \brief Private implementation. +bool HideIfRegistered_(const std::string& paramName); + //! \brief Private implementation. bool IsSet_(const std::string& paramName, bool errorIfNotRegistered); @@ -313,6 +316,17 @@ void Hide() detail::Hide_(detail::getParamName()); } +/*! + * \brief Indicate that a given parameter should not be mentioned in the help message if present. + * + * This is useful for parameters that are only registered for some models or output modules. + */ +template +bool HideIfRegistered() +{ + return detail::HideIfRegistered_(detail::getParamName()); +} + /*! * \brief Query whether parameter registration is open or not. * \return True if registration is open, false otherwise diff --git a/opm/simulators/flow/FlowGenericProblem.cpp b/opm/simulators/flow/FlowGenericProblem.cpp index 45b64eab30..76e076c601 100644 --- a/opm/simulators/flow/FlowGenericProblem.cpp +++ b/opm/simulators/flow/FlowGenericProblem.cpp @@ -29,6 +29,7 @@ #include #include +#include #include @@ -40,29 +41,81 @@ namespace Opm { +using CpLeafGridView = Dune::GridView>; + #define INSTANTIATE_TYPE(T) \ template class FlowGenericProblem< \ - Dune::GridView< \ - Dune::DefaultLeafGridViewTraits>, \ + CpLeafGridView, \ BlackOilFluidSystem>; +#define INSTANTIATE_COMP_TYPE(T, N, W) \ + template class FlowGenericProblem< \ + CpLeafGridView, \ + GenericOilGasWaterFluidSystem>; + +#define INSTANTIATE_COMP_VARIANTS(T, N) \ + INSTANTIATE_COMP_TYPE(T, N, false) \ + INSTANTIATE_COMP_TYPE(T, N, true) + +#define INSTANTIATE_FLOW_GENERIC_COMPONENTS(T) \ + INSTANTIATE_COMP_VARIANTS(T, 2) \ + INSTANTIATE_COMP_VARIANTS(T, 3) \ + INSTANTIATE_COMP_VARIANTS(T, 4) \ + INSTANTIATE_COMP_VARIANTS(T, 5) \ + INSTANTIATE_COMP_VARIANTS(T, 6) \ + INSTANTIATE_COMP_VARIANTS(T, 7) \ + INSTANTIATE_COMP_VARIANTS(T, 8) \ + INSTANTIATE_COMP_VARIANTS(T, 9) \ + INSTANTIATE_COMP_VARIANTS(T, 10) + INSTANTIATE_TYPE(double) +INSTANTIATE_FLOW_GENERIC_COMPONENTS(double) #if FLOW_INSTANTIATE_FLOAT INSTANTIATE_TYPE(float) +INSTANTIATE_FLOW_GENERIC_COMPONENTS(float) #endif #if HAVE_DUNE_FEM using GV = Dune::Fem::AdaptiveLeafGridPart; + +#define INSTANTIATE_DUNE_FEM_COMP_TYPE(T, N, W) \ +template class FlowGenericProblem>; + +#define INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, N) \ + INSTANTIATE_DUNE_FEM_COMP_TYPE(T, N, false) \ + INSTANTIATE_DUNE_FEM_COMP_TYPE(T, N, true) + +#define INSTANTIATE_DUNE_FEM_FLOW_GENERIC_COMPONENTS(T) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 2) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 3) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 4) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 5) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 6) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 7) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 8) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 9) \ + INSTANTIATE_DUNE_FEM_COMP_VARIANTS(T, 10) + template class FlowGenericProblem>; +INSTANTIATE_DUNE_FEM_FLOW_GENERIC_COMPONENTS(double) #if FLOW_INSTANTIATE_FLOAT template class FlowGenericProblem>; +INSTANTIATE_DUNE_FEM_FLOW_GENERIC_COMPONENTS(float) #endif +#undef INSTANTIATE_DUNE_FEM_FLOW_GENERIC_COMPONENTS +#undef INSTANTIATE_DUNE_FEM_COMP_VARIANTS +#undef INSTANTIATE_DUNE_FEM_COMP_TYPE + #endif // HAVE_DUNE_FEM +#undef INSTANTIATE_FLOW_GENERIC_COMPONENTS +#undef INSTANTIATE_COMP_VARIANTS + } // end namespace Opm diff --git a/opm/simulators/flow/FlowProblemComp.hpp b/opm/simulators/flow/FlowProblemComp.hpp index 85b9ef659c..7087efe4e1 100644 --- a/opm/simulators/flow/FlowProblemComp.hpp +++ b/opm/simulators/flow/FlowProblemComp.hpp @@ -424,6 +424,18 @@ public: EclWriterType& eclWriter() { return *eclWriter_; } + void setSubStepReport(const SimulatorReportSingle& report) + { return eclWriter_->setSubStepReport(report); } + + void setSimulationReport(const SimulatorReport& report) + { return eclWriter_->setSimulationReport(report); } + + void finalizeOutput() + { + OPM_TIMEBLOCK(finalizeOutput); + eclWriter_.reset(); + } + // TODO: do we need this one? template void serializeOp(Serializer& serializer) diff --git a/opm/simulators/flow/FlowProblemCompProperties.hpp b/opm/simulators/flow/FlowProblemCompProperties.hpp index 17128bf019..d671d368b6 100644 --- a/opm/simulators/flow/FlowProblemCompProperties.hpp +++ b/opm/simulators/flow/FlowProblemCompProperties.hpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -51,6 +52,10 @@ struct FlowBaseProblemComp { }; } +template +struct NonlinearSystem +{ using type = NonlinearSystemCompositional; }; + // Set the problem property template struct Problem diff --git a/opm/simulators/flow/FlowUtils.cpp b/opm/simulators/flow/FlowUtils.cpp index f982991bb0..716fb8c530 100644 --- a/opm/simulators/flow/FlowUtils.cpp +++ b/opm/simulators/flow/FlowUtils.cpp @@ -215,47 +215,52 @@ void hideUnusedParameters() // the default eWoms checkpoint/restart mechanism does not work with flow Parameters::Hide>(); Parameters::Hide(); - // hide all vtk related it is not currently possible to do this dependet on if the vtk writing is used - //if(not(Parameters::Get())){ - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - //} - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); - Parameters::Hide(); + // hide all vtk related it is not currently possible to do this dependet on if the vtk writing + // is used + // if(not(Parameters::Get())){ + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + //} + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); + Parameters::HideIfRegistered(); // hide average density option - Parameters::Hide(); + Parameters::HideIfRegistered(); } namespace { diff --git a/opm/simulators/flow/NonlinearSolver.cpp b/opm/simulators/flow/NonlinearSolver.cpp index 8203f01ac5..c2a194c30e 100644 --- a/opm/simulators/flow/NonlinearSolver.cpp +++ b/opm/simulators/flow/NonlinearSolver.cpp @@ -120,7 +120,12 @@ using BV = Dune::BlockVector>; INSTANTIATE(T,3) \ INSTANTIATE(T,4) \ INSTANTIATE(T,5) \ - INSTANTIATE(T,6) + INSTANTIATE(T,6) \ + INSTANTIATE(T,7) \ + INSTANTIATE(T,8) \ + INSTANTIATE(T,9) \ + INSTANTIATE(T,10) \ + INSTANTIATE(T,11) INSTANTIATE_TYPE(double) diff --git a/opm/simulators/flow/NonlinearSystemCompositional.hpp b/opm/simulators/flow/NonlinearSystemCompositional.hpp new file mode 100644 index 0000000000..2c883278d2 --- /dev/null +++ b/opm/simulators/flow/NonlinearSystemCompositional.hpp @@ -0,0 +1,122 @@ +/* + Copyright 2026, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_NONLINEAR_SYSTEM_COMPOSITIONAL_HEADER_INCLUDED +#define OPM_NONLINEAR_SYSTEM_COMPOSITIONAL_HEADER_INCLUDED + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +namespace Opm { + +template +class NonlinearSystemCompositional : public NonlinearSystem +{ +public: + using ParentType = NonlinearSystem; + using Simulator = typename ParentType::Simulator; + using Grid = typename ParentType::Grid; + using FluidSystem = typename ParentType::FluidSystem; + using Indices = typename ParentType::Indices; + using Scalar = typename ParentType::Scalar; + using ComponentName = typename ParentType::ComponentName; + using SparseMatrixAdapter = GetPropType; + using ModelParameters = BlackoilModelParameters; + + static constexpr int numEq = Indices::numEq; + + using VectorBlockType = Dune::FieldVector; + using BVector = Dune::BlockVector; + + NonlinearSystemCompositional(Simulator& simulator, + const ModelParameters& param, + CompWellModel& wellModel, + bool terminalOutput); + + SimulatorReportSingle prepareStep(const SimulatorTimerInterface& timer); + + void initialLinearization(SimulatorReportSingle& report, + int minIter, + int maxIter, + const SimulatorTimerInterface& timer) override; + + template + SimulatorReportSingle nonlinearIteration(const SimulatorTimerInterface& timer, + NonlinearSolverType& nonlinearSolver); + + template + SimulatorReportSingle nonlinearIterationNewton(const SimulatorTimerInterface& timer, + NonlinearSolverType& nonlinearSolver); + + Scalar relativeChange() const; + + int linearIterationsLastSolve() const + { return this->simulator_.model().newtonMethod().linearSolver().iterations(); } + + void solveJacobianSystem(BVector& x); + + bool hasNlddSolver() const + { return false; } + + const SimulatorReport& localAccumulatedReports() const + { + static const SimulatorReport emptyReport{}; + return emptyReport; + } + + const std::vector& domainAccumulatedReports() const + { + static const std::vector emptyReports{}; + return emptyReports; + } + + void writeNonlinearIterationsPerCell(const std::filesystem::path&) const {} + + template + std::vector> computeFluidInPlace(const T&, const std::vector& fipnum) const + { return computeFluidInPlace(fipnum); } + + std::vector> computeFluidInPlace(const std::vector&) const + { return {}; } + + void writePartitions(const std::filesystem::path&) const {} + +private: + std::vector reservoirResidualMetrics() const; + + double linear_solve_setup_time_ = 0.0; +}; + +} // namespace Opm + +#include + +#endif // OPM_NONLINEAR_SYSTEM_COMPOSITIONAL_HEADER_INCLUDED diff --git a/opm/simulators/flow/NonlinearSystemCompositional_impl.hpp b/opm/simulators/flow/NonlinearSystemCompositional_impl.hpp new file mode 100644 index 0000000000..db941b124d --- /dev/null +++ b/opm/simulators/flow/NonlinearSystemCompositional_impl.hpp @@ -0,0 +1,335 @@ +/* + Copyright 2026, SINTEF Digital + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_NONLINEAR_SYSTEM_COMPOSITIONAL_IMPL_HEADER_INCLUDED +#define OPM_NONLINEAR_SYSTEM_COMPOSITIONAL_IMPL_HEADER_INCLUDED + +#ifndef OPM_NONLINEAR_SYSTEM_COMPOSITIONAL_HEADER_INCLUDED +#include +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +namespace Opm { + +template +NonlinearSystemCompositional:: +NonlinearSystemCompositional(Simulator& simulator, + const ModelParameters& param, + CompWellModel& wellModel, + const bool terminalOutput) + : ParentType(simulator, param, wellModel, terminalOutput) +{ + this->convergence_reports_.reserve(64); +} + +template +SimulatorReportSingle +NonlinearSystemCompositional:: +prepareStep(const SimulatorTimerInterface& timer) +{ + SimulatorReportSingle report; + Dune::Timer perfTimer; + perfTimer.start(); + + const int lastStepFailed = timer.lastStepFailed(); + if (this->grid_.comm().size() > 1 + && this->grid_.comm().max(lastStepFailed) != this->grid_.comm().min(lastStepFailed)) { + OPM_THROW(std::runtime_error, + "Misalignment of the parallel simulation run in prepareStep " + "- the previous step succeeded on some ranks but failed on others."); + } + + if (lastStepFailed) { + this->wellModel().restoreLastValidState(); + this->simulator_.model().updateFailed(); + } + else { + this->simulator_.model().advanceTimeLevel(); + } + + this->simulator_.setTime(timer.simulationTimeElapsed()); + this->simulator_.setTimeStepSize(timer.currentStepLength()); + + this->simulator_.problem().resetIterationForNewTimestep(); + this->simulator_.problem().beginTimeStep(); + + report.pre_post_time += perfTimer.stop(); + return report; +} + +template +void +NonlinearSystemCompositional:: +initialLinearization(SimulatorReportSingle& report, + const int minIter, + const int maxIter, + const SimulatorTimerInterface& timer) +{ + ParentType::initialLinearization(report, + minIter, + maxIter, + timer); + Dune::Timer perfTimer; + perfTimer.start(); + + auto convrep = ConvergenceReport{timer.simulationTimeElapsed()}; + const auto residualMetrics = this->reservoirResidualMetrics(); + const auto tolerance = this->simulator_.model().newtonMethod().tolerance(); + + for (int compIdx = 0; compIdx < numEq; ++compIdx) { + const std::array residual{residualMetrics[compIdx]}; + const std::array types{ + ConvergenceReport::ReservoirFailure::Type::MassBalance + }; + const std::array tolerances{tolerance}; + + this->addReservoirConvergenceMetrics( + convrep, + compIdx, + this->compNames_.name(compIdx), + residual, + types, + tolerances, + this->param_.max_residual_allowed_, + [this](const std::string& message) + { + if (this->terminal_output_) { + OpmLog::debug(message); + } + }); + } + + const auto severity = convrep.severityOfWorstFailure(); + const bool wellConverged = this->wellModel().getWellConvergence(); + report.converged = convrep.converged() && wellConverged && + this->simulator_.problem().iterationContext().iteration() >= minIter; + + this->convergence_reports_.back().report.push_back(std::move(convrep)); + report.update_time += perfTimer.stop(); + this->residual_norms_history_.push_back(residualMetrics); + + if (severity == ConvergenceReport::Severity::NotANumber) { + this->failureReport_ += report; + OPM_THROW_PROBLEM(NumericalProblem, "NaN residual found!"); + } + + if (severity == ConvergenceReport::Severity::TooLarge) { + this->failureReport_ += report; + OPM_THROW_NOLOG(NumericalProblem, "Too large residual found!"); + } +} + +template +template +SimulatorReportSingle +NonlinearSystemCompositional:: +nonlinearIteration(const SimulatorTimerInterface& timer, + NonlinearSolverType& nonlinearSolver) +{ + if (this->simulator_.problem().iterationContext().needsTimestepInit()) { + this->residual_norms_history_.clear(); + this->current_relaxation_ = 1.0; + this->dx_old_ = 0.0; + this->convergence_reports_.push_back({timer.reportStepNum(), timer.currentStepNum(), {}}); + this->convergence_reports_.back().report.reserve(numEq); + } + + auto result = this->nonlinearIterationNewton(timer, nonlinearSolver); + this->simulator_.problem().advanceIteration(); + return result; +} + +template +template +SimulatorReportSingle +NonlinearSystemCompositional:: +nonlinearIterationNewton(const SimulatorTimerInterface& timer, + NonlinearSolverType& nonlinearSolver) +{ + OPM_TIMEFUNCTION(); + + SimulatorReportSingle report; + Dune::Timer perfTimer; + + this->initialLinearization(report, + this->param_.newton_min_iter_, + this->param_.newton_max_iter_, + timer); + + if (!report.converged) { + perfTimer.reset(); + perfTimer.start(); + report.total_newton_iterations = 1; + + BVector x(this->simulator_.model().numGridDof()); + this->linear_solve_setup_time_ = 0.0; + + try { + auto& linearizer = this->simulator_.model().linearizer(); + linearizer.linearizeAuxiliaryEquations(); + linearizer.finalize(); + + this->solveJacobianSystem(x); + + report.linear_solve_setup_time += this->linear_solve_setup_time_; + report.linear_solve_time += perfTimer.stop(); + report.total_linear_iterations += this->linearIterationsLastSolve(); + } + catch (...) { + report.linear_solve_setup_time += this->linear_solve_setup_time_; + report.linear_solve_time += perfTimer.stop(); + report.total_linear_iterations += this->linearIterationsLastSolve(); + + this->failureReport_ += report; + throw; + } + + perfTimer.reset(); + perfTimer.start(); + + auto& model = this->simulator_.model(); + for (unsigned auxModIdx = 0; auxModIdx < model.numAuxiliaryModules(); ++auxModIdx) { + model.auxiliaryModule(auxModIdx)->postSolve(x); + } + + if (this->param_.use_update_stabilization_) { + bool isOscillate = false; + bool isStagnate = false; + nonlinearSolver.detectOscillations(this->residual_norms_history_, + this->residual_norms_history_.size() - 1, + isOscillate, + isStagnate); + + if (isOscillate) { + this->current_relaxation_ -= nonlinearSolver.relaxIncrement(); + this->current_relaxation_ = std::max(this->current_relaxation_, nonlinearSolver.relaxMax()); + + if (this->terminalOutputEnabled()) { + OpmLog::info(" Oscillating behavior detected: Relaxation set to " + + std::to_string(this->current_relaxation_)); + } + } + + nonlinearSolver.stabilizeNonlinearUpdate(x, this->dx_old_, this->current_relaxation_); + } + + this->updateSolution(x); + report.update_time += perfTimer.stop(); + } + + return report; +} + +template +typename NonlinearSystemCompositional::Scalar +NonlinearSystemCompositional:: +relativeChange() const +{ + Scalar resultDelta = 0.0; + Scalar resultDenom = 0.0; + + const auto& elemMapper = this->simulator_.model().elementMapper(); + const auto& gridView = this->simulator_.gridView(); + + for (const auto& elem : elements(gridView, Dune::Partitions::interior)) { + const unsigned globalElemIdx = elemMapper.index(elem); + const auto& priVarsNew = this->simulator_.model().solution(/*timeIdx=*/0)[globalElemIdx]; + const auto& priVarsOld = this->simulator_.model().solution(/*timeIdx=*/1)[globalElemIdx]; + + for (int pvIdx = 0; pvIdx < static_cast(priVarsNew.size()); ++pvIdx) { + const auto delta = priVarsNew[pvIdx] - priVarsOld[pvIdx]; + resultDelta += delta * delta; + resultDenom += priVarsNew[pvIdx] * priVarsNew[pvIdx]; + } + } + + resultDelta = gridView.comm().sum(resultDelta); + resultDenom = gridView.comm().sum(resultDenom); + + return resultDenom > 0.0 ? resultDelta / resultDenom : 0.0; +} + +template +void +NonlinearSystemCompositional:: +solveJacobianSystem(BVector& x) +{ + auto& jacobian = this->simulator_.model().linearizer().jacobian(); + auto& residual = this->simulator_.model().linearizer().residual(); + auto& linSolver = this->simulator_.model().newtonMethod().linearSolver(); + + x = 0.0; + + Dune::Timer perfTimer; + perfTimer.start(); + linSolver.prepare(jacobian, residual); + this->linear_solve_setup_time_ = perfTimer.stop(); + linSolver.setResidual(residual); + linSolver.getResidual(residual); + linSolver.setMatrix(jacobian); + linSolver.solve(x); +} + +template +std::vector::Scalar> +NonlinearSystemCompositional:: +reservoirResidualMetrics() const +{ + const auto& model = this->simulator_.model(); + const auto& residual = model.linearizer().residual(); + const auto& constraintsMap = model.linearizer().constraintsMap(); + + std::vector residualMetrics(numEq, 0.0); + + for (unsigned dofIdx = 0; dofIdx < residual.size(); ++dofIdx) { + if (dofIdx >= model.numGridDof() || model.dofTotalVolume(dofIdx) <= 0.0) { + continue; + } + + if (constraintsMap.count(dofIdx) > 0) { + continue; + } + + const auto& localResidual = residual[dofIdx]; + for (int eqIdx = 0; eqIdx < numEq; ++eqIdx) { + residualMetrics[eqIdx] = std::max( + residualMetrics[eqIdx], + std::abs(localResidual[eqIdx] * model.eqWeight(dofIdx, eqIdx))); + } + } + + if (this->grid_.comm().size() > 1 && !residualMetrics.empty()) { + this->grid_.comm().max(residualMetrics.data(), residualMetrics.size()); + } + + return residualMetrics; +} + +} // namespace Opm + +#endif // OPM_NONLINEAR_SYSTEM_COMPOSITIONAL_IMPL_HEADER_INCLUDED diff --git a/opm/simulators/flow/OutputCompositionalModule.hpp b/opm/simulators/flow/OutputCompositionalModule.hpp index 841ec68cd7..8577ec4568 100644 --- a/opm/simulators/flow/OutputCompositionalModule.hpp +++ b/opm/simulators/flow/OutputCompositionalModule.hpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -52,6 +53,9 @@ #include #include +#include +#include +#include #include #include #include @@ -90,6 +94,12 @@ class OutputCompositionalModule : public GenericOutputBlackoilModule OutputCompositionalModule(const Simulator& simulator, const SummaryConfig& smryCfg, @@ -181,6 +191,92 @@ public: BaseType::assignToSolution(sol); } + void outputFipAndResvLog(const Inplace& inplace, + const std::size_t reportStepNum, + double elapsed, + boost::posix_time::ptime currentDate, + const bool substep, + const Parallel::Communication& comm) + { + if (comm.rank() != 0) { + return; + } + + std::unique_ptr fipSched; + if (reportStepNum > 0) { + const auto& rpt = this->schedule_[reportStepNum - 1].rpt_config.get(); + fipSched = std::make_unique(rpt); + } + + const FIPConfig& fipc = reportStepNum == 0 + ? this->eclState_.getEclipseConfig().fip() + : *fipSched; + + if (!substep && !this->forceDisableFipOutput_ && fipc.output(FIPConfig::OutputField::FIELD)) { + this->logOutput_.timeStamp("BALANCE", elapsed, reportStepNum, currentDate); + + const auto& initial_inplace = *this->initialInplace(); + this->logOutput_.fip(inplace, initial_inplace, ""); + + if (fipc.output(FIPConfig::OutputField::FIPNUM)) { + this->logOutput_.fip(inplace, initial_inplace, "FIPNUM"); + + if (fipc.output(FIPConfig::OutputField::RESV)) { + this->logOutput_.fipResv(inplace, "FIPNUM"); + } + } + + if (fipc.output(FIPConfig::OutputField::FIP)) { + for (const auto& reg : this->regions_) { + if (reg.first != "FIPNUM") { + std::ostringstream ss; + ss << "BAL" << reg.first.substr(3); + this->logOutput_.timeStamp(ss.str(), elapsed, reportStepNum, currentDate); + this->logOutput_.fip(inplace, initial_inplace, reg.first); + + if (fipc.output(FIPConfig::OutputField::RESV)) { + this->logOutput_.fipResv(inplace, reg.first); + } + } + } + } + } + } + + void outputFipAndResvLogToCSV(const std::size_t reportStepNum, + const bool substep, + const Parallel::Communication& comm) + { + if (comm.rank() != 0) { + return; + } + + if ((reportStepNum == 0) && (!substep) && + (this->schedule_.initialReportConfiguration().has_value()) && + (this->schedule_.initialReportConfiguration()->contains("CSVFIP"))) { + + std::ostringstream csv_stream; + + this->logOutput_.csv_header(csv_stream); + + const auto& initial_inplace = *this->initialInplace(); + + this->logOutput_.fip_csv(csv_stream, initial_inplace, "FIPNUM"); + + for (const auto& reg : this->regions_) { + if (reg.first != "FIPNUM") { + this->logOutput_.fip_csv(csv_stream, initial_inplace, reg.first); + } + } + + const IOConfig& io = this->eclState_.getIOConfig(); + auto csv_fname = io.getOutputDir() + "/" + io.getBaseName() + ".CSV"; + + std::ofstream outputFile(csv_fname); + outputFile << csv_stream.str(); + } + } + //! \brief Setup list of active element-level data extractors void setupExtractors(const bool /*isSubStep*/, const std::size_t /*reportStepNum*/) diff --git a/opm/simulators/utils/ComponentName.cpp b/opm/simulators/utils/ComponentName.cpp index 7cecee1693..fbbe685e69 100644 --- a/opm/simulators/utils/ComponentName.cpp +++ b/opm/simulators/utils/ComponentName.cpp @@ -25,80 +25,51 @@ #include #include +#include #include #include #include +#include -#include -#include +#include + +#include "ComponentName_impl.hpp" namespace Opm { -template -ComponentName::ComponentName() - : names_(Indices::numEq) -{ - for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) { - if (!FluidSystem::phaseIsActive(phaseIdx)) { - continue; - } - - const unsigned canonicalCompIdx = FluidSystem::solventComponentIndex(phaseIdx); - names_[FluidSystem::canonicalToActiveCompIdx(canonicalCompIdx)] - = FluidSystem::componentName(canonicalCompIdx); - } - - if constexpr (Indices::enableSolvent) { - names_[Indices::solventSaturationIdx] = "Solvent"; - } - - if constexpr (Indices::enableExtbo) { - names_[Indices::zFractionIdx] = "ZFraction"; - } - - if constexpr (Indices::enablePolymer) { - names_[Indices::polymerConcentrationIdx] = "Polymer"; - } - - if constexpr (Indices::polymerMoleWeightIdx != std::numeric_limits::max()) { - assert(Indices::enablePolymer); - names_[Indices::polymerMoleWeightIdx] = "MolecularWeightP"; - } - - if constexpr (Indices::enableFullyImplicitThermal) { - names_[Indices::temperatureIdx] = "Energy"; - } - - if constexpr (Indices::numFoam == 1) { - names_[Indices::foamConcentrationIdx] = "Foam"; - } - - if constexpr (Indices::numBrine == 1) { - names_[Indices::saltConcentrationIdx] = "Brine"; - } - - if constexpr (Indices::enableMICP) { - names_[Indices::microbialConcentrationIdx] = "Microbes"; - names_[Indices::oxygenConcentrationIdx] = "Oxygen"; - names_[Indices::ureaConcentrationIdx] = "Urea"; - names_[Indices::biofilmVolumeFractionIdx] = "Biofilm"; - names_[Indices::calciteVolumeFractionIdx] = "Calcite"; - } - - if constexpr (Indices::enableBiofilm) { - names_[Indices::microbialConcentrationIdx] = "Microbes"; - names_[Indices::biofilmVolumeFractionIdx] = "Biofilm"; - } -} - - #include +#define INSTANTIATE_COMPONENT_NAME_TYPE_TAG(T, N, W, TAG) \ +template class ComponentName, \ + FlashIndices, 0>>; + +#define INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, N, W) \ + INSTANTIATE_COMPONENT_NAME_TYPE_TAG(T, N, W, FlowCompProblem) + +#define INSTANTIATE_COMPONENT_NAME_COUNTS(T, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 2, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 3, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 4, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 5, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 6, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 7, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 8, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 9, W) \ + INSTANTIATE_COMPONENT_NAME_PROBLEMS(T, 10, W) + INSTANTIATE_TYPE_INDICES(ComponentName, double) +INSTANTIATE_COMPONENT_NAME_COUNTS(double, false) +INSTANTIATE_COMPONENT_NAME_COUNTS(double, true) #if FLOW_INSTANTIATE_FLOAT INSTANTIATE_TYPE_INDICES(ComponentName, float) +INSTANTIATE_COMPONENT_NAME_COUNTS(float, false) +INSTANTIATE_COMPONENT_NAME_COUNTS(float, true) #endif +#undef INSTANTIATE_COMPONENT_NAME_COUNTS +#undef INSTANTIATE_COMPONENT_NAME_PROBLEMS +#undef INSTANTIATE_COMPONENT_NAME_TYPE_TAG + } // namespace Opm diff --git a/opm/simulators/utils/ComponentNameFlowExp.cpp b/opm/simulators/utils/ComponentNameFlowExp.cpp new file mode 100644 index 0000000000..fe0d46c86f --- /dev/null +++ b/opm/simulators/utils/ComponentNameFlowExp.cpp @@ -0,0 +1,41 @@ +#include + +#include + +#include + +#include + +#include + +#include "ComponentName_impl.hpp" + +namespace Opm { + +#define INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, N, W) \ +template class ComponentName, \ + FlashIndices, 0>>; + +#define INSTANTIATE_FLOWEXP_COMPONENT_NAME_COUNTS(T, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 2, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 3, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 4, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 5, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 6, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 7, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 8, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 9, W) \ + INSTANTIATE_FLOWEXP_COMPONENT_NAME(T, 10, W) + +INSTANTIATE_FLOWEXP_COMPONENT_NAME_COUNTS(double, false) +INSTANTIATE_FLOWEXP_COMPONENT_NAME_COUNTS(double, true) + +#if FLOW_INSTANTIATE_FLOAT +INSTANTIATE_FLOWEXP_COMPONENT_NAME_COUNTS(float, false) +INSTANTIATE_FLOWEXP_COMPONENT_NAME_COUNTS(float, true) +#endif + +#undef INSTANTIATE_FLOWEXP_COMPONENT_NAME_COUNTS +#undef INSTANTIATE_FLOWEXP_COMPONENT_NAME + +} // namespace Opm \ No newline at end of file diff --git a/opm/simulators/utils/ComponentName_impl.hpp b/opm/simulators/utils/ComponentName_impl.hpp new file mode 100644 index 0000000000..76fbb80fcc --- /dev/null +++ b/opm/simulators/utils/ComponentName_impl.hpp @@ -0,0 +1,114 @@ +#ifndef OPM_COMPONENT_NAME_IMPL_HPP +#define OPM_COMPONENT_NAME_IMPL_HPP + +#include +#include + +namespace Opm { + +template +ComponentName::ComponentName() + : names_(Indices::numEq) +{ + if constexpr (requires { + FluidSystem::solventComponentIndex(0u); + FluidSystem::canonicalToActiveCompIdx(0u); + }) { + for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) { + if (!FluidSystem::phaseIsActive(phaseIdx)) { + continue; + } + + const unsigned canonicalCompIdx = FluidSystem::solventComponentIndex(phaseIdx); + names_[FluidSystem::canonicalToActiveCompIdx(canonicalCompIdx)] + = FluidSystem::componentName(canonicalCompIdx); + } + } + else { + constexpr auto numNamedComponents = + (Indices::numEq < FluidSystem::numComponents) ? Indices::numEq : FluidSystem::numComponents; + + for (unsigned compIdx = 0; compIdx < numNamedComponents; ++compIdx) { + names_[compIdx] = FluidSystem::componentName(compIdx); + } + + if constexpr (requires { Indices::waterEnabled; Indices::water0Idx; }) { + if constexpr (Indices::waterEnabled + && (Indices::water0Idx >= 0) + && (Indices::water0Idx < Indices::numEq)) { + names_[Indices::water0Idx] = "Water"; + } + } + } + + if constexpr (requires { Indices::enableSolvent; Indices::solventSaturationIdx; }) { + if constexpr (Indices::enableSolvent) { + names_[Indices::solventSaturationIdx] = "Solvent"; + } + } + + if constexpr (requires { Indices::enableExtbo; Indices::zFractionIdx; }) { + if constexpr (Indices::enableExtbo) { + names_[Indices::zFractionIdx] = "ZFraction"; + } + } + + if constexpr (requires { Indices::enablePolymer; Indices::polymerConcentrationIdx; }) { + if constexpr (Indices::enablePolymer) { + names_[Indices::polymerConcentrationIdx] = "Polymer"; + } + } + + if constexpr (requires { Indices::polymerMoleWeightIdx; }) { + if constexpr (Indices::polymerMoleWeightIdx != std::numeric_limits::max()) { + assert(Indices::enablePolymer); + names_[Indices::polymerMoleWeightIdx] = "MolecularWeightP"; + } + } + + if constexpr (requires { Indices::enableFullyImplicitThermal; Indices::temperatureIdx; }) { + if constexpr (Indices::enableFullyImplicitThermal) { + names_[Indices::temperatureIdx] = "Energy"; + } + } + + if constexpr (requires { Indices::numFoam; Indices::foamConcentrationIdx; }) { + if constexpr (Indices::numFoam == 1) { + names_[Indices::foamConcentrationIdx] = "Foam"; + } + } + + if constexpr (requires { Indices::numBrine; Indices::saltConcentrationIdx; }) { + if constexpr (Indices::numBrine == 1) { + names_[Indices::saltConcentrationIdx] = "Brine"; + } + } + + if constexpr (requires { + Indices::enableMICP; + Indices::microbialConcentrationIdx; + Indices::oxygenConcentrationIdx; + Indices::ureaConcentrationIdx; + Indices::biofilmVolumeFractionIdx; + Indices::calciteVolumeFractionIdx; + }) { + if constexpr (Indices::enableMICP) { + names_[Indices::microbialConcentrationIdx] = "Microbes"; + names_[Indices::oxygenConcentrationIdx] = "Oxygen"; + names_[Indices::ureaConcentrationIdx] = "Urea"; + names_[Indices::biofilmVolumeFractionIdx] = "Biofilm"; + names_[Indices::calciteVolumeFractionIdx] = "Calcite"; + } + } + + if constexpr (requires { Indices::enableBiofilm; Indices::microbialConcentrationIdx; Indices::biofilmVolumeFractionIdx; }) { + if constexpr (Indices::enableBiofilm) { + names_[Indices::microbialConcentrationIdx] = "Microbes"; + names_[Indices::biofilmVolumeFractionIdx] = "Biofilm"; + } + } +} + +} // namespace Opm + +#endif \ No newline at end of file diff --git a/regressionTests.cmake b/regressionTests.cmake index dcc997edcc..ff3ba9416c 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -21,20 +21,14 @@ add_test_compareECLFiles( spe1 ) -add_test_compareECLFiles( - CASENAME - 1dcompositional - FILENAME - 1D_COMP - SIMULATOR - flowexp_comp - ABS_TOL - ${abs_tol} - REL_TOL - ${rel_tol} - DIR - compositional -) +add_test_compareECLFiles(CASENAME 1dcompositional + FILENAME 1D_COMP + SIMULATOR flowexp_comp + DEV_SIMULATOR flowexp_comp3_2p + REFERENCE_SIMULATOR flowexp_comp + ABS_TOL ${abs_tol} + REL_TOL ${rel_tol} + DIR compositional) add_test_compareECLFiles( CASENAME