https://mooseframework.inl.gov
Loading...
Searching...
No Matches
VariableCondensationPreconditioner.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
11
12// MOOSE includes
13#include "FEProblem.h"
14#include "MooseUtils.h"
15#include "MooseVariableFE.h"
16#include "NonlinearSystem.h"
18#include "MooseEnum.h"
19
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"
35
36#include <petscmat.h>
37
39
42{
44
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.");
49
50 params.addParam<std::vector<NonlinearVariableName>>(
51 "coupled_groups",
52 {},
53 "List multiple space separated groups of comma separated variables. "
54 "Off-diagonal jacobians will be generated for all pairs within a group.");
55
56 params.addParam<bool>(
57 "is_lm_coupling_diagonal",
58 false,
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.");
62 params.addParam<bool>(
63 "adaptive_condensation",
64 true,
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.");
68 params.addRequiredParam<std::vector<std::string>>(
69 "lm_variable",
70 "Name of the variable(s) that is to be condensed out. Usually "
71 "this will be the Lagrange multiplier variable(s).");
72 params.addRequiredParam<std::vector<std::string>>(
73 "primary_variable",
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.");
76 return params;
77}
78
80 const InputParameters & params)
81 : MoosePreconditioner(params),
82 Preconditioner<Number>(MoosePreconditioner::_communicator),
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")),
91 _D(std::make_unique<PetscMatrix<Number>>(MoosePreconditioner::_communicator)),
92 _M(std::make_unique<PetscMatrix<Number>>(MoosePreconditioner::_communicator)),
93 _K(std::make_unique<PetscMatrix<Number>>(MoosePreconditioner::_communicator)),
94 _dinv(nullptr),
95 _J_condensed(std::make_unique<PetscMatrix<Number>>(MoosePreconditioner::_communicator)),
96 _x_hat(NumericVector<Number>::build(MoosePreconditioner::_communicator)),
97 _y_hat(NumericVector<Number>::build(MoosePreconditioner::_communicator)),
98 _primary_rhs_vec(NumericVector<Number>::build(MoosePreconditioner::_communicator)),
99 _lm_sol_vec(NumericVector<Number>::build(MoosePreconditioner::_communicator)),
100 _need_condense(true),
101 _init_timer(registerTimedSection("init", 2)),
102 _apply_timer(registerTimedSection("apply", 1))
103{
104 if (_lm_var_names.size() != _primary_var_names.size())
105 paramError("coupled_variable", "coupled_variable should have the same size as the variable.");
106
107 if (!_mesh.getMesh().is_replicated())
108 mooseError("The VariableCondensationPreconditioner cannot be used with DistributedMesh");
109
110 // get variable ids from the variable names
111 for (const auto & var_name : _lm_var_names)
112 {
113 if (!_nl.system().has_variable(var_name))
114 paramError("variable ", var_name, " does not exist in the system");
115 const unsigned int id = _nl.system().variable_number(var_name);
116 _lm_var_ids.push_back(id);
117 }
118
119 // get coupled variable ids from the coupled variable names
120 for (const auto & var_name : _primary_var_names)
121 {
122 if (!_nl.system().has_variable(var_name))
123 paramError("coupled_variable ", var_name, " does not exist in the system");
124 const unsigned int id = _nl.system().variable_number(var_name);
125 _primary_var_ids.push_back(id);
126 }
127
128 // PC type
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 ",
132 pc_type[0],
133 " preconditioner is utilized.");
134 _pre_type = Utility::string_to_enum<PreconditionerType>(pc_type[0]);
135
136 // The following obtains and sets the coupling matrix.
137 // TODO: This part can be refactored together with what are in other classes, e.g.,
138 // PhysicsBasedPreconditioner
139 std::unique_ptr<CouplingMatrix> cm = std::make_unique<CouplingMatrix>(_n_vars);
140 const bool full = getParam<bool>("full");
141
142 if (!full)
143 {
144 // put 1s on diagonal
145 for (const auto i : make_range(_n_vars))
146 (*cm)(i, i) = 1;
147
148 // off-diagonal entries from the off_diag_row and off_diag_column parameters
149 std::vector<std::vector<unsigned int>> off_diag(_n_vars);
150 if (isParamValid("off_diag_row") && isParamValid("off_diag_column"))
151
152 for (const auto i : index_range(getParam<std::vector<NonlinearVariableName>>("off_diag_row")))
153 {
154 const unsigned int row =
155 _nl.getVariable(0, getParam<std::vector<NonlinearVariableName>>("off_diag_row")[i])
156 .number();
157 const unsigned int column =
158 _nl.getVariable(0, getParam<std::vector<NonlinearVariableName>>("off_diag_column")[i])
159 .number();
160 (*cm)(row, column) = 1;
161 }
162
163 // off-diagonal entries from the coupled_groups parameters
164 for (const auto & coupled_group :
165 getParam<std::vector<NonlinearVariableName>>("coupled_groups"))
166 {
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)
171 {
172 const unsigned int row = _nl.getVariable(0, vars[j]).number();
173 const unsigned int column = _nl.getVariable(0, vars[k]).number();
174 (*cm)(row, column) = 1;
175 (*cm)(column, row) = 1;
176 }
177 }
178 }
179 else
180 {
181 for (unsigned int i = 0; i < _n_vars; i++)
182 for (unsigned int j = 0; j < _n_vars; j++)
183 (*cm)(i, j) = 1;
184 }
185
186 setCouplingMatrix(std::move(cm));
187
189}
190
192
193void
195{
196 // clean the containers if we want to update the dofs
197 _global_lm_dofs.clear();
198 _lm_dofs.clear();
199 _global_primary_dofs.clear();
200 _primary_dofs.clear();
203
204 // TODO: this might not work for distributed mesh and needs to be improved
205 NodeRange * active_nodes = _mesh.getActiveNodeRange();
206
207 // loop through the variable ids
208 std::vector<dof_id_type> di, cp_di;
209 for (const auto & vn : index_range(_lm_var_ids))
210 for (const auto & node : *active_nodes)
211 {
212 di.clear();
213 cp_di.clear();
214 const auto var_id = _lm_var_ids[vn];
215 // get coupled variable id
216 const auto cp_var_id = _primary_var_ids[vn];
217 // get var and cp_var dofs associated with this node
218 _dofmap.dof_indices(node, di, var_id);
219 // skip when di is empty
220 if (di.empty())
221 continue;
222 _dofmap.dof_indices(node, cp_di, cp_var_id);
223 if (cp_di.size() != di.size())
224 mooseError("variable and coupled variable do not have the same number of dof on node ",
225 node->id(),
226 ".");
227 for (const auto & i : index_range(di))
228 {
229 // when we have adaptive condensation, skip when di does not contain any indices in
230 // _zero_rows
231 if (std::find(_zero_rows.begin(), _zero_rows.end(), di[i]) == _zero_rows.end() &&
233 break;
234 _global_lm_dofs.push_back(di[i]);
235 if (_dofmap.local_index(di[i]))
236 _lm_dofs.push_back(di[i]);
237
238 // save the corresponding coupled dof indices
239 _global_primary_dofs.push_back(cp_di[i]);
240 if (_dofmap.local_index(cp_di[i]))
241 _primary_dofs.push_back(cp_di[i]);
242 _map_global_lm_primary.insert(std::make_pair(di[i], cp_di[i]));
243 }
244 }
245
246 // check if we endup with none dof to condense
247 if (_global_lm_dofs.empty())
248 {
249 _need_condense = false;
250 _console << std::endl
251 << "The variable(s) provided do not have a saddle-point character at this step. VCP "
252 "will continue without condensing the dofs."
253 << std::endl
254 << std::endl;
255 }
256 else
257 _need_condense = true;
258
259 std::sort(_global_lm_dofs.begin(), _global_lm_dofs.end());
260 std::sort(_lm_dofs.begin(), _lm_dofs.end());
261
262 std::sort(_global_primary_dofs.begin(), _global_primary_dofs.end());
263 std::sort(_primary_dofs.begin(), _primary_dofs.end());
264
265 for (const auto & i : index_range(_global_lm_dofs))
266 {
267 auto it = _map_global_lm_primary.find(_global_lm_dofs[i]);
268 mooseAssert(it != _map_global_lm_primary.end(), "Index does not exist in the map.");
269 _map_global_primary_order.insert(std::make_pair(it->second, i));
270 }
271}
272
273void
275{
276 // clean the containers if we want to update the dofs
277 _global_rows.clear();
278 _rows.clear();
279 _global_cols.clear();
280 _cols.clear();
281 _global_rows_to_idx.clear();
282 _rows_to_idx.clear();
283 _global_cols_to_idx.clear();
284 _cols_to_idx.clear();
285 // row: all without primary variable dofs
286 // col: all without lm variable dofs
287 for (dof_id_type i = 0; i < _dofmap.n_dofs(); ++i)
288 {
289 if (std::find(_global_primary_dofs.begin(), _global_primary_dofs.end(), i) !=
291 continue;
292
293 _global_rows.push_back(i);
294 _global_rows_to_idx.insert(std::make_pair(i, _global_rows.size() - 1));
295 if (_dofmap.local_index(i))
296 {
297 _rows.push_back(i);
298 _rows_to_idx.insert(std::make_pair(i, _global_rows_to_idx[i]));
299 }
300
301 // ensure the lm and primary correspondance, so that the condensed Jacobian has non-zero
302 // diagonal if the dof corresponds to the lm variable, then find the corresponding primary
303 // variable dof and add to _global_cols
305 {
306 auto primary_idx = _map_global_lm_primary[i];
307 _global_cols.push_back(primary_idx);
308 _global_cols_to_idx.insert(std::make_pair(primary_idx, _global_cols.size() - 1));
309
310 if (_dofmap.local_index(primary_idx))
311 {
312 _cols.push_back(primary_idx);
313 _cols_to_idx.insert(std::make_pair(primary_idx, _global_cols_to_idx[primary_idx]));
314 }
315 }
316 else // if the dof does not correspond to the lm nor primary variable, just add to _global_cols
317 {
318 _global_cols.push_back(i);
319 _global_cols_to_idx.insert(std::make_pair(i, _global_cols.size() - 1));
320
321 if (_dofmap.local_index(i))
322 {
323 _cols.push_back(i);
324 _cols_to_idx.insert(std::make_pair(i, _global_cols_to_idx[i]));
325 }
326 }
327 }
328}
329
330void
332{
333 TIME_SECTION(_init_timer);
334
335 if (!_preconditioner)
337 Preconditioner<Number>::build_preconditioner(MoosePreconditioner::_communicator);
338
339 _is_initialized = true;
340}
341
342void
344{
345 // extract _M from the original matrix
347
348 // get the row associated with the coupled primary variable
349 _K->init(_global_primary_dofs.size(), _global_cols.size(), _primary_dofs.size(), _cols.size());
350 // Note: enabling nonzero allocation may be expensive. Improved memeory pre-allocation will be
351 // investigated in the future
352 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
353 MatSetOption(_K->mat(), MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
354 // here the _global_cols may not be sorted
356
358
359 // clean dinv
360 if (_dinv)
361 {
362 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&_dinv));
363 _dinv = nullptr;
364 }
365
366 // Compute inverse of D
368 // when _D is strictly diagonal, we only need to compute the reciprocal number of the diagonal
369 // entries
371 else
372 // for general cases when _D is not necessarily strict diagonal, we compute the inverse of _D
373 // using LU
375
376 Mat MdinvK;
377 // calculate MdinvK
378 LibmeshPetscCallA(
379 this->MoosePreconditioner::comm().get(),
380 MatMatMatMult(_M->mat(), _dinv, _K->mat(), MAT_INITIAL_MATRIX, PETSC_DEFAULT, &MdinvK));
381 PetscMatrix<Number> MDinv_K(MdinvK, MoosePreconditioner::_communicator);
382
383 // Preallocate memory for _J_condensed
384 // memory info is obtained from _matrix and MDinv_K
385 // indices are from _global_rows, _global_cols
386 auto pc_original_mat = cast_ptr<PetscMatrix<Number> *>(_matrix);
388 *_J_condensed, *pc_original_mat, _rows, _cols, _global_rows, _global_cols, MDinv_K);
389
390 // Extract unchanged parts from _matrix and add changed parts (MDinv_K) to _J_condensed
391 computeCondensedJacobian(*_J_condensed, *pc_original_mat, _global_rows, MDinv_K);
392
393 // Destroy MdinvK here otherwise we will have memory leak
394 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&MdinvK));
395}
396
397void
399 PetscMatrix<Number> & original_mat,
400 const std::vector<dof_id_type> & grows,
401 PetscMatrix<Number> & block_mat)
402{
403 // obtain entries from the original matrix
404 PetscInt pc_ncols = 0, block_ncols = 0;
405 const PetscInt *pc_cols, *block_cols;
406 const PetscScalar *pc_vals, *block_vals;
407
408 // containers for the data
409 std::vector<PetscInt> sub_cols;
410 std::vector<PetscScalar> sub_vals;
411
412 for (const auto & i : index_range(grows))
413 {
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())
417 {
418 // get one row of data from the original matrix
419 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
420 MatGetRow(original_mat.mat(), rid, &pc_ncols, &pc_cols, &pc_vals));
421 // get corresponding row of data from the block matrix
422 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
423 MatGetRow(block_mat.mat(), i, &block_ncols, &block_cols, &block_vals));
424 // extract data from certain cols, subtract the value from the block mat, and save the indices
425 // and entries sub_cols and sub_vals
426 // First, save the submatrix col index and value as a map
427 std::map<PetscInt, PetscScalar> pc_col_map;
428 for (PetscInt pc_idx = 0; pc_idx < pc_ncols; pc_idx++)
429 {
430 // save only if the col exists in the condensed matrix
431 if (_global_cols_to_idx.find(pc_cols[pc_idx]) != _global_cols_to_idx.end())
432 pc_col_map.insert(std::make_pair(_global_cols_to_idx[pc_cols[pc_idx]], pc_vals[pc_idx]));
433 }
434 // Second, check the block cols and calculate new entries for the condensed system
435 for (PetscInt block_idx = 0; block_idx < block_ncols; block_idx++)
436 {
437 PetscInt block_col = block_cols[block_idx];
438 PetscScalar block_val = block_vals[block_idx];
439 // if the block mat has nonzero at the same column, subtract value
440 // otherwise, create a new key and save the negative value from the block matrix
441 if (pc_col_map.find(block_col) != pc_col_map.end())
442 pc_col_map[block_col] -= block_val;
443 else
444 pc_col_map[block_col] = -block_val;
445 }
446
447 // Third, save keys in the sub_cols and values in the sub_vals
448 for (std::map<PetscInt, PetscScalar>::iterator it = pc_col_map.begin();
449 it != pc_col_map.end();
450 ++it)
451 {
452 sub_cols.push_back(it->first);
453 sub_vals.push_back(it->second);
454 }
455
456 // Then, set values
457 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
458 MatSetValues(condensed_mat.mat(),
459 1,
460 sub_rid,
461 sub_vals.size(),
462 sub_cols.data(),
463 sub_vals.data(),
464 INSERT_VALUES));
465 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
466 MatRestoreRow(original_mat.mat(), rid, &pc_ncols, &pc_cols, &pc_vals));
467 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
468 MatRestoreRow(block_mat.mat(), i, &block_ncols, &block_cols, &block_vals));
469 // clear data for this row
470 sub_cols.clear();
471 sub_vals.clear();
472 }
473 }
474 condensed_mat.close();
475}
476
477void
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)
486{
487 // quantities from the original matrix and the block matrix
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;
493
494 std::vector<PetscInt> block_cols_to_org; // stores the nonzero column indices of the block
495 // matrix w.r.t original matrix
496 std::vector<PetscInt>
497 merged_cols; // stores the nonzero column indices estimate of the condensed matrix
498
499 // number of nonzeros in each row of the DIAGONAL and OFF-DIAGONAL portion of the local
500 // condensed matrix
501 std::vector<dof_id_type> n_nz, n_oz;
502
503 // Get number of nonzeros from original_mat and block_mat for each row
504 for (const auto & row_id : _rows)
505 {
506 // get number of non-zeros in the original matrix
507 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
508 MatGetRow(original_mat.mat(), row_id, &ncols, &col_vals, &vals));
509
510 // get number of non-zeros in the block matrix
511 dof_id_type block_row_id; // row id in the block matrix
512
513 if (_global_rows_to_idx.find(row_id) != _global_rows_to_idx.end())
514 block_row_id = _global_rows_to_idx[row_id];
515 else
516 mooseError("DoF ", row_id, " does not exist in the rows of condensed_mat");
517
518 LibmeshPetscCallA(
519 this->MoosePreconditioner::comm().get(),
520 MatGetRow(block_mat.mat(), block_row_id, &block_ncols, &block_col_vals, &block_vals));
521
522 // make sure the block index is transformed in terms of the original mat
523 block_cols_to_org.clear();
524 for (PetscInt i = 0; i < block_ncols; ++i)
525 {
526 auto idx = gcols[block_col_vals[i]];
527 block_cols_to_org.push_back(idx);
528 }
529
530 // Now store nonzero column indices for the condensed Jacobian
531 // merge `col_vals` and `block_cols_to_org` and save the common indices in `merged_cols`.
532 mergeArrays(col_vals, block_cols_to_org.data(), ncols, block_ncols, merged_cols);
533
534 LibmeshPetscCallA(
535 this->MoosePreconditioner::comm().get(),
536 MatRestoreRow(block_mat.mat(), block_row_id, &block_ncols, &block_col_vals, &block_vals));
537
538 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
539 MatRestoreRow(original_mat.mat(), row_id, &ncols, &col_vals, &vals));
540
541 // Count the nnz for DIAGONAL and OFF-DIAGONAL parts
542 PetscInt row_n_nz = 0, row_n_oz = 0;
543 for (const auto & merged_col : merged_cols)
544 {
545 // find corresponding index in the block mat and skip the cols that do not exist in the
546 // condensed system
547 if (_global_cols_to_idx.find(merged_col) == _global_cols_to_idx.end())
548 continue;
549
550 dof_id_type col_idx = _global_cols_to_idx[merged_col];
551 // find the corresponding row index
552 dof_id_type row_idx = grows[col_idx];
553 // check whether the index is local;
554 // yes - DIAGONAL, no - OFF-DIAGONAL
555 if (_rows_to_idx.find(row_idx) != _rows_to_idx.end())
556 row_n_nz++;
557 else
558 row_n_oz++;
559 }
560
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));
563 }
564 // Then initialize and allocate memory for the condensed system matrix
565 condensed_mat.init(grows.size(), gcols.size(), rows.size(), cols.size(), n_nz, n_oz);
566}
567
568void
570 const PetscInt * b,
571 const PetscInt & na,
572 const PetscInt & nb,
573 std::vector<PetscInt> & c)
574{
575 c.clear();
576
577 // use map to store unique elements.
578 std::map<PetscInt, bool> mp;
579
580 // Inserting values to a map.
581 for (const auto & i : make_range(na))
582 mp[a[i]] = true;
583
584 for (const auto & i : make_range(nb))
585 mp[b[i]] = true;
586
587 // Save the merged values to c, if only the value also exist in gcols
588 for (const auto & i : mp)
589 c.push_back(i.first);
590}
591
592void
594{
597
598 // save dofs that are to be condensed out
600
601 // solve the condensed system only when needed, otherwise solve the original system
602 if (_need_condense)
603 {
604 // get condensed dofs for rows and cols
605 getDofColRow();
606
608
609 // make sure diagonal entries are not empty
610 for (const auto & i : make_range(_J_condensed->row_start(), _J_condensed->row_stop()))
611 _J_condensed->add(i, i, 0.0);
612 _J_condensed->close();
613
614 _preconditioner->set_matrix(*_J_condensed);
615 }
616 else
617 _preconditioner->set_matrix(*_matrix);
618
619 _preconditioner->set_type(_pre_type);
620 _preconditioner->init();
621}
622
623void
624VariableCondensationPreconditioner::apply(const NumericVector<Number> & y,
625 NumericVector<Number> & x)
626{
627 TIME_SECTION(_apply_timer);
628
629 if (_need_condense)
630 {
631 getCondensedXY(y, x);
632
633 _preconditioner->apply(*_y_hat, *_x_hat);
634
636
637 getFullSolution(y, x);
638 }
639 else
640 {
641 _preconditioner->apply(y, x);
642 }
643}
644
645void
647 NumericVector<Number> & x)
648{
649 Mat mdinv;
650 // calculate mdinv
651 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
652 MatMatMult(_M->mat(), _dinv, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &mdinv));
653 PetscMatrix<Number> MDinv(mdinv, MoosePreconditioner::_communicator);
654
655 _x_hat->init(_J_condensed->n(), _J_condensed->local_n(), false, PARALLEL);
656 _y_hat->init(_J_condensed->m(), _J_condensed->local_m(), false, PARALLEL);
657
658 x.create_subvector(*_x_hat, _global_cols);
659 y.create_subvector(*_y_hat, _global_rows);
660
661 _primary_rhs_vec->init(MDinv.n(), MDinv.local_n(), false, PARALLEL);
662
663 std::unique_ptr<NumericVector<Number>> mdinv_primary_rhs(
664 NumericVector<Number>::build(MoosePreconditioner::_communicator));
665 mdinv_primary_rhs->init(MDinv.m(), MDinv.local_m(), false, PARALLEL);
666
667 // get _primary_rhs_vec from the original y
668 y.create_subvector(*_primary_rhs_vec, _global_primary_dofs);
669
670 MDinv.vector_mult(*mdinv_primary_rhs, *_primary_rhs_vec);
671 mdinv_primary_rhs->close();
672
673 (*_y_hat) -= (*mdinv_primary_rhs);
674
675 _y_hat->close();
676 _x_hat->close();
677
678 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&mdinv));
679}
680
681void
683{
684 _lm_sol_vec->clear();
685
686 PetscMatrix<Number> Dinv(_dinv, MoosePreconditioner::_communicator);
687
688 _lm_sol_vec->init(_D->m(), _D->local_m(), false, PARALLEL);
689
690 std::unique_ptr<NumericVector<Number>> K_xhat(
691 NumericVector<Number>::build(MoosePreconditioner::_communicator));
692 K_xhat->init(_K->m(), _K->local_m(), false, PARALLEL);
693 _K->vector_mult(*K_xhat, *_x_hat);
694 K_xhat->close();
695
696 (*_primary_rhs_vec) -= (*K_xhat);
697 _primary_rhs_vec->close();
698 Dinv.vector_mult(*_lm_sol_vec, *_primary_rhs_vec);
699 _lm_sol_vec->close();
700}
701
702void
703VariableCondensationPreconditioner::getFullSolution(const NumericVector<Number> & /*y*/,
704 NumericVector<Number> & x)
705{
706 std::vector<dof_id_type> dof_indices;
707 std::vector<Number> vals;
708
709 // save values and indices from _x_hat and _lm_sol_vec
710 for (const auto & i : make_range(_x_hat->first_local_index(), _x_hat->last_local_index()))
711 {
712 dof_indices.push_back(_global_cols[i]);
713 vals.push_back((*_x_hat)(i));
714 }
715
716 for (const auto & i :
717 make_range(_lm_sol_vec->first_local_index(), _lm_sol_vec->last_local_index()))
718 {
719 dof_indices.push_back(_global_lm_dofs[i]);
720 vals.push_back((*_lm_sol_vec)(i));
721 }
722
723 x.insert(vals.data(), dof_indices);
724 x.close();
725}
726
727void
729 std::vector<dof_id_type> & indices)
730{
731 indices.clear();
732 IS zerodiags, zerodiags_all;
733 const PetscInt * petsc_idx;
734 PetscInt nrows;
735 // make sure we have a PETSc matrix
736 auto * const petsc_mat = cast_ptr<PetscMatrix<Number> *>(&mat);
737 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
738 MatFindZeroDiagonals(petsc_mat->mat(), &zerodiags));
739 // synchronize all indices
740 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
741 ISAllGather(zerodiags, &zerodiags_all));
742 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
743 ISGetIndices(zerodiags_all, &petsc_idx));
744 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), ISGetSize(zerodiags_all, &nrows));
745
746 for (PetscInt i = 0; i < nrows; ++i)
747 indices.push_back(petsc_idx[i]);
748
749 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
750 ISRestoreIndices(zerodiags_all, &petsc_idx));
751 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), ISDestroy(&zerodiags));
752 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), ISDestroy(&zerodiags_all));
753}
754
755void
757{
758 if (_dinv != nullptr)
759 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&_dinv));
760}
761
762void
764{
765 Mat F, I, dinv_dense;
766 IS perm, iperm;
767 MatFactorInfo info;
768
769 LibmeshPetscCallA(
770 this->MoosePreconditioner::comm().get(),
771 MatCreateDense(
772 PETSC_COMM_WORLD, _D->local_n(), _D->local_m(), _D->n(), _D->m(), NULL, &dinv_dense));
773
774 // Create an identity matrix as the right-hand-side
775 LibmeshPetscCallA(
776 this->MoosePreconditioner::comm().get(),
777 MatCreateDense(PETSC_COMM_WORLD, _D->local_m(), _D->local_m(), _D->m(), _D->m(), NULL, &I));
778
779 for (unsigned int i = 0; i < _D->m(); ++i)
780 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
781 MatSetValue(I, i, i, 1.0, INSERT_VALUES));
782
783 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
784 MatAssemblyBegin(I, MAT_FINAL_ASSEMBLY));
785 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatAssemblyEnd(I, MAT_FINAL_ASSEMBLY));
786
787 // Factorize D
788 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
789 MatGetOrdering(_D->mat(), MATORDERINGND, &perm, &iperm));
790
791 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatFactorInfoInitialize(&info));
792
793 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
794 MatGetFactor(_D->mat(), MATSOLVERSUPERLU_DIST, MAT_FACTOR_LU, &F));
795
796 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
797 MatLUFactorSymbolic(F, _D->mat(), perm, iperm, &info));
798
799 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
800 MatLUFactorNumeric(F, _D->mat(), &info));
801
802 // Solve for Dinv
803 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatMatSolve(F, I, dinv_dense));
804
805 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
806 MatAssemblyBegin(dinv_dense, MAT_FINAL_ASSEMBLY));
807 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
808 MatAssemblyEnd(dinv_dense, MAT_FINAL_ASSEMBLY));
809
810 // copy value to dinv
811 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
812 MatConvert(dinv_dense, MATAIJ, MAT_INITIAL_MATRIX, &dinv));
813
814 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&dinv_dense));
815
816 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&I));
817 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&F));
818 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), ISDestroy(&perm));
819 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), ISDestroy(&iperm));
820}
821
822void
824{
825 auto diag_D = NumericVector<Number>::build(MoosePreconditioner::_communicator);
826 // Initialize dinv
827 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
828 MatCreateAIJ(PETSC_COMM_WORLD,
829 _D->local_n(),
830 _D->local_m(),
831 _D->n(),
832 _D->m(),
833 1,
834 NULL,
835 0,
836 NULL,
837 &dinv));
838 // Allocate storage
839 diag_D->init(_D->m(), _D->local_m(), false, PARALLEL);
840 // Fill entries
841 for (const auto & i : make_range(_D->row_start(), _D->row_stop()))
842 {
844 mooseAssert(it != _map_global_primary_order.end(), "Index does not exist in the map.");
845 diag_D->set(it->second, (*_D)(i, it->second));
846 }
847
848 for (const auto & i : make_range(_D->row_start(), _D->row_stop()))
849 {
850 if (MooseUtils::absoluteFuzzyEqual((*diag_D)(i), 0.0))
851 mooseError("Trying to compute reciprocal of 0.");
852 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
853 MatSetValue(dinv,
854 i,
856 1.0 / (*diag_D)(i),
857 INSERT_VALUES));
858 }
859
860 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
861 MatAssemblyBegin(dinv, MAT_FINAL_ASSEMBLY));
862 LibmeshPetscCallA(this->MoosePreconditioner::comm().get(),
863 MatAssemblyEnd(dinv, MAT_FINAL_ASSEMBLY));
864}
char ** vars
registerMooseObjectAliased("MooseApp", VariableCondensationPreconditioner, "VCP")
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
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...
Definition MooseBase.h:271
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition MooseBase.h:406
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition MooseMesh.C:3557
libMesh::NodeRange * getActiveNodeRange()
Definition MooseMesh.C:1251
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.
Definition SystemBase.C:89
Interface for condensing out LMs for the dual mortar approach.
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".
const bool _is_lm_coupling_diagonal
Whether the coupling is diagonal.
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.
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.
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 &params)
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
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::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.
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
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
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
MeshBase & mesh