LCOV - code coverage report
Current view: top level - src/dirackernels - DiracKernelBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 135 159 84.9 %
Date: 2025-07-17 01:28:37 Functions: 11 12 91.7 %
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             : // Moose includes
      11             : #include "DiracKernelBase.h"
      12             : #include "Assembly.h"
      13             : #include "SystemBase.h"
      14             : #include "Problem.h"
      15             : #include "MooseMesh.h"
      16             : 
      17             : #include "libmesh/quadrature.h"
      18             : 
      19             : InputParameters
      20      158713 : DiracKernelBase::validParams()
      21             : {
      22      158713 :   InputParameters params = ResidualObject::validParams();
      23      158713 :   params += MaterialPropertyInterface::validParams();
      24      158713 :   params += BlockRestrictable::validParams();
      25             : 
      26      476139 :   params.addParam<bool>("use_displaced_mesh",
      27      317426 :                         false,
      28             :                         "Whether or not this object should use the displaced mesh for computation. "
      29             :                         "Note that in the case this is true but no displacements are provided in "
      30             :                         "the Mesh block the undisplaced mesh will still be used.");
      31             : 
      32      476139 :   params.addParam<bool>(
      33             :       "drop_duplicate_points",
      34      317426 :       true,
      35             :       "By default points added to a DiracKernel are dropped if a point at the same location"
      36             :       "has been added before. If this option is set to false duplicate points are retained"
      37             :       "and contribute to residual and Jacobian.");
      38             : 
      39      158713 :   MooseEnum point_not_found_behavior("ERROR WARNING IGNORE", "IGNORE");
      40      158713 :   params.addParam<MooseEnum>(
      41             :       "point_not_found_behavior",
      42             :       point_not_found_behavior,
      43             :       "By default (IGNORE), it is ignored if an added point cannot be located in the "
      44             :       "specified subdomains. If this option is set to ERROR, this situation will result in an "
      45             :       "error. If this option is set to WARNING, then a warning will be issued.");
      46             : 
      47      476139 :   params.addParam<bool>("allow_moving_sources",
      48      317426 :                         false,
      49             :                         "If true, allow Dirac sources to move, even if the mesh does not move, "
      50             :                         "during the simulation.");
      51             : 
      52      158713 :   params.addParamNamesToGroup("use_displaced_mesh drop_duplicate_points", "Advanced");
      53      158713 :   params.declareControllable("enable");
      54      158713 :   params.registerSystemAttributeName("DiracKernel");
      55             : 
      56      317426 :   return params;
      57      158713 : }
      58             : 
      59         950 : DiracKernelBase::DiracKernelBase(const InputParameters & parameters)
      60             :   : ResidualObject(parameters),
      61             :     CoupleableMooseVariableDependencyIntermediateInterface(this, false),
      62             :     MaterialPropertyInterface(this, Moose::EMPTY_BLOCK_IDS, Moose::EMPTY_BOUNDARY_IDS),
      63             :     GeometricSearchInterface(this),
      64             :     BlockRestrictable(this),
      65        1900 :     _current_elem(_assembly.elem()),
      66         950 :     _coord_sys(_assembly.coordSystem()),
      67         950 :     _dirac_kernel_info(_subproblem.diracKernelInfo()),
      68         950 :     _q_point(_assembly.qPoints()),
      69         950 :     _physical_point(_assembly.physicalPoints()),
      70         950 :     _qrule(_assembly.qRule()),
      71         950 :     _JxW(_assembly.JxW()),
      72         950 :     _drop_duplicate_points(parameters.get<bool>("drop_duplicate_points")),
      73         950 :     _point_not_found_behavior(
      74         950 :         parameters.get<MooseEnum>("point_not_found_behavior").getEnum<PointNotFoundBehavior>()),
      75        2850 :     _allow_moving_sources(getParam<bool>("allow_moving_sources"))
      76             : {
      77             :   // Stateful material properties are not allowed on DiracKernels
      78         950 :   statefulPropertiesAllowed(false);
      79         950 : }
      80             : 
      81             : Real
      82           0 : DiracKernelBase::computeQpOffDiagJacobian(unsigned int /*jvar*/)
      83             : {
      84           0 :   return 0;
      85             : }
      86             : 
      87             : void
      88      368139 : DiracKernelBase::addPoint(const Elem * elem, Point p, unsigned /*id*/)
      89             : {
      90      368139 :   if (!elem || !hasBlocks(elem->subdomain_id()))
      91             :   {
      92       12838 :     std::stringstream msg;
      93       12838 :     msg << "Point " << p << " not found in block(s) " << Moose::stringify(blockIDs(), ", ") << ".";
      94       12838 :     switch (_point_not_found_behavior)
      95             :     {
      96           4 :       case PointNotFoundBehavior::ERROR:
      97           4 :         mooseError(msg.str());
      98             :         break;
      99         329 :       case PointNotFoundBehavior::WARNING:
     100         329 :         mooseDoOnce(mooseWarning(msg.str()));
     101         329 :         break;
     102       12505 :       case PointNotFoundBehavior::IGNORE:
     103       12505 :         break;
     104           0 :       default:
     105           0 :         mooseError("Internal enum error.");
     106             :     }
     107       12834 :     return;
     108       12834 :   }
     109             : 
     110      355301 :   if (elem->processor_id() != processor_id())
     111           0 :     return;
     112             : 
     113      355301 :   _dirac_kernel_info.addPoint(elem, p);
     114      355301 :   _local_dirac_kernel_info.addPoint(elem, p);
     115             : }
     116             : 
     117             : const Elem *
     118       76859 : DiracKernelBase::addPoint(Point p, unsigned id)
     119             : {
     120             :   // Make sure that this method was called with the same id on all
     121             :   // processors.  It's an extra communication, though, so let's only
     122             :   // do it in DEBUG mode.
     123             :   libmesh_assert(comm().verify(id));
     124             : 
     125       76859 :   if (id != libMesh::invalid_uint)
     126       39657 :     return addPointWithValidId(p, id);
     127             : 
     128             :   // If id == libMesh::invalid_uint (the default), the user is not
     129             :   // enabling caching when they add Dirac points.  So all we can do is
     130             :   // the PointLocator lookup, and call the other addPoint() method.
     131       37202 :   const Elem * elem = _dirac_kernel_info.findPoint(p, _mesh, blockIDs());
     132       37202 :   addPoint(elem, p, id);
     133       37202 :   return elem;
     134             : }
     135             : 
     136             : const Elem *
     137       39657 : DiracKernelBase::addPointWithValidId(Point p, unsigned id)
     138             : {
     139             :   // The Elem we'll eventually return.  We can't return early on some
     140             :   // processors, because we may need to call parallel_only() functions in
     141             :   // the remainder of this scope.
     142       39657 :   const Elem * return_elem = NULL;
     143             : 
     144             :   // May be set if the Elem is found in our cache, otherwise stays as NULL.
     145       39657 :   const Elem * cached_elem = NULL;
     146             : 
     147             :   // OK, the user gave us an ID, let's see if we already have it...
     148       39657 :   point_cache_t::iterator it = _point_cache.find(id);
     149             : 
     150             :   // Was the point found in a _point_cache on at least one processor?
     151       39657 :   unsigned int i_found_it = static_cast<unsigned int>(it != _point_cache.end());
     152       39657 :   unsigned int we_found_it = i_found_it;
     153       39657 :   comm().max(we_found_it);
     154             : 
     155             :   // If nobody found it in their local caches, it means we need to
     156             :   // do the PointLocator look-up and update the caches.  This is
     157             :   // safe, because all processors have the same value of we_found_it.
     158       39657 :   if (!we_found_it)
     159             :   {
     160        3929 :     const Elem * elem = _dirac_kernel_info.findPoint(p, _mesh, blockIDs());
     161             : 
     162             :     // Only add the point to the cache on this processor if the Elem is local
     163        3929 :     if (elem && (elem->processor_id() == processor_id()))
     164             :     {
     165             :       // Add the point to the cache...
     166         588 :       _point_cache[id] = std::make_pair(elem, p);
     167             : 
     168             :       // ... and to the reverse cache.
     169         588 :       std::vector<std::pair<Point, unsigned>> & points = _reverse_point_cache[elem];
     170         588 :       points.push_back(std::make_pair(p, id));
     171             :     }
     172             : 
     173             :     // Call the other addPoint() method.  This method ignores non-local
     174             :     // and NULL elements automatically.
     175        3929 :     addPoint(elem, p, id);
     176        3925 :     return_elem = elem;
     177             :   }
     178             : 
     179             :   // If the point was found in a cache, but not my cache, I'm not
     180             :   // responsible for it.
     181             :   //
     182             :   // We can't return early here: then we aren't allowed to call any more
     183             :   // parallel_only() functions in the remainder of this function!
     184       39653 :   if (we_found_it && !i_found_it)
     185       10066 :     return_elem = NULL;
     186             : 
     187             :   // This flag may be set by the processor that cached the Elem because it
     188             :   // needs to call findPoint() (due to moving mesh, etc.). If so, we will
     189             :   // call it at the end of the while loop below.
     190       39653 :   bool i_need_find_point = false;
     191             : 
     192             :   // Now that we only cache local data, some processors may enter
     193             :   // this if statement and some may not.  Therefore we can't call
     194             :   // any parallel_only() functions inside this if statement.
     195       39653 :   while (i_found_it)
     196             :   {
     197             :     // We have something cached, now make sure it's actually the same Point.
     198             :     // TODO: we should probably use this same comparison in the DiracKernelInfo code!
     199       25662 :     Point cached_point = (it->second).second;
     200             : 
     201       25662 :     if (cached_point.relative_fuzzy_equals(p))
     202             :     {
     203             :       // Find the cached element associated to this point
     204       25658 :       cached_elem = (it->second).first;
     205             : 
     206             :       // If the cached element's processor ID doesn't match ours, we
     207             :       // are no longer responsible for caching it.  This can happen
     208             :       // due to adaptivity...
     209       25658 :       if (cached_elem->processor_id() != processor_id())
     210             :       {
     211             :         // Update the caches, telling them to drop the cached Elem.
     212             :         // Analogously to the rest of the DiracKernel system, we
     213             :         // also return NULL because the Elem is non-local.
     214           0 :         updateCaches(cached_elem, NULL, p, id);
     215           0 :         return_elem = NULL;
     216       25658 :         break; // out of while loop
     217             :       }
     218             : 
     219       25658 :       bool active = cached_elem->active();
     220       25658 :       bool contains_point = cached_elem->contains_point(p);
     221             : 
     222             :       // If the cached Elem is active and the point is still
     223             :       // contained in it, call the other addPoint() method and
     224             :       // return its result.
     225       25658 :       if (active && contains_point)
     226             :       {
     227       25572 :         addPoint(cached_elem, p, id);
     228       25572 :         return_elem = cached_elem;
     229       25572 :         break; // out of while loop
     230             :       }
     231             : 
     232             :       // Is the Elem not active (been refined) but still contains the point?
     233             :       // Then search in its active children and update the caches.
     234          86 :       else if (!active && contains_point)
     235             :       {
     236             :         // Get the list of active children
     237           0 :         std::vector<const Elem *> active_children;
     238           0 :         cached_elem->active_family_tree(active_children);
     239             : 
     240             :         // Linear search through active children for the one that contains p
     241           0 :         for (unsigned c = 0; c < active_children.size(); ++c)
     242           0 :           if (active_children[c]->contains_point(p))
     243             :           {
     244           0 :             updateCaches(cached_elem, active_children[c], p, id);
     245           0 :             addPoint(active_children[c], p, id);
     246           0 :             return_elem = active_children[c];
     247           0 :             break; // out of for loop
     248             :           }
     249             : 
     250             :         // If we got here without setting return_elem, it means the Point was
     251             :         // found in the parent element, but not in any of the active
     252             :         // children... this is not possible under normal
     253             :         // circumstances, so something must have gone seriously
     254             :         // wrong!
     255           0 :         if (!return_elem)
     256           0 :           mooseError("Error, Point not found in any of the active children!");
     257             : 
     258           0 :         break; // out of while loop
     259           0 :       }
     260             : 
     261          86 :       else if (
     262             :           // Is the Elem active but the point is not contained in it any
     263             :           // longer?  (For example, did the Mesh move out from under
     264             :           // it?)  Then we fall back to the expensive Point Locator
     265             :           // lookup.  TODO: we could try and do something more optimized
     266             :           // like checking if any of the active neighbors contains the
     267             :           // point.  Update the caches.
     268          86 :           (active && !contains_point) ||
     269             : 
     270             :           // The Elem has been refined *and* the Mesh has moved out
     271             :           // from under it, we fall back to doing the expensive Point
     272             :           // Locator lookup.  TODO: We could try and look in the
     273             :           // active children of this Elem's neighbors for the Point.
     274             :           // Update the caches.
     275           0 :           (!active && !contains_point))
     276             :       {
     277          86 :         i_need_find_point = true;
     278          86 :         break; // out of while loop
     279             :       }
     280             : 
     281             :       else
     282           0 :         mooseError("We'll never get here!");
     283             :     } // if (cached_point.relative_fuzzy_equals(p))
     284             :     else
     285           4 :       mooseError("Cached Dirac point ",
     286             :                  cached_point,
     287             :                  " already exists with ID: ",
     288             :                  id,
     289             :                  " and does not match point ",
     290             :                  p,
     291             :                  "If Dirac sources are moving, please set 'allow_moving_sources' to true");
     292             : 
     293             :     // We only want one iteration of this while loop at maximum.
     294             :     i_found_it = false;
     295             :   } // while (i_found_it)
     296             : 
     297             :   // We are back to all processors here because we do not return
     298             :   // early in the code above...
     299             : 
     300             :   // Does we need to call findPoint() on all processors.
     301       39649 :   unsigned int we_need_find_point = static_cast<unsigned int>(i_need_find_point);
     302       39649 :   comm().max(we_need_find_point);
     303             : 
     304       39649 :   if (we_need_find_point)
     305             :   {
     306             :     // findPoint() is a parallel-only function
     307         116 :     const Elem * elem = _dirac_kernel_info.findPoint(p, _mesh, blockIDs());
     308             : 
     309         116 :     updateCaches(cached_elem, elem, p, id);
     310         116 :     addPoint(elem, p, id);
     311         116 :     return_elem = elem;
     312             :   }
     313             : 
     314       39649 :   return return_elem;
     315             : }
     316             : 
     317             : bool
     318      322054 : DiracKernelBase::hasPointsOnElem(const Elem * elem)
     319             : {
     320      322054 :   return _local_dirac_kernel_info.getElements().count(_mesh.elemPtr(elem->id())) != 0;
     321             : }
     322             : 
     323             : bool
     324      315754 : DiracKernelBase::isActiveAtPoint(const Elem * elem, const Point & p)
     325             : {
     326      315754 :   return _local_dirac_kernel_info.hasPoint(elem, p);
     327             : }
     328             : 
     329             : void
     330       52022 : DiracKernelBase::clearPoints()
     331             : {
     332       52022 :   _local_dirac_kernel_info.clearPoints();
     333             : 
     334             :   // If points can move, we can't trust caches
     335       52022 :   if (_allow_moving_sources)
     336           0 :     clearPointsCaches();
     337       52022 : }
     338             : 
     339             : void
     340          82 : DiracKernelBase::clearPointsCaches()
     341             : {
     342          82 :   _point_cache.clear();
     343          82 :   _reverse_point_cache.clear();
     344          82 : }
     345             : 
     346             : void
     347         116 : DiracKernelBase::updateCaches(const Elem * old_elem, const Elem * new_elem, Point p, unsigned id)
     348             : {
     349             :   // Update the point cache.  Remove old cached data, only cache
     350             :   // new_elem if it is non-NULL and local.
     351         116 :   _point_cache.erase(id);
     352         116 :   if (new_elem && (new_elem->processor_id() == processor_id()))
     353          70 :     _point_cache[id] = std::make_pair(new_elem, p);
     354             : 
     355             :   // Update the reverse cache
     356             :   //
     357             :   // First, remove the Point from the old_elem's vector
     358         116 :   reverse_cache_t::iterator it = _reverse_point_cache.find(old_elem);
     359         116 :   if (it != _reverse_point_cache.end())
     360             :   {
     361          86 :     reverse_cache_t::mapped_type & points = it->second;
     362             :     {
     363          86 :       reverse_cache_t::mapped_type::iterator points_it = points.begin(), points_end = points.end();
     364             : 
     365          86 :       for (; points_it != points_end; ++points_it)
     366             :       {
     367             :         // If the point matches, remove it from the vector of points
     368          86 :         if (p.relative_fuzzy_equals(points_it->first))
     369             :         {
     370             :           // Vector erasure.  It can be slow but these vectors are
     371             :           // generally very short.  It also invalidates existing
     372             :           // iterators, so we need to break out of the loop and
     373             :           // not use them any more.
     374          86 :           points.erase(points_it);
     375          86 :           break;
     376             :         }
     377             :       }
     378             : 
     379             :       // If the points vector is now empty, remove old_elem from the reverse cache entirely
     380          86 :       if (points.empty())
     381          86 :         _reverse_point_cache.erase(old_elem);
     382             :     }
     383             :   }
     384             : 
     385             :   // Next, if new_elem is not NULL and local, add the point to the new_elem's vector
     386         116 :   if (new_elem && (new_elem->processor_id() == processor_id()))
     387             :   {
     388          70 :     reverse_cache_t::mapped_type & points = _reverse_point_cache[new_elem];
     389          70 :     points.push_back(std::make_pair(p, id));
     390             :   }
     391         116 : }
     392             : 
     393             : unsigned
     394       56272 : DiracKernelBase::currentPointCachedID()
     395             : {
     396       56272 :   reverse_cache_t::iterator it = _reverse_point_cache.find(_current_elem);
     397             : 
     398             :   // If the current Elem is not in the cache, return invalid_uint
     399       56272 :   if (it == _reverse_point_cache.end())
     400           0 :     return libMesh::invalid_uint;
     401             : 
     402             :   // Do a linear search in the (hopefully small) vector of Points for this Elem
     403       56272 :   reverse_cache_t::mapped_type & points = it->second;
     404             : 
     405       56272 :   for (const auto & points_it : points)
     406             :   {
     407             :     // If the current_point equals the cached point, return the associated id
     408       56272 :     if (_current_point.relative_fuzzy_equals(points_it.first))
     409       56272 :       return points_it.second;
     410             :   }
     411             : 
     412             :   // If we made it here, we didn't find the cached point, so return invalid_uint
     413           0 :   return libMesh::invalid_uint;
     414             : }

Generated by: LCOV version 1.14