https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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),
62 MaterialPropertyInterface(this, Moose::EMPTY_BLOCK_IDS, Moose::EMPTY_BOUNDARY_IDS),
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
81void
82DiracKernelBase::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
90 _dirac_kernel_info.addPoint(elem, p, value);
91 _local_dirac_kernel_info.addPoint(elem, p, value);
92}
93
94const Elem *
95DiracKernelBase::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
115const Elem *
116DiracKernelBase::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
298bool
300{
301 return _local_dirac_kernel_info.getElements().count(_mesh.elemPtr(elem->id())) != 0;
302}
303
304bool
305DiracKernelBase::isActiveAtPoint(const Elem * elem, const Point & p)
306{
307 return _local_dirac_kernel_info.hasPoint(elem, p);
308}
309
310void
312{
314
315 // If points can move, we can't trust caches
318}
319
320void
326
327void
328DiracKernelBase::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
374unsigned
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())
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
395}
An interface that restricts an object to subdomains via the 'blocks' input parameter.
bool hasBlocks(const SubdomainName &name) const
Test if the supplied block name is valid for this object.
virtual const std::set< SubdomainID > & blockIDs() const
Return the block subdomain ids for this object Note, if this is not block restricted,...
static InputParameters validParams()
Intermediate base class that ties together all the interfaces for getting MooseVariableFEBases with t...
DiracKernelBase(const InputParameters &parameters)
const bool _allow_moving_sources
Whether Dirac sources can move during the simulation.
const Elem * addPointWithValidId(Point p, unsigned id, Real value)
A helper function for addPoint(Point, id) for when id != invalid_uint.
const Elem *const & _current_elem
Current element.
reverse_cache_t _reverse_point_cache
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...
DiracKernelInfo & _dirac_kernel_info
Place for storing Point/Elem information shared across all DiracKernel objects.
const DiracKernelInfo::PointNotFoundBehavior _point_not_found_behavior
What to do if the point is not found. See DiracKernelInfo for definition.
bool hasPointsOnElem(const Elem *elem)
Whether or not this DiracKernel has something to distribute on this element.
point_cache_t _point_cache
void clearPoints()
Remove all of the current points and elements.
bool isActiveAtPoint(const Elem *elem, const Point &p)
Whether or not this DiracKernel has something to distribute at this Point.
void clearPointsCaches()
Clear the cache of points because the points may have moved.
Point _current_point
The current point.
DiracKernelInfo _local_dirac_kernel_info
Place for storing Point/Elem information only for this DiracKernel.
static InputParameters validParams()
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.
unsigned currentPointCachedID()
Returns the user-assigned ID of the current Dirac point if it exits, and libMesh::invalid_uint otherw...
The DiracKernelInfo object is a place where all the Dirac points added by different DiracKernels are ...
bool hasPoint(const Elem *elem, const Point &p)
Return true if we have Point 'p' in Element 'elem'.
void clearPoints()
Remove all of the current points and elements.
std::set< const Elem * > & getElements()
Returns a writeable reference to the _elements container.
void addPoint(const Elem *elem, const Point &p, const Real &value=1)
Adds a point source.
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.
static InputParameters validParams()
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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...
void declareControllable(const std::string &name, std::set< ExecFlagType > execute_flags={})
Declare the given parameters as controllable.
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...
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.
An interface for accessing Materials.
void statefulPropertiesAllowed(bool)
Derived classes can declare whether or not they work with stateful material properties.
static InputParameters validParams()
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:271
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
virtual Elem * elemPtr(const dof_id_type i)
Definition MooseMesh.C:3214
This is the common base class for objects that give residual contributions.
static InputParameters validParams()
MooseMesh & _mesh
Reference to this Kernel's mesh object.
void max(const T &r, T &o, Request &req) const
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
const unsigned int invalid_uint