opm-simulators/examples/flow.cpp
Andreas Lauser cede806bd5 flow: refactor the specializations
The motivation for this PR is that currently the build fails on my
Ubuntu 17.10 laptop with two processes because that machine "only" has
8 GB of RAM (granted, the optimization options may have been a bit too
excessive). under the new scheme, each specialization of the simulator
is put into a separate compile unit which is part of
libopmsimulators. this has the advantages that the specialized
simulators and the main binary automatically stay consistent, the
compilation is faster (2m25s vs 4m16s on my machine) because all
compile units can be built in parallel and that compilation takes up
less RAM because there is no need to instantiate all specializations
in a single compile unit.

on the minus side, all specializations must now always be compiled,
the approach means slightly more work for the maintainers and the
flow_* startup code gets even more complicated.
2017-10-06 15:35:00 +02:00

201 lines
7.4 KiB
C++

/*
Copyright 2013, 2014, 2015 SINTEF ICT, Applied Mathematics.
Copyright 2014 Dr. Blatt - HPC-Simulation-Software & Services
Copyright 2015, 2017 IRIS AS
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 3 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/>.
*/
#include "config.h"
#include <opm/simulators/flow_ebos_blackoil.hpp>
#include <opm/simulators/flow_ebos_gasoil.hpp>
#include <opm/simulators/flow_ebos_oilwater.hpp>
#include <opm/simulators/flow_ebos_solvent.hpp>
#include <opm/simulators/flow_ebos_polymer.hpp>
#include <opm/autodiff/MissingFeatures.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/common/ResetLocale.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/checkDeck.hpp>
#if HAVE_DUNE_FEM
#include <dune/fem/misc/mpimanager.hh>
#else
#include <dune/common/parallel/mpihelper.hh>
#endif
namespace detail
{
boost::filesystem::path simulationCaseName( const std::string& casename ) {
namespace fs = boost::filesystem;
const auto exists = []( const fs::path& f ) -> bool {
if( !fs::exists( f ) ) return false;
if( fs::is_regular_file( f ) ) return true;
return fs::is_symlink( f )
&& fs::is_regular_file( fs::read_symlink( f ) );
};
auto simcase = fs::path( casename );
if( exists( simcase ) ) {
return simcase;
}
for( const auto& ext : { std::string("data"), std::string("DATA") } ) {
if( exists( simcase.replace_extension( ext ) ) ) {
return simcase;
}
}
throw std::invalid_argument( "Cannot find input case " + casename );
}
}
// ----------------- Main program -----------------
int main(int argc, char** argv)
{
// MPI setup.
#if HAVE_DUNE_FEM
Dune::Fem::MPIManager::initialize(argc, argv);
int mpiRank = Dune::Fem::MPIManager::rank();
#else
// the design of the plain dune MPIHelper class is quite flawed: there is no way to
// get the instance without having the argc and argv parameters available and it is
// not possible to determine the MPI rank and size without an instance. (IOW: the
// rank() and size() methods are supposed to be static.)
const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv);
int mpiRank = mpiHelper.rank();
#endif
const bool outputCout = (mpiRank == 0);
// we always want to use the default locale, and thus spare us the trouble
// with incorrect locale settings.
Opm::resetLocale();
Opm::ParameterGroup param(argc, argv, false, outputCout);
// See if a deck was specified on the command line.
if (!param.unhandledArguments().empty()) {
if (param.unhandledArguments().size() != 1) {
if (outputCout)
std::cerr << "You can only specify a single input deck on the command line.\n";
return EXIT_FAILURE;
} else {
const auto casename = detail::simulationCaseName( param.unhandledArguments()[ 0 ] );
param.insertParameter("deck_filename", casename.string() );
}
}
// We must have an input deck. Grid and props will be read from that.
if (!param.has("deck_filename")) {
if (outputCout)
std::cerr << "This program must be run with an input deck.\n"
"Specify the deck filename either\n"
" a) as a command line argument by itself\n"
" b) as a command line parameter with the syntax deck_filename=<path to your deck>, or\n"
" c) as a parameter in a parameter file (.param or .xml) passed to the program.\n";
return EXIT_FAILURE;
}
std::string deckFilename = param.get<std::string>("deck_filename");
// Create Deck and EclipseState.
try {
Opm::Parser parser;
typedef std::pair<std::string, Opm::InputError::Action> ParseModePair;
typedef std::vector<ParseModePair> ParseModePairs;
ParseModePairs tmp;
tmp.push_back(ParseModePair(Opm::ParseContext::PARSE_RANDOM_SLASH, Opm::InputError::IGNORE));
tmp.push_back(ParseModePair(Opm::ParseContext::PARSE_MISSING_DIMS_KEYWORD, Opm::InputError::WARN));
tmp.push_back(ParseModePair(Opm::ParseContext::SUMMARY_UNKNOWN_WELL, Opm::InputError::WARN));
tmp.push_back(ParseModePair(Opm::ParseContext::SUMMARY_UNKNOWN_GROUP, Opm::InputError::WARN));
Opm::ParseContext parseContext(tmp);
std::shared_ptr<Opm::Deck> deck = std::make_shared< Opm::Deck >( parser.parseFile(deckFilename , parseContext) );
if ( outputCout ) {
Opm::checkDeck(*deck, parser);
Opm::MissingFeatures::checkKeywords(*deck);
}
std::shared_ptr<Opm::EclipseState> eclipseState =
std::make_shared< Opm::EclipseState > ( *deck, parseContext );
Opm::Runspec runspec( *deck );
const auto& phases = runspec.phases();
// Twophase cases
if( phases.size() == 2 ) {
// oil-gas
if (phases.active( Opm::Phase::GAS ))
{
Opm::flowEbosGasOilSetDeck(*deck, *eclipseState);
return Opm::flowEbosGasOilMain(argc, argv);
}
// oil-water
else if ( phases.active( Opm::Phase::WATER ) )
{
Opm::flowEbosOilWaterSetDeck(*deck, *eclipseState);
return Opm::flowEbosOilWaterMain(argc, argv);
}
else {
if (outputCout)
std::cerr << "No suitable configuration found, valid are Twophase (oilwater and oilgas), polymer, solvent, or blackoil" << std::endl;
return EXIT_FAILURE;
}
}
// Polymer case
else if ( phases.active( Opm::Phase::POLYMER ) ) {
Opm::flowEbosPolymerSetDeck(*deck, *eclipseState);
return Opm::flowEbosPolymerMain(argc, argv);
}
// Solvent case
else if ( phases.active( Opm::Phase::SOLVENT ) ) {
Opm::flowEbosSolventSetDeck(*deck, *eclipseState);
return Opm::flowEbosSolventMain(argc, argv);
}
// Blackoil case
else if( phases.size() == 3 ) {
Opm::flowEbosBlackoilSetDeck(*deck, *eclipseState);
return Opm::flowEbosBlackoilMain(argc, argv);
}
else
{
if (outputCout)
std::cerr << "No suitable configuration found, valid are Twophase, polymer, solvent, or blackoil" << std::endl;
return EXIT_FAILURE;
}
}
catch (const std::invalid_argument& e)
{
if (outputCout) {
std::cerr << "Failed to create valid EclipseState object." << std::endl;
std::cerr << "Exception caught: " << e.what() << std::endl;
}
throw;
}
return EXIT_SUCCESS;
}