20#include "libmesh/coupling_matrix.h"
21#include "libmesh/libmesh_common.h"
22#include "libmesh/equation_systems.h"
23#include "libmesh/nonlinear_implicit_system.h"
24#include "libmesh/nonlinear_solver.h"
25#include "libmesh/linear_implicit_system.h"
26#include "libmesh/transient_system.h"
27#include "libmesh/numeric_vector.h"
28#include "libmesh/sparse_matrix.h"
29#include "libmesh/string_to_enum.h"
30#include "libmesh/mesh_base.h"
31#include "libmesh/variable.h"
32#include "libmesh/petsc_matrix.h"
33#include "libmesh/parallel_object.h"
34#include "libmesh/boundary_info.h"
46 "Variable condensation preconditioner (VCP) condenses out specified variable(s) "
47 "from the Jacobian matrix and produces a system of equations with less unkowns to "
48 "be solved by the underlying preconditioners.");
50 params.
addParam<std::vector<NonlinearVariableName>>(
53 "List multiple space separated groups of comma separated variables. "
54 "Off-diagonal jacobians will be generated for all pairs within a group.");
57 "is_lm_coupling_diagonal",
59 "Set to true if you are sure the coupling matrix between Lagrange multiplier variable and "
60 "the coupled primal variable is strict diagonal. This will speedup the linear solve. "
61 "Otherwise set to false to ensure linear solve accuracy.");
63 "adaptive_condensation",
65 "By default VCP will check the Jacobian and only condense the rows with zero diagonals. Set "
66 "to false if you want to condense out all the specified variable dofs.");
67 params.
addRequiredParam<std::vector<std::string>>(
"preconditioner",
"Preconditioner type.");
70 "Name of the variable(s) that is to be condensed out. Usually "
71 "this will be the Lagrange multiplier variable(s).");
74 "Name of the variable(s) that couples with the variable(s) specified in the `variable` "
75 "block. Usually this is the primary variable that the Lagrange multiplier correspond to.");
83 _nl(_fe_problem.getNonlinearSystemBase(_nl_sys_num)),
84 _mesh(_fe_problem.
mesh()),
85 _dofmap(_nl.system().get_dof_map()),
86 _is_lm_coupling_diagonal(getParam<bool>(
"is_lm_coupling_diagonal")),
87 _adaptive_condensation(getParam<bool>(
"adaptive_condensation")),
88 _n_vars(_nl.nVariables()),
89 _lm_var_names(getParam<
std::vector<
std::string>>(
"lm_variable")),
90 _primary_var_names(getParam<
std::vector<
std::string>>(
"primary_variable")),
100 _need_condense(true),
101 _init_timer(registerTimedSection(
"init", 2)),
102 _apply_timer(registerTimedSection(
"apply", 1))
105 paramError(
"coupled_variable",
"coupled_variable should have the same size as the variable.");
108 mooseError(
"The VariableCondensationPreconditioner cannot be used with DistributedMesh");
114 paramError(
"variable ", var_name,
" does not exist in the system");
123 paramError(
"coupled_variable ", var_name,
" does not exist in the system");
129 const std::vector<std::string> & pc_type = getParam<std::vector<std::string>>(
"preconditioner");
130 if (pc_type.size() > 1)
131 mooseWarning(
"We only use one preconditioner type in VCP, the ",
133 " preconditioner is utilized.");
134 _pre_type = Utility::string_to_enum<PreconditionerType>(pc_type[0]);
139 std::unique_ptr<CouplingMatrix> cm = std::make_unique<CouplingMatrix>(
_n_vars);
140 const bool full = getParam<bool>(
"full");
145 for (
const auto i : make_range(
_n_vars))
149 std::vector<std::vector<unsigned int>> off_diag(
_n_vars);
152 for (
const auto i : index_range(
getParam<std::vector<NonlinearVariableName>>(
"off_diag_row")))
154 const unsigned int row =
157 const unsigned int column =
160 (*cm)(row, column) = 1;
164 for (
const auto & coupled_group :
165 getParam<std::vector<NonlinearVariableName>>(
"coupled_groups"))
167 std::vector<NonlinearVariableName>
vars;
168 MooseUtils::tokenize<NonlinearVariableName>(coupled_group,
vars, 1,
",");
169 for (
unsigned int j : index_range(
vars))
170 for (
unsigned int k = j + 1; k <
vars.size(); ++k)
174 (*cm)(row, column) = 1;
175 (*cm)(column, row) = 1;
181 for (
unsigned int i = 0; i <
_n_vars; i++)
182 for (
unsigned int j = 0; j <
_n_vars; j++)
208 std::vector<dof_id_type> di, cp_di;
210 for (
const auto & node : *active_nodes)
223 if (cp_di.size() != di.size())
224 mooseError(
"variable and coupled variable do not have the same number of dof on node ",
227 for (
const auto & i : index_range(di))
251 <<
"The variable(s) provided do not have a saddle-point character at this step. VCP "
252 "will continue without condensing the dofs."
312 _cols.push_back(primary_idx);
353 MatSetOption(
_K->mat(), MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
380 MatMatMatMult(
_M->mat(),
_dinv,
_K->mat(), MAT_INITIAL_MATRIX, PETSC_DEFAULT, &MdinvK));
386 auto pc_original_mat = cast_ptr<PetscMatrix<Number> *>(
_matrix);
399 PetscMatrix<Number> & original_mat,
400 const std::vector<dof_id_type> & grows,
401 PetscMatrix<Number> & block_mat)
404 PetscInt pc_ncols = 0, block_ncols = 0;
405 const PetscInt *pc_cols, *block_cols;
406 const PetscScalar *pc_vals, *block_vals;
409 std::vector<PetscInt> sub_cols;
410 std::vector<PetscScalar> sub_vals;
412 for (
const auto & i : index_range(grows))
414 PetscInt sub_rid[] = {
static_cast<PetscInt
>(i)};
415 PetscInt rid = grows[i];
416 if (grows[i] >= original_mat.row_start() && grows[i] < original_mat.row_stop())
420 MatGetRow(original_mat.mat(), rid, &pc_ncols, &pc_cols, &pc_vals));
423 MatGetRow(block_mat.mat(), i, &block_ncols, &block_cols, &block_vals));
427 std::map<PetscInt, PetscScalar> pc_col_map;
428 for (PetscInt pc_idx = 0; pc_idx < pc_ncols; pc_idx++)
435 for (PetscInt block_idx = 0; block_idx < block_ncols; block_idx++)
437 PetscInt block_col = block_cols[block_idx];
438 PetscScalar block_val = block_vals[block_idx];
441 if (pc_col_map.find(block_col) != pc_col_map.end())
442 pc_col_map[block_col] -= block_val;
444 pc_col_map[block_col] = -block_val;
448 for (std::map<PetscInt, PetscScalar>::iterator it = pc_col_map.begin();
449 it != pc_col_map.end();
452 sub_cols.push_back(it->first);
453 sub_vals.push_back(it->second);
458 MatSetValues(condensed_mat.mat(),
466 MatRestoreRow(original_mat.mat(), rid, &pc_ncols, &pc_cols, &pc_vals));
468 MatRestoreRow(block_mat.mat(), i, &block_ncols, &block_cols, &block_vals));
474 condensed_mat.close();
479 PetscMatrix<Number> & condensed_mat,
480 PetscMatrix<Number> & original_mat,
481 const std::vector<dof_id_type> & rows,
482 const std::vector<dof_id_type> & cols,
483 const std::vector<dof_id_type> & grows,
484 const std::vector<dof_id_type> & gcols,
485 PetscMatrix<Number> & block_mat)
488 PetscInt ncols = 0, block_ncols = 0;
489 const PetscInt * col_vals;
490 const PetscInt * block_col_vals;
491 const PetscScalar * vals;
492 const PetscScalar * block_vals;
494 std::vector<PetscInt> block_cols_to_org;
496 std::vector<PetscInt>
501 std::vector<dof_id_type> n_nz, n_oz;
504 for (
const auto & row_id :
_rows)
508 MatGetRow(original_mat.mat(), row_id, &ncols, &col_vals, &vals));
511 dof_id_type block_row_id;
516 mooseError(
"DoF ", row_id,
" does not exist in the rows of condensed_mat");
520 MatGetRow(block_mat.mat(), block_row_id, &block_ncols, &block_col_vals, &block_vals));
523 block_cols_to_org.clear();
524 for (PetscInt i = 0; i < block_ncols; ++i)
526 auto idx = gcols[block_col_vals[i]];
527 block_cols_to_org.push_back(idx);
532 mergeArrays(col_vals, block_cols_to_org.data(), ncols, block_ncols, merged_cols);
536 MatRestoreRow(block_mat.mat(), block_row_id, &block_ncols, &block_col_vals, &block_vals));
539 MatRestoreRow(original_mat.mat(), row_id, &ncols, &col_vals, &vals));
542 PetscInt row_n_nz = 0, row_n_oz = 0;
543 for (
const auto & merged_col : merged_cols)
552 dof_id_type row_idx = grows[col_idx];
561 n_nz.push_back(cast_int<dof_id_type>(row_n_nz));
562 n_oz.push_back(cast_int<dof_id_type>(row_n_oz));
565 condensed_mat.init(grows.size(), gcols.size(), rows.size(), cols.size(), n_nz, n_oz);
573 std::vector<PetscInt> & c)
578 std::map<PetscInt, bool> mp;
581 for (
const auto & i : make_range(na))
584 for (
const auto & i : make_range(nb))
588 for (
const auto & i : mp)
589 c.push_back(i.first);
625 NumericVector<Number> & x)
647 NumericVector<Number> & x)
652 MatMatMult(
_M->mat(),
_dinv, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &mdinv));
663 std::unique_ptr<NumericVector<Number>> mdinv_primary_rhs(
665 mdinv_primary_rhs->init(MDinv.m(), MDinv.local_m(),
false, PARALLEL);
671 mdinv_primary_rhs->close();
673 (*_y_hat) -= (*mdinv_primary_rhs);
690 std::unique_ptr<NumericVector<Number>> K_xhat(
692 K_xhat->init(
_K->m(),
_K->local_m(),
false, PARALLEL);
696 (*_primary_rhs_vec) -= (*K_xhat);
704 NumericVector<Number> & x)
706 std::vector<dof_id_type> dof_indices;
707 std::vector<Number> vals;
710 for (
const auto & i : make_range(
_x_hat->first_local_index(),
_x_hat->last_local_index()))
713 vals.push_back((*
_x_hat)(i));
716 for (
const auto & i :
723 x.insert(vals.data(), dof_indices);
729 std::vector<dof_id_type> & indices)
732 IS zerodiags, zerodiags_all;
733 const PetscInt * petsc_idx;
736 auto *
const petsc_mat = cast_ptr<PetscMatrix<Number> *>(&mat);
738 MatFindZeroDiagonals(petsc_mat->mat(), &zerodiags));
741 ISAllGather(zerodiags, &zerodiags_all));
743 ISGetIndices(zerodiags_all, &petsc_idx));
746 for (PetscInt i = 0; i < nrows; ++i)
747 indices.push_back(petsc_idx[i]);
750 ISRestoreIndices(zerodiags_all, &petsc_idx));
758 if (
_dinv !=
nullptr)
765 Mat F, I, dinv_dense;
772 PETSC_COMM_WORLD,
_D->local_n(),
_D->local_m(),
_D->n(),
_D->m(), NULL, &dinv_dense));
777 MatCreateDense(PETSC_COMM_WORLD,
_D->local_m(),
_D->local_m(),
_D->m(),
_D->m(), NULL, &I));
779 for (
unsigned int i = 0; i <
_D->m(); ++i)
781 MatSetValue(I, i, i, 1.0, INSERT_VALUES));
784 MatAssemblyBegin(I, MAT_FINAL_ASSEMBLY));
789 MatGetOrdering(
_D->mat(), MATORDERINGND, &perm, &iperm));
794 MatGetFactor(
_D->mat(), MATSOLVERSUPERLU_DIST, MAT_FACTOR_LU, &F));
797 MatLUFactorSymbolic(F,
_D->mat(), perm, iperm, &info));
800 MatLUFactorNumeric(F,
_D->mat(), &info));
806 MatAssemblyBegin(dinv_dense, MAT_FINAL_ASSEMBLY));
808 MatAssemblyEnd(dinv_dense, MAT_FINAL_ASSEMBLY));
812 MatConvert(dinv_dense, MATAIJ, MAT_INITIAL_MATRIX, &dinv));
828 MatCreateAIJ(PETSC_COMM_WORLD,
839 diag_D->init(
_D->m(),
_D->local_m(),
false, PARALLEL);
841 for (
const auto & i : make_range(
_D->row_start(),
_D->row_stop()))
845 diag_D->set(it->second, (*
_D)(i, it->second));
848 for (
const auto & i : make_range(
_D->row_start(),
_D->row_stop()))
850 if (MooseUtils::absoluteFuzzyEqual((*diag_D)(i), 0.0))
851 mooseError(
"Trying to compute reciprocal of 0.");
861 MatAssemblyBegin(dinv, MAT_FINAL_ASSEMBLY));
863 MatAssemblyEnd(dinv, MAT_FINAL_ASSEMBLY));
registerMooseObjectAliased("MooseApp", VariableCondensationPreconditioner, "VCP")
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
libMesh::NodeRange * getActiveNodeRange()
Base class for MOOSE preconditioners.
void setCouplingMatrix(std::unique_ptr< libMesh::CouplingMatrix > cm)
Setup the coupling matrix on the finite element problem.
static InputParameters validParams()
unsigned int number() const
Get variable number coming from libMesh.
virtual void attachPreconditioner(libMesh::Preconditioner< Number > *preconditioner)=0
Attach a customized preconditioner that requires physics knowledge.
virtual libMesh::System & system() override
Get the reference to the libMesh system.
void mooseWarning(Args &&... args) const
MooseVariableFieldBase & getVariable(THREAD_ID tid, const std::string &var_name) const
Gets a reference to a variable of with specified name.
Interface for condensing out LMs for the dual mortar approach.
std::vector< unsigned int > _primary_var_ids
std::unordered_map< dof_id_type, dof_id_type > _map_global_lm_primary
Maps to keep track of the dof orders for keeping nonzero diagonal entries of the condensed system _ma...
virtual void init()
Initialize data structures if not done so already.
std::unordered_map< dof_id_type, dof_id_type > _map_global_primary_order
void condenseSystem()
Reconstruct the equation system.
std::unique_ptr< NumericVector< Number > > _x_hat
_x_hat, _y_hat: condensed solution and RHS vectors _primary_rhs_vec: part of the RHS vector that corr...
virtual void setup()
This is called every time the "operator might have changed".
std::vector< dof_id_type > _global_cols
const bool _is_lm_coupling_diagonal
Whether the coupling is diagonal.
std::vector< dof_id_type > _primary_dofs
const std::vector< std::string > _lm_var_names
Name and ID of the variables that are to be condensed out (usually the Lagrange multiplier variable)
void computeDInverseDiag(Mat &mat)
Compute (approximate) inverse of D by inverting its diagonal entries.
virtual void apply(const NumericVector< Number > &y, NumericVector< Number > &x)
Computes the preconditioned vector "x" based on input "y".
std::unique_ptr< NumericVector< Number > > _lm_sol_vec
void getFullSolution(const NumericVector< Number > &y, NumericVector< Number > &x)
Assemble the full solution vector.
const bool _adaptive_condensation
Whether to condense all specified variable.
PerfID _init_timer
Timers.
static InputParameters validParams()
std::vector< dof_id_type > _lm_dofs
std::unique_ptr< PetscMatrix< Number > > _D
Submatrices that are frequently needed while computing the condensed system _D: the submatrix that co...
std::unique_ptr< Preconditioner< Number > > _preconditioner
Holds one Preconditioner object for the condensed system to solve.
libMesh::PreconditionerType _pre_type
Which preconditioner to use for the solve.
std::vector< dof_id_type > _global_lm_dofs
Vectors of DoFs: indices associated with lagrange multipliers, and its coupled primary variable globa...
void preallocateCondensedJacobian(PetscMatrix< Number > &condensed_mat, PetscMatrix< Number > &original_mat, const std::vector< dof_id_type > &rows, const std::vector< dof_id_type > &cols, const std::vector< dof_id_type > &grows, const std::vector< dof_id_type > &gcols, PetscMatrix< Number > &block_mat)
Preallocate memory for the condensed Jacobian matrix.
bool _need_condense
Whether the DoFs associated the variable are to be condensed.
std::unique_ptr< NumericVector< Number > > _primary_rhs_vec
std::unique_ptr< PetscMatrix< Number > > _M
const libMesh::DofMap & _dofmap
DofMap for easy reference.
MooseMesh & _mesh
Mesh object for easy reference.
std::unordered_map< dof_id_type, dof_id_type > _rows_to_idx
virtual void clear()
Release all memory and clear data structures.
std::vector< unsigned int > _lm_var_ids
void computeCondensedJacobian(PetscMatrix< Number > &condensed_mat, PetscMatrix< Number > &original_mat, const std::vector< dof_id_type > &grows, PetscMatrix< Number > &block_mat)
The condensed Jacobian matrix is computed in this function.
std::unordered_map< dof_id_type, dof_id_type > _global_cols_to_idx
VariableCondensationPreconditioner(const InputParameters ¶ms)
void mergeArrays(const PetscInt *a, const PetscInt *b, const PetscInt &na, const PetscInt &nb, std::vector< PetscInt > &c)
Find the common part of arrays a and b and save it in c.
std::unique_ptr< PetscMatrix< Number > > _J_condensed
Condensed Jacobian.
std::vector< dof_id_type > _global_rows
row and column indices for the condensed system
virtual ~VariableCondensationPreconditioner()
void computeDInverse(Mat &mat)
Compute inverse of D using LU.
std::unordered_map< dof_id_type, dof_id_type > _global_rows_to_idx
Maps to keep track of row and col indices from the original Jacobian matrix to the condensed Jacobian...
void computeCondensedVariables()
Compute condensed variables (Lagrange multipliers) values using updated solution vector.
void getDofToCondense()
Get dofs for the variable to be condensed out.
std::unique_ptr< NumericVector< Number > > _y_hat
std::vector< dof_id_type > _rows
std::unordered_map< dof_id_type, dof_id_type > _cols_to_idx
void getCondensedXY(const NumericVector< Number > &y, NumericVector< Number > &x)
Get condensed x and y.
const std::vector< std::string > _primary_var_names
Name and ID of the corresponding coupled variable.
void getDofColRow()
Get row and col dofs for the condensed system.
std::vector< dof_id_type > _cols
void findZeroDiagonals(SparseMatrix< Number > &mat, std::vector< dof_id_type > &indices)
Check if the original jacobian has zero diagonal entries and save the row indices.
NonlinearSystemBase & _nl
The nonlinear system this PC is associated with (convenience reference)
std::unique_ptr< PetscMatrix< Number > > _K
std::vector< dof_id_type > _global_primary_dofs
const unsigned int _n_vars
Number of variables.
std::vector< dof_id_type > _zero_rows
The row indices that correspond to the zero diagonal entries in the original Jacobian matrix This is ...
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
bool local_index(dof_id_type dof_index) const
dof_id_type n_dofs(const unsigned int vn) const
const Parallel::Communicator & _communicator
const Parallel::Communicator & comm() const
SparseMatrix< T > * _matrix
virtual void create_submatrix_nosort(SparseMatrix< T > &, const std::vector< numeric_index_type > &, const std::vector< numeric_index_type > &) const
virtual void create_submatrix(SparseMatrix< T > &submatrix, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols) const
unsigned int variable_number(std::string_view var) const
bool has_variable(std::string_view var) const