https://mooseframework.inl.gov
Adaptivity.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 
10 #include "Adaptivity.h"
11 
12 #include "AuxiliarySystem.h"
13 #include "DisplacedProblem.h"
14 #include "FEProblem.h"
15 #include "FlagElementsThread.h"
16 #include "MooseMesh.h"
17 #include "NonlinearSystemBase.h"
19 
20 // libMesh
21 #include "libmesh/equation_systems.h"
22 #include "libmesh/kelly_error_estimator.h"
23 #include "libmesh/patch_recovery_error_estimator.h"
24 #include "libmesh/fourth_error_estimators.h"
25 #include "libmesh/parallel.h"
26 #include "libmesh/error_vector.h"
27 #include "libmesh/distributed_mesh.h"
28 
29 using namespace libMesh;
30 
31 #ifdef LIBMESH_ENABLE_AMR
32 
34  : ConsoleStreamInterface(fe_problem.getMooseApp()),
35  PerfGraphInterface(fe_problem.getMooseApp().perfGraph(), "Adaptivity"),
36  ParallelObject(fe_problem.getMooseApp()),
37  _fe_problem(fe_problem),
38  _mesh(_fe_problem.mesh()),
39  _mesh_refinement_on(false),
40  _initialized(false),
41  _initial_steps(0),
42  _steps(0),
43  _print_mesh_changed(false),
44  _t(_fe_problem.time()),
45  _step(_fe_problem.timeStep()),
46  _interval(1),
47  _start_time(-std::numeric_limits<Real>::max()),
48  _stop_time(std::numeric_limits<Real>::max()),
49  _cycles_per_step(1),
50  _use_new_system(false),
51  _adaptivity_type(AdaptivityType::H),
52  _max_h_level(0),
53  _recompute_markers_during_cycles(false)
54 {
55 }
56 
58 
59 void
60 Adaptivity::init(const unsigned int steps,
61  const unsigned int initial_steps,
62  const AdaptivityType adaptivity_type)
63 {
64  // Get the pointer to the DisplacedProblem, this cannot be done at construction because
65  // DisplacedProblem
66  // does not exist at that point.
68 
69  _mesh_refinement = std::make_unique<MeshRefinement>(_mesh);
70  _error = std::make_unique<ErrorVector>();
71 
73  es.parameters.set<bool>("adaptivity") = true;
74 
75  _initial_steps = initial_steps;
76  _steps = steps;
77 
78  _adaptivity_type = adaptivity_type;
79 
80  _mesh_refinement_on = true;
81 
83  _mesh.doingPRefinement(true);
84 
86  {
87  if (_fe_problem.numSolverSystems() > 1)
88  mooseError("HP adaptivity doesn't support multiple solver systems because currently only the "
89  "zeroth solver system solution is analyzed for determining whether to toggle "
90  "h-refinement flags to p-refinement flags");
91 
92  _sibling_coupling = std::make_unique<SiblingCoupling>();
95  }
96 
97  _mesh_refinement->set_periodic_boundaries_ptr(
99 
100  if (_displaced_problem)
101  {
102  EquationSystems & displaced_es = _displaced_problem->es();
103  displaced_es.parameters.set<bool>("adaptivity") = true;
104 
106  _displaced_mesh_refinement = std::make_unique<MeshRefinement>(_displaced_problem->mesh());
107 
108  // The periodic boundaries pointer allows the MeshRefinement
109  // object to determine elements which are "topological" neighbors,
110  // i.e. neighbors across periodic boundaries, for the purposes of
111  // refinement.
112  _displaced_mesh_refinement->set_periodic_boundaries_ptr(
114 
115  // TODO: This is currently an empty function on the DisplacedProblem... could it be removed?
116  _displaced_problem->initAdaptivity();
117  }
118 
119  // indicate the Adaptivity system has been initialized
120  _initialized = true;
121 }
122 
123 void
124 Adaptivity::setErrorEstimator(const MooseEnum & error_estimator_name)
125 {
126  if (error_estimator_name == "KellyErrorEstimator")
127  _error_estimator = std::make_unique<KellyErrorEstimator>();
128  else if (error_estimator_name == "LaplacianErrorEstimator")
129  _error_estimator = std::make_unique<LaplacianErrorEstimator>();
130  else if (error_estimator_name == "PatchRecoveryErrorEstimator")
131  _error_estimator = std::make_unique<PatchRecoveryErrorEstimator>();
132  else
133  mooseError(std::string("Unknown error_estimator selection: ") +
134  std::string(error_estimator_name));
135 }
136 
137 void
139 {
140  mooseAssert(_error_estimator, "error_estimator not initialized. Did you call init_adaptivity()?");
141  _error_estimator->error_norm = sys_norm;
142 }
143 
144 bool
145 Adaptivity::adaptMesh(std::string marker_name /*=std::string()*/)
146 {
147  TIME_SECTION("adaptMesh", 3, "Adapting Mesh");
148 
149  // If the marker name is supplied, use it. Otherwise, use the one in _marker_variable_name
150  if (marker_name.empty())
151  marker_name = _marker_variable_name;
152 
153  bool mesh_changed = false;
154 
155  // If mesh adaptivity is carried out in a distributed (scalable) way
156  bool distributed_adaptivity = false;
157 
158  if (_use_new_system)
159  {
160  if (!marker_name.empty()) // Only flag if a marker variable name has been set
161  {
162  _mesh_refinement->clean_refinement_flags();
163 
164  std::vector<Number> serialized_solution;
165 
166  auto distributed_mesh = dynamic_cast<DistributedMesh *>(&_fe_problem.mesh().getMesh());
167 
168  // Element range
169  std::unique_ptr<ConstElemRange> all_elems;
170  // If the mesh is distributed and we do not do "gather to zero" or "allgather".
171  // Then it is safe to not serialize solution.
172  // Some output idiom (Exodus) will do "gather to zero". That being said,
173  // if you have exodus output on, mesh adaptivty is not scalable.
174  if (distributed_mesh && !distributed_mesh->is_serial_on_zero())
175  {
176  // We update here to make sure local solution is up-to-date
178  distributed_adaptivity = true;
179 
180  // We can not assume that geometric and algebraic ghosting functors cover
181  // the same set of elements/nodes. That being said, in general,
182  // we would expect G(e) > A(e). Here G(e) is the set of elements reserved
183  // by the geometric ghosting functors, and A(e) corresponds to
184  // the one covered by the algebraic ghosting functors.
185  // Therefore, we have to work only on local elements instead of
186  // ghosted + local elements. The ghosted solution might not be enough
187  // for ghosted+local elements. But it is always sufficient for local elements.
188  // After we set markers for all local elements, we will do a global
189  // communication to sync markers for ghosted elements from their owners.
190  all_elems = std::make_unique<ConstElemRange>(
191  _fe_problem.mesh().getMesh().active_local_elements_begin(),
192  _fe_problem.mesh().getMesh().active_local_elements_end());
193  }
194  else // This is not scalable but it might be useful for small-size problems
195  {
197  _fe_problem.getAuxiliarySystem().solution().localize(serialized_solution);
198  distributed_adaptivity = false;
199 
200  // For a replicated mesh or a serialized distributed mesh, the solution
201  // is serialized to everyone. Then we update markers for all active elements.
202  // In this case, we can avoid a global communication to update mesh.
203  // I do not know if it is a good idea, but it the old code behavior.
204  // We might not care about much since a replicated mesh
205  // or a serialized distributed mesh is not scalable anyway.
206  all_elems =
207  std::make_unique<ConstElemRange>(_fe_problem.mesh().getMesh().active_elements_begin(),
208  _fe_problem.mesh().getMesh().active_elements_end());
209  }
210 
211  FlagElementsThread fet(
212  _fe_problem, serialized_solution, _max_h_level, marker_name, !distributed_adaptivity);
213  Threads::parallel_reduce(*all_elems, fet);
215  }
216  }
217  else
218  {
219  if (_fe_problem.numSolverSystems() > 1)
220  mooseError("The old adaptivity system based on libMesh error estimators does not currently "
221  "support multiple solver systems because we currently only use the zeroth solver "
222  "system solution for estimating errors");
223 
224  // Compute the error for each active element
225  _error_estimator->estimate_error(_fe_problem.getSolverSystem(/*nl_sys=*/0).system(), *_error);
226 
227  // Flag elements to be refined and coarsened
228  _mesh_refinement->flag_elements_by_error_fraction(*_error);
229  }
230 
231  // Moving some of h flagged elements to p flagged based on the
232  // local smoothness and prior h & p error estimates
234  {
235  _hp_coarsen_test = std::make_unique<HPCoarsenTest>();
236  _hp_coarsen_test->select_refinement(_fe_problem.getSolverSystem(/*nl_sys=*/0).system());
237  }
238 
239  // If the DisplacedProblem is active, undisplace the DisplacedMesh
240  // in preparation for refinement. We can't safely refine the
241  // DisplacedMesh directly, since the Hilbert keys computed on the
242  // inconsistenly-displaced Mesh are different on different
243  // processors, leading to inconsistent Hilbert keys. We must do
244  // this before the undisplaced Mesh is refined, so that the
245  // element and node numbering is still consistent.
246  if (_displaced_problem)
247  _displaced_problem->undisplaceMesh();
248 
249  // If markers are added to only local elements,
250  // we sync them here.
251  if (distributed_adaptivity)
252  _mesh_refinement->make_flags_parallel_consistent();
253 
254  // Sync flags from the reference mesh
255  if (_displaced_problem)
256  for (auto * const displaced_elem :
257  _displaced_problem->mesh().getMesh().active_element_ptr_range())
258  {
259  const auto * const reference_elem = _fe_problem.mesh().elemPtr(displaced_elem->id());
260  displaced_elem->set_refinement_flag(reference_elem->refinement_flag());
261  displaced_elem->set_p_refinement_flag(reference_elem->p_refinement_flag());
262  }
263 
265  _mesh_refinement->switch_h_to_p_refinement();
266 
267  // Perform refinement and coarsening
268  mesh_changed = _mesh_refinement->refine_and_coarsen_elements();
269 
270  if (_displaced_problem && mesh_changed)
271  {
273  _displaced_mesh_refinement->switch_h_to_p_refinement();
274 
275 #ifndef NDEBUG
276  bool displaced_mesh_changed =
277 #endif
278  _displaced_mesh_refinement->refine_and_coarsen_elements();
279 
280  // Since the undisplaced mesh changed, the displaced mesh better have changed!
281  mooseAssert(displaced_mesh_changed, "Undisplaced mesh changed, but displaced mesh did not!");
282  }
283 
284  if (mesh_changed && _print_mesh_changed)
285  {
286  _console << "\nMesh Changed:\n";
287  _mesh.printInfo();
288  _console << std::flush;
289  }
290 
291  return mesh_changed;
292 }
293 
294 bool
296 {
298 }
299 
300 void
301 Adaptivity::uniformRefine(MooseMesh * mesh, unsigned int level /*=libMesh::invalid_uint*/)
302 {
303  mooseAssert(mesh, "Mesh pointer must not be NULL");
304 
305  // NOTE: we are using a separate object here, since adaptivity may not be on, but we need to be
306  // able to do refinements
307  MeshRefinement mesh_refinement(*mesh);
308  if (level == libMesh::invalid_uint)
309  level = mesh->uniformRefineLevel();
310 
311  // Skip deletion and repartition will make uniform refinements run more
312  // efficiently, but at the same time, there might be extra ghosting elements.
313  // The number of layers of additional ghosting elements depends on the number
314  // of uniform refinement levels. This should happen only when you have a "fine enough"
315  // coarse mesh and want to refine the mesh by a few levels. Otherwise, it might
316  // introduce an unbalanced workload and too large ghosting domain.
317  if (mesh->skipDeletionRepartitionAfterRefine())
318  {
319  mesh->getMesh().skip_partitioning(true);
320  mesh->getMesh().allow_remote_element_removal(false);
321  mesh->needsRemoteElemDeletion(false);
322  }
323 
324  mesh_refinement.uniformly_refine(level);
325 }
326 
327 void
329 {
330  TIME_SECTION("uniformRefineWithProjection", 2, "Uniformly Refining and Reprojecting");
331 
332  // NOTE: we are using a separate object here, since adaptivity may not be on, but we need to be
333  // able to do refinements
334  MeshRefinement mesh_refinement(_mesh);
335  unsigned int level = _mesh.uniformRefineLevel();
336  MeshRefinement displaced_mesh_refinement(_displaced_problem ? _displaced_problem->mesh() : _mesh);
337 
338  // we have to go step by step so EquationSystems::reinit() won't freak out
339  for (unsigned int i = 0; i < level; i++)
340  {
341  // See comment above about why refining the displaced mesh is potentially unsafe.
342  if (_displaced_problem)
343  _displaced_problem->undisplaceMesh();
344 
345  mesh_refinement.uniformly_refine(1);
346 
347  if (_displaced_problem)
348  displaced_mesh_refinement.uniformly_refine(1);
350  /*intermediate_step=*/false, /*contract_mesh=*/true, /*clean_refinement_flags=*/true);
351  }
352 }
353 
354 void
356 {
357  // check if Adaptivity has been initialized before turning on
358  if (state == true && !_initialized)
359  mooseError("Mesh adaptivity system not available");
360 
361  _mesh_refinement_on = state;
362 }
363 
364 void
365 Adaptivity::setTimeActive(Real start_time, Real stop_time)
366 {
367  _start_time = start_time;
368  _stop_time = stop_time;
369 }
370 
371 void
373 {
374  _use_new_system = true;
375 }
376 
377 void
378 Adaptivity::setMarkerVariableName(std::string marker_field)
379 {
380  _marker_variable_name = marker_field;
381 }
382 
383 void
385 {
386  _initial_marker_variable_name = marker_field;
387 }
388 
389 ErrorVector &
390 Adaptivity::getErrorVector(const std::string & indicator_field)
391 {
392  // Insert or retrieve error vector
393  auto insert_pair = moose_try_emplace(
394  _indicator_field_to_error_vector, indicator_field, std::make_unique<ErrorVector>());
395  return *insert_pair.first->second;
396 }
397 
398 void
400 {
401  TIME_SECTION("updateErrorVectors", 5, "Updating Error Vectors");
402 
403  // Resize all of the ErrorVectors in case the mesh has changed
404  for (const auto & it : _indicator_field_to_error_vector)
405  {
406  ErrorVector & vec = *(it.second);
407  vec.assign(_mesh.getMesh().max_elem_id(), 0);
408  }
409 
410  // Fill the vectors with the local contributions
412  Threads::parallel_reduce(*_mesh.getActiveLocalElementRange(), uevt);
413 
414  // Now sum across all processors
415  for (const auto & it : _indicator_field_to_error_vector)
416  _fe_problem.comm().sum((std::vector<float> &)*(it.second));
417 }
418 
419 bool
421 {
422  return _mesh_refinement_on && (_start_time <= _t && _t < _stop_time) && _step % _interval == 0;
423 }
424 
425 #endif // LIBMESH_ENABLE_AMR
bool adaptMesh(std::string marker_name=std::string())
Adapts the mesh based on the error estimator used.
Definition: Adaptivity.C:145
bool initialAdaptMesh()
Used during initial adaptivity.
Definition: Adaptivity.C:295
virtual void meshChanged(bool intermediate_change, bool contract_mesh, bool clean_refinement_flags)
Update data after a mesh change.
std::unique_ptr< libMesh::ErrorVector > _error
Error vector for use with the error estimator.
Definition: Adaptivity.h:283
libMesh::ConstElemRange * getActiveLocalElementRange()
Return pointers to range objects for various types of ranges (local nodes, boundary elems...
Definition: MooseMesh.C:1261
std::shared_ptr< DisplacedProblem > _displaced_problem
Definition: Adaptivity.h:285
Real _stop_time
When adaptivity stops.
Definition: Adaptivity.h:307
std::string _marker_variable_name
Name of the marker variable if using the new adaptivity system.
Definition: Adaptivity.h:315
const unsigned int invalid_uint
std::unique_ptr< libMesh::SiblingCoupling > _sibling_coupling
Sibling coupling object for HP adaptivity for evaluating data on elements&#39; siblings in HPCoarsenTest...
Definition: Adaptivity.h:331
bool _initialized
on/off flag reporting if the adaptivity system has been initialized
Definition: Adaptivity.h:277
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3240
NumericVector< Number > & solution()
Definition: SystemBase.h:197
static void uniformRefine(MooseMesh *mesh, unsigned int level=libMesh::invalid_uint)
Performs uniform refinement of the passed Mesh object.
Definition: Adaptivity.C:301
AdaptivityType
Defines types of mesh adaptivity options available.
Definition: Adaptivity.h:50
void skip_partitioning(bool skip)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
virtual ~Adaptivity()
Definition: Adaptivity.C:57
std::unique_ptr< libMesh::ErrorEstimator > _error_estimator
Error estimator to be used by the apps.
Definition: Adaptivity.h:281
void updateErrorVectors()
Update the ErrorVectors that have been requested through calls to getErrorVector().
Definition: Adaptivity.C:399
std::unique_ptr< libMesh::MeshRefinement > _mesh_refinement
A mesh refinement object to be used either with initial refinement or with Adaptivity.
Definition: Adaptivity.h:279
void setTimeActive(Real start_time, Real stop_time)
Sets the time when the adaptivity is active.
Definition: Adaptivity.C:365
virtual libMesh::System & system()=0
Get the reference to the libMesh system.
MeshBase & mesh
void set_refinement_flag(const RefinementState rflag)
const Parallel::Communicator & comm() const
void setUseNewSystem()
Tells this object we&#39;re using the "new" adaptivity system.
Definition: Adaptivity.C:372
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
std::pair< typename M::iterator, bool > moose_try_emplace(M &m, const typename M::key_type &k, Args &&... args)
Function to mirror the behavior of the C++17 std::map::try_emplace() method (no hint).
Definition: Moose.h:102
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
std::string _initial_marker_variable_name
Name of the initial marker variable if using the new adaptivity system.
Definition: Adaptivity.h:321
MooseMesh & _mesh
Definition: Adaptivity.h:272
auto max(const L &left, const R &right)
Real & _t
Time.
Definition: Adaptivity.h:299
void update()
Update the system (doing libMesh magic)
Definition: SystemBase.C:1244
bool _print_mesh_changed
True if we want to print out info when mesh has changed.
Definition: Adaptivity.h:296
virtual libMesh::DofMap & dofMap()
Gets writeable reference to the dof map.
Definition: SystemBase.C:1164
void allow_remote_element_removal(bool allow)
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3575
PeriodicBoundaries * get_periodic_boundaries()
bool _mesh_refinement_on
on/off flag reporting if the adaptivity is being used
Definition: Adaptivity.h:275
Adaptivity(FEProblemBase &fe_problem)
Definition: Adaptivity.C:33
virtual libMesh::EquationSystems & es() override
An inteface for the _console for outputting to the Console object.
virtual dof_id_type max_elem_id() const=0
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition: MooseMesh.h:93
void setAdaptivityOn(bool state)
Allow adaptivity to be toggled programatically.
Definition: Adaptivity.C:355
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
std::unique_ptr< libMesh::MeshRefinement > _displaced_mesh_refinement
A mesh refinement object for displaced mesh.
Definition: Adaptivity.h:288
unsigned int uniformRefineLevel() const
Returns the level of uniform refinement requested (zero if AMR is disabled).
Definition: MooseMesh.C:3343
void setErrorEstimator(const MooseEnum &error_estimator_name)
Set the error estimator.
Definition: Adaptivity.C:124
AuxiliarySystem & getAuxiliarySystem()
unsigned int _steps
steps of adaptivity to perform
Definition: Adaptivity.h:293
Interface for objects interacting with the PerfGraph.
virtual void close()=0
void uniformRefineWithProjection()
Performs uniform refinement on the meshes in the current object.
Definition: Adaptivity.C:328
void printInfo(std::ostream &os=libMesh::out, const unsigned int verbosity=0) const
Calls print_info() on the underlying Mesh.
Definition: MooseMesh.C:3589
void init(const unsigned int steps, const unsigned int initial_steps, const AdaptivityType adaptivity_type)
Initialize and turn on adaptivity for the simulation.
Definition: Adaptivity.C:60
unsigned int _initial_steps
the number of adaptivity steps to do at the beginning of simulation
Definition: Adaptivity.h:291
bool _use_new_system
Whether or not to use the "new" adaptivity system.
Definition: Adaptivity.h:312
std::map< std::string, std::unique_ptr< libMesh::ErrorVector > > _indicator_field_to_error_vector
Stores pointers to ErrorVectors associated with indicator field names.
Definition: Adaptivity.h:337
unsigned int _interval
intreval between adaptivity runs
Definition: Adaptivity.h:303
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual std::shared_ptr< const DisplacedProblem > getDisplacedProblem() const
AdaptivityType _adaptivity_type
Type of mesh adaptivity.
Definition: Adaptivity.h:318
libMesh::ErrorVector & getErrorVector(const std::string &indicator_field)
Get an ErrorVector that will be filled up with values corresponding to the indicator field name passe...
Definition: Adaptivity.C:390
T & set(const std::string &)
virtual MooseMesh & mesh() override
SolverSystem & getSolverSystem(unsigned int sys_num)
Get non-constant reference to a solver system.
Real _start_time
When adaptivity start.
Definition: Adaptivity.h:305
int & _step
Time Step.
Definition: Adaptivity.h:301
bool isAdaptivityDue()
Query if an adaptivity step should be performed at the current time / time step.
Definition: Adaptivity.C:420
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
void setMarkerVariableName(std::string marker_field)
Sets the name of the field variable to actually use to flag elements for refinement / coarsening...
Definition: Adaptivity.C:378
void doingPRefinement(bool doing_p_refinement)
Indicate whether the kind of adaptivity we&#39;re doing includes p-refinement.
Definition: MooseMesh.h:1501
unsigned int _max_h_level
The maximum number of refinement levels.
Definition: Adaptivity.h:324
void add_algebraic_ghosting_functor(GhostingFunctor &evaluable_functor, bool to_mesh=true)
virtual std::size_t numSolverSystems() const override
const DofMap & get_dof_map() const
void setErrorNorm(libMesh::SystemNorm &sys_norm)
Set the error norm (FIXME: improve description)
Definition: Adaptivity.C:138
void setInitialMarkerVariableName(std::string marker_field)
Sets the name of the field variable to actually use to flag elements for initial refinement / coarsen...
Definition: Adaptivity.C:384
FEProblemBase & _fe_problem
Definition: Adaptivity.h:271
std::unique_ptr< libMesh::HPCoarsenTest > _hp_coarsen_test
Object for HP adaptivity.
Definition: Adaptivity.h:334
void uniformly_refine(unsigned int n=1)
virtual void localize(std::vector< T > &v_local) const=0