https://mooseframework.inl.gov
ElementSubdomainModifierBase.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 #include "DisplacedProblem.h"
12 #include "MaterialWarehouse.h"
13 
14 #include "libmesh/dof_map.h"
15 #include "libmesh/remote_elem.h"
16 #include "libmesh/parallel_ghost_sync.h"
17 #include "libmesh/numeric_vector.h"
18 #include "libmesh/parameters.h"
19 #include <iterator>
20 #include <unordered_set>
21 
24 {
26 
27  // ESMs only operate on the undisplaced mesh to gather subdomain and sideset information. This
28  // information is used to modify both the undisplaced and the displaced meshes. It is the
29  // developer's responsibility to make sure the element IDs and sideset info are consistent across
30  // both meshes.
31  params.set<bool>("use_displaced_mesh") = false;
32  params.suppressParameter<bool>("use_displaced_mesh");
33 
34  params.addParam<std::vector<BoundaryName>>(
35  "moving_boundaries",
36  {},
37  "Moving boundaries between subdomains. These boundaries (both sidesets and nodesets) will be "
38  "updated for elements that change subdomain. The subdomains that each moving "
39  "boundary lies between shall be specified using the parameter "
40  "'moving_boundary_subdomain_pairs'. If one boundary and multiple subdomain pairs are "
41  "specified, then it is assumed that the pairs all apply to the boundary. A boundary will be "
42  "created on the mesh if it does not already exist.");
43  params.addParam<std::vector<std::vector<SubdomainName>>>(
44  "moving_boundary_subdomain_pairs",
45  {},
46  "The subdomain pairs associated with each moving boundary. For each pair of subdomains, only "
47  "the element side from the first subdomain will be added to the moving boundary, i.e., the "
48  "side normal is pointing from the first subdomain to the second subdomain. The pairs shall "
49  "be delimited by ';'. If a pair only has one subdomain, the moving boundary is associated "
50  "with the subdomain's external boundary, i.e., when the elements have no neighboring "
51  "elements.");
52 
53  params.addParam<std::vector<SubdomainName>>(
54  "reinitialize_subdomains",
55  {"ANY_BLOCK_ID"},
56  "By default, any element which changes subdomain is reinitialized. If a list of subdomains "
57  "(IDs or names) is provided, then only elements whose new subdomain is in the list will be "
58  "reinitialized. If an empty list is set, then no elements will be reinitialized.");
59 
60  params.addParam<bool>(
61  "old_subdomain_reinitialized",
62  true,
63  "This parameter must be set with a non-empty list in 'reinitialize_subdomains'. When set to "
64  "the default true, the element's old subdomain is not considered when determining if an "
65  "element should be reinitialized. If set to false, only elements whose old subdomain was not "
66  "in 'reinitialize_subdomains' are reinitialized. ");
67 
68  params.addParam<std::vector<VariableName>>(
69  "reinitialize_variables", {}, "Which variables to reinitialize when subdomain changes.");
70  MooseEnum reinit_strategy("IC POLYNOMIAL_NEIGHBOR POLYNOMIAL_WHOLE POLYNOMIAL_NEARBY NONE", "IC");
71  params.addParam<std::vector<MooseEnum>>(
72  "reinitialization_strategy",
73  {reinit_strategy},
74  "The strategy used to reinitialize the solution when elements change subdomain. If multiple "
75  "strategies are provided, each strategy will be applied to the corresponding variable. If "
76  "only one strategy is provided, it will be applied to all variables.");
77  params.addParam<std::vector<UserObjectName>>(
78  "polynomial_fitters",
79  {},
80  "List of NodalPatchRecovery UserObjects used for polynomial fitting during variable "
81  "reinitialization. Required only if 'reinitialization_strategy' includes "
82  "POLYNOMIAL_NEIGHBOR, POLYNOMIAL_WHOLE, or POLYNOMIAL_NEARBY.");
83  params.addParam<int>(
84  "nearby_kd_tree_leaf_max_size",
85  10,
86  "Maximum number of elements in a leaf node of the K-D tree used to search for nearby "
87  "elements. Only needed if 'reinitialization_strategy' is set to POLYNOMIAL_NEARBY.");
88  params.addParam<double>(
89  "nearby_distance_threshold",
90  -1.0,
91  "Threshold for considering elements as 'nearby' in the K-D tree search. Only elements within "
92  "this distance will be considered for polynomial fitting.");
93  params.addParam<std::vector<bool>>(
94  "restore_overridden_dofs",
95  {},
96  "A list of boolean flags, one for each variable in 'reinitialize_variables', specifying "
97  "whether overridden DOF values should be restored after reinitialization for each variable. "
98  "This is useful when the solved values on these DOFs should be preserved. If the list is "
99  "empty, overridden DOF values will NOT be restored for any variable by default.");
100 
101  params.addParam<bool>("skip_restore_subdomain_changes",
102  false,
103  "Skip restoring the subdomain changes if the timestep is not advanced.");
104 
105  params.registerBase("MeshModifier");
106 
107  return params;
108 }
109 
111  : ElementUserObject(parameters),
112  _displaced_problem(_fe_problem.getDisplacedProblem().get()),
113  _displaced_mesh(_displaced_problem ? &_displaced_problem->mesh() : nullptr),
114  _nl_sys(_fe_problem.getNonlinearSystemBase(systemNumber())),
115  _aux_sys(_fe_problem.getAuxiliarySystem()),
116  _t_step_old(declareRestartableData<int>("t_step_old", 0)),
117  _restep(false),
118  _skip_restore_subdomain_changes(getParam<bool>("skip_restore_subdomain_changes")),
119  _old_subdomain_reinitialized(getParam<bool>("old_subdomain_reinitialized")),
120  _pr_names(getParam<std::vector<UserObjectName>>("polynomial_fitters")),
121  _reinit_vars(getParam<std::vector<VariableName>>("reinitialize_variables")),
122  _leaf_max_size(getParam<int>("nearby_kd_tree_leaf_max_size")),
123  _nearby_distance_threshold(getParam<double>("nearby_distance_threshold"))
124 {
125 }
126 
127 void
129 {
130  const std::vector<SubdomainName> subdomain_names_to_reinitialize =
131  getParam<std::vector<SubdomainName>>("reinitialize_subdomains");
132 
133  if (std::find(subdomain_names_to_reinitialize.begin(),
134  subdomain_names_to_reinitialize.end(),
135  "ANY_BLOCK_ID") != subdomain_names_to_reinitialize.end())
137  else
138  _subdomain_ids_to_reinitialize = _mesh.getSubdomainIDs(subdomain_names_to_reinitialize);
139 
140  std::set<SubdomainID> set_subdomain_ids_to_reinitialize(_subdomain_ids_to_reinitialize.begin(),
142 
143  if (_old_subdomain_reinitialized == false &&
147  set_subdomain_ids_to_reinitialize == _mesh.meshSubdomains()))
148  paramError("old_subdomain_reinitialized",
149  "'old_subdomain_reinitialized' can only be set to false if "
150  "reinitialize_subdomains does "
151  "not cover the whole model, otherwise no elements will be reinitialized as it is "
152  "impossible for an element's old subdomain to not be in the list.");
154  paramError("old_subdomain_reinitialized",
155  "'old_subdomain_reinitialized' can only be set to false if "
156  "reinitialize_subdomains is set to a non-empty list of subdomains, otherwise no "
157  "elements will be reinitialized, as it is impossible for an element's new subdomain "
158  "to be in the list.");
159 
160  auto bnd_names = getParam<std::vector<BoundaryName>>("moving_boundaries");
161  auto bnd_ids = _mesh.getBoundaryIDs(bnd_names, true);
162  const auto bnd_subdomains =
163  getParam<std::vector<std::vector<SubdomainName>>>("moving_boundary_subdomain_pairs");
164 
165  if (bnd_names.size() == 1 && bnd_subdomains.size() > 1)
166  {
167  bnd_names.insert(bnd_names.end(), bnd_subdomains.size() - 1, bnd_names[0]);
168  bnd_ids.insert(bnd_ids.end(), bnd_subdomains.size() - 1, bnd_ids[0]);
169  }
170  else if (bnd_names.size() != bnd_subdomains.size())
171  paramError("moving_boundary_subdomain_pairs",
172  "Each moving boundary must correspond to a pair of subdomains. ",
173  bnd_names.size(),
174  " boundaries are specified by the parameter 'moving_boundaries', while ",
175  bnd_subdomains.size(),
176  " subdomain pairs are provided. Alternatively, if one boundary and multiple "
177  "subdomain pairs are provided, then the subdomain pairs all apply to one boundary.");
178 
179  for (auto i : index_range(bnd_names))
180  {
181  _moving_boundary_names[bnd_ids[i]] = bnd_names[i];
182 
183  if (bnd_subdomains[i].size() == 2)
184  _moving_boundaries[{_mesh.getSubdomainID(bnd_subdomains[i][0]),
185  _mesh.getSubdomainID(bnd_subdomains[i][1])}] = bnd_ids[i];
186  else if (bnd_subdomains[i].size() == 1)
188  bnd_ids[i];
189  else
190  paramError("moving_boundary_subdomain_pairs",
191  "Each subdomain pair must contain 1 or 2 subdomain names, but ",
192  bnd_subdomains[i].size(),
193  " are given.");
194  }
195 
196  // Check if the variables to reinitialize exist in the system
197  for (const auto & var_name : _reinit_vars)
198  if (!_nl_sys.hasVariable(var_name) && !_aux_sys.hasVariable(var_name))
199  paramError("reinitialize_variables", "Variable ", var_name, " does not exist.");
200 
201  // Determine the reinitialization strategy for each variable.
202  // (1) If they are of the same size, we perform a 1-to-1 mapping.
203  // (2) If only one strategy or restore flag is provided, it applies to all variables.
204  const auto reinit_strategy_in = getParam<std::vector<MooseEnum>>("reinitialization_strategy");
205  const auto restore_overridden_dofs_in = getParam<std::vector<bool>>("restore_overridden_dofs");
206 
207  if (std::any_of(reinit_strategy_in.begin(),
208  reinit_strategy_in.end(),
209  [](const MooseEnum & val) { return val == "POLYNOMIAL_NEARBY"; }) &&
210  !isParamSetByUser("nearby_distance_threshold"))
211  mooseError("The 'nearby_distance_threshold' parameter must be set when using the "
212  "POLYNOMIAL_NEARBY reinitialization strategy.");
213 
214  if (std::all_of(reinit_strategy_in.begin(),
215  reinit_strategy_in.end(),
216  [](const MooseEnum & val) { return val != "POLYNOMIAL_NEARBY"; }) &&
217  (isParamSetByUser("nearby_distance_threshold") ||
218  isParamSetByUser("nearby_kd_tree_leaf_max_size")))
219  mooseWarning("The 'nearby_distance_threshold' and 'nearby_kd_tree_leaf_max_size' parameters "
220  "will be ignored because no 'reinitialization_strategy' is set to "
221  "POLYNOMIAL_NEARBY.");
222 
223  if (reinit_strategy_in.size() == 1)
224  _reinit_strategy.resize(_reinit_vars.size(), reinit_strategy_in[0].getEnum<ReinitStrategy>());
225  else if (reinit_strategy_in.size() == _reinit_vars.size())
226  for (const auto & e : reinit_strategy_in)
227  _reinit_strategy.push_back(e.getEnum<ReinitStrategy>());
228  else
229  paramError(
230  "reinitialization_strategy",
231  "The 'reinitialization_strategy' parameter must have either a single value or a number "
232  "of values equal to the number of 'reinitialize_variables'. "
233  "Got ",
234  reinit_strategy_in.size(),
235  " strategies for ",
236  _reinit_vars.size(),
237  " variables.");
238 
239  if (restore_overridden_dofs_in.size() == 1)
240  {
241  if (restore_overridden_dofs_in[0])
243  _reinit_vars; // Restore overridden DOFs for all reinitialized variables
244  }
245  else if (restore_overridden_dofs_in.size() == _reinit_vars.size())
246  {
247  for (auto i : index_range(_reinit_vars))
248  if (restore_overridden_dofs_in[i])
250  }
251  else
252  {
253  if (!restore_overridden_dofs_in.empty())
254  paramError(
255  "restore_overridden_dofs",
256  "The 'restore_overridden_dofs' parameter must have either a single value or a number "
257  "of values equal to the number of 'reinitialize_variables'. "
258  "Got ",
259  restore_overridden_dofs_in.size(),
260  " restore_overridden_dofs for ",
261  _reinit_vars.size(),
262  " variables.");
263  }
264 
265  // For all the other variables, we set the reinitialization strategy to IC
266  for (const auto & var_name : _fe_problem.getVariableNames())
267  if (std::find(_reinit_vars.begin(), _reinit_vars.end(), var_name) == _reinit_vars.end())
268  {
269  _reinit_vars.push_back(var_name);
271  }
272 
273  // Map variable names to the index of the nodal patch recovery user object
274  _pr.resize(_pr_names.size());
275  std::size_t pr_count = 0;
276  for (auto i : index_range(_reinit_vars))
280  {
281  _var_name_to_pr_idx[_reinit_vars[i]] = pr_count;
282  if (pr_count >= _pr_names.size())
283  paramError("polynomial_fitters",
284  "The number of polynomial fitters (",
285  _pr_names.size(),
286  ") is less than the number of variables to reinitialize with polynomial "
287  "extrapolation.");
288  _pr[pr_count] =
290  mooseAssert(_pr[pr_count]->variableName() == _reinit_vars[i],
291  "The patch recovery UserObject's variable name must match the variable being "
292  "reinitialized in ElementSubdomainModifierBase.");
293  _depend_uo.insert(_pr_names[pr_count]);
294  pr_count++;
295  }
296  if (_pr_names.size() != pr_count)
297  paramError("polynomial_fitters",
298  "Mismatch between number of reinitialization strategies using polynomial "
299  "extrapolation and polynomial fitters (expected: ",
300  pr_count,
301  ", given: ",
302  _pr_names.size(),
303  ").");
304 }
305 
306 void
308 {
310  {
311  mooseInfoRepeated(name(), ": Restoring element subdomain changes.");
312 
313  // Reverse the subdomain changes
314  auto moved_elem_reversed = _moved_elems;
315  for (auto & [elem_id, subdomain] : moved_elem_reversed)
316  std::swap(subdomain.first, subdomain.second);
317 
318  _restep = true;
319  modify(moved_elem_reversed);
320  _restep = false;
321  }
322 
324 }
325 
326 void
328  const std::unordered_map<dof_id_type, std::pair<SubdomainID, SubdomainID>> & moved_elems)
329 {
330  if (!_restep)
331  // Cache the moved elements for potential restore
332  _moved_elems = moved_elems;
333 
334  // If nothing need to change, just return.
335  // This will skip all mesh changes, and so no adaptivity mesh files will be written.
336  auto n_moved_elem = moved_elems.size();
337  gatherSum(n_moved_elem);
338  if (n_moved_elem == 0)
339  return;
340 
341  // Create moving boundaries on the undisplaced and displaced meshes
342  //
343  // Note: We do this _everytime_ because previous execution might have removed the sidesets and
344  // nodesets. Most of the moving boundary algorithms below assume that the moving sidesets and
345  // nodesets already exist on the mesh.
347  if (_displaced_mesh)
349 
350  // This has to be done *before* subdomain changes are applied
351  findReinitializedElemsAndNodes(moved_elems);
352 
353  // Apply cached subdomain changes
354  applySubdomainChanges(moved_elems, _mesh);
355  if (_displaced_mesh)
356  applySubdomainChanges(moved_elems, *_displaced_mesh);
357 
358  // Update moving boundaries
359  gatherMovingBoundaryChanges(moved_elems);
361  if (_displaced_mesh)
363 
364  // Some variable reinitialization strategies require patch elements to be gathered
365  // This has to be done *before* reinitializing the equation systems because we need to find
366  // currently evaluable elements
367  if (!_restep)
368  {
369  _evaluable_elems.clear();
370  _patch_elem_ids.clear();
371  for (auto i : index_range(_reinit_vars))
373  }
374 
375  // Reinit equation systems
377  /*intermediate_change=*/false, /*contract_mesh=*/false, /*clean_refinement_flags=*/false);
378 
379  // Initialize solution and stateful material properties
380  if (!_restep)
381  {
382  applyIC();
385  }
386 }
387 
388 void
390 {
391  auto & bnd_info = mesh.getMesh().get_boundary_info();
392  for (const auto & [bnd_id, bnd_name] : _moving_boundary_names)
393  {
394  bnd_info.sideset_name(bnd_id) = bnd_name;
395  bnd_info.nodeset_name(bnd_id) = bnd_name;
396  }
397 }
398 
399 void
401  const std::unordered_map<dof_id_type, std::pair<SubdomainID, SubdomainID>> & moved_elems,
402  MooseMesh & mesh)
403 {
404  for (const auto & [elem_id, subdomain] : moved_elems)
405  {
406  // Change the element's subdomain ID
407  auto elem = mesh.elemPtr(elem_id);
408  const auto & [from, to] = subdomain;
409  mooseAssert(elem->subdomain_id() == from, "Inconsistent element subdomain ID.");
410  elem->subdomain_id() = to;
411 
412  // Change the ancestors' (if any) subdomain ID
413  setAncestorsSubdomainIDs(elem, to);
414  }
415 
416  // Synchronize ghost element subdomain changes
417  libMesh::SyncSubdomainIds sync(mesh.getMesh());
418  Parallel::sync_dofobject_data_by_id(
419  mesh.getMesh().comm(), mesh.getMesh().elements_begin(), mesh.getMesh().elements_end(), sync);
420 }
421 
422 void
424 {
425  auto curr_elem = elem;
426 
427  for (unsigned int i = curr_elem->level(); i > 0; --i)
428  {
429  // Change the parent's subdomain
430  curr_elem = curr_elem->parent();
431  curr_elem->subdomain_id() = subdomain_id;
432  }
433 }
434 
435 void
437  const std::unordered_map<dof_id_type, std::pair<SubdomainID, SubdomainID>> & moved_elems)
438 {
439  // Clear moving boundary changes from last execution
440  _add_element_sides.clear();
441  _add_neighbor_sides.clear();
442  _remove_element_sides.clear();
443  _remove_neighbor_sides.clear();
444 
445  const auto & sidesets = _mesh.getMesh().get_boundary_info().get_sideset_map();
446 
447  for (const auto & [elem_id, subdomain_assignment] : moved_elems)
448  {
449  auto elem = _mesh.elemPtr(elem_id);
450 
451  // The existing moving boundaries on the element side should be removed
452  for (auto itr = sidesets.lower_bound(elem); itr != sidesets.upper_bound(elem); itr++)
453  if (_moving_boundary_names.count(itr->second.second))
454  _remove_element_sides[elem->id()].emplace(itr->second.first, itr->second.second);
455 
456  for (auto side : elem->side_index_range())
457  {
458  auto neigh = elem->neighbor_ptr(side);
459 
460  // Don't mess with remote element neighbor
461  if (neigh && neigh == libMesh::remote_elem)
462  continue;
463  // If neighbor doesn't exist
464  else if (!neigh)
465  gatherMovingBoundaryChangesHelper(elem, side, nullptr, 0);
466  // If neighbor exists
467  else
468  {
469  auto neigh_side = neigh->which_neighbor_am_i(elem);
470 
471  if (neigh->active())
472  gatherMovingBoundaryChangesHelper(elem, side, neigh, neigh_side);
473  else
474  {
475  // Find the active neighbors of the element
476  std::vector<const Elem *> active_neighs;
477  // Neighbor has active children, they are neighbors of the element along that side
478  mooseAssert(!neigh->subactive(),
479  "The case where the active neighbor is an ancestor of this neighbor is not "
480  "handled at this time.");
481  neigh->active_family_tree_by_neighbor(active_neighs, elem);
482 
483  for (auto active_neigh : active_neighs)
484  gatherMovingBoundaryChangesHelper(elem, side, active_neigh, neigh_side);
485  }
486  }
487  }
488  }
489 }
490 
491 void
493  unsigned short side,
494  const Elem * neigh,
495  unsigned short neigh_side)
496 {
497  const auto & sidesets = _mesh.getMesh().get_boundary_info().get_sideset_map();
498 
499  // Detect element side change
500  SubdomainPair subdomain_pair = {elem->subdomain_id(),
501  neigh ? neigh->subdomain_id() : Moose::INVALID_BLOCK_ID};
502  if (_moving_boundaries.count(subdomain_pair))
503  _add_element_sides[elem->id()].emplace(side, _moving_boundaries.at(subdomain_pair));
504 
505  if (neigh)
506  {
507  // The existing moving boundaries on the neighbor side should be removed
508  for (auto itr = sidesets.lower_bound(neigh); itr != sidesets.upper_bound(neigh); itr++)
509  if (itr->second.first == neigh_side && _moving_boundary_names.count(itr->second.second))
510  _remove_neighbor_sides[neigh->id()].emplace(itr->second.first, itr->second.second);
511 
512  // Detect neighbor side change (by reversing the subdomain pair)
513  subdomain_pair = {subdomain_pair.second, subdomain_pair.first};
514  if (_moving_boundaries.count(subdomain_pair))
515  _add_neighbor_sides[neigh->id()].emplace(neigh_side, _moving_boundaries.at(subdomain_pair));
516  }
517 }
518 
519 void
521 {
522  auto & bnd_info = mesh.getMesh().get_boundary_info();
523 
524  // Remove all boundary nodes from the previous moving boundaries
525  auto nodesets = bnd_info.get_nodeset_map();
526  for (const auto & [node_id, bnd] : nodesets)
527  if (_moving_boundary_names.count(bnd))
528  bnd_info.remove_node(node_id, bnd);
529 
530  // Keep track of ghost element changes
531  std::unordered_map<processor_id_type,
532  std::vector<std::tuple<dof_id_type, unsigned short, BoundaryID>>>
533  add_ghost_sides, remove_ghost_sides;
534 
535  // Remove element sides from moving boundaries
536  for (const auto & [elem_id, sides] : _remove_element_sides)
537  for (const auto & [side, bnd] : sides)
538  bnd_info.remove_side(mesh.elemPtr(elem_id), side, bnd);
539 
540  // Remove neighbor sides from moving boundaries
541  for (const auto & [elem_id, sides] : _remove_neighbor_sides)
542  {
543  auto elem = mesh.elemPtr(elem_id);
544  for (const auto & [side, bnd] : sides)
545  {
546  bnd_info.remove_side(elem, side, bnd);
547  // Keep track of changes to ghosted elements
548  if (elem->processor_id() != processor_id())
549  remove_ghost_sides[elem->processor_id()].push_back({elem_id, side, bnd});
550  }
551  }
552 
553  Parallel::push_parallel_vector_data(
554  bnd_info.comm(),
555  remove_ghost_sides,
556  [&mesh,
557  &bnd_info](processor_id_type,
558  const std::vector<std::tuple<dof_id_type, unsigned short, BoundaryID>> & received)
559  {
560  for (const auto & [elem_id, side, bnd] : received)
561  bnd_info.remove_side(mesh.elemPtr(elem_id), side, bnd);
562  });
563 
564  // Add element sides to moving boundaries
565  for (const auto & [elem_id, sides] : _add_element_sides)
566  for (const auto & [side, bnd] : sides)
567  bnd_info.add_side(mesh.elemPtr(elem_id), side, bnd);
568 
569  // Add neighbor sides to moving boundaries
570  for (const auto & [elem_id, sides] : _add_neighbor_sides)
571  {
572  auto elem = mesh.elemPtr(elem_id);
573  for (const auto & [side, bnd] : sides)
574  {
575  bnd_info.add_side(elem, side, bnd);
576  // Keep track of changes to ghosted elements
577  if (elem->processor_id() != processor_id())
578  add_ghost_sides[elem->processor_id()].push_back({elem_id, side, bnd});
579  }
580  }
581 
582  Parallel::push_parallel_vector_data(
583  bnd_info.comm(),
584  add_ghost_sides,
585  [&mesh,
586  &bnd_info](processor_id_type,
587  const std::vector<std::tuple<dof_id_type, unsigned short, BoundaryID>> & received)
588  {
589  for (const auto & [elem_id, side, bnd] : received)
590  bnd_info.add_side(mesh.elemPtr(elem_id), side, bnd);
591  });
592 
593  bnd_info.parallel_sync_side_ids();
594  bnd_info.parallel_sync_node_ids();
595  mesh.update();
596 }
597 
598 void
600  ReinitStrategy reinit_strategy)
601 {
602  switch (reinit_strategy)
603  {
605  case ReinitStrategy::IC:
606  // No additional preparation needed for IC and NONE strategies
607  break;
611  {
612  if (_var_name_to_pr_idx.find(var_name) == _var_name_to_pr_idx.end())
613  return;
614  const int pr_idx = _var_name_to_pr_idx[var_name];
615  // The patch elements might be different for each variable
616  gatherPatchElements(var_name, reinit_strategy);
617 
618  // Notify the patch recovery user object about the patch elements
619  _pr[pr_idx]->sync(_patch_elem_ids[var_name]);
620 
621  break;
622  }
623  default:
624  mooseError("Unknown reinitialization strategy");
625  break;
626  }
627 }
628 
629 void
631 {
632  // Clear cached ranges
636 
638  if (_displaced_mesh)
640 }
641 
642 void
644 {
645  auto & bnd_info = mesh.getMesh().get_boundary_info();
646  auto sidesets = bnd_info.get_sideset_map();
647  for (const auto & i : sidesets)
648  {
649  auto elem = i.first;
650  auto side = i.second.first;
651  auto bnd = i.second.second;
652  if (_moving_boundary_names.count(bnd) && !elem->active())
653  {
654  bnd_info.remove_side(elem, side, bnd);
655 
656  std::vector<const Elem *> elem_family;
657  elem->active_family_tree_by_side(elem_family, side);
658  for (auto felem : elem_family)
659  bnd_info.add_side(felem, side, bnd);
660  }
661  }
662 
663  bnd_info.parallel_sync_side_ids();
664  bnd_info.parallel_sync_node_ids();
665 }
666 
667 void
669  const std::unordered_map<dof_id_type, std::pair<SubdomainID, SubdomainID>> & moved_elems)
670 {
671  // Clear cached element reinitialization data
672  _reinitialized_elems.clear();
673  _reinitialized_nodes.clear();
674 
675  // One more algorithm:
676  // (1) Loop over moved elements
677  // (2) If neighbor element processor ID is not the same as current processor ID (ghost element),
678  // push the moved element ID to the neighbor processor
679 
680  std::unordered_map<processor_id_type, std::unordered_set<dof_id_type>> push_data_set;
681  std::unordered_map<processor_id_type, std::vector<dof_id_type>> push_data;
682 
683  for (const auto & [elem_id, subdomain] : moved_elems)
684  {
685  mooseAssert(_mesh.elemPtr(elem_id)->active(), "Moved elements should be active");
686  // Default: any element that changes subdomain is reinitialized
690  _reinitialized_elems.insert(elem_id);
691  else // Reinitialize if new subdomain is in list of subdomains to be reinitialized
692  {
693  const auto & [from, to] = subdomain;
695  _reinitialized_elems.insert(elem_id);
696  // Only reinitialize if original subdomain is not in list of subdomains
699  _reinitialized_elems.insert(elem_id);
700  else // New subdomain is not in list of subdomains
701  continue;
702  }
703  const auto & elem = _mesh.elemPtr(elem_id);
704 
705  // (1) Loop over nodes of moved elements
706  // (2) node to element map is used to find neighbor elements
707  // (3) If neighbor element processor ID is not the same as current processor ID (means that the
708  // current element is ghosted element to the neighbor processor), push the moved element (or
709  // reinitialized, or newly-activated) ID to the neighbor processor
710  for (const auto & node : elem->node_ref_range())
711  for (const auto & neigh_id : _mesh.nodeToElemMap().at(node.id()))
712  if (neigh_id != elem_id) // Don't check the element itself
713  {
714  const auto neigh_elem = _mesh.elemPtr(neigh_id);
715  if (neigh_elem->processor_id() != processor_id())
716  push_data_set[neigh_elem->processor_id()].insert(elem_id);
717  }
718 
719  for (unsigned int i = 0; i < elem->n_nodes(); ++i)
720  if (nodeIsNewlyReinitialized(elem->node_id(i)))
721  _reinitialized_nodes.insert(elem->node_id(i));
722  }
723 
724  for (auto & [pid, s] : push_data_set)
725  push_data[pid] = {s.begin(), s.end()};
726 
728 
729  auto push_receiver =
730  [this](const processor_id_type, const std::vector<dof_id_type> & received_data)
731  {
732  for (const auto & id : received_data)
734  };
735 
736  Parallel::push_parallel_vector_data(_mesh.comm(), push_data, push_receiver);
737 }
738 
739 bool
741 {
742  // Default: any element that changes subdomain is reinitialized
746  return true;
747 
748  // Is subdomain in list of subdomains to be reinitialized
751  id) != _subdomain_ids_to_reinitialize.end();
752 }
753 
754 bool
756 {
757  // If any of the node neighbor elements has reinitialized, then the node is NOT newly
758  // reinitialized.
759  for (auto neighbor_elem_id : _mesh.nodeToElemMap().at(node_id))
760  if (subdomainIsReinitialized(_mesh.elemPtr(neighbor_elem_id)->subdomain_id()))
761  return false;
762  return true;
763 }
764 
765 void
767 {
768  // Before reinitializing variables, some DOFs may be overwritten.
769  // By default, these overwritten DOF values are NOT restored.
770  // If the user sets `restore_overridden_dofs` to true, we first save the current
771  // values of these DOFs, then restore them after reinitialization.
772  for (const auto & var_name : _vars_to_restore_overridden_dofs)
773  storeOverriddenDofValues(var_name);
774 
775  // ReinitStrategy::IC
776  std::set<VariableName> ic_vars;
777  for (auto i : index_range(_reinit_vars))
779  ic_vars.insert(_reinit_vars[i]);
780  if (!ic_vars.empty())
783 
784  // ReinitStrategy::POLYNOMIAL_NEIGHBOR, POLYNOMIAL_WHOLE, POLYNOMIAL_NEARBY
785  for (const auto & [var, patch] : _patch_elem_ids)
787 
788  // See the comment above, now we restore the values of the dofs that were overridden
789  for (const auto & var_name : _vars_to_restore_overridden_dofs)
790  restoreOverriddenDofValues(var_name);
791 
792  mooseAssert(_fe_problem.numSolverSystems() <= 1,
793  "This code was written for a single nonlinear system");
794  // Set old and older solutions on the reinitialized dofs to the reinitialized values
795  // note: from current -> old -> older
801 
802  // Note: Need method to handle solve failures at timesteps where subdomain changes. The old
803  // solutions are now set to the reinitialized values. Does this impact restoring solutions
804 }
805 
806 void
808 {
809  const auto & sys = _fe_problem.getSystem(var_name);
810  const auto & current_solution = *sys.current_local_solution;
811  const auto & dof_map = sys.get_dof_map();
812  const auto & var = _fe_problem.getStandardVariable(0, var_name);
813  const auto var_num = var.number();
814 
815  // Get the DOFs on the reinitialized elements
816  // Here we should loop over both ghosted and local reinitialized elements.
817  // The ghosted elements here can take care of DoFs that is belong to the reinitialized
818  // elements but are not on the current processor.
819  std::set<dof_id_type> reinitialized_dofs;
820  for (const auto & elem_id : _semi_local_reinitialized_elems)
821  {
822  const auto & elem = _mesh.elemPtr(elem_id);
823  std::vector<dof_id_type> elem_dofs;
824  dof_map.dof_indices(elem, elem_dofs, var_num);
825  reinitialized_dofs.insert(elem_dofs.begin(), elem_dofs.end());
826  }
827 
828  // Get existing DOFs on the active elements excluding reinitialized elements
829  std::set<dof_id_type> existing_dofs;
830  for (const auto * elem : *_mesh.getActiveLocalElementRange())
831  {
832  if (_reinitialized_elems.count(elem->id()))
833  continue; // Skip reinitialized elements
834  std::vector<dof_id_type> elem_dofs;
835  dof_map.dof_indices(elem, elem_dofs, var_num);
836  existing_dofs.insert(elem_dofs.begin(), elem_dofs.end());
837  }
838 
839  // Get the DOFs on the nodes that are overridden on reinitialized elements
840  std::vector<dof_id_type> overridden_dofs;
841  std::set_intersection(reinitialized_dofs.begin(),
842  reinitialized_dofs.end(),
843  existing_dofs.begin(),
844  existing_dofs.end(),
845  std::back_inserter(overridden_dofs));
846 
847  // Values before overriding (to be restored later)
848  std::vector<Number> values;
849  for (auto dof : overridden_dofs)
850  values.push_back(current_solution(dof));
851 
852  _overridden_values_on_reinit_elems[var_name] = {overridden_dofs, values};
853 }
854 
855 void
857 {
858  const auto sn = _fe_problem.systemNumForVariable(var_name);
859  auto & sys = _fe_problem.getSystemBase(sn);
860  auto & sol = sys.solution();
861  const auto & dof_map = sys.dofMap();
862  const auto & [dof_ids, values] = _overridden_values_on_reinit_elems[var_name];
863 
864  std::unordered_map<processor_id_type, std::vector<std::pair<dof_id_type, Number>>> push_data;
865 
866  for (const int i : index_range(dof_ids))
867  {
868  if (dof_map.dof_owner(dof_ids[i]) == processor_id())
869  sol.set(dof_ids[i], values[i]);
870  else
871  push_data[dof_map.dof_owner(dof_ids[i])].emplace_back(dof_ids[i], values[i]);
872  }
873 
874  auto push_receiver = [&](const processor_id_type,
875  const std::vector<std::pair<dof_id_type, Number>> & received_data)
876  {
877  for (const auto & [id, value] : received_data)
878  sol.set(id, value);
879  };
880 
881  Parallel::push_parallel_vector_data(_mesh.comm(), push_data, push_receiver);
882 
883  sol.close();
884  sol.localize(*sys.system().current_local_solution, sys.dofMap().get_send_list());
885 }
886 
887 void
889 {
891 }
892 
895 {
897  return *_reinitialized_elem_range.get();
898 
899  // Create a vector of the newly reinitialized elements
900  std::vector<Elem *> elems;
901  for (auto elem_id : _reinitialized_elems)
902  elems.push_back(_mesh.elemPtr(elem_id));
903 
904  // Make some fake element iterators defining this vector of elements
905  Elem * const * elem_itr_begin = const_cast<Elem * const *>(elems.data());
906  Elem * const * elem_itr_end = elem_itr_begin + elems.size();
907 
908  const auto elems_begin = MeshBase::const_element_iterator(
909  elem_itr_begin, elem_itr_end, Predicates::NotNull<Elem * const *>());
910  const auto elems_end = MeshBase::const_element_iterator(
911  elem_itr_end, elem_itr_end, Predicates::NotNull<Elem * const *>());
912 
913  _reinitialized_elem_range = std::make_unique<ConstElemRange>(elems_begin, elems_end);
914 
915  return reinitializedElemRange();
916 }
917 
920 {
922  return *_reinitialized_bnd_node_range.get();
923 
924  // Create a vector of the newly reinitialized boundary nodes
925  std::vector<const BndNode *> nodes;
926  auto bnd_nodes = _mesh.getBoundaryNodeRange();
927  for (auto bnd_node : *bnd_nodes)
928  if (bnd_node->_node)
929  if (_reinitialized_nodes.count(bnd_node->_node->id()))
930  nodes.push_back(bnd_node);
931 
932  BndNode * const * bnd_node_itr_begin = const_cast<BndNode * const *>(nodes.data());
933  BndNode * const * bnd_node_itr_end = bnd_node_itr_begin + nodes.size();
934 
935  const auto bnd_nodes_begin = MooseMesh::const_bnd_node_iterator(
936  bnd_node_itr_begin, bnd_node_itr_end, Predicates::NotNull<const BndNode * const *>());
937  const auto bnd_nodes_end = MooseMesh::const_bnd_node_iterator(
938  bnd_node_itr_end, bnd_node_itr_end, Predicates::NotNull<const BndNode * const *>());
939 
941  std::make_unique<ConstBndNodeRange>(bnd_nodes_begin, bnd_nodes_end);
942 
943  return reinitializedBndNodeRange();
944 }
945 
948 {
950  return *_reinitialized_node_range.get();
951 
952  // Create a vector of the newly reinitialized nodes
953  std::vector<const Node *> nodes;
954 
955  for (auto node_id : _reinitialized_nodes)
956  nodes.push_back(_mesh.nodePtr(node_id)); // displaced mesh shares the same node object
957 
958  Node * const * node_itr_begin = const_cast<Node * const *>(nodes.data());
959  Node * const * node_itr_end = node_itr_begin + nodes.size();
960 
961  const auto nodes_begin = MeshBase::const_node_iterator(
962  node_itr_begin, node_itr_end, Predicates::NotNull<const Node * const *>());
963  const auto nodes_end = MeshBase::const_node_iterator(
964  node_itr_end, node_itr_end, Predicates::NotNull<const Node * const *>());
965 
966  _reinitialized_node_range = std::make_unique<ConstNodeRange>(nodes_begin, nodes_end);
967 
968  return *_reinitialized_node_range.get();
969 }
970 
971 void
973  ConstElemRange & elem_range,
974  ConstBndNodeRange & bnd_node_range)
975 {
976  for (auto bnd : bnd_node_range)
977  {
978  const Node * bnode = bnd->_node;
979  if (!bnode)
980  continue;
981  }
982 
983  // Don't do anything if this is a steady simulation
984  if (!sys.hasSolutionState(1))
985  return;
986 
987  NumericVector<Number> & current_solution = *sys.system().current_local_solution;
988  NumericVector<Number> & old_solution = sys.solutionOld();
989  NumericVector<Number> * older_solution = sys.hasSolutionState(2) ? &sys.solutionOlder() : nullptr;
990 
991  // Get dofs for the reinitialized elements and nodes
992  DofMap & dof_map = sys.dofMap();
993  std::vector<dof_id_type> dofs;
994 
995  for (auto & elem : elem_range)
996  {
997  std::vector<dof_id_type> elem_dofs;
998  dof_map.dof_indices(elem, elem_dofs);
999  dofs.insert(dofs.end(), elem_dofs.begin(), elem_dofs.end());
1000  }
1001 
1002  for (auto & bnd_node : bnd_node_range)
1003  {
1004  std::vector<dof_id_type> bnd_node_dofs;
1005  dof_map.dof_indices(bnd_node->_node, bnd_node_dofs);
1006  dofs.insert(dofs.end(), bnd_node_dofs.begin(), bnd_node_dofs.end());
1007  }
1008 
1009  // Set the old and older solutions to match the reinitialization
1010  for (auto dof : dofs)
1011  {
1012  old_solution.set(dof, current_solution(dof));
1013  if (older_solution)
1014  older_solution->set(dof, current_solution(dof));
1015  }
1016 
1017  old_solution.close();
1018  if (older_solution)
1019  older_solution->close();
1020 }
1021 
1022 void
1024  ReinitStrategy reinit_strategy)
1025 {
1026  _patch_elem_ids[var_name].clear();
1027 
1028  // First collect all elements who own dofs in the current dofmap
1029  auto & sys = _fe_problem.getSystem(var_name);
1030 
1031  // Cache evaluable elements for the system if not already done
1032  if (!_evaluable_elems.count(sys.number()))
1033  {
1034  auto & [candidate_elems, candidate_elem_ids] = _evaluable_elems[sys.number()];
1035  const auto & dof_map = sys.get_dof_map();
1036  std::vector<dof_id_type> elem_dofs;
1037  auto vn = sys.variable_number(static_cast<std::string>(var_name));
1038  for (const auto elem : *_mesh.getActiveLocalElementRange())
1039  {
1040  if (std::find(_reinitialized_elems.begin(), _reinitialized_elems.end(), elem->id()) !=
1041  _reinitialized_elems.end())
1042  continue; // Skip elements that were reinitialized
1043 
1044  dof_map.dof_indices(elem, elem_dofs, vn);
1045  if (!elem_dofs.empty())
1046  {
1047  candidate_elems.insert(elem);
1048  candidate_elem_ids.push_back(elem->id());
1049  }
1050  }
1051  }
1052  auto & [candidate_elems, candidate_elem_ids] = _evaluable_elems[sys.number()];
1053 
1054  // Now we gather patch elements based on the reinit strategy
1055  auto & patch_elems = _patch_elem_ids[var_name];
1056 
1057  switch (reinit_strategy)
1058  {
1060  {
1061  auto has_neighbor_in_reinit_elems = [&](const Elem * elem) -> bool
1062  {
1063  for (const auto & node : elem->node_ref_range())
1064  for (const auto & neigh_id : _mesh.nodeToElemMap().at(node.id()))
1065  // here we need to use _global_reinitialized_elems gathering from all processors
1066  if (_semi_local_reinitialized_elems.count(neigh_id))
1067  return true;
1068  return false;
1069  };
1070  // Loop over all candidate elements, for each element, if any of its point neighbor belongs
1071  // to the reinitialized elements, we will include that element in the patch element set.
1072  for (const auto * elem : candidate_elems)
1073  if (has_neighbor_in_reinit_elems(elem))
1074  patch_elems.push_back(elem->id());
1075  break;
1076  }
1078  {
1079  // This is simple: all candidate elements are patch elements
1080  patch_elems = candidate_elem_ids;
1081  break;
1082  }
1084  {
1085  std::vector<Point> kd_points;
1086  std::vector<dof_id_type> global_candidate_elem_ids;
1087 
1088  if (_mesh.isDistributedMesh())
1089  {
1090  std::vector<std::pair<Point, dof_id_type>> pts_ids(candidate_elem_ids.size());
1091  for (std::size_t i = 0; i < candidate_elem_ids.size(); ++i)
1092  pts_ids[i] = {_mesh.elemPtr(candidate_elem_ids[i])->vertex_average(),
1093  candidate_elem_ids[i]};
1094  _mesh.comm().allgather(pts_ids);
1095  for (const auto & [pt, id] : pts_ids)
1096  {
1097  kd_points.push_back(pt);
1098  global_candidate_elem_ids.push_back(id);
1099  }
1100  }
1101  else
1102  {
1103  _mesh.comm().allgather(candidate_elem_ids);
1104  global_candidate_elem_ids = candidate_elem_ids;
1105  for (const auto & id : candidate_elem_ids)
1106  kd_points.push_back(_mesh.elemPtr(id)->vertex_average());
1107  }
1108 
1109  const auto kd_tree = std::make_unique<KDTree>(kd_points, _leaf_max_size);
1110 
1111  std::vector<nanoflann::ResultItem<std::size_t, Real>> query_result;
1112  for (const auto & elem_id : _reinitialized_elems)
1113  {
1114  const Point & centroid = _mesh.elemPtr(elem_id)->vertex_average();
1115  kd_tree->radiusSearch(centroid, _nearby_distance_threshold, query_result);
1116  for (const auto & [qid, dist] : query_result)
1117  patch_elems.push_back(global_candidate_elem_ids[qid]);
1118  }
1119  break;
1120  }
1121  case ReinitStrategy::NONE:
1122  // do nothing
1123  break;
1124  default:
1125  mooseError("Unknown reinitialization strategy");
1126  break;
1127  }
1128 
1129  // every processor should have the same patch elements to do the polynomial extrapolation,
1130  // so we gather them across all processors
1131  _mesh.comm().allgather(patch_elems);
1132 
1133  // Remove duplicates from the patch elements (espcially important for POLYNOMIAL_NEARBY)
1134  std::sort(patch_elems.begin(), patch_elems.end());
1135  patch_elems.erase(std::unique(patch_elems.begin(), patch_elems.end()), patch_elems.end());
1136 }
1137 
1138 void
1140 {
1141  const auto & coef =
1142  _pr[_var_name_to_pr_idx[var_name]]->getCachedCoefficients(_patch_elem_ids[var_name]);
1143 
1144  const unsigned dim = _mesh.dimension();
1145 
1146  libMesh::Parameters function_parameters;
1147 
1148  const auto & multi_index = _pr[_var_name_to_pr_idx[var_name]]->multiIndex();
1149 
1150  function_parameters.set<std::vector<std::vector<unsigned int>>>("multi_index") = multi_index;
1151 
1152  std::vector<Real> coef_vec(coef.size());
1153  for (auto i = 0; i < coef.size(); ++i)
1154  coef_vec[i] = coef(i);
1155 
1156  function_parameters.set<std::vector<Real>>("multi_index_coefficients") = coef_vec;
1157  function_parameters.set<unsigned int>("dimension_for_projection") = dim;
1158 
1159  // Define projection function
1160  auto poly_func = [](const Point & p,
1162  const std::string &,
1163  const std::string &) -> libMesh::Number
1164  {
1165  const auto & multi_index =
1166  parameters.get<std::vector<std::vector<unsigned int>>>("multi_index");
1167  const auto & coeffs = parameters.get<std::vector<Real>>("multi_index_coefficients");
1168 
1169  Real val = 0.0;
1170 
1171  for (unsigned int r = 0; r < multi_index.size(); r++)
1172  {
1173  Real monomial = 1.0;
1174  for (unsigned int d = 0; d < multi_index[r].size(); d++)
1175  {
1176  const auto power = multi_index[r][d];
1177  if (power == 0)
1178  continue;
1179 
1180  monomial *= std::pow(p(d), power);
1181  }
1182  val += coeffs[r] * monomial;
1183  }
1184 
1185  return val;
1186  };
1187 
1188  // Define gradient
1189  auto poly_func_grad = [](const Point & p,
1191  const std::string &,
1192  const std::string &) -> libMesh::Gradient
1193  {
1194  const unsigned int dim = parameters.get<unsigned int>("dimension_for_projection");
1195 
1196  const auto & multi_index =
1197  parameters.get<std::vector<std::vector<unsigned int>>>("multi_index");
1198  const auto & coeffs = parameters.get<std::vector<Real>>("multi_index_coefficients");
1199 
1200  libMesh::Gradient grad; // Zero-initialized
1201 
1202  for (unsigned int r = 0; r < multi_index.size(); ++r)
1203  {
1204  const auto & powers = multi_index[r];
1205  const Real coef = coeffs[r];
1206 
1207  for (unsigned int d = 0; d < dim; ++d) // Loop over dimension
1208  {
1209  const auto power_d = powers[d];
1210  if (power_d == 0)
1211  continue;
1212 
1213  // Compute partial derivative in direction d
1214  Real partial = coef * power_d;
1215 
1216  for (unsigned int i = 0; i < powers.size(); ++i)
1217  {
1218  if (i == d)
1219  {
1220  if (powers[i] > 1)
1221  partial *= std::pow(p(i), powers[i] - 1); // reduce power by 1
1222  }
1223  else
1224  {
1225  if (powers[i] > 0)
1226  partial *= std::pow(p(i), powers[i]); // full power
1227  }
1228  }
1229 
1230  grad(d) += partial;
1231  }
1232  }
1233 
1234  return grad;
1235  };
1236 
1237  std::vector<VariableName> var_names = {var_name};
1239  reinitializedElemRange(), poly_func, poly_func_grad, function_parameters, var_names);
1240 }
ConstNodeRange & reinitializedNodeRange()
Range of reinitialized nodes.
const std::vector< UserObjectName > _pr_names
Names of the NodalPatchRecoveryVariable user objects.
virtual void meshChanged(bool intermediate_change, bool contract_mesh, bool clean_refinement_flags)
Update data after a mesh change.
bool _restep
Whether this is a re-step.
int & _t_step_old
Previous time step number.
void gatherSum(T &value)
Gather the parallel sum of the variable passed in.
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
libMesh::ConstElemRange * getActiveLocalElementRange()
Return pointers to range objects for various types of ranges (local nodes, boundary elems...
Definition: MooseMesh.C:1261
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
virtual libMesh::System & getSystem(const std::string &var_name) override
Returns the equation system containing the variable provided.
T & getUserObject(const std::string &name, unsigned int tid=0) const
Get the user object by its name.
std::map< VariableName, unsigned int > _var_name_to_pr_idx
map from variable name to the index of the nodal patch recovery user object in _pr ...
void projectFunctionOnCustomRange(ConstElemRange &elem_range, Number(*func)(const Point &, const libMesh::Parameters &, const std::string &, const std::string &), Gradient(*func_grad)(const Point &, const libMesh::Parameters &, const std::string &, const std::string &), const libMesh::Parameters &params, const std::vector< VariableName > &target_vars)
Project a function onto a range of elements for a given variable.
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:467
NonlinearSystemBase & _nl_sys
Nonlinear system.
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3240
void prepareVariableForReinitialization(const VariableName &var_name, ReinitStrategy reinit_strategy)
NumericVector< Number > & solution()
Definition: SystemBase.h:197
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
unsigned int number() const
Get variable number coming from libMesh.
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
Combine two vector parameters into a single vector of pairs.
static InputParameters validParams()
std::unordered_set< dof_id_type > _reinitialized_elems
Reinitialized elements.
void swap(std::vector< T > &data, const std::size_t idx0, const std::size_t idx1, const libMesh::Parallel::Communicator &comm)
Swap function for serial or distributed vector of data.
Definition: Shuffle.h:495
std::pair< SubdomainID, SubdomainID > SubdomainPair
Moving boundaries associated with each subdomain pair.
std::vector< VariableName > _vars_to_restore_overridden_dofs
List of variable names for which overridden DOF values should be restored.
void initElementStatefulProps()
Reinitialize stateful material properties on range of elements and nodes to be reinitialized.
void gatherPatchElements(const VariableName &var_name, ReinitStrategy reinit_strategy)
Gather patch elements for reinitialized elements based on the reinitialization strategy.
const InputParameters & parameters() const
Get the parameters of the object.
Definition: MooseBase.h:131
std::unordered_map< BoundaryID, BoundaryName > _moving_boundary_names
Boundary names associated with each moving boundary ID.
std::unordered_map< dof_id_type, std::pair< SubdomainID, SubdomainID > > _moved_elems
Cached moved elements for potential restore.
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
MeshBase & mesh
void gatherMovingBoundaryChanges(const std::unordered_map< dof_id_type, std::pair< SubdomainID, SubdomainID >> &moved_elems)
void mooseInfoRepeated(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition: MooseError.h:409
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:163
const Parallel::Communicator & comm() const
The definition of the const_bnd_node_iterator struct.
Definition: MooseMesh.h:2170
std::map< unsigned int, std::pair< std::unordered_set< const Elem * >, std::vector< dof_id_type > > > _evaluable_elems
local evaluable elements before reinitializing the equation systems Key of the map is the system numb...
std::vector< VariableName > _reinit_vars
List of variable names to be initialized for IC.
NumericVector< Number > & solutionOlder()
Definition: SystemBase.h:199
std::set< std::string > _depend_uo
Depend UserObjects that to be used both for determining user object sorting and by AuxKernel for find...
const MaterialWarehouse & getMaterialWarehouse() const
void initElementStatefulProps(const libMesh::ConstElemRange &elem_range, const bool threaded)
Initialize stateful properties for elements in a specific elem_range This is needed when elements/bou...
Base class for a system (of equations)
Definition: SystemBase.h:85
void gatherMovingBoundaryChangesHelper(const Elem *elem, unsigned short side, const Elem *neigh, unsigned short neigh_side)
std::vector< SubdomainID > getSubdomainIDs(const std::vector< SubdomainName > &subdomain_names) const
Get the associated subdomainIDs for the subdomain names that are passed in.
Definition: MooseMesh.C:1759
void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job...
bool _skip_restore_subdomain_changes
Skipping restoring the subdomain changes if the timestep is not advanced.
StoredRange< MeshBase::const_element_iterator, const Elem *> ConstElemRange
void suppressParameter(const std::string &name)
This method suppresses an inherited parameter so that it isn&#39;t required or valid in the derived class...
void updateAMRMovingBoundary(MooseMesh &mesh)
Update boundaries for adaptive mesh from the parent to children elements.
const SubdomainID INVALID_BLOCK_ID
Definition: MooseTypes.C:20
void registerBase(const std::string &value)
This method must be called from every base "Moose System" to create linkage with the Action System...
virtual void modify(const std::unordered_map< dof_id_type, std::pair< SubdomainID, SubdomainID >> &moved_elems)
Modify the element subdomains.
FEProblemBase & _fe_problem
Reference to the FEProblemBase for this user object.
uint8_t processor_id_type
void mooseWarning(Args &&... args) const
virtual libMesh::DofMap & dofMap()
Gets writeable reference to the dof map.
Definition: SystemBase.C:1164
void createMovingBoundaries(MooseMesh &mesh)
Create moving boundaries.
virtual std::vector< VariableName > getVariableNames()
Returns a list of all the variables in the problem (both from the NL and Aux systems.
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:103
void storeOverriddenDofValues(const VariableName &var_name)
Store values from non-reinitialized DoFs on reinitialized elements Stores the value before re-initial...
int & _t_step
The number of the time step.
ReinitStrategy
Strategies for (re)initializing the solution:
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3575
ConstElemRange & reinitializedElemRange()
Range of reinitialized elements.
std::unordered_set< dof_id_type > _semi_local_reinitialized_elems
Semi-local reinitialized elements: ghosted and local reinitialized elements.
char ** sides
virtual unsigned int dimension() const
Returns MeshBase::mesh_dimension(), (not MeshBase::spatial_dimension()!) of the underlying libMesh me...
Definition: MooseMesh.C:3012
void meshChanged() override
Called on this object when the mesh changes.
void setAncestorsSubdomainIDs(Elem *elem, const SubdomainID subdomain_id)
Change the subdomain ID of all ancestor elements.
void applySubdomainChanges(const std::unordered_map< dof_id_type, std::pair< SubdomainID, SubdomainID >> &moved_elems, MooseMesh &mesh)
std::unordered_set< dof_id_type > _reinitialized_nodes
Reinitialized nodes.
virtual const Node * nodePtr(const dof_id_type i) const
Definition: MooseMesh.C:848
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition: MooseMesh.h:93
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
bool subdomainIsReinitialized(SubdomainID id) const
Determine if a subdomain is to be reinitialized.
int _leaf_max_size
KD-tree related members.
std::vector< SubdomainID > _subdomain_ids_to_reinitialize
Reinitialize moved elements whose new subdomain is in this list.
NonlinearSystemBase & getNonlinearSystemBase(const unsigned int sys_num)
virtual bool hasSolutionState(const unsigned int state, Moose::SolutionIterationType iteration_type=Moose::SolutionIterationType::Time) const
Whether or not the system has the solution state (0 = current, 1 = old, 2 = older, etc).
Definition: SystemBase.h:1087
unsigned int number() const
Gets the number of this system.
Definition: SystemBase.C:1158
std::map< VariableName, std::pair< std::vector< dof_id_type >, std::vector< Number > > > _overridden_values_on_reinit_elems
A map from variable name to a pair of: (1) a vector of DOF IDs associated with non-reinitialized node...
AuxiliarySystem & getAuxiliarySystem()
unsigned int systemNumForVariable(const VariableName &variable_name) const
virtual bool hasVariable(const std::string &var_name) const
Query a system for a variable.
Definition: SystemBase.C:852
std::vector< NodalPatchRecoveryVariable * > _pr
Apply initial conditions using polynomial extrapolation.
virtual void close()=0
AuxiliarySystem & _aux_sys
Auxiliary system.
virtual MooseVariable & getStandardVariable(const THREAD_ID tid, const std::string &var_name) override
Returns the variable reference for requested MooseVariable which may be in any system.
std::unordered_map< dof_id_type, std::unordered_map< unsigned short, BoundaryID > > _add_element_sides
Element sides to be added.
void restoreOverriddenDofValues(const VariableName &var_name)
Restore values to non-reinitialized DoFs on reinitialized elements.
SystemBase & _sys
Reference to the system object for this user object.
virtual const SystemBase & getSystemBase(const unsigned int sys_num) const
Get constant reference to a system in this problem.
const SubdomainID ANY_BLOCK_ID
Definition: MooseTypes.C:19
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::unordered_map< dof_id_type, std::unordered_map< unsigned short, BoundaryID > > _remove_element_sides
Element sides to be removed.
bool hasActiveObjects(THREAD_ID tid=0) const
void applyMovingBoundaryChanges(MooseMesh &mesh)
bool nodeIsNewlyReinitialized(dof_id_type node_id) const
Determine if a node is newly reinitialized.
T & set(const std::string &)
std::unique_ptr< ConstBndNodeRange > _reinitialized_bnd_node_range
Range of reinitialized boundary nodes.
std::map< VariableName, std::vector< dof_id_type > > _patch_elem_ids
A map from variable names to their corresponding patch element IDs.
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:281
MooseMesh * _displaced_mesh
Displaced mesh.
std::vector< ReinitStrategy > _reinit_strategy
The strategies used to apply IC on newly activated elements, for each variable.
void applyIC()
Reinitialize variables on range of elements and nodes to be reinitialized.
std::unique_ptr< NumericVector< Number > > current_local_solution
std::unique_ptr< ConstElemRange > _reinitialized_elem_range
Range of reinitialized elements.
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...
virtual void set(const numeric_index_type i, const Number value)=0
void setOldAndOlderSolutions(SystemBase &sys, ConstElemRange &elem_range, ConstBndNodeRange &bnd_node_range)
Set old and older solutions to reinitialized elements and nodes.
std::vector< BoundaryID > getBoundaryIDs(const Elem *const elem, const unsigned short int side) const
Returns a vector of boundary IDs for the requested element on the requested side. ...
Patch recovery from a coupled variable.
void findReinitializedElemsAndNodes(const std::unordered_map< dof_id_type, std::pair< SubdomainID, SubdomainID >> &moved_elems)
Real Number
NumericVector< Number > & solutionOld()
Definition: SystemBase.h:198
processor_id_type processor_id() const
virtual bool isDistributedMesh() const
Returns the final Mesh distribution type.
Definition: MooseMesh.h:1140
virtual std::size_t numSolverSystems() const override
void timestepSetup() override
Gets called at the beginning of the timestep before this object is asked to do its job...
std::unique_ptr< ConstNodeRange > _reinitialized_node_range
Range of reinitialized nodes.
bool isParamSetByUser(const std::string &name) const
Test if the supplied parameter is set by a user, as opposed to not set or set to default.
Definition: MooseBase.h:215
MooseUnits pow(const MooseUnits &, int)
Definition: Units.C:537
std::unordered_map< SubdomainPair, BoundaryID > _moving_boundaries
ConstBndNodeRange & reinitializedBndNodeRange()
Range of reinitialized boundary nodes.
double _nearby_distance_threshold
Radius threshold for the k-d tree neighbor search.
libMesh::StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > * getBoundaryNodeRange()
Definition: MooseMesh.C:1312
void ErrorVector unsigned int
auto index_range(const T &sizable)
std::unordered_map< dof_id_type, std::unordered_map< unsigned short, BoundaryID > > _remove_neighbor_sides
Neighbor sides to be removed.
const Elem & get(const ElemType type_in)
std::unordered_map< dof_id_type, std::unordered_map< unsigned short, BoundaryID > > _add_neighbor_sides
Neighbor sides to be added.
ElementSubdomainModifierBase(const InputParameters &parameters)
void projectInitialConditionOnCustomRange(libMesh::ConstElemRange &elem_range, ConstBndNodeRange &bnd_node_range, const std::optional< std::set< VariableName >> &target_vars=std::nullopt)
Project initial conditions for custom elem_range and bnd_node_range This is needed when elements/boun...
const bool _old_subdomain_reinitialized
Whether to reinitialize moved elements whose old subdomain was in _reinitialize_subdomains.
uint8_t dof_id_type
const std::map< dof_id_type, std::vector< dof_id_type > > & nodeToElemMap()
If not already created, creates a map from every node to all elements to which they are connected...
Definition: MooseMesh.C:1201
const std::set< SubdomainID > & meshSubdomains() const
Returns a read-only reference to the set of subdomains currently present in the Mesh.
Definition: MooseMesh.C:3298
SubdomainID getSubdomainID(const SubdomainName &subdomain_name) const
Get the associated subdomain ID for the subdomain name.
Definition: MooseMesh.C:1753
void extrapolatePolynomial(const VariableName &var_name)
Extrapolate polynomial for the given variable onto the reinitialized elements.
const RemoteElem * remote_elem