https://mooseframework.inl.gov
DiracKernelBase.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 // 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 
21 {
26 
27  params.addParam<bool>("use_displaced_mesh",
28  false,
29  "Whether or not this object should use the displaced mesh for computation. "
30  "Note that in the case this is true but no displacements are provided in "
31  "the Mesh block the undisplaced mesh will still be used.");
32 
33  params.addParam<bool>(
34  "drop_duplicate_points",
35  true,
36  "By default points added to a DiracKernel are dropped if a point at the same location"
37  "has been added before. If this option is set to false duplicate points are retained"
38  "and contribute to residual and Jacobian.");
39 
40  params.addParam<MooseEnum>(
41  "point_not_found_behavior",
42  MooseEnum(DiracKernelInfo::getPointNotFoundBehaviorOptions(), "IGNORE"),
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  params.addParam<bool>("allow_moving_sources",
48  false,
49  "If true, allow Dirac sources to move, even if the mesh does not move, "
50  "during the simulation.");
51 
52  params.addParamNamesToGroup("use_displaced_mesh drop_duplicate_points", "Advanced");
53  params.declareControllable("enable");
54  params.registerSystemAttributeName("DiracKernel");
55 
56  return params;
57 }
58 
60  : ResidualObject(parameters),
64  BlockRestrictable(this),
65  _current_elem(_assembly.elem()),
66  _coord_sys(_assembly.coordSystem()),
67  _dirac_kernel_info(_subproblem.diracKernelInfo()),
68  _q_point(_assembly.qPoints()),
69  _physical_point(_assembly.physicalPoints()),
70  _qrule(_assembly.qRule()),
71  _JxW(_assembly.JxW()),
72  _drop_duplicate_points(parameters.get<bool>("drop_duplicate_points")),
73  _point_not_found_behavior(parameters.get<MooseEnum>("point_not_found_behavior")
74  .getEnum<DiracKernelInfo::PointNotFoundBehavior>()),
75  _allow_moving_sources(getParam<bool>("allow_moving_sources"))
76 {
77  // Stateful material properties are not allowed on DiracKernels
79 }
80 
81 void
82 DiracKernelBase::addPoint(const Elem * elem, Point p, unsigned /*id*/, Real value)
83 {
84  if (!elem || !hasBlocks(elem->subdomain_id()))
85  return;
86 
87  if (elem->processor_id() != processor_id())
88  return;
89 
92 }
93 
94 const Elem *
95 DiracKernelBase::addPoint(Point p, unsigned id, Real value)
96 {
97  // Make sure that this method was called with the same id on all
98  // processors. It's an extra communication, though, so let's only
99  // do it in DEBUG mode.
100  libmesh_assert(comm().verify(id));
101  libmesh_assert(comm().verify(value));
102 
103  if (id != libMesh::invalid_uint)
104  return addPointWithValidId(p, id, value);
105 
106  // If id == libMesh::invalid_uint (the default), the user is not
107  // enabling caching when they add Dirac points. So all we can do is
108  // the PointLocator lookup, and call the other addPoint() method.
109  const Elem * elem =
111  addPoint(elem, p, id);
112  return elem;
113 }
114 
115 const Elem *
116 DiracKernelBase::addPointWithValidId(Point p, unsigned id, Real value)
117 {
118  // The Elem we'll eventually return. We can't return early on some
119  // processors, because we may need to call parallel_only() functions in
120  // the remainder of this scope.
121  const Elem * return_elem = NULL;
122 
123  // May be set if the Elem is found in our cache, otherwise stays as NULL.
124  const Elem * cached_elem = NULL;
125 
126  // OK, the user gave us an ID, let's see if we already have it...
127  point_cache_t::iterator it = _point_cache.find(id);
128 
129  // Was the point found in a _point_cache on at least one processor?
130  unsigned int i_found_it = static_cast<unsigned int>(it != _point_cache.end());
131  unsigned int we_found_it = i_found_it;
132  comm().max(we_found_it);
133 
134  // If nobody found it in their local caches, it means we need to
135  // do the PointLocator look-up and update the caches. This is
136  // safe, because all processors have the same value of we_found_it.
137  if (!we_found_it)
138  {
139  const Elem * elem =
141 
142  // Only add the point to the cache on this processor if the Elem is local
143  if (elem && (elem->processor_id() == processor_id()))
144  {
145  // Add the point to the cache...
146  _point_cache[id] = std::make_pair(elem, p);
147 
148  // ... and to the reverse cache.
149  std::vector<std::pair<Point, unsigned>> & points = _reverse_point_cache[elem];
150  points.push_back(std::make_pair(p, id));
151  }
152 
153  // Call the other addPoint() method. (no-op on elem=nullptr)
154  addPoint(elem, p, id, value);
155 
156  return_elem = elem;
157  }
158 
159  // If the point was found in a cache, but not my cache, I'm not
160  // responsible for it.
161  //
162  // We can't return early here: then we aren't allowed to call any more
163  // parallel_only() functions in the remainder of this function!
164  if (we_found_it && !i_found_it)
165  return_elem = NULL;
166 
167  // This flag may be set by the processor that cached the Elem because it
168  // needs to call findPoint() (due to moving mesh, etc.). If so, we will
169  // call it at the end of the while loop below.
170  bool i_need_find_point = false;
171 
172  // Now that we only cache local data, some processors may enter
173  // this if statement and some may not. Therefore we can't call
174  // any parallel_only() functions inside this if statement.
175  while (i_found_it)
176  {
177  // We have something cached, now make sure it's actually the same Point.
178  // TODO: we should probably use this same comparison in the DiracKernelInfo code!
179  Point cached_point = (it->second).second;
180 
181  if (cached_point.relative_fuzzy_equals(p))
182  {
183  // Find the cached element associated to this point
184  cached_elem = (it->second).first;
185 
186  // If the cached element's processor ID doesn't match ours, we
187  // are no longer responsible for caching it. This can happen
188  // due to adaptivity...
189  if (cached_elem->processor_id() != processor_id())
190  {
191  // Update the caches, telling them to drop the cached Elem.
192  // Analogously to the rest of the DiracKernel system, we
193  // also return NULL because the Elem is non-local.
194  updateCaches(cached_elem, NULL, p, id);
195  return_elem = NULL;
196  break; // out of while loop
197  }
198 
199  bool active = cached_elem->active();
200  bool contains_point = cached_elem->contains_point(p);
201 
202  // If the cached Elem is active and the point is still
203  // contained in it, call the other addPoint() method and
204  // return its result.
205  if (active && contains_point)
206  {
207  addPoint(cached_elem, p, id, value);
208  return_elem = cached_elem;
209  break; // out of while loop
210  }
211 
212  // Is the Elem not active (been refined) but still contains the point?
213  // Then search in its active children and update the caches.
214  else if (!active && contains_point)
215  {
216  // Get the list of active children
217  std::vector<const Elem *> active_children;
218  cached_elem->active_family_tree(active_children);
219 
220  // Linear search through active children for the one that contains p
221  for (unsigned c = 0; c < active_children.size(); ++c)
222  if (active_children[c]->contains_point(p))
223  {
224  updateCaches(cached_elem, active_children[c], p, id);
225  addPoint(active_children[c], p, id, value);
226  return_elem = active_children[c];
227  break; // out of for loop
228  }
229 
230  // If we got here without setting return_elem, it means the Point was
231  // found in the parent element, but not in any of the active
232  // children... this is not possible under normal
233  // circumstances, so something must have gone seriously
234  // wrong!
235  if (!return_elem)
236  mooseError("Error, Point not found in any of the active children!");
237 
238  break; // out of while loop
239  }
240 
241  else if (
242  // Is the Elem active but the point is not contained in it any
243  // longer? (For example, did the Mesh move out from under
244  // it?) Then we fall back to the expensive Point Locator
245  // lookup. TODO: we could try and do something more optimized
246  // like checking if any of the active neighbors contains the
247  // point. Update the caches.
248  (active && !contains_point) ||
249 
250  // The Elem has been refined *and* the Mesh has moved out
251  // from under it, we fall back to doing the expensive Point
252  // Locator lookup. TODO: We could try and look in the
253  // active children of this Elem's neighbors for the Point.
254  // Update the caches.
255  (!active && !contains_point))
256  {
257  i_need_find_point = true;
258  break; // out of while loop
259  }
260 
261  else
262  mooseError("We'll never get here!");
263  } // if (cached_point.relative_fuzzy_equals(p))
264  else
265  mooseError("Cached Dirac point ",
266  cached_point,
267  " already exists with ID: ",
268  id,
269  " and does not match point ",
270  p,
271  "If Dirac sources are moving, please set 'allow_moving_sources' to true");
272 
273  // We only want one iteration of this while loop at maximum.
274  i_found_it = false;
275  } // while (i_found_it)
276 
277  // We are back to all processors here because we do not return
278  // early in the code above...
279 
280  // Does we need to call findPoint() on all processors.
281  unsigned int we_need_find_point = static_cast<unsigned int>(i_need_find_point);
282  comm().max(we_need_find_point);
283 
284  if (we_need_find_point)
285  {
286  // findPoint() is a parallel-only function
287  const Elem * elem =
289 
290  updateCaches(cached_elem, elem, p, id);
291  addPoint(elem, p, id, value);
292  return_elem = elem;
293  }
294 
295  return return_elem;
296 }
297 
298 bool
300 {
301  return _local_dirac_kernel_info.getElements().count(_mesh.elemPtr(elem->id())) != 0;
302 }
303 
304 bool
305 DiracKernelBase::isActiveAtPoint(const Elem * elem, const Point & p)
306 {
307  return _local_dirac_kernel_info.hasPoint(elem, p);
308 }
309 
310 void
312 {
314 
315  // If points can move, we can't trust caches
318 }
319 
320 void
322 {
323  _point_cache.clear();
324  _reverse_point_cache.clear();
325 }
326 
327 void
328 DiracKernelBase::updateCaches(const Elem * old_elem, const Elem * new_elem, Point p, unsigned id)
329 {
330  // Update the point cache. Remove old cached data, only cache
331  // new_elem if it is non-NULL and local.
332  _point_cache.erase(id);
333  if (new_elem && (new_elem->processor_id() == processor_id()))
334  _point_cache[id] = std::make_pair(new_elem, p);
335 
336  // Update the reverse cache
337  //
338  // First, remove the Point from the old_elem's vector
339  reverse_cache_t::iterator it = _reverse_point_cache.find(old_elem);
340  if (it != _reverse_point_cache.end())
341  {
342  reverse_cache_t::mapped_type & points = it->second;
343  {
344  reverse_cache_t::mapped_type::iterator points_it = points.begin(), points_end = points.end();
345 
346  for (; points_it != points_end; ++points_it)
347  {
348  // If the point matches, remove it from the vector of points
349  if (p.relative_fuzzy_equals(points_it->first))
350  {
351  // Vector erasure. It can be slow but these vectors are
352  // generally very short. It also invalidates existing
353  // iterators, so we need to break out of the loop and
354  // not use them any more.
355  points.erase(points_it);
356  break;
357  }
358  }
359 
360  // If the points vector is now empty, remove old_elem from the reverse cache entirely
361  if (points.empty())
362  _reverse_point_cache.erase(old_elem);
363  }
364  }
365 
366  // Next, if new_elem is not NULL and local, add the point to the new_elem's vector
367  if (new_elem && (new_elem->processor_id() == processor_id()))
368  {
369  reverse_cache_t::mapped_type & points = _reverse_point_cache[new_elem];
370  points.push_back(std::make_pair(p, id));
371  }
372 }
373 
374 unsigned
376 {
377  reverse_cache_t::iterator it = _reverse_point_cache.find(_current_elem);
378 
379  // If the current Elem is not in the cache, return invalid_uint
380  if (it == _reverse_point_cache.end())
381  return libMesh::invalid_uint;
382 
383  // Do a linear search in the (hopefully small) vector of Points for this Elem
384  reverse_cache_t::mapped_type & points = it->second;
385 
386  for (const auto & points_it : points)
387  {
388  // If the current_point equals the cached point, return the associated id
389  if (_current_point.relative_fuzzy_equals(points_it.first))
390  return points_it.second;
391  }
392 
393  // If we made it here, we didn't find the cached point, so return invalid_uint
394  return libMesh::invalid_uint;
395 }
MooseMesh & _mesh
Reference to this Kernel&#39;s mesh object.
const bool _allow_moving_sources
Whether Dirac sources can move during the simulation.
bool hasPointsOnElem(const Elem *elem)
Whether or not this DiracKernel has something to distribute on this element.
bool hasPoint(const Elem *elem, const Point &p)
Return true if we have Point &#39;p&#39; in Element &#39;elem&#39;.
The DiracKernelInfo object is a place where all the Dirac points added by different DiracKernels are ...
void addPoint(const Elem *elem, const Point &p, const Real &value=1)
Adds a point source.
const unsigned int invalid_uint
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3240
void clearPoints()
Remove all of the current points and elements.
DiracKernelInfo & _dirac_kernel_info
Place for storing Point/Elem information shared across all DiracKernel objects.
point_cache_t _point_cache
void registerSystemAttributeName(const std::string &value)
This method is used to define the MOOSE system name that is used by the TheWarehouse object for stori...
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
const Parallel::Communicator & comm() const
unsigned currentPointCachedID()
Returns the user-assigned ID of the current Dirac point if it exits, and libMesh::invalid_uint otherw...
static InputParameters validParams()
virtual const std::set< SubdomainID > & blockIDs() const
Return the block subdomain ids for this object Note, if this is not block restricted, this function returns all mesh subdomain ids.
static InputParameters validParams()
DiracKernelBase(const InputParameters &parameters)
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
static InputParameters validParams()
void addPoint(const Elem *elem, Point p, unsigned id=libMesh::invalid_uint, Real value=1.0)
Add the physical x,y,z point located in the element "elem" to the list of points this DiracKernel wil...
bool isActiveAtPoint(const Elem *elem, const Point &p)
Whether or not this DiracKernel has something to distribute at this Point.
libmesh_assert(ctx)
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
static InputParameters validParams()
DiracKernelInfo _local_dirac_kernel_info
Place for storing Point/Elem information only for this DiracKernel.
const Elem * addPointWithValidId(Point p, unsigned id, Real value)
A helper function for addPoint(Point, id) for when id != invalid_uint.
void clearPoints()
Remove all of the current points and elements.
const Elem *const & _current_elem
Current element.
reverse_cache_t _reverse_point_cache
void statefulPropertiesAllowed(bool)
Derived classes can declare whether or not they work with stateful material properties.
This is the common base class for objects that give residual contributions.
const std::set< SubdomainID > EMPTY_BLOCK_IDS
Definition: MooseTypes.h:732
const Elem * findPoint(const Point &p, const MooseMesh &mesh, const std::set< SubdomainID > &blocks, const PointNotFoundBehavior point_not_found_behavior, const MooseBase &consumer)
Used by client DiracKernel classes to determine the Elem in which the Point p resides.
void max(const T &r, T &o, Request &req) const
An interface for accessing Materials.
const std::set< BoundaryID > EMPTY_BOUNDARY_IDS
Definition: MooseTypes.h:733
const DiracKernelInfo::PointNotFoundBehavior _point_not_found_behavior
What to do if the point is not found. See DiracKernelInfo for definition.
Intermediate base class that ties together all the interfaces for getting MooseVariableFEBases with t...
void clearPointsCaches()
Clear the cache of points because the points may have moved.
An interface that restricts an object to subdomains via the &#39;blocks&#39; input parameter.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:281
void updateCaches(const Elem *old_elem, const Elem *new_elem, Point p, unsigned id)
This function is used internally when the Elem for a locally-cached point needs to be updated...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
std::set< const Elem * > & getElements()
Returns a writeable reference to the _elements container.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
bool hasBlocks(const SubdomainName &name) const
Test if the supplied block name is valid for this object.
static InputParameters validParams()
processor_id_type processor_id() const
void declareControllable(const std::string &name, std::set< ExecFlagType > execute_flags={})
Declare the given parameters as controllable.
const Elem & get(const ElemType type_in)
Point _current_point
The current point.
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...