Loading [MathJax]/extensions/tex2jax.js
https://mooseframework.inl.gov
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends
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 
38 using namespace libMesh;
39 
41 
44 {
46 
47  params.addClassDescription(
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),
84  Preconditioner<Number>(MoosePreconditioner::_communicator),
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 
195 void
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();
203  _map_global_lm_primary.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 
275 void
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) !=
292  _global_primary_dofs.end())
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
306  if (_map_global_lm_primary.find(i) != _map_global_lm_primary.end())
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 
332 void
334 {
335  TIME_SECTION(_init_timer);
336 
337  if (!_preconditioner)
340 
341  _is_initialized = true;
342 }
343 
344 void
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 
399 void
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 
479 void
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 
570 void
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 
594 void
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 
609  condenseSystem();
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 
625 void
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 
647 void
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 
683 void
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 
704 void
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 
729 void
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  PetscMatrix<Number> * 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 
757 void
759 {
760  if (_dinv != nullptr)
761  LibmeshPetscCallA(this->MoosePreconditioner::comm().get(), MatDestroy(&_dinv));
762 }
763 
764 void
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 
824 void
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 }
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.
Definition: MooseUtils.h:372
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.
MPI_Info info
PARALLEL
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.
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::vector< dof_id_type > _global_lm_dofs
Vectors of DoFs: indices associated with lagrange multipliers, and its coupled primary variable globa...
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 &params)
std::unique_ptr< PetscMatrix< Number > > _M
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.
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...
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.
dof_id_type n_dofs() const
bool _need_condense
Whether the DoFs associated the variable are to be condensed.
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3417
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 &param, 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
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
virtual void close()=0
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.
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()
Definition: MooseMesh.C:1229
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.
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 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...
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.
Definition: SystemBase.C:89
registerMooseObjectAliased("MooseApp", VariableCondensationPreconditioner, "VCP")
std::unique_ptr< NumericVector< Number > > _primary_rhs_vec
Real Number
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...
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)
uint8_t dof_id_type
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.