91 using BVector =
typename BlackoilModel<TypeTag>::BVector;
94 using Mat =
typename BlackoilModel<TypeTag>::Mat;
96 static constexpr int numEq = Indices::numEq;
104 , wellModel_(model.wellModel())
105 , rank_(model_.simulator().vanguard().grid().comm().rank())
129 model.
wellModel().setNlddAdapter(&wellModel_);
132 std::vector<int>
sizes(num_domains, 0);
138 using EntitySeed =
typename Grid::template Codim<0>::EntitySeed;
139 std::vector<std::vector<EntitySeed>>
seeds(num_domains);
140 std::vector<std::vector<int>>
partitions(num_domains);
148 const auto& grid = model_.simulator().vanguard().grid();
150 std::vector<int> count(num_domains, 0);
151 const auto& gridView = grid.leafGridView();
155 for (
auto it =
beg; it != end; ++it, ++cell) {
157 seeds[
p][count[
p]] = it->seed();
164 for (
int index = 0; index < num_domains; ++index) {
170 Dune::SubGridPart<Grid> view{grid, std::move(
seeds[index])};
174 this->domains_.emplace_back(index,
182 const auto numCells = grid.size(0);
183 previousMobilities_.resize(numCells * FluidSystem::numActivePhases(), 0.0);
184 for (
const auto&
domain : domains_) {
189 domain_needs_solving_.resize(num_domains,
true);
192 domain_matrices_.resize(num_domains);
195 for (
int index = 0; index < num_domains; ++index) {
199 const auto& eclState = model_.simulator().vanguard().eclState();
202 loc_param.init(eclState.getSimulationConfig().useCPR());
204 if (domains_[index].cells.size() < 200) {
207 loc_param.linear_solver_print_json_definition_ =
false;
210 domain_linsolvers_.back().setDomainIndex(index);
213 assert(
int(domains_.size()) == num_domains);
215 domain_reports_accumulated_.resize(num_domains);
221 local_reports_accumulated_,
222 domain_reports_accumulated_,
224 wellModel_.numLocalWellsEnd());
231 wellModel_.setupDomains(domains_);
235 template <
class NonlinearSolverType>
243 if (
iteration < model_.param().nldd_num_initial_newton_iter_) {
244 report = model_.nonlinearIterationNewton(
iteration,
250 model_.initialLinearization(report,
iteration, model_.param().newton_min_iter_,
251 model_.param().newton_max_iter_, timer);
253 if (report.converged) {
262 auto& solution = model_.simulator().model().solution(0);
268 local_reports_accumulated_.success.pre_post_time +=
detailTimer.stop();
272 std::vector<SimulatorReportSingle>
domain_reports(domains_.size());
290 switch (model_.param().local_solve_approach_) {
291 case DomainSolveApproach::Jacobi:
296 case DomainSolveApproach::GaussSeidel:
311 logger.debug(fmt::format(
"Convergence failure in domain {} on rank {}." ,
domain.index, rank_));
329 int& num_domains =
counts[3];
336 domain_needs_solving_[i] =
false;
340 if (
dr.total_newton_iterations == 0) {
345 domain_needs_solving_[i] =
true;
348 if (
dr.skipped_domains) {
353 domain_reports_accumulated_[i] +=
dr;
355 local_reports_accumulated_ +=
dr;
360 if (model_.param().local_solve_approach_ == DomainSolveApproach::Jacobi) {
362 model_.simulator().model().invalidateAndUpdateIntensiveQuantities(0);
373 const auto& comm = model_.simulator().vanguard().grid().comm();
374 if (comm.size() > 1) {
375 const auto*
ccomm = model_.simulator().model().newtonMethod().linearSolver().comm();
378 ccomm->copyOwnerToAll(solution, solution);
381 const std::size_t
num = solution.size();
383 for (std::size_t
ii = 0;
ii <
num; ++
ii) {
387 for (std::size_t
ii = 0;
ii <
num; ++
ii) {
392 model_.simulator().model().invalidateAndUpdateIntensiveQuantitiesOverlap(0);
401 OpmLog::debug(fmt::format(
"Local solves finished. Converged for {}/{} domains. {} domains were skipped. {} domains did no work. {} total local Newton iterations.\n",
407 local_reports_accumulated_.success.pre_post_time +=
detailTimer.stop();
417 report.converged =
true;
425 return local_reports_accumulated_;
433 const auto dr_size = domain_reports_accumulated_.size();
436 domain_reports_accumulated_[i].success.num_wells = 0;
439 for (
const auto& [
wname,
domain] : wellModel_.well_domain()) {
440 domain_reports_accumulated_[
domain].success.num_wells++;
442 return domain_reports_accumulated_;
449 const auto& grid = this->model_.simulator().vanguard().grid();
450 const auto& elementMapper = this->model_.simulator().model().elementMapper();
451 const auto&
cartMapper = this->model_.simulator().vanguard().cartesianIndexMapper();
464 const auto& grid = this->model_.simulator().vanguard().grid();
465 const auto& elementMapper = this->model_.simulator().model().elementMapper();
466 const auto&
cartMapper = this->model_.simulator().vanguard().cartesianIndexMapper();
471 domain_reports_accumulated_,
480 solveDomain(
const Domain&
domain,
502 wellModel_.assemble(
modelSimulator.model().newtonMethod().numIterations(),
511 this->assembleReservoirDomain(
domain);
530 model_.wellModel().linearizeDomain(
domain,
538 const int max_iter = model_.param().max_local_solve_iterations_;
547 const int nc = grid.size(0);
551 this->solveJacobianSystemDomain(
domain, x);
552 model_.wellModel().postSolveDomain(x,
domain);
557 local_report.linear_solve_setup_time += model_.linearSolveSetupTime();
558 local_report.total_linear_iterations = model_.linearIterationsLastSolve();
563 this->updateDomainSolution(
domain, x);
574 wellModel_.assemble(
modelSimulator.model().newtonMethod().numIterations(),
582 this->assembleReservoirDomain(
domain);
597 model_.wellModel().linearizeDomain(
domain,
628 void assembleReservoirDomain(
const Domain&
domain)
632 model_.simulator().model().linearizer().linearizeDomain(
domain);
636 void solveJacobianSystemDomain(
const Domain&
domain, BVector&
global_x)
644 if (domain_matrices_[
domain.index]) {
649 auto&
jac = *domain_matrices_[
domain.index];
650 auto res = Details::extractVector(
modelSimulator.model().linearizer().residual(),
661 model_.linearSolveSetupTime() =
perfTimer.stop();
669 void updateDomainSolution(
const Domain&
domain,
const BVector&
dx)
672 auto& simulator = model_.simulator();
673 auto& newtonMethod = simulator.model().newtonMethod();
674 SolutionVector& solution = simulator.model().solution(0);
676 newtonMethod.update_(solution,
685 simulator.model().invalidateAndUpdateIntensiveQuantities(0,
domain);
689 std::pair<Scalar, Scalar> localDomainConvergenceData(
const Domain&
domain,
690 std::vector<Scalar>&
R_sum,
692 std::vector<Scalar>&
B_avg,
705 const auto& gridView =
domain.view;
706 const auto&
elemEndIt = gridView.template end<0>();
713 if (
elemIt->partitionType() != Dune::InteriorEntity) {
717 elemCtx.updatePrimaryStencil(
elem);
718 elemCtx.updatePrimaryIntensiveQuantities(0);
720 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
721 const auto& intQuants = elemCtx.intensiveQuantities(0, 0);
722 const auto& fs = intQuants.fluidState();
739 for (
int i = 0; i<
bSize; ++i )
747 ConvergenceReport getDomainReservoirConvergence(
const double reportTime,
752 std::vector<Scalar>&
B_avg,
755 using Vector = std::vector<Scalar>;
771 iteration >= model_.param().min_strict_cnv_iter_;
774 const Scalar
tol_cnv = model_.param().local_tolerance_scaling_cnv_
776 : model_.param().tolerance_cnv_);
779 const Scalar
tol_mb = model_.param().local_tolerance_scaling_mb_
780 * (
use_relaxed_mb ? model_.param().tolerance_mb_relaxed_ : model_.param().tolerance_mb_);
793 ConvergenceReport report{reportTime};
794 using CR = ConvergenceReport;
797 CR::ReservoirFailure::Type
types[2] = { CR::ReservoirFailure::Type::MassBalance,
798 CR::ReservoirFailure::Type::Cnv };
800 for (
int ii : {0, 1}) {
801 if (std::isnan(
res[
ii])) {
802 report.setReservoirFailed({
types[
ii], CR::Severity::NotANumber,
compIdx});
803 logger.debug(
"NaN residual for " + model_.compNames().name(
compIdx) +
" equation.");
804 }
else if (
res[
ii] > model_.param().max_residual_allowed_) {
805 report.setReservoirFailed({
types[
ii], CR::Severity::TooLarge,
compIdx});
806 logger.debug(
"Too large residual for " + model_.compNames().name(
compIdx) +
" equation.");
807 }
else if (
res[
ii] < 0.0) {
808 report.setReservoirFailed({
types[
ii], CR::Severity::Normal,
compIdx});
809 logger.debug(
"Negative residual for " + model_.compNames().name(
compIdx) +
" equation.");
811 report.setReservoirFailed({
types[
ii], CR::Severity::Normal,
compIdx});
823 std::string
msg = fmt::format(
"Domain {} on rank {}, size {}, containing cell {}\n| Iter",
838 std::ostringstream
ss;
840 const std::streamsize
oprec =
ss.precision(3);
841 const std::ios::fmtflags
oflags =
ss.setf(std::ios::scientific);
857 ConvergenceReport getDomainConvergence(
const Domain&
domain,
858 const SimulatorTimerInterface& timer,
864 std::vector<Scalar>
B_avg(numEq, 0.0);
865 auto report = this->getDomainReservoirConvergence(timer.simulationTimeElapsed(),
866 timer.currentStepLength(),
877 std::vector<int> getSubdomainOrder()
885 if (model_.param().local_solve_approach_ == DomainSolveApproach::Jacobi) {
888 }
else if (model_.param().local_solve_approach_ == DomainSolveApproach::GaussSeidel) {
891 switch (model_.param().local_domains_ordering_) {
892 case DomainOrderingMeasure::AveragePressure: {
894 for (
const auto&
domain : domains_) {
896 std::accumulate(
domain.cells.begin(),
domain.cells.end(), Scalar{0},
897 [&solution](
const auto acc,
const auto c)
898 { return acc + solution[c][Indices::pressureSwitchIdx]; });
904 case DomainOrderingMeasure::MaxPressure: {
906 for (
const auto&
domain : domains_) {
908 std::accumulate(
domain.cells.begin(),
domain.cells.end(), Scalar{0},
909 [&solution](
const auto acc,
const auto c)
910 { return std::max(acc, solution[c][Indices::pressureSwitchIdx]); });
914 case DomainOrderingMeasure::Residual: {
916 const auto& residual =
modelSimulator.model().linearizer().residual();
917 const int num_vars = residual[0].size();
918 for (
const auto&
domain : domains_) {
920 for (
const int c :
domain.cells) {
934 [&
m](
const int i1,
const int i2){ return m[i1] > m[i2]; });
937 throw std::logic_error(
"Domain solve approach must be Jacobi or Gauss-Seidel");
941 template<
class GlobalEqVector>
942 void solveDomainJacobi(GlobalEqVector& solution,
947 const SimulatorTimerInterface& timer,
957 model_.simulator().model().invalidateAndUpdateIntensiveQuantities(0,
domain);
961 model_.simulator().model().invalidateAndUpdateIntensiveQuantities(0,
domain);
965 template<
class GlobalEqVector>
966 void solveDomainGaussSeidel(GlobalEqVector& solution,
971 const SimulatorTimerInterface& timer,
985 for (
const auto&
rc :
convrep.reservoirConvergence()) {
986 if (
rc.type() == ConvergenceReport::ReservoirFailure::Type::MassBalance) {
988 }
else if (
rc.type() == ConvergenceReport::ReservoirFailure::Type::Cnv) {
998 logger.debug(fmt::format(
"Accepting solution in unconverged domain {} on rank {}.",
domain.index, rank_));
1001 logger.debug(
"Unconverged local solution.");
1004 logger.debug(
"Unconverged local solution with well convergence failures:");
1005 for (
const auto&
wf :
convrep.wellFailures()) {
1018 model_.simulator().model().invalidateAndUpdateIntensiveQuantities(0,
domain);
1022 Scalar computeCnvErrorPvLocal(
const Domain&
domain,
1023 const std::vector<Scalar>&
B_avg,
double dt)
const
1026 const auto& simulator = model_.simulator();
1027 const auto& model = simulator.model();
1028 const auto& problem = simulator.problem();
1029 const auto& residual = simulator.model().linearizer().residual();
1050 decltype(
auto) partitionCells()
const
1052 const auto& grid = this->model_.simulator().vanguard().grid();
1054 using GridView = std::remove_cv_t<std::remove_reference_t<
1055 decltype(grid.leafGridView())>>;
1057 using Element = std::remove_cv_t<std::remove_reference_t<
1058 typename GridView::template Codim<0>::Entity>>;
1060 const auto& param = this->model_.param();
1064 zoltan_ctrl.domain_imbalance = param.local_domains_partition_imbalance_;
1067 [elementMapper = &this->model_.simulator().model().elementMapper()]
1068 (
const Element& element)
1070 return elementMapper->index(element);
1074 [
cartMapper = &this->model_.simulator().vanguard().cartesianIndexMapper()]
1081 const auto need_wells = param.local_domains_partition_method_ ==
"zoltan";
1084 ? this->model_.simulator().vanguard().schedule().getWellsatEnd()
1085 : std::vector<Well>{};
1088 ? this->model_.simulator().vanguard().schedule().getPossibleFutureConnections()
1089 : std::unordered_map<std::string, std::set<int>> {};
1093 const int num_domains = (param.num_local_domains_ > 0)
1094 ? param.num_local_domains_
1096 return ::Opm::partitionCells(param.local_domains_partition_method_,
1097 num_domains, grid.leafGridView(), wells,
1099 param.local_domains_partition_well_neighbor_levels_);
1102 void updateMobilities(
const Domain&
domain)
1104 if (
domain.skip || model_.param().nldd_relative_mobility_change_tol_ == 0.0) {
1108 for (
const auto globalDofIdx :
domain.cells) {
1109 const auto& intQuants = model_.simulator().model().intensiveQuantities(globalDofIdx, 0);
1119 bool checkIfSubdomainNeedsSolving(
const Domain&
domain,
const int iteration)
1127 if (domain_needs_solving_[
domain.index]) {
1132 if (model_.param().nldd_relative_mobility_change_tol_ == 0.0) {
1141 return checkSubdomainChangeRelative(
domain);
1144 bool checkSubdomainChangeRelative(
const Domain&
domain)
1149 for (
const auto globalDofIdx :
domain.cells) {
1150 const auto& intQuants = model_.simulator().model().intensiveQuantities(globalDofIdx, 0);
1165 if (
relDiff > model_.param().nldd_relative_mobility_change_tol_) {
1175 std::vector<Domain> domains_;
1176 std::vector<std::unique_ptr<Mat>> domain_matrices_;
1177 std::vector<ISTLSolverType> domain_linsolvers_;
1178 SimulatorReport local_reports_accumulated_;
1180 mutable std::vector<SimulatorReport> domain_reports_accumulated_;
1183 std::vector<Scalar> previousMobilities_;
1185 std::vector<bool> domain_needs_solving_;