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" 48 "Variable condensation preconditioner (VCP) condenses out specified variable(s) " 49 "from the Jacobian matrix and produces a system of equations with less unkowns to " 50 "be solved by the underlying preconditioners.");
52 params.
addParam<std::vector<NonlinearVariableName>>(
55 "List multiple space separated groups of comma separated variables. " 56 "Off-diagonal jacobians will be generated for all pairs within a group.");
59 "is_lm_coupling_diagonal",
61 "Set to true if you are sure the coupling matrix between Lagrange multiplier variable and " 62 "the coupled primal variable is strict diagonal. This will speedup the linear solve. " 63 "Otherwise set to false to ensure linear solve accuracy.");
65 "adaptive_condensation",
67 "By default VCP will check the Jacobian and only condense the rows with zero diagonals. Set " 68 "to false if you want to condense out all the specified variable dofs.");
69 params.
addRequiredParam<std::vector<std::string>>(
"preconditioner",
"Preconditioner type.");
72 "Name of the variable(s) that is to be condensed out. Usually " 73 "this will be the Lagrange multiplier variable(s).");
76 "Name of the variable(s) that couples with the variable(s) specified in the `variable` " 77 "block. Usually this is the primary variable that the Lagrange multiplier correspond to.");
85 _nl(_fe_problem.getNonlinearSystemBase(_nl_sys_num)),
86 _mesh(_fe_problem.
mesh()),
87 _dofmap(_nl.system().get_dof_map()),
88 _is_lm_coupling_diagonal(getParam<bool>(
"is_lm_coupling_diagonal")),
89 _adaptive_condensation(getParam<bool>(
"adaptive_condensation")),
90 _n_vars(_nl.nVariables()),
91 _lm_var_names(getParam<
std::vector<
std::string>>(
"lm_variable")),
92 _primary_var_names(getParam<
std::vector<
std::string>>(
"primary_variable")),
102 _need_condense(true),
103 _init_timer(registerTimedSection(
"init", 2)),
104 _apply_timer(registerTimedSection(
"apply", 1))
107 paramError(
"coupled_variable",
"coupled_variable should have the same size as the variable.");
110 mooseError(
"The VariableCondensationPreconditioner cannot be used with DistributedMesh");
116 paramError(
"variable ", var_name,
" does not exist in the system");
125 paramError(
"coupled_variable ", var_name,
" does not exist in the system");
131 const std::vector<std::string> & pc_type = getParam<std::vector<std::string>>(
"preconditioner");
132 if (pc_type.size() > 1)
133 mooseWarning(
"We only use one preconditioner type in VCP, the ",
135 " preconditioner is utilized.");
136 _pre_type = Utility::string_to_enum<PreconditionerType>(pc_type[0]);
141 std::unique_ptr<CouplingMatrix> cm = std::make_unique<CouplingMatrix>(
_n_vars);
142 const bool full = getParam<bool>(
"full");
151 std::vector<std::vector<unsigned int>> off_diag(
_n_vars);
154 for (
const auto i :
index_range(
getParam<std::vector<NonlinearVariableName>>(
"off_diag_row")))
156 const unsigned int row =
159 const unsigned int column =
162 (*cm)(row, column) = 1;
166 for (
const auto & coupled_group :
167 getParam<std::vector<NonlinearVariableName>>(
"coupled_groups"))
169 std::vector<NonlinearVariableName> vars;
170 MooseUtils::tokenize<NonlinearVariableName>(coupled_group, vars, 1,
",");
172 for (
unsigned int k = j + 1; k < vars.size(); ++k)
176 (*cm)(row, column) = 1;
177 (*cm)(column, row) = 1;
183 for (
unsigned int i = 0; i <
_n_vars; i++)
184 for (
unsigned int j = 0; j <
_n_vars; j++)
210 std::vector<dof_id_type> di, cp_di;
212 for (
const auto & node : *active_nodes)
225 if (cp_di.size() != di.size())
226 mooseError(
"variable and coupled variable do not have the same number of dof on node ",
253 <<
"The variable(s) provided do not have a saddle-point character at this step. VCP " 254 "will continue without condensing the dofs." 314 _cols.push_back(primary_idx);
355 MatSetOption(
_K->mat(), MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
382 MatMatMatMult(
_M->mat(),
_dinv,
_K->mat(), MAT_INITIAL_MATRIX, PETSC_DEFAULT, &MdinvK));
388 auto pc_original_mat = cast_ptr<PetscMatrix<Number> *>(
_matrix);
402 const std::vector<dof_id_type> & grows,
406 PetscInt pc_ncols = 0, block_ncols = 0;
407 const PetscInt *pc_cols, *block_cols;
408 const PetscScalar *pc_vals, *block_vals;
411 std::vector<PetscInt> sub_cols;
412 std::vector<PetscScalar> sub_vals;
416 PetscInt sub_rid[] = {
static_cast<PetscInt
>(i)};
417 PetscInt rid = grows[i];
422 MatGetRow(original_mat.
mat(), rid, &pc_ncols, &pc_cols, &pc_vals));
425 MatGetRow(block_mat.
mat(), i, &block_ncols, &block_cols, &block_vals));
429 std::map<PetscInt, PetscScalar> pc_col_map;
430 for (PetscInt pc_idx = 0; pc_idx < pc_ncols; pc_idx++)
437 for (PetscInt block_idx = 0; block_idx < block_ncols; block_idx++)
439 PetscInt block_col = block_cols[block_idx];
440 PetscScalar block_val = block_vals[block_idx];
443 if (pc_col_map.find(block_col) != pc_col_map.end())
444 pc_col_map[block_col] -= block_val;
446 pc_col_map[block_col] = -block_val;
450 for (std::map<PetscInt, PetscScalar>::iterator it = pc_col_map.begin();
451 it != pc_col_map.end();
454 sub_cols.push_back(it->first);
455 sub_vals.push_back(it->second);
460 MatSetValues(condensed_mat.
mat(),
468 MatRestoreRow(original_mat.
mat(), rid, &pc_ncols, &pc_cols, &pc_vals));
470 MatRestoreRow(block_mat.
mat(), i, &block_ncols, &block_cols, &block_vals));
476 condensed_mat.
close();
483 const std::vector<dof_id_type> & rows,
484 const std::vector<dof_id_type> & cols,
485 const std::vector<dof_id_type> & grows,
486 const std::vector<dof_id_type> & gcols,
490 PetscInt ncols = 0, block_ncols = 0;
491 const PetscInt * col_vals;
492 const PetscInt * block_col_vals;
493 const PetscScalar * vals;
494 const PetscScalar * block_vals;
496 std::vector<PetscInt> block_cols_to_org;
498 std::vector<PetscInt>
503 std::vector<dof_id_type> n_nz, n_oz;
506 for (
const auto & row_id :
_rows)
510 MatGetRow(original_mat.
mat(), row_id, &ncols, &col_vals, &vals));
518 mooseError(
"DoF ", row_id,
" does not exist in the rows of condensed_mat");
522 MatGetRow(block_mat.
mat(), block_row_id, &block_ncols, &block_col_vals, &block_vals));
525 block_cols_to_org.clear();
526 for (PetscInt i = 0; i < block_ncols; ++i)
528 auto idx = gcols[block_col_vals[i]];
529 block_cols_to_org.push_back(
idx);
534 mergeArrays(col_vals, block_cols_to_org.data(), ncols, block_ncols, merged_cols);
538 MatRestoreRow(block_mat.
mat(), block_row_id, &block_ncols, &block_col_vals, &block_vals));
541 MatRestoreRow(original_mat.
mat(), row_id, &ncols, &col_vals, &vals));
544 PetscInt row_n_nz = 0, row_n_oz = 0;
545 for (
const auto & merged_col : merged_cols)
563 n_nz.push_back(cast_int<dof_id_type>(row_n_nz));
564 n_oz.push_back(cast_int<dof_id_type>(row_n_oz));
567 condensed_mat.
init(grows.size(), gcols.size(), rows.size(), cols.size(), n_nz, n_oz);
575 std::vector<PetscInt> & c)
580 std::map<PetscInt, bool> mp;
590 for (
const auto & i : mp)
591 c.push_back(i.first);
654 MatMatMult(
_M->mat(),
_dinv, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &mdinv));
665 std::unique_ptr<NumericVector<Number>> mdinv_primary_rhs(
673 mdinv_primary_rhs->close();
675 (*_y_hat) -= (*mdinv_primary_rhs);
692 std::unique_ptr<NumericVector<Number>> K_xhat(
698 (*_primary_rhs_vec) -= (*K_xhat);
708 std::vector<dof_id_type> dof_indices;
709 std::vector<Number> vals;
715 vals.push_back((*
_x_hat)(i));
718 for (
const auto & i :
725 x.
insert(vals.data(), dof_indices);
731 std::vector<dof_id_type> & indices)
734 IS zerodiags, zerodiags_all;
735 const PetscInt * petsc_idx;
740 MatFindZeroDiagonals(petsc_mat->
mat(), &zerodiags));
743 ISAllGather(zerodiags, &zerodiags_all));
745 ISGetIndices(zerodiags_all, &petsc_idx));
748 for (PetscInt i = 0; i < nrows; ++i)
749 indices.push_back(petsc_idx[i]);
752 ISRestoreIndices(zerodiags_all, &petsc_idx));
760 if (
_dinv !=
nullptr)
767 Mat F, I, dinv_dense;
774 PETSC_COMM_WORLD,
_D->local_n(),
_D->local_m(),
_D->n(),
_D->m(), NULL, &dinv_dense));
779 MatCreateDense(PETSC_COMM_WORLD,
_D->local_m(),
_D->local_m(),
_D->m(),
_D->m(), NULL, &I));
781 for (
unsigned int i = 0; i <
_D->m(); ++i)
783 MatSetValue(I, i, i, 1.0, INSERT_VALUES));
786 MatAssemblyBegin(I, MAT_FINAL_ASSEMBLY));
791 MatGetOrdering(
_D->mat(), MATORDERINGND, &perm, &iperm));
796 MatGetFactor(
_D->mat(), MATSOLVERSUPERLU_DIST, MAT_FACTOR_LU, &F));
799 MatLUFactorSymbolic(F,
_D->mat(), perm, iperm, &
info));
802 MatLUFactorNumeric(F,
_D->mat(), &
info));
808 MatAssemblyBegin(dinv_dense, MAT_FINAL_ASSEMBLY));
810 MatAssemblyEnd(dinv_dense, MAT_FINAL_ASSEMBLY));
814 MatConvert(dinv_dense, MATAIJ, MAT_INITIAL_MATRIX, &dinv));
830 MatCreateAIJ(PETSC_COMM_WORLD,
843 for (
const auto & i :
make_range(
_D->row_start(),
_D->row_stop()))
847 diag_D->set(it->second, (*
_D)(i, it->second));
850 for (
const auto & i :
make_range(
_D->row_start(),
_D->row_stop()))
853 mooseError(
"Trying to compute reciprocal of 0.");
863 MatAssemblyBegin(dinv, MAT_FINAL_ASSEMBLY));
865 MatAssemblyEnd(dinv, MAT_FINAL_ASSEMBLY));
std::vector< dof_id_type > _global_rows
row and column indices for the condensed system
virtual numeric_index_type n() const override
virtual void insert(const T *v, const std::vector< numeric_index_type > &dof_indices)
void getDofToCondense()
Get dofs for the variable to be condensed out.
virtual void create_submatrix(SparseMatrix< T > &submatrix, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols) const
virtual numeric_index_type local_n() const final
NonlinearSystemBase & _nl
The nonlinear system this PC is associated with (convenience reference)
virtual numeric_index_type row_stop() const override
std::unordered_map< dof_id_type, dof_id_type > _rows_to_idx
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
virtual numeric_index_type local_m() const final
virtual void clear()
Release all memory and clear data structures.
void getCondensedXY(const NumericVector< Number > &y, NumericVector< Number > &x)
Get condensed x and y.
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.
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
unsigned int number() const
Get variable number coming from libMesh.
void vector_mult(NumericVector< T > &dest, const NumericVector< T > &arg) const
std::unique_ptr< PetscMatrix< Number > > _J_condensed
Condensed Jacobian.
std::unique_ptr< PetscMatrix< Number > > _K
virtual numeric_index_type m() const override
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::vector< dof_id_type > _global_lm_dofs
Vectors of DoFs: indices associated with lagrange multipliers, and its coupled primary variable globa...
std::vector< dof_id_type > _cols
const Parallel::Communicator & comm() const
const Parallel::Communicator & _communicator
std::unordered_map< dof_id_type, dof_id_type > _cols_to_idx
virtual void attachPreconditioner(libMesh::Preconditioner< Number > *preconditioner)=0
Attach a customized preconditioner that requires physics knowledge.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
VariableCondensationPreconditioner(const InputParameters ¶ms)
std::unique_ptr< PetscMatrix< Number > > _M
std::vector< dof_id_type > _rows
void computeDInverse(Mat &mat)
Compute inverse of D using LU.
virtual void apply(const NumericVector< Number > &y, NumericVector< Number > &x)
Computes the preconditioned vector "x" based on input "y".
void mooseWarning(Args &&... args) const
Emits a warning prefixed with object name and type.
std::vector< dof_id_type > _global_primary_dofs
std::vector< unsigned int > _lm_var_ids
Base class for MOOSE preconditioners.
unsigned int variable_number(std::string_view var) const
std::unique_ptr< NumericVector< Number > > _y_hat
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
virtual void create_subvector(NumericVector< T > &, const std::vector< numeric_index_type > &, bool=true) const
bool has_variable(std::string_view var) const
std::unique_ptr< Preconditioner< Number > > _preconditioner
Holds one Preconditioner object for the condensed system to solve.
virtual ~VariableCondensationPreconditioner()
dof_id_type n_dofs() const
bool _need_condense
Whether the DoFs associated the variable are to be condensed.
PerfID _init_timer
Timers.
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
void getFullSolution(const NumericVector< Number > &y, NumericVector< Number > &x)
Assemble the full solution vector.
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
static InputParameters validParams()
virtual void init()
Initialize data structures if not done so already.
virtual void setup()
This is called every time the "operator might have changed".
std::unordered_map< dof_id_type, dof_id_type > _global_cols_to_idx
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 ...
virtual void close() override
static InputParameters validParams()
virtual void create_submatrix_nosort(SparseMatrix< T > &, const std::vector< numeric_index_type > &, const std::vector< numeric_index_type > &) const
const unsigned int _n_vars
Number of variables.
std::unordered_map< dof_id_type, dof_id_type > _map_global_primary_order
std::vector< dof_id_type > _global_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.
const bool _adaptive_condensation
Whether to condense all specified variable.
std::vector< unsigned int > _primary_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::unique_ptr< NumericVector< Number > > _lm_sol_vec
void getDofColRow()
Get row and col dofs for the condensed system.
const std::vector< std::string > _primary_var_names
Name and ID of the corresponding coupled variable.
void computeDInverseDiag(Mat &mat)
Compute (approximate) inverse of D by inverting its diagonal entries.
libMesh::NodeRange * getActiveNodeRange()
void condenseSystem()
Reconstruct the equation system.
virtual bool is_replicated() const
const bool _is_lm_coupling_diagonal
Whether the coupling is diagonal.
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
virtual numeric_index_type row_start() const override
void setCouplingMatrix(std::unique_ptr< libMesh::CouplingMatrix > cm)
Setup the coupling matrix on the finite element problem.
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...
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...
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
MooseVariableFieldBase & getVariable(THREAD_ID tid, const std::string &var_name) const
Gets a reference to a variable of with specified name.
registerMooseObjectAliased("MooseApp", VariableCondensationPreconditioner, "VCP")
std::unique_ptr< NumericVector< Number > > _primary_rhs_vec
std::vector< dof_id_type > _primary_dofs
std::vector< dof_id_type > _lm_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) ...
const libMesh::DofMap & _dofmap
DofMap for easy reference.
MooseMesh & _mesh
Mesh object for easy reference.
auto index_range(const T &sizable)
Interface for condensing out LMs for the dual mortar approach.
virtual void init(const numeric_index_type m, const numeric_index_type n, const numeric_index_type m_l, const numeric_index_type n_l, const numeric_index_type n_nz=30, const numeric_index_type n_oz=10, const numeric_index_type blocksize=1) override
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...
libMesh::PreconditionerType _pre_type
Which preconditioner to use for the solve.
SparseMatrix< Number > * _matrix
std::unique_ptr< PetscMatrix< Number > > _D
Submatrices that are frequently needed while computing the condensed system _D: the submatrix that co...
std::vector< dof_id_type > _zero_rows
The row indices that correspond to the zero diagonal entries in the original Jacobian matrix This is ...
bool local_index(dof_id_type dof_index) const
void computeCondensedVariables()
Compute condensed variables (Lagrange multipliers) values using updated solution vector.
virtual libMesh::System & system() override
Get the reference to the libMesh system.