23#ifndef OPM_TRANSMISSIBILITY_IMPL_HPP
24#define OPM_TRANSMISSIBILITY_IMPL_HPP
26#ifndef OPM_TRANSMISSIBILITY_HPP
31#include <dune/common/version.hh>
32#include <dune/grid/common/mcmgmapper.hh>
34#include <opm/common/OpmLog/KeywordLocation.hpp>
35#include <opm/common/utility/ThreadSafeMapBuilder.hpp>
37#include <opm/grid/CpGrid.hpp>
38#include <opm/grid/utility/ElementChunks.hpp>
40#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
41#include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
42#include <opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
43#include <opm/input/eclipse/EclipseState/Grid/TransMult.hpp>
44#include <opm/input/eclipse/Units/Units.hpp>
55#include <initializer_list>
64#include <fmt/format.h>
72 std::uint64_t
isId(std::uint32_t elemIdx1, std::uint32_t elemIdx2)
74 const std::uint32_t elemAIdx = std::min(elemIdx1, elemIdx2);
75 const std::uint64_t elemBIdx = std::max(elemIdx1, elemIdx2);
80 std::pair<std::uint32_t, std::uint32_t>
isIdReverse(
const std::uint64_t&
id)
85 const std::uint32_t elemAIdx =
static_cast<uint32_t
>(id);
86 const std::uint32_t elemBIdx = (
id - elemAIdx) >>
elemIdxShift;
88 return std::make_pair(elemAIdx, elemBIdx);
93 return (std::uint64_t(elemIdx1) <<
elemIdxShift) + elemIdx2;
97template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
100 const GridView& gridView,
101 const CartesianIndexMapper& cartMapper,
103 std::function<std::array<double,dimWorld>(
int)> centroids,
105 bool enableDiffusivity,
106 bool enableDispersivity)
107 : eclState_(eclState)
108 , gridView_(gridView)
109 , cartMapper_(cartMapper)
111 , centroids_(centroids)
112 , enableEnergy_(enableEnergy)
113 , enableDiffusivity_(enableDiffusivity)
114 , enableDispersivity_(enableDispersivity)
115 , lookUpData_(gridView)
116 , lookUpCartesianData_(gridView, cartMapper)
118 const UnitSystem& unitSystem =
eclState_.getDeckUnitSystem();
122template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
124gridJoins_(
unsigned elemIdx1,
unsigned elemIdx2)
const
128 ElementMapper elemMapper(gridView_, Dune::mcmgElementLayout());
130 for (
const auto& elem : elements(gridView_)) {
131 if (elemMapper.index(elem) !=
static_cast<Ix
>(elemIdx1)) {
135 for (
const auto& intersection : intersections(gridView_, elem)) {
136 if (intersection.neighbor() &&
137 (elemMapper.index(intersection.outside()) ==
static_cast<Ix
>(elemIdx2)))
149template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
153 auto text = fmt::format(
"cell {}", elemIdx);
155 if (elemIdx >=
static_cast<unsigned>(gridView_.size(0))) {
156 return text +
" (out of range)";
159 const auto cartIdx = cartMapper_.cartesianIndex(elemIdx);
160 std::array<int,dimWorld> ijk{};
161 cartMapper_.cartesianCoordinate(elemIdx, ijk);
163 text += fmt::format(
" (Cartesian {} = [{},{},{}]", cartIdx, ijk[0], ijk[1], ijk[2]);
165 if constexpr (
requires { grid_.maxLevel(); }) {
166 if (grid_.maxLevel() > 0) {
173 ElementMapper elemMapper(gridView_, Dune::mcmgElementLayout());
174 for (
const auto& elem : elements(gridView_)) {
175 if (elemMapper.index(elem) ==
static_cast<Ix
>(elemIdx)) {
176 text += fmt::format(
", level {}", elem.level());
186template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
188lookupTrans_(
const std::unordered_map<std::uint64_t, Scalar>& map,
189 unsigned elemIdx1,
unsigned elemIdx2, std::string_view what)
const
191 const auto entry = map.find(
details::isId(elemIdx1, elemIdx2));
192 if (entry == map.end()) {
197 const auto& cartDims = cartMapper_.cartesianDimensions();
198 const auto gc1 = cartMapper_.cartesianIndex(std::min(elemIdx1, elemIdx2));
199 const auto gc2 = cartMapper_.cartesianIndex(std::max(elemIdx1, elemIdx2));
200 const auto delta = gc2 - gc1;
202 (delta == 0) ?
"same coarse cell (refined siblings)" :
203 (delta == 1) ?
"neighbours in I" :
204 (delta == cartDims[0]) ?
"neighbours in J" :
205 (delta == cartDims[0]*cartDims[1]) ?
"neighbours in K" :
206 ((delta % (cartDims[0]*cartDims[1])) == 0) ?
"same column, several layers apart (a pinch-out or vertical NNC)"
207 :
"not Cartesian neighbours (a non-neighbour connection)";
212 const auto joined = this->gridJoins_(elemIdx1, elemIdx2);
214 OPM_THROW(std::out_of_range,
215 fmt::format(
"No {} between {} and {}: {}. {}",
216 what, this->describeCell_(elemIdx1), this->describeCell_(elemIdx2), kind,
218 ?
"The grid does join them, so the transmissibility "
219 "calculation skipped a face it should have computed."
220 :
"The grid does not join them either, so the caller asked "
221 "about a connection that does not exist -- the cell "
222 "indices it used are not the ones this grid knows."));
225 return entry->second;
228template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
232 return this->lookupTrans_(trans_, elemIdx1, elemIdx2,
"transmissibility");
235template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
239 return transBoundary_.at(std::make_pair(elemIdx, boundaryFaceIdx));
242template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
249template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
253 if (!storeHalfTrans_) {
257 OPM_THROW(std::logic_error,
258 "One-sided half transmissibilities were not stored. "
259 "Call setStoreHalfTrans(true) before update() to have them "
260 "computed; they are off by default because only the adjoint "
267template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
271 return thermalHalfTransBoundary_.at(std::make_pair(insideElemIdx, boundaryFaceIdx));
274template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
278 return thermalHalfTransBoundary_;
281template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
283diffusivity(
unsigned elemIdx1,
unsigned elemIdx2)
const
285 if (diffusivity_.empty())
288 return this->lookupTrans_(diffusivity_, elemIdx1, elemIdx2,
"diffusivity");
291template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
295 if (dispersivity_.empty())
298 return this->lookupTrans_(dispersivity_, elemIdx1, elemIdx2,
"dispersivity");
301template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
304 const std::function<
unsigned int(
unsigned int)>& map,
const bool applyNncMultregT)
307 const bool onlyTrans = (update_quantities == TransUpdateQuantities::Trans);
308 const auto& cartDims = cartMapper_.cartesianDimensions();
309 const auto& transMult = eclState_.getTransMult();
310 const auto& comm = gridView_.comm();
311 ElementMapper elemMapper(gridView_, Dune::mcmgElementLayout());
313 unsigned numElements = elemMapper.size();
315 const std::vector<double>& ntg = this->lookUpData_.assignFieldPropsDoubleOnLeaf(eclState_.fieldProps(),
"NTG");
316 const bool updateDiffusivity = eclState_.getSimulationConfig().isDiffusive();
317 const bool updateDispersivity = eclState_.getSimulationConfig().rock_config().dispersion();
319 const bool disableNNC = eclState_.getSimulationConfig().useNONNC();
322 extractPermeability_(map);
325 extractPermeability_();
335 if (num_threads == 1) {
336 trans_.reserve(numElements*3*1.05);
339 transBoundary_.clear();
341 if (storeHalfTrans_) {
343 if (num_threads == 1) {
344 halfTrans_.reserve(numElements*6*1.05);
349 if ( enableEnergy_ && !onlyTrans) {
350 thermalHalfTrans_.clear();
351 if (num_threads == 1) {
352 thermalHalfTrans_.reserve(numElements*6*1.05);
355 thermalHalfTransBoundary_.clear();
359 if (updateDiffusivity && !onlyTrans) {
360 diffusivity_.clear();
361 if (num_threads == 1) {
362 diffusivity_.reserve(numElements*3*1.05);
368 if (updateDispersivity && !onlyTrans) {
369 dispersivity_.clear();
370 if (num_threads == 1) {
371 dispersivity_.reserve(numElements*3*1.05);
373 extractDispersion_();
379 bool useSmallestMultiplier;
380 bool pinchOption4ALL;
382 if (comm.rank() == 0) {
383 const auto& eclGrid = eclState_.getInputGrid();
384 pinchActive = eclGrid.isPinchActive();
385 auto pinchTransCalcMode = eclGrid.getPinchOption();
386 useSmallestMultiplier = eclGrid.getMultzOption() == PinchMode::ALL;
387 pinchOption4ALL = (pinchTransCalcMode == PinchMode::ALL);
388 if (pinchOption4ALL) {
389 useSmallestMultiplier =
false;
392 if (global && comm.size() > 1) {
393 comm.broadcast(&useSmallestMultiplier, 1, 0);
394 comm.broadcast(&pinchOption4ALL, 1, 0);
395 comm.broadcast(&pinchActive, 1, 0);
399 centroids_cache_.resize(gridView_.size(0));
400 for (
const auto& elem : elements(gridView_)) {
401 const unsigned elemIdx = elemMapper.index(elem);
402 centroids_cache_[elemIdx] = centroids_(elemIdx);
405 auto harmonicMean = [](
const Scalar x1,
const Scalar x2)
407 return (std::abs(x1) < 1e-30 || std::abs(x2) < 1e-30)
409 : 1.0 / (1.0 / x1 + 1.0 / x2);
412 auto faceIdToDir = [](
int insideFaceIdx)
414 switch (insideFaceIdx) {
417 return FaceDir::XPlus;
420 return FaceDir::YPlus;
424 return FaceDir::ZPlus;
426 throw std::logic_error(
"Could not determine a face direction");
430 auto halfDiff = [](
const DimVector& faceAreaNormal,
435 return computeHalfDiffusivity_(faceAreaNormal,
440 ThreadSafeMapBuilder transBoundary(transBoundary_, num_threads,
441 MapBuilderInsertionMode::Insert_Or_Assign);
442 ThreadSafeMapBuilder transMap(trans_, num_threads,
443 MapBuilderInsertionMode::Insert_Or_Assign);
444 ThreadSafeMapBuilder thermalHalfTransBoundary(thermalHalfTransBoundary_, num_threads,
445 MapBuilderInsertionMode::Insert_Or_Assign);
446 ThreadSafeMapBuilder thermalHalfTrans(thermalHalfTrans_, num_threads,
447 MapBuilderInsertionMode::Insert_Or_Assign);
448 ThreadSafeMapBuilder halfTransMap(halfTrans_, num_threads,
449 MapBuilderInsertionMode::Insert_Or_Assign);
450 ThreadSafeMapBuilder diffusivity(diffusivity_, num_threads,
451 MapBuilderInsertionMode::Insert_Or_Assign);
452 ThreadSafeMapBuilder dispersivity(dispersivity_, num_threads,
453 MapBuilderInsertionMode::Insert_Or_Assign);
455 const auto& nnc_input = eclState_.getInputNNC().input();
458#pragma omp parallel for
460 for (
const auto& chunk : ElementChunks(gridView_, Dune::Partitions::all, num_threads)) {
461 for (
const auto& elem : chunk) {
466 inside.
elemIdx = elemMapper.index(elem);
470 template getFieldPropCartesianIdx<Grid>(inside.
elemIdx);
472 auto computeHalf = [
this, &faceAreaNormal, &inside, &outside]
473 (
const auto& halfComputer,
474 const auto& prop1,
const auto& prop2) -> std::array<Scalar,2>
477 halfComputer(faceAreaNormal,
481 halfComputer(faceAreaNormal,
488 auto computeHalfMean = [&inside, &outside, &computeHalf, &ntg, &harmonicMean]
489 (
const auto& halfComputer,
const auto& prop)
491 auto onesided = computeHalf(halfComputer, prop[inside.
elemIdx], prop[outside.
elemIdx]);
492 applyNtg_(onesided[0], inside, ntg);
493 applyNtg_(onesided[1], outside, ntg);
496 return harmonicMean(onesided[0], onesided[1]);
499 unsigned boundaryIsIdx = 0;
500 for (
const auto& intersection : intersections(gridView_, elem)) {
502 if (intersection.boundary()) {
504 const auto& geometry = intersection.geometry();
507 faceAreaNormal = intersection.centerUnitOuterNormal();
508 faceAreaNormal *= geometry.volume();
510 Scalar transBoundaryIs =
511 computeHalfTrans_(faceAreaNormal,
512 intersection.indexInInside(),
514 permeability_[inside.
elemIdx]);
519 applyMultipliers_(transBoundaryIs, intersection.indexInInside(), inside.
cartElemIdx, transMult);
520 transBoundary.insert_or_assign(std::make_pair(inside.
elemIdx, boundaryIsIdx), transBoundaryIs);
524 if (enableEnergy_ && !onlyTrans) {
525 Scalar transBoundaryEnergyIs =
526 computeHalfDiffusivity_(faceAreaNormal,
529 thermalHalfTransBoundary.insert_or_assign(std::make_pair(inside.
elemIdx, boundaryIsIdx),
530 transBoundaryEnergyIs);
537 if (!intersection.neighbor()) {
544 const auto& outsideElem = intersection.outside();
545 outside.
elemIdx = elemMapper.index(outsideElem);
550 template getFieldPropCartesianIdx<Grid>(outside.
elemIdx);
564 inside.
faceIdx = intersection.indexInInside();
565 outside.
faceIdx = intersection.indexInOutside();
572 if (enableEnergy_ && !onlyTrans) {
577 if (updateDiffusivity && !onlyTrans) {
580 if (updateDispersivity && !onlyTrans) {
586 typename std::is_same<Grid, Dune::CpGrid>::type isCpGrid;
587 computeFaceProperties(intersection,
593 Scalar trans = computeHalfMean(computeHalfTrans_, permeability_);
595 if (storeHalfTrans_) {
598 auto onesided = computeHalf(computeHalfTrans_,
600 permeability_[outside.
elemIdx]);
601 applyNtg_(onesided[0], inside, ntg);
602 applyNtg_(onesided[1], outside, ntg);
603 halfTransMap.insert_or_assign(
606 halfTransMap.insert_or_assign(
615 auto find_layer = [&cartDims](std::size_t cell) {
617 auto k = cell / cartDims[1];
625 if (std::abs(kup -kdown) > 1) {
631 if (useSmallestMultiplier) {
635 applyAllZMultipliers_(trans, inside, outside, transMult, cartDims);
643 bool foundInputNNC =
false;
644 if (! nnc_input.empty()) {
646 auto it = std::lower_bound(nnc_input.begin(), nnc_input.end(),
647 NNCdata { inside.cartElemIdx, outside.cartElemIdx, 0.0 });
648 foundInputNNC = it != nnc_input.end() && it->cell1 == inside.
cartElemIdx && it->cell2 == outside.
cartElemIdx;
650 if (! foundInputNNC) {
652 trans *= transMult.getRegionMultiplier(inside.
cartElemIdx,
660 if (enableEnergy_ && !onlyTrans) {
661 const auto half = computeHalf(halfDiff, 1.0, 1.0);
670 if (updateDiffusivity && !onlyTrans) {
672 computeHalfMean(halfDiff, porosity_));
676 if (updateDispersivity && !onlyTrans) {
678 computeHalfMean(halfDiff, dispersion_));
683 centroids_cache_.clear();
686#pragma omp parallel sections
696 transBoundary.finalize();
700 thermalHalfTransBoundary.finalize();
704 thermalHalfTrans.finalize();
708 diffusivity.finalize();
712 dispersivity.finalize();
716 halfTransMap.finalize();
720 this->updateFromEclState_(global);
723 std::unordered_map<std::size_t,int> globalToLocal;
726 for (
const auto& elem : elements(grid_.leafGridView())) {
727 int elemIdx = elemMapper.index(elem);
728 int cartElemIdx = cartMapper_.cartesianIndex(elemIdx);
729 globalToLocal[cartElemIdx] = elemIdx;
738 this->applyPinchNncToGridTrans_(globalToLocal, applyNncMultregT);
739 this->applyNncToGridTrans_(globalToLocal);
740 this->applyEditNncToGridTrans_(globalToLocal);
741 this->applyEditNncrToGridTrans_(globalToLocal);
742 if (applyNncMultregT) {
743 this->applyNncMultreg_(globalToLocal);
745 warnEditNNC_ =
false;
750 this->removeNonCartesianTransmissibilities_(disableNNC);
753template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
757 unsigned numElem = gridView_.size(0);
758 permeability_.resize(numElem);
764 const auto& fp = eclState_.fieldProps();
765 if (fp.has_double(
"PERMX")) {
766 const std::vector<double>& permxData = this-> lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PERMX");
768 std::vector<double> permyData;
769 if (fp.has_double(
"PERMY"))
770 permyData = this-> lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PERMY");
772 permyData = permxData;
774 std::vector<double> permzData;
775 if (fp.has_double(
"PERMZ"))
776 permzData = this-> lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PERMZ");
778 permzData = permxData;
780 for (std::size_t dofIdx = 0; dofIdx < numElem; ++ dofIdx) {
781 permeability_[dofIdx] = 0.0;
782 permeability_[dofIdx][0][0] = permxData[dofIdx];
783 permeability_[dofIdx][1][1] = permyData[dofIdx];
784 permeability_[dofIdx][2][2] = permzData[dofIdx];
791 throw std::logic_error(
"Can't read the intrinsic permeability from the ecl state. "
792 "(The PERM{X,Y,Z} keywords are missing)");
795template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
799 unsigned numElem = gridView_.size(0);
800 permeability_.resize(numElem);
806 const auto& fp = eclState_.fieldProps();
807 if (fp.has_double(
"PERMX")) {
808 const std::vector<double>& permxData =
809 this->lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PERMX");
811 std::vector<double> permyData;
812 if (fp.has_double(
"PERMY")){
813 permyData = this->lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PERMY");
816 permyData = permxData;
819 std::vector<double> permzData;
820 if (fp.has_double(
"PERMZ")) {
821 permzData = this->lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PERMZ");
824 permzData = permxData;
827 for (std::size_t dofIdx = 0; dofIdx < numElem; ++ dofIdx) {
828 permeability_[dofIdx] = 0.0;
829 std::size_t inputDofIdx = map(dofIdx);
830 permeability_[dofIdx][0][0] = permxData[inputDofIdx];
831 permeability_[dofIdx][1][1] = permyData[inputDofIdx];
832 permeability_[dofIdx][2][2] = permzData[inputDofIdx];
838 throw std::logic_error(
"Can't read the intrinsic permeability from the ecl state. "
839 "(The PERM{X,Y,Z} keywords are missing)");
843template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
851 const auto& fp = eclState_.fieldProps();
852 if (fp.has_double(
"PORO")) {
853 if constexpr (std::is_same_v<Scalar,double>) {
854 porosity_ = this->lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PORO");
857 const auto por = this->lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"PORO");
858 porosity_.resize(por.size());
859 std::ranges::copy(por, porosity_.begin());
863 throw std::logic_error(
"Can't read the porosity from the ecl state. "
864 "(The PORO keywords are missing)");
868template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
872 if (!enableDispersivity_) {
873 throw std::runtime_error(
"Dispersion disabled at compile time, but the deck "
874 "contains the DISPERC keyword.");
876 const auto& fp = eclState_.fieldProps();
877 if constexpr (std::is_same_v<Scalar,double>) {
878 dispersion_ = this->lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"DISPERC");
881 const auto disp = this->lookUpData_.assignFieldPropsDoubleOnLeaf(fp,
"DISPERC");
882 dispersion_.resize(disp.size());
883 std::ranges::copy(disp, dispersion_.begin());
887template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
891 const auto& cartDims = cartMapper_.cartesianDimensions();
892 for (
auto&& trans: trans_) {
894 if (removeAll || trans.second < transmissibilityThreshold_) {
895 const auto&
id = trans.first;
897 int gc1 = std::min(cartMapper_.cartesianIndex(elements.first), cartMapper_.cartesianIndex(elements.second));
898 int gc2 = std::max(cartMapper_.cartesianIndex(elements.first), cartMapper_.cartesianIndex(elements.second));
903 if (gc2 - gc1 == 1 || gc2 - gc1 == cartDims[0] || gc2 - gc1 == cartDims[0]*cartDims[1] || gc2 - gc1 == 0) {
912template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
917 const TransMult& transMult,
918 const std::array<int, dimWorld>& cartDims)
920 if (grid_.maxLevel() > 0) {
921 OPM_THROW(std::invalid_argument,
"MULTZ not support with LGRS, yet.");
927 unsigned lastCartElemIdx;
932 lastCartElemIdx = outside.
cartElemIdx - cartDims[0]*cartDims[1];
935 Scalar mult = transMult.getMultiplier(lastCartElemIdx , FaceDir::ZPlus) *
936 transMult.getMultiplier(outside.
cartElemIdx , FaceDir::ZMinus);
940 for (
auto cartElemIdx = inside.
cartElemIdx; cartElemIdx < lastCartElemIdx;) {
941 auto multiplier = transMult.getMultiplier(cartElemIdx, FaceDir::ZPlus);
942 cartElemIdx += cartDims[0]*cartDims[1];
943 multiplier *= transMult.getMultiplier(cartElemIdx, FaceDir::ZMinus);
944 mult = std::min(mult,
static_cast<Scalar
>(multiplier));
955template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
959 const FieldPropsManager* fp =
960 (global) ? &(eclState_.fieldProps()) :
961 &(eclState_.globalFieldProps());
963 std::array<bool,3> is_tran {fp->tran_active(
"TRANX"),
964 fp->tran_active(
"TRANY"),
965 fp->tran_active(
"TRANZ")};
967 if (!(is_tran[0] || is_tran[1] || is_tran[2])) {
972 std::array<std::string, 3> keywords {
"TRANX",
"TRANY",
"TRANZ"};
973 std::array<std::vector<double>,3> trans = createTransmissibilityArrays_(is_tran);
974 auto key = keywords.begin();
975 auto perform = is_tran.begin();
977 for (
auto it = trans.begin(); it != trans.end(); ++it, ++key, ++perform) {
979 if (grid_.maxLevel() > 0) {
980 OPM_THROW(std::invalid_argument,
"Calculations on TRANX/TRANY/TRANZ arrays are not support with LGRS, yet.");
982 fp->apply_tran(*key, *it);
986 resetTransmissibilityFromArrays_(is_tran, trans);
989template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
990std::array<std::vector<double>,3>
994 const auto& cartDims = cartMapper_.cartesianDimensions();
995 ElementMapper elemMapper(gridView_, Dune::mcmgElementLayout());
997 auto numElem = gridView_.size(0);
998 std::array<std::vector<double>,3> trans = {
999 std::vector<double>(is_tran[0] ? numElem : 0, 0),
1000 std::vector<double>(is_tran[1] ? numElem : 0, 0),
1001 std::vector<double>(is_tran[2] ? numElem : 0, 0)
1005 for (
const auto& elem : elements(gridView_)) {
1006 for (
const auto& intersection : intersections(gridView_, elem)) {
1008 if (!intersection.neighbor()) {
1023 unsigned c1 = elemMapper.index(intersection.inside());
1024 unsigned c2 = elemMapper.index(intersection.outside());
1025 int gc1 = cartMapper_.cartesianIndex(c1);
1026 int gc2 = cartMapper_.cartesianIndex(c2);
1027 if (std::tie(gc1, c1) > std::tie(gc2, c2)) {
1041 if ((gc2 - gc1 == 1 || (gc2 == gc1 && (intersection.indexInInside() == 0 || intersection.indexInInside() == 1)))
1046 trans[0][c1] = trans_[isID];
1049 else if ((gc2 - gc1 == cartDims[0] || (gc2 == gc1 && (intersection.indexInInside() == 2 || intersection.indexInInside() == 3)))
1054 trans[1][c1] = trans_[isID];
1057 else if (gc2 - gc1 == cartDims[0]*cartDims[1] ||
1058 (gc2 == gc1 && (intersection.indexInInside() == 4 || intersection.indexInInside() == 5)))
1062 trans[2][c1] = trans_[isID];
1072template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1075 const std::array<std::vector<double>,3>& trans)
1077 const auto& cartDims = cartMapper_.cartesianDimensions();
1078 ElementMapper elemMapper(gridView_, Dune::mcmgElementLayout());
1081 for (
const auto& elem : elements(gridView_)) {
1082 for (
const auto& intersection : intersections(gridView_, elem)) {
1083 if (!intersection.neighbor()) {
1098 unsigned c1 = elemMapper.index(intersection.inside());
1099 unsigned c2 = elemMapper.index(intersection.outside());
1100 int gc1 = cartMapper_.cartesianIndex(c1);
1101 int gc2 = cartMapper_.cartesianIndex(c2);
1102 if (std::tie(gc1, c1) > std::tie(gc2, c2)) {
1116 if ((gc2 - gc1 == 1 || (gc2 == gc1 && (intersection.indexInInside() == 0 || intersection.indexInInside() == 1)))
1121 trans_[isID] = trans[0][c1];
1124 else if ((gc2 - gc1 == cartDims[0] || (gc2 == gc1 && (intersection.indexInInside() == 2|| intersection.indexInInside() == 3)))
1129 trans_[isID] = trans[1][c1];
1132 else if (gc2 - gc1 == cartDims[0]*cartDims[1] ||
1133 (gc2 == gc1 && (intersection.indexInInside() == 4 || intersection.indexInInside() == 5)))
1137 trans_[isID] = trans[2][c1];
1146template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1147template<
class Intersection>
1153 std::false_type)
const
1156 const auto& geometry = intersection.geometry();
1159 faceAreaNormal = intersection.centerUnitOuterNormal();
1160 faceAreaNormal *= geometry.volume();
1163template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1164template<
class Intersection>
1170 std::true_type)
const
1172 int faceIdx = intersection.id();
1174 if (grid_.maxLevel() == 0) {
1177 faceAreaNormal = grid_.faceAreaNormalEcl(faceIdx);
1180 if ((intersection.inside().level() != intersection.outside().level())) {
1187 const auto& parentIntersection =
1188 grid_.getParentIntersectionFromLgrBoundaryFace(intersection);
1189 const auto& parentIntersectionGeometry = parentIntersection.geometry();
1193 inside.
faceCenter = (intersection.inside().level() == 0)
1194 ? parentIntersectionGeometry.center()
1195 : grid_.faceCenterEcl(inside.
elemIdx, inside.
faceIdx, intersection);
1196 outside.
faceCenter = (intersection.outside().level() == 0)
1197 ? parentIntersectionGeometry.center()
1198 : grid_.faceCenterEcl(outside.
elemIdx, outside.
faceIdx, intersection);
1206 faceAreaNormal = intersection.centerUnitOuterNormal();
1207 faceAreaNormal *= intersection.geometry().volume();
1210 assert(intersection.inside().level() == intersection.outside().level());
1216 if (intersection.inside().level() > 0) {
1217 faceAreaNormal = intersection.centerUnitOuterNormal();
1218 faceAreaNormal *= intersection.geometry().volume();
1221 faceAreaNormal = grid_.faceAreaNormalEcl(faceIdx);
1227template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1231 const bool applyNncMultregT)
1233 const auto& pinchNnc = eclState_.getPinchNNC();
1234 const auto& transMult = this->eclState_.getTransMult();
1236 for (
const auto& nncEntry : pinchNnc) {
1237 auto c1 = nncEntry.cell1;
1238 auto c2 = nncEntry.cell2;
1239 auto lowIt = cartesianToCompressed.find(c1);
1240 auto highIt = cartesianToCompressed.find(c2);
1241 int low = (lowIt == cartesianToCompressed.end())? -1 : lowIt->second;
1242 int high = (highIt == cartesianToCompressed.end())? -1 : highIt->second;
1245 std::swap(low, high);
1248 if (low == -1 && high == -1) {
1253 if (low == -1 || high == -1) {
1262 if (candidate != trans_.end()) {
1266 candidate->second = nncEntry.trans;
1267 if (applyNncMultregT) {
1268 const auto mult = transMult.getRegionMultiplierNNC(c1, c2);
1269 candidate->second *= mult;
1276template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1282 const auto& nnc_input = eclState_.getInputNNC().input();
1284 for (
const auto& nncEntry : nnc_input) {
1285 auto c1 = nncEntry.cell1;
1286 auto c2 = nncEntry.cell2;
1287 auto lowIt = cartesianToCompressed.find(c1);
1288 auto highIt = cartesianToCompressed.find(c2);
1289 int low = (lowIt == cartesianToCompressed.end())? -1 : lowIt->second;
1290 int high = (highIt == cartesianToCompressed.end())? -1 : highIt->second;
1293 std::swap(low, high);
1296 if (low == -1 && high == -1) {
1301 if (low == -1 || high == -1) {
1303 std::ostringstream sstr;
1304 sstr <<
"NNC between active and inactive cells ("
1305 << low <<
" -> " << high <<
") with globalcell is (" << c1 <<
"->" << c2 <<
")";
1306 OpmLog::warning(sstr.str());
1310 if (
auto candidate = trans_.find(
details::isId(low, high)); candidate != trans_.end()) {
1314 candidate->second += nncEntry.trans;
1344template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1348 const auto& input = eclState_.getInputNNC();
1349 applyEditNncToGridTransHelper_(globalToLocal,
"EDITNNC",
1351 [&input](
const NNCdata& nnc){
1352 return input.edit_location(nnc);},
1354 [](Scalar& trans,
const Scalar& rhs){ trans *= rhs;});
1357template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1361 const auto& input = eclState_.getInputNNC();
1362 applyEditNncToGridTransHelper_(globalToLocal,
"EDITNNCR",
1364 [&input](
const NNCdata& nnc){
1365 return input.editr_location(nnc);},
1367 [](Scalar& trans,
const Scalar& rhs){ trans = rhs;});
1370template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1373 const std::string& keyword,
1374 const std::vector<NNCdata>& nncs,
1375 const std::function<KeywordLocation(
const NNCdata&)>& getLocation,
1376 const std::function<
void(Scalar&,
const Scalar&)>& apply)
1381 const auto& cartDims = cartMapper_.cartesianDimensions();
1383 auto format_ijk = [&cartDims](std::size_t cell) -> std::string
1385 auto i = cell % cartDims[0]; cell /= cartDims[0];
1386 auto j = cell % cartDims[1];
1387 auto k = cell / cartDims[1];
1389 return fmt::format(
"({},{},{})", i + 1,j + 1,k + 1);
1392 auto print_warning = [&format_ijk, &getLocation, &keyword] (
const NNCdata& nnc)
1394 const auto& location = getLocation( nnc );
1395 auto warning = fmt::format(
"Problem with {} keyword\n"
1397 "No NNC defined for connection {} -> {}", keyword, location.filename,
1398 location.lineno, format_ijk(nnc.cell1), format_ijk(nnc.cell2));
1399 OpmLog::warning(keyword, warning);
1405 auto nnc = nncs.begin();
1406 auto end = nncs.end();
1407 std::size_t warning_count = 0;
1408 while (nnc != end) {
1409 auto c1 = nnc->cell1;
1410 auto c2 = nnc->cell2;
1411 auto lowIt = globalToLocal.find(c1);
1412 auto highIt = globalToLocal.find(c2);
1414 if (lowIt == globalToLocal.end() || highIt == globalToLocal.end()) {
1416 if (lowIt != highIt && warnEditNNC_) {
1417 print_warning(*nnc);
1424 auto low = lowIt->second, high = highIt->second;
1427 std::swap(low, high);
1431 if (candidate == trans_.end() && warnEditNNC_) {
1432 print_warning(*nnc);
1438 while (nnc != end && c1 == nnc->cell1 && c2 == nnc->cell2) {
1439 apply(candidate->second, nnc->trans);
1445 if (warning_count > 0) {
1446 auto warning = fmt::format(
"Problems with {} keyword\n"
1447 "A total of {} connections not defined in grid", keyword, warning_count);
1448 OpmLog::warning(warning);
1452template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1455applyNncMultreg_(
const std::unordered_map<std::size_t,int>& cartesianToCompressed)
1457 const auto& inputNNC = this->eclState_.getInputNNC();
1458 const auto& transMult = this->eclState_.getTransMult();
1460 auto compressedIdx = [&cartesianToCompressed](
const std::size_t globIdx)
1462 auto ixPos = cartesianToCompressed.find(globIdx);
1463 return (ixPos == cartesianToCompressed.end()) ? -1 : ixPos->second;
1477 for (
const auto& nncList : {&NNC::input, &NNC::editr}) {
1478 for (
const auto& nncEntry : (inputNNC.*nncList)()) {
1479 const auto c1 = nncEntry.cell1;
1480 const auto c2 = nncEntry.cell2;
1482 auto low = compressedIdx(c1);
1483 auto high = compressedIdx(c2);
1485 if ((low == -1) || (high == -1)) {
1490 std::swap(low, high);
1493 auto candidate = this->trans_.find(
details::isId(low, high));
1494 if (candidate != this->trans_.end()) {
1495 candidate->second *= transMult.getRegionMultiplierNNC(c1, c2);
1501template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1509 assert(faceIdx >= 0);
1510 unsigned dimIdx = faceIdx / 2;
1511 assert(dimIdx < dimWorld);
1512 Scalar halfTrans = perm[dimIdx][dimIdx];
1513 halfTrans *= std::abs(Dune::dot(areaNormal, distance));
1514 halfTrans /= distance.two_norm2();
1519template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1526 Scalar halfDiff = poro;
1527 halfDiff *= std::abs(Dune::dot(areaNormal, distance));
1528 halfDiff /= distance.two_norm2();
1533template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1537 const unsigned& cellIdx)
const
1539 const auto& cellCenter = centroids_cache_.empty() ? centroids_(cellIdx)
1540 : centroids_cache_[cellIdx];
1542 for (
unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
1543 x[dimIdx] -= cellCenter[dimIdx];
1549template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1553 unsigned cartElemIdx,
1554 const TransMult& transMult)
const
1559 trans *= transMult.getMultiplier(cartElemIdx,
1560 FaceDir::FromIntersectionIndex(faceIdx));
1563template<
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper,
class Scalar>
1567 const std::vector<double>& ntg)
static unsigned maxThreads()
Return the maximum number of threads of the current process.
Definition: threadmanager.hpp:66
void applyMultipliers_(Scalar &trans, unsigned faceIdx, unsigned cartElemIdx, const TransMult &transMult) const
Definition: Transmissibility_impl.hpp:1551
Scalar diffusivity(unsigned elemIdx1, unsigned elemIdx2) const
Return the diffusivity for the intersection between two elements.
Definition: Transmissibility_impl.hpp:283
void applyEditNncToGridTrans_(const std::unordered_map< std::size_t, int > &globalToLocal)
Multiplies the grid transmissibilities according to EDITNNC.
Definition: Transmissibility_impl.hpp:1346
void computeFaceProperties(const Intersection &intersection, FaceInfo &inside, FaceInfo &outside, DimVector &faceAreaNormal, std::false_type) const
Definition: Transmissibility_impl.hpp:1149
Dune::FieldMatrix< Scalar, dimWorld, dimWorld > DimMatrix
Definition: Transmissibility.hpp:60
void update(bool global, TransUpdateQuantities update_quantities=TransUpdateQuantities::All, const std::function< unsigned int(unsigned int)> &map={}, bool applyNncMultRegT=false)
Definition: Transmissibility_impl.hpp:303
DimVector distanceVector_(const DimVector &faceCenter, const unsigned &cellIdx) const
Definition: Transmissibility_impl.hpp:1536
void applyNncMultreg_(const std::unordered_map< std::size_t, int > &globalToLocal)
Definition: Transmissibility_impl.hpp:1455
void applyEditNncrToGridTrans_(const std::unordered_map< std::size_t, int > &globalToLocal)
Resets the grid transmissibilities according to EDITNNCR.
Definition: Transmissibility_impl.hpp:1359
void extractDispersion_()
Definition: Transmissibility_impl.hpp:870
const std::map< std::pair< unsigned, unsigned >, Scalar > & getThermalHalfTransBoundary() const
Definition: Transmissibility_impl.hpp:276
Scalar thermalHalfTrans(unsigned insideElemIdx, unsigned outsideElemIdx) const
Return the thermal "half transmissibility" for the intersection between two elements.
Definition: Transmissibility_impl.hpp:244
Scalar transmissibilityBoundary(unsigned elemIdx, unsigned boundaryFaceIdx) const
Return the transmissibility for a given boundary segment.
Definition: Transmissibility_impl.hpp:237
std::array< std::vector< double >, 3 > createTransmissibilityArrays_(const std::array< bool, 3 > &is_tran)
Creates TRANS{XYZ} arrays for modification by FieldProps data.
Definition: Transmissibility_impl.hpp:992
void applyEditNncToGridTransHelper_(const std::unordered_map< std::size_t, int > &globalToLocal, const std::string &keyword, const std::vector< NNCdata > &nncs, const std::function< KeywordLocation(const NNCdata &)> &getLocation, const std::function< void(Scalar &, const Scalar &)> &apply)
Definition: Transmissibility_impl.hpp:1372
void updateFromEclState_(bool global)
Definition: Transmissibility_impl.hpp:957
void extractPermeability_()
Definition: Transmissibility_impl.hpp:755
void applyAllZMultipliers_(Scalar &trans, const FaceInfo &inside, const FaceInfo &outside, const TransMult &transMult, const std::array< int, dimWorld > &cartDims)
Apply the Multipliers for the case PINCH(4)==TOPBOT.
Definition: Transmissibility_impl.hpp:914
Scalar lookupTrans_(const std::unordered_map< std::uint64_t, Scalar > &map, unsigned elemIdx1, unsigned elemIdx2, std::string_view what) const
Definition: Transmissibility_impl.hpp:188
TransUpdateQuantities
Compute all transmissibilities.
Definition: Transmissibility.hpp:178
void resetTransmissibilityFromArrays_(const std::array< bool, 3 > &is_tran, const std::array< std::vector< double >, 3 > &trans)
overwrites calculated transmissibilities
Definition: Transmissibility_impl.hpp:1074
Dune::FieldVector< Scalar, dimWorld > DimVector
Definition: Transmissibility.hpp:61
static Scalar computeHalfDiffusivity_(const DimVector &areaNormal, const DimVector &distance, const Scalar poro)
Definition: Transmissibility_impl.hpp:1522
Scalar transmissibilityThreshold_
Definition: Transmissibility.hpp:322
Scalar transmissibility(unsigned elemIdx1, unsigned elemIdx2) const
Return the transmissibility for the intersection between two elements.
Definition: Transmissibility_impl.hpp:230
std::string describeCell_(unsigned elemIdx) const
Cell index, its Cartesian index, and on a refined grid its level.
Definition: Transmissibility_impl.hpp:151
static void applyNtg_(Scalar &trans, const FaceInfo &face, const std::vector< double > &ntg)
Definition: Transmissibility_impl.hpp:1565
const EclipseState & eclState_
Definition: Transmissibility.hpp:316
Scalar dispersivity(unsigned elemIdx1, unsigned elemIdx2) const
Return the dispersivity for the intersection between two elements.
Definition: Transmissibility_impl.hpp:293
Transmissibility(const EclipseState &eclState, const GridView &gridView, const CartesianIndexMapper &cartMapper, const Grid &grid, std::function< std::array< double, dimWorld >(int)> centroids, bool enableEnergy, bool enableDiffusivity, bool enableDispersivity)
Definition: Transmissibility_impl.hpp:99
void applyNncToGridTrans_(const std::unordered_map< std::size_t, int > &cartesianToCompressed)
Definition: Transmissibility_impl.hpp:1279
Scalar halfTransmissibility(unsigned insideElemIdx, unsigned outsideElemIdx) const
One-sided (half) transmissibility of the face between two cells, seen from the inside cell (NTG appli...
Definition: Transmissibility_impl.hpp:251
void applyPinchNncToGridTrans_(const std::unordered_map< std::size_t, int > &cartesianToCompressed, bool applyNncMultregT)
Applies the previous calculate transmissibilities to the NNCs created via PINCH.
Definition: Transmissibility_impl.hpp:1230
bool gridJoins_(unsigned elemIdx1, unsigned elemIdx2) const
Whether the grid has an intersection between the two cells.
Definition: Transmissibility_impl.hpp:124
void extractPorosity_()
Definition: Transmissibility_impl.hpp:845
void removeNonCartesianTransmissibilities_(bool removeAll)
Definition: Transmissibility_impl.hpp:889
static Scalar computeHalfTrans_(const DimVector &areaNormal, int faceIdx, const DimVector &distance, const DimMatrix &perm)
Definition: Transmissibility_impl.hpp:1504
Scalar thermalHalfTransBoundary(unsigned insideElemIdx, unsigned boundaryFaceIdx) const
Definition: Transmissibility_impl.hpp:269
if(!linsolver_)
Definition: FlexibleSolver_impl.hpp:326
int Index
The type of an index of a degree of freedom.
Definition: overlaptypes.hh:44
constexpr unsigned elemIdxShift
Definition: FacePropertiesTPSA_impl.hpp:52
std::pair< std::uint32_t, std::uint32_t > isIdReverse(const std::uint64_t &id)
Definition: Transmissibility_impl.hpp:80
std::uint64_t directionalIsId(std::uint32_t elemIdx1, std::uint32_t elemIdx2)
Definition: Transmissibility_impl.hpp:91
std::uint64_t isId(std::uint32_t elemIdx1, std::uint32_t elemIdx2)
Definition: Transmissibility_impl.hpp:72
Definition: blackoilbioeffectsmodules.hh:45
Definition: Transmissibility.hpp:188
unsigned cartElemIdx
Definition: Transmissibility.hpp:192
DimVector faceCenter
Definition: Transmissibility.hpp:189
unsigned elemIdx
Definition: Transmissibility.hpp:191
int faceIdx
Definition: Transmissibility.hpp:190