// -*- 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 . 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_MPI #include #endif // HAVE_MPI #include #include namespace Opm { std::unique_ptr FlowGenericVanguard::comm_; FlowGenericVanguard::SimulationModelParams FlowGenericVanguard::modelParams_; FlowGenericVanguard::FlowGenericVanguard() : FlowGenericVanguard(std::move(modelParams_)) {} FlowGenericVanguard::FlowGenericVanguard(SimulationModelParams&& params) : python(std::make_shared()) { defineSimulationModel(std::move(params)); } FlowGenericVanguard::SimulationModelParams FlowGenericVanguard::serializationTestParams() { SimulationModelParams result; result.setupTime_ = 1.234; result.actionState_ = std::make_unique(Action::State::serializationTestObject()); result.eclSchedule_ = std::make_unique(Schedule::serializationTestObject()); result.summaryState_ = std::make_unique(SummaryState::serializationTestObject()); result.udqState_ = std::make_unique(UDQState::serializationTestObject()); // Remaining members left as null pointers: wtestState_, eclState_ and eclSummaryConfig_. return result; } FlowGenericVanguard::~FlowGenericVanguard() = default; void FlowGenericVanguard::defineSimulationModel(SimulationModelParams&& params) { actionState_ = std::move(params.actionState_); eclSchedule_ = std::move(params.eclSchedule_); eclState_ = std::move(params.eclState_); eclSummaryConfig_ = std::move(params.eclSummaryConfig_); setupTime_ = params.setupTime_; udqState_ = std::move(params.udqState_); wtestState_ = std::move(params.wtestState_); summaryState_ = std::move(params.summaryState_); } void FlowGenericVanguard::readDeck(const std::string& filename) { Dune::Timer setupTimer; setupTimer.start(); Opm::readDeck(comm(), filename, modelParams_.eclState_, modelParams_.eclSchedule_, modelParams_.udqState_, modelParams_.actionState_, modelParams_.wtestState_, modelParams_.eclSummaryConfig_, nullptr, "normal", "normal", "100", false, false, false, {}); modelParams_.setupTime_ = setupTimer.stop(); } std::string FlowGenericVanguard::canonicalDeckPath(const std::string& caseName) { const auto fileExists = [](const std::filesystem::path& f) -> bool { if (!std::filesystem::exists(f)) return false; if (std::filesystem::is_regular_file(f)) return true; return std::filesystem::is_symlink(f) && std::filesystem::is_regular_file(std::filesystem::read_symlink(f)); }; auto simcase = std::filesystem::path(caseName); if (fileExists(simcase)) return simcase.string(); for (const auto& ext : { std::string("data"), std::string("DATA") }) { if (fileExists(simcase.replace_extension(ext))) return simcase.string(); } throw std::invalid_argument("Cannot find input case '"+caseName+"'"); } void FlowGenericVanguard::updateNOSIM_(std::string_view dryRunString) { try { // Possible to force initialization only behavior (NOSIM). if (dryRunString != "" && dryRunString != "auto") { bool enableDryRun; if (dryRunString == "true" || dryRunString == "t" || dryRunString == "1") enableDryRun = true; else if (dryRunString == "false" || dryRunString == "f" || dryRunString == "0") enableDryRun = false; else throw std::invalid_argument("Invalid value for parameter EnableDryRun: '" + std::string(dryRunString) + "'"); auto& ioConfig = eclState().getIOConfig(); ioConfig.overrideNOSIM(enableDryRun); } } catch (const std::invalid_argument& e) { std::cerr << "Failed to create valid EclipseState object" << std::endl; std::cerr << "Exception caught: " << e.what() << std::endl; throw; } } void FlowGenericVanguard::updateOutputDir_(std::string outputDir, bool enableEclCompatFile) { // update the location for output auto& ioConfig = eclState_->getIOConfig(); if (outputDir.empty()) // If no output directory parameter is specified, use the output directory // which Opm::IOConfig thinks that should be used. Normally this is the // directory in which the input files are located. outputDir = ioConfig.getOutputDir(); // ensure that the output directory exists and that it is a directory if (!std::filesystem::is_directory(outputDir)) { try { std::filesystem::create_directories(outputDir); } catch (...) { throw std::runtime_error("Creation of output directory '"+outputDir+"' failed\n"); } } // specify the directory output. This is not a very nice mechanism because // the eclState is supposed to be immutable here, IMO. ioConfig.setOutputDir(outputDir); ioConfig.setEclCompatibleRST(enableEclCompatFile); } void FlowGenericVanguard::init() { // Make proper case name. { if (fileName_.empty()) throw std::runtime_error("No input deck file has been specified as a command line argument," " or via '--ecl-deck-file-name=CASE.DATA'"); fileName_ = canonicalDeckPath(fileName_); // compute the base name of the input file name const char directorySeparator = '/'; long int i; for (i = fileName_.size(); i >= 0; -- i) if (fileName_[i] == directorySeparator) break; std::string baseName = fileName_.substr(i + 1, fileName_.size()); // remove the extension from the input file for (i = baseName.size(); i >= 0; -- i) if (baseName[i] == '.') break; std::string rawCaseName; if (i < 0) rawCaseName = baseName; else rawCaseName = baseName.substr(0, i); // transform the result to ALL_UPPERCASE caseName_ = rawCaseName; std::transform(caseName_.begin(), caseName_.end(), caseName_.begin(), ::toupper); } // set communicator if not set as in opm flow if(!comm_){ FlowGenericVanguard::setCommunication(std::make_unique()); } // set eclState if not already set as in opm flow // it means that setParams is called if(!eclState_){ this->readDeck(fileName_); this->defineSimulationModel(std::move(this->modelParams_)); } if (!this->summaryState_) { this->summaryState_ = std::make_unique (TimeService::from_time_t(this->eclSchedule_->getStartTime()), this->eclState_->runspec().udqParams().undefinedValue()); } // Initialize parallelWells with all local wells const auto& schedule_wells = schedule().getWellsatEnd(); parallelWells_.reserve(schedule_wells.size()); for (const auto& well: schedule_wells) { parallelWells_.emplace_back(well.name(), true); } std::sort(parallelWells_.begin(), parallelWells_.end()); // Check whether allowing distribute wells makes sense if (enableDistributedWells() ) { int hasMsWell = false; const auto& comm = FlowGenericVanguard::comm(); if (useMultisegmentWell_) { if (comm.rank() == 0) { const auto& wells = this->schedule().getWellsatEnd(); for (const auto& well : wells) { hasMsWell = hasMsWell || well.isMultiSegment(); } } } hasMsWell = comm.max(hasMsWell); if (hasMsWell) { if (comm.rank() == 0) { std::string message = std::string("Option --allow-distributed-wells=true is only allowed if model\n") + "only has only standard wells. You need to provide option \n" + " with --enable-multisegement-wells=false to treat existing \n" + "multisegment wells as standard wells."; OpmLog::error(message); } comm.barrier(); OPM_THROW(std::invalid_argument, "All wells need to be standard wells!"); } } } bool FlowGenericVanguard::drsdtconEnabled() const { for (const auto& schIt : this->schedule()) { const auto& oilVaporizationControl = schIt.oilvap(); if (oilVaporizationControl.getType() == OilVaporizationProperties::OilVaporization::DRSDTCON) { return true; } } return false; } std::unordered_map FlowGenericVanguard::allAquiferCells() const { return this->eclState_->aquifer().numericalAquifers().allAquiferCells(); } template<> void FlowGenericVanguard:: serializeOp>(Serializer& serializer) { serializer(*summaryState_); serializer(*udqState_); serializer(*actionState_); serializer(*eclSchedule_); } bool FlowGenericVanguard::operator==(const FlowGenericVanguard& rhs) const { auto cmp_ptr = [](const auto& a, const auto& b) { if (!a && !b) { return true; } if (a && b) { return *a == *b; } return false; }; return cmp_ptr(this->summaryState_, rhs.summaryState_); cmp_ptr(this->udqState_, rhs.udqState_) && cmp_ptr(this->actionState_, rhs.actionState_) && cmp_ptr(this->eclSchedule_, rhs.eclSchedule_); } template void FlowGenericVanguard::registerParameters_() { Parameters::Register ("The name of the file which contains the ECL deck to be simulated"); Parameters::Register ("The number of report steps that ought to be skipped between two writes of ECL results"); Parameters::Register ("Specify if the simulation ought to be actually run, or just pretended to be"); Parameters::Register ("Include OPM-specific keywords in the ECL restart file to " "enable restart of OPM simulators from these files"); Parameters::Register ("List of Eclipse keywords which should be ignored. As a ':' separated string."); Parameters::Register ("Set strictness of parsing process. Available options are " "normal (stop for critical errors), " "high (stop for all errors) and " "low (as normal, except do not stop due to unsupported " "keywords even if marked critical"); Parameters::Register ("Set strictness of parsing process for ActionX and PyAction. Available options are " "normal (do not apply keywords that have not been tested for ActionX or PyAction) and " "low (try to apply all keywords, beware: the simulation outcome might be incorrect)."); Parameters::Register ("Set compatibility mode for the SKIP100/SKIP300 keywords. Options are " "100 (skip SKIP100..ENDSKIP, keep SKIP300..ENDSKIP) [default], " "300 (skip SKIP300..ENDSKIP, keep SKIP100..ENDSKIP) and " "all (skip both SKIP100..ENDSKIP and SKIP300..ENDSKIP) "); Parameters::Register ("When restarting: should we try to initialize wells and " "groups from historical SCHEDULE section."); Parameters::Register ("Choose edge-weighing strategy: 0=uniform, 1=trans, 2=log(trans)."); #if HAVE_OPENCL || HAVE_ROCSPARSE || HAVE_CUDA Parameters::Register ("Number of blocks to be created for the Block-Jacobi preconditioner."); #endif Parameters::Register ("Order cells owned by rank before ghost/overlap cells."); #if HAVE_MPI Parameters::Register ("Choose partitioning strategy: 0=simple, 1=Zoltan, 2=METIS."); Parameters::Register ("Perform partitioning for parallel runs on a single process."); Parameters::Register> ("Tolerable imbalance of the loadbalancing provided by Zoltan. DEPRECATED: Use --imbalance-tol instead"); Parameters::Register ("Configuration of Zoltan partitioner. " "Valid options are: graph, hypergraph or scotch. " "Alternatively, you can request a configuration to be read " "from a JSON file by giving the filename here, ending with '.json.' " "See https://sandialabs.github.io/Zoltan/ug_html/ug.html " "for available Zoltan options."); Parameters::Register> ("Tolerable imbalance of the loadbalancing (default: 1.1)."); Parameters::Register ("Configuration of Metis partitioner. " "You can request a configuration to be read " "from a JSON file by giving the filename here, ending with '.json.' " "See http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/manual.pdf" "for available METIS options."); Parameters::Register ("Name of file from which to load an externally generated " "partitioning of the model's active cells for MPI " "distribution purposes. If empty, the built-in partitioning " "method will be employed."); Parameters::Hide(); Parameters::Hide>(); Parameters::Hide(); #endif Parameters::Register ("Allow the perforations of a well to be distributed to interior of multiple processes"); Parameters::Register ("Allow inactive (never non-shut) wells to be split across multiple domains"); // register here for the use in the tests without BlackoilModelParameters Parameters::Register ("Use the well model for multi-segment wells instead of the one for single-segment wells"); } template void FlowGenericVanguard::registerParameters_(); #if FLOW_INSTANTIATE_FLOAT template void FlowGenericVanguard::registerParameters_(); #endif } // namespace Opm