Merge pull request #4576 from bska/face-area-fraction

Calculate Local Face Area Fraction for Aquifer's Connections
This commit is contained in:
Kai Bao 2023-05-25 13:25:51 +02:00 committed by GitHub
commit 675ce03e52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 188 additions and 83 deletions

View File

@ -87,17 +87,45 @@ public:
BlackoilIndices::numPhases>; BlackoilIndices::numPhases>;
// Constructor // Constructor
AquiferAnalytical(int aqID, AquiferAnalytical(const int aqID,
const std::vector<Aquancon::AquancCell>& connections, const std::vector<Aquancon::AquancCell>& connections,
const Simulator& ebosSimulator) const Simulator& ebosSimulator)
: AquiferInterface<TypeTag>(aqID, ebosSimulator) : AquiferInterface<TypeTag>(aqID, ebosSimulator)
, connections_(connections) , connections_(connections)
{ {
this->initializeConnectionMappings();
} }
// Destructor // Destructor
virtual ~AquiferAnalytical() virtual ~AquiferAnalytical()
{}
void computeFaceAreaFraction(const std::vector<double>& total_face_area) override
{ {
assert (total_face_area.size() >= static_cast<std::vector<double>::size_type>(this->aquiferID()));
const auto tfa = total_face_area[this->aquiferID() - 1];
const auto eps_sqrt = std::sqrt(std::numeric_limits<double>::epsilon());
if (tfa < eps_sqrt) {
this->alphai_.assign(this->size(), Scalar{0});
}
else {
std::transform(this->faceArea_connected_.begin(),
this->faceArea_connected_.end(),
this->alphai_.begin(),
[tfa](const Scalar area)
{
return area / tfa;
});
}
this->area_fraction_ = this->totalFaceArea() / tfa;
}
double totalFaceArea() const override
{
return this->total_face_area_;
} }
void initFromRestart(const data::Aquifers& aquiferSoln) override void initFromRestart(const data::Aquifers& aquiferSoln) override
@ -108,8 +136,9 @@ public:
this->assignRestartData(xaqPos->second); this->assignRestartData(xaqPos->second);
this->W_flux_ = xaqPos->second.volume; this->W_flux_ = xaqPos->second.volume * this->area_fraction_;
this->pa0_ = xaqPos->second.initPressure; this->pa0_ = xaqPos->second.initPressure;
this->solution_set_from_restart_ = true; this->solution_set_from_restart_ = true;
} }
@ -226,19 +255,17 @@ protected:
void initQuantities() void initQuantities()
{ {
// We reset the cumulative flux at the start of any simulation, so, W_flux = 0 // We reset the cumulative flux at the start of any simulation, so, W_flux = 0
if (!this->solution_set_from_restart_) { if (! this->solution_set_from_restart_) {
W_flux_ = Scalar{0}; W_flux_ = Scalar{0};
} }
// We next get our connections to the aquifer and initialize these quantities using the initialize_connections this->initializeConnectionDepths();
// function this->calculateAquiferCondition();
initializeConnections(); this->calculateAquiferConstants();
calculateAquiferCondition();
calculateAquiferConstants();
pressure_previous_.resize(this->connections_.size(), Scalar{0}); this->pressure_previous_.resize(this->size(), Scalar{0});
pressure_current_.resize(this->connections_.size(), Scalar{0}); this->pressure_current_.resize(this->size(), Scalar{0});
Qai_.resize(this->connections_.size(), Scalar{0}); this->Qai_.resize(this->size(), Scalar{0});
} }
void updateCellPressure(std::vector<Eval>& pressure_water, void updateCellPressure(std::vector<Eval>& pressure_water,
@ -257,54 +284,54 @@ protected:
pressure_water.at(idx) = fs.pressure(this->phaseIdx_()).value(); pressure_water.at(idx) = fs.pressure(this->phaseIdx_()).value();
} }
void initializeConnections() void initializeConnectionMappings()
{ {
this->cell_depth_.resize(this->size(), this->aquiferDepth());
this->alphai_.resize(this->size(), 1.0); this->alphai_.resize(this->size(), 1.0);
this->faceArea_connected_.resize(this->size(), Scalar{0}); this->faceArea_connected_.resize(this->size(), Scalar{0});
// Translate the C face tag into the enum used by opm-parser's TransMult class // total_face_area_ is the sum of the areas connected to an aquifer
FaceDir::DirEnum faceDirection; this->total_face_area_ = Scalar{0};
bool has_active_connection_on_proc = false;
// denom_face_areas is the sum of the areas connected to an aquifer
Scalar denom_face_areas{0};
this->cellToConnectionIdx_.resize(this->ebos_simulator_.gridView().size(/*codim=*/0), -1); this->cellToConnectionIdx_.resize(this->ebos_simulator_.gridView().size(/*codim=*/0), -1);
const auto& gridView = this->ebos_simulator_.vanguard().gridView(); const auto& gridView = this->ebos_simulator_.vanguard().gridView();
for (std::size_t idx = 0; idx < this->size(); ++idx) { for (std::size_t idx = 0; idx < this->size(); ++idx) {
const auto global_index = this->connections_[idx].global_index; const auto global_index = this->connections_[idx].global_index;
const int cell_index = this->ebos_simulator_.vanguard().compressedIndex(global_index); const int cell_index = this->ebos_simulator_.vanguard().compressedIndex(global_index);
auto elemIt = gridView.template begin</*codim=*/ 0>(); if (cell_index < 0) {
if (cell_index > 0)
std::advance(elemIt, cell_index);
//the global_index is not part of this grid
if ( cell_index < 0 || elemIt->partitionType() != Dune::InteriorEntity)
continue; continue;
}
has_active_connection_on_proc = true; auto elemIt = gridView.template begin</*codim=*/ 0>();
std::advance(elemIt, cell_index);
// The global_index is not part of this grid
if (elemIt->partitionType() != Dune::InteriorEntity) {
continue;
}
this->cellToConnectionIdx_[cell_index] = idx; this->cellToConnectionIdx_[cell_index] = idx;
this->cell_depth_.at(idx) = this->ebos_simulator_.vanguard().cellCenterDepth(cell_index);
} }
// get areas for all connections
ElementMapper elemMapper(gridView, Dune::mcmgElementLayout());
for (const auto& elem : elements(gridView)) {
unsigned cell_index = elemMapper.index(elem);
int idx = this->cellToConnectionIdx_[cell_index];
// only deal with connections given by the aquifer // Translate the C face tag into the enum used by opm-parser's TransMult class
if( idx < 0) FaceDir::DirEnum faceDirection;
// Get areas for all connections
const auto& elemMapper = this->ebos_simulator_.model().dofMapper();
for (const auto& elem : elements(gridView)) {
const unsigned cell_index = elemMapper.index(elem);
const int idx = this->cellToConnectionIdx_[cell_index];
// Only deal with connections given by the aquifer
if (idx < 0) {
continue; continue;
}
for (const auto& intersection : intersections(gridView, elem)) { for (const auto& intersection : intersections(gridView, elem)) {
// only deal with grid boundaries // Only deal with grid boundaries
if (!intersection.boundary()) if (! intersection.boundary()) {
continue; continue;
}
int insideFaceIdx = intersection.indexInInside(); switch (intersection.indexInInside()) {
switch (insideFaceIdx) {
case 0: case 0:
faceDirection = FaceDir::XMinus; faceDirection = FaceDir::XMinus;
break; break;
@ -325,51 +352,41 @@ protected:
break; break;
default: default:
OPM_THROW(std::logic_error, OPM_THROW(std::logic_error,
"Internal error in initialization of aquifer."); "Internal error in initialization of aquifer.");
} }
if (faceDirection == this->connections_[idx].face_dir) { if (faceDirection == this->connections_[idx].face_dir) {
this->faceArea_connected_[idx] = this->connections_[idx].influx_coeff; this->faceArea_connected_[idx] = this->connections_[idx].influx_coeff;
break; break;
} }
} }
denom_face_areas += this->faceArea_connected_.at(idx);
}
const auto& comm = this->ebos_simulator_.vanguard().grid().comm(); this->total_face_area_ += this->faceArea_connected_.at(idx);
comm.sum(&denom_face_areas, 1);
const double eps_sqrt = std::sqrt(std::numeric_limits<double>::epsilon());
for (std::size_t idx = 0; idx < this->size(); ++idx) {
// Protect against division by zero NaNs.
this->alphai_.at(idx) = (denom_face_areas < eps_sqrt)
? Scalar{0}
: this->faceArea_connected_.at(idx) / denom_face_areas;
}
if (this->solution_set_from_restart_) {
this->rescaleProducedVolume(has_active_connection_on_proc);
} }
} }
void rescaleProducedVolume(const bool has_active_connection_on_proc) void initializeConnectionDepths()
{ {
// Needed in parallel restart to approximate influence of aquifer this->cell_depth_.resize(this->size(), this->aquiferDepth());
// being "owned" by a subset of the parallel processes. If the
// aquifer is fully owned by a single process--i.e., if all cells
// connecting to the aquifer are on a single process--then this_area
// is tot_area on that process and zero elsewhere.
const auto this_area = has_active_connection_on_proc const auto& gridView = this->ebos_simulator_.vanguard().gridView();
? std::accumulate(this->alphai_.begin(), for (std::size_t idx = 0; idx < this->size(); ++idx) {
this->alphai_.end(), const int cell_index = this->ebos_simulator_.vanguard()
Scalar{0}) .compressedIndex(this->connections_[idx].global_index);
: Scalar{0}; if (cell_index < 0) {
continue;
}
const auto tot_area = this->ebos_simulator_.vanguard() auto elemIt = gridView.template begin</*codim=*/ 0>();
.grid().comm().sum(this_area); std::advance(elemIt, cell_index);
this->W_flux_ *= this_area / tot_area; // The global_index is not part of this grid
if (elemIt->partitionType() != Dune::InteriorEntity) {
continue;
}
this->cell_depth_.at(idx) = this->ebos_simulator_.vanguard().cellCenterDepth(cell_index);
}
} }
// This function is for calculating the aquifer properties from equilibrium state with the reservoir // This function is for calculating the aquifer properties from equilibrium state with the reservoir
@ -433,9 +450,13 @@ protected:
std::optional<Scalar> Ta0_{}; // initial aquifer temperature std::optional<Scalar> Ta0_{}; // initial aquifer temperature
Scalar rhow_{}; Scalar rhow_{};
Scalar total_face_area_{};
Scalar area_fraction_{Scalar{1}};
Eval W_flux_; Eval W_flux_;
bool solution_set_from_restart_ {false}; bool solution_set_from_restart_ {false};
bool has_active_connection_on_proc_{false};
}; };
} // namespace Opm } // namespace Opm

