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 // Ray tracing's per-thread data structures (below) are always sized to the process-wide
189 // libMesh/OpenMP thread count, not this application's (possibly smaller) thread count, so the
190 // two must match or ray tracing threads would run with no corresponding per-thread storage.
192 mooseError("Ray tracing requires that this application's number of threads "
193 "(Application/num_threads) match the number of threads used by libMesh/OpenMP "
194 "(set by --n-threads).");
195
196 // Initialize a tracing object for each thread
197 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
198 {
199 // Initialize a tracing object for each thread
200 _threaded_trace_ray[tid] = std::make_shared<TraceRay>(*this, tid);
201
202 // Setup the face FEs for normal computation on the fly
203 _threaded_fe_face[tid] =
204 FEBase::build(_mesh.dimension(), FEType(CONSTANT, MONOMIAL).set_p_refinement(false));
205 _threaded_q_face[tid] = QBase::build(libMesh::QGAUSS, _mesh.dimension() - 1, CONSTANT);
206 _threaded_fe_face[tid]->attach_quadrature_rule(_threaded_q_face[tid].get());
207 _threaded_fe_face[tid]->get_normals();
208 }
209
210 // Evaluating on residual and Jacobian evaluation
212 {
213 if (!getParam<bool>("allow_other_flags_with_prekernels") && _execute_enum.size() > 1)
214 paramError("execute_on",
215 "PRE_KERNELS cannot be mixed with any other execution flag.\nThat is, you cannot "
216 "currently "
217 "mix RayKernels that contribute to the Jacobian/residual with those that do not.");
218
219 if (_app.useEigenvalue())
220 mooseError("Execution on residual and Jacobian evaluation (execute_on = PRE_KERNELS)\n",
221 "is not supported for an eigenvalue solve.");
222 }
223
226
227 // Scale the bounding box for loose checking
229 _loose_b_box.scale(TOLERANCE * TOLERANCE);
230}
231
232void
234{
235 // Keep track of initialSetup call to avoid registration of various things
237
238 // Sets up a local index for each elem this proc knows about
240
241 // Check for RayKernel coverage
243
244 // Make sure the dependencies exist, if any
246
247 // Check for traceable element types
249
250 // Check for sane periodic boundaries
252
253 // Setup for internal sidesets
255
257
258 // Setup approximate hmax for each subdomain
260
261 // Call initial setup on all of the objects
262 for (auto & rto : getRayTracingObjects())
263 rto->initialSetup();
264
265 // Check for proper exec flags with RayKernels
266 std::vector<RayKernelBase *> ray_kernels;
267 getRayKernels(ray_kernels, 0);
268 for (const auto & rkb : ray_kernels)
269 if (dynamic_cast<RayKernel *>(rkb) && !_execute_enum.isValueSet(EXEC_PRE_KERNELS))
270 mooseError("This study has RayKernel objects that contribute to residuals and Jacobians.",
271 "\nIn this case, the study must use the execute_on = PRE_KERNELS");
272
273 // Build 1D quadrature rule for along a segment
274 _segment_qrule = QBase::build(
276}
277
278void
280{
281 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
282 mooseAssert(_num_cached[tid] == 0, "Cached residuals/Jacobians not empty");
283
284 for (auto & rto : getRayTracingObjects())
285 rto->residualSetup();
286}
287
288void
290{
291 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
292 mooseAssert(_num_cached[tid] == 0, "Cached residuals/Jacobians not empty");
293
294 for (auto & rto : getRayTracingObjects())
295 rto->jacobianSetup();
296}
297
298void
300{
301 for (auto & rto : getRayTracingObjects())
302 rto->timestepSetup();
303}
304
305void
307{
312
314
315 for (const auto & trace_ray : _threaded_trace_ray)
316 trace_ray->meshChanged();
317}
318
319void
324
325void
327{
328 // Check for coverage of RayKernels on domain
330 {
331 std::vector<RayKernelBase *> ray_kernels;
332 getRayKernels(ray_kernels, 0);
333
334 std::set<SubdomainID> ray_kernel_blocks;
335 for (const auto & rk : ray_kernels)
336 ray_kernel_blocks.insert(rk->blockIDs().begin(), rk->blockIDs().end());
337
338 std::set<SubdomainID> missing;
339 std::set_difference(_mesh.meshSubdomains().begin(),
340 _mesh.meshSubdomains().end(),
341 ray_kernel_blocks.begin(),
342 ray_kernel_blocks.end(),
343 std::inserter(missing, missing.begin()));
344
345 if (!missing.empty() && !ray_kernel_blocks.count(Moose::ANY_BLOCK_ID))
346 {
347 std::ostringstream error;
348 error << "Subdomains { ";
349 std::copy(missing.begin(), missing.end(), std::ostream_iterator<SubdomainID>(error, " "));
350 error << "} do not have RayKernels defined!";
351
352 mooseError(error.str());
353 }
354 }
355}
356
357void
359{
360 std::vector<RayTracingObject *> ray_tracing_objects;
361
362 getRayKernels(ray_tracing_objects, 0);
363 verifyDependenciesExist(ray_tracing_objects);
364
365 getRayBCs(ray_tracing_objects, 0);
366 verifyDependenciesExist(ray_tracing_objects);
367}
368
369void
370RayTracingStudy::verifyDependenciesExist(const std::vector<RayTracingObject *> & rtos)
371{
372 for (const auto & rto : rtos)
373 for (const auto & dep_name : rto->getRequestedItems())
374 {
375 bool found = false;
376 for (const auto & rto_search : rtos)
377 if (rto_search->name() == dep_name)
378 {
379 found = true;
380 break;
381 }
382
383 if (!found)
384 rto->paramError("depends_on", "The ", rto->getBase(), " '", dep_name, "' does not exist");
385 }
386}
387
388void
390{
391 for (const auto & elem : *_mesh.getActiveLocalElementRange())
392 {
394 {
396 mooseError("Element type ",
397 Utility::enum_to_string(elem->type()),
398 " is not supported in ray tracing with adaptivity");
399 }
400 else if (!TraceRayTools::isTraceableElem(elem))
401 mooseError("Element type ",
402 Utility::enum_to_string(elem->type()),
403 " is not supported in ray tracing");
404 }
405}
406
407void
409{
410 // Collect the PeriodicRayBCs
411 std::vector<const RayBoundaryConditionBase *> rbc_ptrs;
412 getRayBCs(rbc_ptrs, 0);
413 std::vector<const PeriodicRayBC *> prbc_ptrs;
414 for (const auto rbc_ptr : rbc_ptrs)
415 if (const auto prbc_ptr = dynamic_cast<const PeriodicRayBC *>(rbc_ptr))
416 prbc_ptrs.push_back(prbc_ptr);
417 if (prbc_ptrs.empty())
418 return;
419
420 // Collect each of the periodic boundaries
421 std::map<boundary_id_type,
422 std::tuple<const PeriodicRayBC *,
424 std::unordered_set<dof_id_type>>>
425 boundary_map;
426 for (const auto prbc_ptr : prbc_ptrs)
427 {
428 for (const auto & [bid, pb] : prbc_ptr->getPeriodicBoundaries())
429 {
430 const auto [it, inserted] = boundary_map.emplace(
431 std::piecewise_construct,
432 std::tuple{bid},
433 std::forward_as_tuple(prbc_ptr, pb.get(), std::unordered_set<dof_id_type>()));
434 if (!inserted)
435 prbc_ptr->mooseError("The periodic boundary '",
437 "' has been defined in both ",
438 prbc_ptr->typeAndName(),
439 " and ",
440 std::get<0>(it->second)->typeAndName());
441 }
442 }
443
444 // Because we don't have ghosting setup correctly yet, we need to check if any
445 // of the periodic boundaries are neighbors with distributed mesh. If the
446 // mesh is replicated, we don't need to check this. See #31280.
447 if (comm().size() == 1 || !_mesh.isDistributedMesh())
448 return;
449
450 // Collect all of the nodes that are on each periodic boundary
451 const auto & sideset_map = _mesh.getMesh().get_boundary_info().get_sideset_map();
452 for (const auto & [elem, side_bid_pair] : sideset_map)
453 {
454 const auto [side, bid] = side_bid_pair;
455 if (auto it = boundary_map.find(bid); it != boundary_map.end())
456 for (const auto n : elem->nodes_on_side(side))
457 std::get<2>(it->second).insert(elem->node_ref(n).id());
458 }
459
460 // Distributed meshes have distributed boundary information, so sync
461 for (auto & bid_tuple_pair : boundary_map)
462 comm().set_union(std::get<2>(bid_tuple_pair.second));
463
464 // Check for periodic boundaries that share nodes
465 std::map<std::pair<boundary_id_type, boundary_id_type>,
466 std::pair<const PeriodicRayBC *, const PeriodicRayBC *>>
467 warn_boundaries;
468 for (auto it = boundary_map.begin(); it != boundary_map.end(); ++it)
469 {
470 const auto & [bid, tup] = *it;
471 const auto [prbc_ptr, pb, node_ids] = tup;
472
473 for (auto other_it = std::next(it); other_it != boundary_map.end(); ++other_it)
474 {
475 const auto & [other_bid, other_tup] = *other_it;
476
477 // Don't check against boundaries that are paried together
478 if (pb->pairedboundary == other_bid)
479 continue;
480
481 const auto other_prbc_ptr = std::get<0>(other_tup);
482 const auto & other_node_ids = std::get<2>(other_tup);
483 for (const auto node_id : node_ids)
484 if (other_node_ids.count(node_id))
485 {
486 if (!warn_boundaries.count(std::make_pair(other_bid, bid)))
487 warn_boundaries.emplace(std::make_pair(bid, other_bid),
488 std::make_pair(prbc_ptr, other_prbc_ptr));
489 break;
490 }
491 }
492 }
493
494 if (warn_boundaries.size())
495 {
496 std::ostringstream oss;
497 oss << warn_boundaries.size()
498 << " ray tracing periodic boundaries were found to be neighbors:\n\n";
499 for (const auto & [bids_pair, prbc_ptrs_pair] : warn_boundaries)
500 {
501 const auto [bid, paired_bid] = bids_pair;
502 const auto [prbc_ptr, paired_prbc_ptr] = prbc_ptrs_pair;
503 oss << " '" << _mesh.getBoundaryString(bid) << "' (in " << prbc_ptr->typeAndName()
504 << ") <-> '" << _mesh.getBoundaryString(paired_bid) << "' (in "
505 << paired_prbc_ptr->typeAndName() << ")\n";
506 }
507 oss << "\nThe periodic propagation of rays at points where two or more periodic"
508 << "\nboundaries meet is not fully supported with a distributed mesh."
509 << "\n\nIf you encounter trace failures, you should use a replicated mesh.";
510 mooseWarning(oss.str());
511 }
512}
513
514void
516{
517 // TODO: We could probably minimize this to local active elements followed by
518 // boundary point neighbors, but if using distibuted mesh it really shouldn't matter
519 _elem_index_helper.initialize(_mesh.getMesh().active_element_ptr_range());
520}
521
522void
524{
525 // Even if we have _use_internal_sidesets == false, we will make sure the user didn't add RayBCs
526 // on internal boundaries
527
528 // Clear the data structures and size the map based on the elements that we know about
529 _internal_sidesets.clear();
532
533 // First, we are going to store all elements with internal sidesets (if any) that have active
534 // RayBCs on them as elem -> vector of (side, vector of boundary ids)
535 for (const auto & bnd_elem : *_mesh.getBoundaryElementRange())
536 {
537 Elem * elem = bnd_elem->_elem;
538 const unsigned int side = bnd_elem->_side;
539 const auto bnd_id = bnd_elem->_bnd_id;
540
541 // Not internal
542 const Elem * const neighbor = elem->neighbor_ptr(side);
543 if (!neighbor || neighbor == remote_elem)
544 continue;
545
546 // No RayBCs on this sideset
547 std::vector<RayBoundaryConditionBase *> result;
548 getRayBCs(result, bnd_id, 0);
549 if (result.empty())
550 continue;
551
552 if (neighbor->subdomain_id() == elem->subdomain_id())
553 mooseError("RayBCs exist on internal sidesets that are not bounded by a different",
554 "\nsubdomain on each side.",
555 "\n\nIn order to use RayBCs on internal sidesets, said sidesets must have",
556 "\na different subdomain on each side.");
557
558 // Mark that this boundary is an internal sideset with RayBC(s)
559 _internal_sidesets.insert(bnd_id);
560
561 // Get elem's entry in the internal sidset data structure
562 const auto index = _elem_index_helper.getIndex(elem);
563 auto & entry = _internal_sidesets_map[index];
564
565 // Initialize this elem's sides if they have not been already
566 if (entry.empty())
567 entry.resize(elem->n_sides(), std::vector<BoundaryID>());
568
569 // Add the internal boundary to the side entry
570 entry[side].push_back(bnd_id);
571 }
572
574 mooseError("RayBCs are defined on internal sidesets, but the study is not set to use ",
575 "internal sidesets during tracing.",
576 "\n\nSet the parameter use_internal_sidesets = true to enable this capability.");
577}
578
579void
581{
582 _has_non_planar_sides = false;
583 bool warned = !_warn_non_planar;
584
585 // Nothing to do here for 2D or 1D
586 if (_mesh.dimension() != 3)
587 return;
588
589 // Clear the data structure and size it based on the elements that we know about
590 _non_planar_sides.clear();
592
593 for (const Elem * elem : _mesh.getMesh().active_element_ptr_range())
594 {
595 const auto index = _elem_index_helper.getIndex(elem);
596 auto & entry = _non_planar_sides[index];
597 entry.resize(elem->n_sides(), 0);
598
599 for (const auto s : elem->side_index_range())
600 {
601 const auto & side = elemSide(*elem, s);
602 if (side.n_vertices() < 4)
603 continue;
604
605 if (!side.has_affine_map())
606 {
607 entry[s] = 1;
609
610 if (!warned)
611 {
612 mooseWarning("The mesh contains non-planar faces.\n\n",
613 "Ray tracing on non-planar faces is an approximation and may fail.\n\n",
614 "Use at your own risk! You can disable this warning by setting the\n",
615 "parameter 'warn_non_planar' to false.");
616 warned = true;
617 }
618 }
619 }
620 }
621}
622
623void
625{
626 // Setup map with subdomain keys
627 _subdomain_hmax.clear();
628 for (const auto subdomain_id : _mesh.meshSubdomains())
629 _subdomain_hmax[subdomain_id] = std::numeric_limits<Real>::min();
630
631 // Set local max for each subdomain
632 for (const auto & elem : *_mesh.getActiveLocalElementRange())
633 {
634 auto & entry = _subdomain_hmax.at(elem->subdomain_id());
635 entry = std::max(entry, elem->hmax());
636 }
637
638 // Accumulate global max for each subdomain
640
641 if (getParam<bool>("warn_subdomain_hmax"))
642 {
643 const auto warn_prefix = type() + " '" + name() + "': ";
644 const auto warn_suffix =
645 "\n\nRay tracing uses an approximate element size for each subdomain to scale the\n"
646 "tolerances used in computing ray intersections. This warning suggests that the\n"
647 "approximate element size is not a good approximation. This is likely due to poor\n"
648 "element aspect ratios.\n\n"
649 "This warning is only output for the first element affected.\n"
650 "To disable this warning, set warn_subdomain_hmax = false.\n";
651
652 for (const auto & elem : *_mesh.getActiveLocalElementRange())
653 {
654 const auto hmin = elem->hmin();
655 const auto hmax = elem->hmax();
656 const auto max_hmax = subdomainHmax(elem->subdomain_id());
657
658 const auto hmax_rel = hmax / max_hmax;
659 if (hmax_rel < 1.e-2 || hmax_rel > 1.e2)
660 mooseDoOnce(mooseWarning(warn_prefix,
661 "Element hmax varies significantly from subdomain hmax.\n",
662 warn_suffix,
663 "First element affected:\n",
664 Moose::stringify(*elem)););
665
666 const auto h_rel = max_hmax / hmin;
667 if (h_rel > 1.e2)
668 mooseDoOnce(mooseWarning(warn_prefix,
669 "Element hmin varies significantly from subdomain hmax.\n",
670 warn_suffix,
671 "First element affected:\n",
672 Moose::stringify(*elem)););
673 }
674 }
675}
676
677void
679{
680 // First, clear the objects associated with each Ray on each thread
681 const auto num_rays = _registered_ray_map.size();
682 for (auto & entry : _threaded_ray_object_registration)
683 {
684 entry.clear();
685 entry.resize(num_rays);
686 }
687
688 const auto rtos = getRayTracingObjects();
689
691 {
692 // All of the registered ray names - used when a RayTracingObject did not specify
693 // any Rays so it should be associated with all Rays.
694 std::vector<std::string> all_ray_names;
695 all_ray_names.reserve(_registered_ray_map.size());
696 for (const auto & pair : _registered_ray_map)
697 all_ray_names.push_back(pair.first);
698
699 for (auto & rto : rtos)
700 {
701 // The Ray names associated with this RayTracingObject
702 const auto & ray_names = rto->parameters().get<std::vector<std::string>>("rays");
703 // The registration for RayTracingObjects for the thread rto is on
704 const auto tid = rto->parameters().get<THREAD_ID>("_tid");
705 auto & registration = _threaded_ray_object_registration[tid];
706
707 // Register each Ray for this object in the registration
708 for (const auto & ray_name : (ray_names.empty() ? all_ray_names : ray_names))
709 {
710 const auto id = registeredRayID(ray_name, /* graceful = */ true);
711 if (ray_names.size() && id == Ray::INVALID_RAY_ID)
712 rto->paramError(
713 "rays", "Supplied ray '", ray_name, "' is not a registered Ray in ", typeAndName());
714 registration[id].insert(rto);
715 }
716 }
717 }
718 // Not using Ray registration
719 else
720 {
721 for (const auto & rto : rtos)
722 if (rto->parameters().get<std::vector<std::string>>("rays").size())
723 rto->paramError(
724 "rays",
725 "Rays cannot be supplied when the study does not require Ray registration.\n\n",
726 type(),
727 " does not require Ray registration.");
728 }
729}
730
731void
733{
734 std::set<std::string> vars_to_be_zeroed;
735 std::vector<RayKernelBase *> ray_kernels;
736 getRayKernels(ray_kernels, 0);
737 for (auto & rk : ray_kernels)
738 {
739 AuxRayKernel * aux_rk = dynamic_cast<AuxRayKernel *>(rk);
740 if (aux_rk)
741 vars_to_be_zeroed.insert(aux_rk->variable().name());
742 }
743
744 std::vector<std::string> vars_to_be_zeroed_vec(vars_to_be_zeroed.begin(),
745 vars_to_be_zeroed.end());
746 _fe_problem.getAuxiliarySystem().zeroVariables(vars_to_be_zeroed_vec);
747}
748
749void
751 const THREAD_ID tid,
752 const RayID ray_id)
753{
754 mooseAssert(currentlyPropagating(), "Should not call while not propagating");
755
756 // Call subdomain setup on FE
757 _fe_problem.subdomainSetup(subdomain, tid);
758
759 std::set<MooseVariableFEBase *> needed_moose_vars;
760 std::unordered_set<unsigned int> needed_mat_props;
761
762 // Get RayKernels and their dependencies and call subdomain setup
763 getRayKernels(_threaded_current_ray_kernels[tid], subdomain, tid, ray_id);
764 for (auto & rkb : _threaded_current_ray_kernels[tid])
765 {
766 rkb->subdomainSetup();
767
768 const auto & mv_deps = rkb->getMooseVariableDependencies();
769 needed_moose_vars.insert(mv_deps.begin(), mv_deps.end());
770
771 const auto & mp_deps = rkb->getMatPropDependencies();
772 needed_mat_props.insert(mp_deps.begin(), mp_deps.end());
773 }
774
775 // Prepare aux vars
776 for (auto & var : needed_moose_vars)
777 if (var->kind() == Moose::VarKindType::VAR_AUXILIARY)
778 var->prepareAux();
779
780 _fe_problem.setActiveElementalMooseVariables(needed_moose_vars, tid);
781 _fe_problem.prepareMaterials(needed_mat_props, subdomain, tid);
782}
783
784void
786 const Elem * elem, const Point & start, const Point & end, const Real length, THREAD_ID tid)
787{
788 mooseAssert(MooseUtils::absoluteFuzzyEqual((start - end).norm(), length), "Invalid length");
789 mooseAssert(currentlyPropagating(), "Should not call while not propagating");
790
792
793 // If we have any variables or material properties that are active, we definitely need to reinit
796 // If not, make sure that the RayKernels have not requested a reinit (this could happen when a
797 // RayKernel doesn't have variables or materials but still does an integration and needs qps)
798 if (!reinit)
799 for (const RayKernelBase * rk : currentRayKernels(tid))
800 if (rk->needSegmentReinit())
801 {
802 reinit = true;
803 break;
804 }
805
806 if (reinit)
807 {
808 _fe_problem.prepare(elem, tid);
809
810 std::vector<Point> points;
811 std::vector<Real> weights;
812 buildSegmentQuadrature(start, end, length, points, weights);
813 _fe_problem.reinitElemPhys(elem, points, tid);
815
816 _fe_problem.reinitMaterials(elem->subdomain_id(), tid);
817 }
818}
819
820void
822 const Point & end,
823 const Real length,
824 std::vector<Point> & points,
825 std::vector<Real> & weights) const
826{
827 points.resize(_segment_qrule->n_points());
828 weights.resize(_segment_qrule->n_points());
829
830 const Point diff = end - start;
831 const Point sum = end + start;
832 mooseAssert(MooseUtils::absoluteFuzzyEqual(length, diff.norm()), "Invalid length");
833
834 // The standard quadrature rule should be on x = [-1, 1]
835 // To scale the points, you...
836 // - Scale to size of the segment in 3D
837 // initial_scaled_qp = x_qp * 0.5 * (end - start) = 0.5 * x_qp * diff
838 // - Shift quadrature midpoint to segment midpoint
839 // final_qp = initial_scaled_qp + 0.5 * (end - start) = initial_scaled_qp + 0.5 * sum
840 // = 0.5 * (x_qp * diff + sum)
841 for (unsigned int qp = 0; qp < _segment_qrule->n_points(); ++qp)
842 {
843 points[qp] = 0.5 * (_segment_qrule->qp(qp)(0) * diff + sum);
844 weights[qp] = 0.5 * _segment_qrule->w(qp) * length;
845 }
846}
847
848void
849RayTracingStudy::postOnSegment(const THREAD_ID tid, const std::shared_ptr<Ray> & /* ray */)
850{
851 mooseAssert(currentlyPropagating(), "Should not call while not propagating");
853 mooseAssert(_num_cached[tid] == 0,
854 "Values should only be cached when computing Jacobian/residual");
855
856 // Fill into cached Jacobian/residuals if necessary
858 {
860
861 if (++_num_cached[tid] == 20)
862 {
863 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
865 _num_cached[tid] = 0;
866 }
867 }
869 {
871
872 if (++_num_cached[tid] == 20)
873 {
874 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
876 _num_cached[tid] = 0;
877 }
878 }
879}
880
881void
883{
884 TIME_SECTION("executeStudy", 2, "Executing Study");
885
886 mooseAssert(_called_initial_setup, "Initial setup not called");
887
888 // Reset ray start/complete timers
892
893 // Reset physical tracing stats
894 for (auto & val : _local_trace_ray_results)
895 val = 0;
896
897 // Reset crossing and intersection
905 _total_distance = 0;
906
907 // Zero the AuxVariables that our AuxRayKernels contribute to before they accumulate
909
911 for (auto & rto : getRayTracingObjects())
912 rto->preExecuteStudy();
913
914 _ray_bank.clear();
915
916 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
917 {
918 _threaded_trace_ray[tid]->preExecute();
919 _threaded_cached_normals[tid].clear();
920 }
921
923 _execution_start_time = std::chrono::steady_clock::now();
924
925 _parallel_ray_study->preExecute();
926
927 {
928 {
929 auto generation_start_time = std::chrono::steady_clock::now();
930
931 TIME_SECTION("generateRays", 2, "Generating Rays");
932
933 generateRays();
934
935 _generation_time = std::chrono::steady_clock::now() - generation_start_time;
936 }
937
938 // At this point, nobody is working so this is good time to make sure
939 // Rays are unique across all processors in the working buffer
940 if (verifyRays())
941 {
942 verifyUniqueRays(_parallel_ray_study->workBuffer().begin(),
943 _parallel_ray_study->workBuffer().end(),
944 /* error_suffix = */ "after generateRays()");
945
946 verifyUniqueRayIDs(_parallel_ray_study->workBuffer().begin(),
947 _parallel_ray_study->workBuffer().end(),
948 /* global = */ true,
949 /* error_suffix = */ "after generateRays()");
950 }
951
953
954 {
955 TIME_SECTION("propagateRays", 2, "Propagating Rays");
956
957 const auto propagation_start_time = std::chrono::steady_clock::now();
958
959 _parallel_ray_study->execute();
960
961 _propagation_time = std::chrono::steady_clock::now() - propagation_start_time;
962 }
963 }
964
965 _execution_time = std::chrono::steady_clock::now() - _execution_start_time;
966
967 if (verifyRays())
968 {
969 verifyUniqueRays(_parallel_ray_study->workBuffer().begin(),
970 _parallel_ray_study->workBuffer().end(),
971 /* error_suffix = */ "after tracing completed");
972
973#ifndef NDEBUG
974 // Outside of debug, _ray_bank always holds all of the Rays that have ended on this processor
975 // We can use this as a global point to check for unique IDs for every Ray that has traced
977 _ray_bank.end(),
978 /* global = */ true,
979 /* error_suffix = */ "after tracing completed");
980#endif
981 }
982
983 // Update counters from the threaded trace objects
984 for (const auto & tr : _threaded_trace_ray)
985 for (std::size_t i = 0; i < _local_trace_ray_results.size(); ++i)
986 _local_trace_ray_results[i] += tr->results()[i];
987
988 // Update local ending counters
995 // ...and communicate the global values
1002
1003 // Throw a warning with the number of failed (tolerated) traces
1005 {
1007 _communicator.sum(failures);
1008 if (failures)
1010 type(), " '", name(), "': ", failures, " ray tracing failures were tolerated.\n");
1011 }
1012
1013 // Clear the current RayKernels
1014 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1016
1017 // Move the threaded cache trace information into the full cached trace vector
1018 // Here, we only clear the cached vectors so that we might not have to
1019 // reallocate on future traces
1020 std::size_t num_entries = 0;
1021 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1022 num_entries += _threaded_cached_traces[tid].size();
1023 _cached_traces.clear();
1024 _cached_traces.reserve(num_entries);
1025 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1026 {
1027 for (const auto & entry : _threaded_cached_traces[tid])
1028 _cached_traces.emplace_back(std::move(entry));
1029 _threaded_cached_traces[tid].clear();
1030 }
1031
1032 // Add any stragglers that contribute to the Jacobian or residual
1033 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1034 if (_num_cached[tid] != 0)
1035 {
1038 "Should not have cached values without Jacobian/residual computation");
1039
1042 else
1044
1045 _num_cached[tid] = 0;
1046 }
1047
1048 // AuxRayKernels may have modified AuxVariables
1051
1052 // Clear FE
1053 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1054 {
1057 }
1058
1060 for (auto & rto : getRayTracingObjects())
1061 rto->postExecuteStudy();
1062}
1063
1064void
1065RayTracingStudy::onCompleteRay(const std::shared_ptr<Ray> & ray)
1066{
1067 mooseAssert(currentlyPropagating(), "Should only be called during Ray propagation");
1068
1069 _ending_processor_crossings += ray->processorCrossings();
1071 std::max(_ending_max_processor_crossings, ray->processorCrossings());
1072 _ending_intersections += ray->intersections();
1073 _ending_max_intersections = std::max(_ending_max_intersections, ray->intersections());
1075 std::max(_ending_max_trajectory_changes, ray->trajectoryChanges());
1076 _ending_distance += ray->distance();
1077
1078#ifdef NDEBUG
1079 // In non-opt modes, we will always bank the Rays for debugging
1081#endif
1082 _ray_bank.emplace_back(ray);
1083}
1084
1086RayTracingStudy::registerRayDataInternal(const std::string & name, const bool aux)
1087{
1088 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1089
1091 mooseError("Cannot register Ray ", (aux ? "aux " : ""), "data after initialSetup()");
1092
1093 auto & map = aux ? _ray_aux_data_map : _ray_data_map;
1094 const auto find = map.find(name);
1095 if (find != map.end())
1096 return find->second;
1097
1098 auto & other_map = aux ? _ray_data_map : _ray_aux_data_map;
1099 if (other_map.find(name) != other_map.end())
1100 mooseError("Cannot register Ray aux data with name ",
1101 name,
1102 " because Ray ",
1103 (aux ? "(non-aux)" : "aux"),
1104 " data already exists with said name.");
1105
1106 // Add into the name -> index map
1107 map.emplace(name, map.size());
1108
1109 // Add into the index -> names vector
1110 auto & vector = aux ? _ray_aux_data_names : _ray_data_names;
1111 vector.push_back(name);
1112
1113 return map.size() - 1;
1114}
1115
1116std::vector<RayDataIndex>
1117RayTracingStudy::registerRayDataInternal(const std::vector<std::string> & names, const bool aux)
1118{
1119 std::vector<RayDataIndex> indices(names.size());
1120 for (std::size_t i = 0; i < names.size(); ++i)
1121 indices[i] = registerRayDataInternal(names[i], aux);
1122 return indices;
1123}
1124
1127 const bool aux,
1128 const bool graceful) const
1129{
1130 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1131
1132 const auto & map = aux ? _ray_aux_data_map : _ray_data_map;
1133 const auto find = map.find(name);
1134 if (find != map.end())
1135 return find->second;
1136
1137 if (graceful)
1139
1140 const auto & other_map = aux ? _ray_data_map : _ray_aux_data_map;
1141 if (other_map.find(name) != other_map.end())
1142 mooseError("Ray data with name '",
1143 name,
1144 "' was not found.\n\n",
1145 "However, Ray ",
1146 (aux ? "non-aux" : "aux"),
1147 " data with said name was found.\n",
1148 "Did you mean to use ",
1149 (aux ? "getRayDataIndex()/getRayDataIndices()?"
1150 : "getRayAuxDataIndex()/getRayAuxDataIndices()"),
1151 "?");
1152
1153 mooseError("Unknown Ray ", (aux ? "aux " : ""), "data with name ", name);
1154}
1155
1156std::vector<RayDataIndex>
1157RayTracingStudy::getRayDataIndicesInternal(const std::vector<std::string> & names,
1158 const bool aux,
1159 const bool graceful) const
1160{
1161 std::vector<RayDataIndex> indices(names.size());
1162 for (std::size_t i = 0; i < names.size(); ++i)
1163 indices[i] = getRayDataIndexInternal(names[i], aux, graceful);
1164 return indices;
1165}
1166
1167const std::string &
1169{
1170 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1171
1172 if ((aux ? rayAuxDataSize() : rayDataSize()) < index)
1173 mooseError("Unknown Ray ", aux ? "aux " : "", "data with index ", index);
1174 return aux ? _ray_aux_data_names[index] : _ray_data_names[index];
1175}
1176
1179{
1180 return registerRayDataInternal(name, /* aux = */ false);
1181}
1182
1183std::vector<RayDataIndex>
1184RayTracingStudy::registerRayData(const std::vector<std::string> & names)
1185{
1186 return registerRayDataInternal(names, /* aux = */ false);
1187}
1188
1190RayTracingStudy::getRayDataIndex(const std::string & name, const bool graceful /* = false */) const
1191{
1192 return getRayDataIndexInternal(name, /* aux = */ false, graceful);
1193}
1194
1195std::vector<RayDataIndex>
1196RayTracingStudy::getRayDataIndices(const std::vector<std::string> & names,
1197 const bool graceful /* = false */) const
1198{
1199 return getRayDataIndicesInternal(names, /* aux = */ false, graceful);
1200}
1201
1202const std::string &
1204{
1205 return getRayDataNameInternal(index, /* aux = */ false);
1206}
1207
1210{
1211 return registerRayDataInternal(name, /* aux = */ true);
1212}
1213
1214std::vector<RayDataIndex>
1215RayTracingStudy::registerRayAuxData(const std::vector<std::string> & names)
1216{
1217 return registerRayDataInternal(names, /* aux = */ true);
1218}
1219
1222 const bool graceful /* = false */) const
1223{
1224 return getRayDataIndexInternal(name, /* aux = */ true, graceful);
1225}
1226
1227std::vector<RayDataIndex>
1228RayTracingStudy::getRayAuxDataIndices(const std::vector<std::string> & names,
1229 const bool graceful /* = false */) const
1230{
1231 return getRayDataIndicesInternal(names, /* aux = */ true, graceful);
1232}
1233
1234const std::string &
1236{
1237 return getRayDataNameInternal(index, /* aux = */ true);
1238}
1239
1240bool
1242{
1243 std::vector<RayKernelBase *> result;
1244 getRayKernels(result, tid);
1245 return result.size();
1246}
1247
1248void
1249RayTracingStudy::getRayKernels(std::vector<RayKernelBase *> & result, SubdomainID id, THREAD_ID tid)
1250{
1251 // If the cache doesn't have any attributes yet, it means that we haven't set
1252 // the conditions yet. We do this so that it can be generated on the fly on first use.
1253 if (!_threaded_cache_ray_kernel[tid].numAttribs())
1254 {
1256 mooseError("Should not call getRayKernels() before initialSetup()");
1257
1259 .query()
1260 .condition<AttribRayTracingStudy>(this)
1261 .condition<AttribSystem>("RayKernel")
1262 .condition<AttribThread>(tid);
1263 _threaded_cache_ray_kernel[tid] = query.clone();
1264 }
1265
1266 _threaded_cache_ray_kernel[tid].queryInto(result, id);
1267}
1268
1269void
1270RayTracingStudy::getRayKernels(std::vector<RayKernelBase *> & result,
1271 SubdomainID id,
1272 THREAD_ID tid,
1273 RayID ray_id)
1274{
1275 // No Ray registration: no need to sift through objects
1277 {
1278 getRayKernels(result, id, tid);
1279 }
1280 // Has Ray registration: only pick the objects associated with ray_id
1281 else
1282 {
1283 // Get all of the kernels on this block
1284 std::vector<RayKernelBase *> rkbs;
1285 getRayKernels(rkbs, id, tid);
1286
1287 // The RayTracingObjects associated with this ray
1288 const auto & ray_id_rtos = _threaded_ray_object_registration[tid][ray_id];
1289
1290 // The result is the union of all of the kernels and the objects associated with this Ray
1291 result.clear();
1292 for (auto rkb : rkbs)
1293 if (ray_id_rtos.count(rkb))
1294 result.push_back(rkb);
1295 }
1296}
1297
1298void
1299RayTracingStudy::getRayBCs(std::vector<RayBoundaryConditionBase *> & result,
1300 BoundaryID id,
1301 THREAD_ID tid)
1302{
1303 // If the cache doesn't have any attributes yet, it means that we haven't set
1304 // the conditions yet. We do this so that it can be generated on the fly on first use.
1305 if (!_threaded_cache_ray_bc[tid].numAttribs())
1306 {
1308 mooseError("Should not call getRayBCs() before initialSetup()");
1309
1311 .query()
1312 .condition<AttribRayTracingStudy>(this)
1313 .condition<AttribSystem>("RayBoundaryCondition")
1314 .condition<AttribThread>(tid);
1315 _threaded_cache_ray_bc[tid] = query.clone();
1316 }
1317
1318 _threaded_cache_ray_bc[tid].queryInto(result, std::make_tuple(id, false));
1319}
1320
1321void
1322RayTracingStudy::getRayBCs(std::vector<RayBoundaryConditionBase *> & result,
1323 const std::vector<TraceRayBndElement> & bnd_elems,
1324 THREAD_ID tid,
1325 RayID ray_id)
1326{
1327 // No Ray registration: no need to sift through objects
1329 {
1330 if (bnd_elems.size() == 1)
1331 getRayBCs(result, bnd_elems[0].bnd_id, tid);
1332 else
1333 {
1334 std::vector<BoundaryID> bnd_ids(bnd_elems.size());
1335 for (MooseIndex(bnd_elems.size()) i = 0; i < bnd_elems.size(); ++i)
1336 bnd_ids[i] = bnd_elems[i].bnd_id;
1337 getRayBCs(result, bnd_ids, tid);
1338 }
1339 }
1340 // Has Ray registration: only pick the objects associated with ray_id
1341 else
1342 {
1343 // Get all of the RayBCs on these boundaries
1344 std::vector<RayBoundaryConditionBase *> rbcs;
1345 if (bnd_elems.size() == 1)
1346 getRayBCs(rbcs, bnd_elems[0].bnd_id, tid);
1347 else
1348 {
1349 std::vector<BoundaryID> bnd_ids(bnd_elems.size());
1350 for (MooseIndex(bnd_elems.size()) i = 0; i < bnd_elems.size(); ++i)
1351 bnd_ids[i] = bnd_elems[i].bnd_id;
1352 getRayBCs(rbcs, bnd_ids, tid);
1353 }
1354
1355 // The RayTracingObjects associated with this ray
1356 mooseAssert(ray_id < _threaded_ray_object_registration[tid].size(), "Not in registration");
1357 const auto & ray_id_rtos = _threaded_ray_object_registration[tid][ray_id];
1358
1359 // The result is the union of all of the kernels and the objects associated with this Ray
1360 result.clear();
1361 for (auto rbc : rbcs)
1362 if (ray_id_rtos.count(rbc))
1363 result.push_back(rbc);
1364 }
1365}
1366
1367std::vector<RayTracingObject *>
1369{
1370 std::vector<RayTracingObject *> result;
1371 _fe_problem.theWarehouse().query().condition<AttribRayTracingStudy>(this).queryInto(result);
1372 return result;
1373}
1374
1375const std::vector<std::shared_ptr<Ray>> &
1377{
1379 mooseError("The Ray bank is not available because the private parameter "
1380 "'_bank_rays_on_completion' is set to false.");
1382 mooseError("Cannot get the Ray bank during generation or propagation.");
1383
1384 return _ray_bank;
1385}
1386
1387std::shared_ptr<Ray>
1389{
1390 // This is only a linear search - can be improved on with a map in the future
1391 // if this is used on a larger scale
1392 std::shared_ptr<Ray> ray;
1393 for (const std::shared_ptr<Ray> & possible_ray : rayBank())
1394 if (possible_ray->id() == ray_id)
1395 {
1396 ray = possible_ray;
1397 break;
1398 }
1399
1400 // Make sure one and only one processor has the Ray
1401 unsigned int have_ray = ray ? 1 : 0;
1402 _communicator.sum(have_ray);
1403 if (have_ray == 0)
1404 mooseError("Could not find a Ray with the ID ", ray_id, " in the Ray banks.");
1405
1406 // This should never happen... but let's make sure
1407 mooseAssert(have_ray == 1, "Multiple rays with the same ID were found in the Ray banks");
1408
1409 return ray;
1410}
1411
1412RayData
1414 const RayDataIndex index,
1415 const bool aux) const
1416{
1417 // Will be a nullptr shared_ptr if this processor doesn't own the Ray
1418 const std::shared_ptr<Ray> ray = getBankedRay(ray_id);
1419
1420 Real value = ray ? (aux ? ray->auxData(index) : ray->data(index)) : 0;
1421 _communicator.sum(value);
1422 return value;
1423}
1424
1425RayData
1427{
1428 return getBankedRayDataInternal(ray_id, index, /* aux = */ false);
1429}
1430
1431RayData
1433{
1434 return getBankedRayDataInternal(ray_id, index, /* aux = */ true);
1435}
1436
1437RayID
1439{
1440 libmesh_parallel_only(comm());
1441
1442 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1443
1445 mooseError("Cannot use registerRay() with Ray registration disabled");
1446
1447 // This is parallel only for now. We could likely stagger the ID building like we do with
1448 // the unique IDs, but it would require a sync point which isn't there right now
1449 libmesh_parallel_only(comm());
1450
1451 const auto & it = _registered_ray_map.find(name);
1452 if (it != _registered_ray_map.end())
1453 return it->second;
1454
1455 const auto id = _reverse_registered_ray_map.size();
1456 _registered_ray_map.emplace(name, id);
1458 return id;
1459}
1460
1461RayID
1462RayTracingStudy::registeredRayID(const std::string & name, const bool graceful /* = false */) const
1463{
1464 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1465
1467 mooseError("Should not use registeredRayID() with Ray registration disabled");
1468
1469 const auto search = _registered_ray_map.find(name);
1470 if (search != _registered_ray_map.end())
1471 return search->second;
1472
1473 if (graceful)
1474 return Ray::INVALID_RAY_ID;
1475
1476 mooseError("Attempted to obtain ID of registered Ray ",
1477 name,
1478 ", but a Ray with said name is not registered.");
1479}
1480
1481const std::string &
1483{
1484 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1485
1487 mooseError("Should not use registeredRayName() with Ray registration disabled");
1488
1489 if (_reverse_registered_ray_map.size() > ray_id)
1490 return _reverse_registered_ray_map[ray_id];
1491
1492 mooseError("Attempted to obtain name of registered Ray with ID ",
1493 ray_id,
1494 ", but a Ray with said ID is not registered.");
1495}
1496
1497Real
1499{
1500 Real volume = 0;
1501 for (const auto & elem : *_mesh.getActiveLocalElementRange())
1502 volume += elem->volume();
1503 _communicator.sum(volume);
1504 return volume;
1505}
1506
1507const std::vector<std::vector<BoundaryID>> &
1509{
1510 mooseAssert(_use_internal_sidesets, "Not using internal sidesets");
1511 mooseAssert(hasInternalSidesets(), "Processor does not have internal sidesets");
1512 mooseAssert(_internal_sidesets_map.size() > _elem_index_helper.getIndex(elem),
1513 "Internal sideset map not initialized");
1514
1515 const auto index = _elem_index_helper.getIndex(elem);
1516 return _internal_sidesets_map[index];
1517}
1518
1519TraceData &
1520RayTracingStudy::initThreadedCachedTrace(const std::shared_ptr<Ray> & ray, THREAD_ID tid)
1521{
1522 mooseAssert(shouldCacheTrace(ray), "Not caching trace");
1523 mooseAssert(currentlyPropagating(), "Should only use while tracing");
1524
1525 _threaded_cached_traces[tid].emplace_back(ray);
1526 return _threaded_cached_traces[tid].back();
1527}
1528
1529void
1530RayTracingStudy::verifyUniqueRayIDs(const std::vector<std::shared_ptr<Ray>>::const_iterator begin,
1531 const std::vector<std::shared_ptr<Ray>>::const_iterator end,
1532 const bool global,
1533 const std::string & error_suffix) const
1534{
1535 // Determine the unique set of Ray IDs on this processor,
1536 // and if not locally unique throw an error. Once we build this set,
1537 // we will send it to rank 0 to verify globally
1538 std::set<RayID> local_rays;
1539 for (const std::shared_ptr<Ray> & ray : as_range(begin, end))
1540 {
1541 mooseAssert(ray, "Null ray");
1542
1543 // Try to insert into the set; the second entry in the pair
1544 // will be false if it was not inserted
1545 if (!local_rays.insert(ray->id()).second)
1546 {
1547 for (const std::shared_ptr<Ray> & other_ray : as_range(begin, end))
1548 if (ray.get() != other_ray.get() && ray->id() == other_ray->id())
1549 mooseError("Multiple Rays exist with ID ",
1550 ray->id(),
1551 " on processor ",
1552 _pid,
1553 " ",
1554 error_suffix,
1555 "\n\nOffending Ray information:\n\n",
1556 ray->getInfo(),
1557 "\n",
1558 other_ray->getInfo());
1559 }
1560 }
1561
1562 // Send IDs from all procs to rank 0 and verify on rank 0
1563 if (global)
1564 {
1565 // Package our local IDs and send to rank 0
1566 std::map<processor_id_type, std::vector<RayID>> send_ids;
1567 if (local_rays.size())
1568 send_ids.emplace(std::piecewise_construct,
1569 std::forward_as_tuple(0),
1570 std::forward_as_tuple(local_rays.begin(), local_rays.end()));
1571 local_rays.clear();
1572
1573 // Mapping on rank 0 from ID -> processor ID
1574 std::map<RayID, processor_id_type> global_map;
1575
1576 // Verify another processor's IDs against the global map on rank 0
1577 const auto check_ids =
1578 [this, &global_map, &error_suffix](processor_id_type pid, const std::vector<RayID> & ids)
1579 {
1580 for (const RayID id : ids)
1581 {
1582 const auto emplace_pair = global_map.emplace(id, pid);
1583
1584 // Means that this ID already exists in the map
1585 if (!emplace_pair.second)
1586 mooseError("Ray with ID ",
1587 id,
1588 " exists on ranks ",
1589 emplace_pair.first->second,
1590 " and ",
1591 pid,
1592 "\n",
1593 error_suffix);
1594 }
1595 };
1596
1597 Parallel::push_parallel_vector_data(_communicator, send_ids, check_ids);
1598 }
1599}
1600
1601void
1602RayTracingStudy::verifyUniqueRays(const std::vector<std::shared_ptr<Ray>>::const_iterator begin,
1603 const std::vector<std::shared_ptr<Ray>>::const_iterator end,
1604 const std::string & error_suffix)
1605{
1606 std::set<const Ray *> rays;
1607 for (const std::shared_ptr<Ray> & ray : as_range(begin, end))
1608 if (!rays.insert(ray.get()).second) // false if not inserted into rays
1609 mooseError("Multiple shared_ptrs were found that point to the same Ray ",
1610 error_suffix,
1611 "\n\nOffending Ray:\n",
1612 ray->getInfo());
1613}
1614
1615void
1616RayTracingStudy::moveRayToBuffer(std::shared_ptr<Ray> & ray)
1617{
1618 mooseAssert(currentlyGenerating(), "Can only use while generating");
1619 mooseAssert(ray, "Null ray");
1620 mooseAssert(ray->shouldContinue(), "Ray is not continuing");
1621
1622 _parallel_ray_study->moveWorkToBuffer(ray, /* tid = */ 0);
1623}
1624
1625void
1626RayTracingStudy::moveRaysToBuffer(std::vector<std::shared_ptr<Ray>> & rays)
1627{
1628 mooseAssert(currentlyGenerating(), "Can only use while generating");
1629#ifndef NDEBUG
1630 for (const std::shared_ptr<Ray> & ray : rays)
1631 {
1632 mooseAssert(ray, "Null ray");
1633 mooseAssert(ray->shouldContinue(), "Ray is not continuing");
1634 }
1635#endif
1636
1637 _parallel_ray_study->moveWorkToBuffer(rays, /* tid = */ 0);
1638}
1639
1640void
1642 const THREAD_ID tid,
1644{
1645 mooseAssert(ray, "Null ray");
1646 mooseAssert(currentlyPropagating(), "Can only use while tracing");
1647
1648 _parallel_ray_study->moveWorkToBuffer(ray, tid);
1649}
1650
1651void
1653{
1654 if (!currentlyGenerating())
1655 mooseError("Can only reserve in Ray buffer during generateRays()");
1656
1657 _parallel_ray_study->reserveBuffer(size);
1658}
1659
1660const Point &
1661RayTracingStudy::getSideNormal(const Elem * elem, unsigned short side, const THREAD_ID tid)
1662{
1663 std::unordered_map<std::pair<const Elem *, unsigned short>, Point> & cache =
1665
1666 // See if we've already cached this side normal
1667 const auto elem_side_pair = std::make_pair(elem, side);
1668 const auto search = cache.find(elem_side_pair);
1669
1670 // Haven't cached this side normal: compute it and then cache it
1671 if (search == cache.end())
1672 {
1673 _threaded_fe_face[tid]->reinit(elem, side);
1674 const auto & normal = _threaded_fe_face[tid]->get_normals()[0];
1675 cache.emplace(elem_side_pair, normal);
1676 return normal;
1677 }
1678
1679 // Have cached this side normal: simply return it
1680 return search->second;
1681}
1682
1683bool
1685{
1686 unsigned int min_level = std::numeric_limits<unsigned int>::max();
1687 unsigned int max_level = std::numeric_limits<unsigned int>::min();
1688
1689 for (const auto & elem : *_mesh.getActiveLocalElementRange())
1690 {
1691 const auto level = elem->level();
1692 min_level = std::min(level, min_level);
1693 max_level = std::max(level, max_level);
1694 }
1695
1696 _communicator.min(min_level);
1697 _communicator.max(max_level);
1698
1699 return min_level == max_level;
1700}
1701
1702Real
1704{
1705 const auto find = _subdomain_hmax.find(subdomain_id);
1706 if (find == _subdomain_hmax.end())
1707 mooseError("Subdomain ", subdomain_id, " not found in subdomain hmax map");
1708 return find->second;
1709}
1710
1711bool
1713{
1714 Real bbox_volume = 1;
1715 for (unsigned int d = 0; d < _mesh.dimension(); ++d)
1716 bbox_volume *= std::abs(_b_box.max()(d) - _b_box.min()(d));
1717
1718 return MooseUtils::absoluteFuzzyEqual(bbox_volume, totalVolume(), TOLERANCE);
1719}
1720
1721void
1723{
1724 libmesh_parallel_only(comm());
1725
1726 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1727
1728 mooseAssert(!currentlyGenerating() && !currentlyPropagating(),
1729 "Cannot be reset during generation or propagation");
1730
1731 for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
1733}
1734
1735RayID
1737{
1738 // Get the current ID to return
1739 const auto id = _threaded_next_ray_id[tid];
1740
1741 // Advance so that the next call has the correct ID
1743
1744 return id;
1745}
1746
1747void
1749{
1750 libmesh_parallel_only(comm());
1751
1752 Threads::spin_mutex::scoped_lock lock(_spin_mutex);
1753
1754 mooseAssert(!currentlyGenerating() && !currentlyPropagating(),
1755 "Cannot be reset during generation or propagation");
1756
1758}
1759
1760RayID
1765
1766bool
1767RayTracingStudy::sideIsIncoming(const Elem * const elem,
1768 const unsigned short side,
1769 const Point & direction,
1770 const THREAD_ID tid)
1771{
1772 const auto & normal = getSideNormal(elem, side, tid);
1773 const auto dot = normal * direction;
1774 return dot < TraceRayTools::TRACE_TOLERANCE;
1775}
1776
1777std::shared_ptr<Ray>
1779{
1780 mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1781
1782 return _parallel_ray_study->acquireParallelData(
1783 /* tid = */ 0,
1784 this,
1785 generateUniqueRayID(/* tid = */ 0),
1786 rayDataSize(),
1788 /* reset = */ true,
1790}
1791
1792std::shared_ptr<Ray>
1794{
1795 mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1796
1797 return _parallel_ray_study->acquireParallelData(/* tid = */ 0,
1798 this,
1799 generateUniqueRayID(/* tid = */ 0),
1800 /* data_size = */ 0,
1801 /* aux_data_size = */ 0,
1802 /* reset = */ true,
1804}
1805
1806std::shared_ptr<Ray>
1808{
1809 mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1810 libmesh_parallel_only(comm());
1811
1812 return _parallel_ray_study->acquireParallelData(
1813 /* tid = */ 0,
1814 this,
1816 rayDataSize(),
1818 /* reset = */ true,
1820}
1821
1822std::shared_ptr<Ray>
1824{
1825 mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1826
1827 // Either register a Ray or get an already registered Ray id
1828 const RayID id = registerRay(name);
1829
1830 // Acquire a Ray with the properly sized data initialized to zero
1831 return _parallel_ray_study->acquireParallelData(
1832 /* tid = */ 0,
1833 this,
1834 id,
1835 rayDataSize(),
1837 /* reset = */ true,
1839}
1840
1841std::shared_ptr<Ray>
1843{
1844 mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
1845 return _parallel_ray_study->acquireParallelData(
1846 /* tid = */ 0, &ray, Ray::ConstructRayKey());
1847}
1848
1849std::shared_ptr<Ray>
1851{
1852 mooseAssert(currentlyPropagating(), "Can only use during propagation");
1853 return _parallel_ray_study->acquireParallelData(tid,
1854 this,
1856 rayDataSize(),
1858 /* reset = */ true,
1860}
boundary_id_type BoundaryID
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
THREAD_ID numThreads() const
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