13 #include "libmesh/petsc_matrix.h" 14 #include "libmesh/static_condensation.h" 24 params.
addClassDescription(
"A problem that handles Schur complement preconditioning of the " 25 "incompressible Navier-Stokes equations");
27 "mass_matrix",
"",
"The matrix tag name corresponding to the mass matrix.");
31 "The matrix tag name corresponding to the diffusive part of the velocity equations.");
32 params.
addParam<std::vector<unsigned int>>(
35 "if not provided then the top field split is assumed to be the " 36 "Schur split. This is a vector to allow recursive nesting");
37 MooseEnum set_schur_pre(
"false mass a11_and_mass",
"false");
41 "Whether and what to set as the user-provided Schur complement preconditioner");
45 "Whether to use the commuted form of the LSC preconditioner, created by Olshanskii");
50 #
if PETSC_RELEASE_LESS_THAN(3, 20, 0)
52 mooseError(
"The preconditioning techniques made available through this class require a PETSc " 53 "version of at least 3.20");
62 _set_schur_pre((getParam<MooseEnum>(
"set_schur_pre").getEnum<SetSchurPreType>())),
68 paramError(
"mass_matrix",
69 "A pressure mass matrix must be provided if we are commuting the LSC commutator.");
71 paramError(
"L_matrix",
72 "A matrix corresponding to the viscous component of the momentum equation must be " 73 "provided if we are commuting the LSC commutator.");
76 paramError(
"L_matrix",
77 "If not commuting the LSC commutator, then the 'L_matrix' should not be provided " 78 "because it will not be used. For Elman LSC preconditioning, L will be computed " 79 "automatically using system matrix data (e.g. the off-diagonal blocks in the " 80 "velocity-pressure system).");
85 "If requesting to use the mass matrix as part of the Schur complement preconditioner via " 86 "'set_schur_pre', then a pressure 'mass_matrix' must be provided");
91 auto ierr = (PetscErrorCode)0;
95 CHKERRABORT(this->
comm().
get(), ierr);
99 ierr = MatDestroy(&
_L);
100 CHKERRABORT(this->
comm().
get(), ierr);
109 if (solver_sys->system().has_static_condensation())
112 mooseError(
"Static condensation and LSC preconditioning not supported together");
114 cast_ref<StaticCondensation &>(solver_sys->getMatrix(
massMatrixTagID()))
115 .uncondensed_dofs_only();
132 auto sub_ksp_index = *it;
135 LibmeshPetscCall(KSPGetPC(node, &fs_pc));
138 LibmeshPetscCall(PetscObjectTypeCompare((PetscObject)fs_pc, PCFIELDSPLIT, &is_fs));
140 mooseError(
"Not a field split. Please check the 'schur_fs_index' parameter");
144 LibmeshPetscCall(PCSetUp(fs_pc));
147 LibmeshPetscCall(PCFieldSplitGetSubKSP(fs_pc, &num_splits, &subksp));
148 next_ksp = subksp[sub_ksp_index];
152 LibmeshPetscCall(PCFieldSplitGetISByIndex(fs_pc, sub_ksp_index, &
is));
159 LibmeshPetscCall(PetscFree(subksp));
170 KSP schur_complement_ksp;
174 IS velocity_is, pressure_is;
175 PetscInt rstart, rend;
176 PetscBool is_lsc, is_fs;
177 std::vector<Mat> intermediate_Qs;
178 std::vector<Mat> intermediate_Ls;
181 LibmeshPetscCall(KSPGetPC(schur_ksp, &schur_pc));
182 LibmeshPetscCall(PetscObjectTypeCompare((PetscObject)schur_pc, PCFIELDSPLIT, &is_fs));
184 mooseError(
"Not a field split. Please check the 'schur_fs_index' parameter");
189 Mat global_Q =
nullptr;
195 cast_ref<StaticCondensation &>(sparse_mass_mat).get_condensed_mat())
198 global_Q = cast_ref<PetscMatrixBase<Number> &>(sparse_mass_mat).mat();
203 Mat global_L =
nullptr;
213 auto process_intermediate_mats = [
this](
auto & intermediate_mats,
auto parent_mat)
215 mooseAssert(parent_mat,
"This should be non-null");
220 Mat intermediate_mat;
221 LibmeshPetscCall(MatCreateSubMatrix(i == 0 ? parent_mat : intermediate_mats[i - 1],
226 intermediate_mats[i] = intermediate_mat;
228 return _index_sets.empty() ? parent_mat : intermediate_mats.back();
231 Mat our_parent_Q =
nullptr;
233 our_parent_Q = process_intermediate_mats(intermediate_Qs, global_Q);
234 Mat our_parent_L =
nullptr;
236 our_parent_L = process_intermediate_mats(intermediate_Ls, global_L);
240 LibmeshPetscCall(PCSetUp(schur_pc));
244 LibmeshPetscCall(PCFieldSplitGetISByIndex(schur_pc, 0, &velocity_is));
247 LibmeshPetscCall(MatGetOwnershipRange(our_parent_Q, &rstart, &rend));
253 mooseAssert(our_parent_L,
"This should be non-null");
258 MatCreateSubMatrix(our_parent_L, velocity_is, velocity_is, MAT_INITIAL_MATRIX, &
_L));
262 MatCreateSubMatrix(our_parent_L, velocity_is, velocity_is, MAT_REUSE_MATRIX, &
_L));
266 LibmeshPetscCall(ISComplement(velocity_is, rstart, rend, &pressure_is));
268 auto create_q_scale_submat =
269 [our_parent_Q,
this, velocity_is, pressure_is](
const auto & mat_initialization)
275 mooseAssert(our_parent_Q,
"This should be non-null");
277 LibmeshPetscCall(MatCreateSubMatrix(
278 our_parent_Q, pressure_is, pressure_is, mat_initialization, &
_Q_scale));
285 mooseAssert(our_parent_Q,
"This should be non-null");
288 LibmeshPetscCall(MatCreateSubMatrix(
289 our_parent_Q, velocity_is, velocity_is, mat_initialization, &
_Q_scale));
295 create_q_scale_submat(MAT_INITIAL_MATRIX);
301 LibmeshPetscCall(MatDestroy(&
_Q_scale));
302 create_q_scale_submat(MAT_INITIAL_MATRIX);
306 create_q_scale_submat(MAT_REUSE_MATRIX);
310 LibmeshPetscCall(ISDestroy(&pressure_is));
313 for (
auto & mat : intermediate_Qs)
314 LibmeshPetscCall(MatDestroy(&mat));
315 for (
auto & mat : intermediate_Ls)
316 LibmeshPetscCall(MatDestroy(&mat));
320 LibmeshPetscCall(PCFieldSplitGetSubKSP(schur_pc, &num_splits, &subksp));
322 mooseError(
"The number of splits should be two");
325 schur_complement_ksp = subksp[1];
329 mooseAssert(
_Q_scale,
"This should be non-null");
333 LibmeshPetscCall(KSPGetOperators(schur_complement_ksp, &
S,
nullptr));
337 MatSchurComplementGetSubMatrices(
S,
nullptr,
nullptr,
nullptr,
nullptr, &A11));
338 LibmeshPetscCall(MatAXPY(
_Q_scale, 1, A11, DIFFERENT_NONZERO_PATTERN));
341 LibmeshPetscCall(PCFieldSplitSetSchurPre(schur_pc, PC_FIELDSPLIT_SCHUR_PRE_USER,
_Q_scale));
344 LibmeshPetscCall(KSPSetOperators(schur_complement_ksp,
S,
_Q_scale));
349 LibmeshPetscCall(KSPGetPC(schur_complement_ksp, &lsc_pc));
351 LibmeshPetscCall(PetscObjectTypeCompare(PetscObject(lsc_pc), PCLSC, &is_lsc));
353 mooseError(
"Not an LSC PC. Please check the 'schur_fs_index' parameter");
356 LibmeshPetscCall(PCGetOperators(lsc_pc,
nullptr, &lsc_pc_pmat));
361 mooseAssert(
_L,
"This should be non-null");
364 LibmeshPetscCall(PetscObjectCompose((PetscObject)lsc_pc_pmat,
"LSC_L", (PetscObject)
_L));
366 mooseAssert(
_have_mass_matrix,
"This is to verify we will enter the next conditional");
370 mooseAssert(
_Q_scale,
"This should be non-null");
374 PetscObjectCompose((PetscObject)lsc_pc_pmat,
"LSC_Qscale", (PetscObject)
_Q_scale));
379 LibmeshPetscCall(PetscFree(subksp));
389 auto schur_ksp = ns_problem->findSchurKSP(root_ksp, 0);
390 ns_problem->setupLSCMatrices(schur_ksp);
404 "If we don't have a mass matrix, which is only supported for traditional LSC " 405 "preconditioning (e.g. Elman, not Olshanskii), then we also shouldn't have an L matrix " 406 "because we automatically form the L matrix when doing traditional LSC preconditioning");
virtual void initPetscOutputAndSomeSolverSettings()
const bool _have_mass_matrix
Whether the user attached a mass matrix.
const std::vector< unsigned int > & _schur_fs_index
The length of this vector should correspond to the number of split nesting levels there are in the fi...
virtual void initialSetup() override
_mass_matrix(getParam< TagName >("mass_matrix"))
Tnew cast_ref(Told &oldvar)
void clearIndexSets()
Clear the field split index sets.
std::vector< IS > _index_sets
This will end up being the same length as _schur_fs_index.
Mat _L
The Poisson operator.
const Parallel::Communicator & comm() const
_have_L_matrix(!_L_matrix.empty())
_commute_lsc(getParam< bool >("commute_lsc"))
virtual ~NavierStokesProblem()
Will destroy any matrices we allocated.
std::vector< std::shared_ptr< SolverSystem > > _solver_systems
KSP findSchurKSP(KSP node, unsigned int tree_position)
bool has_static_condensation() const
_have_mass_matrix(!_mass_matrix.empty())
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
_L_matrix(getParam< TagName >("L_matrix"))
static InputParameters validParams()
NonlinearSystemBase * _current_nl_sys
_set_schur_pre((getParam< MooseEnum >("set_schur_pre").getEnum< SetSchurPreType >()))
static const std::string S
FieldSplitPreconditionerBase & getFieldSplitPreconditioner()
NonlinearSystemBase & currentNonlinearSystem()
registerMooseObject("NavierStokesApp", NavierStokesProblem)
NavierStokesProblem(const InputParameters ¶meters)
const bool _commute_lsc
Whether to commute operators in the style of Olshanskii.
void initialSetup() override
PetscErrorCode PetscInt const PetscInt IS * is
A problem that handles Schur complement preconditioning of the incompressible Navier-Stokes equations...
virtual void initPetscOutputAndSomeSolverSettings() override
Reinitialize PETSc output for proper linear/nonlinear iteration display.
Mat _Q_scale
The mass matrix used for scaling.
enum NavierStokesProblem::SetSchurPreType _set_schur_pre
TagID LMatrixTagID() const
virtual libMesh::SparseMatrix< Number > & getMatrix(TagID tag)
void setupLSCMatrices(KSP schur_ksp)
Setup the Least Squares Commutator (LSC) preconditioner given the Schur complement KSP object...
void mooseError(Args &&... args) const
_schur_fs_index(getParam< std::vector< unsigned int >>("schur_fs_index"))
static InputParameters validParams()
PetscErrorCode navierStokesKSPPreSolve(KSP root_ksp, Vec, Vec, void *context)
TagID massMatrixTagID() const
PetscFunctionReturn(LIBMESH_PETSC_SUCCESS)
const bool _have_L_matrix
Whether the user attached a Poisson operator matrix.
auto index_range(const T &sizable)
virtual libMesh::System & system() override