mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
changed: move NLDD BlackoilWellModel code to separate class
This commit is contained in:
parent
e288a61c59
commit
078249503f
@ -167,6 +167,7 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
opm/simulators/wells/BlackoilWellModelGasLift.cpp
|
||||
opm/simulators/wells/BlackoilWellModelGeneric.cpp
|
||||
opm/simulators/wells/BlackoilWellModelGuideRates.cpp
|
||||
opm/simulators/wells/BlackoilWellModelNldd.cpp
|
||||
opm/simulators/wells/BlackoilWellModelRestart.cpp
|
||||
opm/simulators/wells/BlackoilWellModelWBP.cpp
|
||||
opm/simulators/wells/ConnFiltrateData.cpp
|
||||
@ -972,6 +973,8 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/simulators/wells/BlackoilWellModelGasLift_impl.hpp
|
||||
opm/simulators/wells/BlackoilWellModelGeneric.hpp
|
||||
opm/simulators/wells/BlackoilWellModelGuideRates.hpp
|
||||
opm/simulators/wells/BlackoilWellModelNldd.hpp
|
||||
opm/simulators/wells/BlackoilWellModelNldd_impl.hpp
|
||||
opm/simulators/wells/BlackoilWellModelRestart.hpp
|
||||
opm/simulators/wells/BlackoilWellModelWBP.hpp
|
||||
opm/simulators/wells/ConnectionIndexMap.hpp
|
||||
|
@ -50,6 +50,8 @@
|
||||
|
||||
#include <opm/simulators/utils/ComponentName.hpp>
|
||||
|
||||
#include <opm/simulators/wells/BlackoilWellModelNldd.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <algorithm>
|
||||
@ -64,6 +66,7 @@
|
||||
#include <ios>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@ -76,7 +79,8 @@ template<class TypeTag> class BlackoilModel;
|
||||
|
||||
/// A NLDD implementation for three-phase black oil.
|
||||
template <class TypeTag>
|
||||
class BlackoilModelNldd {
|
||||
class BlackoilModelNldd
|
||||
{
|
||||
public:
|
||||
using ElementContext = GetPropType<TypeTag, Properties::ElementContext>;
|
||||
using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>;
|
||||
@ -98,11 +102,16 @@ public:
|
||||
//! \param param param Model parameters
|
||||
//! \param compNames Names of the solution components
|
||||
explicit BlackoilModelNldd(BlackoilModel<TypeTag>& model)
|
||||
: model_(model), rank_(model_.simulator().vanguard().grid().comm().rank())
|
||||
: model_(model)
|
||||
, wellModel_(model.wellModel())
|
||||
, rank_(model_.simulator().vanguard().grid().comm().rank())
|
||||
{
|
||||
// Create partitions.
|
||||
const auto& [partition_vector, num_domains] = this->partitionCells();
|
||||
|
||||
// Set nldd handler in main well model
|
||||
model.wellModel().setNlddAdapter(&wellModel_);
|
||||
|
||||
// Scan through partitioning to get correct size for each.
|
||||
std::vector<int> sizes(num_domains, 0);
|
||||
for (const auto& p : partition_vector) {
|
||||
@ -179,7 +188,7 @@ public:
|
||||
void prepareStep()
|
||||
{
|
||||
// Setup domain->well mapping.
|
||||
model_.wellModel().setupDomains(domains_);
|
||||
wellModel_.setupDomains(domains_);
|
||||
}
|
||||
|
||||
//! \brief Do one non-linear NLDD iteration.
|
||||
@ -352,7 +361,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//! \brief Solve the equation system for a single domain.
|
||||
std::pair<SimulatorReportSingle, ConvergenceReport>
|
||||
solveDomain(const Domain& domain,
|
||||
@ -380,9 +388,9 @@ private:
|
||||
modelSimulator.model().newtonMethod().setIterationIndex(iter);
|
||||
// TODO: we should have a beginIterationLocal function()
|
||||
// only handling the well model for now
|
||||
modelSimulator.problem().wellModel().assembleDomain(modelSimulator.model().newtonMethod().numIterations(),
|
||||
modelSimulator.timeStepSize(),
|
||||
domain);
|
||||
wellModel_.assemble(modelSimulator.model().newtonMethod().numIterations(),
|
||||
modelSimulator.timeStepSize(),
|
||||
domain);
|
||||
// Assemble reservoir locally.
|
||||
report += this->assembleReservoirDomain(domain);
|
||||
report.assemble_time += detailTimer.stop();
|
||||
@ -445,9 +453,9 @@ private:
|
||||
// TODO: we should have a beginIterationLocal function()
|
||||
// only handling the well model for now
|
||||
// Assemble reservoir locally.
|
||||
modelSimulator.problem().wellModel().assembleDomain(modelSimulator.model().newtonMethod().numIterations(),
|
||||
modelSimulator.timeStepSize(),
|
||||
domain);
|
||||
wellModel_.assemble(modelSimulator.model().newtonMethod().numIterations(),
|
||||
modelSimulator.timeStepSize(),
|
||||
domain);
|
||||
report += this->assembleReservoirDomain(domain);
|
||||
report.assemble_time += detailTimer.stop();
|
||||
|
||||
@ -736,7 +744,7 @@ private:
|
||||
logger,
|
||||
B_avg,
|
||||
residual_norms);
|
||||
report += model_.wellModel().getDomainWellConvergence(domain, B_avg, logger);
|
||||
report += wellModel_.getWellConvergence(domain, B_avg, logger);
|
||||
return report;
|
||||
}
|
||||
|
||||
@ -815,7 +823,7 @@ private:
|
||||
const SimulatorTimerInterface& timer,
|
||||
const Domain& domain)
|
||||
{
|
||||
auto initial_local_well_primary_vars = model_.wellModel().getPrimaryVarsDomain(domain.index);
|
||||
auto initial_local_well_primary_vars = 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 +833,7 @@ private:
|
||||
Details::setGlobal(initial_local_solution, domain.cells, solution);
|
||||
model_.simulator().model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0, domain);
|
||||
} else {
|
||||
model_.wellModel().setPrimaryVarsDomain(domain.index, initial_local_well_primary_vars);
|
||||
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 +848,7 @@ private:
|
||||
const SimulatorTimerInterface& timer,
|
||||
const Domain& domain)
|
||||
{
|
||||
auto initial_local_well_primary_vars = model_.wellModel().getPrimaryVarsDomain(domain.index);
|
||||
auto initial_local_well_primary_vars = 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 +889,7 @@ private:
|
||||
auto local_solution = Details::extractVector(solution, domain.cells);
|
||||
Details::setGlobal(local_solution, domain.cells, locally_solved);
|
||||
} else {
|
||||
model_.wellModel().setPrimaryVarsDomain(domain.index, initial_local_well_primary_vars);
|
||||
wellModel_.setPrimaryVarsDomain(domain.index, initial_local_well_primary_vars);
|
||||
Details::setGlobal(initial_local_solution, domain.cells, solution);
|
||||
model_.simulator().model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0, domain);
|
||||
}
|
||||
@ -996,6 +1004,7 @@ private:
|
||||
}
|
||||
|
||||
BlackoilModel<TypeTag>& model_; //!< Reference to model
|
||||
BlackoilWellModelNldd<TypeTag> wellModel_; //!< NLDD well model adapter
|
||||
std::vector<Domain> domains_; //!< Vector of subdomains
|
||||
std::vector<std::unique_ptr<Mat>> domain_matrices_; //!< Vector of matrix operator for each subdomain
|
||||
std::vector<ISTLSolverType> domain_linsolvers_; //!< Vector of linear solvers for each domain
|
||||
|
@ -57,8 +57,7 @@ namespace Opm
|
||||
|
||||
/// Representing a part of a grid, in a way suitable for performing
|
||||
/// local solves.
|
||||
template <class Grid>
|
||||
struct SubDomain
|
||||
struct SubDomainIndices
|
||||
{
|
||||
// The index of a subdomain is arbitrary, but can be used by the
|
||||
// solvers to keep track of well locations etc.
|
||||
@ -72,10 +71,21 @@ namespace Opm
|
||||
std::vector<bool> interior;
|
||||
// Enables subdomain solves and linearization using the generic linearization
|
||||
// approach (i.e. FvBaseLinearizer as opposed to TpfaLinearizer).
|
||||
SubDomainIndices(const int i, std::vector<int>&& c, std::vector<bool>&& in)
|
||||
: index(i), cells(std::move(c)), interior(std::move(in))
|
||||
{}
|
||||
};
|
||||
|
||||
/// Representing a part of a grid, in a way suitable for performing
|
||||
/// local solves.
|
||||
template <class Grid>
|
||||
struct SubDomain : public SubDomainIndices
|
||||
{
|
||||
Dune::SubGridPart<Grid> view;
|
||||
// Constructor that moves from its argument.
|
||||
SubDomain(const int i, std::vector<int>&& c, std::vector<bool>&& in, Dune::SubGridPart<Grid>&& v)
|
||||
: index(i), cells(std::move(c)), interior(std::move(in)), view(std::move(v))
|
||||
: SubDomainIndices(i, std::move(c), std::move(in))
|
||||
, view(std::move(v))
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -188,7 +188,8 @@ public:
|
||||
std::size_t well_index = 0;
|
||||
for (const auto& well : this->wellMod_) {
|
||||
if (this->wellMod_.well_domain().at(well->name()) == domainIndex_) {
|
||||
this->applySingleWell(x, y, well, this->wellMod_.well_local_cells()[well_index]);
|
||||
this->applySingleWell(x, y, well,
|
||||
this->wellMod_.well_local_cells()[well_index]);
|
||||
}
|
||||
++well_index;
|
||||
}
|
||||
@ -199,7 +200,10 @@ public:
|
||||
const bool use_well_weights) const override
|
||||
{
|
||||
OPM_TIMEBLOCK(addWellPressureEquations);
|
||||
this->wellMod_.addWellPressureEquationsDomain(jacobian, weights, use_well_weights, domainIndex_);
|
||||
this->wellMod_.addWellPressureEquationsDomain(jacobian,
|
||||
weights,
|
||||
use_well_weights,
|
||||
domainIndex_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -30,8 +30,6 @@
|
||||
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
|
||||
#include <opm/grid/utility/SparseTable.hpp>
|
||||
|
||||
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Schedule.hpp>
|
||||
@ -41,7 +39,6 @@
|
||||
|
||||
#include <opm/simulators/flow/countGlobalCells.hpp>
|
||||
#include <opm/simulators/flow/FlowBaseVanguard.hpp>
|
||||
#include <opm/simulators/flow/SubDomain.hpp>
|
||||
|
||||
#include <opm/simulators/linalg/matrixblock.hh>
|
||||
|
||||
@ -81,6 +78,9 @@
|
||||
|
||||
namespace Opm {
|
||||
|
||||
template<class Scalar> class BlackoilWellModelNldd;
|
||||
template<class T> class SparseTable;
|
||||
|
||||
#if COMPILE_GPU_BRIDGE
|
||||
template<class Scalar> class WellContributions;
|
||||
#endif
|
||||
@ -132,8 +132,6 @@ template<class Scalar> class WellContributions;
|
||||
using AverageRegionalPressureType = RegionAverageCalculator::
|
||||
AverageRegionalPressure<FluidSystem, std::vector<int> >;
|
||||
|
||||
using Domain = SubDomain<Grid>;
|
||||
|
||||
explicit BlackoilWellModel(Simulator& simulator);
|
||||
|
||||
void init();
|
||||
@ -248,11 +246,6 @@ template<class Scalar> class WellContributions;
|
||||
// Check if well equations is converged.
|
||||
ConvergenceReport getWellConvergence(const std::vector<Scalar>& B_avg, const bool checkWellGroupControls = false) const;
|
||||
|
||||
// Check if well equations are converged locally.
|
||||
ConvergenceReport getDomainWellConvergence(const Domain& domain,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
DeferredLogger& local_deferredLogger) const;
|
||||
|
||||
const SimulatorReportSingle& lastReport() const;
|
||||
|
||||
void addWellContributions(SparseMatrixAdapter& jacobian) const;
|
||||
@ -274,7 +267,6 @@ template<class Scalar> class WellContributions;
|
||||
// at the beginning of each time step (Not report step)
|
||||
void prepareTimeStep(DeferredLogger& deferred_logger);
|
||||
void initPrimaryVariablesEvaluation() const;
|
||||
void initPrimaryVariablesEvaluationDomain(const Domain& domain) const;
|
||||
|
||||
std::pair<bool, bool>
|
||||
updateWellControls(const bool mandatory_network_balance, DeferredLogger& deferred_logger, const bool relax_network_tolerance = false);
|
||||
@ -292,15 +284,23 @@ template<class Scalar> class WellContributions;
|
||||
|
||||
using PressureMatrix = Dune::BCRSMatrix<Opm::MatrixBlock<Scalar, 1, 1>>;
|
||||
|
||||
void addWellPressureEquations(PressureMatrix& jacobian, const BVector& weights,const bool use_well_weights) const;
|
||||
|
||||
void addWellPressureEquationsDomain([[maybe_unused]] PressureMatrix& jacobian,
|
||||
[[maybe_unused]] const BVector& weights,
|
||||
[[maybe_unused]] const bool use_well_weights,
|
||||
[[maybe_unused]] const int domainIndex) const;
|
||||
|
||||
|
||||
void addWellPressureEquations(PressureMatrix& jacobian,
|
||||
const BVector& weights,
|
||||
const bool use_well_weights) const;
|
||||
void addWellPressureEquationsStruct(PressureMatrix& jacobian) const;
|
||||
void addWellPressureEquationsDomain(PressureMatrix& jacobian,
|
||||
const BVector& weights,
|
||||
const bool use_well_weights,
|
||||
const int domainIndex) const
|
||||
{
|
||||
if (!nldd_) {
|
||||
OPM_THROW(std::logic_error, "Attempt to access NLDD data without a NLDD solver");
|
||||
}
|
||||
return nldd_->addWellPressureEquations(jacobian,
|
||||
weights,
|
||||
use_well_weights,
|
||||
domainIndex);
|
||||
}
|
||||
|
||||
/// \brief Get list of local nonshut wells
|
||||
const std::vector<WellInterfacePtr>& localNonshutWells() const
|
||||
@ -308,24 +308,32 @@ template<class Scalar> class WellContributions;
|
||||
return well_container_;
|
||||
}
|
||||
|
||||
// prototype for assemble function for ASPIN solveLocal()
|
||||
// will try to merge back to assemble() when done prototyping
|
||||
void assembleDomain(const int iterationIdx,
|
||||
const double dt,
|
||||
const Domain& domain);
|
||||
void updateWellControlsDomain(DeferredLogger& deferred_logger, const Domain& domain);
|
||||
|
||||
void setupDomains(const std::vector<Domain>& domains);
|
||||
|
||||
const SparseTable<int>& well_local_cells() const
|
||||
{
|
||||
return well_local_cells_;
|
||||
if (!nldd_) {
|
||||
OPM_THROW(std::logic_error, "Attempt to access NLDD data without a NLDD solver");
|
||||
}
|
||||
return nldd_->well_local_cells();
|
||||
}
|
||||
|
||||
const std::map<std::string, int>& well_domain() const
|
||||
{
|
||||
if (!nldd_) {
|
||||
OPM_THROW(std::logic_error, "Attempt to access NLDD data without a NLDD solver");
|
||||
}
|
||||
|
||||
return nldd_->well_domain();
|
||||
}
|
||||
|
||||
auto begin() const { return well_container_.begin(); }
|
||||
auto end() const { return well_container_.end(); }
|
||||
bool empty() const { return well_container_.empty(); }
|
||||
|
||||
bool addMatrixContributions() const { return param_.matrix_add_well_contributions_; }
|
||||
bool addMatrixContributions() const
|
||||
{ return param_.matrix_add_well_contributions_; }
|
||||
|
||||
int numStrictIterations() const
|
||||
{ return param_.strict_outer_iter_wells_; }
|
||||
|
||||
int compressedIndexForInterior(int cartesian_cell_idx) const override
|
||||
{
|
||||
@ -339,7 +347,16 @@ template<class Scalar> class WellContributions;
|
||||
// using the solution x to recover the solution xw for wells and applying
|
||||
// xw to update Well State
|
||||
void recoverWellSolutionAndUpdateWellStateDomain(const BVector& x,
|
||||
const Domain& domain);
|
||||
const int domainIdx);
|
||||
|
||||
const Grid& grid() const
|
||||
{ return simulator_.vanguard().grid(); }
|
||||
|
||||
const Simulator& simulator() const
|
||||
{ return simulator_; }
|
||||
|
||||
void setNlddAdapter(BlackoilWellModelNldd<TypeTag>* mod)
|
||||
{ nldd_ = mod; }
|
||||
|
||||
protected:
|
||||
Simulator& simulator_;
|
||||
@ -385,12 +402,6 @@ template<class Scalar> class WellContributions;
|
||||
|
||||
std::vector<Scalar> B_avg_{};
|
||||
|
||||
// Store the local index of the wells perforated cells in the domain, if using subdomains
|
||||
SparseTable<int> well_local_cells_;
|
||||
|
||||
const Grid& grid() const
|
||||
{ return simulator_.vanguard().grid(); }
|
||||
|
||||
const EquilGrid& equilGrid() const
|
||||
{ return simulator_.vanguard().equilGrid(); }
|
||||
|
||||
@ -464,7 +475,6 @@ template<class Scalar> class WellContributions;
|
||||
int reportStepIndex() const;
|
||||
|
||||
void assembleWellEq(const double dt, DeferredLogger& deferred_logger);
|
||||
void assembleWellEqDomain(const double dt, const Domain& domain, DeferredLogger& deferred_logger);
|
||||
|
||||
void prepareWellsBeforeAssembling(const double dt, DeferredLogger& deferred_logger);
|
||||
|
||||
@ -495,6 +505,7 @@ template<class Scalar> class WellContributions;
|
||||
BlackoilWellModel(Simulator& simulator, const PhaseUsage& pu);
|
||||
|
||||
BlackoilWellModelGasLift<TypeTag> gaslift_;
|
||||
BlackoilWellModelNldd<TypeTag>* nldd_ = nullptr; //!< NLDD well model adapter (not owned)
|
||||
|
||||
// These members are used to avoid reallocation in specific functions
|
||||
// instead of using local variables.
|
||||
|
@ -2017,35 +2017,6 @@ logPrimaryVars() const
|
||||
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>::
|
||||
reportGroupSwitching(DeferredLogger& local_deferredLogger) const
|
||||
|
@ -118,7 +118,12 @@ public:
|
||||
// whether there exists any multisegment well open on this process
|
||||
bool anyMSWellOpenLocal() const;
|
||||
|
||||
const std::vector<Well>& eclWells() const { return wells_ecl_; }
|
||||
const std::vector<Well>& eclWells() const
|
||||
{ return wells_ecl_; }
|
||||
|
||||
bool terminalOutput() const
|
||||
{ return terminal_output_; }
|
||||
|
||||
const Well& getWellEcl(const std::string& well_name) const;
|
||||
std::vector<Well> getLocalWells(const int timeStepIdx) const;
|
||||
const Schedule& schedule() const { return schedule_; }
|
||||
@ -127,6 +132,9 @@ public:
|
||||
std::vector<const WellInterfaceGeneric<Scalar>*> genericWells() const
|
||||
{ return {well_container_generic_.begin(), well_container_generic_.end()}; }
|
||||
|
||||
std::vector<WellInterfaceGeneric<Scalar>*> genericWells()
|
||||
{ return well_container_generic_; }
|
||||
|
||||
/*
|
||||
Immutable version of the currently active wellstate.
|
||||
*/
|
||||
@ -217,11 +225,6 @@ public:
|
||||
const std::map<std::string, double>& wellOpenTimes() const { return well_open_times_; }
|
||||
const std::map<std::string, double>& wellCloseTimes() const { return well_close_times_; }
|
||||
|
||||
const std::map<std::string, int>& well_domain() const
|
||||
{
|
||||
return well_domain_;
|
||||
}
|
||||
|
||||
std::vector<int> getCellsForConnections(const Well& well) const;
|
||||
|
||||
bool reportStepStarts() const { return report_step_starts_; }
|
||||
@ -236,8 +239,6 @@ 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)
|
||||
@ -537,9 +538,6 @@ 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);
|
||||
|
||||
|
170
opm/simulators/wells/BlackoilWellModelNldd.cpp
Normal file
170
opm/simulators/wells/BlackoilWellModelNldd.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright 2016 - 2019 SINTEF Digital, Mathematics & Cybernetics.
|
||||
Copyright 2016 - 2018 Equinor ASA.
|
||||
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
|
||||
Copyright 2016 - 2018 Norce AS
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <opm/simulators/wells/BlackoilWellModelNldd.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
template <typename Scalar>
|
||||
std::vector<Scalar>
|
||||
BlackoilWellModelNlddGeneric<Scalar>::
|
||||
getPrimaryVarsDomain(const int domainIdx) const
|
||||
{
|
||||
std::vector<Scalar> ret;
|
||||
for (const auto& well : genWellModel_.genericWells()) {
|
||||
if (this->well_domain_.at(well->name()) == domainIdx) {
|
||||
const auto& pv = well->getPrimaryVars();
|
||||
ret.insert(ret.end(), pv.begin(), pv.end());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
BlackoilWellModelNlddGeneric<Scalar>::
|
||||
setPrimaryVarsDomain(const int domainIdx, const std::vector<Scalar>& vars)
|
||||
{
|
||||
std::size_t offset = 0;
|
||||
for (const auto& well : genWellModel_.genericWells()) {
|
||||
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 <typename Scalar>
|
||||
void
|
||||
BlackoilWellModelNlddGeneric<Scalar>::
|
||||
findWellDomains(const std::vector<const SubDomainIndices*>& domains)
|
||||
{
|
||||
// TODO: This loop nest may be slow for very large numbers of
|
||||
// domains and wells, but that has not been observed on tests so
|
||||
// far. Using the partition vector instead would be faster if we
|
||||
// need to change.
|
||||
for (const auto& wellPtr : genWellModel_.genericWells()) {
|
||||
const int first_well_cell = wellPtr->cells().front();
|
||||
for (const auto& domain : domains) {
|
||||
auto cell_present = [domain](const auto cell)
|
||||
{
|
||||
return std::binary_search(domain->cells.begin(),
|
||||
domain->cells.end(), cell);
|
||||
};
|
||||
|
||||
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.
|
||||
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()) {
|
||||
if (! cell_present(well_cell)) {
|
||||
OPM_THROW(std::runtime_error,
|
||||
fmt::format("Well '{}' found on multiple domains.",
|
||||
wellPtr->name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
BlackoilWellModelNlddGeneric<Scalar>::
|
||||
logDomains() const
|
||||
{
|
||||
// Write well/domain info to the DBG file.
|
||||
const int rank = genWellModel_.comm().rank();
|
||||
DeferredLogger local_log;
|
||||
if (!this->well_domain_.empty()) {
|
||||
std::ostringstream os;
|
||||
os << "Well name Rank Domain\n";
|
||||
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());
|
||||
}
|
||||
auto global_log = gatherDeferredLogger(local_log, genWellModel_.comm());
|
||||
if (genWellModel_.terminalOutput()) {
|
||||
global_log.logMessages();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
BlackoilWellModelNlddGeneric<Scalar>::
|
||||
calcLocalIndices(const std::vector<const SubDomainIndices*>& domains)
|
||||
{
|
||||
well_local_cells_.clear();
|
||||
well_local_cells_.reserve(genWellModel_.genericWells().size(), 10);
|
||||
std::vector<int> local_cells;
|
||||
for (const auto& well : genWellModel_.genericWells()) {
|
||||
const auto& global_cells = well->cells();
|
||||
const int domain_index = this->well_domain_.at(well->name());
|
||||
const auto& domain_cells = domains[domain_index]->cells;
|
||||
local_cells.resize(global_cells.size());
|
||||
|
||||
// find the local cell index for each well cell in the domain
|
||||
// assume domain_cells is sorted
|
||||
for (size_t i = 0; i < global_cells.size(); ++i) {
|
||||
auto it = std::lower_bound(domain_cells.begin(), domain_cells.end(), global_cells[i]);
|
||||
if (it != domain_cells.end() && *it == global_cells[i]) {
|
||||
local_cells[i] = std::distance(domain_cells.begin(), it);
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, fmt::format("Cell {} not found in domain {}",
|
||||
global_cells[i], domain_index));
|
||||
}
|
||||
}
|
||||
well_local_cells_.appendRow(local_cells.begin(), local_cells.end());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
void
|
||||
BlackoilWellModelNlddGeneric<Scalar>::
|
||||
calcDomains(const std::vector<const SubDomainIndices*>& domains)
|
||||
{
|
||||
const Opm::Parallel::Communication& comm = genWellModel_.comm();
|
||||
|
||||
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
||||
this->findWellDomains(domains);
|
||||
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::setupDomains(): "
|
||||
"well found on multiple domains.", comm);
|
||||
|
||||
// Write well/domain info to the DBG file.
|
||||
this->logDomains();
|
||||
|
||||
// Pre-calculate the local cell indices for each well
|
||||
this->calcLocalIndices(domains);
|
||||
}
|
||||
|
||||
template class BlackoilWellModelNlddGeneric<double>;
|
||||
|
||||
#if FLOW_INSTANTIATE_FLOAT
|
||||
template class BlackoilWellModelNlddGeneric<float>;
|
||||
#endif
|
||||
|
||||
} // namespace Opm
|
141
opm/simulators/wells/BlackoilWellModelNldd.hpp
Normal file
141
opm/simulators/wells/BlackoilWellModelNldd.hpp
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2016 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2016 - 2017 Statoil ASA.
|
||||
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
|
||||
Copyright 2016 - 2018 IRIS AS
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef OPM_BLACKOILWELLMODEL_NLDD_HEADER_INCLUDED
|
||||
#define OPM_BLACKOILWELLMODEL_NLDD_HEADER_INCLUDED
|
||||
|
||||
#include <opm/grid/utility/SparseTable.hpp>
|
||||
|
||||
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
|
||||
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
|
||||
#include <opm/simulators/wells/BlackoilWellModel.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
template<class Scalar>
|
||||
class BlackoilWellModelNlddGeneric
|
||||
{
|
||||
public:
|
||||
std::vector<Scalar> getPrimaryVarsDomain(const int domainIdx) const;
|
||||
void setPrimaryVarsDomain(const int domainIdx, const std::vector<Scalar>& vars);
|
||||
|
||||
const SparseTable<int>& well_local_cells() const
|
||||
{ return well_local_cells_; }
|
||||
|
||||
const std::map<std::string, int>& well_domain() const
|
||||
{ return well_domain_; }
|
||||
|
||||
protected:
|
||||
BlackoilWellModelNlddGeneric(BlackoilWellModelGeneric<Scalar>& model)
|
||||
: genWellModel_(model)
|
||||
{}
|
||||
|
||||
void calcDomains(const std::vector<const SubDomainIndices*>& domains);
|
||||
|
||||
private:
|
||||
void logDomains() const;
|
||||
|
||||
void findWellDomains(const std::vector<const SubDomainIndices*>& domains);
|
||||
|
||||
void calcLocalIndices(const std::vector<const SubDomainIndices*>& domains);
|
||||
|
||||
BlackoilWellModelGeneric<Scalar>& genWellModel_;
|
||||
|
||||
// Keep track of the domain of each well
|
||||
std::map<std::string, int> well_domain_{};
|
||||
|
||||
// Store the local index of the wells perforated cells in the domain
|
||||
SparseTable<int> well_local_cells_{};
|
||||
};
|
||||
|
||||
/// Class for handling the blackoil well model in a NLDD solver.
|
||||
template<typename TypeTag>
|
||||
class BlackoilWellModelNldd :
|
||||
public BlackoilWellModelNlddGeneric<GetPropType<TypeTag, Properties::Scalar>>
|
||||
{
|
||||
public:
|
||||
// --------- Types ---------
|
||||
using Grid = GetPropType<TypeTag, Properties::Grid>;
|
||||
using Scalar = GetPropType<TypeTag, Properties::Scalar>;
|
||||
using PressureMatrix = typename BlackoilWellModel<TypeTag>::PressureMatrix;
|
||||
using BVector = typename BlackoilWellModel<TypeTag>::BVector;
|
||||
|
||||
using Domain = SubDomain<Grid>;
|
||||
|
||||
BlackoilWellModelNldd(BlackoilWellModel<TypeTag>& model)
|
||||
: BlackoilWellModelNlddGeneric<Scalar>(model)
|
||||
, wellModel_(model)
|
||||
{}
|
||||
|
||||
void initPrimaryVariablesEvaluation(const Domain& domain) const;
|
||||
|
||||
void addWellPressureEquations(PressureMatrix& jacobian,
|
||||
const BVector& weights,
|
||||
const bool use_well_weights,
|
||||
const int domainIndex) const;
|
||||
|
||||
// prototype for assemble function for ASPIN solveLocal()
|
||||
// will try to merge back to assemble() when done prototyping
|
||||
void assemble(const int iterationIdx,
|
||||
const double dt,
|
||||
const Domain& domain);
|
||||
|
||||
void updateWellControls(DeferredLogger& deferred_logger,
|
||||
const Domain& domain);
|
||||
|
||||
void setupDomains(const std::vector<Domain>& domains);
|
||||
|
||||
// Check if well equations are converged locally.
|
||||
ConvergenceReport getWellConvergence(const Domain& domain,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
DeferredLogger& local_deferredLogger) const;
|
||||
|
||||
// using the solution x to recover the solution xw for wells and applying
|
||||
// xw to update Well State
|
||||
void recoverWellSolutionAndUpdateWellState(const BVector& x,
|
||||
const int domainIdx);
|
||||
|
||||
private:
|
||||
BlackoilWellModel<TypeTag>& wellModel_;
|
||||
|
||||
void assembleWellEq(const double dt,
|
||||
const Domain& domain,
|
||||
DeferredLogger& deferred_logger);
|
||||
|
||||
// These members are used to avoid reallocation in specific functions
|
||||
// instead of using local variables.
|
||||
// Their state is not relevant between function calls, so they can
|
||||
// (and must) be mutable, as the functions using them are const.
|
||||
mutable BVector x_local_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include "BlackoilWellModelNldd_impl.hpp"
|
||||
|
||||
#endif
|
227
opm/simulators/wells/BlackoilWellModelNldd_impl.hpp
Normal file
227
opm/simulators/wells/BlackoilWellModelNldd_impl.hpp
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
Copyright 2016 - 2019 SINTEF Digital, Mathematics & Cybernetics.
|
||||
Copyright 2016 - 2018 Equinor ASA.
|
||||
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
|
||||
Copyright 2016 - 2018 Norce AS
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_BLACKOILWELLMODEL_NLDD_IMPL_HEADER_INCLUDED
|
||||
#define OPM_BLACKOILWELLMODEL_NLDD_IMPL_HEADER_INCLUDED
|
||||
|
||||
// Improve IDE experience
|
||||
#ifndef OPM_BLACKOILWELLMODEL_NLDD_HEADER_INCLUDED
|
||||
#include <config.h>
|
||||
#include <opm/simulators/wells/BlackoilWellModelNldd.hpp>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
assemble(const int /*iterationIdx*/,
|
||||
const double dt,
|
||||
const Domain& domain)
|
||||
{
|
||||
// We assume that calculateExplicitQuantities() and
|
||||
// prepareTimeStep() have been called already for the entire
|
||||
// well model, so we do not need to do it here (when
|
||||
// iterationIdx is 0).
|
||||
|
||||
DeferredLogger local_deferredLogger;
|
||||
this->updateWellControls(local_deferredLogger, domain);
|
||||
this->initPrimaryVariablesEvaluation(domain);
|
||||
this->assembleWellEq(dt, domain, local_deferredLogger);
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
assembleWellEq(const double dt,
|
||||
const Domain& domain,
|
||||
DeferredLogger& deferred_logger)
|
||||
{
|
||||
for (const auto& well : wellModel_.localNonshutWells()) {
|
||||
if (this->well_domain().at(well->name()) == domain.index) {
|
||||
well->assembleWellEq(wellModel_.simulator(),
|
||||
dt,
|
||||
wellModel_.wellState(),
|
||||
wellModel_.groupState(),
|
||||
deferred_logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
addWellPressureEquations(PressureMatrix& /*jacobian*/,
|
||||
const BVector& /*weights*/,
|
||||
const bool /*use_well_weights*/,
|
||||
const int /*domainIndex*/) const
|
||||
{
|
||||
throw std::logic_error("CPRW is not yet implemented for NLDD subdomains");
|
||||
// To fix this function, rdofs should be the size of the domain, and the nw should be the number of wells in the domain
|
||||
// int nw = this->numLocalWellsEnd(); // should number of wells in the domain
|
||||
// int rdofs = local_num_cells_; // should be the size of the domain
|
||||
// for ( int i = 0; i < nw; i++ ) {
|
||||
// int wdof = rdofs + i;
|
||||
// jacobian[wdof][wdof] = 1.0;// better scaling ?
|
||||
// }
|
||||
|
||||
// for ( const auto& well : well_container_ ) {
|
||||
// if (well_domain_.at(well->name()) == domainIndex) {
|
||||
// weights should be the size of the domain
|
||||
// well->addWellPressureEquations(jacobian, weights, pressureVarIndex, use_well_weights, this->wellState());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
recoverWellSolutionAndUpdateWellState(const BVector& x,
|
||||
const int domainIdx)
|
||||
{
|
||||
// Note: no point in trying to do a parallel gathering
|
||||
// try/catch here, as this function is not called in
|
||||
// parallel but for each individual domain of each rank.
|
||||
DeferredLogger local_deferredLogger;
|
||||
for (const auto& well : wellModel_.localNonshutWells()) {
|
||||
if (this->well_domain().at(well->name()) == domainIdx) {
|
||||
const auto& cells = well->cells();
|
||||
x_local_.resize(cells.size());
|
||||
|
||||
for (size_t i = 0; i < cells.size(); ++i) {
|
||||
x_local_[i] = x[cells[i]];
|
||||
}
|
||||
well->recoverWellSolutionAndUpdateWellState(wellModel_.simulator(),
|
||||
x_local_,
|
||||
wellModel_.wellState(),
|
||||
local_deferredLogger);
|
||||
}
|
||||
}
|
||||
// TODO: avoid losing the logging information that could
|
||||
// be stored in the local_deferredlogger in a parallel case.
|
||||
if (wellModel_.terminalOutput()) {
|
||||
local_deferredLogger.logMessages();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
initPrimaryVariablesEvaluation(const Domain& domain) const
|
||||
{
|
||||
for (auto& well : wellModel_.localNonshutWells()) {
|
||||
if (this->well_domain().at(well->name()) == domain.index) {
|
||||
well->initPrimaryVariablesEvaluation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
ConvergenceReport
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
getWellConvergence(const Domain& domain,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
DeferredLogger& local_deferredLogger) const
|
||||
{
|
||||
const int iterationIdx = wellModel_.simulator().model().newtonMethod().numIterations();
|
||||
const bool relax_tolerance = iterationIdx > wellModel_.numStrictIterations();
|
||||
|
||||
ConvergenceReport report;
|
||||
for (const auto& well : wellModel_.localNonshutWells()) {
|
||||
if ((this->well_domain().at(well->name()) == domain.index)) {
|
||||
if (well->isOperableAndSolvable() || well->wellIsStopped()) {
|
||||
report += well->getWellConvergence(wellModel_.simulator(),
|
||||
wellModel_.wellState(),
|
||||
B_avg,
|
||||
local_deferredLogger,
|
||||
relax_tolerance);
|
||||
} else {
|
||||
ConvergenceReport xreport;
|
||||
using CR = ConvergenceReport;
|
||||
xreport.setWellFailed({CR::WellFailure::Type::Unsolvable,
|
||||
CR::Severity::Normal, -1, well->name()});
|
||||
report += xreport;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log debug messages for NaN or too large residuals.
|
||||
if (wellModel_.terminalOutput()) {
|
||||
for (const auto& f : report.wellFailures()) {
|
||||
if (f.severity() == ConvergenceReport::Severity::NotANumber) {
|
||||
local_deferredLogger.debug("NaN residual found with phase " +
|
||||
std::to_string(f.phase()) +
|
||||
" for well " + f.wellName());
|
||||
} else if (f.severity() == ConvergenceReport::Severity::TooLarge) {
|
||||
local_deferredLogger.debug("Too large residual found with phase " +
|
||||
std::to_string(f.phase()) +
|
||||
" for well " + f.wellName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
updateWellControls(DeferredLogger& deferred_logger,
|
||||
const Domain& domain)
|
||||
{
|
||||
if (!wellModel_.wellsActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: decide on and implement an approach to handling of
|
||||
// group controls, network and similar for domain solves.
|
||||
|
||||
// Check only individual well constraints and communicate.
|
||||
for (const auto& well : wellModel_.localNonshutWells()) {
|
||||
if (this->well_domain().at(well->name()) == domain.index) {
|
||||
constexpr auto mode = WellInterface<TypeTag>::IndividualOrGroup::Individual;
|
||||
well->updateWellControl(wellModel_.simulator(),
|
||||
mode,
|
||||
wellModel_.wellState(),
|
||||
wellModel_.groupState(),
|
||||
deferred_logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
setupDomains(const std::vector<Domain>& domains)
|
||||
{
|
||||
std::vector<const SubDomainIndices*> genDomains;
|
||||
std::transform(domains.begin(), domains.end(),
|
||||
std::back_inserter(genDomains),
|
||||
[](const auto& domain)
|
||||
{ return static_cast<const SubDomainIndices*>(&domain); });
|
||||
this->calcDomains(genDomains);
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif
|
@ -41,6 +41,7 @@
|
||||
#include <opm/input/eclipse/Units/UnitSystem.hpp>
|
||||
|
||||
#include <opm/simulators/wells/BlackoilWellModelConstraints.hpp>
|
||||
#include <opm/simulators/wells/BlackoilWellModelNldd.hpp>
|
||||
#include <opm/simulators/wells/ParallelPAvgDynamicSourceData.hpp>
|
||||
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
|
||||
#include <opm/simulators/wells/VFPProperties.hpp>
|
||||
@ -1449,46 +1450,6 @@ namespace Opm {
|
||||
return well_group_thp_updated;
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
assembleDomain([[maybe_unused]] const int iterationIdx,
|
||||
const double dt,
|
||||
const Domain& domain)
|
||||
{
|
||||
last_report_ = SimulatorReportSingle();
|
||||
Dune::Timer perfTimer;
|
||||
perfTimer.start();
|
||||
|
||||
{
|
||||
const int episodeIdx = simulator_.episodeIndex();
|
||||
const auto& network = this->schedule()[episodeIdx].network();
|
||||
if (!this->wellsActive() && !network.active()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We assume that calculateExplicitQuantities() and
|
||||
// prepareTimeStep() have been called already for the entire
|
||||
// well model, so we do not need to do it here (when
|
||||
// iterationIdx is 0).
|
||||
|
||||
DeferredLogger local_deferredLogger;
|
||||
updateWellControlsDomain(local_deferredLogger, domain);
|
||||
initPrimaryVariablesEvaluationDomain(domain);
|
||||
assembleWellEqDomain(dt, domain, local_deferredLogger);
|
||||
|
||||
// TODO: errors here must be caught higher up, as this method is not called in parallel.
|
||||
// We will log errors on rank 0, but not other ranks for now.
|
||||
if (this->terminal_output_) {
|
||||
local_deferredLogger.logMessages();
|
||||
}
|
||||
|
||||
last_report_.converged = true;
|
||||
last_report_.assemble_time_well += perfTimer.stop();
|
||||
}
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
@ -1500,19 +1461,6 @@ namespace Opm {
|
||||
}
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
assembleWellEqDomain(const double dt, const Domain& domain, DeferredLogger& deferred_logger)
|
||||
{
|
||||
for (auto& well : well_container_) {
|
||||
if (this->well_domain_.at(well->name()) == domain.index) {
|
||||
well->assembleWellEq(simulator_, dt, this->wellState(), this->groupState(), deferred_logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
@ -1613,31 +1561,6 @@ namespace Opm {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
addWellPressureEquationsDomain([[maybe_unused]] PressureMatrix& jacobian,
|
||||
[[maybe_unused]] const BVector& weights,
|
||||
[[maybe_unused]] const bool use_well_weights,
|
||||
[[maybe_unused]] const int domainIndex) const
|
||||
{
|
||||
throw std::logic_error("CPRW is not yet implemented for NLDD subdomains");
|
||||
// To fix this function, rdofs should be the size of the domain, and the nw should be the number of wells in the domain
|
||||
// int nw = this->numLocalWellsEnd(); // should number of wells in the domain
|
||||
// int rdofs = local_num_cells_; // should be the size of the domain
|
||||
// for ( int i = 0; i < nw; i++ ) {
|
||||
// int wdof = rdofs + i;
|
||||
// jacobian[wdof][wdof] = 1.0;// better scaling ?
|
||||
// }
|
||||
|
||||
// for ( const auto& well : well_container_ ) {
|
||||
// if (well_domain_.at(well->name()) == domainIndex) {
|
||||
// weights should be the size of the domain
|
||||
// well->addWellPressureEquations(jacobian, weights, pressureVarIndex, use_well_weights, this->wellState());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
template <typename TypeTag>
|
||||
void BlackoilWellModel<TypeTag>::
|
||||
addReservoirSourceTerms(GlobalEqVector& residual,
|
||||
@ -1718,30 +1641,13 @@ namespace Opm {
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
recoverWellSolutionAndUpdateWellStateDomain(const BVector& x, const Domain& domain)
|
||||
recoverWellSolutionAndUpdateWellStateDomain(const BVector& x, const int domainIdx)
|
||||
{
|
||||
// Note: no point in trying to do a parallel gathering
|
||||
// try/catch here, as this function is not called in
|
||||
// parallel but for each individual domain of each rank.
|
||||
DeferredLogger local_deferredLogger;
|
||||
for (auto& well : well_container_) {
|
||||
if (this->well_domain_.at(well->name()) == domain.index) {
|
||||
const auto& cells = well->cells();
|
||||
x_local_.resize(cells.size());
|
||||
if (!nldd_) {
|
||||
OPM_THROW(std::logic_error, "Attempt to call NLDD method without a NLDD solver");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cells.size(); ++i) {
|
||||
x_local_[i] = x[cells[i]];
|
||||
}
|
||||
well->recoverWellSolutionAndUpdateWellState(simulator_, x_local_,
|
||||
this->wellState(),
|
||||
local_deferredLogger);
|
||||
}
|
||||
}
|
||||
// TODO: avoid losing the logging information that could
|
||||
// be stored in the local_deferredlogger in a parallel case.
|
||||
if (this->terminal_output_) {
|
||||
local_deferredLogger.logMessages();
|
||||
}
|
||||
return nldd_->recoverWellSolutionAndUpdateWellState(x, domainIdx);
|
||||
}
|
||||
|
||||
|
||||
@ -1756,68 +1662,6 @@ namespace Opm {
|
||||
}
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
initPrimaryVariablesEvaluationDomain(const Domain& domain) const
|
||||
{
|
||||
for (auto& well : well_container_) {
|
||||
if (this->well_domain_.at(well->name()) == domain.index) {
|
||||
well->initPrimaryVariablesEvaluation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
ConvergenceReport
|
||||
BlackoilWellModel<TypeTag>::
|
||||
getDomainWellConvergence(const Domain& domain,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
DeferredLogger& local_deferredLogger) const
|
||||
{
|
||||
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
||||
const bool relax_tolerance = iterationIdx > param_.strict_outer_iter_wells_;
|
||||
|
||||
ConvergenceReport report;
|
||||
for (const auto& well : well_container_) {
|
||||
if ((this->well_domain_.at(well->name()) == domain.index)) {
|
||||
if (well->isOperableAndSolvable() || well->wellIsStopped()) {
|
||||
report += well->getWellConvergence(simulator_,
|
||||
this->wellState(),
|
||||
B_avg,
|
||||
local_deferredLogger,
|
||||
relax_tolerance);
|
||||
} else {
|
||||
ConvergenceReport xreport;
|
||||
using CR = ConvergenceReport;
|
||||
xreport.setWellFailed({CR::WellFailure::Type::Unsolvable, CR::Severity::Normal, -1, well->name()});
|
||||
report += xreport;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log debug messages for NaN or too large residuals.
|
||||
if (this->terminal_output_) {
|
||||
for (const auto& f : report.wellFailures()) {
|
||||
if (f.severity() == ConvergenceReport::Severity::NotANumber) {
|
||||
local_deferredLogger.debug("NaN residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
|
||||
} else if (f.severity() == ConvergenceReport::Severity::TooLarge) {
|
||||
local_deferredLogger.debug("Too large residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
ConvergenceReport
|
||||
BlackoilWellModel<TypeTag>::
|
||||
@ -1981,29 +1825,6 @@ namespace Opm {
|
||||
return { changed_well_group, more_network_update };
|
||||
}
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
updateWellControlsDomain(DeferredLogger& deferred_logger, const Domain& domain)
|
||||
{
|
||||
if ( !this->wellsActive() ) return ;
|
||||
|
||||
// TODO: decide on and implement an approach to handling of
|
||||
// group controls, network and similar for domain solves.
|
||||
|
||||
// Check only individual well constraints and communicate.
|
||||
for (const auto& well : well_container_) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
@ -2480,87 +2301,6 @@ namespace Opm {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
setupDomains(const std::vector<Domain>& domains)
|
||||
{
|
||||
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
||||
// TODO: This loop nest may be slow for very large numbers of
|
||||
// domains and wells, but that has not been observed on tests so
|
||||
// far. Using the partition vector instead would be faster if we
|
||||
// need to change.
|
||||
for (const auto& wellPtr : this->well_container_) {
|
||||
const int first_well_cell = wellPtr->cells().front();
|
||||
for (const auto& domain : domains) {
|
||||
auto cell_present = [&domain](const auto cell)
|
||||
{
|
||||
return std::binary_search(domain.cells.begin(),
|
||||
domain.cells.end(), cell);
|
||||
};
|
||||
|
||||
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.
|
||||
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()) {
|
||||
if (! cell_present(well_cell)) {
|
||||
OPM_THROW(std::runtime_error,
|
||||
fmt::format("Well '{}' found on multiple domains.",
|
||||
wellPtr->name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::setupDomains(): well found on multiple domains.",
|
||||
simulator_.gridView().comm());
|
||||
|
||||
// Write well/domain info to the DBG file.
|
||||
const Opm::Parallel::Communication& comm = grid().comm();
|
||||
const int rank = comm.rank();
|
||||
DeferredLogger local_log;
|
||||
if (!this->well_domain_.empty()) {
|
||||
std::ostringstream os;
|
||||
os << "Well name Rank Domain\n";
|
||||
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());
|
||||
}
|
||||
auto global_log = gatherDeferredLogger(local_log, comm);
|
||||
if (this->terminal_output_) {
|
||||
global_log.logMessages();
|
||||
}
|
||||
|
||||
// Pre-calculate the local cell indices for each well
|
||||
well_local_cells_.clear();
|
||||
well_local_cells_.reserve(well_container_.size(), 10);
|
||||
std::vector<int> local_cells;
|
||||
for (const auto& well : well_container_) {
|
||||
const auto& global_cells = well->cells();
|
||||
const int domain_index = this->well_domain_.at(well->name());
|
||||
const auto& domain_cells = domains[domain_index].cells;
|
||||
local_cells.resize(global_cells.size());
|
||||
|
||||
// find the local cell index for each well cell in the domain
|
||||
// assume domain_cells is sorted
|
||||
for (size_t i = 0; i < global_cells.size(); ++i) {
|
||||
auto it = std::lower_bound(domain_cells.begin(), domain_cells.end(), global_cells[i]);
|
||||
if (it != domain_cells.end() && *it == global_cells[i]) {
|
||||
local_cells[i] = std::distance(domain_cells.begin(), it);
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, fmt::format("Cell {} not found in domain {}",
|
||||
global_cells[i], domain_index));
|
||||
}
|
||||
}
|
||||
well_local_cells_.appendRow(local_cells.begin(), local_cells.end());
|
||||
}
|
||||
}
|
||||
} // namespace Opm
|
||||
|
||||
#endif
|
||||
|
@ -128,7 +128,7 @@ public:
|
||||
|
||||
void postSolveDomain(GlobalEqVector& deltaX, const Domain& domain)
|
||||
{
|
||||
model_.recoverWellSolutionAndUpdateWellStateDomain(deltaX, domain);
|
||||
model_.recoverWellSolutionAndUpdateWellStateDomain(deltaX, domain.index);
|
||||
}
|
||||
|
||||
template <class Restarter>
|
||||
|
@ -39,6 +39,10 @@ namespace Opm {
|
||||
|
||||
#include <opm/material/fluidstates/BlackOilFluidState.hpp>
|
||||
|
||||
#include <opm/models/blackoil/blackoilproperties.hh>
|
||||
|
||||
#include <opm/simulators/linalg/linalgproperties.hh>
|
||||
|
||||
#include <opm/simulators/wells/BlackoilWellModel.hpp>
|
||||
#include <opm/simulators/wells/GasLiftGroupInfo.hpp>
|
||||
#include <opm/simulators/wells/GasLiftSingleWell.hpp>
|
||||
|
Loading…
Reference in New Issue
Block a user