// -*- 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.
*/
/*!
* \file
* \copydoc Opm::EclBaseVanguard
*/
#ifndef EWOMS_ECL_BASE_VANGUARD_HH
#define EWOMS_ECL_BASE_VANGUARD_HH
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Opm {
template
class EclBaseVanguard;
}
namespace Opm::Properties {
namespace TTag {
struct EclBaseVanguard {};
}
// declare the properties required by the for the ecl simulator vanguard
template
struct EquilGrid {
using type = UndefinedProperty;
};
template
struct EnableOpmRstFile {
using type = UndefinedProperty;
};
template
struct ParsingStrictness {
using type = UndefinedProperty;
};
template
struct SchedRestart {
using type = UndefinedProperty;
};
template
struct EclOutputInterval {
using type = UndefinedProperty;
};
template
struct IgnoreKeywords {
using type = UndefinedProperty;
};
template
struct EdgeWeightsMethod {
using type = UndefinedProperty;
};
#if HAVE_OPENCL
template
struct NumJacobiBlocks {
using type = UndefinedProperty;
};
#endif // HAVE_OPENCL
template
struct OwnerCellsFirst {
using type = UndefinedProperty;
};
template
struct SerialPartitioning {
using type = UndefinedProperty;
};
template
struct ZoltanImbalanceTol {
using type = UndefinedProperty;
};
template
struct ZoltanParams {
using type = UndefinedProperty;
};
template
struct AllowDistributedWells {
using type = UndefinedProperty;
};
template
struct IgnoreKeywords {
static constexpr auto value = "";
};
template
struct EclDeckFileName {
static constexpr auto value = "";
};
template
struct EclOutputInterval {
static constexpr int value = -1;
};
template
struct EnableOpmRstFile {
static constexpr bool value = false;
};
template
struct ParsingStrictness {
static constexpr auto value = "normal";
};
template
struct SchedRestart {
static constexpr bool value = false;
};
template
struct EdgeWeightsMethod {
static constexpr int value = 1;
};
#if HAVE_OPENCL
template
struct NumJacobiBlocks {
static constexpr int value = 0;
};
#endif // HAVE_OPENCL
template
struct OwnerCellsFirst {
static constexpr bool value = true;
};
template
struct SerialPartitioning {
static constexpr bool value = false;
};
template
struct ZoltanImbalanceTol {
static constexpr double value = 1.1;
};
template
struct ZoltanParams {
static constexpr auto value = "graph";
};
template
struct AllowDistributedWells {
static constexpr bool value = false;
};
template
struct UseMultisegmentWell;
// Same as in BlackoilModelParametersEbos.hpp but for here.
template
struct UseMultisegmentWell {
static constexpr bool value = true;
};
} // namespace Opm::Properties
namespace Opm {
/*!
* \ingroup EclBlackOilSimulator
*
* \brief Helper class for grid instantiation of ECL file-format using problems.
*/
template
class EclBaseVanguard : public BaseVanguard,
public EclGenericVanguard
{
using ParentType = BaseVanguard;
using Implementation = GetPropType;
using Scalar = GetPropType;
using Simulator = GetPropType;
using ElementMapper = GetPropType;
enum { enableExperiments = getPropValue() };
public:
using Grid = GetPropType;
using GridView = GetPropType;
protected:
static const int dimension = Grid::dimension;
static const int dimensionworld = Grid::dimensionworld;
using Element = typename GridView::template Codim<0>::Entity;
using CartesianIndexMapper = Dune::CartesianIndexMapper;
public:
/*!
* \brief Register the common run-time parameters for all ECL simulator vanguards.
*/
static void registerParameters()
{
EWOMS_REGISTER_PARAM(TypeTag, std::string, EclDeckFileName,
"The name of the file which contains the ECL deck to be simulated");
EWOMS_REGISTER_PARAM(TypeTag, int, EclOutputInterval,
"The number of report steps that ought to be skipped between two writes of ECL results");
EWOMS_REGISTER_PARAM(TypeTag, bool, EnableOpmRstFile,
"Include OPM-specific keywords in the ECL restart file to enable restart of OPM simulators from these files");
EWOMS_REGISTER_PARAM(TypeTag, std::string, IgnoreKeywords,
"List of Eclipse keywords which should be ignored. As a ':' separated string.");
EWOMS_REGISTER_PARAM(TypeTag, std::string, ParsingStrictness,
"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");
EWOMS_REGISTER_PARAM(TypeTag, bool, SchedRestart,
"When restarting: should we try to initialize wells and groups from historical SCHEDULE section.");
EWOMS_REGISTER_PARAM(TypeTag, int, EdgeWeightsMethod,
"Choose edge-weighing strategy: 0=uniform, 1=trans, 2=log(trans).");
#if HAVE_OPENCL
EWOMS_REGISTER_PARAM(TypeTag, int, NumJacobiBlocks,
"Number of blocks to be created for the Block-Jacobi preconditioner.");
#endif
EWOMS_REGISTER_PARAM(TypeTag, bool, OwnerCellsFirst,
"Order cells owned by rank before ghost/overlap cells.");
#if HAVE_MPI
EWOMS_REGISTER_PARAM(TypeTag, bool, SerialPartitioning,
"Perform partitioning for parallel runs on a single process.");
EWOMS_REGISTER_PARAM(TypeTag, double, ZoltanImbalanceTol,
"Tolerable imbalance of the loadbalancing provided by Zoltan (default: 1.1).");
EWOMS_REGISTER_PARAM(TypeTag, std::string, ZoltanParams,
"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.");
EWOMS_HIDE_PARAM(TypeTag, ZoltanParams);
#endif
EWOMS_REGISTER_PARAM(TypeTag, bool, AllowDistributedWells,
"Allow the perforations of a well to be distributed to interior of multiple processes");
// register here for the use in the tests without BlackoildModelParametersEbos
EWOMS_REGISTER_PARAM(TypeTag, bool, UseMultisegmentWell, "Use the well model for multi-segment wells instead of the one for single-segment wells");
}
/*!
* \brief Create the grid for problem data files which use the ECL file format.
*
* This is the file format used by the commercial ECLiPSE simulator. Usually it uses
* a cornerpoint description of the grid.
*/
EclBaseVanguard(Simulator& simulator)
: ParentType(simulator)
{
fileName_ = EWOMS_GET_PARAM(TypeTag, std::string, EclDeckFileName);
edgeWeightsMethod_ = Dune::EdgeWeightMethod(EWOMS_GET_PARAM(TypeTag, int, EdgeWeightsMethod));
#if HAVE_OPENCL
numJacobiBlocks_ = EWOMS_GET_PARAM(TypeTag, int, NumJacobiBlocks);
#endif
ownersFirst_ = EWOMS_GET_PARAM(TypeTag, bool, OwnerCellsFirst);
#if HAVE_MPI
serialPartitioning_ = EWOMS_GET_PARAM(TypeTag, bool, SerialPartitioning);
zoltanImbalanceTol_ = EWOMS_GET_PARAM(TypeTag, double, ZoltanImbalanceTol);
zoltanParams_ = EWOMS_GET_PARAM(TypeTag, std::string, ZoltanParams);
#endif
enableDistributedWells_ = EWOMS_GET_PARAM(TypeTag, bool, AllowDistributedWells);
ignoredKeywords_ = EWOMS_GET_PARAM(TypeTag, std::string, IgnoreKeywords);
int output_param = EWOMS_GET_PARAM(TypeTag, int, EclOutputInterval);
if (output_param >= 0)
outputInterval_ = output_param;
useMultisegmentWell_ = EWOMS_GET_PARAM(TypeTag, bool, UseMultisegmentWell);
enableExperiments_ = enableExperiments;
init();
}
const CartesianIndexMapper& cartesianMapper() const
{ return asImp_().cartesianIndexMapper(); }
/*!
* \brief Returns the number of logically Cartesian cells in each direction
*/
const std::array& cartesianDimensions() const
{ return asImp_().cartesianIndexMapper().cartesianDimensions(); }
/*!
* \brief Returns the overall number of cells of the logically Cartesian grid
*/
int cartesianSize() const
{ return asImp_().cartesianIndexMapper().cartesianSize(); }
/*!
* \brief Returns the overall number of cells of the logically EquilCartesian grid
*/
int equilCartesianSize() const
{ return asImp_().equilCartesianIndexMapper().cartesianSize(); }
/*!
* \brief Returns the Cartesian cell id for identifaction with ECL data
*/
unsigned cartesianIndex(unsigned compressedCellIdx) const
{ return asImp_().cartesianIndexMapper().cartesianIndex(compressedCellIdx); }
/*!
* \brief Return the index of the cells in the logical Cartesian grid
*/
unsigned cartesianIndex(const std::array& coords) const
{
unsigned cartIndex = coords[0];
int factor = cartesianDimensions()[0];
for (unsigned i = 1; i < dimension; ++i) {
cartIndex += coords[i]*factor;
factor *= cartesianDimensions()[i];
}
return cartIndex;
}
/*!
* \brief Return compressed index from cartesian index
*
* \return compressed index of cell is in interior, -1 otherwise
*
*/
int compressedIndex(int cartesianCellIdx) const
{
auto index_pair = cartesianToCompressed_.find(cartesianCellIdx);
if (index_pair!=cartesianToCompressed_.end())
return index_pair->second;
else
return -1;
}
/*!
* \brief Return compressed index from cartesian index only in interior
*
* \return compressed index of cell is in interior, -1 otherwise
*
*/
int compressedIndexForInterior(int cartesianCellIdx) const
{
auto index_pair = cartesianToCompressed_.find(cartesianCellIdx);
if (index_pair == cartesianToCompressed_.end() ||
!is_interior_[index_pair->second])
{
return -1;
}
else
{
return index_pair->second;
}
}
/*!
* \brief Extract Cartesian index triplet (i,j,k) of an active cell.
*
* \param [in] cellIdx Active cell index.
* \param [out] ijk Cartesian index triplet
*/
void cartesianCoordinate(unsigned cellIdx, std::array& ijk) const
{ return asImp_().cartesianIndexMapper().cartesianCoordinate(cellIdx, ijk); }
/*!
* \brief Returns the Cartesian cell id given an element index for the grid used for equilibration
*/
unsigned equilCartesianIndex(unsigned compressedEquilCellIdx) const
{ return asImp_().equilCartesianIndexMapper().cartesianIndex(compressedEquilCellIdx); }
/*!
* \brief Extract Cartesian index triplet (i,j,k) of an active cell of the grid used for EQUIL.
*
* \param [in] cellIdx Active cell index.
* \param [out] ijk Cartesian index triplet
*/
void equilCartesianCoordinate(unsigned cellIdx, std::array& ijk) const
{ return asImp_().equilCartesianIndexMapper().cartesianCoordinate(cellIdx, ijk); }
/*!
* \brief Returns the depth of a degree of freedom [m]
*
* For ECL problems this is defined as the average of the depth of an element and is
* thus slightly different from the depth of an element's centroid.
*/
Scalar cellCenterDepth(unsigned globalSpaceIdx) const
{
return cellCenterDepth_[globalSpaceIdx];
}
const std::vector& cellCenterDepths() const
{
return cellCenterDepth_;
}
/*!
* \brief Returns the thickness of a degree of freedom [m]
*
* For ECL problems this is defined as the average of the depths of the top surface
* corners minus the average of the depths of the bottom surface corners
* The cell thickness is computed only when needed.
*/
Scalar cellThickness(unsigned globalSpaceIdx) const
{
assert(!cellThickness_.empty());
return cellThickness_[globalSpaceIdx];
}
/*!
* \brief Get the number of cells in the global leaf grid view.
* \warn This is a collective operation that needs to be called
* on all ranks.
*/
std::size_t globalNumCells() const
{
const auto& grid = asImp_().grid();
if (grid.comm().size() == 1)
{
return grid.leafGridView().size(0);
}
const auto& gridView = grid.leafGridView();
constexpr int codim = 0;
constexpr auto Part = Dune::Interior_Partition;
auto local_cells = std::distance(gridView.template begin(),
gridView.template end());
return grid.comm().sum(local_cells);
}
void setupCartesianToCompressed_() {
this->updateCartesianToCompressedMapping_();
}
protected:
/*!
* \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.
* \param cartMapper The cartesian index mapper for lookup of
* cartesian indices
*/
template
std::function(int)>
cellCentroids_(const CartMapper& cartMapper) const
{
return [this, cartMapper](int elemIdx) {
const auto& centroids = this->centroids_;
auto rank = this->gridView().comm().rank();
std::array centroid;
if (rank == 0) {
unsigned cartesianCellIdx = cartMapper.cartesianIndex(elemIdx);
centroid = this->eclState().getInputGrid().getCellCenter(cartesianCellIdx);
} else
{
std::copy(centroids.begin() + elemIdx * dimensionworld,
centroids.begin() + (elemIdx + 1) * dimensionworld,
centroid.begin());
}
return centroid;
};
}
void callImplementationInit()
{
asImp_().createGrids_();
asImp_().filterConnections_();
std::string outputDir = EWOMS_GET_PARAM(TypeTag, std::string, OutputDir);
bool enableEclCompatFile = !EWOMS_GET_PARAM(TypeTag, bool, EnableOpmRstFile);
asImp_().updateOutputDir_(outputDir, enableEclCompatFile);
asImp_().finalizeInit_();
}
void updateCartesianToCompressedMapping_()
{
size_t num_cells = asImp_().grid().leafGridView().size(0);
is_interior_.resize(num_cells);
ElementMapper elemMapper(this->gridView(), Dune::mcmgElementLayout());
for (const auto& element : elements(this->gridView()))
{
const auto elemIdx = elemMapper.index(element);
unsigned cartesianCellIdx = cartesianIndex(elemIdx);
cartesianToCompressed_[cartesianCellIdx] = elemIdx;
if (element.partitionType() == Dune::InteriorEntity)
{
is_interior_[elemIdx] = 1;
}
else
{
is_interior_[elemIdx] = 0;
}
}
}
void updateCellDepths_()
{
int numCells = this->gridView().size(/*codim=*/0);
cellCenterDepth_.resize(numCells);
ElementMapper elemMapper(this->gridView(), Dune::mcmgElementLayout());
const auto num_aqu_cells = this->allAquiferCells();
for(const auto& element : elements(this->gridView())) {
const unsigned int elemIdx = elemMapper.index(element);
cellCenterDepth_[elemIdx] = cellCenterDepth(element);
if (!num_aqu_cells.empty()) {
const unsigned int global_index = cartesianIndex(elemIdx);
const auto search = num_aqu_cells.find(global_index);
if (search != num_aqu_cells.end()) {
// updating the cell depth using aquifer cell depth
cellCenterDepth_[elemIdx] = search->second->depth;
}
}
}
}
void updateCellThickness_()
{
if (!this->drsdtconEnabled())
return;
ElementMapper elemMapper(this->gridView(), Dune::mcmgElementLayout());
int numElements = this->gridView().size(/*codim=*/0);
cellThickness_.resize(numElements);
for (const auto& elem : elements(this->gridView())) {
const unsigned int elemIdx = elemMapper.index(elem);
cellThickness_[elemIdx] = computeCellThickness(elem);
}
}
private:
// computed from averaging cell corner depths
Scalar cellCenterDepth(const Element& element) const
{
typedef typename Element::Geometry Geometry;
static constexpr int zCoord = Element::dimension - 1;
Scalar zz = 0.0;
const Geometry& geometry = element.geometry();
const int corners = geometry.corners();
for (int i=0; i < corners; ++i)
zz += geometry.corner(i)[zCoord];
return zz/Scalar(corners);
}
Scalar computeCellThickness(const typename GridView::template Codim<0>::Entity& element) const
{
typedef typename Element::Geometry Geometry;
static constexpr int zCoord = Element::dimension - 1;
Scalar zz1 = 0.0;
Scalar zz2 = 0.0;
const Geometry& geometry = element.geometry();
// This code only works with CP-grid where the
// number of corners are 8 and
// also assumes that the first
// 4 corners are the top surface and
// the 4 next are the bottomn.
assert(geometry.corners() == 8);
for (int i=0; i < 4; ++i){
zz1 += geometry.corner(i)[zCoord];
zz2 += geometry.corner(i+4)[zCoord];
}
zz1 /=4;
zz2 /=4;
return zz2-zz1;
}
Implementation& asImp_()
{ return *static_cast(this); }
const Implementation& asImp_() const
{ return *static_cast(this); }
protected:
/*! \brief The cell centroids after loadbalance was called.
* Empty otherwise. Used by EclTransmissibilty.
*/
std::vector centroids_;
/*! \brief Mapping between cartesian and compressed cells.
* It is initialized the first time it is called
*/
std::unordered_map cartesianToCompressed_;
/*! \brief Cell center depths
*/
std::vector cellCenterDepth_;
/*! \brief Cell thickness
*/
std::vector cellThickness_;
/*! \brief Whether a cells is in the interior.
*/
std::vector is_interior_;
};
} // namespace Opm
#endif