opm-simulators
FlowMain.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 
7  This file is part of the Open Porous Media project (OPM).
8 
9  OPM is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  (at your option) any later version.
13 
14  OPM is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with OPM. If not, see <http://www.gnu.org/licenses/>.
21 */
22 #ifndef OPM_FLOW_MAIN_HEADER_INCLUDED
23 #define OPM_FLOW_MAIN_HEADER_INCLUDED
24 
25 #include <opm/input/eclipse/EclipseState/EclipseState.hpp>
26 #include <opm/input/eclipse/EclipseState/IOConfig/IOConfig.hpp>
27 #include <opm/input/eclipse/EclipseState/InitConfig/InitConfig.hpp>
28 
30 
31 #include <opm/simulators/flow/Banners.hpp>
32 #include <opm/simulators/flow/FlowUtils.hpp>
33 #include <opm/simulators/flow/NlddReporting.hpp>
34 #include <opm/simulators/flow/SimulatorFullyImplicitBlackoil.hpp>
35 #include <opm/simulators/flow/rescoup/ReservoirCouplingEnabled.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 #ifdef _OPENMP
44 #include <omp.h>
45 #endif
46 
47 #include <charconv>
48 #include <cstddef>
49 #include <memory>
50 
51 namespace Opm::Parameters {
52 
53 // Do not merge parallel output files or warn about them
54 struct EnableLoggingFalloutWarning { static constexpr bool value = false; };
55 struct OutputInterval { static constexpr int value = 1; };
56 // Set global debug verbosity level
57 struct DebugVerbosityLevel { static constexpr int value = 1; };
58 } // namespace Opm::Parameters
59 
60 namespace Opm {
61 
62  class Deck;
63 
64  // The FlowMain class is the black-oil simulator.
65  template <class TypeTag>
66  class FlowMain
67  {
68  public:
69  using MaterialLawManager = typename GetProp<TypeTag, Properties::MaterialLaw>::EclMaterialLawManager;
70  using ModelSimulator = GetPropType<TypeTag, Properties::Simulator>;
76 
78 
79  FlowMain(int argc, char **argv, bool output_cout, bool output_files )
80  : argc_{argc}, argv_{argv},
81  output_cout_{output_cout}, output_files_{output_files}
82  {
83 
84  }
85 
86  // Read the command line parameters. Throws an exception if something goes wrong.
87  static int setupParameters_(int argc, char** argv, Parallel::Communication comm)
88  {
89  if (!Parameters::IsRegistrationOpen()) {
90  // We have already successfully run setupParameters_().
91  // For the dynamically chosen runs (as from the main flow
92  // executable) we must run this function again with the
93  // real typetag to be used, as the first time was with the
94  // "FlowEarlyBird" typetag. However, for the static ones (such
95  // as 'flow_onephase_energy') it has already been run with the
96  // correct typetag.
97  return EXIT_SUCCESS;
98  }
99  // register the flow specific parameters
100  Parameters::Register<Parameters::OutputInterval>
101  ("Specify the number of report steps between two consecutive writes of restart data");
102  Parameters::Register<Parameters::EnableLoggingFalloutWarning>
103  ("Developer option to see whether logging was on non-root processors. "
104  "In that case it will be appended to the *.DBG or *.PRT files");
105  Parameters::Register<Parameters::DebugVerbosityLevel>
106  ("Set debug verbosity level globally. Default is 1, increasing values give additional output and 0 disables most messages to the .DBG file");
107 
108  // register the base parameters
109  registerAllParameters_<TypeTag>(/*finalizeRegistration=*/false);
110 
111  Simulator::registerParameters();
112 
113  detail::hideUnusedParameters<Scalar>();
114 
115  Parameters::endRegistration();
116 
117  int mpiRank = comm.rank();
118 
119  // read in the command line parameters
120  int status = ::Opm::setupParameters_<TypeTag>(argc,
121  const_cast<const char**>(argv),
122  /*doRegistration=*/false,
123  /*allowUnused=*/true,
124  /*handleHelp=*/(mpiRank==0),
125  mpiRank);
126  if (status == 0) {
127 
128  // deal with unknown parameters.
129 
130  int unknownKeyWords = 0;
131  if (mpiRank == 0) {
132  unknownKeyWords = Parameters::printUnused(std::cerr);
133  }
134  int globalUnknownKeyWords = comm.sum(unknownKeyWords);
135  unknownKeyWords = globalUnknownKeyWords;
136  if ( unknownKeyWords )
137  {
138  if ( mpiRank == 0 )
139  {
140  std::string msg = "Aborting simulation due to unknown "
141  "parameters. Please query \"flow --help\" for "
142  "supported command line parameters.";
143  if (OpmLog::hasBackend("STREAMLOG"))
144  {
145  OpmLog::error(msg);
146  }
147  else {
148  std::cerr << msg << std::endl;
149  }
150  }
151  return EXIT_FAILURE;
152  }
153 
154  // deal with --print-parameters and unknown parameters.
155  if (Parameters::Get<Parameters::PrintParameters>() == 1) {
156  if (mpiRank == 0) {
157  Parameters::printValues(std::cout);
158  }
159  return -1;
160  }
161  }
162 
163  // set the maximum limit on OMP threads
164  setMaxThreads();
165 
166  return status;
167  }
168 
172  int execute()
173  {
174  return execute_(&FlowMain::runSimulator, /*cleanup=*/true);
175  }
176 
177  int executeInitStep()
178  {
179  return execute_(&FlowMain::runSimulatorInit, /*cleanup=*/false);
180  }
181 
182  // Returns true unless "EXIT" was encountered in the schedule
183  // section of the input datafile.
184  int executeStep()
185  {
186  return simulator_->runStep(*simtimer_);
187  }
188 
189  // Called from Python to cleanup after having executed the last
190  // executeStep()
191  int executeStepsCleanup()
192  {
193  SimulatorReport report = simulator_->finalize();
194  runSimulatorAfterSim_(report);
195  return report.success.exit_status;
196  }
197 
198  ModelSimulator* getSimulatorPtr()
199  {
200  return modelSimulator_.get();
201  }
202 
203  SimulatorTimer* getSimTimer()
204  {
205  return simtimer_.get();
206  }
207 
210  {
211  return simtimer_->stepLengthTaken();
212  }
213 
214  private:
215  // called by execute() or executeInitStep()
216  int execute_(int (FlowMain::* runOrInitFunc)(), bool cleanup)
217  {
218  auto logger = [this](const std::exception& e, const std::string& message_start) {
219  std::ostringstream message;
220  message << message_start << e.what();
221 
222  if (this->output_cout_) {
223  // in some cases exceptions are thrown before the logging system is set
224  // up.
225  if (OpmLog::hasBackend("STREAMLOG")) {
226  OpmLog::error(message.str());
227  }
228  else {
229  std::cout << message.str() << "\n";
230  }
231  }
232  detail::checkAllMPIProcesses();
233  return EXIT_FAILURE;
234  };
235 
236  try {
237  // deal with some administrative boilerplate
238 
239  Dune::Timer setupTimerAfterReadingDeck;
240  setupTimerAfterReadingDeck.start();
241 
242  int status = setupParameters_(this->argc_, this->argv_, FlowGenericVanguard::comm());
243  if (status) {
244  return status;
245  }
246 
247  setupParallelism();
248  setupModelSimulator();
249  createSimulator();
250 
251  this->deck_read_time_ = modelSimulator_->vanguard().setupTime();
252  this->total_setup_time_ = setupTimerAfterReadingDeck.elapsed() + this->deck_read_time_;
253 
254  // if run, do the actual work, else just initialize
255  int exitCode = (this->*runOrInitFunc)();
256  if (cleanup) {
257  executeCleanup_();
258  }
259  return exitCode;
260  }
261  catch (const TimeSteppingBreakdown& e) {
262  auto exitCode = logger(e, "Simulation aborted: ");
263  executeCleanup_();
264  return exitCode;
265  }
266  catch (const std::exception& e) {
267  auto exitCode = logger(e, "Simulation aborted as program threw an unexpected exception: ");
268  executeCleanup_();
269  return exitCode;
270  }
271  }
272 
273  void executeCleanup_() {
274  // clean up
275  mergeParallelLogFiles();
276  }
277 
278  protected:
279  void setupParallelism()
280  {
281  // determine the rank of the current process and the number of processes
282  // involved in the simulation. MPI must have already been initialized
283  // here. (yes, the name of this method is misleading.)
284  auto comm = FlowGenericVanguard::comm();
285  mpi_rank_ = comm.rank();
286  mpi_size_ = comm.size();
287 
288  setMaxThreads();
289  }
290 
291  static void setMaxThreads()
292  {
293 #if _OPENMP
294  // If openMP is available, default to 2 threads per process unless
295  // OMP_NUM_THREADS is set or command line --threads-per-process used.
296  // Issue a warning if both OMP_NUM_THREADS and --threads-per-process are set,
297  // but let the environment variable take precedence.
298  constexpr int default_threads = 2;
299  const bool isSet = Parameters::IsSet<Parameters::ThreadsPerProcess>();
300  const int requested_threads = Parameters::Get<Parameters::ThreadsPerProcess>();
301  int threads = requested_threads > 0 ? requested_threads : default_threads;
302 
303  const char* env_var = getenv("OMP_NUM_THREADS");
304  if (env_var) {
305  int omp_num_threads = -1;
306  auto result = std::from_chars(env_var, env_var + std::strlen(env_var), omp_num_threads);
307  if (result.ec == std::errc() && omp_num_threads > 0) {
308  // Set threads to omp_num_threads if it was successfully parsed and is positive
309  threads = omp_num_threads;
310  if (isSet) {
311  OpmLog::warning("Environment variable OMP_NUM_THREADS takes precedence over the --threads-per-process cmdline argument.");
312  }
313  } else {
314  OpmLog::warning("Invalid value for OMP_NUM_THREADS environment variable.");
315  }
316  }
317 
318  // Requesting -1 thread will let OMP automatically deduce the number
319  // but setting OMP_NUM_THREADS takes precedence.
320  if (env_var || !(isSet && requested_threads == -1)) {
321  omp_set_num_threads(threads);
322  }
323 #endif
324 
325  using TM = GetPropType<TypeTag, Properties::ThreadManager>;
326  TM::init(false);
327  }
328 
329  void mergeParallelLogFiles()
330  {
331  // force closing of all log files.
332  OpmLog::removeAllBackends();
333 
334  if (mpi_rank_ != 0 || mpi_size_ < 2 || !this->output_files_ || !modelSimulator_) {
335  return;
336  }
337 
338  detail::mergeParallelLogFiles(eclState().getIOConfig().getOutputDir(),
339  Parameters::Get<Parameters::EclDeckFileName>(),
340  Parameters::Get<Parameters::EnableLoggingFalloutWarning>());
341  }
342 
343  void setupModelSimulator()
344  {
345  modelSimulator_ = std::make_unique<ModelSimulator>(FlowGenericVanguard::comm(), /*verbose=*/false);
346  modelSimulator_->executionTimer().start();
347  modelSimulator_->model().applyInitialSolution();
348  }
349 
350  const EclipseState& eclState() const
351  { return modelSimulator_->vanguard().eclState(); }
352 
353  EclipseState& eclState()
354  { return modelSimulator_->vanguard().eclState(); }
355 
356  const Schedule& schedule() const
357  { return modelSimulator_->vanguard().schedule(); }
358 
359  // Run the simulator.
360  int runSimulator()
361  {
362  return runSimulatorInitOrRun_(&FlowMain::runSimulatorRunCallback_);
363  }
364 
365  int runSimulatorInit()
366  {
367  return runSimulatorInitOrRun_(&FlowMain::runSimulatorInitCallback_);
368  }
369 
370  private:
371  // Callback that will be called from runSimulatorInitOrRun_().
372  int runSimulatorRunCallback_()
373  {
374 #ifdef RESERVOIR_COUPLING_ENABLED
375  SimulatorReport report = simulator_->run(*simtimer_, this->argc_, this->argv_);
376 #else
377  SimulatorReport report = simulator_->run(*simtimer_);
378 #endif
379  runSimulatorAfterSim_(report);
380  return report.success.exit_status;
381  }
382 
383  // Callback that will be called from runSimulatorInitOrRun_().
384  int runSimulatorInitCallback_()
385  {
386 #ifdef RESERVOIR_COUPLING_ENABLED
387  simulator_->init(*simtimer_, this->argc_, this->argv_);
388 #else
389  simulator_->init(*simtimer_);
390 #endif
391  return EXIT_SUCCESS;
392  }
393 
394  // Output summary after simulation has completed
395  void runSimulatorAfterSim_(SimulatorReport &report)
396  {
397  if (simulator_->model().hasNlddSolver()) {
398  const auto& odir = eclState().getIOConfig().getOutputDir();
399  // Write the number of nonlinear iterations per cell to a file in ResInsight compatible format
400  simulator_->model().writeNonlinearIterationsPerCell(odir);
401  // Write the NLDD statistics to the DBG file
402  reportNlddStatistics(simulator_->model().domainAccumulatedReports(),
403  simulator_->model().localAccumulatedReports(),
404  this->output_cout_,
406  }
407 
408  if (! this->output_cout_) {
409  return;
410  }
411 
412  const int threads
413 #if !defined(_OPENMP) || !_OPENMP
414  = 1;
415 #else
416  = omp_get_max_threads();
417 #endif
418 
419  printFlowTrailer(mpi_size_, threads, total_setup_time_, deck_read_time_, report);
420 
421  detail::handleExtraConvergenceOutput(report,
422  Parameters::Get<Parameters::OutputExtraConvergenceInfo>(),
423  R"(OutputExtraConvergenceInfo (--output-extra-convergence-info))",
424  eclState().getIOConfig().getOutputDir(),
425  eclState().getIOConfig().getBaseName());
426  }
427 
428  // Run the simulator.
429  int runSimulatorInitOrRun_(int (FlowMain::* initOrRunFunc)())
430  {
431 
432  const auto& schedule = this->schedule();
433  auto& ioConfig = eclState().getIOConfig();
434  simtimer_ = std::make_unique<SimulatorTimer>();
435 
436  // initialize variables
437  const auto& initConfig = eclState().getInitConfig();
438  simtimer_->init(schedule, static_cast<std::size_t>(initConfig.getRestartStep()));
439 
440  if (this->output_cout_) {
441  std::ostringstream oss;
442 
443  // This allows a user to catch typos and misunderstandings in the
444  // use of simulator parameters.
445  if (Parameters::printUnused(oss)) {
446  std::cout << "----------------- Unrecognized parameters: -----------------\n";
447  std::cout << oss.str();
448  std::cout << "----------------------------------------------------------------" << std::endl;
449  }
450  }
451 
452  if (!ioConfig.initOnly()) {
453  if (this->output_cout_) {
454  std::string msg;
455  msg = "\n\n================ Starting main simulation loop ===============\n";
456  OpmLog::info(msg);
457  }
458 
459  return (this->*initOrRunFunc)();
460  }
461  else {
462  if (this->output_cout_) {
463  std::cout << "\n\n================ Simulation turned off ===============\n" << std::flush;
464  }
465  return EXIT_SUCCESS;
466  }
467  }
468 
469  protected:
470 
472  // Create simulator instance.
473  // Writes to:
474  // simulator_
476  {
477  // Create the simulator instance.
478  simulator_ = std::make_unique<Simulator>(*modelSimulator_);
479  }
480 
481  Grid& grid()
482  { return modelSimulator_->vanguard().grid(); }
483 
484  private:
485  std::unique_ptr<ModelSimulator> modelSimulator_;
486  int mpi_rank_ = 0;
487  int mpi_size_ = 1;
488  std::any parallel_information_;
489  std::unique_ptr<Simulator> simulator_;
490  std::unique_ptr<SimulatorTimer> simtimer_;
491  int argc_;
492  char **argv_;
493  bool output_cout_;
494  bool output_files_;
495  double total_setup_time_ = 0.0;
496  double deck_read_time_ = 0.0;
497  };
498 
499 } // namespace Opm
500 
501 #endif // OPM_FLOW_MAIN_HEADER_INCLUDED
Definition: FlowMain.hpp:55
Definition: FlowMain.hpp:57
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
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type GetProp
get the type of a property (equivalent to old macro GET_PROP(...))
Definition: propertysystem.hh:224
void reportNlddStatistics(const std::vector< SimulatorReport > &domain_reports, const SimulatorReport &local_report, const bool output_cout, const Parallel::Communication &comm)
Reports NLDD statistics after simulation.
Definition: NlddReporting.cpp:97
void createSimulator()
This is the main function of Flow.
Definition: FlowMain.hpp:475
Definition: FlowMain.hpp:66
double getPreviousReportStepSize()
Get the size of the previous report step.
Definition: FlowMain.hpp:209
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: blackoilbioeffectsmodules.hh:45
Definition: blackoilnewtonmethodparams.hpp:31
static Parallel::Communication & comm()
Obtain global communicator.
Definition: FlowGenericVanguard.hpp:336
int execute()
This is the main function of Flow.
Definition: FlowMain.hpp:172
a simulator for the blackoil model
Definition: SimulatorFullyImplicitBlackoil.hpp:93
Provides convenience routines to bring up the simulation at runtime.