opm-simulators
Main.hpp
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 
33 #include <opm/simulators/flow/Banners.hpp>
34 #include <opm/simulators/flow/FlowMain.hpp>
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
44 #include <opm/simulators/utils/ParallelEclipseState.hpp>
45 #include <opm/simulators/flow/rescoup/ReservoirCoupling.hpp>
46 #endif
47 
48 #if HAVE_CUDA
49 #include <opm/simulators/linalg/gpuistl/device_management.hpp>
50 #endif
51 
52 #if HAVE_DAMARIS
53 #include <opm/simulators/utils/DamarisKeywords.hpp>
54 #endif
55 
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 
68 namespace Opm::Properties {
69 
70 // this is a dummy type tag that is used to setup the parameters before the actual
71 // simulator.
72 namespace TTag {
73 
74 struct FlowEarlyBird {
75  using InheritsFrom = std::tuple<FlowProblem>;
76 };
77 
78 }
79 
80 // Disable convective mixing
81 template<class TypeTag>
82 struct EnableConvectiveMixing<TypeTag, TTag::FlowEarlyBird>
83 { static constexpr bool value = false; };
84 
85 // Disable diffusion
86 template<class TypeTag>
87 struct EnableDiffusion<TypeTag, TTag::FlowEarlyBird>
88 { static constexpr bool value = false; };
89 
90 } // namespace Opm::Properties
91 
92 namespace Opm {
93 
94 namespace Action { class State; }
95 class UDQState;
96 class WellTestState;
97 
98 // ----------------- Main program -----------------
99 template <class TypeTag>
100 int 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.
117 class Main
118 {
119 public:
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 
134  ~Main();
135 
136  void setArgvArgc_(const std::string& filename);
137  void maybeSaveReservoirCouplingSlaveLogFilename_();
138  void maybeRedirectReservoirCouplingSlaveOutput_();
139  void initMPI();
140 
148  {
149  int exitCode = EXIT_SUCCESS;
150  if (initialize_<Properties::TTag::FlowEarlyBird>(exitCode)) {
151  Parameters::reset();
152  if (isSimulationRank_) {
153  return this->dispatchDynamic_();
154  }
155  }
156 
157  return exitCode;
158  }
159 
167  template <class TypeTag>
168  int runStatic()
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 
188 protected:
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.");
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_) {
308  printFlowBanner(FlowGenericVanguard::comm().size(),
309  getNumThreads(),
311  std::ostringstream str;
312  Parameters::printValues(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
371  Opm::gpuistl::printDevice();
372 #endif
373 
374  exitCode = EXIT_SUCCESS;
375  return true;
376  }
377 
378  void setupVanguard();
379 
380 private:
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 
568 protected:
569  int argc_{0};
570  char** argv_{nullptr};
571  bool outputCout_{false};
572  bool outputFiles_{false};
573 
574 private:
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
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
Definition: Main.hpp:117
std::string moduleVersionName()
Return the version name of the module, for example "2015.10" (for a release branch) or "2016...
Definition: moduleVersion.cpp:34
int runDynamic()
Run simulation.
Definition: Main.hpp:147
This file provides the infrastructure to retrieve run-time parameters.
Definition: FlowMain.hpp:66
std::string compileTimestamp()
Return a string "dd-mm-yyyy at HH::MM::SS hrs" which is the time the binary was compiled.
Definition: moduleVersion.cpp:57
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
static Parallel::Communication & comm()
Obtain global communicator.
Definition: FlowGenericVanguard.hpp:336
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
int runStatic()
Run simulation.
Definition: Main.hpp:168
int justInitialize()
Used for test_outputdir.
Definition: Main.hpp:181
bool initialize_(int &exitCode, bool keepKeywords=false)
Initialize.
Definition: Main.hpp:197
The Opm property system, traits with inheritance.
std::string moduleVersion()
Return a string containing both the name and hash, if N is the name and H is the hash it will be "N (...
Definition: moduleVersion.cpp:50
Definition: blackoilmodel.hh:75
Enable diffusive fluxes?
Definition: multiphasebaseproperties.hh:91
Enable convective mixing?
Definition: multiphasebaseproperties.hh:99