LCOV - code coverage report
Current view: top level - src/base - Adaptivity.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 158 168 94.0 %
Date: 2025-07-17 01:28:37 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       57974 : Adaptivity::Adaptivity(FEProblemBase & fe_problem)
      34             :   : ConsoleStreamInterface(fe_problem.getMooseApp()),
      35       57974 :     PerfGraphInterface(fe_problem.getMooseApp().perfGraph(), "Adaptivity"),
      36       57974 :     ParallelObject(fe_problem.getMooseApp()),
      37       57974 :     _fe_problem(fe_problem),
      38      115948 :     _mesh(_fe_problem.mesh()),
      39       57974 :     _mesh_refinement_on(false),
      40       57974 :     _initialized(false),
      41       57974 :     _initial_steps(0),
      42       57974 :     _steps(0),
      43       57974 :     _print_mesh_changed(false),
      44       57974 :     _t(_fe_problem.time()),
      45       57974 :     _step(_fe_problem.timeStep()),
      46       57974 :     _interval(1),
      47       57974 :     _start_time(-std::numeric_limits<Real>::max()),
      48       57974 :     _stop_time(std::numeric_limits<Real>::max()),
      49       57974 :     _cycles_per_step(1),
      50       57974 :     _use_new_system(false),
      51       57974 :     _max_h_level(0),
      52      173922 :     _recompute_markers_during_cycles(false)
      53             : {
      54       57974 : }
      55             : 
      56       53701 : Adaptivity::~Adaptivity() {}
      57             : 
      58             : void
      59        2202 : Adaptivity::init(const unsigned int steps,
      60             :                  const unsigned int initial_steps,
      61             :                  const bool p_refinement)
      62             : {
      63             :   // Get the pointer to the DisplacedProblem, this cannot be done at construction because
      64             :   // DisplacedProblem
      65             :   // does not exist at that point.
      66        2202 :   _displaced_problem = _fe_problem.getDisplacedProblem();
      67             : 
      68        2202 :   _mesh_refinement = std::make_unique<MeshRefinement>(_mesh);
      69        2202 :   _error = std::make_unique<ErrorVector>();
      70             : 
      71        2202 :   EquationSystems & es = _fe_problem.es();
      72        2202 :   es.parameters.set<bool>("adaptivity") = true;
      73             : 
      74        2202 :   _initial_steps = initial_steps;
      75        2202 :   _steps = steps;
      76        2202 :   _p_refinement_flag = p_refinement;
      77        2202 :   _mesh_refinement_on = true;
      78             : 
      79        2202 :   if (_p_refinement_flag)
      80         204 :     _mesh.doingPRefinement(true);
      81             : 
      82        4404 :   _mesh_refinement->set_periodic_boundaries_ptr(
      83        2202 :       _fe_problem.getNonlinearSystemBase(/*nl_sys=*/0).dofMap().get_periodic_boundaries());
      84             : 
      85             :   // displaced problem
      86        2202 :   if (_displaced_problem != nullptr)
      87             :   {
      88         130 :     EquationSystems & displaced_es = _displaced_problem->es();
      89         130 :     displaced_es.parameters.set<bool>("adaptivity") = true;
      90             : 
      91         130 :     if (!_displaced_mesh_refinement)
      92         130 :       _displaced_mesh_refinement = std::make_unique<MeshRefinement>(_displaced_problem->mesh());
      93             : 
      94             :     // The periodic boundaries pointer allows the MeshRefinement
      95             :     // object to determine elements which are "topological" neighbors,
      96             :     // i.e. neighbors across periodic boundaries, for the purposes of
      97             :     // refinement.
      98         260 :     _displaced_mesh_refinement->set_periodic_boundaries_ptr(
      99         130 :         _fe_problem.getNonlinearSystemBase(/*nl_sys=*/0).dofMap().get_periodic_boundaries());
     100             : 
     101             :     // TODO: This is currently an empty function on the DisplacedProblem... could it be removed?
     102         130 :     _displaced_problem->initAdaptivity();
     103             :   }
     104             : 
     105             :   // indicate the Adaptivity system has been initialized
     106        2202 :   _initialized = true;
     107        2202 : }
     108             : 
     109             : void
     110         426 : Adaptivity::setErrorEstimator(const MooseEnum & error_estimator_name)
     111             : {
     112         426 :   if (error_estimator_name == "KellyErrorEstimator")
     113         417 :     _error_estimator = std::make_unique<KellyErrorEstimator>();
     114           9 :   else if (error_estimator_name == "LaplacianErrorEstimator")
     115           0 :     _error_estimator = std::make_unique<LaplacianErrorEstimator>();
     116           9 :   else if (error_estimator_name == "PatchRecoveryErrorEstimator")
     117           9 :     _error_estimator = std::make_unique<PatchRecoveryErrorEstimator>();
     118             :   else
     119           0 :     mooseError(std::string("Unknown error_estimator selection: ") +
     120           0 :                std::string(error_estimator_name));
     121         426 : }
     122             : 
     123             : void
     124          12 : Adaptivity::setErrorNorm(SystemNorm & sys_norm)
     125             : {
     126             :   mooseAssert(_error_estimator, "error_estimator not initialized. Did you call init_adaptivity()?");
     127          12 :   _error_estimator->error_norm = sys_norm;
     128          12 : }
     129             : 
     130             : bool
     131        5725 : Adaptivity::adaptMesh(std::string marker_name /*=std::string()*/)
     132             : {
     133        5725 :   TIME_SECTION("adaptMesh", 3, "Adapting Mesh");
     134             : 
     135             :   // If the marker name is supplied, use it. Otherwise, use the one in _marker_variable_name
     136        5725 :   if (marker_name.empty())
     137        4870 :     marker_name = _marker_variable_name;
     138             : 
     139        5725 :   bool mesh_changed = false;
     140             : 
     141             :   // If mesh adaptivity is carried out in a distributed (scalable) way
     142        5725 :   bool distributed_adaptivity = false;
     143             : 
     144        5725 :   if (_use_new_system)
     145             :   {
     146        4866 :     if (!marker_name.empty()) // Only flag if a marker variable name has been set
     147             :     {
     148        4833 :       _mesh_refinement->clean_refinement_flags();
     149             : 
     150        4833 :       std::vector<Number> serialized_solution;
     151             : 
     152        4833 :       auto distributed_mesh = dynamic_cast<DistributedMesh *>(&_fe_problem.mesh().getMesh());
     153             : 
     154             :       // Element range
     155        4833 :       std::unique_ptr<ConstElemRange> all_elems;
     156             :       // If the mesh is distributed and we do not do "gather to zero" or "allgather".
     157             :       // Then it is safe to not serialize solution.
     158             :       // Some output idiom (Exodus) will do "gather to zero". That being said,
     159             :       // if you have exodus output on, mesh adaptivty is not scalable.
     160        4833 :       if (distributed_mesh && !distributed_mesh->is_serial_on_zero())
     161             :       {
     162             :         // We update here to make sure local solution is up-to-date
     163         894 :         _fe_problem.getAuxiliarySystem().update();
     164         894 :         distributed_adaptivity = true;
     165             : 
     166             :         // We can not assume that geometric and algebraic ghosting functors cover
     167             :         // the same set of elements/nodes. That being said, in general,
     168             :         // we would expect G(e) > A(e). Here G(e) is the set of elements reserved
     169             :         // by the geometric ghosting functors, and A(e) corresponds to
     170             :         // the one covered by the algebraic ghosting functors.
     171             :         // Therefore, we have to work only on local elements instead of
     172             :         // ghosted + local elements. The ghosted solution might not be enough
     173             :         // for ghosted+local elements. But it is always sufficient for local elements.
     174             :         // After we set markers for all local elements, we will do a global
     175             :         // communication to sync markers for ghosted elements from their owners.
     176        1788 :         all_elems = std::make_unique<ConstElemRange>(
     177        1788 :             _fe_problem.mesh().getMesh().active_local_elements_begin(),
     178        2682 :             _fe_problem.mesh().getMesh().active_local_elements_end());
     179             :       }
     180             :       else // This is not scalable but it might be useful for small-size problems
     181             :       {
     182        3939 :         _fe_problem.getAuxiliarySystem().solution().close();
     183        3939 :         _fe_problem.getAuxiliarySystem().solution().localize(serialized_solution);
     184        3939 :         distributed_adaptivity = false;
     185             : 
     186             :         // For a replicated mesh or a serialized distributed mesh, the solution
     187             :         // is serialized to everyone. Then we update markers for all active elements.
     188             :         // In this case, we can avoid a global communication to update mesh.
     189             :         // I do not know if it is a good idea, but it the old code behavior.
     190             :         // We might not care about much since a replicated mesh
     191             :         // or a serialized distributed mesh is not scalable anyway.
     192             :         all_elems =
     193        7878 :             std::make_unique<ConstElemRange>(_fe_problem.mesh().getMesh().active_elements_begin(),
     194       11817 :                                              _fe_problem.mesh().getMesh().active_elements_end());
     195             :       }
     196             : 
     197             :       FlagElementsThread fet(
     198        4833 :           _fe_problem, serialized_solution, _max_h_level, marker_name, !distributed_adaptivity);
     199        4833 :       Threads::parallel_reduce(*all_elems, fet);
     200        4833 :       _fe_problem.getAuxiliarySystem().solution().close();
     201        4833 :     }
     202             :   }
     203             :   else
     204             :   {
     205             :     // Compute the error for each active element
     206        1718 :     _error_estimator->estimate_error(_fe_problem.getNonlinearSystemBase(/*nl_sys=*/0).system(),
     207         859 :                                      *_error);
     208             : 
     209             :     // Flag elements to be refined and coarsened
     210         859 :     _mesh_refinement->flag_elements_by_error_fraction(*_error);
     211             : 
     212         859 :     if (_displaced_problem)
     213             :       // Reuse the error vector and refine the displaced mesh
     214          31 :       _displaced_mesh_refinement->flag_elements_by_error_fraction(*_error);
     215             :   }
     216             : 
     217             :   // If the DisplacedProblem is active, undisplace the DisplacedMesh
     218             :   // in preparation for refinement.  We can't safely refine the
     219             :   // DisplacedMesh directly, since the Hilbert keys computed on the
     220             :   // inconsistenly-displaced Mesh are different on different
     221             :   // processors, leading to inconsistent Hilbert keys.  We must do
     222             :   // this before the undisplaced Mesh is refined, so that the
     223             :   // element and node numbering is still consistent.
     224        5725 :   if (_displaced_problem)
     225         524 :     _displaced_problem->undisplaceMesh();
     226             : 
     227             :   // If markers are added to only local elements,
     228             :   // we sync them here.
     229        5725 :   if (distributed_adaptivity)
     230         894 :     _mesh_refinement->make_flags_parallel_consistent();
     231             : 
     232        5725 :   if (_p_refinement_flag)
     233         253 :     _mesh_refinement->switch_h_to_p_refinement();
     234             : 
     235             :   // Perform refinement and coarsening
     236        5725 :   mesh_changed = _mesh_refinement->refine_and_coarsen_elements();
     237             : 
     238        5725 :   if (_displaced_problem && mesh_changed)
     239             :   {
     240             :     // If markers are added to only local elements,
     241             :     // we sync them here.
     242         399 :     if (distributed_adaptivity)
     243         129 :       _displaced_mesh_refinement->make_flags_parallel_consistent();
     244             : 
     245         399 :     if (_p_refinement_flag)
     246           0 :       _displaced_mesh_refinement->switch_h_to_p_refinement();
     247             : 
     248             : #ifndef NDEBUG
     249             :     bool displaced_mesh_changed =
     250             : #endif
     251         399 :         _displaced_mesh_refinement->refine_and_coarsen_elements();
     252             : 
     253             :     // Since the undisplaced mesh changed, the displaced mesh better have changed!
     254             :     mooseAssert(displaced_mesh_changed, "Undisplaced mesh changed, but displaced mesh did not!");
     255             :   }
     256             : 
     257        5725 :   if (mesh_changed && _print_mesh_changed)
     258             :   {
     259           0 :     _console << "\nMesh Changed:\n";
     260           0 :     _mesh.printInfo();
     261           0 :     _console << std::flush;
     262             :   }
     263             : 
     264        5725 :   return mesh_changed;
     265        5725 : }
     266             : 
     267             : bool
     268        1036 : Adaptivity::initialAdaptMesh()
     269             : {
     270        1036 :   return adaptMesh(_initial_marker_variable_name);
     271             : }
     272             : 
     273             : void
     274        4803 : Adaptivity::uniformRefine(MooseMesh * mesh, unsigned int level /*=libMesh::invalid_uint*/)
     275             : {
     276             :   mooseAssert(mesh, "Mesh pointer must not be NULL");
     277             : 
     278             :   // NOTE: we are using a separate object here, since adaptivity may not be on, but we need to be
     279             :   // able to do refinements
     280        4803 :   MeshRefinement mesh_refinement(*mesh);
     281        4803 :   if (level == libMesh::invalid_uint)
     282        4719 :     level = mesh->uniformRefineLevel();
     283             : 
     284             :   // Skip deletion and repartition will make uniform refinements run more
     285             :   // efficiently, but at the same time, there might be extra ghosting elements.
     286             :   // The number of layers of additional ghosting elements depends on the number
     287             :   // of uniform refinement levels. This should happen only when you have a "fine enough"
     288             :   // coarse mesh and want to refine the mesh by a few levels. Otherwise, it might
     289             :   // introduce an unbalanced workload and too large ghosting domain.
     290        4803 :   if (mesh->skipDeletionRepartitionAfterRefine())
     291             :   {
     292          24 :     mesh->getMesh().skip_partitioning(true);
     293          24 :     mesh->getMesh().allow_remote_element_removal(false);
     294          24 :     mesh->needsRemoteElemDeletion(false);
     295             :   }
     296             : 
     297        4803 :   mesh_refinement.uniformly_refine(level);
     298        4803 : }
     299             : 
     300             : void
     301          10 : Adaptivity::uniformRefineWithProjection()
     302             : {
     303          10 :   TIME_SECTION("uniformRefineWithProjection", 2, "Uniformly Refining and Reprojecting");
     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          10 :   MeshRefinement mesh_refinement(_mesh);
     308          10 :   unsigned int level = _mesh.uniformRefineLevel();
     309          10 :   MeshRefinement displaced_mesh_refinement(_displaced_problem ? _displaced_problem->mesh() : _mesh);
     310             : 
     311             :   // we have to go step by step so EquationSystems::reinit() won't freak out
     312          20 :   for (unsigned int i = 0; i < level; i++)
     313             :   {
     314             :     // See comment above about why refining the displaced mesh is potentially unsafe.
     315          10 :     if (_displaced_problem)
     316           0 :       _displaced_problem->undisplaceMesh();
     317             : 
     318          10 :     mesh_refinement.uniformly_refine(1);
     319             : 
     320          10 :     if (_displaced_problem)
     321           0 :       displaced_mesh_refinement.uniformly_refine(1);
     322          10 :     _fe_problem.meshChanged(
     323             :         /*intermediate_step=*/false, /*contract_mesh=*/true, /*clean_refinement_flags=*/true);
     324             :   }
     325          10 : }
     326             : 
     327             : void
     328         203 : Adaptivity::setAdaptivityOn(bool state)
     329             : {
     330             :   // check if Adaptivity has been initialized before turning on
     331         203 :   if (state == true && !_initialized)
     332           0 :     mooseError("Mesh adaptivity system not available");
     333             : 
     334         203 :   _mesh_refinement_on = state;
     335         203 : }
     336             : 
     337             : void
     338        2202 : Adaptivity::setTimeActive(Real start_time, Real stop_time)
     339             : {
     340        2202 :   _start_time = start_time;
     341        2202 :   _stop_time = stop_time;
     342        2202 : }
     343             : 
     344             : void
     345        1776 : Adaptivity::setUseNewSystem()
     346             : {
     347        1776 :   _use_new_system = true;
     348        1776 : }
     349             : 
     350             : void
     351        1140 : Adaptivity::setMarkerVariableName(std::string marker_field)
     352             : {
     353        1140 :   _marker_variable_name = marker_field;
     354        1140 : }
     355             : 
     356             : void
     357         654 : Adaptivity::setInitialMarkerVariableName(std::string marker_field)
     358             : {
     359         654 :   _initial_marker_variable_name = marker_field;
     360         654 : }
     361             : 
     362             : ErrorVector &
     363         503 : Adaptivity::getErrorVector(const std::string & indicator_field)
     364             : {
     365             :   // Insert or retrieve error vector
     366         503 :   auto insert_pair = moose_try_emplace(
     367         503 :       _indicator_field_to_error_vector, indicator_field, std::make_unique<ErrorVector>());
     368        1006 :   return *insert_pair.first->second;
     369             : }
     370             : 
     371             : void
     372        6428 : Adaptivity::updateErrorVectors()
     373             : {
     374        6428 :   TIME_SECTION("updateErrorVectors", 5, "Updating Error Vectors");
     375             : 
     376             :   // Resize all of the ErrorVectors in case the mesh has changed
     377        8624 :   for (const auto & it : _indicator_field_to_error_vector)
     378             :   {
     379        2196 :     ErrorVector & vec = *(it.second);
     380        2196 :     vec.assign(_mesh.getMesh().max_elem_id(), 0);
     381             :   }
     382             : 
     383             :   // Fill the vectors with the local contributions
     384        6428 :   UpdateErrorVectorsThread uevt(_fe_problem, _indicator_field_to_error_vector);
     385        6428 :   Threads::parallel_reduce(*_mesh.getActiveLocalElementRange(), uevt);
     386             : 
     387             :   // Now sum across all processors
     388        8624 :   for (const auto & it : _indicator_field_to_error_vector)
     389        2196 :     _fe_problem.comm().sum((std::vector<float> &)*(it.second));
     390        6428 : }
     391             : 
     392             : bool
     393      163150 : Adaptivity::isAdaptivityDue()
     394             : {
     395      163150 :   return _mesh_refinement_on && (_start_time <= _t && _t < _stop_time) && _step % _interval == 0;
     396             : }
     397             : 
     398             : #endif // LIBMESH_ENABLE_AMR

Generated by: LCOV version 1.14