Merge pull request #5742 from akva2/more_code_generic_well_model

More some more code to generic well model
This commit is contained in:
Bård Skaflestad
2024-12-19 13:50:31 +01:00
committed by GitHub
7 changed files with 240 additions and 262 deletions

View File

@@ -815,7 +815,7 @@ private:
const SimulatorTimerInterface& timer,
const Domain& domain)
{
auto initial_local_well_primary_vars = model_.wellModel().getPrimaryVarsDomain(domain);
auto initial_local_well_primary_vars = model_.wellModel().getPrimaryVarsDomain(domain.index);
auto initial_local_solution = Details::extractVector(solution, domain.cells);
auto res = solveDomain(domain, timer, logger, iteration, false);
local_report = res.first;
@@ -825,7 +825,7 @@ private:
Details::setGlobal(initial_local_solution, domain.cells, solution);
model_.simulator().model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0, domain);
} else {
model_.wellModel().setPrimaryVarsDomain(domain, initial_local_well_primary_vars);
model_.wellModel().setPrimaryVarsDomain(domain.index, initial_local_well_primary_vars);
Details::setGlobal(initial_local_solution, domain.cells, solution);
model_.simulator().model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0, domain);
}
@@ -840,7 +840,7 @@ private:
const SimulatorTimerInterface& timer,
const Domain& domain)
{
auto initial_local_well_primary_vars = model_.wellModel().getPrimaryVarsDomain(domain);
auto initial_local_well_primary_vars = model_.wellModel().getPrimaryVarsDomain(domain.index);
auto initial_local_solution = Details::extractVector(solution, domain.cells);
auto res = solveDomain(domain, timer, logger, iteration, true);
local_report = res.first;
@@ -881,7 +881,7 @@ private:
auto local_solution = Details::extractVector(solution, domain.cells);
Details::setGlobal(local_solution, domain.cells, locally_solved);
} else {
model_.wellModel().setPrimaryVarsDomain(domain, initial_local_well_primary_vars);
model_.wellModel().setPrimaryVarsDomain(domain.index, initial_local_well_primary_vars);
Details::setGlobal(initial_local_solution, domain.cells, solution);
model_.simulator().model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0, domain);
}

View File

