mirror of
https://github.com/OPM/opm-simulators.git
synced 2024-12-02 05:49:09 -06:00
c0c96123bc
This commit adds a new (hidden) debugging option, DebugEmitCellPartition (--debug-emit-cell-partition) which, when set, will cause each rank to write a three-column text file of the form MPI_Rank Cartesian_Index NLDD_Domain_ID into the directory partition/CaseName of the run's output directory. That file will be named according to the process' MPI rank, so the first column will be the same as the file name. The option is primarily intended for debugging the NLDD partitioning scheme, so is mostly reserved for runs with low MPI sizes (e.g., less than 20). While here, also make the MPIPartitionFromFile helper class aware of this format so that we can use concatenated output files as an input to the MPI partitioning algorithm for repeatability.
394 lines
13 KiB
C++
394 lines
13 KiB
C++
// -*- 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
|
|
* \copydoc Opm::EclCpGridVanguard
|
|
*/
|
|
#ifndef EWOMS_ECL_CP_GRID_VANGUARD_HH
|
|
#define EWOMS_ECL_CP_GRID_VANGUARD_HH
|
|
|
|
#include <ebos/eclbasevanguard.hh>
|
|
#include <ebos/eclgenericcpgridvanguard.hh>
|
|
#include <ebos/ecltransmissibility.hh>
|
|
#include <ebos/femcpgridcompat.hh>
|
|
|
|
#include <opm/common/TimingMacros.hpp>
|
|
|
|
#include <opm/models/common/multiphasebaseproperties.hh>
|
|
#include <opm/models/blackoil/blackoilproperties.hh>
|
|
|
|
#include <array>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
#if HAVE_MPI
|
|
|
|
namespace Opm { namespace details {
|
|
class MPIPartitionFromFile
|
|
{
|
|
public:
|
|
explicit MPIPartitionFromFile(const std::filesystem::path& partitionFile)
|
|
: partitionFile_(partitionFile)
|
|
{}
|
|
|
|
std::vector<int> operator()(const Dune::CpGrid& grid) const;
|
|
|
|
private:
|
|
std::filesystem::path partitionFile_{};
|
|
};
|
|
|
|
inline std::vector<int>
|
|
MPIPartitionFromFile::operator()(const Dune::CpGrid& grid) const
|
|
{
|
|
std::ifstream pfile { this->partitionFile_ };
|
|
|
|
auto partition = std::vector<int> {
|
|
std::istream_iterator<int> { pfile },
|
|
std::istream_iterator<int> {}
|
|
};
|
|
|
|
const auto nc =
|
|
static_cast<std::vector<int>::size_type>(grid.size(0));
|
|
|
|
if (partition.size() == nc) {
|
|
// Input is one process ID for each active cell
|
|
return partition;
|
|
}
|
|
|
|
if (partition.size() == 3 * nc) {
|
|
// Partition file is of the form
|
|
//
|
|
// Process_ID Cartesian_Idx NLDD_Domain
|
|
//
|
|
// with one row for each active cell. Select first column.
|
|
auto g2l = std::unordered_map<int, int>{};
|
|
auto locCell = 0;
|
|
for (const auto& globCell : grid.globalCell()) {
|
|
g2l.insert_or_assign(globCell, locCell++);
|
|
}
|
|
|
|
auto filtered_partition = std::vector<int>(nc);
|
|
for (auto c = 0*nc; c < nc; ++c) {
|
|
auto pos = g2l.find(partition[3*c + 1]);
|
|
if (pos != g2l.end()) {
|
|
filtered_partition[pos->second] = partition[3*c + 0];
|
|
}
|
|
}
|
|
|
|
return filtered_partition;
|
|
}
|
|
|
|
throw std::invalid_argument {
|
|
fmt::format("Partition file '{}' with {} values does "
|
|
"not match CpGrid instance with {} cells",
|
|
this->partitionFile_.generic_string(),
|
|
partition.size(), nc)
|
|
};
|
|
}
|
|
}} // namespace Opm::details
|
|
|
|
#endif // HAVE_MPI
|
|
|
|
namespace Opm {
|
|
template <class TypeTag>
|
|
class EclCpGridVanguard;
|
|
}
|
|
|
|
namespace Opm::Properties {
|
|
|
|
namespace TTag {
|
|
struct EclCpGridVanguard {
|
|
using InheritsFrom = std::tuple<EclBaseVanguard>;
|
|
};
|
|
}
|
|
|
|
// declare the properties
|
|
template<class TypeTag>
|
|
struct Vanguard<TypeTag, TTag::EclCpGridVanguard> {
|
|
using type = EclCpGridVanguard<TypeTag>;
|
|
};
|
|
template<class TypeTag>
|
|
struct Grid<TypeTag, TTag::EclCpGridVanguard> {
|
|
using type = Dune::CpGrid;
|
|
};
|
|
template<class TypeTag>
|
|
struct EquilGrid<TypeTag, TTag::EclCpGridVanguard> {
|
|
using type = GetPropType<TypeTag, Properties::Grid>;
|
|
};
|
|
|
|
} // namespace Opm::Properties
|
|
|
|
namespace Opm {
|
|
|
|
/*!
|
|
* \ingroup EclBlackOilSimulator
|
|
*
|
|
* \brief Helper class for grid instantiation of ECL file-format using problems.
|
|
*
|
|
* This class uses Dune::CpGrid as the simulation grid.
|
|
*/
|
|
template <class TypeTag>
|
|
class EclCpGridVanguard : public EclBaseVanguard<TypeTag>
|
|
, public EclGenericCpGridVanguard<GetPropType<TypeTag, Properties::ElementMapper>,
|
|
GetPropType<TypeTag, Properties::GridView>,
|
|
GetPropType<TypeTag, Properties::Scalar>>
|
|
{
|
|
friend class EclBaseVanguard<TypeTag>;
|
|
using ParentType = EclBaseVanguard<TypeTag>;
|
|
|
|
using Scalar = GetPropType<TypeTag, Properties::Scalar>;
|
|
using Simulator = GetPropType<TypeTag, Properties::Simulator>;
|
|
using ElementMapper = GetPropType<TypeTag, Properties::ElementMapper>;
|
|
|
|
public:
|
|
using Grid = GetPropType<TypeTag, Properties::Grid>;
|
|
using CartesianIndexMapper = Dune::CartesianIndexMapper<Grid>;
|
|
using EquilGrid = GetPropType<TypeTag, Properties::EquilGrid>;
|
|
using GridView = GetPropType<TypeTag, Properties::GridView>;
|
|
using TransmissibilityType = EclTransmissibility<Grid, GridView, ElementMapper, CartesianIndexMapper, Scalar>;
|
|
static constexpr int dimensionworld = Grid::dimensionworld;
|
|
using Indices = GetPropType<TypeTag, Properties::Indices>;
|
|
static constexpr bool waterEnabled = Indices::waterEnabled;
|
|
static constexpr bool gasEnabled = Indices::gasEnabled;
|
|
static constexpr bool oilEnabled = Indices::oilEnabled;
|
|
private:
|
|
using Element = typename GridView::template Codim<0>::Entity;
|
|
|
|
public:
|
|
EclCpGridVanguard(Simulator& simulator)
|
|
: EclBaseVanguard<TypeTag>(simulator)
|
|
{
|
|
this->checkConsistency();
|
|
this->callImplementationInit();
|
|
}
|
|
|
|
/*!
|
|
* Checking consistency of simulator
|
|
*/
|
|
void checkConsistency()
|
|
{
|
|
const auto& runspec = this->eclState().runspec();
|
|
const auto& config = this->eclState().getSimulationConfig();
|
|
const auto& phases = runspec.phases();
|
|
|
|
// check for correct module setup
|
|
if (config.isThermal()) {
|
|
if (getPropValue<TypeTag, Properties::EnableEnergy>() == false) {
|
|
throw std::runtime_error("Input specifies energy while simulator has disabled it, try xxx_energy");
|
|
}
|
|
} else {
|
|
if (getPropValue<TypeTag, Properties::EnableEnergy>() == true) {
|
|
throw std::runtime_error("Input specifies no energy while simulator has energy, try run without _energy");
|
|
}
|
|
}
|
|
|
|
if (config.isDiffusive()) {
|
|
if (getPropValue<TypeTag, Properties::EnableDiffusion>() == false) {
|
|
throw std::runtime_error("Input specifies diffusion while simulator has disabled it, try xxx_diffusion");
|
|
}
|
|
}
|
|
|
|
if (runspec.micp()) {
|
|
if (getPropValue<TypeTag, Properties::EnableMICP>() == false) {
|
|
throw std::runtime_error("Input specifies MICP while simulator has it disabled");
|
|
}
|
|
}
|
|
|
|
if (phases.active(Phase::BRINE)) {
|
|
if (getPropValue<TypeTag, Properties::EnableBrine>() == false) {
|
|
throw std::runtime_error("Input specifies Brine while simulator has it disabled");
|
|
}
|
|
}
|
|
|
|
if (phases.active(Phase::POLYMER)) {
|
|
if (getPropValue<TypeTag, Properties::EnablePolymer>() == false) {
|
|
throw std::runtime_error("Input specifies Polymer while simulator has it disabled");
|
|
}
|
|
}
|
|
|
|
// checking for correct phases is more difficult TODO!
|
|
if (phases.active(Phase::ZFRACTION)) {
|
|
if (getPropValue<TypeTag, Properties::EnableExtbo>() == false) {
|
|
throw std::runtime_error("Input specifies ExBo while simulator has it disabled");
|
|
}
|
|
}
|
|
if (phases.active(Phase::FOAM)) {
|
|
if (getPropValue<TypeTag, Properties::EnableFoam>() == false) {
|
|
throw std::runtime_error("Input specifies Foam while simulator has it disabled");
|
|
}
|
|
}
|
|
|
|
if (phases.active(Phase::SOLVENT)) {
|
|
if (getPropValue<TypeTag, Properties::EnableSolvent>() == false) {
|
|
throw std::runtime_error("Input specifies Solvent while simulator has it disabled");
|
|
}
|
|
}
|
|
if(phases.active(Phase::WATER)){
|
|
if(waterEnabled == false){
|
|
throw std::runtime_error("Input specifies water while simulator has it disabled");
|
|
}
|
|
}
|
|
if(phases.active(Phase::GAS)){
|
|
if(gasEnabled == false){
|
|
throw std::runtime_error("Input specifies gas while simulator has it disabled");
|
|
}
|
|
}
|
|
if(phases.active(Phase::OIL)){
|
|
if(oilEnabled == false){
|
|
throw std::runtime_error("Input specifies oil while simulator has it disabled");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*!
|
|
* \brief Free the memory occupied by the global transmissibility object.
|
|
*
|
|
* After writing the initial solution, this array should not be necessary anymore.
|
|
*/
|
|
void releaseGlobalTransmissibilities()
|
|
{
|
|
globalTrans_.reset();
|
|
}
|
|
|
|
const TransmissibilityType& globalTransmissibility() const
|
|
{
|
|
assert( globalTrans_ != nullptr );
|
|
return *globalTrans_;
|
|
}
|
|
|
|
void releaseGlobalTransmissibility()
|
|
{
|
|
globalTrans_.reset();
|
|
}
|
|
|
|
/*!
|
|
* \brief Distribute the simulation grid over multiple processes
|
|
*
|
|
* (For parallel simulation runs.)
|
|
*/
|
|
void loadBalance()
|
|
{
|
|
#if HAVE_MPI
|
|
if (const auto& extPFile = this->externalPartitionFile();
|
|
!extPFile.empty() && (extPFile != "none"))
|
|
{
|
|
this->setExternalLoadBalancer(details::MPIPartitionFromFile { extPFile });
|
|
}
|
|
|
|
this->doLoadBalance_(this->edgeWeightsMethod(), this->ownersFirst(),
|
|
this->serialPartitioning(), this->enableDistributedWells(),
|
|
this->zoltanImbalanceTol(), this->gridView(),
|
|
this->schedule(), this->eclState(),
|
|
this->parallelWells_, this->numJacobiBlocks());
|
|
#endif
|
|
|
|
this->updateGridView_();
|
|
this->updateCartesianToCompressedMapping_();
|
|
this->updateCellDepths_();
|
|
this->updateCellThickness_();
|
|
|
|
#if HAVE_MPI
|
|
this->distributeFieldProps_(this->eclState());
|
|
#endif
|
|
}
|
|
|
|
unsigned int gridEquilIdxToGridIdx(unsigned int elemIndex) const {
|
|
return elemIndex;
|
|
}
|
|
|
|
unsigned int gridIdxToEquilGridIdx(unsigned int elemIndex) const {
|
|
return elemIndex;
|
|
}
|
|
/*!
|
|
* \brief Get function to query cell centroids for a distributed grid.
|
|
*
|
|
* Currently this only non-empty for a loadbalanced CpGrid.
|
|
* It is a function return the centroid for the given element
|
|
* index.
|
|
*/
|
|
std::function<std::array<double,dimensionworld>(int)>
|
|
cellCentroids() const
|
|
{
|
|
return this->cellCentroids_(this->cartesianIndexMapper(), true);
|
|
}
|
|
|
|
const std::vector<int>& globalCell()
|
|
{
|
|
return this->grid().globalCell();
|
|
}
|
|
|
|
protected:
|
|
void createGrids_()
|
|
{
|
|
this->doCreateGrids_(this->eclState());
|
|
}
|
|
|
|
void allocTrans() override
|
|
{
|
|
OPM_TIMEBLOCK(allocateTrans);
|
|
globalTrans_.reset(new TransmissibilityType(this->eclState(),
|
|
this->gridView(),
|
|
this->cartesianIndexMapper(),
|
|
this->grid(),
|
|
this->cellCentroids(),
|
|
getPropValue<TypeTag, Properties::EnableEnergy>(),
|
|
getPropValue<TypeTag, Properties::EnableDiffusion>(),
|
|
getPropValue<TypeTag, Properties::EnableDispersion>()));
|
|
globalTrans_->update(false);
|
|
}
|
|
|
|
double getTransmissibility(unsigned I, unsigned J) const override
|
|
{
|
|
return globalTrans_->transmissibility(I,J);
|
|
}
|
|
|
|
#if HAVE_MPI
|
|
const std::string& zoltanParams() const override
|
|
{
|
|
return this->zoltanParams_;
|
|
}
|
|
#endif
|
|
|
|
// removing some connection located in inactive grid cells
|
|
void filterConnections_()
|
|
{
|
|
this->doFilterConnections_(this->schedule());
|
|
}
|
|
|
|
std::unique_ptr<TransmissibilityType> globalTrans_;
|
|
};
|
|
|
|
} // namespace Opm
|
|
|
|
#endif
|