File: start.hh

package info (click to toggle)
opm-models 2022.10%2Bds-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 6,216 kB
  • sloc: cpp: 37,910; ansic: 1,897; sh: 277; xml: 182; makefile: 10
file content (428 lines) | stat: -rw-r--r-- 14,923 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*
  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 2 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 <http://www.gnu.org/licenses/>.

  Consult the COPYING file in the top-level source directory of this
  module for the precise wording of the license and the list of
  copyright holders.
*/
/*!
 * \file
 * \brief Provides convenience routines to bring up the simulation at runtime.
 */
#ifndef EWOMS_START_HH
#define EWOMS_START_HH

#include <opm/models/utils/propertysystem.hh>
// the following header is not required here, but it must be included before
// dune/common/densematrix.hh because of some c++ ideosyncrasies
#include <opm/material/densead/Evaluation.hpp>

#include "parametersystem.hh"

#include <opm/models/utils/simulator.hh>
#include <opm/models/utils/timer.hh>

#include <opm/material/common/Valgrind.hpp>

#include <opm/material/common/ResetLocale.hpp>

#include <dune/grid/io/file/dgfparser/dgfparser.hh>
#include <dune/common/version.hh>
#include <dune/common/parametertreeparser.hh>
#include <dune/common/parallel/mpihelper.hh>

#if HAVE_DUNE_FEM
#include <dune/fem/misc/mpimanager.hh>
#endif

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <locale>

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <string.h>

#if HAVE_MPI
#include <mpi.h>
#endif

//! \cond SKIP_THIS

