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