Main.hpp
Go to the documentation of this file.
1/*
2 Copyright 2013, 2014, 2015 SINTEF ICT, Applied Mathematics.
3 Copyright 2014 Dr. Blatt - HPC-Simulation-Software & Services
4 Copyright 2015 IRIS AS
5 Copyright 2014 STATOIL ASA.
6 Copyright 2023 Inria
7
8 This file is part of the Open Porous Media project (OPM).
9
10 OPM is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 OPM is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with OPM. If not, see <http://www.gnu.org/licenses/>.
22*/
23#ifndef OPM_MAIN_HEADER_INCLUDED
24#define OPM_MAIN_HEADER_INCLUDED
25
26#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
27#include <opm/input/eclipse/Schedule/Action/State.hpp>
28#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
29
32
36
37#if HAVE_DUNE_FEM
38#include <dune/fem/misc/mpimanager.hh>
39#else
40#include <dune/common/parallel/mpihelper.hh>
41#endif
42
43#if HAVE_MPI
45#endif
46
47#if HAVE_CUDA
49#endif
50
51#if HAVE_DAMARIS
53#endif
54
55#include <cassert>
56#include <charconv>
57#include <cstdlib>
58#include <filesystem>
59#include <iostream>
60#include <memory>
61#include <stdexcept>
62#include <string>
63#include <string_view>
64#include <type_traits>
65#include <utility>
66
67namespace Opm::Properties {
68
69// this is a dummy type tag that is used to setup the parameters before the actual
70// simulator.
71namespace TTag {
72
74 using InheritsFrom = std::tuple<FlowProblem>;
75};
76
77}
78
79// Disable convective mixing
80template<class TypeTag>
81struct EnableConvectiveMixing<TypeTag, TTag::FlowEarlyBird>
82{ static constexpr bool value = false; };
83
84// Disable diffusion
85template<class TypeTag>
86struct EnableDiffusion<TypeTag, TTag::FlowEarlyBird>
87{ static constexpr bool value = false; };
88
89} // namespace Opm::Properties
90
91namespace Opm {
92
93namespace Action { class State; }
94class UDQState;
95class WellTestState;
96
97// ----------------- Main program -----------------
98template <class TypeTag>
99int flowMain(int argc, char** argv, bool outputCout, bool outputFiles)
100{
101 // we always want to use the default locale, and thus spare us the trouble
102 // with incorrect locale settings.
103 resetLocale();
104
105 FlowMain<TypeTag> mainfunc(argc, argv, outputCout, outputFiles);
106 return mainfunc.execute();
107}
108
109// ----------------- Main class -----------------
110// For now, we will either be instantiated from main() in flow.cpp,
111// or from a Python pybind11 module..
112// NOTE (March 2020): When used from a pybind11 module, we do not neccessarily
113// want to run the whole simulation by calling run(), it is also
114// useful to just run one report step at a time. According to these different
115// usage scenarios, we refactored the original run() in flow.cpp into this class.
116class Main
117{
118public:
119 Main(int argc, char** argv, bool ownMPI = true);
120
121 // This constructor can be called from Python
122 explicit Main(const std::string& filename, bool mpi_init = true, bool mpi_finalize = true);
123
124 // This constructor can be called from Python when Python has
125 // already parsed a deck
126 Main(const std::string& filename,
127 std::shared_ptr<EclipseState> eclipseState,
128 std::shared_ptr<Schedule> schedule,
129 std::shared_ptr<SummaryConfig> summaryConfig,
130 bool mpi_init = true,
131 bool mpi_finalize = true);
132
134
135 void setArgvArgc_(const std::string& filename);
138 void initMPI();
139
147 {
148 int exitCode = EXIT_SUCCESS;
149 if (initialize_<Properties::TTag::FlowEarlyBird>(exitCode)) {
151 if (isSimulationRank_) {
152 return this->dispatchDynamic_();
153 }
154 }
155
156 return exitCode;
157 }
158
166 template <class TypeTag>
168 {
169 int exitCode = EXIT_SUCCESS;
170 if (initialize_<TypeTag>(exitCode)) {
171 if (isSimulationRank_) {
172 return this->dispatchStatic_<TypeTag>();
173 }
174 }
175
176 return exitCode;
177 }
178
181 {
182 int exitCode = EXIT_SUCCESS;
183 initialize_<Properties::TTag::FlowEarlyBird>(exitCode);
184 return exitCode;
185 }
186
187protected:
195 template <class TypeTagEarlyBird>
196 bool initialize_(int& exitCode, bool keepKeywords = false)
197 {
198 Dune::Timer externalSetupTimer;
199 externalSetupTimer.start();
200
201 handleVersionCmdLine_(argc_, argv_, Opm::moduleVersionName());
202
203 // we always want to use the default locale, and thus spare us the trouble
204 // with incorrect locale settings.
205 resetLocale();
206
207 // this is a work-around for a catch 22: we do not know what code path to use without
208 // parsing the deck, but we don't know the deck without having access to the
209 // parameters and this requires to know the type tag to be used. To solve this, we
210 // use a type tag just for parsing the parameters before we instantiate the actual
211 // simulator object. (Which parses the parameters again, but since this is done in an
212 // identical manner it does not matter.)
213 typedef TypeTagEarlyBird PreTypeTag;
215
216 PreProblem::setBriefDescription("Flow, an advanced reservoir simulator for ECL-decks provided by the Open Porous Media project.");
217 int status = FlowMain<PreTypeTag>::setupParameters_(argc_, argv_, FlowGenericVanguard::comm());
218 if (status != 0) {
219 // if setupParameters_ returns a value smaller than 0, there was no error, but
220 // the program should abort. This is the case e.g. for the --help and the
221 // --print-properties parameters.
222#if HAVE_MPI
223 if (status >= 0)
224 MPI_Abort(MPI_COMM_WORLD, status);
225#endif
226 exitCode = (status > 0) ? status : EXIT_SUCCESS;
227 return false; // Whether to run the simulator
228 }
229
230 OpmLog::setDebugVerbosityLevel(Parameters::Get<Parameters::DebugVerbosityLevel>());
231
232 std::string deckFilename;
233 std::string outputDir;
234 if ( eclipseState_ ) {
235 deckFilename = eclipseState_->getIOConfig().fullBasePath();
236 outputDir = eclipseState_->getIOConfig().getOutputDir();
237 }
238 else {
239 deckFilename = Parameters::Get<Parameters::EclDeckFileName>();
240 outputDir = Parameters::Get<Parameters::OutputDir>();
241 }
242
243#if HAVE_DAMARIS
244 enableDamarisOutput_ = Parameters::Get<Parameters::EnableDamarisOutput>();
245
246 // Reset to false as we cannot use Damaris if there is only one rank.
247 if ((enableDamarisOutput_ == true) && (FlowGenericVanguard::comm().size() == 1)) {
248 std::string msg ;
249 msg = "\nUse of Damaris (command line argument --enable-damaris-output=true) has been disabled for run with only one rank.\n" ;
250 OpmLog::warning(msg);
251 enableDamarisOutput_ = false ;
252 }
253
254 if (enableDamarisOutput_) {
255 // Deal with empty (defaulted) output dir, should be deck dir
256 auto damarisOutputDir = outputDir;
257 if (outputDir.empty()) {
258 auto odir = std::filesystem::path{deckFilename}.parent_path();
259 if (odir.empty()) {
260 damarisOutputDir = ".";
261 } else {
262 damarisOutputDir = odir.generic_string();
263 }
264 }
265 // Damaris server ranks will block here until damaris_stop() is called by client ranks
266 this->setupDamaris(damarisOutputDir);
267 }
268#endif // HAVE_DAMARIS
269
270 // Guard for when the Damaris core(s) return from damaris_start()
271 // which happens when damaris_stop() is called in main simulation
272 if (!isSimulationRank_) {
273 exitCode = EXIT_SUCCESS;
274 return true;
275 }
276
277 int mpiRank = FlowGenericVanguard::comm().rank();
278 outputCout_ = false;
279 if (mpiRank == 0)
280 outputCout_ = Parameters::Get<Parameters::EnableTerminalOutput>();
281
282 if (deckFilename.empty()) {
283 if (mpiRank == 0) {
284 std::cerr << "No input case given. Try '--help' for a usage description.\n";
285 }
286 exitCode = EXIT_FAILURE;
287 return false;
288 }
289
291 try {
292 deckFilename = PreVanguard::canonicalDeckPath(deckFilename);
293 }
294 catch (const std::exception& e) {
295 if ( mpiRank == 0 ) {
296 std::cerr << "Exception received: " << e.what() << ". Try '--help' for a usage description.\n";
297 }
298#if HAVE_MPI
299 MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
300#endif
301 exitCode = EXIT_FAILURE;
302 return false;
303 }
304
305 std::string cmdline_params;
306 if (outputCout_) {
308 getNumThreads(),
310 std::ostringstream str;
312 cmdline_params = str.str();
313 }
314
315 // Create Deck and EclipseState.
316 try {
317 this->readDeck(deckFilename,
318 outputDir,
319 Parameters::Get<Parameters::OutputMode>(),
320 !Parameters::Get<Parameters::SchedRestart>(),
321 Parameters::Get<Parameters::EnableLoggingFalloutWarning>(),
322 Parameters::Get<Parameters::ParsingStrictness>(),
323 Parameters::Get<Parameters::ActionParsingStrictness>(),
324 Parameters::Get<Parameters::InputSkipMode>(),
325 keepKeywords,
326 getNumThreads(),
327 Parameters::Get<Parameters::EclOutputInterval>(),
328 Parameters::Get<Parameters::Slave>(),
329 cmdline_params,
332 setupTime_ = externalSetupTimer.elapsed();
333 }
334 // readDeck() may throw std::runtime_error on parse failure.
335 // These exceptions are synchronized across MPI ranks via comm.min()
336 // inside readDeck(), so cooperative shutdown is safe — returning false
337 // lets Main::~Main() call MPI_Finalize().
338 catch (const std::runtime_error& e)
339 {
340 if (outputCout_) {
341 std::cerr << "Failed to create valid EclipseState object." << std::endl;
342 std::cerr << e.what() << std::endl;
343 }
344 exitCode = EXIT_FAILURE;
345 return false;
346 }
347 // Other exceptions (e.g., std::bad_alloc, std::invalid_argument)
348 // may not be synchronized across ranks. Use MPI_Abort() to prevent
349 // deadlocks from unsynchronized failures.
350 catch (const std::exception& e)
351 {
352 if (outputCout_) {
353 std::cerr << "Unexpected error during initialization." << std::endl;
354 std::cerr << "Exception caught: " << e.what() << std::endl;
355 }
356#if HAVE_MPI
357 MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
358#endif
359 exitCode = EXIT_FAILURE;
360 return false;
361 }
362
363#if HAVE_CUDA
365#endif
366
367 exitCode = EXIT_SUCCESS;
368 return true;
369 }
370
372
373private:
374 // This function is an extreme special case, if the program has been invoked
375 // *exactly* as:
376 //
377 // flow --version
378 //
379 // the call is intercepted by this function which will print "flow $version"
380 // on stdout and exit(0).
381 void handleVersionCmdLine_(int argc, char** argv,
382 std::string_view moduleVersionName);
383
384 // This function is a special case, if the program has been invoked
385 // with the argument "--test-split-communicator=true" as the FIRST
386 // argument, it will be removed from the argument list and we set the
387 // test_split_comm_ flag to true.
388 // Note: initializing the parameter system before MPI could make this
389 // use the parameter system instead.
390 void handleTestSplitCommunicatorCmdLine_();
391
397 int dispatchDynamic_();
398
407 template <class TypeTag>
408 int dispatchStatic_()
409 {
410 this->setupVanguard();
411 return flowMain<TypeTag>(argc_, argv_, outputCout_, outputFiles_);
412 }
413
422 int runMICP(const Phases& phases);
423
432 int runTwoPhase(const Phases& phases);
433
442 int runBiofilm(const Phases& phases);
443
452 int runPolymer(const Phases& phases);
453
459 int runFoam();
460
469 int runWaterOnly(const Phases& phases);
470
479 int runWaterOnlyEnergy(const Phases& phases);
480
489 int runBrine(const Phases& phases);
490
499 int runSolvent(const Phases& phases);
500
506 int runExtendedBlackOil();
507
516 int runThermal(const Phases& phases);
517
524 int runBlackOilTemp();
525
531 int runBlackOil();
532
533
534
535 void readDeck(const std::string& deckFilename,
536 const std::string& outputDir,
537 const std::string& outputMode,
538 const bool init_from_restart_file,
539 const bool allRanksDbgPrtLog,
540 const std::string& parsingStrictness,
541 const std::string& actionParsingStrictness,
542 const std::string& inputSkipMode,
543 const bool keepKeywords,
544 const std::size_t numThreads,
545 const int output_param,
546 const bool slaveMode,
547 const std::string& parameters,
548 std::string_view moduleVersion,
549 std::string_view compileTimestamp);
550
551 static int getNumThreads();
552
553#if HAVE_DAMARIS
554 void setupDamaris(const std::string& outputDir);
555#endif
556
557protected:
558 int argc_{0};
559 char** argv_{nullptr};
560 bool outputCout_{false};
561 bool outputFiles_{false};
562
563private:
564 bool ownMPI_{true};
565 double setupTime_{0.0};
566 std::string deckFilename_{};
567 std::string flowProgName_{};
568 char *saveArgs_[3]{nullptr};
569 std::unique_ptr<UDQState> udqState_{};
570 std::unique_ptr<Action::State> actionState_{};
571 std::unique_ptr<WellTestState> wtestState_{};
572
573 // These variables may be owned by both Python and the simulator
574 std::shared_ptr<EclipseState> eclipseState_{};
575 std::shared_ptr<Schedule> schedule_{};
576 std::shared_ptr<SummaryConfig> summaryConfig_{};
577 bool mpi_init_{true};
578 bool mpi_finalize_{true};
579
580 // To demonstrate run with non_world_comm
581 bool test_split_comm_ = false;
582 bool isSimulationRank_ = true;
583#if HAVE_MPI
584 std::string reservoirCouplingSlaveOutputFilename_{};
585#endif
586#if HAVE_DAMARIS
587 bool enableDamarisOutput_ = false;
588#endif
589};
590
591} // namespace Opm
592
593#endif // OPM_MAIN_HEADER_INCLUDED
static Parallel::Communication & comm()
Obtain global communicator.
Definition: FlowGenericVanguard.hpp:336
Definition: Main.hpp:117
int argc_
Definition: Main.hpp:558
int justInitialize()
Used for test_outputdir.
Definition: Main.hpp:180
void maybeSaveReservoirCouplingSlaveLogFilename_()
bool initialize_(int &exitCode, bool keepKeywords=false)
Initialize.
Definition: Main.hpp:196
bool outputFiles_
Definition: Main.hpp:561
void maybeRedirectReservoirCouplingSlaveOutput_()
int runDynamic()
Definition: Main.hpp:146
Main(int argc, char **argv, bool ownMPI=true)
char ** argv_
Definition: Main.hpp:559
Main(const std::string &filename, bool mpi_init=true, bool mpi_finalize=true)
int runStatic()
Definition: Main.hpp:167
void setupVanguard()
void setArgvArgc_(const std::string &filename)
bool outputCout_
Definition: Main.hpp:560
void initMPI()
Main(const std::string &filename, std::shared_ptr< EclipseState > eclipseState, std::shared_ptr< Schedule > schedule, std::shared_ptr< SummaryConfig > summaryConfig, bool mpi_init=true, bool mpi_finalize=true)
void printValues(std::ostream &os)
Print values of the run-time parameters.
void reset()
Reset parameter system.
Definition: blackoilmodel.hh:75
void printDevice()
Definition: blackoilbioeffectsmodules.hh:45
std::string moduleVersionName()
int flowMain(int argc, char **argv, bool outputCout, bool outputFiles)
Definition: Main.hpp:99
std::string compileTimestamp()
void printFlowBanner(int nprocs, int threads, std::string_view moduleVersionName)
std::string moduleVersion()
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:233
This file provides the infrastructure to retrieve run-time parameters.
The Opm property system, traits with inheritance.
Enable convective mixing?
Definition: multiphasebaseproperties.hh:99
Enable diffusive fluxes?
Definition: multiphasebaseproperties.hh:91
std::tuple< FlowProblem > InheritsFrom
Definition: Main.hpp:74