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