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