namespace Opm {
/*!
 * \brief Announce all runtime parameters to the registry but do not specify them yet.
 */
template <class TypeTag>
static inline void registerAllParameters_(bool finalizeRegistration = true)
{
    using Simulator = GetPropType<TypeTag, Properties::Simulator>;
    using ThreadManager = GetPropType<TypeTag, Properties::ThreadManager>;

    EWOMS_REGISTER_PARAM(TypeTag, std::string, ParameterFile,
                         "An .ini file which contains a set of run-time "
                         "parameters");
    EWOMS_REGISTER_PARAM(TypeTag, int, PrintProperties,
                         "Print the values of the compile time properties at "
                         "the start of the simulation");
    EWOMS_REGISTER_PARAM(TypeTag, int, PrintParameters,
                         "Print the values of the run-time parameters at the "
                         "start of the simulation");

    Simulator::registerParameters();
    ThreadManager::registerParameters();

    if (finalizeRegistration)
        EWOMS_END_PARAM_REGISTRATION(TypeTag);
}

/*!
 * \brief Register all runtime parameters, parse the command line
 *        arguments and the parameter file.
 *
 * \param argc The number of command line arguments
 * \param argv Array with the command line argument strings
 * \return A negative value if --help or --print-properties was provided,
 *         a positive value for errors or 0 for success.
 */
template <class TypeTag>
static inline int setupParameters_(int argc,
                                   const char **argv,
                                   bool registerParams=true,
                                   bool allowUnused=false,
                                   bool handleHelp = true)
{
    using Problem = GetPropType<TypeTag, Properties::Problem>;

    // first, get the MPI rank of the current process
    int myRank = 0;

    ////////////////////////////////////////////////////////////
    // Register all parameters
    ////////////////////////////////////////////////////////////
    if (registerParams)
        registerAllParameters_<TypeTag>();

    ////////////////////////////////////////////////////////////
    // set the parameter values
    ////////////////////////////////////////////////////////////

    // fill the parameter tree with the options from the command line
    const auto& positionalParamCallback = Problem::handlePositionalParameter;
    std::string helpPreamble = ""; // print help if non-empty!
    if (myRank == 0 && handleHelp)
        helpPreamble = Problem::helpPreamble(argc, argv);
    std::string s =
        Parameters::parseCommandLineOptions<TypeTag>(argc,
                                                     argv,
                                                     helpPreamble,
                                                     positionalParamCallback);
    if (!s.empty())
    {
        int status = 1;
        if (s == "Help called") // only on master process
            status = -1; // Use negative values to indicate --help argument
#if HAVE_MPI
        // Force -1 if the master process has that.
        int globalStatus;
        MPI_Allreduce(&status, &globalStatus, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
        return globalStatus;
#endif
        return status;
    }

    std::string paramFileName = EWOMS_GET_PARAM_(TypeTag, std::string, ParameterFile);
    if (paramFileName != "") {
        ////////////////////////////////////////////////////////////
        // add the parameters specified using an .ini file
        ////////////////////////////////////////////////////////////

        // check whether the parameter file is readable.
        std::ifstream tmp;
        tmp.open(paramFileName.c_str());
        if (!tmp.is_open()) {
            std::ostringstream oss;
            if (myRank == 0) {
                oss << "Parameter file \"" << paramFileName
                    << "\" does not exist or is not readable.";
                Parameters::printUsage<TypeTag>(argv[0], oss.str());
            }
            return /*status=*/1;
        }

        // read the parameter file.
        Parameters::parseParameterFile<TypeTag>(paramFileName, /*overwrite=*/false);
    }

    // make sure that no unknown parameters are encountered
    using KeyValuePair = std::pair<std::string, std::string>;
    using ParamList = std::list<KeyValuePair>;

    ParamList usedParams;
    ParamList unusedParams;

    EWOMS_GET_PARAM_LISTS(TypeTag, usedParams, unusedParams);
    if (!allowUnused && !unusedParams.empty()) {
        if (myRank == 0) {
            if (unusedParams.size() == 1)
                std::cerr << "The following explicitly specified parameter is unknown:\n";
            else
                std::cerr << "The following " << unusedParams.size()
                          << " explicitly specified parameters are unknown:\n";

            std::cerr << "\n";
            for (const auto& keyValue : unusedParams)
                std::cerr << "   " << keyValue.first << "=\"" << keyValue.second << "\"\n";
            std::cerr << "\n";

            std::cerr << "Use\n"
                      << "\n"
                      << "  " << argv[0] << " --help\n"
                      << "\n"
                      <<"to obtain the list of recognized command line parameters.\n\n";
        }
        return /*status=*/1;
    }

    return /*status=*/0;
}

/*!
 * \brief Resets the current TTY to a usable state if the program was aborted.
 *
 * This is intended to be called as part of a generic exception handler
 */
static inline void resetTerminal_()
{
    // make sure stderr and stderr do not contain any unwritten data and make sure that
    // the TTY does not see any unfinished ANSI escape sequence.
    std::cerr << "    \r\n";
    std::cerr.flush();
    std::cout << "    \r\n";
    std::cout.flush();

    // it seems like some terminals sometimes takes their time to react, so let's
    // accommodate them.
    usleep(/*usec=*/500*1000);

    // this requires the 'stty' command to be available in the command search path. on
    // most linux systems, is the case. (but even if the system() function fails, the
    // worst thing which can happen is that the TTY stays potentially choked up...)
    if (system("stty sane") != 0)
        std::cout << "Executing the 'stty' command failed."
                  << " Terminal might be left in an undefined state!\n";
}

/*!
 * \brief Resets the current TTY to a usable state if the program was interrupted by
 *        SIGABRT or SIGINT.
 */
static inline void resetTerminal_(int signum)
{
    // first thing to do when a nuke hits: restore the default signal handler
    signal(signum, SIG_DFL);

#if HAVE_MPI
    int rank = 0;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    if (rank != 0) {
        // re-raise the signal
        raise(signum);

        return;
    }
#endif

    if (isatty(fileno(stdout)) && isatty(fileno(stdin))) {
        std::cout << "\n\nReceived signal " << signum
                  << " (\"" << strsignal(signum) << "\")."
                  << " Trying to reset the terminal.\n";

        resetTerminal_();
    }

    // after we did our best to clean the pedestrian way, re-raise the signal
    raise(signum);
}
//! \endcond

/*!
 * \ingroup Common
 *
 * \brief Provides a main function which reads in parameters from the
 *        command line and a parameter file and runs the simulation
 *
 * \tparam TypeTag  The type tag of the problem which needs to be solved
 *
 * \param argc The number of command line arguments
 * \param argv The array of the command line arguments
 */
template <class TypeTag>
static inline int start(int argc, char **argv,  bool registerParams=true)
{
    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
    using Simulator = GetPropType<TypeTag, Properties::Simulator>;
    using Problem = GetPropType<TypeTag, Properties::Problem>;
    using ThreadManager = GetPropType<TypeTag, Properties::ThreadManager>;

    // set the signal handlers to reset the TTY to a well defined state on unexpected
    // program aborts
    if (isatty(STDIN_FILENO)) {
        signal(SIGINT, resetTerminal_);
        signal(SIGHUP, resetTerminal_);
        signal(SIGABRT, resetTerminal_);
        signal(SIGFPE, resetTerminal_);
        signal(SIGSEGV, resetTerminal_);
        signal(SIGPIPE, resetTerminal_);
        signal(SIGTERM, resetTerminal_);
    }

    resetLocale();

    int myRank = 0;
    try
    {
        int paramStatus = setupParameters_<TypeTag>(argc, const_cast<const char**>(argv), registerParams);
        if (paramStatus == 1)
            return 1;
        if (paramStatus == 2)
            return 0;

        ThreadManager::init();

        // initialize MPI, finalize is done automatically on exit
#if HAVE_DUNE_FEM
        Dune::Fem::MPIManager::initialize(argc, argv);
        myRank = Dune::Fem::MPIManager::rank();
#else
        myRank = Dune::MPIHelper::instance(argc, argv).rank();
#endif

        // read the initial time step and the end time
        Scalar endTime = EWOMS_GET_PARAM(TypeTag, Scalar, EndTime);
        if (endTime < -1e50) {
            if (myRank == 0)
                Parameters::printUsage<TypeTag>(argv[0],
                                                "Mandatory parameter '--end-time' not specified!");
            return 1;
        }

        Scalar initialTimeStepSize = EWOMS_GET_PARAM(TypeTag, Scalar, InitialTimeStepSize);
        if (initialTimeStepSize < -1e50) {
            if (myRank == 0)
                Parameters::printUsage<TypeTag>(argv[0],
                                                "Mandatory parameter '--initial-time-step-size' "
                                                "not specified!");
            return 1;
        }

        if (myRank == 0) {
#ifdef EWOMS_VERSION
            std::string versionString = EWOMS_VERSION;
#else
            std::string versionString = "";
#endif
            const std::string briefDescription = Problem::briefDescription();
            if (!briefDescription.empty()) {
                std::string tmp = Parameters::breakLines_(briefDescription,
                                                          /*indentWidth=*/0,
                                                          Parameters::getTtyWidth_());
                std::cout << tmp << std::endl << std::endl;
            }
            else
                std::cout << "eWoms " << versionString
                          << " will now start the trip. "
                          << "Please sit back, relax and enjoy the ride.\n"
                          << std::flush;
        }

        // print the parameters if requested
        int printParams = EWOMS_GET_PARAM(TypeTag, int, PrintParameters);
        if (myRank == 0) {
            std::string endParametersSeparator("# [end of parameters]\n");
            if (printParams) {
                bool printSeparator = false;
                if (printParams == 1 || !isatty(fileno(stdout))) {
                    Parameters::printValues<TypeTag>();
                    printSeparator = true;
                }
                else
                    // always print the list of specified but unused parameters
                    printSeparator =
                        printSeparator ||
                        Parameters::printUnused<TypeTag>();
                if (printSeparator)
                    std::cout << endParametersSeparator;
            }
            else
                // always print the list of specified but unused parameters
                if (Parameters::printUnused<TypeTag>())
                    std::cout << endParametersSeparator;
        }

        // print the properties if requested
        int printProps = EWOMS_GET_PARAM(TypeTag, int, PrintProperties);
        if (printProps && myRank == 0) {
            if (printProps == 1 || !isatty(fileno(stdout)))
                Properties::printValues<TypeTag>();
        }

        // instantiate and run the concrete problem. make sure to
        // deallocate the problem and before the time manager and the
        // grid
        Simulator simulator;
        simulator.run();

        if (myRank == 0) {
            std::cout << "eWoms reached the destination. If it is not the one that was intended, "
                      << "change the booking and try again.\n"
                      << std::flush;
        }
        return 0;
    }
    catch (std::exception& e)
    {
        if (myRank == 0) {
            std::cout << e.what() << ". Abort!\n" << std::flush;

            std::cout << "Trying to reset TTY.\n";
            resetTerminal_();
        }

        return 1;
    }
    catch (...)
    {
        if (myRank == 0) {
            std::cout << "Unknown exception thrown!\n" << std::flush;

            std::cout << "Trying to reset TTY.\n";
            resetTerminal_();
        }

        return 3;
    }
}

} // namespace Opm

#endif