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
46#endif
47
48#if HAVE_CUDA
50#endif
51
52#if HAVE_DAMARIS
54#endif
56#include <cassert>
57#include <charconv>
58#include <cstdlib>
59#include <filesystem>
60#include <iostream>
61#include <memory>
62#include <stdexcept>
63#include <string>
64#include <string_view>
65#include <type_traits>
66#include <utility>
67
68namespace Opm::Properties {
69
70// this is a dummy type tag that is used to setup the parameters before the actual
71// simulator.
72namespace TTag {
73
75 using InheritsFrom = std::tuple<FlowProblem>;
76};
77
78}
79
80// Disable convective mixing
81template<class TypeTag>
82struct EnableConvectiveMixing<TypeTag, TTag::FlowEarlyBird>
83{ static constexpr bool value = false; };
84
85// Disable diffusion
86template<class TypeTag>
87struct EnableDiffusion<TypeTag, TTag::FlowEarlyBird>
88{ static constexpr bool value = false; };
89
90} // namespace Opm::Properties
91
92namespace Opm {
93
94namespace Action { class State; }
95class UDQState;
96class WellTestState;
97
98// ----------------- Main program -----------------
99template <class TypeTag>
100int flowMain(int argc, char** argv, bool outputCout, bool outputFiles)
101{
102 // we always want to use the default locale, and thus spare us the trouble
103 // with incorrect locale settings.
104 resetLocale();
105
106 FlowMain<TypeTag> mainfunc(argc, argv, outputCout, outputFiles);
107 return mainfunc.execute();
108}
109
110// ----------------- Main class -----------------
111// For now, we will either be instantiated from main() in flow.cpp,
112// or from a Python pybind11 module..
113// NOTE (March 2020): When used from a pybind11 module, we do not neccessarily
114// want to run the whole simulation by calling run(), it is also
115// useful to just run one report step at a time. According to these different
116// usage scenarios, we refactored the original run() in flow.cpp into this class.
117class Main
118{
119public:
120 Main(int argc, char** argv, bool ownMPI = true);
121
122 // This constructor can be called from Python
123 explicit Main(const std::string& filename, bool mpi_init = true, bool mpi_finalize = true);
124
125 // This constructor can be called from Python when Python has
126 // already parsed a deck
127 Main(const std::string& filename,
128 std::shared_ptr<EclipseState> eclipseState,
129 std::shared_ptr<Schedule> schedule,
130 std::shared_ptr<SummaryConfig> summaryConfig,
131 bool mpi_init = true,
132 bool mpi_finalize = true);
133
135
136 void setArgvArgc_(const std::string& filename);
139 void initMPI();
140
148 {
149 int exitCode = EXIT_SUCCESS;
150 if (initialize_<Properties::TTag::FlowEarlyBird>(exitCode)) {
152 if (isSimulationRank_) {
153 return this->dispatchDynamic_();
154 }
155 }
156
157 return exitCode;
158 }
159
167 template <class TypeTag>
169 {
170 int exitCode = EXIT_SUCCESS;
171 if (initialize_<TypeTag>(exitCode)) {
172 if (isSimulationRank_) {
173 return this->dispatchStatic_<TypeTag>();
174 }
175 }
176
177 return exitCode;
178 }
179
182 {
183 int exitCode = EXIT_SUCCESS;
184 initialize_<Properties::TTag::FlowEarlyBird>(exitCode);
185 return exitCode;
186 }
187
188protected:
196 template <class TypeTagEarlyBird>
197 bool initialize_(int& exitCode, bool keepKeywords = false)
198 {
199 Dune::Timer externalSetupTimer;
200 externalSetupTimer.start();
201
202 handleVersionCmdLine_(argc_, argv_, Opm::moduleVersionName());
203
204 // we always want to use the default locale, and thus spare us the trouble
205 // with incorrect locale settings.
206 resetLocale();
207
208 // this is a work-around for a catch 22: we do not know what code path to use without
209 // parsing the deck, but we don't know the deck without having access to the
210 // parameters and this requires to know the type tag to be used. To solve this, we
211 // use a type tag just for parsing the parameters before we instantiate the actual
212 // simulator object. (Which parses the parameters again, but since this is done in an
213 // identical manner it does not matter.)
214 typedef TypeTagEarlyBird PreTypeTag;
216
217 PreProblem::setBriefDescription("Flow, an advanced reservoir simulator for ECL-decks provided by the Open Porous Media project.");
218 int status = FlowMain<PreTypeTag>::setupParameters_(argc_, argv_, FlowGenericVanguard::comm());
219 if (status != 0) {
220 // if setupParameters_ returns a value smaller than 0, there was no error, but
221 // the program should abort. This is the case e.g. for the --help and the
222 // --print-properties parameters.
223#if HAVE_MPI
224 if (status >= 0)
225 MPI_Abort(MPI_COMM_WORLD, status);
226#endif
227 exitCode = (status > 0) ? status : EXIT_SUCCESS;
228 return false; // Whether to run the simulator
229 }
230
231 OpmLog::setDebugVerbosityLevel(Parameters::Get<Parameters::DebugVerbosityLevel>());
232
233 std::string deckFilename;
234 std::string outputDir;
235 if ( eclipseState_ ) {
236 deckFilename = eclipseState_->getIOConfig().fullBasePath();
237 outputDir = eclipseState_->getIOConfig().getOutputDir();
238 }
239 else {
240 deckFilename = Parameters::Get<Parameters::EclDeckFileName>();
241 outputDir = Parameters::Get<Parameters::OutputDir>();
242 }
243
244#if HAVE_DAMARIS
245 enableDamarisOutput_ = Parameters::Get<Parameters::EnableDamarisOutput>();
246
247 // Reset to false as we cannot use Damaris if there is only one rank.
248 if ((enableDamarisOutput_ == true) && (FlowGenericVanguard::comm().size() == 1)) {
249 std::string msg ;
250 msg = "\nUse of Damaris (command line argument --enable-damaris-output=true) has been disabled for run with only one rank.\n" ;
251 OpmLog::warning(msg);
252 enableDamarisOutput_ = false ;
253 }
254
255 if (enableDamarisOutput_) {
256 // Deal with empty (defaulted) output dir, should be deck dir
257 auto damarisOutputDir = outputDir;
258 if (outputDir.empty()) {
259 auto odir = std::filesystem::path{deckFilename}.parent_path();
260 if (odir.empty()) {
261 damarisOutputDir = ".";
262 } else {
263 damarisOutputDir = odir.generic_string();
264 }
265 }
266 // Damaris server ranks will block here until damaris_stop() is called by client ranks
267 this->setupDamaris(damarisOutputDir);
268 }
269#endif // HAVE_DAMARIS
270
271 // Guard for when the Damaris core(s) return from damaris_start()
272 // which happens when damaris_stop() is called in main simulation
273 if (!isSimulationRank_) {
274 exitCode = EXIT_SUCCESS;
275 return true;
276 }
277
278 int mpiRank = FlowGenericVanguard::comm().rank();
279 outputCout_ = false;
280 if (mpiRank == 0)
281 outputCout_ = Parameters::Get<Parameters::EnableTerminalOutput>();
282
283 if (deckFilename.empty()) {
284 if (mpiRank == 0) {
285 std::cerr << "No input case given. Try '--help' for a usage description.\n";
286 }
287 exitCode = EXIT_FAILURE;
288 return false;
289 }
290
292 try {
293 deckFilename = PreVanguard::canonicalDeckPath(deckFilename);
294 }
295 catch (const std::exception& e) {
296 if ( mpiRank == 0 ) {
297 std::cerr << "Exception received: " << e.what() << ". Try '--help' for a usage description.\n";
298 }
299#if HAVE_MPI
300 MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
301#endif
302 exitCode = EXIT_FAILURE;
303 return false;
304 }
305
306 std::string cmdline_params;
307 if (outputCout_) {
309 getNumThreads(),
311 std::ostringstream str;
313 cmdline_params = str.str();
314 }
315
316 // Create Deck and EclipseState.
317 try {
318 this->readDeck(deckFilename,
319 outputDir,
320 Parameters::Get<Parameters::OutputMode>(),
321 !Parameters::Get<Parameters::SchedRestart>(),
322 Parameters::Get<Parameters::EnableLoggingFalloutWarning>(),
323 Parameters::Get<Parameters::ParsingStrictness>(),
324 Parameters::Get<Parameters::ActionParsingStrictness>(),
325 Parameters::Get<Parameters::InputSkipMode>(),
326 keepKeywords,
327 getNumThreads(),
328 Parameters::Get<Parameters::EclOutputInterval>(),
329 Parameters::Get<Parameters::Slave>(),
330 cmdline_params,
333 setupTime_ = externalSetupTimer.elapsed();
334 }
335 // readDeck() may throw std::runtime_error on parse failure.
336 // These exceptions are synchronized across MPI ranks via comm.min()
337 // inside readDeck(), so cooperative shutdown is safe — returning false
338 // lets Main::~Main() call MPI_Finalize().
339 catch (const std::runtime_error& e)
340 {
341 if (outputCout_) {
342 std::cerr << "Failed to create valid EclipseState object." << std::endl;
343 std::cerr << e.what() << std::endl;
344 }
345#if HAVE_MPI
346 // If we are a spawned slave, notify the master that initialization
347 // failed so it does not hang waiting for our initial data exchange.
348 // Only rank 0 sends the status (master receives from source=0).
349 notifyMasterSlaveInitFailed_();
350#endif
351 exitCode = EXIT_FAILURE;
352 return false;
353 }
354 // Other exceptions (e.g., std::bad_alloc, std::invalid_argument)
355 // may not be synchronized across ranks. Use MPI_Abort() to prevent
356 // deadlocks from unsynchronized failures.
357 catch (const std::exception& e)
358 {
359 if (outputCout_) {
360 std::cerr << "Unexpected error during initialization." << std::endl;
361 std::cerr << "Exception caught: " << e.what() << std::endl;
362 }
363#if HAVE_MPI
364 MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
365#endif
366 exitCode = EXIT_FAILURE;
367 return false;
368 }
369
370#if HAVE_CUDA
372#endif
373
374 exitCode = EXIT_SUCCESS;
375 return true;
376 }
377
379
380private:
381 // This function is an extreme special case, if the program has been invoked
382 // *exactly* as:
383 //
384 // flow --version
385 //
386 // the call is intercepted by this function which will print "flow $version"
387 // on stdout and exit(0).
388 void handleVersionCmdLine_(int argc, char** argv,
389 std::string_view moduleVersionName);
390
391 // This function is a special case, if the program has been invoked
392 // with the argument "--test-split-communicator=true" as the FIRST
393 // argument, it will be removed from the argument list and we set the
394 // test_split_comm_ flag to true.
395 // Note: initializing the parameter system before MPI could make this
396 // use the parameter system instead.
397 void handleTestSplitCommunicatorCmdLine_();
398
399 // If this process is a reservoir coupling slave, notify the master that
400 // initialization failed so it does not hang waiting for the initial data exchange.
401 void notifyMasterSlaveInitFailed_();
402
408 int dispatchDynamic_();
409
418 template <class TypeTag>
419 int dispatchStatic_()
420 {
421 this->setupVanguard();
422 return flowMain<TypeTag>(argc_, argv_, outputCout_, outputFiles_);
423 }
424
433 int runMICP(const Phases& phases);
434
443 int runTwoPhase(const Phases& phases);
444
453 int runBiofilm(const Phases& phases);
454
463 int runPolymer(const Phases& phases);
464
470 int runFoam();
471
480 int runWaterOnly(const Phases& phases);
481
490 int runWaterOnlyEnergy(const Phases& phases);
491
500 int runBrine(const Phases& phases);
501
510 int runSolvent(const Phases& phases);
511
517 int runExtendedBlackOil();
518
527 int runThermal(const Phases& phases);
528
535 int runBlackOilTemp();
536
542 int runBlackOil();
543
544
545
546 void readDeck(const std::string& deckFilename,
547 const std::string& outputDir,
548 const std::string& outputMode,
549 const bool init_from_restart_file,
550 const bool allRanksDbgPrtLog,
551 const std::string& parsingStrictness,
552 const std::string& actionParsingStrictness,
553 const std::string& inputSkipMode,
554 const bool keepKeywords,
555 const std::size_t numThreads,
556 const int output_param,
557 const bool slaveMode,
558 const std::string& parameters,
559 std::string_view moduleVersion,
560 std::string_view compileTimestamp);
561
562 static int getNumThreads();
563
564#if HAVE_DAMARIS
565 void setupDamaris(const std::string& outputDir);
566#endif
567
568protected:
569 int argc_{0};
570 char** argv_{nullptr};
571 bool outputCout_{false};
572 bool outputFiles_{false};
573
574private:
575 bool ownMPI_{true};
576 double setupTime_{0.0};
577 std::string deckFilename_{};
578 std::string flowProgName_{};
579 char *saveArgs_[3]{nullptr};
580 std::unique_ptr<UDQState> udqState_{};
581 std::unique_ptr<Action::State> actionState_{};
582 std::unique_ptr<WellTestState> wtestState_{};
583
584 // These variables may be owned by both Python and the simulator
585 std::shared_ptr<EclipseState> eclipseState_{};
586 std::shared_ptr<Schedule> schedule_{};
587 std::shared_ptr<SummaryConfig> summaryConfig_{};
588 bool mpi_init_{true};
589 bool mpi_finalize_{true};
590
591 // To demonstrate run with non_world_comm
592 bool test_split_comm_ = false;
593 bool isSimulationRank_ = true;
594#if HAVE_MPI
595 std::string reservoirCouplingSlaveOutputFilename_{};
596#endif
597#if HAVE_DAMARIS
598 bool enableDamarisOutput_ = false;
599#endif
600};
601
602} // namespace Opm
603
604#endif // OPM_MAIN_HEADER_INCLUDED
static Parallel::Communication & comm()
Obtain global communicator.
Definition: FlowGenericVanguard.hpp:336
Definition: Main.hpp:118
int argc_
Definition: Main.hpp:569
int justInitialize()
Used for test_outputdir.
Definition: Main.hpp:181
void maybeSaveReservoirCouplingSlaveLogFilename_()
bool initialize_(int &exitCode, bool keepKeywords=false)
Initialize.
Definition: Main.hpp:197
bool outputFiles_
Definition: Main.hpp:572
void maybeRedirectReservoirCouplingSlaveOutput_()
int runDynamic()
Definition: Main.hpp:147
Main(int argc, char **argv, bool ownMPI=true)
char ** argv_
Definition: Main.hpp:570
Main(const std::string &filename, bool mpi_init=true, bool mpi_finalize=true)
int runStatic()
Definition: Main.hpp:168
void setupVanguard()
void setArgvArgc_(const std::string &filename)
bool outputCout_
Definition: Main.hpp:571
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:100
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:75