@@ -296,7 +296,7 @@ template<class Scalar> class WellContributions;
data::WellBlockAveragePressures wellBlockAveragePressures() const
{
return this->computeWellBlockAveragePressures();
return this->computeWellBlockAveragePressures(this->gravity_);
}
// subtract B*inv(D)*C * x from A*x
@@ -387,10 +387,6 @@ template<class Scalar> class WellContributions;
const Domain& domain);
void updateWellControlsDomain(DeferredLogger& deferred_logger, const Domain& domain);
void logPrimaryVars() const;
std::vector<Scalar> getPrimaryVarsDomain(const Domain& domain) const;
void setPrimaryVarsDomain(const Domain& domain, const std::vector<Scalar>& vars);
void setupDomains(const std::vector<Domain>& domains);
protected:
@@ -429,14 +425,6 @@ template<class Scalar> class WellContributions;
std::unique_ptr<RateConverterType> rateConverter_{};
std::map<std::string, std::unique_ptr<AverageRegionalPressureType>> regionalAveragePressureCalculator_{};
struct WBPCalcID
{
std::optional<typename std::vector<WellInterfacePtr>::size_type> openWellIdx_{};
std::size_t wbpCalcIdx_{};
};
std::vector<WBPCalcID> wbpCalcMap_{};
SimulatorReportSingle last_report_{};
// Pre-step network solve at static reservoir conditions (group and well states might be updated)
@@ -447,9 +435,6 @@ template<class Scalar> class WellContributions;
std::vector<Scalar> B_avg_{};
// Keep track of the domain of each well, if using subdomains.
std::map<std::string, int> well_domain_;
// Store the local index of the wells perforated cells in the domain, if using subdomains
SparseTable<int> well_local_cells_;
@@ -515,16 +500,6 @@ template<class Scalar> class WellContributions;
// setting the well_solutions_ based on well_state.
void updatePrimaryVariables(DeferredLogger& deferred_logger);
void initializeWBPCalculationService();
data::WellBlockAveragePressures
computeWellBlockAveragePressures() const;
typename ParallelWBPCalculation<Scalar>::EvaluatorFactory
makeWellSourceEvaluatorFactory(const std::vector<Well>::size_type wellIdx) const;
void registerOpenWellsForWBPCalculation();
void updateAverageFormationFactor();
void computePotentials(const std::size_t widx,

View File

@@ -69,9 +69,8 @@
#include <algorithm>
#include <cassert>
#include <functional>
#include <stack>
#include <stdexcept>
#include <string_view>
#include <sstream>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
@@ -2041,6 +2040,182 @@ updateFiltrationModelsPreStep(DeferredLogger& deferred_logger)
}
}
template<class Scalar>
void BlackoilWellModelGeneric<Scalar>::
logPrimaryVars() const
{
std::ostringstream os;
for (const auto& w : this->well_container_generic_) {
os << w->name() << ":";
auto pv = w->getPrimaryVars();
for (const Scalar v : pv) {
os << ' ' << v;
}
os << '\n';
}
OpmLog::debug(os.str());
}
template<class Scalar>
std::vector<Scalar>
BlackoilWellModelGeneric<Scalar>::
getPrimaryVarsDomain(const int domainIdx) const
{
std::vector<Scalar> ret;
for (const auto& well : this->well_container_generic_) {
if (this->well_domain_.at(well->name()) == domainIdx) {
const auto& pv = well->getPrimaryVars();
ret.insert(ret.end(), pv.begin(), pv.end());
}
}
return ret;
}
template<class Scalar>
void BlackoilWellModelGeneric<Scalar>::
setPrimaryVarsDomain(const int domainIdx, const std::vector<Scalar>& vars)
{
std::size_t offset = 0;
for (auto& well : this->well_container_generic_) {
if (this->well_domain_.at(well->name()) == domainIdx) {
int num_pri_vars = well->setPrimaryVars(vars.begin() + offset);
offset += num_pri_vars;
}
}
assert(offset == vars.size());
}
template<class Scalar>
void BlackoilWellModelGeneric<Scalar>::
registerOpenWellsForWBPCalculation()
{
assert (this->wbpCalcMap_.size() == this->wells_ecl_.size());
for (auto& wbpCalc : this->wbpCalcMap_) {
wbpCalc.openWellIdx_.reset();
}
auto openWellIdx = typename std::vector<WellInterfaceGeneric<Scalar>*>::size_type{0};
for (const auto* openWell : this->well_container_generic_) {
this->wbpCalcMap_[openWell->indexOfWell()].openWellIdx_ = openWellIdx++;
}
}
template<class Scalar>
typename ParallelWBPCalculation<Scalar>::EvaluatorFactory
BlackoilWellModelGeneric<Scalar>::
makeWellSourceEvaluatorFactory(const std::vector<Well>::size_type wellIdx) const
{
using Span = typename PAvgDynamicSourceData<Scalar>::template SourceDataSpan<Scalar>;
using Item = typename Span::Item;
return [wellIdx, this]() -> typename ParallelWBPCalculation<Scalar>::Evaluator
{
if (! this->wbpCalcMap_[wellIdx].openWellIdx_.has_value()) {
// Well is stopped/shut. Return evaluator for stopped wells.
return []([[maybe_unused]] const int connIdx, Span sourceTerm)
{
// Well/connection is stopped/shut. Set all items to
// zero.
sourceTerm
.set(Item::Pressure , 0.0)
.set(Item::PoreVol , 0.0)
.set(Item::MixtureDensity, 0.0)
.set(Item::Depth , 0.0)
;
};
}
// Well is open. Return an evaluator for open wells/open connections.
return [this, wellPtr = this->well_container_generic_[*this->wbpCalcMap_[wellIdx].openWellIdx_]]
(const int connIdx, Span sourceTerm)
{
// Note: The only item which actually matters for the WBP
// calculation at the well reservoir connection level is the
// mixture density. Set other items to zero.
const auto& connIdxMap =
this->conn_idx_map_[wellPtr->indexOfWell()];
const auto rho = wellPtr->
connectionDensity(connIdxMap.global(connIdx),
connIdxMap.open(connIdx));
sourceTerm
.set(Item::Pressure , 0.0)
.set(Item::PoreVol , 0.0)
.set(Item::MixtureDensity, rho)
.set(Item::Depth , 0.0)
;
};
};
}
template<class Scalar>
void BlackoilWellModelGeneric<Scalar>::
initializeWBPCalculationService()
{
this->wbpCalcMap_.clear();
this->wbpCalcMap_.resize(this->wells_ecl_.size());
this->registerOpenWellsForWBPCalculation();
auto wellID = std::size_t{0};
for (const auto& well : this->wells_ecl_) {
this->wbpCalcMap_[wellID].wbpCalcIdx_ = this->wbpCalculationService_
.createCalculator(well,
this->local_parallel_well_info_[wellID],
this->conn_idx_map_[wellID].local(),
this->makeWellSourceEvaluatorFactory(wellID));
++wellID;
}
this->wbpCalculationService_.defineCommunication();
}
template<class Scalar>
data::WellBlockAveragePressures
BlackoilWellModelGeneric<Scalar>::
computeWellBlockAveragePressures(const Scalar gravity) const
{
auto wbpResult = data::WellBlockAveragePressures{};
using Calculated = typename PAvgCalculatorResult<Scalar>::WBPMode;
using Output = data::WellBlockAvgPress::Quantity;
this->wbpCalculationService_.collectDynamicValues();
const auto numWells = this->wells_ecl_.size();
for (auto wellID = 0*numWells; wellID < numWells; ++wellID) {
const auto calcIdx = this->wbpCalcMap_[wellID].wbpCalcIdx_;
const auto& well = this->wells_ecl_[wellID];
if (! well.hasRefDepth()) {
// Can't perform depth correction without at least a
// fall-back datum depth.
continue;
}
this->wbpCalculationService_
.inferBlockAveragePressures(calcIdx, well.pavg(),
gravity,
well.getWPaveRefDepth());
const auto& result = this->wbpCalculationService_
.averagePressures(calcIdx);
auto& reported = wbpResult.values[well.name()];
reported[Output::WBP] = result.value(Calculated::WBP);
reported[Output::WBP4] = result.value(Calculated::WBP4);
reported[Output::WBP5] = result.value(Calculated::WBP5);
reported[Output::WBP9] = result.value(Calculated::WBP9);
}
return wbpResult;
}
template class BlackoilWellModelGeneric<double>;

