LCOV - code coverage report
Current view: top level - src/base - Adaptivity.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 170 182 93.4 %
Date: 2026-05-29 20:35:17 Functions: 17 18 94.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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"
      18             : #include "UpdateErrorVectorsThread.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             : 
      33       61643 : Adaptivity::Adaptivity(FEProblemBase & fe_problem)
      34             :   : ConsoleStreamInterface(fe_problem.getMooseApp()),
      35       61643 :     PerfGraphInterface(fe_problem.getMooseApp().perfGraph(), "Adaptivity"),
      36       61643 :     ParallelObject(fe_problem.getMooseApp()),
      37       61643 :     _fe_problem(fe_problem),
      38      123286 :     _mesh(_fe_problem.mesh()),
      39       61643 :     _mesh_refinement_on(false),
      40       61643 :     _initialized(false),
      41       61643 :     _initial_steps(0),
      42       61643 :     _steps(0),
      43       61643 :     _print_mesh_changed(false),
      44       61643 :     _t(_fe_problem.time()),
      45       61643 :     _step(_fe_problem.timeStep()),
      46       61643 :     _interval(1),
      47       61643 :     _start_time(-std::numeric_limits<Real>::max()),
      48       61643 :     _stop_time(std::numeric_limits<Real>::max()),
      49       61643 :     _cycles_per_step(1),
      50       61643 :     _use_new_system(false),
      51       61643 :     _adaptivity_type(AdaptivityType::H),
      52       61643 :     _max_h_level(0),
      53      308215 :     _recompute_markers_during_cycles(false)
      54             : {
      55       61643 : }
      56             : 
      57       58563 : Adaptivity::~Adaptivity() {}
      58             : 
      59             : void
      60        2247 : 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.
      67        2247 :   _displaced_problem = _fe_problem.getDisplacedProblem();
      68             : 
      69        2247 :   _mesh_refinement = std::make_unique<MeshRefinement>(_mesh);
      70        2247 :   _error = std::make_unique<ErrorVector>();
      71             : 
      72        2247 :   EquationSystems & es = _fe_problem.es();
      73        4494 :   es.parameters.set<bool>("adaptivity") = true;
      74             : 
      75        2247 :   _initial_steps = initial_steps;
      76        2247 :   _steps = steps;
      77             : 
      78        2247 :   _adaptivity_type = adaptivity_type;
      79             : 
      80        2247 :   _mesh_refinement_on = true;
      81             : 
      82        2247 :   if (_adaptivity_type == AdaptivityType::P || _adaptivity_type == AdaptivityType::HP)
      83         228 :     _mesh.doingPRefinement(true);
      84             : 
      85        2247 :   if (_adaptivity_type == AdaptivityType::HP)
      86             :   {
      87          24 :     if (_fe_problem.numSolverSystems() > 1)
      88           0 :       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          24 :     _sibling_coupling = std::make_unique<SiblingCoupling>();
      93          48 :     _fe_problem.getSolverSystem(0).system().get_dof_map().add_algebraic_ghosting_functor(
      94          24 :         *_sibling_coupling);
      95             :   }
      96             : 
      97        4494 :   _mesh_refinement->set_periodic_boundaries_ptr(
      98        2247 :       _fe_problem.getSolverSystem(/*nl_sys=*/0).dofMap().get_periodic_boundaries());
      99             : 
     100        2247 :   if (_displaced_problem)
     101             :   {
     102         124 :     EquationSystems & displaced_es = _displaced_problem->es();
     103         248 :     displaced_es.parameters.set<bool>("adaptivity") = true;
     104             : 
     105         124 :     if (!_displaced_mesh_refinement)
     106         124 :       _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         248 :     _displaced_mesh_refinement->set_periodic_boundaries_ptr(
     113         124 :         _fe_problem.getSolverSystem(/*nl_sys=*/0).dofMap().get_periodic_boundaries());
     114             : 
     115             :     // TODO: This is currently an empty function on the DisplacedProblem... could it be removed?
     116         124 :     _displaced_problem->initAdaptivity();
     117             :   }
     118             : 
     119             :   // indicate the Adaptivity system has been initialized
     120        2247 :   _initialized = true;
     121        2247 : }
     122             : 
     123             : void
     124         422 : Adaptivity::setErrorEstimator(const MooseEnum & error_estimator_name)
     125             : {
     126         422 :   if (error_estimator_name == "KellyErrorEstimator")
     127         416 :     _error_estimator = std::make_unique<KellyErrorEstimator>();
     128           6 :   else if (error_estimator_name == "LaplacianErrorEstimator")
     129           0 :     _error_estimator = std::make_unique<LaplacianErrorEstimator>();
     130           6 :   else if (error_estimator_name == "PatchRecoveryErrorEstimator")
     131           6 :     _error_estimator = std::make_unique<PatchRecoveryErrorEstimator>();
     132             :   else
     133           0 :     mooseError(std::string("Unknown error_estimator selection: ") +
     134           0 :                std::string(error_estimator_name));
     135         422 : }
     136             : 
     137             : void
     138          12 : Adaptivity::setErrorNorm(SystemNorm & sys_norm)
     139             : {
     140             :   mooseAssert(_error_estimator, "error_estimator not initialized. Did you call init_adaptivity()?");
     141          12 :   _error_estimator->error_norm = sys_norm;
     142          12 : }
     143             : 
     144             : bool
     145        5762 : Adaptivity::adaptMesh(std::string marker_name /*=std::string()*/)
     146             : {
     147       28810 :   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        5762 :   if (marker_name.empty())
     151        4882 :     marker_name = _marker_variable_name;
     152             : 
     153        5762 :   bool mesh_changed = false;
     154             : 
     155             :   // If mesh adaptivity is carried out in a distributed (scalable) way
     156        5762 :   bool distributed_adaptivity = false;
     157             : 
     158        5762 :   if (_use_new_system)
     159             :   {
     160        4890 :     if (!marker_name.empty()) // Only flag if a marker variable name has been set
     161             :     {
     162        4857 :       _mesh_refinement->clean_refinement_flags();
     163             : 
     164        4857 :       std::vector<Number> serialized_solution;
     165             : 
     166        4857 :       auto distributed_mesh = dynamic_cast<DistributedMesh *>(&_fe_problem.mesh().getMesh());
     167             : 
     168             :       // Element range
     169        4857 :       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        4857 :       if (distributed_mesh && !distributed_mesh->is_serial_on_zero())
     175             :       {
     176             :         // We update here to make sure local solution is up-to-date
     177         904 :         _fe_problem.getAuxiliarySystem().update();
     178         904 :         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        1808 :         all_elems = std::make_unique<ConstElemRange>(
     191        1808 :             _fe_problem.mesh().getMesh().active_local_elements_begin(),
     192        2712 :             _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             :       {
     196        3953 :         _fe_problem.getAuxiliarySystem().solution().close();
     197        3953 :         _fe_problem.getAuxiliarySystem().solution().localize(serialized_solution);
     198        3953 :         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        7906 :             std::make_unique<ConstElemRange>(_fe_problem.mesh().getMesh().active_elements_begin(),
     208       11859 :                                              _fe_problem.mesh().getMesh().active_elements_end());
     209             :       }
     210             : 
     211             :       FlagElementsThread fet(
     212        4857 :           _fe_problem, serialized_solution, _max_h_level, marker_name, !distributed_adaptivity);
     213        4857 :       Threads::parallel_reduce(*all_elems, fet);
     214        4857 :       _fe_problem.getAuxiliarySystem().solution().close();
     215        4857 :     }
     216             :   }
     217             :   else
     218             :   {
     219         872 :     if (_fe_problem.numSolverSystems() > 1)
     220           0 :       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         872 :     _error_estimator->estimate_error(_fe_problem.getSolverSystem(/*nl_sys=*/0).system(), *_error);
     226             : 
     227             :     // Flag elements to be refined and coarsened
     228         872 :     _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
     233        5762 :   if (_adaptivity_type == AdaptivityType::HP)
     234             :   {
     235          66 :     _hp_coarsen_test = std::make_unique<HPCoarsenTest>();
     236          66 :     _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        5762 :   if (_displaced_problem)
     247         510 :     _displaced_problem->undisplaceMesh();
     248             : 
     249             :   // If markers are added to only local elements,
     250             :   // we sync them here.
     251        5762 :   if (distributed_adaptivity)
     252         904 :     _mesh_refinement->make_flags_parallel_consistent();
     253             : 
     254             :   // Sync flags from the reference mesh
     255        5762 :   if (_displaced_problem)
     256         510 :     for (auto * const displaced_elem :
     257      337800 :          _displaced_problem->mesh().getMesh().active_element_ptr_range())
     258             :     {
     259      336780 :       const auto * const reference_elem = _fe_problem.mesh().elemPtr(displaced_elem->id());
     260      336780 :       displaced_elem->set_refinement_flag(reference_elem->refinement_flag());
     261      336780 :       displaced_elem->set_p_refinement_flag(reference_elem->p_refinement_flag());
     262         510 :     }
     263             : 
     264        5762 :   if (_adaptivity_type == AdaptivityType::P)
     265         253 :     _mesh_refinement->switch_h_to_p_refinement();
     266             : 
     267             :   // Perform refinement and coarsening
     268        5762 :   mesh_changed = _mesh_refinement->refine_and_coarsen_elements();
     269             : 
     270        5762 :   if (_displaced_problem && mesh_changed)
     271             :   {
     272         387 :     if (_adaptivity_type == AdaptivityType::P)
     273           0 :       _displaced_mesh_refinement->switch_h_to_p_refinement();
     274             : 
     275             : #ifndef NDEBUG
     276             :     bool displaced_mesh_changed =
     277             : #endif
     278         387 :         _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        5762 :   if (mesh_changed && _print_mesh_changed)
     285             :   {
     286           0 :     _console << "\nMesh Changed:\n";
     287           0 :     _mesh.printInfo();
     288           0 :     _console << std::flush;
     289             :   }
     290             : 
     291        5762 :   return mesh_changed;
     292        5762 : }
     293             : 
     294             : bool
     295        1055 : Adaptivity::initialAdaptMesh()
     296             : {
     297        1055 :   return adaptMesh(_initial_marker_variable_name);
     298             : }
     299             : 
     300             : void
     301        3698 : 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        3698 :   MeshRefinement mesh_refinement(*mesh);
     308        3698 :   if (level == libMesh::invalid_uint)
     309        3614 :     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        3698 :   if (mesh->skipDeletionRepartitionAfterRefine())
     318             :   {
     319          24 :     mesh->getMesh().skip_partitioning(true);
     320          24 :     mesh->getMesh().allow_remote_element_removal(false);
     321          24 :     mesh->needsRemoteElemDeletion(false);
     322             :   }
     323             : 
     324        3698 :   mesh_refinement.uniformly_refine(level);
     325        3698 : }
     326             : 
     327             : void
     328          10 : Adaptivity::uniformRefineWithProjection()
     329             : {
     330          50 :   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          10 :   MeshRefinement mesh_refinement(_mesh);
     335          10 :   unsigned int level = _mesh.uniformRefineLevel();
     336          10 :   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          20 :   for (unsigned int i = 0; i < level; i++)
     340             :   {
     341             :     // See comment above about why refining the displaced mesh is potentially unsafe.
     342          10 :     if (_displaced_problem)
     343           0 :       _displaced_problem->undisplaceMesh();
     344             : 
     345          10 :     mesh_refinement.uniformly_refine(1);
     346             : 
     347          10 :     if (_displaced_problem)
     348           0 :       displaced_mesh_refinement.uniformly_refine(1);
     349          10 :     _fe_problem.meshChanged(
     350             :         /*intermediate_step=*/false, /*contract_mesh=*/true, /*clean_refinement_flags=*/true);
     351             :   }
     352          10 : }
     353             : 
     354             : void
     355         206 : Adaptivity::setAdaptivityOn(bool state)
     356             : {
     357             :   // check if Adaptivity has been initialized before turning on
     358         206 :   if (state == true && !_initialized)
     359           0 :     mooseError("Mesh adaptivity system not available");
     360             : 
     361         206 :   _mesh_refinement_on = state;
     362         206 : }
     363             : 
     364             : void
     365        2247 : Adaptivity::setTimeActive(Real start_time, Real stop_time)
     366             : {
     367        2247 :   _start_time = start_time;
     368        2247 :   _stop_time = stop_time;
     369        2247 : }
     370             : 
     371             : void
     372        1825 : Adaptivity::setUseNewSystem()
     373             : {
     374        1825 :   _use_new_system = true;
     375        1825 : }
     376             : 
     377             : void
     378        1142 : Adaptivity::setMarkerVariableName(std::string marker_field)
     379             : {
     380        1142 :   _marker_variable_name = marker_field;
     381        1142 : }
     382             : 
     383             : void
     384         670 : Adaptivity::setInitialMarkerVariableName(std::string marker_field)
     385             : {
     386         670 :   _initial_marker_variable_name = marker_field;
     387         670 : }
     388             : 
     389             : ErrorVector &
     390         530 : Adaptivity::getErrorVector(const std::string & indicator_field)
     391             : {
     392             :   // Insert or retrieve error vector
     393         530 :   auto insert_pair = moose_try_emplace(
     394         530 :       _indicator_field_to_error_vector, indicator_field, std::make_unique<ErrorVector>());
     395        1060 :   return *insert_pair.first->second;
     396             : }
     397             : 
     398             : void
     399        6477 : Adaptivity::updateErrorVectors()
     400             : {
     401       32385 :   TIME_SECTION("updateErrorVectors", 5, "Updating Error Vectors");
     402             : 
     403             :   // Resize all of the ErrorVectors in case the mesh has changed
     404        8731 :   for (const auto & it : _indicator_field_to_error_vector)
     405             :   {
     406        2254 :     ErrorVector & vec = *(it.second);
     407        2254 :     vec.assign(_mesh.getMesh().max_elem_id(), 0);
     408             :   }
     409             : 
     410             :   // Fill the vectors with the local contributions
     411        6477 :   UpdateErrorVectorsThread uevt(_fe_problem, _indicator_field_to_error_vector);
     412        6477 :   Threads::parallel_reduce(*_mesh.getActiveLocalElementRange(), uevt);
     413             : 
     414             :   // Now sum across all processors
     415        8731 :   for (const auto & it : _indicator_field_to_error_vector)
     416        2254 :     _fe_problem.comm().sum((std::vector<float> &)*(it.second));
     417        6477 : }
     418             : 
     419             : bool
     420      164935 : Adaptivity::isAdaptivityDue()
     421             : {
     422      164935 :   return _mesh_refinement_on && (_start_time <= _t && _t < _stop_time) && _step % _interval == 0;
     423             : }
     424             : 
     425             : #endif // LIBMESH_ENABLE_AMR

Generated by: LCOV version 1.14