https://mooseframework.inl.gov
RayTracingStudy.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 #include "RayTracingStudy.h"
11 
12 #include "AuxRayKernel.h"
14 #include "RayKernel.h"
15 #include "TraceRay.h"
16 #include "TraceRayTools.h"
17 #include "PeriodicRayBC.h"
18 
19 #include "AuxiliarySystem.h"
20 #include "Assembly.h"
21 #include "NonlinearSystemBase.h"
22 
23 #include "libmesh/enum_to_string.h"
24 #include "libmesh/mesh_tools.h"
25 #include "libmesh/parallel_sync.h"
26 #include "libmesh/remote_elem.h"
27 #include "libmesh/periodic_boundary.h"
28 #include "libmesh/periodic_boundaries.h"
29 
30 using namespace libMesh;
31 
34 {
35  auto params = GeneralUserObject::validParams();
36 
37  // Parameters for the execution of the rays
39 
40  params.addRangeCheckedParam<Real>("ray_distance",
41  std::numeric_limits<Real>::max(),
42  "ray_distance > 0",
43  "The maximum distance all Rays can travel");
44 
45  params.addParam<bool>(
46  "tolerate_failure", false, "Whether or not to tolerate a ray tracing failure");
47 
48  MooseEnum work_buffers("lifo circular", "circular");
49  params.addParam<MooseEnum>("work_buffer_type", work_buffers, "The work buffer type to use");
50 
51  params.addParam<bool>(
52  "ray_kernel_coverage_check", true, "Whether or not to perform coverage checks on RayKernels");
53  params.addParam<bool>("warn_non_planar",
54  true,
55  "Whether or not to produce a warning if any element faces are non-planar.");
56 
57  params.addParam<bool>(
58  "always_cache_traces",
59  false,
60  "Whether or not to cache the Ray traces on every execution, primarily for use in output. "
61  "Warning: this can get expensive very quick with a large number of rays!");
62  params.addParam<bool>("data_on_cache_traces",
63  false,
64  "Whether or not to also cache the Ray's data when caching its traces");
65  params.addParam<bool>("aux_data_on_cache_traces",
66  false,
67  "Whether or not to also cache the Ray's aux data when caching its traces");
68  params.addParam<bool>(
69  "segments_on_cache_traces",
70  true,
71  "Whether or not to cache individual segments when trace caching is enabled. If false, we "
72  "will instead cache a segment for each part of the trace where the direction is the same. "
73  "This minimizes the number of segments requied to represent the Ray's path, but removes the "
74  "ability to show Ray field data on each segment through an element.");
75 
76  params.addParam<bool>("use_internal_sidesets",
77  false,
78  "Whether or not to use internal sidesets for RayBCs in ray tracing");
79 
80  params.addParam<bool>("warn_subdomain_hmax",
81  true,
82  "Whether or not to warn if the approximated hmax (constant on subdomain) "
83  "varies significantly for an element");
84 
85  params.addParam<bool>(
86  "verify_rays",
87  true,
88  "Whether or not to verify the generated Rays. This includes checking their "
89  "starting information and the uniqueness of Rays before and after execution. This is also "
90  "used by derived studies for more specific verification.");
91  params.addParam<bool>("verify_trace_intersections",
92  true,
93  "Whether or not to verify the trace intersections in devel and dbg modes. "
94  "Trace intersections are not verified regardless of this parameter in "
95  "optimized modes (opt, oprof).");
96 
97  params.addParam<bool>("allow_other_flags_with_prekernels",
98  false,
99  "Whether or not to allow the list of execution flags to have PRE_KERNELS "
100  "mixed with other flags. If this parameter is not set then if PRE_KERNELS "
101  "is provided it must be the only execution flag.");
102 
103  ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
105 
106  params.addParamNamesToGroup(
107  "always_cache_traces data_on_cache_traces aux_data_on_cache_traces segments_on_cache_traces",
108  "Trace cache");
109  params.addParamNamesToGroup("warn_non_planar warn_subdomain_hmax", "Tracing Warnings");
110  params.addParamNamesToGroup("ray_kernel_coverage_check verify_rays verify_trace_intersections",
111  "Checks and verifications");
112 
113  // Whether or not each Ray must be registered using the registerRay() API
114  params.addPrivateParam<bool>("_use_ray_registration", true);
115  // Whether or not to bank Rays on completion
116  params.addPrivateParam<bool>("_bank_rays_on_completion", true);
118  params.addPrivateParam<bool>("_ray_dependent_subdomain_setup", true);
119 
120  // Add a point neighbor relationship manager
121  params.addRelationshipManager("ElementPointNeighborLayers",
124  [](const InputParameters &, InputParameters & rm_params)
125  { rm_params.set<unsigned short>("layers") = 1; });
126 
127  return params;
128 }
129 
131  : GeneralUserObject(parameters),
132  _mesh(_fe_problem.mesh()),
133  _comm(_mesh.comm()),
134  _pid(_comm.rank()),
135 
136  _ray_kernel_coverage_check(getParam<bool>("ray_kernel_coverage_check")),
137  _warn_non_planar(getParam<bool>("warn_non_planar")),
138  _use_ray_registration(getParam<bool>("_use_ray_registration")),
139  _use_internal_sidesets(getParam<bool>("use_internal_sidesets")),
140  _tolerate_failure(getParam<bool>("tolerate_failure")),
141  _bank_rays_on_completion(getParam<bool>("_bank_rays_on_completion")),
142  _ray_dependent_subdomain_setup(getParam<bool>("_ray_dependent_subdomain_setup")),
143 
144  _always_cache_traces(getParam<bool>("always_cache_traces")),
145  _data_on_cache_traces(getParam<bool>("data_on_cache_traces")),
146  _aux_data_on_cache_traces(getParam<bool>("aux_data_on_cache_traces")),
147  _segments_on_cache_traces(getParam<bool>("segments_on_cache_traces")),
148  _ray_max_distance(getParam<Real>("ray_distance")),
149  _verify_rays(getParam<bool>("verify_rays")),
150 #ifndef NDEBUG
151  _verify_trace_intersections(getParam<bool>("verify_trace_intersections")),
152 #endif
153 
154  _threaded_elem_side_builders(libMesh::n_threads()),
155 
156  _registered_ray_map(
157  declareRestartableData<std::unordered_map<std::string, RayID>>("registered_ray_map")),
158  _reverse_registered_ray_map(
159  declareRestartableData<std::vector<std::string>>("reverse_registered_ray_map")),
160 
161  _threaded_cached_traces(libMesh::n_threads()),
162 
163  _num_cached(libMesh::n_threads(), 0),
164 
165  _has_non_planar_sides(true),
166  _has_same_level_active_elems(sameLevelActiveElems()),
167 
168  _b_box(MeshTools::create_nodal_bounding_box(_mesh.getMesh())),
169  _domain_max_length(1.01 * (_b_box.max() - _b_box.min()).norm()),
170  _total_volume(computeTotalVolume()),
171 
172  _threaded_cache_ray_kernel(libMesh::n_threads()),
173  _threaded_cache_ray_bc(libMesh::n_threads()),
174  _threaded_ray_object_registration(libMesh::n_threads()),
175  _threaded_current_ray_kernels(libMesh::n_threads()),
176  _threaded_trace_ray(libMesh::n_threads()),
177  _threaded_fe_face(libMesh::n_threads()),
178  _threaded_q_face(libMesh::n_threads()),
179  _threaded_cached_normals(libMesh::n_threads()),
180  _threaded_next_ray_id(libMesh::n_threads()),
181 
182  _parallel_ray_study(std::make_unique<ParallelRayStudy>(*this, _threaded_trace_ray)),
183 
184  _local_trace_ray_results(TraceRay::FAILED_TRACES + 1, 0),
185 
186  _called_initial_setup(false),
187 
188  _elem_index_helper(_mesh.getMesh(), name() + "_elem_index")
189 {
190  // Initialize a tracing object for each thread
191  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
192  {
193  // Initialize a tracing object for each thread
194  _threaded_trace_ray[tid] = std::make_shared<TraceRay>(*this, tid);
195 
196  // Setup the face FEs for normal computation on the fly
197  _threaded_fe_face[tid] = FEBase::build(_mesh.dimension(), FEType(CONSTANT, MONOMIAL));
198  _threaded_q_face[tid] = QBase::build(QGAUSS, _mesh.dimension() - 1, CONSTANT);
199  _threaded_fe_face[tid]->attach_quadrature_rule(_threaded_q_face[tid].get());
200  _threaded_fe_face[tid]->get_normals();
201  }
202 
203  // Evaluating on residual and Jacobian evaluation
205  {
206  if (!getParam<bool>("allow_other_flags_with_prekernels") && _execute_enum.size() > 1)
207  paramError("execute_on",
208  "PRE_KERNELS cannot be mixed with any other execution flag.\nThat is, you cannot "
209  "currently "
210  "mix RayKernels that contribute to the Jacobian/residual with those that do not.");
211 
212  if (_app.useEigenvalue())
213  mooseError("Execution on residual and Jacobian evaluation (execute_on = PRE_KERNELS)\n",
214  "is not supported for an eigenvalue solve.");
215  }
216 
219 
220  // Scale the bounding box for loose checking
222  _loose_b_box.scale(TOLERANCE * TOLERANCE);
223 }
224 
225 void
227 {
228  // Keep track of initialSetup call to avoid registration of various things
229  _called_initial_setup = true;
230 
231  // Sets up a local index for each elem this proc knows about
233 
234  // Check for RayKernel coverage
235  coverageChecks();
236 
237  // Make sure the dependencies exist, if any
239 
240  // Check for traceable element types
242 
243  // Check for sane periodic boundaries
245 
246  // Setup for internal sidesets
248 
250 
251  // Setup approximate hmax for each subdomain
253 
254  // Call initial setup on all of the objects
255  for (auto & rto : getRayTracingObjects())
256  rto->initialSetup();
257 
258  // Check for proper exec flags with RayKernels
259  std::vector<RayKernelBase *> ray_kernels;
260  getRayKernels(ray_kernels, 0);
261  for (const auto & rkb : ray_kernels)
262  if (dynamic_cast<RayKernel *>(rkb) && !_execute_enum.isValueSet(EXEC_PRE_KERNELS))
263  mooseError("This study has RayKernel objects that contribute to residuals and Jacobians.",
264  "\nIn this case, the study must use the execute_on = PRE_KERNELS");
265 
266  // Build 1D quadrature rule for along a segment
268  QBase::build(QGAUSS, 1, _fe_problem.getSystemBase(_sys.number()).getMinQuadratureOrder());
269 }
270 
271 void
273 {
274  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
275  mooseAssert(_num_cached[tid] == 0, "Cached residuals/Jacobians not empty");
276 
277  for (auto & rto : getRayTracingObjects())
278  rto->residualSetup();
279 }
280 
281 void
283 {
284  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
285  mooseAssert(_num_cached[tid] == 0, "Cached residuals/Jacobians not empty");
286 
287  for (auto & rto : getRayTracingObjects())
288  rto->jacobianSetup();
289 }
290 
291 void
293 {
294  for (auto & rto : getRayTracingObjects())
295  rto->timestepSetup();
296 }
297 
298 void
300 {
305 
307 
308  for (const auto & trace_ray : _threaded_trace_ray)
309  trace_ray->meshChanged();
310 }
311 
312 void
314 {
315  executeStudy();
316 }
317 
318 void
320 {
321  // Check for coverage of RayKernels on domain
323  {
324  std::vector<RayKernelBase *> ray_kernels;
325  getRayKernels(ray_kernels, 0);
326 
327  std::set<SubdomainID> ray_kernel_blocks;
328  for (const auto & rk : ray_kernels)
329  ray_kernel_blocks.insert(rk->blockIDs().begin(), rk->blockIDs().end());
330 
331  std::set<SubdomainID> missing;
332  std::set_difference(_mesh.meshSubdomains().begin(),
333  _mesh.meshSubdomains().end(),
334  ray_kernel_blocks.begin(),
335  ray_kernel_blocks.end(),
336  std::inserter(missing, missing.begin()));
337 
338  if (!missing.empty() && !ray_kernel_blocks.count(Moose::ANY_BLOCK_ID))
339  {
340  std::ostringstream error;
341  error << "Subdomains { ";
342  std::copy(missing.begin(), missing.end(), std::ostream_iterator<SubdomainID>(error, " "));
343  error << "} do not have RayKernels defined!";
344 
345  mooseError(error.str());
346  }
347  }
348 }
349 
350 void
352 {
353  std::vector<RayTracingObject *> ray_tracing_objects;
354 
355  getRayKernels(ray_tracing_objects, 0);
356  verifyDependenciesExist(ray_tracing_objects);
357 
358  getRayBCs(ray_tracing_objects, 0);
359  verifyDependenciesExist(ray_tracing_objects);
360 }
361 
362 void
363 RayTracingStudy::verifyDependenciesExist(const std::vector<RayTracingObject *> & rtos)
364 {
365  for (const auto & rto : rtos)
366  for (const auto & dep_name : rto->getRequestedItems())
367  {
368  bool found = false;
369  for (const auto & rto_search : rtos)
370  if (rto_search->name() == dep_name)
371  {
372  found = true;
373  break;
374  }
375 
376  if (!found)
377  rto->paramError("depends_on", "The ", rto->getBase(), " '", dep_name, "' does not exist");
378  }
379 }
380 
381 void
383 {
384  for (const auto & elem : *_mesh.getActiveLocalElementRange())
385  {
386  if (_fe_problem.adaptivity().isOn())
387  {
389  mooseError("Element type ",
390  Utility::enum_to_string(elem->type()),
391  " is not supported in ray tracing with adaptivity");
392  }
393  else if (!TraceRayTools::isTraceableElem(elem))
394  mooseError("Element type ",
395  Utility::enum_to_string(elem->type()),
396  " is not supported in ray tracing");
397  }
398 }
399 
400 void
402 {
403  // Collect the PeriodicRayBCs
404  std::vector<const RayBoundaryConditionBase *> rbc_ptrs;
405  getRayBCs(rbc_ptrs, 0);
406  std::vector<const PeriodicRayBC *> prbc_ptrs;
407  for (const auto rbc_ptr : rbc_ptrs)
408  if (const auto prbc_ptr = dynamic_cast<const PeriodicRayBC *>(rbc_ptr))
409  prbc_ptrs.push_back(prbc_ptr);
410  if (prbc_ptrs.empty())
411  return;
412 
413  // Collect each of the periodic boundaries
414  std::map<boundary_id_type,
415  std::tuple<const PeriodicRayBC *,
417  std::unordered_set<dof_id_type>>>
418  boundary_map;
419  for (const auto prbc_ptr : prbc_ptrs)
420  {
421  for (const auto & [bid, pb] : prbc_ptr->getPeriodicBoundaries())
422  {
423  const auto [it, inserted] = boundary_map.emplace(
424  std::piecewise_construct,
425  std::tuple{bid},
426  std::forward_as_tuple(prbc_ptr, pb.get(), std::unordered_set<dof_id_type>()));
427  if (!inserted)
428  prbc_ptr->mooseError("The periodic boundary '",
430  "' has been defined in both ",
431  prbc_ptr->typeAndName(),
432  " and ",
433  std::get<0>(it->second)->typeAndName());
434  }
435  }
436 
437  // Because we don't have ghosting setup correctly yet, we need to check if any
438  // of the periodic boundaries are neighbors with distributed mesh. If the
439  // mesh is replicated, we don't need to check this. See #31280.
440  if (comm().size() == 1 || !_mesh.isDistributedMesh())
441  return;
442 
443  // Collect all of the nodes that are on each periodic boundary
444  const auto & sideset_map = _mesh.getMesh().get_boundary_info().get_sideset_map();
445  for (const auto & [elem, side_bid_pair] : sideset_map)
446  {
447  const auto [side, bid] = side_bid_pair;
448  if (auto it = boundary_map.find(bid); it != boundary_map.end())
449  for (const auto n : elem->nodes_on_side(side))
450  std::get<2>(it->second).insert(elem->node_ref(n).id());
451  }
452 
453  // Distributed meshes have distributed boundary information, so sync
454  for (auto & bid_tuple_pair : boundary_map)
455  comm().set_union(std::get<2>(bid_tuple_pair.second));
456 
457  // Check for periodic boundaries that share nodes
458  std::map<std::pair<boundary_id_type, boundary_id_type>,
459  std::pair<const PeriodicRayBC *, const PeriodicRayBC *>>
460  warn_boundaries;
461  for (auto it = boundary_map.begin(); it != boundary_map.end(); ++it)
462  {
463  const auto & [bid, tup] = *it;
464  const auto [prbc_ptr, pb, node_ids] = tup;
465 
466  for (auto other_it = std::next(it); other_it != boundary_map.end(); ++other_it)
467  {
468  const auto & [other_bid, other_tup] = *other_it;
469 
470  // Don't check against boundaries that are paried together
471  if (pb->pairedboundary == other_bid)
472  continue;
473 
474  const auto other_prbc_ptr = std::get<0>(other_tup);
475  const auto & other_node_ids = std::get<2>(other_tup);
476  for (const auto node_id : node_ids)
477  if (other_node_ids.count(node_id))
478  {
479  if (!warn_boundaries.count(std::make_pair(other_bid, bid)))
480  warn_boundaries.emplace(std::make_pair(bid, other_bid),
481  std::make_pair(prbc_ptr, other_prbc_ptr));
482  break;
483  }
484  }
485  }
486 
487  if (warn_boundaries.size())
488  {
489  std::ostringstream oss;
490  oss << warn_boundaries.size()
491  << " ray tracing periodic boundaries were found to be neighbors:\n\n";
492  for (const auto & [bids_pair, prbc_ptrs_pair] : warn_boundaries)
493  {
494  const auto [bid, paired_bid] = bids_pair;
495  const auto [prbc_ptr, paired_prbc_ptr] = prbc_ptrs_pair;
496  oss << " '" << _mesh.getBoundaryString(bid) << "' (in " << prbc_ptr->typeAndName()
497  << ") <-> '" << _mesh.getBoundaryString(paired_bid) << "' (in "
498  << paired_prbc_ptr->typeAndName() << ")\n";
499  }
500  oss << "\nThe periodic propagation of rays at points where two or more periodic"
501  << "\nboundaries meet is not fully supported with a distributed mesh."
502  << "\n\nIf you encounter trace failures, you should use a replicated mesh.";
503  mooseWarning(oss.str());
504  }
505 }
506 
507 void
509 {
510  // TODO: We could probably minimize this to local active elements followed by
511  // boundary point neighbors, but if using distibuted mesh it really shouldn't matter
512  _elem_index_helper.initialize(_mesh.getMesh().active_element_ptr_range());
513 }
514 
515 void
517 {
518  // Even if we have _use_internal_sidesets == false, we will make sure the user didn't add RayBCs
519  // on internal boundaries
520 
521  // Clear the data structures and size the map based on the elements that we know about
522  _internal_sidesets.clear();
523  _internal_sidesets_map.clear();
525 
526  // First, we are going to store all elements with internal sidesets (if any) that have active
527  // RayBCs on them as elem -> vector of (side, vector of boundary ids)
528  for (const auto & bnd_elem : *_mesh.getBoundaryElementRange())
529  {
530  Elem * elem = bnd_elem->_elem;
531  const unsigned int side = bnd_elem->_side;
532  const auto bnd_id = bnd_elem->_bnd_id;
533 
534  // Not internal
535  const Elem * const neighbor = elem->neighbor_ptr(side);
536  if (!neighbor || neighbor == remote_elem)
537  continue;
538 
539  // No RayBCs on this sideset
540  std::vector<RayBoundaryConditionBase *> result;
541  getRayBCs(result, bnd_id, 0);
542  if (result.empty())
543  continue;
544 
545  if (neighbor->subdomain_id() == elem->subdomain_id())
546  mooseError("RayBCs exist on internal sidesets that are not bounded by a different",
547  "\nsubdomain on each side.",
548  "\n\nIn order to use RayBCs on internal sidesets, said sidesets must have",
549  "\na different subdomain on each side.");
550 
551  // Mark that this boundary is an internal sideset with RayBC(s)
552  _internal_sidesets.insert(bnd_id);
553 
554  // Get elem's entry in the internal sidset data structure
555  const auto index = _elem_index_helper.getIndex(elem);
556  auto & entry = _internal_sidesets_map[index];
557 
558  // Initialize this elem's sides if they have not been already
559  if (entry.empty())
560  entry.resize(elem->n_sides(), std::vector<BoundaryID>());
561 
562  // Add the internal boundary to the side entry
563  entry[side].push_back(bnd_id);
564  }
565 
567  mooseError("RayBCs are defined on internal sidesets, but the study is not set to use ",
568  "internal sidesets during tracing.",
569  "\n\nSet the parameter use_internal_sidesets = true to enable this capability.");
570 }
571 
572 void
574 {
575  _has_non_planar_sides = false;
576  bool warned = !_warn_non_planar;
577 
578  // Nothing to do here for 2D or 1D
579  if (_mesh.dimension() != 3)
580  return;
581 
582  // Clear the data structure and size it based on the elements that we know about
583  _non_planar_sides.clear();
585 
586  for (const Elem * elem : _mesh.getMesh().active_element_ptr_range())
587  {
588  const auto index = _elem_index_helper.getIndex(elem);
589  auto & entry = _non_planar_sides[index];
590  entry.resize(elem->n_sides(), 0);
591 
592  for (const auto s : elem->side_index_range())
593  {
594  const auto & side = elemSide(*elem, s);
595  if (side.n_vertices() < 4)
596  continue;
597 
598  if (!side.has_affine_map())
599  {
600  entry[s] = 1;
601  _has_non_planar_sides = true;
602 
603  if (!warned)
604  {
605  mooseWarning("The mesh contains non-planar faces.\n\n",
606  "Ray tracing on non-planar faces is an approximation and may fail.\n\n",
607  "Use at your own risk! You can disable this warning by setting the\n",
608  "parameter 'warn_non_planar' to false.");
609  warned = true;
610  }
611  }
612  }
613  }
614 }
615 
616 void
618 {
619  // Setup map with subdomain keys
620  _subdomain_hmax.clear();
621  for (const auto subdomain_id : _mesh.meshSubdomains())
622  _subdomain_hmax[subdomain_id] = std::numeric_limits<Real>::min();
623 
624  // Set local max for each subdomain
625  for (const auto & elem : *_mesh.getActiveLocalElementRange())
626  {
627  auto & entry = _subdomain_hmax.at(elem->subdomain_id());
628  entry = std::max(entry, elem->hmax());
629  }
630 
631  // Accumulate global max for each subdomain
633 
634  if (getParam<bool>("warn_subdomain_hmax"))
635  {
636  const auto warn_prefix = type() + " '" + name() + "': ";
637  const auto warn_suffix =
638  "\n\nRay tracing uses an approximate element size for each subdomain to scale the\n"
639  "tolerances used in computing ray intersections. This warning suggests that the\n"
640  "approximate element size is not a good approximation. This is likely due to poor\n"
641  "element aspect ratios.\n\n"
642  "This warning is only output for the first element affected.\n"
643  "To disable this warning, set warn_subdomain_hmax = false.\n";
644 
645  for (const auto & elem : *_mesh.getActiveLocalElementRange())
646  {
647  const auto hmin = elem->hmin();
648  const auto hmax = elem->hmax();
649  const auto max_hmax = subdomainHmax(elem->subdomain_id());
650 
651  const auto hmax_rel = hmax / max_hmax;
652  if (hmax_rel < 1.e-2 || hmax_rel > 1.e2)
653  mooseDoOnce(mooseWarning(warn_prefix,
654  "Element hmax varies significantly from subdomain hmax.\n",
655  warn_suffix,
656  "First element affected:\n",
657  Moose::stringify(*elem)););
658 
659  const auto h_rel = max_hmax / hmin;
660  if (h_rel > 1.e2)
661  mooseDoOnce(mooseWarning(warn_prefix,
662  "Element hmin varies significantly from subdomain hmax.\n",
663  warn_suffix,
664  "First element affected:\n",
665  Moose::stringify(*elem)););
666  }
667  }
668 }
669 
670 void
672 {
673  // First, clear the objects associated with each Ray on each thread
674  const auto num_rays = _registered_ray_map.size();
675  for (auto & entry : _threaded_ray_object_registration)
676  {
677  entry.clear();
678  entry.resize(num_rays);
679  }
680 
681  const auto rtos = getRayTracingObjects();
682 
684  {
685  // All of the registered ray names - used when a RayTracingObject did not specify
686  // any Rays so it should be associated with all Rays.
687  std::vector<std::string> all_ray_names;
688  all_ray_names.reserve(_registered_ray_map.size());
689  for (const auto & pair : _registered_ray_map)
690  all_ray_names.push_back(pair.first);
691 
692  for (auto & rto : rtos)
693  {
694  // The Ray names associated with this RayTracingObject
695  const auto & ray_names = rto->parameters().get<std::vector<std::string>>("rays");
696  // The registration for RayTracingObjects for the thread rto is on
697  const auto tid = rto->parameters().get<THREAD_ID>("_tid");
698  auto & registration = _threaded_ray_object_registration[tid];
699 
700  // Register each Ray for this object in the registration
701  for (const auto & ray_name : (ray_names.empty() ? all_ray_names : ray_names))
702  {
703  const auto id = registeredRayID(ray_name, /* graceful = */ true);
704  if (ray_names.size() && id == Ray::INVALID_RAY_ID)
705  rto->paramError(
706  "rays", "Supplied ray '", ray_name, "' is not a registered Ray in ", typeAndName());
707  registration[id].insert(rto);
708  }
709  }
710  }
711  // Not using Ray registration
712  else
713  {
714  for (const auto & rto : rtos)
715  if (rto->parameters().get<std::vector<std::string>>("rays").size())
716  rto->paramError(
717  "rays",
718  "Rays cannot be supplied when the study does not require Ray registration.\n\n",
719  type(),
720  " does not require Ray registration.");
721  }
722 }
723 
724 void
726 {
727  std::set<std::string> vars_to_be_zeroed;
728  std::vector<RayKernelBase *> ray_kernels;
729  getRayKernels(ray_kernels, 0);
730  for (auto & rk : ray_kernels)
731  {
732  AuxRayKernel * aux_rk = dynamic_cast<AuxRayKernel *>(rk);
733  if (aux_rk)
734  vars_to_be_zeroed.insert(aux_rk->variable().name());
735  }
736 
737  std::vector<std::string> vars_to_be_zeroed_vec(vars_to_be_zeroed.begin(),
738  vars_to_be_zeroed.end());
739  _fe_problem.getAuxiliarySystem().zeroVariables(vars_to_be_zeroed_vec);
740 }
741 
742 void
744  const THREAD_ID tid,
745  const RayID ray_id)
746 {
747  mooseAssert(currentlyPropagating(), "Should not call while not propagating");
748 
749  // Call subdomain setup on FE
750  _fe_problem.subdomainSetup(subdomain, tid);
751 
752  std::set<MooseVariableFEBase *> needed_moose_vars;
753  std::unordered_set<unsigned int> needed_mat_props;
754 
755  // Get RayKernels and their dependencies and call subdomain setup
756  getRayKernels(_threaded_current_ray_kernels[tid], subdomain, tid, ray_id);
757  for (auto & rkb : _threaded_current_ray_kernels[tid])
758  {
759  rkb->subdomainSetup();
760 
761  const auto & mv_deps = rkb->getMooseVariableDependencies();
762  needed_moose_vars.insert(mv_deps.begin(), mv_deps.end());
763 
764  const auto & mp_deps = rkb->getMatPropDependencies();
765  needed_mat_props.insert(mp_deps.begin(), mp_deps.end());
766  }
767 
768  // Prepare aux vars
769  for (auto & var : needed_moose_vars)
770  if (var->kind() == Moose::VarKindType::VAR_AUXILIARY)
771  var->prepareAux();
772 
773  _fe_problem.setActiveElementalMooseVariables(needed_moose_vars, tid);
774  _fe_problem.prepareMaterials(needed_mat_props, subdomain, tid);
775 }
776 
777 void
779  const Elem * elem, const Point & start, const Point & end, const Real length, THREAD_ID tid)
780 {
781  mooseAssert(MooseUtils::absoluteFuzzyEqual((start - end).norm(), length), "Invalid length");
782  mooseAssert(currentlyPropagating(), "Should not call while not propagating");
783 
785 
786  // If we have any variables or material properties that are active, we definitely need to reinit
787  bool reinit = _fe_problem.hasActiveElementalMooseVariables(tid) ||
789  // If not, make sure that the RayKernels have not requested a reinit (this could happen when a
790  // RayKernel doesn't have variables or materials but still does an integration and needs qps)
791  if (!reinit)
792  for (const RayKernelBase * rk : currentRayKernels(tid))
793  if (rk->needSegmentReinit())
794  {
795  reinit = true;
796  break;
797  }
798 
799  if (reinit)
800  {
801  _fe_problem.prepare(elem, tid);
802 
803  std::vector<Point> points;
804  std::vector<Real> weights;
805  buildSegmentQuadrature(start, end, length, points, weights);
806  _fe_problem.reinitElemPhys(elem, points, tid);
807  _fe_problem.assembly(tid, _sys.number()).modifyArbitraryWeights(weights);
808 
810  }
811 }
812 
813 void
815  const Point & end,
816  const Real length,
817  std::vector<Point> & points,
818  std::vector<Real> & weights) const
819 {
820  points.resize(_segment_qrule->n_points());
821  weights.resize(_segment_qrule->n_points());
822 
823  const Point diff = end - start;
824  const Point sum = end + start;
825  mooseAssert(MooseUtils::absoluteFuzzyEqual(length, diff.norm()), "Invalid length");
826 
827  // The standard quadrature rule should be on x = [-1, 1]
828  // To scale the points, you...
829  // - Scale to size of the segment in 3D
830  // initial_scaled_qp = x_qp * 0.5 * (end - start) = 0.5 * x_qp * diff
831  // - Shift quadrature midpoint to segment midpoint
832  // final_qp = initial_scaled_qp + 0.5 * (end - start) = initial_scaled_qp + 0.5 * sum
833  // = 0.5 * (x_qp * diff + sum)
834  for (unsigned int qp = 0; qp < _segment_qrule->n_points(); ++qp)
835  {
836  points[qp] = 0.5 * (_segment_qrule->qp(qp)(0) * diff + sum);
837  weights[qp] = 0.5 * _segment_qrule->w(qp) * length;
838  }
839 }
840 
841 void
842 RayTracingStudy::postOnSegment(const THREAD_ID tid, const std::shared_ptr<Ray> & /* ray */)
843 {
844  mooseAssert(currentlyPropagating(), "Should not call while not propagating");
846  mooseAssert(_num_cached[tid] == 0,
847  "Values should only be cached when computing Jacobian/residual");
848 
849  // Fill into cached Jacobian/residuals if necessary
851  {
853 
854  if (++_num_cached[tid] == 20)
855  {
856  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
858  _num_cached[tid] = 0;
859  }
860  }
862  {
864 
865  if (++_num_cached[tid] == 20)
866  {
867  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
869  _num_cached[tid] = 0;
870  }
871  }
872 }
873 
874 void
876 {
877  TIME_SECTION("executeStudy", 2, "Executing Study");
878 
879  mooseAssert(_called_initial_setup, "Initial setup not called");
880 
881  // Reset ray start/complete timers
883  _max_intersections = 0;
885 
886  // Reset physical tracing stats
887  for (auto & val : _local_trace_ray_results)
888  val = 0;
889 
890  // Reset crossing and intersection
897  _ending_distance = 0;
898  _total_distance = 0;
899 
900  // Zero the AuxVariables that our AuxRayKernels contribute to before they accumulate
902 
903  preExecuteStudy();
904  for (auto & rto : getRayTracingObjects())
905  rto->preExecuteStudy();
906 
907  _ray_bank.clear();
908 
909  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
910  {
911  _threaded_trace_ray[tid]->preExecute();
912  _threaded_cached_normals[tid].clear();
913  }
914 
916  _execution_start_time = std::chrono::steady_clock::now();
917 
918  _parallel_ray_study->preExecute();
919 
920  {
921  {
922  auto generation_start_time = std::chrono::steady_clock::now();
923 
924  TIME_SECTION("generateRays", 2, "Generating Rays");
925 
926  generateRays();
927 
928  _generation_time = std::chrono::steady_clock::now() - generation_start_time;
929  }
930 
931  // At this point, nobody is working so this is good time to make sure
932  // Rays are unique across all processors in the working buffer
933  if (verifyRays())
934  {
935  verifyUniqueRays(_parallel_ray_study->workBuffer().begin(),
936  _parallel_ray_study->workBuffer().end(),
937  /* error_suffix = */ "after generateRays()");
938 
939  verifyUniqueRayIDs(_parallel_ray_study->workBuffer().begin(),
940  _parallel_ray_study->workBuffer().end(),
941  /* global = */ true,
942  /* error_suffix = */ "after generateRays()");
943  }
944 
946 
947  {
948  TIME_SECTION("propagateRays", 2, "Propagating Rays");
949 
950  const auto propagation_start_time = std::chrono::steady_clock::now();
951 
952  _parallel_ray_study->execute();
953 
954  _propagation_time = std::chrono::steady_clock::now() - propagation_start_time;
955  }
956  }
957 
958  _execution_time = std::chrono::steady_clock::now() - _execution_start_time;
959 
960  if (verifyRays())
961  {
962  verifyUniqueRays(_parallel_ray_study->workBuffer().begin(),
963  _parallel_ray_study->workBuffer().end(),
964  /* error_suffix = */ "after tracing completed");
965 
966 #ifndef NDEBUG
967  // Outside of debug, _ray_bank always holds all of the Rays that have ended on this processor
968  // We can use this as a global point to check for unique IDs for every Ray that has traced
970  _ray_bank.end(),
971  /* global = */ true,
972  /* error_suffix = */ "after tracing completed");
973 #endif
974  }
975 
976  // Update counters from the threaded trace objects
977  for (const auto & tr : _threaded_trace_ray)
978  for (std::size_t i = 0; i < _local_trace_ray_results.size(); ++i)
979  _local_trace_ray_results[i] += tr->results()[i];
980 
981  // Update local ending counters
988  // ...and communicate the global values
995 
996  // Throw a warning with the number of failed (tolerated) traces
997  if (_tolerate_failure)
998  {
1000  _communicator.sum(failures);
1001  if (failures)
1002  mooseWarning(
1003  type(), " '", name(), "': ", failures, " ray tracing failures were tolerated.\n");
1004  }
1005 
1006  // Clear the current RayKernels
1007  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1009 
1010  // Move the threaded cache trace information into the full cached trace vector
1011  // Here, we only clear the cached vectors so that we might not have to
1012  // reallocate on future traces
1013  std::size_t num_entries = 0;
1014  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1015  num_entries += _threaded_cached_traces[tid].size();
1016  _cached_traces.clear();
1017  _cached_traces.reserve(num_entries);
1018  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1019  {
1020  for (const auto & entry : _threaded_cached_traces[tid])
1021  _cached_traces.emplace_back(std::move(entry));
1022  _threaded_cached_traces[tid].clear();
1023  }
1024 
1025  // Add any stragglers that contribute to the Jacobian or residual
1026  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1027  if (_num_cached[tid] != 0)
1028  {
1029  mooseAssert(_fe_problem.currentlyComputingJacobian() ||
1031  "Should not have cached values without Jacobian/residual computation");
1032 
1035  else
1037 
1038  _num_cached[tid] = 0;
1039  }
1040 
1041  // AuxRayKernels may have modified AuxVariables
1044 
1045  // Clear FE
1046  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1047  {
1050  }
1051 
1052  postExecuteStudy();
1053  for (auto & rto : getRayTracingObjects())
1054  rto->postExecuteStudy();
1055 }
1056 
1057 void
1058 RayTracingStudy::onCompleteRay(const std::shared_ptr<Ray> & ray)
1059 {
1060  mooseAssert(currentlyPropagating(), "Should only be called during Ray propagation");
1061 
1062  _ending_processor_crossings += ray->processorCrossings();
1064  std::max(_ending_max_processor_crossings, ray->processorCrossings());
1065  _ending_intersections += ray->intersections();
1066  _ending_max_intersections = std::max(_ending_max_intersections, ray->intersections());
1068  std::max(_ending_max_trajectory_changes, ray->trajectoryChanges());
1069  _ending_distance += ray->distance();
1070 
1071 #ifdef NDEBUG
1072  // In non-opt modes, we will always bank the Rays for debugging
1074 #endif
1075  _ray_bank.emplace_back(ray);
1076 }
1077 
1079 RayTracingStudy::registerRayDataInternal(const std::string & name, const bool aux)
1080 {
1081  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1082 
1084  mooseError("Cannot register Ray ", (aux ? "aux " : ""), "data after initialSetup()");
1085 
1086  auto & map = aux ? _ray_aux_data_map : _ray_data_map;
1087  const auto find = map.find(name);
1088  if (find != map.end())
1089  return find->second;
1090 
1091  auto & other_map = aux ? _ray_data_map : _ray_aux_data_map;
1092  if (other_map.find(name) != other_map.end())
1093  mooseError("Cannot register Ray aux data with name ",
1094  name,
1095  " because Ray ",
1096  (aux ? "(non-aux)" : "aux"),
1097  " data already exists with said name.");
1098 
1099  // Add into the name -> index map
1100  map.emplace(name, map.size());
1101 
1102  // Add into the index -> names vector
1103  auto & vector = aux ? _ray_aux_data_names : _ray_data_names;
1104  vector.push_back(name);
1105 
1106  return map.size() - 1;
1107 }
1108 
1109 std::vector<RayDataIndex>
1110 RayTracingStudy::registerRayDataInternal(const std::vector<std::string> & names, const bool aux)
1111 {
1112  std::vector<RayDataIndex> indices(names.size());
1113  for (std::size_t i = 0; i < names.size(); ++i)
1114  indices[i] = registerRayDataInternal(names[i], aux);
1115  return indices;
1116 }
1117 
1120  const bool aux,
1121  const bool graceful) const
1122 {
1123  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1124 
1125  const auto & map = aux ? _ray_aux_data_map : _ray_data_map;
1126  const auto find = map.find(name);
1127  if (find != map.end())
1128  return find->second;
1129 
1130  if (graceful)
1132 
1133  const auto & other_map = aux ? _ray_data_map : _ray_aux_data_map;
1134  if (other_map.find(name) != other_map.end())
1135  mooseError("Ray data with name '",
1136  name,
1137  "' was not found.\n\n",
1138  "However, Ray ",
1139  (aux ? "non-aux" : "aux"),
1140  " data with said name was found.\n",
1141  "Did you mean to use ",
1142  (aux ? "getRayDataIndex()/getRayDataIndices()?"
1143  : "getRayAuxDataIndex()/getRayAuxDataIndices()"),
1144  "?");
1145 
1146  mooseError("Unknown Ray ", (aux ? "aux " : ""), "data with name ", name);
1147 }
1148 
1149 std::vector<RayDataIndex>
1150 RayTracingStudy::getRayDataIndicesInternal(const std::vector<std::string> & names,
1151  const bool aux,
1152  const bool graceful) const
1153 {
1154  std::vector<RayDataIndex> indices(names.size());
1155  for (std::size_t i = 0; i < names.size(); ++i)
1156  indices[i] = getRayDataIndexInternal(names[i], aux, graceful);
1157  return indices;
1158 }
1159 
1160 const std::string &
1161 RayTracingStudy::getRayDataNameInternal(const RayDataIndex index, const bool aux) const
1162 {
1163  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1164 
1165  if ((aux ? rayAuxDataSize() : rayDataSize()) < index)
1166  mooseError("Unknown Ray ", aux ? "aux " : "", "data with index ", index);
1167  return aux ? _ray_aux_data_names[index] : _ray_data_names[index];
1168 }
1169 
1172 {
1173  return registerRayDataInternal(name, /* aux = */ false);
1174 }
1175 
1176 std::vector<RayDataIndex>
1177 RayTracingStudy::registerRayData(const std::vector<std::string> & names)
1178 {
1179  return registerRayDataInternal(names, /* aux = */ false);
1180 }
1181 
1183 RayTracingStudy::getRayDataIndex(const std::string & name, const bool graceful /* = false */) const
1184 {
1185  return getRayDataIndexInternal(name, /* aux = */ false, graceful);
1186 }
1187 
1188 std::vector<RayDataIndex>
1189 RayTracingStudy::getRayDataIndices(const std::vector<std::string> & names,
1190  const bool graceful /* = false */) const
1191 {
1192  return getRayDataIndicesInternal(names, /* aux = */ false, graceful);
1193 }
1194 
1195 const std::string &
1197 {
1198  return getRayDataNameInternal(index, /* aux = */ false);
1199 }
1200 
1203 {
1204  return registerRayDataInternal(name, /* aux = */ true);
1205 }
1206 
1207 std::vector<RayDataIndex>
1208 RayTracingStudy::registerRayAuxData(const std::vector<std::string> & names)
1209 {
1210  return registerRayDataInternal(names, /* aux = */ true);
1211 }
1212 
1215  const bool graceful /* = false */) const
1216 {
1217  return getRayDataIndexInternal(name, /* aux = */ true, graceful);
1218 }
1219 
1220 std::vector<RayDataIndex>
1221 RayTracingStudy::getRayAuxDataIndices(const std::vector<std::string> & names,
1222  const bool graceful /* = false */) const
1223 {
1224  return getRayDataIndicesInternal(names, /* aux = */ true, graceful);
1225 }
1226 
1227 const std::string &
1229 {
1230  return getRayDataNameInternal(index, /* aux = */ true);
1231 }
1232 
1233 bool
1235 {
1236  std::vector<RayKernelBase *> result;
1237  getRayKernels(result, tid);
1238  return result.size();
1239 }
1240 
1241 void
1242 RayTracingStudy::getRayKernels(std::vector<RayKernelBase *> & result, SubdomainID id, THREAD_ID tid)
1243 {
1244  // If the cache doesn't have any attributes yet, it means that we haven't set
1245  // the conditions yet. We do this so that it can be generated on the fly on first use.
1246  if (!_threaded_cache_ray_kernel[tid].numAttribs())
1247  {
1248  if (!_called_initial_setup)
1249  mooseError("Should not call getRayKernels() before initialSetup()");
1250 
1251  auto query = _fe_problem.theWarehouse()
1252  .query()
1253  .condition<AttribRayTracingStudy>(this)
1254  .condition<AttribSystem>("RayKernel")
1255  .condition<AttribThread>(tid);
1256  _threaded_cache_ray_kernel[tid] = query.clone();
1257  }
1258 
1259  _threaded_cache_ray_kernel[tid].queryInto(result, id);
1260 }
1261 
1262 void
1263 RayTracingStudy::getRayKernels(std::vector<RayKernelBase *> & result,
1264  SubdomainID id,
1265  THREAD_ID tid,
1266  RayID ray_id)
1267 {
1268  // No Ray registration: no need to sift through objects
1269  if (!_use_ray_registration)
1270  {
1271  getRayKernels(result, id, tid);
1272  }
1273  // Has Ray registration: only pick the objects associated with ray_id
1274  else
1275  {
1276  // Get all of the kernels on this block
1277  std::vector<RayKernelBase *> rkbs;
1278  getRayKernels(rkbs, id, tid);
1279 
1280  // The RayTracingObjects associated with this ray
1281  const auto & ray_id_rtos = _threaded_ray_object_registration[tid][ray_id];
1282 
1283  // The result is the union of all of the kernels and the objects associated with this Ray
1284  result.clear();
1285  for (auto rkb : rkbs)
1286  if (ray_id_rtos.count(rkb))
1287  result.push_back(rkb);
1288  }
1289 }
1290 
1291 void
1292 RayTracingStudy::getRayBCs(std::vector<RayBoundaryConditionBase *> & result,
1293  BoundaryID id,
1294  THREAD_ID tid)
1295 {
1296  // If the cache doesn't have any attributes yet, it means that we haven't set
1297  // the conditions yet. We do this so that it can be generated on the fly on first use.
1298  if (!_threaded_cache_ray_bc[tid].numAttribs())
1299  {
1300  if (!_called_initial_setup)
1301  mooseError("Should not call getRayBCs() before initialSetup()");
1302 
1303  auto query = _fe_problem.theWarehouse()
1304  .query()
1305  .condition<AttribRayTracingStudy>(this)
1306  .condition<AttribSystem>("RayBoundaryCondition")
1307  .condition<AttribThread>(tid);
1308  _threaded_cache_ray_bc[tid] = query.clone();
1309  }
1310 
1311  _threaded_cache_ray_bc[tid].queryInto(result, std::make_tuple(id, false));
1312 }
1313 
1314 void
1315 RayTracingStudy::getRayBCs(std::vector<RayBoundaryConditionBase *> & result,
1316  const std::vector<TraceRayBndElement> & bnd_elems,
1317  THREAD_ID tid,
1318  RayID ray_id)
1319 {
1320  // No Ray registration: no need to sift through objects
1321  if (!_use_ray_registration)
1322  {
1323  if (bnd_elems.size() == 1)
1324  getRayBCs(result, bnd_elems[0].bnd_id, tid);
1325  else
1326  {
1327  std::vector<BoundaryID> bnd_ids(bnd_elems.size());
1328  for (MooseIndex(bnd_elems.size()) i = 0; i < bnd_elems.size(); ++i)
1329  bnd_ids[i] = bnd_elems[i].bnd_id;
1330  getRayBCs(result, bnd_ids, tid);
1331  }
1332  }
1333  // Has Ray registration: only pick the objects associated with ray_id
1334  else
1335  {
1336  // Get all of the RayBCs on these boundaries
1337  std::vector<RayBoundaryConditionBase *> rbcs;
1338  if (bnd_elems.size() == 1)
1339  getRayBCs(rbcs, bnd_elems[0].bnd_id, tid);
1340  else
1341  {
1342  std::vector<BoundaryID> bnd_ids(bnd_elems.size());
1343  for (MooseIndex(bnd_elems.size()) i = 0; i < bnd_elems.size(); ++i)
1344  bnd_ids[i] = bnd_elems[i].bnd_id;
1345  getRayBCs(rbcs, bnd_ids, tid);
1346  }
1347 
1348  // The RayTracingObjects associated with this ray
1349  mooseAssert(ray_id < _threaded_ray_object_registration[tid].size(), "Not in registration");
1350  const auto & ray_id_rtos = _threaded_ray_object_registration[tid][ray_id];
1351 
1352  // The result is the union of all of the kernels and the objects associated with this Ray
1353  result.clear();
1354  for (auto rbc : rbcs)
1355  if (ray_id_rtos.count(rbc))
1356  result.push_back(rbc);
1357  }
1358 }
1359 
1360 std::vector<RayTracingObject *>
1362 {
1363  std::vector<RayTracingObject *> result;
1364  _fe_problem.theWarehouse().query().condition<AttribRayTracingStudy>(this).queryInto(result);
1365  return result;
1366 }
1367 
1368 const std::vector<std::shared_ptr<Ray>> &
1370 {
1372  mooseError("The Ray bank is not available because the private parameter "
1373  "'_bank_rays_on_completion' is set to false.");
1375  mooseError("Cannot get the Ray bank during generation or propagation.");
1376 
1377  return _ray_bank;
1378 }
1379 
1380 std::shared_ptr<Ray>
1382 {
1383  // This is only a linear search - can be improved on with a map in the future
1384  // if this is used on a larger scale
1385  std::shared_ptr<Ray> ray;
1386  for (const std::shared_ptr<Ray> & possible_ray : rayBank())
1387  if (possible_ray->id() == ray_id)
1388  {
1389  ray = possible_ray;
1390  break;
1391  }
1392 
1393  // Make sure one and only one processor has the Ray
1394  unsigned int have_ray = ray ? 1 : 0;
1395  _communicator.sum(have_ray);
1396  if (have_ray == 0)
1397  mooseError("Could not find a Ray with the ID ", ray_id, " in the Ray banks.");
1398 
1399  // This should never happen... but let's make sure
1400  mooseAssert(have_ray == 1, "Multiple rays with the same ID were found in the Ray banks");
1401 
1402  return ray;
1403 }
1404 
1405 RayData
1407  const RayDataIndex index,
1408  const bool aux) const
1409 {
1410  // Will be a nullptr shared_ptr if this processor doesn't own the Ray
1411  const std::shared_ptr<Ray> ray = getBankedRay(ray_id);
1412 
1413  Real value = ray ? (aux ? ray->auxData(index) : ray->data(index)) : 0;
1415  return value;
1416 }
1417 
1418 RayData
1419 RayTracingStudy::getBankedRayData(const RayID ray_id, const RayDataIndex index) const
1420 {
1421  return getBankedRayDataInternal(ray_id, index, /* aux = */ false);
1422 }
1423 
1424 RayData
1426 {
1427  return getBankedRayDataInternal(ray_id, index, /* aux = */ true);
1428 }
1429 
1430 RayID
1432 {
1433  libmesh_parallel_only(comm());
1434 
1435  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1436 
1437  if (!_use_ray_registration)
1438  mooseError("Cannot use registerRay() with Ray registration disabled");
1439 
1440  // This is parallel only for now. We could likely stagger the ID building like we do with
1441  // the unique IDs, but it would require a sync point which isn't there right now
1442  libmesh_parallel_only(comm());
1443 
1444  const auto & it = _registered_ray_map.find(name);
1445  if (it != _registered_ray_map.end())
1446  return it->second;
1447 
1448  const auto id = _reverse_registered_ray_map.size();
1449  _registered_ray_map.emplace(name, id);
1450  _reverse_registered_ray_map.push_back(name);
1451  return id;
1452 }
1453 
1454 RayID
1455 RayTracingStudy::registeredRayID(const std::string & name, const bool graceful /* = false */) const
1456 {
1457  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1458 
1459  if (!_use_ray_registration)
1460  mooseError("Should not use registeredRayID() with Ray registration disabled");
1461 
1462  const auto search = _registered_ray_map.find(name);
1463  if (search != _registered_ray_map.end())
1464  return search->second;
1465 
1466  if (graceful)
1467  return Ray::INVALID_RAY_ID;
1468 
1469  mooseError("Attempted to obtain ID of registered Ray ",
1470  name,
1471  ", but a Ray with said name is not registered.");
1472 }
1473 
1474 const std::string &
1476 {
1477  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1478 
1479  if (!_use_ray_registration)
1480  mooseError("Should not use registeredRayName() with Ray registration disabled");
1481 
1482  if (_reverse_registered_ray_map.size() > ray_id)
1483  return _reverse_registered_ray_map[ray_id];
1484 
1485  mooseError("Attempted to obtain name of registered Ray with ID ",
1486  ray_id,
1487  ", but a Ray with said ID is not registered.");
1488 }
1489 
1490 Real
1492 {
1493  Real volume = 0;
1494  for (const auto & elem : *_mesh.getActiveLocalElementRange())
1495  volume += elem->volume();
1497  return volume;
1498 }
1499 
1500 const std::vector<std::vector<BoundaryID>> &
1501 RayTracingStudy::getInternalSidesets(const Elem * elem) const
1502 {
1503  mooseAssert(_use_internal_sidesets, "Not using internal sidesets");
1504  mooseAssert(hasInternalSidesets(), "Processor does not have internal sidesets");
1505  mooseAssert(_internal_sidesets_map.size() > _elem_index_helper.getIndex(elem),
1506  "Internal sideset map not initialized");
1507 
1508  const auto index = _elem_index_helper.getIndex(elem);
1509  return _internal_sidesets_map[index];
1510 }
1511 
1512 TraceData &
1513 RayTracingStudy::initThreadedCachedTrace(const std::shared_ptr<Ray> & ray, THREAD_ID tid)
1514 {
1515  mooseAssert(shouldCacheTrace(ray), "Not caching trace");
1516  mooseAssert(currentlyPropagating(), "Should only use while tracing");
1517 
1518  _threaded_cached_traces[tid].emplace_back(ray);
1519  return _threaded_cached_traces[tid].back();
1520 }
1521 
1522 void
1523 RayTracingStudy::verifyUniqueRayIDs(const std::vector<std::shared_ptr<Ray>>::const_iterator begin,
1524  const std::vector<std::shared_ptr<Ray>>::const_iterator end,
1525  const bool global,
1526  const std::string & error_suffix) const
1527 {
1528  // Determine the unique set of Ray IDs on this processor,
1529  // and if not locally unique throw an error. Once we build this set,
1530  // we will send it to rank 0 to verify globally
1531  std::set<RayID> local_rays;
1532  for (const std::shared_ptr<Ray> & ray : as_range(begin, end))
1533  {
1534  mooseAssert(ray, "Null ray");
1535 
1536  // Try to insert into the set; the second entry in the pair
1537  // will be false if it was not inserted
1538  if (!local_rays.insert(ray->id()).second)
1539  {
1540  for (const std::shared_ptr<Ray> & other_ray : as_range(begin, end))
1541  if (ray.get() != other_ray.get() && ray->id() == other_ray->id())
1542  mooseError("Multiple Rays exist with ID ",
1543  ray->id(),
1544  " on processor ",
1545  _pid,
1546  " ",
1547  error_suffix,
1548  "\n\nOffending Ray information:\n\n",
1549  ray->getInfo(),
1550  "\n",
1551  other_ray->getInfo());
1552  }
1553  }
1554 
1555  // Send IDs from all procs to rank 0 and verify on rank 0
1556  if (global)
1557  {
1558  // Package our local IDs and send to rank 0
1559  std::map<processor_id_type, std::vector<RayID>> send_ids;
1560  if (local_rays.size())
1561  send_ids.emplace(std::piecewise_construct,
1562  std::forward_as_tuple(0),
1563  std::forward_as_tuple(local_rays.begin(), local_rays.end()));
1564  local_rays.clear();
1565 
1566  // Mapping on rank 0 from ID -> processor ID
1567  std::map<RayID, processor_id_type> global_map;
1568 
1569  // Verify another processor's IDs against the global map on rank 0
1570  const auto check_ids =
1571  [this, &global_map, &error_suffix](processor_id_type pid, const std::vector<RayID> & ids)
1572  {
1573  for (const RayID id : ids)
1574  {
1575  const auto emplace_pair = global_map.emplace(id, pid);
1576 
1577  // Means that this ID already exists in the map
1578  if (!emplace_pair.second)
1579  mooseError("Ray with ID ",
1580  id,
1581  " exists on ranks ",
1582  emplace_pair.first->second,
1583  " and ",
1584  pid,
1585  "\n",
1586  error_suffix);
1587  }
1588  };
1589 
1590  Parallel::push_parallel_vector_data(_communicator, send_ids, check_ids);
1591  }
1592 }
1593 
1594 void
1595 RayTracingStudy::verifyUniqueRays(const std::vector<std::shared_ptr<Ray>>::const_iterator begin,
1596  const std::vector<std::shared_ptr<Ray>>::const_iterator end,
1597  const std::string & error_suffix)
1598 {
1599  std::set<const Ray *> rays;
1600  for (const std::shared_ptr<Ray> & ray : as_range(begin, end))
1601  if (!rays.insert(ray.get()).second) // false if not inserted into rays
1602  mooseError("Multiple shared_ptrs were found that point to the same Ray ",
1603  error_suffix,
1604  "\n\nOffending Ray:\n",
1605  ray->getInfo());
1606 }
1607 
1608 void
1609 RayTracingStudy::moveRayToBuffer(std::shared_ptr<Ray> & ray)
1610 {
1611  mooseAssert(currentlyGenerating(), "Can only use while generating");
1612  mooseAssert(ray, "Null ray");
1613  mooseAssert(ray->shouldContinue(), "Ray is not continuing");
1614 
1615  _parallel_ray_study->moveWorkToBuffer(ray, /* tid = */ 0);
1616 }
1617 
1618 void
1619 RayTracingStudy::moveRaysToBuffer(std::vector<std::shared_ptr<Ray>> & rays)
1620 {
1621  mooseAssert(currentlyGenerating(), "Can only use while generating");
1622 #ifndef NDEBUG
1623  for (const std::shared_ptr<Ray> & ray : rays)
1624  {
1625  mooseAssert(ray, "Null ray");
1626  mooseAssert(ray->shouldContinue(), "Ray is not continuing");
1627  }
1628 #endif
1629 
1630  _parallel_ray_study->moveWorkToBuffer(rays, /* tid = */ 0);
1631 }
1632 
1633 void
1635  const THREAD_ID tid,
1636  const AcquireMoveDuringTraceKey &)
1637 {
1638  mooseAssert(ray, "Null ray");
1639  mooseAssert(currentlyPropagating(), "Can only use while tracing");
1640 
1641  _parallel_ray_study->moveWorkToBuffer(ray, tid);
1642 }
1643 
1644 void
1645 RayTracingStudy::reserveRayBuffer(const std::size_t size)
1646 {
1647  if (!currentlyGenerating())
1648  mooseError("Can only reserve in Ray buffer during generateRays()");
1649 
1650  _parallel_ray_study->reserveBuffer(size);
1651 }
1652 
1653 const Point &
1654 RayTracingStudy::getSideNormal(const Elem * elem, unsigned short side, const THREAD_ID tid)
1655 {
1656  std::unordered_map<std::pair<const Elem *, unsigned short>, Point> & cache =
1658 
1659  // See if we've already cached this side normal
1660  const auto elem_side_pair = std::make_pair(elem, side);
1661  const auto search = cache.find(elem_side_pair);
1662 
1663  // Haven't cached this side normal: compute it and then cache it
1664  if (search == cache.end())
1665  {
1666  _threaded_fe_face[tid]->reinit(elem, side);
1667  const auto & normal = _threaded_fe_face[tid]->get_normals()[0];
1668  cache.emplace(elem_side_pair, normal);
1669  return normal;
1670  }
1671 
1672  // Have cached this side normal: simply return it
1673  return search->second;
1674 }
1675 
1676 bool
1678 {
1679  unsigned int min_level = std::numeric_limits<unsigned int>::max();
1680  unsigned int max_level = std::numeric_limits<unsigned int>::min();
1681 
1682  for (const auto & elem : *_mesh.getActiveLocalElementRange())
1683  {
1684  const auto level = elem->level();
1685  min_level = std::min(level, min_level);
1686  max_level = std::max(level, max_level);
1687  }
1688 
1689  _communicator.min(min_level);
1690  _communicator.max(max_level);
1691 
1692  return min_level == max_level;
1693 }
1694 
1695 Real
1697 {
1698  const auto find = _subdomain_hmax.find(subdomain_id);
1699  if (find == _subdomain_hmax.end())
1700  mooseError("Subdomain ", subdomain_id, " not found in subdomain hmax map");
1701  return find->second;
1702 }
1703 
1704 bool
1706 {
1707  Real bbox_volume = 1;
1708  for (unsigned int d = 0; d < _mesh.dimension(); ++d)
1709  bbox_volume *= std::abs(_b_box.max()(d) - _b_box.min()(d));
1710 
1711  return MooseUtils::absoluteFuzzyEqual(bbox_volume, totalVolume(), TOLERANCE);
1712 }
1713 
1714 void
1716 {
1717  libmesh_parallel_only(comm());
1718 
1719  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1720 
1721  mooseAssert(!currentlyGenerating() && !currentlyPropagating(),
1722  "Cannot be reset during generation or propagation");
1723 
1724  for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1726 }
1727 
1728 RayID
1730 {
1731  // Get the current ID to return
1732  const auto id = _threaded_next_ray_id[tid];
1733 
1734  // Advance so that the next call has the correct ID
1736 
1737  return id;
1738 }
1739 
1740 void
1742 {
1743  libmesh_parallel_only(comm());
1744 
1745  Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1746 
1747  mooseAssert(!currentlyGenerating() && !currentlyPropagating(),
1748  "Cannot be reset during generation or propagation");
1749 
1751 }
1752 
1753 RayID
1755 {
1756  return _replicated_next_ray_id++;
1757 }
1758 
1759 bool
1761  const unsigned short side,
1762  const Point & direction,
1763  const THREAD_ID tid)
1764 {
1765  const auto & normal = getSideNormal(elem, side, tid);
1766  const auto dot = normal * direction;
1767  return dot < TraceRayTools::TRACE_TOLERANCE;
1768 }
1769 
1770 std::shared_ptr<Ray>
1772 {
1773  mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1774 
1775  return _parallel_ray_study->acquireParallelData(
1776  /* tid = */ 0,
1777  this,
1778  generateUniqueRayID(/* tid = */ 0),
1779  rayDataSize(),
1780  rayAuxDataSize(),
1781  /* reset = */ true,
1783 }
1784 
1785 std::shared_ptr<Ray>
1787 {
1788  mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1789 
1790  return _parallel_ray_study->acquireParallelData(/* tid = */ 0,
1791  this,
1792  generateUniqueRayID(/* tid = */ 0),
1793  /* data_size = */ 0,
1794  /* aux_data_size = */ 0,
1795  /* reset = */ true,
1797 }
1798 
1799 std::shared_ptr<Ray>
1801 {
1802  mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1803  libmesh_parallel_only(comm());
1804 
1805  return _parallel_ray_study->acquireParallelData(
1806  /* tid = */ 0,
1807  this,
1809  rayDataSize(),
1810  rayAuxDataSize(),
1811  /* reset = */ true,
1813 }
1814 
1815 std::shared_ptr<Ray>
1817 {
1818  mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1819 
1820  // Either register a Ray or get an already registered Ray id
1821  const RayID id = registerRay(name);
1822 
1823  // Acquire a Ray with the properly sized data initialized to zero
1824  return _parallel_ray_study->acquireParallelData(
1825  /* tid = */ 0,
1826  this,
1827  id,
1828  rayDataSize(),
1829  rayAuxDataSize(),
1830  /* reset = */ true,
1832 }
1833 
1834 std::shared_ptr<Ray>
1836 {
1837  mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1838  return _parallel_ray_study->acquireParallelData(
1839  /* tid = */ 0, &ray, Ray::ConstructRayKey());
1840 }
1841 
1842 std::shared_ptr<Ray>
1844 {
1845  mooseAssert(currentlyPropagating(), "Can only use during propagation");
1846  return _parallel_ray_study->acquireParallelData(tid,
1847  this,
1848  generateUniqueRayID(tid),
1849  rayDataSize(),
1850  rayAuxDataSize(),
1851  /* reset = */ true,
1853 }
bool hasRayKernels(const THREAD_ID tid)
Whether or not there are currently any active RayKernel objects.
bool currentlyGenerating() const
Whether or not the study is generating.
void getRayKernels(std::vector< RayKernelBase *> &result, SubdomainID id, THREAD_ID tid)
Fills the active RayKernels associated with this study and a block into result.
void internalSidesetSetup()
Does the setup for internal sidesets.
RayDataIndex registerRayData(const std::string &name)
Register a value to be filled in the data on a Ray with a given name.
RayData getBankedRayDataInternal(const RayID ray_id, const RayDataIndex index, const bool aux) const
Internal method for getting the value (replicated across all processors) in a Ray&#39;s data or aux data ...
void periodicBoundaryChecks()
Check for overlapping PeriodicRayBC boundaries and check for cases in which ghosting may not be suffi...
virtual void postOnSegment(const THREAD_ID tid, const std::shared_ptr< Ray > &ray)
Called at the end of a Ray segment.
const bool _tolerate_failure
Whether or not to tolerate a Ray Tracing failure.
Real totalVolume() const
Get the current total volume of the domain.
void traceableMeshChecks()
Check for if all of the element types in the mesh are supported by ray tracing.
virtual void onCompleteRay(const std::shared_ptr< Ray > &ray)
Entry point for acting on a ray when it is completed (shouldContinue() == false)
libMesh::ConstElemRange * getActiveLocalElementRange()
const std::string & getRayDataNameInternal(const RayDataIndex index, const bool aux) const
Internal method for getting the name of Ray data or Ray aux data.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
const std::vector< std::shared_ptr< Ray > > & rayBank() const
Get the Ray bank.
static const RayDataIndex INVALID_RAY_DATA_INDEX
Invalid index into a Ray&#39;s data.
Definition: Ray.h:212
std::vector< std::vector< std::set< const RayTracingObject * > > > _threaded_ray_object_registration
Threaded storage for all of the RayTracingObjects associated with a single Ray.
std::vector< std::string > & _reverse_registered_ray_map
Map from registered Ray ID to name.
unsigned long long int _total_intersections
Total number of Ray/element intersections.
RayID registerRay(const std::string &name)
Registers a Ray with a given name.
unsigned long long int _ending_intersections
Total number of Ray/element intersections for Rays that finished on this processor.
unsigned int _max_trajectory_changes
Max number of trajectory changes for a single Ray.
unsigned long int RayID
Type for a Ray&#39;s ID.
Definition: Ray.h:44
void verifyUniqueRayIDs(const std::vector< std::shared_ptr< Ray >>::const_iterator begin, const std::vector< std::shared_ptr< Ray >>::const_iterator end, const bool global, const std::string &error_suffix) const
Verifies that the Rays in the given range have unique Ray IDs.
unsigned int n_threads()
bool _has_same_level_active_elems
Whether or not the mesh has active elements of the same level.
bool sameLevelActiveElems() const
Determine whether or not the mesh currently has active elements that are all the same level...
auto norm() const
virtual void initialSetup() override
void moveRayToBufferDuringTrace(std::shared_ptr< Ray > &ray, const THREAD_ID tid, const AcquireMoveDuringTraceKey &)
INTERNAL method for moving a Ray into the buffer during tracing.
RayDataIndex registerRayAuxData(const std::string &name)
Register a value to be filled in the aux data on a Ray with a given name.
Class that is used as a parameter to the public constructors/reset methods.
Definition: Ray.h:99
RayBC that enforces periodic boundaries.
Definition: PeriodicRayBC.h:17
void paramError(const std::string &param, Args... args) const
MooseMesh & _mesh
The Mesh.
T & getMesh(MooseMesh &mesh)
function to cast mesh
Definition: SCM.h:35
Attribute for the RayTracingStudy a RayTracingObject is associated with.
static InputParameters validParams()
virtual void prepare(const Elem *elem, const THREAD_ID tid) override
NumericVector< Number > & solution()
std::vector< std::vector< TraceData > > _threaded_cached_traces
The threaded storage for cached traces.
const bool _use_internal_sidesets
Whether or not to use the internal sidesets in ray tracing.
Data structure that stores information for output of a partial trace of a Ray on a processor...
Definition: TraceData.h:42
QGAUSS
bool isTraceableElem(const Elem *elem)
bool isAdaptivityTraceableElem(const Elem *elem)
std::shared_ptr< Ray > acquireRay()
User APIs for constructing Rays within the RayTracingStudy.
std::size_t rayAuxDataSize() const
The registered size of values in the Ray aux data.
std::unordered_map< SubdomainID, Real > _subdomain_hmax
The cached hmax for all elements in a subdomain.
libMesh::dof_id_type maxIndex() const
Gets the maximum index generated using this object.
libMesh::BoundingBox _loose_b_box
Loose nodal bounding box for the domain.
T & set(const std::string &name, bool quiet_mode=false)
const std::set< BoundaryID > & getInternalSidesets() const
Gets the internal sidesets (that have RayBCs) within the local domain.
std::vector< std::vector< unsigned short > > _non_planar_sides
Non planar side data, which is for quick checking if an elem side is non-planar We use unsigned short...
RayData getBankedRayData(const RayID ray_id, const RayDataIndex index) const
Gets the data value for a banked ray with a given ID.
Base object for the RayKernel syntax.
Definition: RayKernelBase.h:27
if(subdm)
MeshBase & mesh
void initialize(const libMesh::SimpleRange< libMesh::MeshBase::element_iterator > elems)
Initializes the indices in a contiguous manner for the given element range.
Real _ending_distance
Total distance traveled by Rays that end on this processor.
virtual void reinitSegment(const Elem *elem, const Point &start, const Point &end, const Real length, THREAD_ID tid)
Reinitialize objects for a Ray segment for ray tracing.
unsigned int size() const
RayDataIndex registerRayDataInternal(const std::string &name, const bool aux)
Internal method for registering Ray data or Ray aux data with a name.
const std::vector< RayKernelBase * > & currentRayKernels(THREAD_ID tid) const
Gets the current RayKernels for a thread, which are set in segmentSubdomainSetup() ...
const Parallel::Communicator & comm() const
Key that is used for restricting access to moveRayToBufferDuringTrace() and acquireRayDuringTrace().
void addAvailableFlags(const ExecFlagType &flag, Args... flags)
std::shared_ptr< Ray > acquireReplicatedRay()
Acquire a Ray from the pool of Rays within generateRays() in a replicated fashion.
std::shared_ptr< Ray > acquireRayDuringTrace(const THREAD_ID tid, const AcquireMoveDuringTraceKey &)
INTERNAL methods for acquiring a Ray during a trace in RayKernels and RayBCs.
const Parallel::Communicator & _communicator
std::shared_ptr< Ray > acquireRegisteredRay(const std::string &name)
Acquires a Ray with a given name within generateRays().
void clearActiveMaterialProperties(const THREAD_ID tid)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
const BoundaryInfo & get_boundary_info() const
RayID _replicated_next_ray_id
Storage for the next available replicated RayID, obtained via generateReplicatedRayID() ...
std::set< BoundaryID > _internal_sidesets
The BoundaryIDs on the local mesh that have internal RayBCs.
virtual RayID generateUniqueRayID(const THREAD_ID tid)
Generates a unique RayID to be used for a Ray.
bool isOn()
std::vector< std::vector< RayKernelBase * > > _threaded_current_ray_kernels
The current RayKernel objects for each thread.
auto max(const L &left, const R &right)
std::vector< TraceData > _cached_traces
Storage for the cached traces.
RayDataIndex getRayAuxDataIndex(const std::string &name, const bool graceful=false) const
Gets the index associated with a registered value in the Ray aux data.
std::vector< std::shared_ptr< TraceRay > > _threaded_trace_ray
The TraceRay objects for each thread (they do the physical tracing)
virtual Assembly & assembly(const THREAD_ID tid, const unsigned int sys_num) override
Real subdomainHmax(const SubdomainID subdomain_id) const
Get the cached hmax for all elements in a subdomain.
const Real TRACE_TOLERANCE
The standard tolerance to use in tracing.
Definition: TraceRayTools.h:48
std::vector< RayID > _threaded_next_ray_id
Storage for the next available unique RayID, obtained via generateUniqueRayID()
bool isRectangularDomain() const
Whether or not the domain is rectangular (if it is prefectly encompassed by its bounding box) ...
std::vector< std::string > _ray_aux_data_names
The names for each Ray aux data entry.
processor_id_type size() const
FEProblemBase & _fe_problem
virtual void setActiveElementalMooseVariables(const std::set< MooseVariableFEBase * > &moose_vars, const THREAD_ID tid) override
Threads::spin_mutex _spin_mutex
Spin mutex object for locks.
uint8_t processor_id_type
std::shared_ptr< Ray > getBankedRay(const RayID ray_id) const
Gets the Ray with the ID ray_id from the Ray bank.
unsigned long long int _total_processor_crossings
Total number of processor crossings.
processor_id_type n_processors() const
CONSTANT
void nonPlanarSideSetup()
Sets up the caching of whether or not each element side is non-planar, which is stored in _non_planar...
bool verifyRays() const
Whether or not to verify if Rays have valid information before being traced.
const std::string & name() const
unsigned int RayDataIndex
Type for the index into the data and aux data on a Ray.
Definition: Ray.h:52
void coverageChecks()
Perform coverage checks (coverage of RayMaterials and RayKernels, if enabled)
bool & useEigenvalue()
void resetReplicatedRayIDs()
Resets the generation of unique replicated RayIDs accessed via generateReplicatedRayID().
int8_t boundary_id_type
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
const bool _bank_rays_on_completion
Whether or not to bank rays on completion.
MeshBase & getMesh()
void min(const T &r, T &o, Request &req) const
unsigned int _ending_max_intersections
Max number of intersections for Rays that finished on this processor.
sideset clear()
TheWarehouse & theWarehouse() const
virtual void postExecuteStudy()
Entry point after study execution.
MooseVariableFE< Real > & variable()
Gets the variable this AuxRayKernel contributes to.
Definition: AuxRayKernel.h:38
unsigned int _max_processor_crossings
Max number of processor crossings for all Rays.
const std::unique_ptr< ParallelRayStudy > _parallel_ray_study
The study that used is to actually execute (trace) the Rays.
virtual unsigned int dimension() const
std::vector< RayTracingObject * > getRayTracingObjects()
Gets all of the currently active RayTracingObjects.
virtual void residualSetup() override
const Point & min() const
const bool & currentlyComputingResidual() const
boundary_id_type BoundaryID
const std::string name
Definition: Setup.h:21
std::vector< TheWarehouse::QueryCache< AttribSubdomains > > _threaded_cache_ray_kernel
Threaded cached subdomain query for RayKernelBase objects pertaining to this study.
virtual void execute() override
Executes the study (generates and propagates Rays)
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
unsigned int _max_intersections
Max number of intersections for a single Ray.
unsigned int _ending_max_trajectory_changes
Max number of trajectory changes for Rays that finished on this processor.
unsigned int _ending_max_processor_crossings
Max number of total processor crossings for Rays that finished on this processor. ...
const std::string & getRayDataName(const RayDataIndex index) const
Gets the name associated with a registered value in the Ray data.
void reserveRayBuffer(const std::size_t size)
Reserve size entires in the Ray buffer.
void zeroAuxVariables()
Zero the AuxVariables that the registered AuxRayKernels contribute to.
std::vector< std::unique_ptr< libMesh::FEBase > > _threaded_fe_face
Face FE used for computing face normals for each thread.
const std::string & type() const
void dependencyChecks()
Perform checks to see if the listed dependencies in the RayTracingObjects exist.
static InputParameters validParams()
const std::string & getRayAuxDataName(const RayDataIndex index) const
Gets the name associated with a registered value in the Ray aux data.
virtual void cacheResidual(const THREAD_ID tid) override
Basic datastructure for a ray that will traverse the mesh.
Definition: Ray.h:57
std::chrono::steady_clock::time_point _execution_start_time
Timing.
std::vector< TheWarehouse::QueryCache< AttribBoundaries > > _threaded_cache_ray_bc
Threaded cached boundary query for RayBC objects pertaining to this study.
virtual void jacobianSetup() override
std::string typeAndName() const
bool isValueSet(const std::string &value) const
unsigned int number() const
std::string stringify(const T &t)
virtual void preExecuteStudy()
Entry point before study execution.
std::unordered_map< std::string, RayDataIndex > _ray_aux_data_map
The map from Ray aux data names to index.
AuxiliarySystem & getAuxiliarySystem()
std::string getBoundaryString(const BoundaryID boundary_id) const
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
const bool _warn_non_planar
Whether not to warn if non-planar faces are found.
libMesh::BoundingBox _b_box
Nodal bounding box for the domain.
virtual void meshChanged() override
libMesh::dof_id_type getIndex(const libMesh::Elem *elem) const
Get the index associated with the element elem.
virtual void setCurrentSubdomainID(const Elem *elem, const THREAD_ID tid) override
virtual void generateRays()=0
Subclasses should override this to determine how to generate Rays.
RayID generateReplicatedRayID()
Generates a Ray ID that is replicated across all processors.
virtual void close()=0
const std::multimap< const Elem *, std::pair< unsigned short int, boundary_id_type > > & get_sideset_map() const
MONOMIAL
const ExecFlagEnum & _execute_enum
RayDataIndex getRayDataIndexInternal(const std::string &name, const bool aux, const bool graceful) const
Internal method for getting the index of Ray data or Ray aux data.
void verifyDependenciesExist(const std::vector< RayTracingObject *> &rtos)
Verifies that the dependencies exist for a set of RayTracingObjects.
unsigned long long int _ending_processor_crossings
Total number of processor crossings for Rays that finished on this processor.
query_obj query
ElemIndexHelper _elem_index_helper
Helper for defining a local contiguous index for each element.
RayTracingStudy(const InputParameters &parameters)
virtual unsigned int n_sides() const=0
SystemBase & _sys
std::vector< std::unordered_map< std::pair< const Elem *, unsigned short >, Point > > _threaded_cached_normals
Threaded cache for side normals that have been computed already during tracing.
TraceData & initThreadedCachedTrace(const std::shared_ptr< Ray > &ray, THREAD_ID tid)
Initialize a Ray in the threaded cached trace map to be filled with segments.
virtual const SystemBase & getSystemBase(const unsigned int sys_num) const
const Elem * neighbor_ptr(unsigned int i) const
virtual const Point & getSideNormal(const Elem *elem, const unsigned short side, const THREAD_ID tid)
Get the outward normal for a given element side.
const SubdomainID ANY_BLOCK_ID
unsigned int level() const
std::vector< RayDataIndex > getRayAuxDataIndices(const std::vector< std::string > &names, const bool graceful=false) const
Gets the indices associated with registered values in the Ray aux data.
std::unordered_map< std::string, RayDataIndex > _ray_data_map
The map from Ray data names to index.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< RayDataIndex > getRayDataIndices(const std::vector< std::string > &names, const bool graceful=false) const
Gets the indices associated with registered values in the Ray data.
bool currentlyPropagating() const
Whether or not the study is propagating (tracing Rays)
subdomain_id_type subdomain_id() const
bool _has_non_planar_sides
Whether or not the local mesh has elements with non-planar sides.
virtual void subdomainSetup(SubdomainID subdomain, const THREAD_ID tid)
const libMesh::Elem & elemSide(const libMesh::Elem &elem, const unsigned int s, const THREAD_ID tid=0)
Get an element&#39;s side pointer without excessive memory allocation.
void max(const T &r, T &o, Request &req) const
void reinitMaterials(SubdomainID blk_id, const THREAD_ID tid, bool swap_stateful=true)
auto norm(const T &a)
void getRayBCs(std::vector< RayBoundaryConditionBase *> &result, BoundaryID id, THREAD_ID tid)
Fills the active RayBCs associated with this study and a boundary into result.
std::vector< std::size_t > _num_cached
Number of currently cached objects for Jacobian/residual for each thread.
std::chrono::steady_clock::duration _propagation_time
Query query()
virtual void segmentSubdomainSetup(const SubdomainID subdomain, const THREAD_ID tid, const RayID ray_id)
Setup for on subdomain change or subdomain AND ray change during ray tracing.
void mooseWarning(Args &&... args) const
RayData getBankedRayAuxData(const RayID ray_id, const RayDataIndex index) const
Gets the data value for a banked ray with a given ID.
bool _called_initial_setup
Whether or not we&#39;ve called initial setup - used to stop from late registration.
void mooseError(Args &&... args) const
void verifyUniqueRays(const std::vector< std::shared_ptr< Ray >>::const_iterator begin, const std::vector< std::shared_ptr< Ray >>::const_iterator end, const std::string &error_suffix)
Verifies that the Rays in the given range are unique.
std::vector< RayDataIndex > getRayDataIndicesInternal(const std::vector< std::string > &names, const bool aux, const bool graceful) const
Internal method for getting the indicies of Ray data or Ray aux data.
Real _total_distance
Total distance traveled by all Rays.
const processor_id_type _pid
The rank of this processor (this actually takes time to lookup - so just do it once) ...
std::unordered_map< std::string, RayID > & _registered_ray_map
Map from registered Ray name to ID.
std::vector< std::string > _ray_data_names
The names for each Ray data entry.
float RayData
Type for a Ray&#39;s data.
Definition: Ray.h:47
const std::string & registeredRayName(const RayID ray_id) const
Gets the name of a registered ray.
void moveRayToBuffer(std::shared_ptr< Ray > &ray)
Moves a ray to the buffer to be traced during generateRays().
const ExecFlagType EXEC_PRE_KERNELS
RayID registeredRayID(const std::string &name, const bool graceful=false) const
Gets the ID of a registered ray.
virtual void buildSegmentQuadrature(const Point &start, const Point &end, const Real length, std::vector< Point > &points, std::vector< Real > &weights) const
Builds quadrature points for a given segment using the _segment_qrule.
libMesh::StoredRange< MooseMesh::const_bnd_elem_iterator, const BndElement *> * getBoundaryElementRange()
const bool _use_ray_registration
Whether or not to use Ray registration.
virtual bool hasActiveElementalMooseVariables(const THREAD_ID tid) const
const bool _ray_kernel_coverage_check
Whether or not to perform coverage checks on RayKernels.
void localElemIndexSetup()
Sets up the _elem_index_helper, which is used for obtaining a contiguous index for all elements that ...
const bool & currentlyComputingJacobian() const
std::vector< std::shared_ptr< Ray > > _ray_bank
Cumulative Ray bank - stored only when _bank_rays_on_completion.
libMesh::BoundingBox create_nodal_bounding_box(const MeshBase &mesh)
const Point & max() const
std::vector< unsigned long long int > _local_trace_ray_results
Cumulative results on this processor from the threaded TraceRay objects.
std::chrono::steady_clock::duration _generation_time
void registeredRaySetup()
Sets up the maps from Ray to associated RayTracingObjects if _use_ray_registration.
void scale(const Real factor)
void moveRaysToBuffer(std::vector< std::shared_ptr< Ray >> &rays)
Moves rays to the buffer to be traced during generateRays().
void executeStudy()
Method for executing the study so that it can be called out of the standard UO execute() ...
virtual bool isDistributedMesh() const
void prepareMaterials(const std::unordered_set< unsigned int > &consumer_needed_mat_props, const SubdomainID blk_id, const THREAD_ID tid)
Adaptivity & adaptivity()
bool hasActiveMaterialProperties(const THREAD_ID tid) const
virtual void cacheJacobian(const THREAD_ID tid) override
auto min(const L &left, const R &right)
virtual void reinitElemPhys(const Elem *elem, const std::vector< Point > &phys_points_in_elem, const THREAD_ID tid) override
std::shared_ptr< Ray > acquireUnsizedRay()
Acquire a Ray from the pool of Rays within generateRays(), without resizing the data (sizes the data ...
RayDataIndex getRayDataIndex(const std::string &name, const bool graceful=false) const
Gets the index associated with a registered value in the Ray data.
void resetUniqueRayIDs()
Resets the generation of unique RayIDs via generateUniqueRayID() to the beginning of the range...
virtual void addCachedResidual(const THREAD_ID tid) override
std::chrono::steady_clock::duration _execution_time
virtual void zeroVariables(std::vector< std::string > &vars_to_be_zeroed)
virtual void clearActiveElementalMooseVariables(const THREAD_ID tid) override
std::vector< std::unique_ptr< libMesh::QBase > > _threaded_q_face
Face quadrature used for computing face normals for each thread.
std::shared_ptr< Ray > acquireCopiedRay(const Ray &ray)
Acquires a Ray that that is copied from another Ray within generateRays().
Traces Rays through the mesh on a single processor.
Definition: TraceRay.h:46
std::vector< std::vector< std::vector< BoundaryID > > > _internal_sidesets_map
Internal sideset data, if internal sidesets exist (indexed with getLocalElemIndex()) ...
void subdomainHMaxSetup()
Caches the hmax for all elements in each subdomain.
bool sideIsIncoming(const Elem *const elem, const unsigned short side, const Point &direction, const THREAD_ID tid)
Whether or not side is incoming on element elem in direction direction.
static const RayID INVALID_RAY_ID
Invalid Ray ID.
Definition: Ray.h:214
std::unique_ptr< libMesh::QBase > _segment_qrule
Quadrature rule for laying points across a 1D ray segment.
bool hasInternalSidesets() const
Whether or not the local mesh has internal sidesets that have RayBCs on them.
std::size_t rayDataSize() const
The registered size of values in the Ray data.
unsigned int THREAD_ID
virtual void timestepSetup() override
const std::set< SubdomainID > & meshSubdomains() const
virtual void addCachedJacobian(const THREAD_ID tid) override
virtual bool shouldCacheTrace(const std::shared_ptr< Ray > &) const
Virtual that allows for selection in if a Ray should be cached or not (only used when _cache_traces)...
void set_union(T &data, const unsigned int root_id) const
const RemoteElem * remote_elem
Real computeTotalVolume()
Helper function for computing the total domain volume.