View File

@@ -219,6 +219,10 @@ public:
}
bool wasDynamicallyShutThisTimeStep(const std::string& well_name) const;
void logPrimaryVars() const;
std::vector<Scalar> getPrimaryVarsDomain(const int domainIdx) const;
void setPrimaryVarsDomain(const int domainIdx, const std::vector<Scalar>& vars);
template<class Serializer>
void serializeOp(Serializer& serializer)
{
@@ -454,6 +458,16 @@ protected:
void assignMassGasRate(data::Wells& wsrpt,
const Scalar& gasDensity) const;
void registerOpenWellsForWBPCalculation();
typename ParallelWBPCalculation<Scalar>::EvaluatorFactory
makeWellSourceEvaluatorFactory(const std::vector<Well>::size_type wellIdx) const;
void initializeWBPCalculationService();
data::WellBlockAveragePressures
computeWellBlockAveragePressures(const Scalar gravity) const;
Schedule& schedule_;
const SummaryState& summaryState_;
const EclipseState& eclState_;
@@ -577,6 +591,14 @@ protected:
std::vector<WellProdIndexCalculator<Scalar>> prod_index_calc_;
mutable ParallelWBPCalculation<Scalar> wbpCalculationService_;
struct WBPCalcID
{
std::optional<typename std::vector<WellInterfaceGeneric<Scalar>*>::size_type> openWellIdx_{};
std::size_t wbpCalcIdx_{};
};
std::vector<WBPCalcID> wbpCalcMap_{};
std::vector<int> pvt_region_idx_;
mutable std::unordered_set<std::string> closed_this_step_;
@@ -613,6 +635,9 @@ protected:
// Store map of group name and close offending well for output
std::map<std::string, std::pair<std::string, std::string>> closed_offending_wells_;
// Keep track of the domain of each well, if using subdomains.
std::map<std::string, int> well_domain_;
private:
WellInterfaceGeneric<Scalar>* getGenWell(const std::string& well_name);

View File

@@ -273,7 +273,7 @@ namespace Opm {
// try/catch here, as this function is not called in
// parallel but for each individual domain of each rank.
for (const auto& well: well_container_) {
if (well_domain_.at(well->name()) == domain.index) {
if (this->well_domain_.at(well->name()) == domain.index) {
// Modifiy the Jacobian with explicit Schur complement
// contributions if requested.
if (param_.matrix_add_well_contributions_) {
@@ -1187,7 +1187,8 @@ namespace Opm {
template<typename TypeTag>
void
BlackoilWellModel<TypeTag>::
doPreStepNetworkRebalance(DeferredLogger& deferred_logger) {
doPreStepNetworkRebalance(DeferredLogger& deferred_logger)
{
const double dt = this->simulator_.timeStepSize();
// TODO: should we also have the group and network backed-up here in case the solution did not get converged?
auto& well_state = this->wellState();
@@ -1288,7 +1289,9 @@ namespace Opm {
template<typename TypeTag>
bool
BlackoilWellModel<TypeTag>::
updateWellControlsAndNetwork(const bool mandatory_network_balance, const double dt, DeferredLogger& local_deferredLogger)
updateWellControlsAndNetwork(const bool mandatory_network_balance,
const double dt,
DeferredLogger& local_deferredLogger)
{
// not necessarily that we always need to update once of the network solutions
bool do_network_update = true;
@@ -1772,7 +1775,7 @@ namespace Opm {
assembleWellEqDomain(const double dt, const Domain& domain, DeferredLogger& deferred_logger)
{
for (auto& well : well_container_) {
if (well_domain_.at(well->name()) == domain.index) {
if (this->well_domain_.at(well->name()) == domain.index) {
well->assembleWellEq(simulator_, dt, this->wellState(), this->groupState(), deferred_logger);
}
}
@@ -1842,7 +1845,7 @@ namespace Opm {
{
for (size_t well_index = 0; well_index < well_container_.size(); ++well_index) {
auto& well = well_container_[well_index];
if (well_domain_.at(well->name()) == domainIndex) {
if (this->well_domain_.at(well->name()) == domainIndex) {
// Well equations B and C uses only the perforated cells, so need to apply on local vectors
// transfer global cells index to local subdomain cells index
const auto& local_cells = well_local_cells_[well_index];
@@ -2082,7 +2085,7 @@ namespace Opm {
// parallel but for each individual domain of each rank.
DeferredLogger local_deferredLogger;
for (auto& well : well_container_) {
if (well_domain_.at(well->name()) == domain.index) {
if (this->well_domain_.at(well->name()) == domain.index) {
const auto& cells = well->cells();
x_local_.resize(cells.size());
@@ -2119,7 +2122,7 @@ namespace Opm {
initPrimaryVariablesEvaluationDomain(const Domain& domain) const
{
for (auto& well : well_container_) {
if (well_domain_.at(well->name()) == domain.index) {
if (this->well_domain_.at(well->name()) == domain.index) {
well->initPrimaryVariablesEvaluation();
}
}
@@ -2142,7 +2145,7 @@ namespace Opm {
ConvergenceReport report;
for (const auto& well : well_container_) {
if ((well_domain_.at(well->name()) == domain.index)) {
if ((this->well_domain_.at(well->name()) == domain.index)) {
if (well->isOperableAndSolvable() || well->wellIsStopped()) {
report += well->getWellConvergence(simulator_,
this->wellState(),
@@ -2245,7 +2248,9 @@ namespace Opm {
template<typename TypeTag>
std::pair<bool, bool>
BlackoilWellModel<TypeTag>::
updateWellControls(const bool mandatory_network_balance, DeferredLogger& deferred_logger, const bool relax_network_tolerance)
updateWellControls(const bool mandatory_network_balance,
DeferredLogger& deferred_logger,
const bool relax_network_tolerance)
{
const int episodeIdx = simulator_.episodeIndex();
const auto& network = this->schedule()[episodeIdx].network();
@@ -2346,7 +2351,7 @@ namespace Opm {
// Check only individual well constraints and communicate.
for (const auto& well : well_container_) {
if (well_domain_.at(well->name()) == domain.index) {
if (this->well_domain_.at(well->name()) == domain.index) {
const auto mode = WellInterface<TypeTag>::IndividualOrGroup::Individual;
well->updateWellControl(simulator_, mode, this->wellState(), this->groupState(), deferred_logger);
}
@@ -2357,156 +2362,6 @@ namespace Opm {
template <typename TypeTag>
void
BlackoilWellModel<TypeTag>::
initializeWBPCalculationService()
{
this->wbpCalcMap_.clear();
this->wbpCalcMap_.resize(this->wells_ecl_.size());
this->registerOpenWellsForWBPCalculation();
auto wellID = std::size_t{0};
for (const auto& well : this->wells_ecl_) {
this->wbpCalcMap_[wellID].wbpCalcIdx_ = this->wbpCalculationService_
.createCalculator(well,
this->local_parallel_well_info_[wellID],
this->conn_idx_map_[wellID].local(),
this->makeWellSourceEvaluatorFactory(wellID));
++wellID;
}
this->wbpCalculationService_.defineCommunication();
}
template <typename TypeTag>
data::WellBlockAveragePressures
BlackoilWellModel<TypeTag>::
computeWellBlockAveragePressures() const
{
auto wbpResult = data::WellBlockAveragePressures{};
using Calculated = typename PAvgCalculatorResult<Scalar>::WBPMode;
using Output = data::WellBlockAvgPress::Quantity;
this->wbpCalculationService_.collectDynamicValues();
const auto numWells = this->wells_ecl_.size();
for (auto wellID = 0*numWells; wellID < numWells; ++wellID) {
const auto calcIdx = this->wbpCalcMap_[wellID].wbpCalcIdx_;
const auto& well = this->wells_ecl_[wellID];
if (! well.hasRefDepth()) {
// Can't perform depth correction without at least a
// fall-back datum depth.
continue;
}
this->wbpCalculationService_
.inferBlockAveragePressures(calcIdx, well.pavg(),
this->gravity_,
well.getWPaveRefDepth());
const auto& result = this->wbpCalculationService_
.averagePressures(calcIdx);
auto& reported = wbpResult.values[well.name()];
reported[Output::WBP] = result.value(Calculated::WBP);
reported[Output::WBP4] = result.value(Calculated::WBP4);
reported[Output::WBP5] = result.value(Calculated::WBP5);
reported[Output::WBP9] = result.value(Calculated::WBP9);
}
return wbpResult;
}
template <typename TypeTag>
typename ParallelWBPCalculation<typename BlackoilWellModel<TypeTag>::Scalar>::EvaluatorFactory
BlackoilWellModel<TypeTag>::
makeWellSourceEvaluatorFactory(const std::vector<Well>::size_type wellIdx) const
{
using Span = typename PAvgDynamicSourceData<Scalar>::template SourceDataSpan<Scalar>;
using Item = typename Span::Item;
return [wellIdx, this]() -> typename ParallelWBPCalculation<Scalar>::Evaluator
{
if (! this->wbpCalcMap_[wellIdx].openWellIdx_.has_value()) {
// Well is stopped/shut. Return evaluator for stopped wells.
return []([[maybe_unused]] const int connIdx, Span sourceTerm)
{
// Well/connection is stopped/shut. Set all items to
// zero.
sourceTerm
.set(Item::Pressure , 0.0)
.set(Item::PoreVol , 0.0)
.set(Item::MixtureDensity, 0.0)
.set(Item::Depth , 0.0)
;
};
}
// Well is open. Return an evaluator for open wells/open connections.
return [this, wellPtr = this->well_container_[*this->wbpCalcMap_[wellIdx].openWellIdx_].get()]
(const int connIdx, Span sourceTerm)
{
// Note: The only item which actually matters for the WBP
// calculation at the well reservoir connection level is the
// mixture density. Set other items to zero.
const auto& connIdxMap =
this->conn_idx_map_[wellPtr->indexOfWell()];
const auto rho = wellPtr->
connectionDensity(connIdxMap.global(connIdx),
connIdxMap.open(connIdx));
sourceTerm
.set(Item::Pressure , 0.0)
.set(Item::PoreVol , 0.0)
.set(Item::MixtureDensity, rho)
.set(Item::Depth , 0.0)
;
};
};
}
template <typename TypeTag>
void
BlackoilWellModel<TypeTag>::
registerOpenWellsForWBPCalculation()
{
assert (this->wbpCalcMap_.size() == this->wells_ecl_.size());
for (auto& wbpCalc : this->wbpCalcMap_) {
wbpCalc.openWellIdx_.reset();
}
auto openWellIdx = typename std::vector<WellInterfacePtr>::size_type{0};
for (const auto* openWell : this->well_container_generic_) {
this->wbpCalcMap_[openWell->indexOfWell()].openWellIdx_ = openWellIdx++;
}
}
template<typename TypeTag>
void
BlackoilWellModel<TypeTag>::
@@ -2985,59 +2840,6 @@ namespace Opm {
}
template <typename TypeTag>
void
BlackoilWellModel<TypeTag>::
logPrimaryVars() const
{
std::ostringstream os;
for (const auto& w : well_container_) {
os << w->name() << ":";
auto pv = w->getPrimaryVars();
for (const Scalar v : pv) {
os << ' ' << v;
}
os << '\n';
}
OpmLog::debug(os.str());
}
template <typename TypeTag>
std::vector<typename BlackoilWellModel<TypeTag>::Scalar>
BlackoilWellModel<TypeTag>::
getPrimaryVarsDomain(const Domain& domain) const
{
std::vector<Scalar> ret;
for (const auto& well : well_container_) {
if (well_domain_.at(well->name()) == domain.index) {
const auto& pv = well->getPrimaryVars();
ret.insert(ret.end(), pv.begin(), pv.end());
}
}
return ret;
}
template <typename TypeTag>
void
BlackoilWellModel<TypeTag>::
setPrimaryVarsDomain(const Domain& domain, const std::vector<Scalar>& vars)
{
std::size_t offset = 0;
for (auto& well : well_container_) {
if (well_domain_.at(well->name()) == domain.index) {
int num_pri_vars = well->setPrimaryVars(vars.begin() + offset);
offset += num_pri_vars;
}
}
assert(offset == vars.size());
}
template <typename TypeTag>
void
BlackoilWellModel<TypeTag>::
@@ -3060,7 +2862,7 @@ namespace Opm {
if (cell_present(first_well_cell)) {
// Assuming that if the first well cell is found in a domain,
// then all of that well's cells are in that same domain.
well_domain_[wellPtr->name()] = domain.index;
this->well_domain_[wellPtr->name()] = domain.index;
// Verify that all of that well's cells are in that same domain.
for (int well_cell : wellPtr->cells()) {
@@ -3080,10 +2882,10 @@ namespace Opm {
const Opm::Parallel::Communication& comm = grid().comm();
const int rank = comm.rank();
DeferredLogger local_log;
if (!well_domain_.empty()) {
if (!this->well_domain_.empty()) {
std::ostringstream os;
os << "Well name Rank Domain\n";
for (const auto& [wname, domain] : well_domain_) {
for (const auto& [wname, domain] : this->well_domain_) {
os << wname << std::setw(19 - wname.size()) << rank << std::setw(12) << domain << '\n';
}
local_log.debug(os.str());
@@ -3099,7 +2901,7 @@ namespace Opm {
std::vector<int> local_cells;
for (const auto& well : well_container_) {
const auto& global_cells = well->cells();
const int domain_index = well_domain_.at(well->name());
const int domain_index = this->well_domain_.at(well->name());
const auto& domain_cells = domains[domain_index].cells;
local_cells.resize(global_cells.size());

View File

@@ -270,9 +270,6 @@ public:
WellState<Scalar>& well_state,
DeferredLogger& deferred_logger) const = 0;
virtual Scalar connectionDensity(const int globalConnIdx,
const int openConnIdx) const = 0;
/// \brief Wether the Jacobian will also have well contributions in it.
virtual bool jacobianContainsWellContributions() const
{
@@ -358,16 +355,6 @@ public:
return connectionRates_;
}
virtual std::vector<Scalar> getPrimaryVars() const
{
return {};
}
virtual int setPrimaryVars(typename std::vector<Scalar>::const_iterator)
{
return 0;
}
std::vector<Scalar> wellIndex(const int perf,
const IntensiveQuantities& intQuants,
const Scalar trans_mult,

View File

@@ -189,6 +189,20 @@ public:
void resetWellOperability();
virtual std::vector<Scalar> getPrimaryVars() const
{
return {};
}
virtual int setPrimaryVars(typename std::vector<Scalar>::const_iterator)
{
return 0;
}
virtual Scalar connectionDensity(const int globalConnIdx,
const int openConnIdx) const = 0;
protected:
bool getAllowCrossFlow() const;