View File

@ -32,8 +32,10 @@
#include <opm/material/densead/Evaluation.hpp> #include <opm/material/densead/Evaluation.hpp>
#include <algorithm> #include <algorithm>
#include <cassert>
#include <numeric> #include <numeric>
#include <stdexcept> #include <stdexcept>
#include <vector>
namespace Opm { namespace Opm {
@ -58,7 +60,7 @@ public:
, aquifer_data_ (aquifer) , aquifer_data_ (aquifer)
, connection_flux_ (connections_.size(), Eval{0}) , connection_flux_ (connections_.size(), Eval{0})
{ {
this->initializeConnections(); this->total_face_area_ = this->initializeConnections();
} }
static AquiferConstantFlux serializationTestObject(const Simulator& ebos_simulator) static AquiferConstantFlux serializationTestObject(const Simulator& ebos_simulator)
@ -71,6 +73,19 @@ public:
virtual ~AquiferConstantFlux() = default; virtual ~AquiferConstantFlux() = default;
void computeFaceAreaFraction(const std::vector<double>& total_face_area) override
{
assert (total_face_area.size() >= static_cast<std::vector<double>::size_type>(this->aquiferID()));
this->area_fraction_ = this->totalFaceArea()
/ total_face_area[this->aquiferID() - 1];
}
double totalFaceArea() const override
{
return this->total_face_area_;
}
void updateAquifer(const SingleAquiferFlux& aquifer) void updateAquifer(const SingleAquiferFlux& aquifer)
{ {
aquifer_data_ = aquifer; aquifer_data_ = aquifer;
@ -149,9 +164,16 @@ private:
std::vector<int> cellToConnectionIdx_{}; std::vector<int> cellToConnectionIdx_{};
double flux_rate_{}; double flux_rate_{};
double cumulative_flux_{}; double cumulative_flux_{};
double total_face_area_{0.0};
double area_fraction_{1.0};
double initializeConnections()
{
auto connected_face_area = 0.0;
this->cellToConnectionIdx_
.resize(this->ebos_simulator_.gridView().size(/*codim=*/0), -1);
void initializeConnections() {
this->cellToConnectionIdx_.resize(this->ebos_simulator_.gridView().size(/*codim=*/0), -1);
for (std::size_t idx = 0; idx < this->connections_.size(); ++idx) { for (std::size_t idx = 0; idx < this->connections_.size(); ++idx) {
const auto global_index = this->connections_[idx].global_index; const auto global_index = this->connections_[idx].global_index;
const int cell_index = this->ebos_simulator_.vanguard() const int cell_index = this->ebos_simulator_.vanguard()
@ -162,11 +184,25 @@ private:
} }
this->cellToConnectionIdx_[cell_index] = idx; this->cellToConnectionIdx_[cell_index] = idx;
connected_face_area += this->connections_[idx].effective_facearea;
} }
// TODO: At the moment, we are using the effective_facearea from the // TODO: At the moment, we are using the effective_facearea from the
// parser. Should we update the facearea here if the grid changed // parser. Should we update the facearea here if the grid changed
// during the preprocessing? // during the preprocessing?
return connected_face_area;
}
double computeFaceAreaFraction(const double connected_face_area) const
{
const auto tot_face_area = this->ebos_simulator_.vanguard()
.grid().comm().sum(connected_face_area);
return (tot_face_area > 0.0)
? connected_face_area / tot_face_area
: 0.0;
} }
// TODO: this is a function from AquiferAnalytical // TODO: this is a function from AquiferAnalytical

View File

@ -58,6 +58,9 @@ public:
virtual data::AquiferData aquiferData() const = 0; virtual data::AquiferData aquiferData() const = 0;
virtual void computeFaceAreaFraction(const std::vector<double>& total_face_area) = 0;
virtual double totalFaceArea() const = 0;
template <class Context> template <class Context>
void addToSource(RateVector& rates, void addToSource(RateVector& rates,
const Context& context, const Context& context,

View File

@ -154,6 +154,14 @@ public:
this->cumulative_flux_ = 0.; this->cumulative_flux_ = 0.;
} }
void computeFaceAreaFraction(const std::vector<double>& /*total_face_area*/) override
{}
double totalFaceArea() const override
{
return 1.0;
}
template<class Serializer> template<class Serializer>
void serializeOp(Serializer& serializer) void serializeOp(Serializer& serializer)
{ {

View File

@ -140,6 +140,8 @@ private:
createAnalyticAquiferPointer(const AquiferData& aqData, createAnalyticAquiferPointer(const AquiferData& aqData,
const int aquiferID, const int aquiferID,
std::string_view aqType) const; std::string_view aqType) const;
void computeConnectionAreaFraction() const;
}; };

View File

@ -45,16 +45,22 @@ template <typename TypeTag>
void void
BlackoilAquiferModel<TypeTag>::initialSolutionApplied() BlackoilAquiferModel<TypeTag>::initialSolutionApplied()
{ {
for (auto& aquifer : aquifers) this->computeConnectionAreaFraction();
for (auto& aquifer : this->aquifers) {
aquifer->initialSolutionApplied(); aquifer->initialSolutionApplied();
}
} }
template <typename TypeTag> template <typename TypeTag>
void void
BlackoilAquiferModel<TypeTag>::initFromRestart(const data::Aquifers& aquiferSoln) BlackoilAquiferModel<TypeTag>::initFromRestart(const data::Aquifers& aquiferSoln)
{ {
for (auto& aquifer : this->aquifers) this->computeConnectionAreaFraction();
for (auto& aquifer : this->aquifers) {
aquifer->initFromRestart(aquiferSoln); aquifer->initFromRestart(aquiferSoln);
}
} }
template <typename TypeTag> template <typename TypeTag>
@ -67,6 +73,8 @@ BlackoilAquiferModel<TypeTag>::beginEpisode()
// SCHEDULE setup in this section it is the beginning of a report step // SCHEDULE setup in this section it is the beginning of a report step
this->createDynamicAquifers(this->simulator_.episodeIndex()); this->createDynamicAquifers(this->simulator_.episodeIndex());
this->computeConnectionAreaFraction();
} }
template <typename TypeTag> template <typename TypeTag>
@ -78,8 +86,9 @@ template <typename TypeTag>
void void
BlackoilAquiferModel<TypeTag>::beginTimeStep() BlackoilAquiferModel<TypeTag>::beginTimeStep()
{ {
for (auto& aquifer : aquifers) for (auto& aquifer : this->aquifers) {
aquifer->beginTimeStep(); aquifer->beginTimeStep();
}
} }
template <typename TypeTag> template <typename TypeTag>
@ -90,8 +99,9 @@ BlackoilAquiferModel<TypeTag>::addToSource(RateVector& rates,
unsigned spaceIdx, unsigned spaceIdx,
unsigned timeIdx) const unsigned timeIdx) const
{ {
for (auto& aquifer : aquifers) for (auto& aquifer : this->aquifers) {
aquifer->addToSource(rates, context, spaceIdx, timeIdx); aquifer->addToSource(rates, context, spaceIdx, timeIdx);
}
} }
template <typename TypeTag> template <typename TypeTag>
@ -100,8 +110,9 @@ BlackoilAquiferModel<TypeTag>::addToSource(RateVector& rates,
unsigned globalSpaceIdx, unsigned globalSpaceIdx,
unsigned timeIdx) const unsigned timeIdx) const
{ {
for (auto& aquifer : aquifers) for (auto& aquifer : this->aquifers) {
aquifer->addToSource(rates, globalSpaceIdx, timeIdx); aquifer->addToSource(rates, globalSpaceIdx, timeIdx);
}
} }
template <typename TypeTag> template <typename TypeTag>
@ -113,9 +124,10 @@ template <typename TypeTag>
void void
BlackoilAquiferModel<TypeTag>::endTimeStep() BlackoilAquiferModel<TypeTag>::endTimeStep()
{ {
for (auto& aquifer : aquifers) { using NumAq = AquiferNumerical<TypeTag>;
for (auto& aquifer : this->aquifers) {
aquifer->endTimeStep(); aquifer->endTimeStep();
using NumAq = AquiferNumerical<TypeTag>;
NumAq* num = dynamic_cast<NumAq*>(aquifer.get()); NumAq* num = dynamic_cast<NumAq*>(aquifer.get());
if (num) if (num)
this->simulator_.vanguard().grid().comm().barrier(); this->simulator_.vanguard().grid().comm().barrier();
@ -159,8 +171,9 @@ template<typename TypeTag>
data::Aquifers BlackoilAquiferModel<TypeTag>::aquiferData() const data::Aquifers BlackoilAquiferModel<TypeTag>::aquiferData() const
{ {
data::Aquifers data; data::Aquifers data;
for (const auto& aqu : this->aquifers) for (const auto& aqu : this->aquifers) {
data.insert_or_assign(aqu->aquiferID(), aqu->aquiferData()); data.insert_or_assign(aqu->aquiferID(), aqu->aquiferData());
}
return data; return data;
} }
@ -170,7 +183,7 @@ template<class Serializer>
void BlackoilAquiferModel<TypeTag>:: void BlackoilAquiferModel<TypeTag>::
serializeOp(Serializer& serializer) serializeOp(Serializer& serializer)
{ {
for (auto& aiPtr : aquifers) { for (auto& aiPtr : this->aquifers) {
auto* ct = dynamic_cast<AquiferCarterTracy<TypeTag>*>(aiPtr.get()); auto* ct = dynamic_cast<AquiferCarterTracy<TypeTag>*>(aiPtr.get());
auto* fetp = dynamic_cast<AquiferFetkovich<TypeTag>*>(aiPtr.get()); auto* fetp = dynamic_cast<AquiferFetkovich<TypeTag>*>(aiPtr.get());
auto* num = dynamic_cast<AquiferNumerical<TypeTag>*>(aiPtr.get()); auto* num = dynamic_cast<AquiferNumerical<TypeTag>*>(aiPtr.get());
@ -304,4 +317,26 @@ void BlackoilAquiferModel<TypeTag>::createDynamicAquifers(const int episode_inde
} }
} }
template <typename TypeTag>
void BlackoilAquiferModel<TypeTag>::computeConnectionAreaFraction() const
{
auto maxAquID =
std::accumulate(this->aquifers.begin(), this->aquifers.end(), 0,
[](const int aquID, const auto& aquifer)
{ return std::max(aquID, aquifer->aquiferID()); });
maxAquID = this->simulator_.vanguard().grid().comm().max(maxAquID);
auto totalConnArea = std::vector<double>(maxAquID, 0.0);
for (const auto& aquifer : this->aquifers) {
totalConnArea[aquifer->aquiferID() - 1] += aquifer->totalFaceArea();
}
this->simulator_.vanguard().grid().comm().sum(totalConnArea.data(), maxAquID);
for (auto& aquifer : this->aquifers) {
aquifer->computeFaceAreaFraction(totalConnArea);
}
}
} // namespace Opm } // namespace Opm