21#include <opm/simulators/linalg/PreconditionerFactory.hpp>
23#include <opm/common/ErrorMacros.hpp>
25#include <opm/simulators/linalg/matrixblock.hh>
27#include <opm/simulators/linalg/FlexibleSolver.hpp>
28#include <opm/simulators/linalg/ilufirstelement.hh>
29#include <opm/simulators/linalg/OwningBlockPreconditioner.hpp>
30#include <opm/simulators/linalg/OwningTwoLevelPreconditioner.hpp>
31#include <opm/simulators/linalg/ParallelOverlappingILU0.hpp>
32#include <opm/simulators/linalg/PressureBhpTransferPolicy.hpp>
33#include <opm/simulators/linalg/PressureTransferPolicy.hpp>
34#include <opm/simulators/linalg/PropertyTree.hpp>
35#include <opm/simulators/linalg/WellOperators.hpp>
37#include <dune/istl/owneroverlapcopy.hh>
38#include <dune/istl/preconditioners.hh>
39#include <dune/istl/paamg/amg.hh>
40#include <dune/istl/paamg/kamg.hh>
41#include <dune/istl/paamg/fastamg.hh>
45template<
class Smoother>
50 using SmootherArgs =
typename Dune::Amg::SmootherTraits<Smoother>::Arguments;
51 SmootherArgs smootherArgs;
52 smootherArgs.iterations = prm.get<
int>(
"iterations", 1);
56 smootherArgs.relaxationFactor = prm.get<
double>(
"relaxation", 1.0);
61template<
class M,
class V,
class C>
67 using SmootherArgs =
typename Dune::Amg::SmootherTraits<Smoother>::Arguments;
68 SmootherArgs smootherArgs;
69 smootherArgs.iterations = prm.get<
int>(
"iterations", 1);
70 const int iluwitdh = prm.get<
int>(
"iluwidth", 0);
71 smootherArgs.setN(iluwitdh);
72 const MILU_VARIANT milu = convertString2Milu(prm.get<std::string>(
"milutype", std::string(
"ilu")));
73 smootherArgs.setMilu(milu);
77 smootherArgs.relaxationFactor = prm.get<
double>(
"relaxation", 1.0);
82template <
class Operator,
class Comm,
class Matrix,
class Vector>
83typename AMGHelper<Operator, Comm, Matrix, Vector>::Criterion
86 Criterion criterion(15, prm.get<
int>(
"coarsenTarget", 1200));
87 criterion.setDefaultValuesIsotropic(2);
88 criterion.setAlpha(prm.get<
double>(
"alpha", 0.33));
89 criterion.setBeta(prm.get<
double>(
"beta", 1e-5));
90 criterion.setMaxLevel(prm.get<
int>(
"maxlevel", 15));
91 criterion.setSkipIsolated(prm.get<
bool>(
"skip_isolated",
false));
92 criterion.setNoPreSmoothSteps(prm.get<
int>(
"pre_smooth", 1));
93 criterion.setNoPostSmoothSteps(prm.get<
int>(
"post_smooth", 1));
94 criterion.setDebugLevel(prm.get<
int>(
"verbosity", 0));
98 criterion.setAccumulate(
static_cast<Dune::Amg::AccumulationMode
>(prm.get<
int>(
"accumulate", 1)));
99 criterion.setProlongationDampingFactor(prm.get<
double>(
"prolongationdamping", 1.6));
100 criterion.setMaxDistance(prm.get<
int>(
"maxdistance", 2));
101 criterion.setMaxConnectivity(prm.get<
int>(
"maxconnectivity", 15));
102 criterion.setMaxAggregateSize(prm.get<
int>(
"maxaggsize", 6));
103 criterion.setMinAggregateSize(prm.get<
int>(
"minaggsize", 4));
107template <
class Operator,
class Comm,
class Matrix,
class Vector>
108template <
class Smoother>
109typename AMGHelper<Operator, Comm, Matrix, Vector>::PrecPtr
110AMGHelper<Operator,Comm,Matrix,Vector>::
111makeAmgPreconditioner(
const Operator& op,
112 const PropertyTree& prm,
115 auto crit = criterion(prm);
116 auto sargs = AMGSmootherArgsHelper<Smoother>::args(prm);
119 return std::make_shared<Type>(op, crit, sargs,
120 prm.get<
size_t>(
"max_krylov", 1),
121 prm.get<
double>(
"min_reduction", 1e-1));
124 return std::make_shared<Type>(op, crit, sargs);
128template<
class Operator,
class Comm>
133 using namespace Dune;
137 using M =
typename F::Matrix;
138 using V =
typename F::Vector;
140 F::addCreator(
"ILU0", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t,
const C& comm) {
141 return createParILU(op, prm, comm, 0);
143 F::addCreator(
"ParOverILU0", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t,
const C& comm) {
144 return createParILU(op, prm, comm, prm.get<
int>(
"ilulevel", 0));
146 F::addCreator(
"ILUn", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t,
const C& comm) {
147 return createParILU(op, prm, comm, prm.get<
int>(
"ilulevel", 0));
149 F::addCreator(
"Jac", [](
const O& op,
const P& prm,
const std::function<V()>&,
150 std::size_t,
const C& comm) {
151 const int n = prm.get<
int>(
"repeats", 1);
152 const double w = prm.get<
double>(
"relaxation", 1.0);
153 return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqJac<M, V, V>>>(comm, op.getmat(), n, w);
155 F::addCreator(
"GS", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t,
const C& comm) {
156 const int n = prm.get<
int>(
"repeats", 1);
157 const double w = prm.get<
double>(
"relaxation", 1.0);
158 return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqGS<M, V, V>>>(comm, op.getmat(), n, w);
160 F::addCreator(
"SOR", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t,
const C& comm) {
161 const int n = prm.get<
int>(
"repeats", 1);
162 const double w = prm.get<
double>(
"relaxation", 1.0);
163 return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqSOR<M, V, V>>>(comm, op.getmat(), n, w);
165 F::addCreator(
"SSOR", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t,
const C& comm) {
166 const int n = prm.get<
int>(
"repeats", 1);
167 const double w = prm.get<
double>(
"relaxation", 1.0);
168 return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqSSOR<M, V, V>>>(comm, op.getmat(), n, w);
175 if constexpr (std::is_same_v<O, Dune::OverlappingSchwarzOperator<M, V, V, C>>) {
176 F::addCreator(
"amg", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t,
const C& comm) {
177 const std::string smoother = prm.get<std::string>(
"smoother",
"ParOverILU0");
178 if (smoother ==
"ILU0" || smoother ==
"ParOverILU0") {
182 return std::make_shared<Dune::Amg::AMGCPR<O, V, Smoother, C>>(op, crit, sargs, comm);
184 OPM_THROW(std::invalid_argument,
"Properties: No smoother with name " << smoother <<
".");
189 F::addCreator(
"cpr", [](
const O& op,
const P& prm,
const std::function<V()> weightsCalculator, std::size_t pressureIndex,
const C& comm) {
190 assert(weightsCalculator);
191 if (pressureIndex == std::numeric_limits<std::size_t>::max())
193 OPM_THROW(std::logic_error,
"Pressure index out of bounds. It needs to specified for CPR");
196 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy, Comm>>(op, prm, weightsCalculator, pressureIndex, comm);
198 F::addCreator(
"cprt", [](
const O& op,
const P& prm,
const std::function<V()> weightsCalculator, std::size_t pressureIndex,
const C& comm) {
199 assert(weightsCalculator);
200 if (pressureIndex == std::numeric_limits<std::size_t>::max())
202 OPM_THROW(std::logic_error,
"Pressure index out of bounds. It needs to specified for CPR");
205 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy, Comm>>(op, prm, weightsCalculator, pressureIndex, comm);
208 if constexpr (std::is_same_v<O, WellModelGhostLastMatrixAdapter<M, V, V, true>>) {
209 F::addCreator(
"cprw",
210 [](
const O& op,
const P& prm,
const std::function<V()> weightsCalculator, std::size_t pressureIndex,
const C& comm) {
211 assert(weightsCalculator);
212 if (pressureIndex == std::numeric_limits<std::size_t>::max()) {
213 OPM_THROW(std::logic_error,
"Pressure index out of bounds. It needs to specified for CPR");
216 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy, Comm>>(op, prm, weightsCalculator, pressureIndex, comm);
223 createParILU(
const Operator& op,
const PropertyTree& prm,
const Comm& comm,
const int ilulevel)
226 using M =
typename F::Matrix;
227 using V =
typename F::Vector;
229 const double w = prm.get<
double>(
"relaxation", 1.0);
230 const bool redblack = prm.get<
bool>(
"redblack",
false);
231 const bool reorder_spheres = prm.get<
bool>(
"reorder_spheres",
false);
235 return std::make_shared<Opm::ParallelOverlappingILU0<M, V, V, Comm>>(
238 return std::make_shared<Opm::ParallelOverlappingILU0<M, V, V, Comm>>(
249 size_t interior_count = 0;
250 size_t highest_interior_index = 0;
251 const auto& is = comm.indexSet();
252 for (
const auto& ind : is) {
253 if (Comm::OwnerSet::contains(ind.local().attribute())) {
255 highest_interior_index = std::max(highest_interior_index, ind.local().local());
258 if (highest_interior_index + 1 == interior_count) {
259 return interior_count;
267template<
class Operator>
272 using namespace Dune;
274 using C = Dune::Amg::SequentialInformation;
276 using M =
typename F::Matrix;
277 using V =
typename F::Vector;
279 F::addCreator(
"ILU0", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
280 const double w = prm.get<
double>(
"relaxation", 1.0);
281 return std::make_shared<Opm::ParallelOverlappingILU0<M, V, V, C>>(
284 F::addCreator(
"ParOverILU0", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
285 const double w = prm.get<
double>(
"relaxation", 1.0);
286 const int n = prm.get<
int>(
"ilulevel", 0);
287 return std::make_shared<Opm::ParallelOverlappingILU0<M, V, V, C>>(
290 F::addCreator(
"ILUn", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
291 const int n = prm.get<
int>(
"ilulevel", 0);
292 const double w = prm.get<
double>(
"relaxation", 1.0);
293 return std::make_shared<Opm::ParallelOverlappingILU0<M, V, V, C>>(
296 F::addCreator(
"Jac", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
297 const int n = prm.get<
int>(
"repeats", 1);
298 const double w = prm.get<
double>(
"relaxation", 1.0);
299 return wrapPreconditioner<SeqJac<M, V, V>>(op.getmat(), n, w);
301 F::addCreator(
"GS", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
302 const int n = prm.get<
int>(
"repeats", 1);
303 const double w = prm.get<
double>(
"relaxation", 1.0);
304 return wrapPreconditioner<SeqGS<M, V, V>>(op.getmat(), n, w);
306 F::addCreator(
"SOR", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
307 const int n = prm.get<
int>(
"repeats", 1);
308 const double w = prm.get<
double>(
"relaxation", 1.0);
309 return wrapPreconditioner<SeqSOR<M, V, V>>(op.getmat(), n, w);
311 F::addCreator(
"SSOR", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
312 const int n = prm.get<
int>(
"repeats", 1);
313 const double w = prm.get<
double>(
"relaxation", 1.0);
314 return wrapPreconditioner<SeqSSOR<M, V, V>>(op.getmat(), n, w);
319 if constexpr (std::is_same_v<O, Dune::MatrixAdapter<M, V, V>>) {
320 F::addCreator(
"amg", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
321 const std::string smoother = prm.get<std::string>(
"smoother",
"ParOverILU0");
322 if (smoother ==
"ILU0" || smoother ==
"ParOverILU0") {
323 #if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 7)
324 using Smoother = SeqILU<M, V, V>;
326 using Smoother = SeqILU0<M, V, V>;
329 }
else if (smoother ==
"Jac") {
330 using Smoother = SeqJac<M, V, V>;
332 }
else if (smoother ==
"SOR") {
333 using Smoother = SeqSOR<M, V, V>;
335 }
else if (smoother ==
"SSOR") {
336 using Smoother = SeqSSOR<M, V, V>;
338 }
else if (smoother ==
"ILUn") {
339 #if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 7)
340 using Smoother = SeqILU<M, V, V>;
342 using Smoother = SeqILUn<M, V, V>;
346 OPM_THROW(std::invalid_argument,
"Properties: No smoother with name " << smoother <<
".");
349 F::addCreator(
"kamg", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
350 const std::string smoother = prm.get<std::string>(
"smoother",
"ParOverILU0");
351 if (smoother ==
"ILU0" || smoother ==
"ParOverILU0") {
352 #if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 7)
353 using Smoother = SeqILU<M, V, V>;
355 using Smoother = SeqILU0<M, V, V>;
358 }
else if (smoother ==
"Jac") {
359 using Smoother = SeqJac<M, V, V>;
361 }
else if (smoother ==
"SOR") {
362 using Smoother = SeqSOR<M, V, V>;
367 }
else if (smoother ==
"SSOR") {
368 using Smoother = SeqSSOR<M, V, V>;
370 }
else if (smoother ==
"ILUn") {
371 #if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 7)
372 using Smoother = SeqILU<M, V, V>;
374 using Smoother = SeqILUn<M, V, V>;
378 OPM_THROW(std::invalid_argument,
"Properties: No smoother with name " << smoother <<
".");
381 F::addCreator(
"famg", [](
const O& op,
const P& prm,
const std::function<V()>&, std::size_t) {
383 Dune::Amg::Parameters parms;
384 parms.setNoPreSmoothSteps(1);
385 parms.setNoPostSmoothSteps(1);
386 return wrapPreconditioner<Dune::Amg::FastAMG<O, V>>(op, crit, parms);
389 if constexpr (std::is_same_v<O, WellModelMatrixAdapter<M, V, V, false>>) {
390 F::addCreator(
"cprw", [](
const O& op,
const P& prm,
const std::function<V()>& weightsCalculator, std::size_t pressureIndex) {
391 if (pressureIndex == std::numeric_limits<std::size_t>::max()) {
392 OPM_THROW(std::logic_error,
"Pressure index out of bounds. It needs to specified for CPR");
395 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy>>(op, prm, weightsCalculator, pressureIndex);
399 F::addCreator(
"cpr", [](
const O& op,
const P& prm,
const std::function<V()>& weightsCalculator, std::size_t pressureIndex) {
400 if (pressureIndex == std::numeric_limits<std::size_t>::max())
402 OPM_THROW(std::logic_error,
"Pressure index out of bounds. It needs to specified for CPR");
405 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy>>(op, prm, weightsCalculator, pressureIndex);
407 F::addCreator(
"cprt", [](
const O& op,
const P& prm,
const std::function<V()>& weightsCalculator, std::size_t pressureIndex) {
408 if (pressureIndex == std::numeric_limits<std::size_t>::max())
410 OPM_THROW(std::logic_error,
"Pressure index out of bounds. It needs to specified for CPR");
413 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy>>(op, prm, weightsCalculator, pressureIndex);
418template <
class Operator,
class Comm>
424template <
class Operator,
class Comm>
425PreconditionerFactory<Operator,Comm>&
426PreconditionerFactory<Operator,Comm>::instance()
428 static PreconditionerFactory singleton;
432template <
class Operator,
class Comm>
434PreconditionerFactory<Operator,Comm>::
435doCreate(
const Operator& op,
const PropertyTree& prm,
436 const std::function<Vector()> weightsCalculator,
437 std::size_t pressureIndex)
440 StandardPreconditioners<Operator,Comm>::add();
443 const std::string& type = prm.get<std::string>(
"type",
"ParOverILU0");
444 auto it = creators_.find(type);
445 if (it == creators_.end()) {
446 std::ostringstream msg;
447 msg <<
"Preconditioner type " << type <<
" is not registered in the factory. Available types are: ";
448 for (
const auto& prec : creators_) {
449 msg << prec.first <<
' ';
452 OPM_THROW(std::invalid_argument, msg.str());
454 return it->second(op, prm, weightsCalculator, pressureIndex);
457template <
class Operator,
class Comm>
459PreconditionerFactory<Operator,Comm>::
460doCreate(
const Operator& op,
const PropertyTree& prm,
461 const std::function<Vector()> weightsCalculator,
462 std::size_t pressureIndex,
const Comm& comm)
465 StandardPreconditioners<Operator,Comm>::add();
468 const std::string& type = prm.get<std::string>(
"type",
"ParOverILU0");
469 auto it = parallel_creators_.find(type);
470 if (it == parallel_creators_.end()) {
471 std::ostringstream msg;
472 msg <<
"Parallel preconditioner type " << type
473 <<
" is not registered in the factory. Available types are: ";
474 for (
const auto& prec : parallel_creators_) {
475 msg << prec.first <<
' ';
478 OPM_THROW(std::invalid_argument, msg.str());
480 return it->second(op, prm, weightsCalculator, pressureIndex, comm);
483template <
class Operator,
class Comm>
484void PreconditionerFactory<Operator,Comm>::
485doAddCreator(
const std::string& type, Creator c)
490template <
class Operator,
class Comm>
491void PreconditionerFactory<Operator,Comm>::
492doAddCreator(
const std::string& type, ParCreator c)
494 parallel_creators_[type] = c;
497template <
class Operator,
class Comm>
501 const std::function<Vector()>& weightsCalculator,
502 std::size_t pressureIndex)
504 return instance().doCreate(op, prm, weightsCalculator, pressureIndex);
507template <
class Operator,
class Comm>
511 const std::function<Vector()>& weightsCalculator,
const Comm& comm,
512 std::size_t pressureIndex)
514 return instance().doCreate(op, prm, weightsCalculator, pressureIndex, comm);
518template <
class Operator,
class Comm>
522 std::size_t pressureIndex)
524 return instance().doCreate(op, prm, std::function<Vector()>(), pressureIndex, comm);
527template <
class Operator,
class Comm>
531 instance().doAddCreator(type, creator);
534template <
class Operator,
class Comm>
536addCreator(
const std::string& type, ParCreator creator)
538 instance().doAddCreator(type, creator);
541using CommSeq = Dune::Amg::SequentialInformation;
544using OpFSeq = Dune::MatrixAdapter<Dune::BCRSMatrix<Dune::FieldMatrix<double,Dim,Dim>>,
545 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
546 Dune::BlockVector<Dune::FieldVector<double,Dim>>>;
548using OpBSeq = Dune::MatrixAdapter<Dune::BCRSMatrix<Opm::MatrixBlock<double,Dim,Dim>>,
549 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
550 Dune::BlockVector<Dune::FieldVector<double,Dim>>>;
552template<
int Dim,
bool overlap>
554 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
555 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
558template<
int Dim,
bool overlap>
560 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
561 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
565using CommPar = Dune::OwnerOverlapCopyCommunication<int,int>;
568using OpFPar = Dune::OverlappingSchwarzOperator<Dune::BCRSMatrix<Dune::FieldMatrix<double,Dim,Dim>>,
569 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
570 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
574using OpBPar = Dune::OverlappingSchwarzOperator<Dune::BCRSMatrix<MatrixBlock<double,Dim,Dim>>,
575 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
576 Dune::BlockVector<Dune::FieldVector<double,Dim>>,
579#define INSTANCE_PF_PAR(Dim) \
580 template class PreconditionerFactory<OpBSeq<Dim>,CommPar>; \
581 template class PreconditionerFactory<OpFPar<Dim>,CommPar>; \
582 template class PreconditionerFactory<OpBPar<Dim>,CommPar>; \
583 template class PreconditionerFactory<OpW<Dim,false>,CommPar>; \
584 template class PreconditionerFactory<OpWG<Dim,true>,CommPar>; \
585 template class PreconditionerFactory<OpBPar<Dim>,CommSeq>;
588#define INSTANCE_PF_SEQ(Dim) \
589 template class PreconditionerFactory<OpFSeq<Dim>,CommSeq>; \
590 template class PreconditionerFactory<OpBSeq<Dim>,CommSeq>; \
591 template class PreconditionerFactory<OpW<Dim,false>,CommSeq>; \
592 template class PreconditionerFactory<OpWG<Dim,true>,CommSeq>;
595#define INSTANCE_PF(Dim) \
596 INSTANCE_PF_PAR(Dim) \
599#define INSTANCE_PF(Dim) \
Parallel algebraic multigrid based on agglomeration.
Definition: amgcpr.hh:87
Definition: PreconditionerWithUpdate.hpp:40
A two-step version of an overlapping Schwarz preconditioner using one step ILU0 as.
Definition: ParallelOverlappingILU0.hpp:145
This is an object factory for creating preconditioners.
Definition: PreconditionerFactory.hpp:69
static PrecPtr create(const Operator &op, const PropertyTree &prm, const std::function< Vector()> &weightsCalculator={}, std::size_t pressureIndex=std::numeric_limits< std::size_t >::max())
Create a new serial preconditioner and return a pointer to it.
Definition: PreconditionerFactory_impl.hpp:500
std::function< PrecPtr(const Operator &, const PropertyTree &, const std::function< Vector()> &, std::size_t)> Creator
The type of creator functions passed to addCreator().
Definition: PreconditionerFactory.hpp:80
static void addCreator(const std::string &type, Creator creator)
Add a creator for a serial preconditioner to the PreconditionerFactory.
Definition: PreconditionerFactory_impl.hpp:529
std::shared_ptr< Dune::PreconditionerWithUpdate< Vector, Vector > > PrecPtr
The type of pointer returned by create().
Definition: PreconditionerFactory.hpp:76
Definition: PressureBhpTransferPolicy.hpp:86
Definition: PressureTransferPolicy.hpp:52
Definition: PropertyTree.hpp:37
Adapter to combine a matrix and another linear operator into a combined linear operator.
Definition: WellOperators.hpp:209
Adapter to combine a matrix and another linear operator into a combined linear operator.
Definition: WellOperators.hpp:123
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: BlackoilPhases.hpp:27
MILU_VARIANT
Definition: MILU.hpp:34
@ ILU
Do not perform modified ILU.
Definition: PreconditionerFactory.hpp:48
Definition: PreconditionerFactory_impl.hpp:47
Definition: PreconditionerFactory_impl.hpp:130
static size_t interiorIfGhostLast(const Comm &comm)
Helper method to determine if the local partitioning has the K interior cells from [0,...
Definition: PreconditionerFactory_impl.hpp:247