LCOV - code coverage report
Current view: top level - src/userobjects - RayTracingStudy.C (source / functions) Hit Total Coverage
Test: idaholab/moose ray_tracing: #31405 (292dce) with base fef103 Lines: 727 737 98.6 %
Date: 2025-09-04 07:56:07 Functions: 79 79 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : // Local Includes
      13             : #include "AuxRayKernel.h"
      14             : #include "RayBoundaryConditionBase.h"
      15             : #include "RayKernel.h"
      16             : #include "TraceRay.h"
      17             : #include "TraceRayTools.h"
      18             : 
      19             : // MOOSE Includes
      20             : #include "AuxiliarySystem.h"
      21             : #include "Assembly.h"
      22             : #include "NonlinearSystemBase.h"
      23             : 
      24             : // libMesh Includes
      25             : #include "libmesh/enum_to_string.h"
      26             : #include "libmesh/mesh_tools.h"
      27             : #include "libmesh/parallel_sync.h"
      28             : #include "libmesh/remote_elem.h"
      29             : 
      30             : using namespace libMesh;
      31             : 
      32             : InputParameters
      33        7576 : RayTracingStudy::validParams()
      34             : {
      35        7576 :   auto params = GeneralUserObject::validParams();
      36             : 
      37             :   // Parameters for the execution of the rays
      38        7576 :   params += ParallelRayStudy::validParams();
      39             : 
      40       22728 :   params.addRangeCheckedParam<Real>("ray_distance",
      41       15152 :                                     std::numeric_limits<Real>::max(),
      42             :                                     "ray_distance > 0",
      43             :                                     "The maximum distance all Rays can travel");
      44             : 
      45       15152 :   params.addParam<bool>(
      46       15152 :       "tolerate_failure", false, "Whether or not to tolerate a ray tracing failure");
      47             : 
      48       15152 :   MooseEnum work_buffers("lifo circular", "circular");
      49       15152 :   params.addParam<MooseEnum>("work_buffer_type", work_buffers, "The work buffer type to use");
      50             : 
      51       15152 :   params.addParam<bool>(
      52       15152 :       "ray_kernel_coverage_check", true, "Whether or not to perform coverage checks on RayKernels");
      53       15152 :   params.addParam<bool>("warn_non_planar",
      54       15152 :                         true,
      55             :                         "Whether or not to produce a warning if any element faces are non-planar.");
      56             : 
      57       15152 :   params.addParam<bool>(
      58             :       "always_cache_traces",
      59       15152 :       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       15152 :   params.addParam<bool>("data_on_cache_traces",
      63       15152 :                         false,
      64             :                         "Whether or not to also cache the Ray's data when caching its traces");
      65       15152 :   params.addParam<bool>("aux_data_on_cache_traces",
      66       15152 :                         false,
      67             :                         "Whether or not to also cache the Ray's aux data when caching its traces");
      68       15152 :   params.addParam<bool>(
      69             :       "segments_on_cache_traces",
      70       15152 :       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       15152 :   params.addParam<bool>("use_internal_sidesets",
      77       15152 :                         false,
      78             :                         "Whether or not to use internal sidesets for RayBCs in ray tracing");
      79             : 
      80       15152 :   params.addParam<bool>("warn_subdomain_hmax",
      81       15152 :                         true,
      82             :                         "Whether or not to warn if the approximated hmax (constant on subdomain) "
      83             :                         "varies significantly for an element");
      84             : 
      85       15152 :   params.addParam<bool>(
      86             :       "verify_rays",
      87       15152 :       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       15152 :   params.addParam<bool>("verify_trace_intersections",
      92       15152 :                         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       15152 :   params.addParam<bool>("allow_other_flags_with_prekernels",
      98       15152 :                         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        7576 :   ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
     104        7576 :   exec_enum.addAvailableFlags(EXEC_PRE_KERNELS);
     105             : 
     106       15152 :   params.addParamNamesToGroup(
     107             :       "always_cache_traces data_on_cache_traces aux_data_on_cache_traces segments_on_cache_traces",
     108             :       "Trace cache");
     109       15152 :   params.addParamNamesToGroup("warn_non_planar warn_subdomain_hmax", "Tracing Warnings");
     110       15152 :   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        7576 :   params.addPrivateParam<bool>("_use_ray_registration", true);
     115             :   // Whether or not to bank Rays on completion
     116        7576 :   params.addPrivateParam<bool>("_bank_rays_on_completion", true);
     117             :   /// Whether or not subdomain setup is dependent on the Ray
     118       15152 :   params.addPrivateParam<bool>("_ray_dependent_subdomain_setup", true);
     119             : 
     120             :   // Add a point neighbor relationship manager
     121       15152 :   params.addRelationshipManager("ElementPointNeighborLayers",
     122             :                                 Moose::RelationshipManagerType::GEOMETRIC |
     123             :                                     Moose::RelationshipManagerType::ALGEBRAIC,
     124       11285 :                                 [](const InputParameters &, InputParameters & rm_params)
     125       11285 :                                 { rm_params.set<unsigned short>("layers") = 1; });
     126             : 
     127        7576 :   return params;
     128        7576 : }
     129             : 
     130        3827 : RayTracingStudy::RayTracingStudy(const InputParameters & parameters)
     131             :   : GeneralUserObject(parameters),
     132        3827 :     _mesh(_fe_problem.mesh()),
     133        3827 :     _comm(_mesh.comm()),
     134        3827 :     _pid(_comm.rank()),
     135             : 
     136        7654 :     _ray_kernel_coverage_check(getParam<bool>("ray_kernel_coverage_check")),
     137        7654 :     _warn_non_planar(getParam<bool>("warn_non_planar")),
     138        7654 :     _use_ray_registration(getParam<bool>("_use_ray_registration")),
     139        7654 :     _use_internal_sidesets(getParam<bool>("use_internal_sidesets")),
     140        7654 :     _tolerate_failure(getParam<bool>("tolerate_failure")),
     141        7654 :     _bank_rays_on_completion(getParam<bool>("_bank_rays_on_completion")),
     142        7654 :     _ray_dependent_subdomain_setup(getParam<bool>("_ray_dependent_subdomain_setup")),
     143             : 
     144        7654 :     _always_cache_traces(getParam<bool>("always_cache_traces")),
     145        7654 :     _data_on_cache_traces(getParam<bool>("data_on_cache_traces")),
     146        7654 :     _aux_data_on_cache_traces(getParam<bool>("aux_data_on_cache_traces")),
     147        7654 :     _segments_on_cache_traces(getParam<bool>("segments_on_cache_traces")),
     148        7654 :     _ray_max_distance(getParam<Real>("ray_distance")),
     149        7654 :     _verify_rays(getParam<bool>("verify_rays")),
     150             : #ifndef NDEBUG
     151             :     _verify_trace_intersections(getParam<bool>("verify_trace_intersections")),
     152             : #endif
     153             : 
     154        3827 :     _threaded_elem_side_builders(libMesh::n_threads()),
     155             : 
     156        3827 :     _registered_ray_map(
     157        3827 :         declareRestartableData<std::unordered_map<std::string, RayID>>("registered_ray_map")),
     158        3827 :     _reverse_registered_ray_map(
     159        7654 :         declareRestartableData<std::vector<std::string>>("reverse_registered_ray_map")),
     160             : 
     161        3827 :     _threaded_cached_traces(libMesh::n_threads()),
     162             : 
     163        3827 :     _num_cached(libMesh::n_threads(), 0),
     164             : 
     165        3827 :     _has_non_planar_sides(true),
     166        3827 :     _has_same_level_active_elems(sameLevelActiveElems()),
     167             : 
     168        3827 :     _b_box(MeshTools::create_nodal_bounding_box(_mesh.getMesh())),
     169        3827 :     _domain_max_length(1.01 * (_b_box.max() - _b_box.min()).norm()),
     170        3827 :     _total_volume(computeTotalVolume()),
     171             : 
     172        3827 :     _threaded_cache_ray_kernel(libMesh::n_threads()),
     173        3827 :     _threaded_cache_ray_bc(libMesh::n_threads()),
     174        3827 :     _threaded_ray_object_registration(libMesh::n_threads()),
     175        3827 :     _threaded_current_ray_kernels(libMesh::n_threads()),
     176        3827 :     _threaded_trace_ray(libMesh::n_threads()),
     177        3827 :     _threaded_fe_face(libMesh::n_threads()),
     178        3827 :     _threaded_q_face(libMesh::n_threads()),
     179        3827 :     _threaded_cached_normals(libMesh::n_threads()),
     180        3827 :     _threaded_next_ray_id(libMesh::n_threads()),
     181             : 
     182        3827 :     _parallel_ray_study(std::make_unique<ParallelRayStudy>(*this, _threaded_trace_ray)),
     183             : 
     184        3827 :     _local_trace_ray_results(TraceRay::FAILED_TRACES + 1, 0),
     185             : 
     186        3827 :     _called_initial_setup(false),
     187             : 
     188       15308 :     _elem_index_helper(_mesh.getMesh(), name() + "_elem_index")
     189             : {
     190             :   // Initialize a tracing object for each thread
     191        8378 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     192             :   {
     193             :     // Initialize a tracing object for each thread
     194        9102 :     _threaded_trace_ray[tid] = std::make_shared<TraceRay>(*this, tid);
     195             : 
     196             :     // Setup the face FEs for normal computation on the fly
     197        9102 :     _threaded_fe_face[tid] = FEBase::build(_mesh.dimension(), FEType(CONSTANT, MONOMIAL));
     198        9102 :     _threaded_q_face[tid] = QBase::build(QGAUSS, _mesh.dimension() - 1, CONSTANT);
     199             :     _threaded_fe_face[tid]->attach_quadrature_rule(_threaded_q_face[tid].get());
     200             :     _threaded_fe_face[tid]->get_normals();
     201             :   }
     202             : 
     203             :   // Evaluating on residual and Jacobian evaluation
     204        3827 :   if (_execute_enum.isValueSet(EXEC_PRE_KERNELS))
     205             :   {
     206         534 :     if (!getParam<bool>("allow_other_flags_with_prekernels") && _execute_enum.size() > 1)
     207           2 :       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         176 :     if (_app.useEigenvalue())
     213           2 :       mooseError("Execution on residual and Jacobian evaluation (execute_on = PRE_KERNELS)\n",
     214             :                  "is not supported for an eigenvalue solve.");
     215             :   }
     216             : 
     217        3823 :   resetUniqueRayIDs();
     218        3823 :   resetReplicatedRayIDs();
     219             : 
     220             :   // Scale the bounding box for loose checking
     221             :   _loose_b_box = _b_box;
     222        3823 :   _loose_b_box.scale(TOLERANCE * TOLERANCE);
     223        3823 : }
     224             : 
     225             : void
     226        3709 : RayTracingStudy::initialSetup()
     227             : {
     228             :   // Keep track of initialSetup call to avoid registration of various things
     229        3709 :   _called_initial_setup = true;
     230             : 
     231             :   // Sets up a local index for each elem this proc knows about
     232        3709 :   localElemIndexSetup();
     233             : 
     234             :   // Check for RayKernel coverage
     235        3709 :   coverageChecks();
     236             : 
     237             :   // Make sure the dependencies exist, if any
     238        3707 :   dependencyChecks();
     239             : 
     240             :   // Check for traceable element types
     241        3703 :   traceableMeshChecks();
     242             : 
     243             :   // Setup for internal sidesets
     244        3701 :   internalSidesetSetup();
     245             : 
     246        3697 :   nonPlanarSideSetup();
     247             : 
     248             :   // Setup approximate hmax for each subdomain
     249        3695 :   subdomainHMaxSetup();
     250             : 
     251             :   // Call initial setup on all of the objects
     252       12082 :   for (auto & rto : getRayTracingObjects())
     253       12082 :     rto->initialSetup();
     254             : 
     255             :   // Check for proper exec flags with RayKernels
     256             :   std::vector<RayKernelBase *> ray_kernels;
     257        3693 :   getRayKernels(ray_kernels, 0);
     258        6949 :   for (const auto & rkb : ray_kernels)
     259        3258 :     if (dynamic_cast<RayKernel *>(rkb) && !_execute_enum.isValueSet(EXEC_PRE_KERNELS))
     260           2 :       mooseError("This study has RayKernel objects that contribute to residuals and Jacobians.",
     261             :                  "\nIn this case, the study must use the execute_on = PRE_KERNELS");
     262             : 
     263             :   // Build 1D quadrature rule for along a segment
     264             :   _segment_qrule =
     265        7382 :       QBase::build(QGAUSS, 1, _fe_problem.getSystemBase(_sys.number()).getMinQuadratureOrder());
     266        3691 : }
     267             : 
     268             : void
     269        4708 : RayTracingStudy::residualSetup()
     270             : {
     271             :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     272             :     mooseAssert(_num_cached[tid] == 0, "Cached residuals/Jacobians not empty");
     273             : 
     274       13743 :   for (auto & rto : getRayTracingObjects())
     275       13743 :     rto->residualSetup();
     276        4708 : }
     277             : 
     278             : void
     279         300 : RayTracingStudy::jacobianSetup()
     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        1148 :   for (auto & rto : getRayTracingObjects())
     285        1148 :     rto->jacobianSetup();
     286         300 : }
     287             : 
     288             : void
     289        3246 : RayTracingStudy::timestepSetup()
     290             : {
     291       10427 :   for (auto & rto : getRayTracingObjects())
     292       10427 :     rto->timestepSetup();
     293        3246 : }
     294             : 
     295             : void
     296         228 : RayTracingStudy::meshChanged()
     297             : {
     298         228 :   localElemIndexSetup();
     299         228 :   internalSidesetSetup();
     300         228 :   nonPlanarSideSetup();
     301         228 :   subdomainHMaxSetup();
     302             : 
     303         228 :   _has_same_level_active_elems = sameLevelActiveElems();
     304             : 
     305         506 :   for (const auto & trace_ray : _threaded_trace_ray)
     306         278 :     trace_ray->meshChanged();
     307         228 : }
     308             : 
     309             : void
     310        8023 : RayTracingStudy::execute()
     311             : {
     312        8023 :   executeStudy();
     313        7923 : }
     314             : 
     315             : void
     316        3709 : RayTracingStudy::coverageChecks()
     317             : {
     318             :   // Check for coverage of RayKernels on domain
     319        3709 :   if (_ray_kernel_coverage_check)
     320             :   {
     321             :     std::vector<RayKernelBase *> ray_kernels;
     322        2900 :     getRayKernels(ray_kernels, 0);
     323             : 
     324             :     std::set<SubdomainID> ray_kernel_blocks;
     325        6146 :     for (const auto & rk : ray_kernels)
     326        6492 :       ray_kernel_blocks.insert(rk->blockIDs().begin(), rk->blockIDs().end());
     327             : 
     328             :     std::set<SubdomainID> missing;
     329        5800 :     std::set_difference(_mesh.meshSubdomains().begin(),
     330        2900 :                         _mesh.meshSubdomains().end(),
     331             :                         ray_kernel_blocks.begin(),
     332             :                         ray_kernel_blocks.end(),
     333             :                         std::inserter(missing, missing.begin()));
     334             : 
     335        2900 :     if (!missing.empty() && !ray_kernel_blocks.count(Moose::ANY_BLOCK_ID))
     336             :     {
     337           2 :       std::ostringstream error;
     338           2 :       error << "Subdomains { ";
     339           2 :       std::copy(missing.begin(), missing.end(), std::ostream_iterator<SubdomainID>(error, " "));
     340           2 :       error << "} do not have RayKernels defined!";
     341             : 
     342           2 :       mooseError(error.str());
     343           0 :     }
     344        2898 :   }
     345        3707 : }
     346             : 
     347             : void
     348        3707 : RayTracingStudy::dependencyChecks()
     349             : {
     350             :   std::vector<RayTracingObject *> ray_tracing_objects;
     351             : 
     352        3707 :   getRayKernels(ray_tracing_objects, 0);
     353        3707 :   verifyDependenciesExist(ray_tracing_objects);
     354             : 
     355        3705 :   getRayBCs(ray_tracing_objects, 0);
     356        3705 :   verifyDependenciesExist(ray_tracing_objects);
     357        3703 : }
     358             : 
     359             : void
     360        7412 : RayTracingStudy::verifyDependenciesExist(const std::vector<RayTracingObject *> & rtos)
     361             : {
     362       14576 :   for (const auto & rto : rtos)
     363        7288 :     for (const auto & dep_name : rto->getRequestedItems())
     364             :     {
     365             :       bool found = false;
     366         256 :       for (const auto & rto_search : rtos)
     367         252 :         if (rto_search->name() == dep_name)
     368             :         {
     369             :           found = true;
     370             :           break;
     371             :         }
     372             : 
     373         124 :       if (!found)
     374           4 :         rto->paramError("depends_on", "The ", rto->getBase(), " '", dep_name, "' does not exist");
     375             :     }
     376        7408 : }
     377             : 
     378             : void
     379        3703 : RayTracingStudy::traceableMeshChecks()
     380             : {
     381             : 
     382      466239 :   for (const auto & elem : *_mesh.getActiveLocalElementRange())
     383             :   {
     384      462538 :     if (_fe_problem.adaptivity().isOn())
     385             :     {
     386        5670 :       if (!TraceRayTools::isAdaptivityTraceableElem(elem))
     387           2 :         mooseError("Element type ",
     388           2 :                    Utility::enum_to_string(elem->type()),
     389             :                    " is not supported in ray tracing with adaptivity");
     390             :     }
     391      456868 :     else if (!TraceRayTools::isTraceableElem(elem))
     392           0 :       mooseError("Element type ",
     393           0 :                  Utility::enum_to_string(elem->type()),
     394             :                  " is not supported in ray tracing");
     395             :   }
     396        3701 : }
     397             : 
     398             : void
     399        3937 : RayTracingStudy::localElemIndexSetup()
     400             : {
     401             :   // TODO: We could probably minimize this to local active elements followed by
     402             :   // boundary point neighbors, but if using distibuted mesh it really shouldn't matter
     403        3937 :   _elem_index_helper.initialize(_mesh.getMesh().active_element_ptr_range());
     404        3937 : }
     405             : 
     406             : void
     407        3929 : RayTracingStudy::internalSidesetSetup()
     408             : {
     409             :   // Even if we have _use_internal_sidesets == false, we will make sure the user didn't add RayBCs
     410             :   // on internal boundaries
     411             : 
     412             :   // Clear the data structures and size the map based on the elements that we know about
     413             :   _internal_sidesets.clear();
     414        3929 :   _internal_sidesets_map.clear();
     415        3929 :   _internal_sidesets_map.resize(_elem_index_helper.maxIndex() + 1);
     416             : 
     417             :   // First, we are going to store all elements with internal sidesets (if any) that have active
     418             :   // RayBCs on them as elem -> vector of (side, vector of boundary ids)
     419      287808 :   for (const auto & bnd_elem : *_mesh.getBoundaryElementRange())
     420             :   {
     421      283881 :     Elem * elem = bnd_elem->_elem;
     422      283881 :     const unsigned int side = bnd_elem->_side;
     423      283881 :     const auto bnd_id = bnd_elem->_bnd_id;
     424             : 
     425             :     // Not internal
     426             :     const Elem * const neighbor = elem->neighbor_ptr(side);
     427      283881 :     if (!neighbor || neighbor == remote_elem)
     428      276718 :       continue;
     429             : 
     430             :     // No RayBCs on this sideset
     431             :     std::vector<RayBoundaryConditionBase *> result;
     432        7163 :     getRayBCs(result, bnd_id, 0);
     433        7163 :     if (result.empty())
     434             :       continue;
     435             : 
     436        7163 :     if (neighbor->subdomain_id() == elem->subdomain_id())
     437           2 :       mooseError("RayBCs exist on internal sidesets that are not bounded by a different",
     438             :                  "\nsubdomain on each side.",
     439             :                  "\n\nIn order to use RayBCs on internal sidesets, said sidesets must have",
     440             :                  "\na different subdomain on each side.");
     441             : 
     442             :     // Mark that this boundary is an internal sideset with RayBC(s)
     443        7161 :     _internal_sidesets.insert(bnd_id);
     444             : 
     445             :     // Get elem's entry in the internal sidset data structure
     446             :     const auto index = _elem_index_helper.getIndex(elem);
     447             :     auto & entry = _internal_sidesets_map[index];
     448             : 
     449             :     // Initialize this elem's sides if they have not been already
     450        7161 :     if (entry.empty())
     451        5864 :       entry.resize(elem->n_sides(), std::vector<BoundaryID>());
     452             : 
     453             :     // Add the internal boundary to the side entry
     454        7161 :     entry[side].push_back(bnd_id);
     455        7161 :   }
     456             : 
     457        3927 :   if (!_use_internal_sidesets && _internal_sidesets.size())
     458           2 :     mooseError("RayBCs are defined on internal sidesets, but the study is not set to use ",
     459             :                "internal sidesets during tracing.",
     460             :                "\n\nSet the parameter use_internal_sidesets = true to enable this capability.");
     461        3925 : }
     462             : 
     463             : void
     464        3925 : RayTracingStudy::nonPlanarSideSetup()
     465             : {
     466        3925 :   _has_non_planar_sides = false;
     467        3925 :   bool warned = !_warn_non_planar;
     468             : 
     469             :   // Nothing to do here for 2D or 1D
     470        3925 :   if (_mesh.dimension() != 3)
     471             :     return;
     472             : 
     473             :   // Clear the data structure and size it based on the elements that we know about
     474        1590 :   _non_planar_sides.clear();
     475        1590 :   _non_planar_sides.resize(_elem_index_helper.maxIndex() + 1);
     476             : 
     477     1840841 :   for (const Elem * elem : _mesh.getMesh().active_element_ptr_range())
     478             :   {
     479             :     const auto index = _elem_index_helper.getIndex(elem);
     480             :     auto & entry = _non_planar_sides[index];
     481      612555 :     entry.resize(elem->n_sides(), 0);
     482             : 
     483     3467221 :     for (const auto s : elem->side_index_range())
     484             :     {
     485     2854668 :       const auto & side = elemSide(*elem, s);
     486     2854668 :       if (side.n_vertices() < 4)
     487     2076172 :         continue;
     488             : 
     489      778496 :       if (!side.has_affine_map())
     490             :       {
     491       11466 :         entry[s] = 1;
     492       11466 :         _has_non_planar_sides = true;
     493             : 
     494       11466 :         if (!warned)
     495             :         {
     496           2 :           mooseWarning("The mesh contains non-planar faces.\n\n",
     497             :                        "Ray tracing on non-planar faces is an approximation and may fail.\n\n",
     498             :                        "Use at your own risk! You can disable this warning by setting the\n",
     499             :                        "parameter 'warn_non_planar' to false.");
     500             :           warned = true;
     501             :         }
     502             :       }
     503             :     }
     504        1588 :   }
     505             : }
     506             : 
     507             : void
     508        3923 : RayTracingStudy::subdomainHMaxSetup()
     509             : {
     510             :   // Setup map with subdomain keys
     511             :   _subdomain_hmax.clear();
     512        8568 :   for (const auto subdomain_id : _mesh.meshSubdomains())
     513        4645 :     _subdomain_hmax[subdomain_id] = std::numeric_limits<Real>::min();
     514             : 
     515             :   // Set local max for each subdomain
     516      474177 :   for (const auto & elem : *_mesh.getActiveLocalElementRange())
     517             :   {
     518      470254 :     auto & entry = _subdomain_hmax.at(elem->subdomain_id());
     519      475378 :     entry = std::max(entry, elem->hmax());
     520             :   }
     521             : 
     522             :   // Accumulate global max for each subdomain
     523        3923 :   _communicator.max(_subdomain_hmax);
     524             : 
     525        7846 :   if (getParam<bool>("warn_subdomain_hmax"))
     526             :   {
     527        3923 :     const auto warn_prefix = type() + " '" + name() + "': ";
     528        3923 :     const auto warn_suffix =
     529             :         "\n\nRay tracing uses an approximate element size for each subdomain to scale the\n"
     530             :         "tolerances used in computing ray intersections. This warning suggests that the\n"
     531             :         "approximate element size is not a good approximation. This is likely due to poor\n"
     532             :         "element aspect ratios.\n\n"
     533             :         "This warning is only output for the first element affected.\n"
     534             :         "To disable this warning, set warn_subdomain_hmax = false.\n";
     535             : 
     536      474175 :     for (const auto & elem : *_mesh.getActiveLocalElementRange())
     537             :     {
     538      470254 :       const auto hmin = elem->hmin();
     539      470254 :       const auto hmax = elem->hmax();
     540      470254 :       const auto max_hmax = subdomainHmax(elem->subdomain_id());
     541             : 
     542      470254 :       const auto hmax_rel = hmax / max_hmax;
     543      470254 :       if (hmax_rel < 1.e-2 || hmax_rel > 1.e2)
     544           0 :         mooseDoOnce(mooseWarning(warn_prefix,
     545             :                                  "Element hmax varies significantly from subdomain hmax.\n",
     546             :                                  warn_suffix,
     547             :                                  "First element affected:\n",
     548             :                                  Moose::stringify(*elem)););
     549             : 
     550      470254 :       const auto h_rel = max_hmax / hmin;
     551      470254 :       if (h_rel > 1.e2)
     552           2 :         mooseDoOnce(mooseWarning(warn_prefix,
     553             :                                  "Element hmin varies significantly from subdomain hmax.\n",
     554             :                                  warn_suffix,
     555             :                                  "First element affected:\n",
     556             :                                  Moose::stringify(*elem)););
     557             :     }
     558             :   }
     559        3921 : }
     560             : 
     561             : void
     562        7957 : RayTracingStudy::registeredRaySetup()
     563             : {
     564             :   // First, clear the objects associated with each Ray on each thread
     565        7957 :   const auto num_rays = _registered_ray_map.size();
     566       18252 :   for (auto & entry : _threaded_ray_object_registration)
     567             :   {
     568       10295 :     entry.clear();
     569       10295 :     entry.resize(num_rays);
     570             :   }
     571             : 
     572        7957 :   const auto rtos = getRayTracingObjects();
     573             : 
     574        7957 :   if (_use_ray_registration)
     575             :   {
     576             :     // All of the registered ray names - used when a RayTracingObject did not specify
     577             :     // any Rays so it should be associated with all Rays.
     578             :     std::vector<std::string> all_ray_names;
     579        5004 :     all_ray_names.reserve(_registered_ray_map.size());
     580       16562 :     for (const auto & pair : _registered_ray_map)
     581       11558 :       all_ray_names.push_back(pair.first);
     582             : 
     583       13930 :     for (auto & rto : rtos)
     584             :     {
     585             :       // The Ray names associated with this RayTracingObject
     586        8928 :       const auto & ray_names = rto->parameters().get<std::vector<std::string>>("rays");
     587             :       // The registration for RayTracingObjects for the thread rto is on
     588        8928 :       const auto tid = rto->parameters().get<THREAD_ID>("_tid");
     589        8928 :       auto & registration = _threaded_ray_object_registration[tid];
     590             : 
     591             :       // Register each Ray for this object in the registration
     592       28400 :       for (const auto & ray_name : (ray_names.empty() ? all_ray_names : ray_names))
     593             :       {
     594       17025 :         const auto id = registeredRayID(ray_name, /* graceful = */ true);
     595       17025 :         if (ray_names.size() && id == Ray::INVALID_RAY_ID)
     596           2 :           rto->paramError(
     597           2 :               "rays", "Supplied ray '", ray_name, "' is not a registered Ray in ", typeAndName());
     598       17023 :         registration[id].insert(rto);
     599             :       }
     600             :     }
     601        5002 :   }
     602             :   // Not using Ray registration
     603             :   else
     604             :   {
     605        9489 :     for (const auto & rto : rtos)
     606        6538 :       if (rto->parameters().get<std::vector<std::string>>("rays").size())
     607           2 :         rto->paramError(
     608             :             "rays",
     609             :             "Rays cannot be supplied when the study does not require Ray registration.\n\n",
     610             :             type(),
     611             :             " does not require Ray registration.");
     612             :   }
     613        7953 : }
     614             : 
     615             : void
     616        8023 : RayTracingStudy::zeroAuxVariables()
     617             : {
     618             :   std::set<std::string> vars_to_be_zeroed;
     619             :   std::vector<RayKernelBase *> ray_kernels;
     620        8023 :   getRayKernels(ray_kernels, 0);
     621       16431 :   for (auto & rk : ray_kernels)
     622             :   {
     623        8408 :     AuxRayKernel * aux_rk = dynamic_cast<AuxRayKernel *>(rk);
     624        8408 :     if (aux_rk)
     625          70 :       vars_to_be_zeroed.insert(aux_rk->variable().name());
     626             :   }
     627             : 
     628             :   std::vector<std::string> vars_to_be_zeroed_vec(vars_to_be_zeroed.begin(),
     629        8023 :                                                  vars_to_be_zeroed.end());
     630        8023 :   _fe_problem.getAuxiliarySystem().zeroVariables(vars_to_be_zeroed_vec);
     631       16046 : }
     632             : 
     633             : void
     634     4955206 : RayTracingStudy::segmentSubdomainSetup(const SubdomainID subdomain,
     635             :                                        const THREAD_ID tid,
     636             :                                        const RayID ray_id)
     637             : {
     638             :   mooseAssert(currentlyPropagating(), "Should not call while not propagating");
     639             : 
     640             :   // Call subdomain setup on FE
     641     4955206 :   _fe_problem.subdomainSetup(subdomain, tid);
     642             : 
     643             :   std::set<MooseVariableFEBase *> needed_moose_vars;
     644             :   std::unordered_set<unsigned int> needed_mat_props;
     645             : 
     646             :   // Get RayKernels and their dependencies and call subdomain setup
     647     4955206 :   getRayKernels(_threaded_current_ray_kernels[tid], subdomain, tid, ray_id);
     648     9917397 :   for (auto & rkb : _threaded_current_ray_kernels[tid])
     649             :   {
     650     4962191 :     rkb->subdomainSetup();
     651             : 
     652     4962191 :     const auto & mv_deps = rkb->getMooseVariableDependencies();
     653     4962191 :     needed_moose_vars.insert(mv_deps.begin(), mv_deps.end());
     654             : 
     655     4962191 :     const auto & mp_deps = rkb->getMatPropDependencies();
     656     4962191 :     needed_mat_props.insert(mp_deps.begin(), mp_deps.end());
     657             :   }
     658             : 
     659             :   // Prepare aux vars
     660     4992976 :   for (auto & var : needed_moose_vars)
     661       37770 :     if (var->kind() == Moose::VarKindType::VAR_AUXILIARY)
     662        8306 :       var->prepareAux();
     663             : 
     664     4955206 :   _fe_problem.setActiveElementalMooseVariables(needed_moose_vars, tid);
     665     4955206 :   _fe_problem.prepareMaterials(needed_mat_props, subdomain, tid);
     666     4955206 : }
     667             : 
     668             : void
     669    33831036 : RayTracingStudy::reinitSegment(
     670             :     const Elem * elem, const Point & start, const Point & end, const Real length, THREAD_ID tid)
     671             : {
     672             :   mooseAssert(MooseUtils::absoluteFuzzyEqual((start - end).norm(), length), "Invalid length");
     673             :   mooseAssert(currentlyPropagating(), "Should not call while not propagating");
     674             : 
     675    33831036 :   _fe_problem.setCurrentSubdomainID(elem, tid);
     676             : 
     677             :   // If we have any variables or material properties that are active, we definitely need to reinit
     678    67551159 :   bool reinit = _fe_problem.hasActiveElementalMooseVariables(tid) ||
     679    33720123 :                 _fe_problem.hasActiveMaterialProperties(tid);
     680             :   // If not, make sure that the RayKernels have not requested a reinit (this could happen when a
     681             :   // RayKernel doesn't have variables or materials but still does an integration and needs qps)
     682             :   if (!reinit)
     683    67439199 :     for (const RayKernelBase * rk : currentRayKernels(tid))
     684    33719796 :       if (rk->needSegmentReinit())
     685             :       {
     686             :         reinit = true;
     687             :         break;
     688             :       }
     689             : 
     690    33831036 :   if (reinit)
     691             :   {
     692      111633 :     _fe_problem.prepare(elem, tid);
     693             : 
     694             :     std::vector<Point> points;
     695             :     std::vector<Real> weights;
     696      111633 :     buildSegmentQuadrature(start, end, length, points, weights);
     697      111633 :     _fe_problem.reinitElemPhys(elem, points, tid);
     698      111633 :     _fe_problem.assembly(tid, _sys.number()).modifyArbitraryWeights(weights);
     699             : 
     700      111633 :     _fe_problem.reinitMaterials(elem->subdomain_id(), tid);
     701      111633 :   }
     702    33831036 : }
     703             : 
     704             : void
     705      111633 : RayTracingStudy::buildSegmentQuadrature(const Point & start,
     706             :                                         const Point & end,
     707             :                                         const Real length,
     708             :                                         std::vector<Point> & points,
     709             :                                         std::vector<Real> & weights) const
     710             : {
     711      111633 :   points.resize(_segment_qrule->n_points());
     712      111633 :   weights.resize(_segment_qrule->n_points());
     713             : 
     714             :   const Point diff = end - start;
     715             :   const Point sum = end + start;
     716             :   mooseAssert(MooseUtils::absoluteFuzzyEqual(length, diff.norm()), "Invalid length");
     717             : 
     718             :   // The standard quadrature rule should be on x = [-1, 1]
     719             :   // To scale the points, you...
     720             :   //  - Scale to size of the segment in 3D
     721             :   //    initial_scaled_qp = x_qp * 0.5 * (end - start) = 0.5 * x_qp * diff
     722             :   //  - Shift quadrature midpoint to segment midpoint
     723             :   //    final_qp = initial_scaled_qp + 0.5 * (end - start) = initial_scaled_qp + 0.5 * sum
     724             :   //             = 0.5 * (x_qp * diff + sum)
     725      312383 :   for (unsigned int qp = 0; qp < _segment_qrule->n_points(); ++qp)
     726             :   {
     727      200750 :     points[qp] = 0.5 * (_segment_qrule->qp(qp)(0) * diff + sum);
     728      200750 :     weights[qp] = 0.5 * _segment_qrule->w(qp) * length;
     729             :   }
     730      111633 : }
     731             : 
     732             : void
     733    35293895 : RayTracingStudy::postOnSegment(const THREAD_ID tid, const std::shared_ptr<Ray> & /* ray */)
     734             : {
     735             :   mooseAssert(currentlyPropagating(), "Should not call while not propagating");
     736    35293895 :   if (!_fe_problem.currentlyComputingJacobian() && !_fe_problem.currentlyComputingResidual())
     737             :     mooseAssert(_num_cached[tid] == 0,
     738             :                 "Values should only be cached when computing Jacobian/residual");
     739             : 
     740             :   // Fill into cached Jacobian/residuals if necessary
     741    35293895 :   if (_fe_problem.currentlyComputingJacobian())
     742             :   {
     743        9078 :     _fe_problem.cacheJacobian(tid);
     744             : 
     745        9078 :     if (++_num_cached[tid] == 20)
     746             :     {
     747             :       Threads::spin_mutex::scoped_lock lock(_spin_mutex);
     748         372 :       _fe_problem.addCachedJacobian(tid);
     749         372 :       _num_cached[tid] = 0;
     750             :     }
     751             :   }
     752    35284817 :   else if (_fe_problem.currentlyComputingResidual())
     753             :   {
     754       74087 :     _fe_problem.cacheResidual(tid);
     755             : 
     756       74087 :     if (++_num_cached[tid] == 20)
     757             :     {
     758             :       Threads::spin_mutex::scoped_lock lock(_spin_mutex);
     759        2212 :       _fe_problem.addCachedResidual(tid);
     760        2212 :       _num_cached[tid] = 0;
     761             :     }
     762             :   }
     763    35293895 : }
     764             : 
     765             : void
     766        8023 : RayTracingStudy::executeStudy()
     767             : {
     768       16046 :   TIME_SECTION("executeStudy", 2, "Executing Study");
     769             : 
     770             :   mooseAssert(_called_initial_setup, "Initial setup not called");
     771             : 
     772             :   // Reset ray start/complete timers
     773        8023 :   _total_intersections = 0;
     774        8023 :   _max_intersections = 0;
     775        8023 :   _max_trajectory_changes = 0;
     776             : 
     777             :   // Reset physical tracing stats
     778      120345 :   for (auto & val : _local_trace_ray_results)
     779      112322 :     val = 0;
     780             : 
     781             :   // Reset crossing and intersection
     782        8023 :   _ending_processor_crossings = 0;
     783        8023 :   _total_processor_crossings = 0;
     784        8023 :   _ending_max_processor_crossings = 0;
     785        8023 :   _ending_intersections = 0;
     786        8023 :   _ending_max_intersections = 0;
     787        8023 :   _ending_max_trajectory_changes = 0;
     788        8023 :   _ending_distance = 0;
     789        8023 :   _total_distance = 0;
     790             : 
     791             :   // Zero the AuxVariables that our AuxRayKernels contribute to before they accumulate
     792        8023 :   zeroAuxVariables();
     793             : 
     794        8023 :   preExecuteStudy();
     795       23491 :   for (auto & rto : getRayTracingObjects())
     796       23491 :     rto->preExecuteStudy();
     797             : 
     798        8023 :   _ray_bank.clear();
     799             : 
     800       18417 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     801             :   {
     802       10394 :     _threaded_trace_ray[tid]->preExecute();
     803             :     _threaded_cached_normals[tid].clear();
     804             :   }
     805             : 
     806        8023 :   _communicator.barrier();
     807        8023 :   _execution_start_time = std::chrono::steady_clock::now();
     808             : 
     809        8023 :   _parallel_ray_study->preExecute();
     810             : 
     811             :   {
     812             :     {
     813        8023 :       auto generation_start_time = std::chrono::steady_clock::now();
     814             : 
     815       16046 :       TIME_SECTION("generateRays", 2, "Generating Rays");
     816             : 
     817        8023 :       generateRays();
     818             : 
     819        7961 :       _generation_time = std::chrono::steady_clock::now() - generation_start_time;
     820             :     }
     821             : 
     822             :     // At this point, nobody is working so this is good time to make sure
     823             :     // Rays are unique across all processors in the working buffer
     824        7961 :     if (verifyRays())
     825             :     {
     826        7961 :       verifyUniqueRays(_parallel_ray_study->workBuffer().begin(),
     827             :                        _parallel_ray_study->workBuffer().end(),
     828             :                        /* error_suffix = */ "after generateRays()");
     829             : 
     830       15916 :       verifyUniqueRayIDs(_parallel_ray_study->workBuffer().begin(),
     831             :                          _parallel_ray_study->workBuffer().end(),
     832             :                          /* global = */ true,
     833             :                          /* error_suffix = */ "after generateRays()");
     834             :     }
     835             : 
     836        7957 :     registeredRaySetup();
     837             : 
     838             :     {
     839       15906 :       TIME_SECTION("propagateRays", 2, "Propagating Rays");
     840             : 
     841        7953 :       const auto propagation_start_time = std::chrono::steady_clock::now();
     842             : 
     843        7953 :       _parallel_ray_study->execute();
     844             : 
     845        7927 :       _propagation_time = std::chrono::steady_clock::now() - propagation_start_time;
     846             :     }
     847             :   }
     848             : 
     849        7927 :   _execution_time = std::chrono::steady_clock::now() - _execution_start_time;
     850             : 
     851        7927 :   if (verifyRays())
     852             :   {
     853       15854 :     verifyUniqueRays(_parallel_ray_study->workBuffer().begin(),
     854             :                      _parallel_ray_study->workBuffer().end(),
     855             :                      /* error_suffix = */ "after tracing completed");
     856             : 
     857             : #ifndef NDEBUG
     858             :     // Outside of debug, _ray_bank always holds all of the Rays that have ended on this processor
     859             :     // We can use this as a global point to check for unique IDs for every Ray that has traced
     860             :     verifyUniqueRayIDs(_ray_bank.begin(),
     861             :                        _ray_bank.end(),
     862             :                        /* global = */ true,
     863             :                        /* error_suffix = */ "after tracing completed");
     864             : #endif
     865             :   }
     866             : 
     867             :   // Update counters from the threaded trace objects
     868       18177 :   for (const auto & tr : _threaded_trace_ray)
     869      153750 :     for (std::size_t i = 0; i < _local_trace_ray_results.size(); ++i)
     870      143500 :       _local_trace_ray_results[i] += tr->results()[i];
     871             : 
     872             :   // Update local ending counters
     873        7927 :   _total_processor_crossings = _ending_processor_crossings;
     874        7927 :   _max_processor_crossings = _ending_max_processor_crossings;
     875        7927 :   _total_intersections = _ending_intersections;
     876        7927 :   _max_intersections = _ending_max_intersections;
     877        7927 :   _max_trajectory_changes = _ending_max_trajectory_changes;
     878        7927 :   _total_distance = _ending_distance;
     879             :   // ...and communicate the global values
     880        7927 :   _communicator.sum(_total_processor_crossings);
     881        7927 :   _communicator.max(_max_processor_crossings);
     882        7927 :   _communicator.sum(_total_intersections);
     883        7927 :   _communicator.max(_max_intersections);
     884        7927 :   _communicator.max(_max_trajectory_changes);
     885        7927 :   _communicator.sum(_total_distance);
     886             : 
     887             :   // Throw a warning with the number of failed (tolerated) traces
     888        7927 :   if (_tolerate_failure)
     889             :   {
     890           8 :     auto failures = _local_trace_ray_results[TraceRay::FAILED_TRACES];
     891           8 :     _communicator.sum(failures);
     892           8 :     if (failures)
     893           8 :       mooseWarning(
     894             :           type(), " '", name(), "': ", failures, " ray tracing failures were tolerated.\n");
     895             :   }
     896             : 
     897             :   // Clear the current RayKernels
     898       18177 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     899       10250 :     _threaded_current_ray_kernels[tid].clear();
     900             : 
     901             :   // Move the threaded cache trace information into the full cached trace vector
     902             :   // Here, we only clear the cached vectors so that we might not have to
     903             :   // reallocate on future traces
     904             :   std::size_t num_entries = 0;
     905       18177 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     906       10250 :     num_entries += _threaded_cached_traces[tid].size();
     907        7927 :   _cached_traces.clear();
     908        7927 :   _cached_traces.reserve(num_entries);
     909       18177 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     910             :   {
     911       24037 :     for (const auto & entry : _threaded_cached_traces[tid])
     912       13787 :       _cached_traces.emplace_back(std::move(entry));
     913       10250 :     _threaded_cached_traces[tid].clear();
     914             :   }
     915             : 
     916             :   // Add any stragglers that contribute to the Jacobian or residual
     917       18177 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     918       10250 :     if (_num_cached[tid] != 0)
     919             :     {
     920             :       mooseAssert(_fe_problem.currentlyComputingJacobian() ||
     921             :                       _fe_problem.currentlyComputingResidual(),
     922             :                   "Should not have cached values without Jacobian/residual computation");
     923             : 
     924        4623 :       if (_fe_problem.currentlyComputingJacobian())
     925         240 :         _fe_problem.addCachedJacobian(tid);
     926             :       else
     927        4383 :         _fe_problem.addCachedResidual(tid);
     928             : 
     929        4623 :       _num_cached[tid] = 0;
     930             :     }
     931             : 
     932             :   // AuxRayKernels may have modified AuxVariables
     933        7927 :   _fe_problem.getAuxiliarySystem().solution().close();
     934        7927 :   _fe_problem.getAuxiliarySystem().update();
     935             : 
     936             :   // Clear FE
     937       18177 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     938             :   {
     939       10250 :     _fe_problem.clearActiveElementalMooseVariables(tid);
     940       10250 :     _fe_problem.clearActiveMaterialProperties(tid);
     941             :   }
     942             : 
     943        7927 :   postExecuteStudy();
     944       23322 :   for (auto & rto : getRayTracingObjects())
     945       23322 :     rto->postExecuteStudy();
     946        7923 : }
     947             : 
     948             : void
     949     4120844 : RayTracingStudy::onCompleteRay(const std::shared_ptr<Ray> & ray)
     950             : {
     951             :   mooseAssert(currentlyPropagating(), "Should only be called during Ray propagation");
     952             : 
     953     4120844 :   _ending_processor_crossings += ray->processorCrossings();
     954     4120844 :   _ending_max_processor_crossings =
     955     4126897 :       std::max(_ending_max_processor_crossings, ray->processorCrossings());
     956     4120844 :   _ending_intersections += ray->intersections();
     957     4138202 :   _ending_max_intersections = std::max(_ending_max_intersections, ray->intersections());
     958     4120844 :   _ending_max_trajectory_changes =
     959     4121259 :       std::max(_ending_max_trajectory_changes, ray->trajectoryChanges());
     960     4120844 :   _ending_distance += ray->distance();
     961             : 
     962             : #ifdef NDEBUG
     963             :   // In non-opt modes, we will always bank the Rays for debugging
     964     4120844 :   if (_bank_rays_on_completion)
     965             : #endif
     966     4120842 :     _ray_bank.emplace_back(ray);
     967     4120844 : }
     968             : 
     969             : RayDataIndex
     970        2555 : RayTracingStudy::registerRayDataInternal(const std::string & name, const bool aux)
     971             : {
     972             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
     973             : 
     974        2555 :   if (_called_initial_setup)
     975           4 :     mooseError("Cannot register Ray ", (aux ? "aux " : ""), "data after initialSetup()");
     976             : 
     977        2553 :   auto & map = aux ? _ray_aux_data_map : _ray_data_map;
     978             :   const auto find = map.find(name);
     979        2553 :   if (find != map.end())
     980          73 :     return find->second;
     981             : 
     982        2480 :   auto & other_map = aux ? _ray_data_map : _ray_aux_data_map;
     983        2480 :   if (other_map.find(name) != other_map.end())
     984           4 :     mooseError("Cannot register Ray aux data with name ",
     985             :                name,
     986             :                " because Ray ",
     987           2 :                (aux ? "(non-aux)" : "aux"),
     988             :                " data already exists with said name.");
     989             : 
     990             :   // Add into the name -> index map
     991        2478 :   map.emplace(name, map.size());
     992             : 
     993             :   // Add into the index -> names vector
     994        2478 :   auto & vector = aux ? _ray_aux_data_names : _ray_data_names;
     995        2478 :   vector.push_back(name);
     996             : 
     997        2478 :   return map.size() - 1;
     998             : }
     999             : 
    1000             : std::vector<RayDataIndex>
    1001         266 : RayTracingStudy::registerRayDataInternal(const std::vector<std::string> & names, const bool aux)
    1002             : {
    1003         266 :   std::vector<RayDataIndex> indices(names.size());
    1004         589 :   for (std::size_t i = 0; i < names.size(); ++i)
    1005         323 :     indices[i] = registerRayDataInternal(names[i], aux);
    1006         266 :   return indices;
    1007           0 : }
    1008             : 
    1009             : RayDataIndex
    1010        1394 : RayTracingStudy::getRayDataIndexInternal(const std::string & name,
    1011             :                                          const bool aux,
    1012             :                                          const bool graceful) const
    1013             : {
    1014             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
    1015             : 
    1016        1394 :   const auto & map = aux ? _ray_aux_data_map : _ray_data_map;
    1017             :   const auto find = map.find(name);
    1018        1394 :   if (find != map.end())
    1019        1380 :     return find->second;
    1020             : 
    1021          14 :   if (graceful)
    1022             :     return Ray::INVALID_RAY_DATA_INDEX;
    1023             : 
    1024           6 :   const auto & other_map = aux ? _ray_data_map : _ray_aux_data_map;
    1025           6 :   if (other_map.find(name) != other_map.end())
    1026           8 :     mooseError("Ray data with name '",
    1027             :                name,
    1028             :                "' was not found.\n\n",
    1029             :                "However, Ray ",
    1030           4 :                (aux ? "non-aux" : "aux"),
    1031             :                " data with said name was found.\n",
    1032             :                "Did you mean to use ",
    1033           6 :                (aux ? "getRayDataIndex()/getRayDataIndices()?"
    1034             :                     : "getRayAuxDataIndex()/getRayAuxDataIndices()"),
    1035             :                "?");
    1036             : 
    1037           4 :   mooseError("Unknown Ray ", (aux ? "aux " : ""), "data with name ", name);
    1038             : }
    1039             : 
    1040             : std::vector<RayDataIndex>
    1041         710 : RayTracingStudy::getRayDataIndicesInternal(const std::vector<std::string> & names,
    1042             :                                            const bool aux,
    1043             :                                            const bool graceful) const
    1044             : {
    1045         710 :   std::vector<RayDataIndex> indices(names.size());
    1046         842 :   for (std::size_t i = 0; i < names.size(); ++i)
    1047         132 :     indices[i] = getRayDataIndexInternal(names[i], aux, graceful);
    1048         710 :   return indices;
    1049           0 : }
    1050             : 
    1051             : const std::string &
    1052        7346 : RayTracingStudy::getRayDataNameInternal(const RayDataIndex index, const bool aux) const
    1053             : {
    1054             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
    1055             : 
    1056       14692 :   if ((aux ? rayAuxDataSize() : rayDataSize()) < index)
    1057           4 :     mooseError("Unknown Ray ", aux ? "aux " : "", "data with index ", index);
    1058       14688 :   return aux ? _ray_aux_data_names[index] : _ray_data_names[index];
    1059             : }
    1060             : 
    1061             : RayDataIndex
    1062         978 : RayTracingStudy::registerRayData(const std::string & name)
    1063             : {
    1064         978 :   return registerRayDataInternal(name, /* aux = */ false);
    1065             : }
    1066             : 
    1067             : std::vector<RayDataIndex>
    1068         136 : RayTracingStudy::registerRayData(const std::vector<std::string> & names)
    1069             : {
    1070         136 :   return registerRayDataInternal(names, /* aux = */ false);
    1071             : }
    1072             : 
    1073             : RayDataIndex
    1074        1104 : RayTracingStudy::getRayDataIndex(const std::string & name, const bool graceful /* = false */) const
    1075             : {
    1076        1104 :   return getRayDataIndexInternal(name, /* aux = */ false, graceful);
    1077             : }
    1078             : 
    1079             : std::vector<RayDataIndex>
    1080         355 : RayTracingStudy::getRayDataIndices(const std::vector<std::string> & names,
    1081             :                                    const bool graceful /* = false */) const
    1082             : {
    1083         355 :   return getRayDataIndicesInternal(names, /* aux = */ false, graceful);
    1084             : }
    1085             : 
    1086             : const std::string &
    1087        3674 : RayTracingStudy::getRayDataName(const RayDataIndex index) const
    1088             : {
    1089        3674 :   return getRayDataNameInternal(index, /* aux = */ false);
    1090             : }
    1091             : 
    1092             : RayDataIndex
    1093        1254 : RayTracingStudy::registerRayAuxData(const std::string & name)
    1094             : {
    1095        1254 :   return registerRayDataInternal(name, /* aux = */ true);
    1096             : }
    1097             : 
    1098             : std::vector<RayDataIndex>
    1099         130 : RayTracingStudy::registerRayAuxData(const std::vector<std::string> & names)
    1100             : {
    1101         130 :   return registerRayDataInternal(names, /* aux = */ true);
    1102             : }
    1103             : 
    1104             : RayDataIndex
    1105         158 : RayTracingStudy::getRayAuxDataIndex(const std::string & name,
    1106             :                                     const bool graceful /* = false */) const
    1107             : {
    1108         158 :   return getRayDataIndexInternal(name, /* aux = */ true, graceful);
    1109             : }
    1110             : 
    1111             : std::vector<RayDataIndex>
    1112         355 : RayTracingStudy::getRayAuxDataIndices(const std::vector<std::string> & names,
    1113             :                                       const bool graceful /* = false */) const
    1114             : {
    1115         355 :   return getRayDataIndicesInternal(names, /* aux = */ true, graceful);
    1116             : }
    1117             : 
    1118             : const std::string &
    1119        3672 : RayTracingStudy::getRayAuxDataName(const RayDataIndex index) const
    1120             : {
    1121        3672 :   return getRayDataNameInternal(index, /* aux = */ true);
    1122             : }
    1123             : 
    1124             : bool
    1125       10394 : RayTracingStudy::hasRayKernels(const THREAD_ID tid)
    1126             : {
    1127             :   std::vector<RayKernelBase *> result;
    1128       10394 :   getRayKernels(result, tid);
    1129       10394 :   return result.size();
    1130       10394 : }
    1131             : 
    1132             : void
    1133     4955208 : RayTracingStudy::getRayKernels(std::vector<RayKernelBase *> & result, SubdomainID id, THREAD_ID tid)
    1134             : {
    1135             :   // If the cache doesn't have any attributes yet, it means that we haven't set
    1136             :   // the conditions yet. We do this so that it can be generated on the fly on first use.
    1137     4955208 :   if (!_threaded_cache_ray_kernel[tid].numAttribs())
    1138             :   {
    1139        2562 :     if (!_called_initial_setup)
    1140           2 :       mooseError("Should not call getRayKernels() before initialSetup()");
    1141             : 
    1142        2560 :     auto query = _fe_problem.theWarehouse()
    1143        2560 :                      .query()
    1144        5120 :                      .condition<AttribRayTracingStudy>(this)
    1145        2560 :                      .condition<AttribSystem>("RayKernel")
    1146        2560 :                      .condition<AttribThread>(tid);
    1147        2560 :     _threaded_cache_ray_kernel[tid] = query.clone();
    1148        2560 :   }
    1149             : 
    1150             :   _threaded_cache_ray_kernel[tid].queryInto(result, id);
    1151     4955206 : }
    1152             : 
    1153             : void
    1154     4955206 : RayTracingStudy::getRayKernels(std::vector<RayKernelBase *> & result,
    1155             :                                SubdomainID id,
    1156             :                                THREAD_ID tid,
    1157             :                                RayID ray_id)
    1158             : {
    1159             :   // No Ray registration: no need to sift through objects
    1160     4955206 :   if (!_use_ray_registration)
    1161             :   {
    1162     4945234 :     getRayKernels(result, id, tid);
    1163             :   }
    1164             :   // Has Ray registration: only pick the objects associated with ray_id
    1165             :   else
    1166             :   {
    1167             :     // Get all of the kernels on this block
    1168             :     std::vector<RayKernelBase *> rkbs;
    1169        9972 :     getRayKernels(rkbs, id, tid);
    1170             : 
    1171             :     // The RayTracingObjects associated with this ray
    1172        9972 :     const auto & ray_id_rtos = _threaded_ray_object_registration[tid][ray_id];
    1173             : 
    1174             :     // The result is the union of all of the kernels and the objects associated with this Ray
    1175        9972 :     result.clear();
    1176       24689 :     for (auto rkb : rkbs)
    1177       14717 :       if (ray_id_rtos.count(rkb))
    1178       10117 :         result.push_back(rkb);
    1179        9972 :   }
    1180     4955206 : }
    1181             : 
    1182             : void
    1183     1332369 : RayTracingStudy::getRayBCs(std::vector<RayBoundaryConditionBase *> & result,
    1184             :                            BoundaryID id,
    1185             :                            THREAD_ID tid)
    1186             : {
    1187             :   // If the cache doesn't have any attributes yet, it means that we haven't set
    1188             :   // the conditions yet. We do this so that it can be generated on the fly on first use.
    1189     1332369 :   if (!_threaded_cache_ray_bc[tid].numAttribs())
    1190             :   {
    1191        2408 :     if (!_called_initial_setup)
    1192           2 :       mooseError("Should not call getRayBCs() before initialSetup()");
    1193             : 
    1194        2406 :     auto query = _fe_problem.theWarehouse()
    1195        2406 :                      .query()
    1196        4812 :                      .condition<AttribRayTracingStudy>(this)
    1197        2406 :                      .condition<AttribSystem>("RayBoundaryCondition")
    1198        2406 :                      .condition<AttribThread>(tid);
    1199        2406 :     _threaded_cache_ray_bc[tid] = query.clone();
    1200        2406 :   }
    1201             : 
    1202     1332367 :   _threaded_cache_ray_bc[tid].queryInto(result, std::make_tuple(id, false));
    1203     1332367 : }
    1204             : 
    1205             : void
    1206     1411579 : RayTracingStudy::getRayBCs(std::vector<RayBoundaryConditionBase *> & result,
    1207             :                            const std::vector<TraceRayBndElement> & bnd_elems,
    1208             :                            THREAD_ID tid,
    1209             :                            RayID ray_id)
    1210             : {
    1211             :   // No Ray registration: no need to sift through objects
    1212     1411579 :   if (!_use_ray_registration)
    1213             :   {
    1214     1409568 :     if (bnd_elems.size() == 1)
    1215     1323548 :       getRayBCs(result, bnd_elems[0].bnd_id, tid);
    1216             :     else
    1217             :     {
    1218       86020 :       std::vector<BoundaryID> bnd_ids(bnd_elems.size());
    1219      265212 :       for (MooseIndex(bnd_elems.size()) i = 0; i < bnd_elems.size(); ++i)
    1220      179192 :         bnd_ids[i] = bnd_elems[i].bnd_id;
    1221       86020 :       getRayBCs(result, bnd_ids, tid);
    1222       86020 :     }
    1223             :   }
    1224             :   // Has Ray registration: only pick the objects associated with ray_id
    1225             :   else
    1226             :   {
    1227             :     // Get all of the RayBCs on these boundaries
    1228             :     std::vector<RayBoundaryConditionBase *> rbcs;
    1229        2011 :     if (bnd_elems.size() == 1)
    1230        1656 :       getRayBCs(rbcs, bnd_elems[0].bnd_id, tid);
    1231             :     else
    1232             :     {
    1233         355 :       std::vector<BoundaryID> bnd_ids(bnd_elems.size());
    1234        1227 :       for (MooseIndex(bnd_elems.size()) i = 0; i < bnd_elems.size(); ++i)
    1235         872 :         bnd_ids[i] = bnd_elems[i].bnd_id;
    1236         355 :       getRayBCs(rbcs, bnd_ids, tid);
    1237         355 :     }
    1238             : 
    1239             :     // The RayTracingObjects associated with this ray
    1240             :     mooseAssert(ray_id < _threaded_ray_object_registration[tid].size(), "Not in registration");
    1241        2011 :     const auto & ray_id_rtos = _threaded_ray_object_registration[tid][ray_id];
    1242             : 
    1243             :     // The result is the union of all of the kernels and the objects associated with this Ray
    1244        2011 :     result.clear();
    1245        5834 :     for (auto rbc : rbcs)
    1246        3823 :       if (ray_id_rtos.count(rbc))
    1247        2049 :         result.push_back(rbc);
    1248        2011 :   }
    1249     1411579 : }
    1250             : 
    1251             : std::vector<RayTracingObject *>
    1252       35850 : RayTracingStudy::getRayTracingObjects()
    1253             : {
    1254             :   std::vector<RayTracingObject *> result;
    1255       71700 :   _fe_problem.theWarehouse().query().condition<AttribRayTracingStudy>(this).queryInto(result);
    1256       35850 :   return result;
    1257           0 : }
    1258             : 
    1259             : const std::vector<std::shared_ptr<Ray>> &
    1260        1108 : RayTracingStudy::rayBank() const
    1261             : {
    1262        1108 :   if (!_bank_rays_on_completion)
    1263           2 :     mooseError("The Ray bank is not available because the private parameter "
    1264             :                "'_bank_rays_on_completion' is set to false.");
    1265        1106 :   if (currentlyGenerating() || currentlyPropagating())
    1266           2 :     mooseError("Cannot get the Ray bank during generation or propagation.");
    1267             : 
    1268        1104 :   return _ray_bank;
    1269             : }
    1270             : 
    1271             : std::shared_ptr<Ray>
    1272         744 : RayTracingStudy::getBankedRay(const RayID ray_id) const
    1273             : {
    1274             :   // This is only a linear search - can be improved on with a map in the future
    1275             :   // if this is used on a larger scale
    1276         744 :   std::shared_ptr<Ray> ray;
    1277        1279 :   for (const std::shared_ptr<Ray> & possible_ray : rayBank())
    1278        1012 :     if (possible_ray->id() == ray_id)
    1279             :     {
    1280             :       ray = possible_ray;
    1281             :       break;
    1282             :     }
    1283             : 
    1284             :   // Make sure one and only one processor has the Ray
    1285         744 :   unsigned int have_ray = ray ? 1 : 0;
    1286         744 :   _communicator.sum(have_ray);
    1287         744 :   if (have_ray == 0)
    1288           2 :     mooseError("Could not find a Ray with the ID ", ray_id, " in the Ray banks.");
    1289             : 
    1290             :   // This should never happen... but let's make sure
    1291             :   mooseAssert(have_ray == 1, "Multiple rays with the same ID were found in the Ray banks");
    1292             : 
    1293         742 :   return ray;
    1294             : }
    1295             : 
    1296             : RayData
    1297         744 : RayTracingStudy::getBankedRayDataInternal(const RayID ray_id,
    1298             :                                           const RayDataIndex index,
    1299             :                                           const bool aux) const
    1300             : {
    1301             :   // Will be a nullptr shared_ptr if this processor doesn't own the Ray
    1302         744 :   const std::shared_ptr<Ray> ray = getBankedRay(ray_id);
    1303             : 
    1304         742 :   Real value = ray ? (aux ? ray->auxData(index) : ray->data(index)) : 0;
    1305         742 :   _communicator.sum(value);
    1306        1219 :   return value;
    1307             : }
    1308             : 
    1309             : RayData
    1310         688 : RayTracingStudy::getBankedRayData(const RayID ray_id, const RayDataIndex index) const
    1311             : {
    1312         688 :   return getBankedRayDataInternal(ray_id, index, /* aux = */ false);
    1313             : }
    1314             : 
    1315             : RayData
    1316          56 : RayTracingStudy::getBankedRayAuxData(const RayID ray_id, const RayDataIndex index) const
    1317             : {
    1318          56 :   return getBankedRayDataInternal(ray_id, index, /* aux = */ true);
    1319             : }
    1320             : 
    1321             : RayID
    1322        2519 : RayTracingStudy::registerRay(const std::string & name)
    1323             : {
    1324             :   libmesh_parallel_only(comm());
    1325             : 
    1326             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
    1327             : 
    1328        2519 :   if (!_use_ray_registration)
    1329           2 :     mooseError("Cannot use registerRay() with Ray registration disabled");
    1330             : 
    1331             :   // This is parallel only for now. We could likely stagger the ID building like we do with
    1332             :   // the unique IDs, but it would require a sync point which isn't there right now
    1333             :   libmesh_parallel_only(comm());
    1334             : 
    1335        2517 :   const auto & it = _registered_ray_map.find(name);
    1336        2517 :   if (it != _registered_ray_map.end())
    1337           0 :     return it->second;
    1338             : 
    1339        2517 :   const auto id = _reverse_registered_ray_map.size();
    1340        2517 :   _registered_ray_map.emplace(name, id);
    1341        2517 :   _reverse_registered_ray_map.push_back(name);
    1342        2517 :   return id;
    1343             : }
    1344             : 
    1345             : RayID
    1346       17719 : RayTracingStudy::registeredRayID(const std::string & name, const bool graceful /* = false */) const
    1347             : {
    1348             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
    1349             : 
    1350       17719 :   if (!_use_ray_registration)
    1351           2 :     mooseError("Should not use registeredRayID() with Ray registration disabled");
    1352             : 
    1353       17717 :   const auto search = _registered_ray_map.find(name);
    1354       17717 :   if (search != _registered_ray_map.end())
    1355       17709 :     return search->second;
    1356             : 
    1357           8 :   if (graceful)
    1358             :     return Ray::INVALID_RAY_ID;
    1359             : 
    1360           2 :   mooseError("Attempted to obtain ID of registered Ray ",
    1361             :              name,
    1362             :              ", but a Ray with said name is not registered.");
    1363             : }
    1364             : 
    1365             : const std::string &
    1366          41 : RayTracingStudy::registeredRayName(const RayID ray_id) const
    1367             : {
    1368             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
    1369             : 
    1370          41 :   if (!_use_ray_registration)
    1371           2 :     mooseError("Should not use registeredRayName() with Ray registration disabled");
    1372             : 
    1373          39 :   if (_reverse_registered_ray_map.size() > ray_id)
    1374          37 :     return _reverse_registered_ray_map[ray_id];
    1375             : 
    1376           2 :   mooseError("Attempted to obtain name of registered Ray with ID ",
    1377             :              ray_id,
    1378             :              ", but a Ray with said ID is not registered.");
    1379             : }
    1380             : 
    1381             : Real
    1382        3827 : RayTracingStudy::computeTotalVolume()
    1383             : {
    1384        3827 :   Real volume = 0;
    1385      466527 :   for (const auto & elem : *_mesh.getActiveLocalElementRange())
    1386      462700 :     volume += elem->volume();
    1387        3827 :   _communicator.sum(volume);
    1388        3827 :   return volume;
    1389             : }
    1390             : 
    1391             : const std::vector<std::vector<BoundaryID>> &
    1392       15182 : RayTracingStudy::getInternalSidesets(const Elem * elem) const
    1393             : {
    1394             :   mooseAssert(_use_internal_sidesets, "Not using internal sidesets");
    1395             :   mooseAssert(hasInternalSidesets(), "Processor does not have internal sidesets");
    1396             :   mooseAssert(_internal_sidesets_map.size() > _elem_index_helper.getIndex(elem),
    1397             :               "Internal sideset map not initialized");
    1398             : 
    1399             :   const auto index = _elem_index_helper.getIndex(elem);
    1400       15182 :   return _internal_sidesets_map[index];
    1401             : }
    1402             : 
    1403             : TraceData &
    1404       13787 : RayTracingStudy::initThreadedCachedTrace(const std::shared_ptr<Ray> & ray, THREAD_ID tid)
    1405             : {
    1406             :   mooseAssert(shouldCacheTrace(ray), "Not caching trace");
    1407             :   mooseAssert(currentlyPropagating(), "Should only use while tracing");
    1408             : 
    1409       13787 :   _threaded_cached_traces[tid].emplace_back(ray);
    1410       13787 :   return _threaded_cached_traces[tid].back();
    1411             : }
    1412             : 
    1413             : void
    1414        8630 : RayTracingStudy::verifyUniqueRayIDs(const std::vector<std::shared_ptr<Ray>>::const_iterator begin,
    1415             :                                     const std::vector<std::shared_ptr<Ray>>::const_iterator end,
    1416             :                                     const bool global,
    1417             :                                     const std::string & error_suffix) const
    1418             : {
    1419             :   // Determine the unique set of Ray IDs on this processor,
    1420             :   // and if not locally unique throw an error. Once we build this set,
    1421             :   // we will send it to rank 0 to verify globally
    1422             :   std::set<RayID> local_rays;
    1423     4050625 :   for (const std::shared_ptr<Ray> & ray : as_range(begin, end))
    1424             :   {
    1425             :     mooseAssert(ray, "Null ray");
    1426             : 
    1427             :     // Try to insert into the set; the second entry in the pair
    1428             :     // will be false if it was not inserted
    1429     4041999 :     if (!local_rays.insert(ray->id()).second)
    1430             :     {
    1431           4 :       for (const std::shared_ptr<Ray> & other_ray : as_range(begin, end))
    1432           4 :         if (ray.get() != other_ray.get() && ray->id() == other_ray->id())
    1433           8 :           mooseError("Multiple Rays exist with ID ",
    1434           4 :                      ray->id(),
    1435             :                      " on processor ",
    1436           4 :                      _pid,
    1437             :                      " ",
    1438             :                      error_suffix,
    1439             :                      "\n\nOffending Ray information:\n\n",
    1440           4 :                      ray->getInfo(),
    1441             :                      "\n",
    1442           4 :                      other_ray->getInfo());
    1443             :     }
    1444             :   }
    1445             : 
    1446             :   // Send IDs from all procs to rank 0 and verify on rank 0
    1447        8626 :   if (global)
    1448             :   {
    1449             :     // Package our local IDs and send to rank 0
    1450             :     std::map<processor_id_type, std::vector<RayID>> send_ids;
    1451        7957 :     if (local_rays.size())
    1452             :       send_ids.emplace(std::piecewise_construct,
    1453        6080 :                        std::forward_as_tuple(0),
    1454        6080 :                        std::forward_as_tuple(local_rays.begin(), local_rays.end()));
    1455             :     local_rays.clear();
    1456             : 
    1457             :     // Mapping on rank 0 from ID -> processor ID
    1458             :     std::map<RayID, processor_id_type> global_map;
    1459             : 
    1460             :     // Verify another processor's IDs against the global map on rank 0
    1461             :     const auto check_ids =
    1462        6080 :         [this, &global_map, &error_suffix](processor_id_type pid, const std::vector<RayID> & ids)
    1463             :     {
    1464     4044512 :       for (const RayID id : ids)
    1465             :       {
    1466     4038432 :         const auto emplace_pair = global_map.emplace(id, pid);
    1467             : 
    1468             :         // Means that this ID already exists in the map
    1469     4038432 :         if (!emplace_pair.second)
    1470           0 :           mooseError("Ray with ID ",
    1471             :                      id,
    1472             :                      " exists on ranks ",
    1473           0 :                      emplace_pair.first->second,
    1474             :                      " and ",
    1475             :                      pid,
    1476             :                      "\n",
    1477             :                      error_suffix);
    1478             :       }
    1479       14037 :     };
    1480             : 
    1481        7957 :     Parallel::push_parallel_vector_data(_communicator, send_ids, check_ids);
    1482             :   }
    1483        8626 : }
    1484             : 
    1485             : void
    1486       15888 : RayTracingStudy::verifyUniqueRays(const std::vector<std::shared_ptr<Ray>>::const_iterator begin,
    1487             :                                   const std::vector<std::shared_ptr<Ray>>::const_iterator end,
    1488             :                                   const std::string & error_suffix)
    1489             : {
    1490             :   std::set<const Ray *> rays;
    1491     4054326 :   for (const std::shared_ptr<Ray> & ray : as_range(begin, end))
    1492     4038440 :     if (!rays.insert(ray.get()).second) // false if not inserted into rays
    1493           2 :       mooseError("Multiple shared_ptrs were found that point to the same Ray ",
    1494             :                  error_suffix,
    1495             :                  "\n\nOffending Ray:\n",
    1496           2 :                  ray->getInfo());
    1497       15886 : }
    1498             : 
    1499             : void
    1500     4037450 : RayTracingStudy::moveRayToBuffer(std::shared_ptr<Ray> & ray)
    1501             : {
    1502             :   mooseAssert(currentlyGenerating(), "Can only use while generating");
    1503             :   mooseAssert(ray, "Null ray");
    1504             :   mooseAssert(ray->shouldContinue(), "Ray is not continuing");
    1505             : 
    1506     4037450 :   _parallel_ray_study->moveWorkToBuffer(ray, /* tid = */ 0);
    1507     4037450 : }
    1508             : 
    1509             : void
    1510         282 : RayTracingStudy::moveRaysToBuffer(std::vector<std::shared_ptr<Ray>> & rays)
    1511             : {
    1512             :   mooseAssert(currentlyGenerating(), "Can only use while generating");
    1513             : #ifndef NDEBUG
    1514             :   for (const std::shared_ptr<Ray> & ray : rays)
    1515             :   {
    1516             :     mooseAssert(ray, "Null ray");
    1517             :     mooseAssert(ray->shouldContinue(), "Ray is not continuing");
    1518             :   }
    1519             : #endif
    1520             : 
    1521         282 :   _parallel_ray_study->moveWorkToBuffer(rays, /* tid = */ 0);
    1522         282 : }
    1523             : 
    1524             : void
    1525       82440 : RayTracingStudy::moveRayToBufferDuringTrace(std::shared_ptr<Ray> & ray,
    1526             :                                             const THREAD_ID tid,
    1527             :                                             const AcquireMoveDuringTraceKey &)
    1528             : {
    1529             :   mooseAssert(ray, "Null ray");
    1530             :   mooseAssert(currentlyPropagating(), "Can only use while tracing");
    1531             : 
    1532       82440 :   _parallel_ray_study->moveWorkToBuffer(ray, tid);
    1533       82440 : }
    1534             : 
    1535             : void
    1536        7563 : RayTracingStudy::reserveRayBuffer(const std::size_t size)
    1537             : {
    1538        7563 :   if (!currentlyGenerating())
    1539           2 :     mooseError("Can only reserve in Ray buffer during generateRays()");
    1540             : 
    1541        7561 :   _parallel_ray_study->reserveBuffer(size);
    1542        7561 : }
    1543             : 
    1544             : const Point &
    1545     7343592 : RayTracingStudy::getSideNormal(const Elem * elem, unsigned short side, const THREAD_ID tid)
    1546             : {
    1547             :   std::unordered_map<std::pair<const Elem *, unsigned short>, Point> & cache =
    1548     7343592 :       _threaded_cached_normals[tid];
    1549             : 
    1550             :   // See if we've already cached this side normal
    1551     7343592 :   const auto elem_side_pair = std::make_pair(elem, side);
    1552             :   const auto search = cache.find(elem_side_pair);
    1553             : 
    1554             :   // Haven't cached this side normal: compute it and then cache it
    1555     7343592 :   if (search == cache.end())
    1556             :   {
    1557     1553301 :     _threaded_fe_face[tid]->reinit(elem, side);
    1558             :     const auto & normal = _threaded_fe_face[tid]->get_normals()[0];
    1559             :     cache.emplace(elem_side_pair, normal);
    1560     1553301 :     return normal;
    1561             :   }
    1562             : 
    1563             :   // Have cached this side normal: simply return it
    1564     5790291 :   return search->second;
    1565             : }
    1566             : 
    1567             : bool
    1568        4055 : RayTracingStudy::sameLevelActiveElems() const
    1569             : {
    1570        4055 :   unsigned int min_level = std::numeric_limits<unsigned int>::max();
    1571        4055 :   unsigned int max_level = std::numeric_limits<unsigned int>::min();
    1572             : 
    1573      474807 :   for (const auto & elem : *_mesh.getActiveLocalElementRange())
    1574             :   {
    1575      470752 :     const auto level = elem->level();
    1576      470752 :     min_level = std::min(level, min_level);
    1577      470752 :     max_level = std::max(level, max_level);
    1578             :   }
    1579             : 
    1580        4055 :   _communicator.min(min_level);
    1581        4055 :   _communicator.max(max_level);
    1582             : 
    1583        4055 :   return min_level == max_level;
    1584             : }
    1585             : 
    1586             : Real
    1587     5855227 : RayTracingStudy::subdomainHmax(const SubdomainID subdomain_id) const
    1588             : {
    1589             :   const auto find = _subdomain_hmax.find(subdomain_id);
    1590     5855227 :   if (find == _subdomain_hmax.end())
    1591           2 :     mooseError("Subdomain ", subdomain_id, " not found in subdomain hmax map");
    1592     5855225 :   return find->second;
    1593             : }
    1594             : 
    1595             : bool
    1596       12485 : RayTracingStudy::isRectangularDomain() const
    1597             : {
    1598             :   Real bbox_volume = 1;
    1599       39980 :   for (unsigned int d = 0; d < _mesh.dimension(); ++d)
    1600       27495 :     bbox_volume *= std::abs(_b_box.max()(d) - _b_box.min()(d));
    1601             : 
    1602       12485 :   return MooseUtils::absoluteFuzzyEqual(bbox_volume, totalVolume(), TOLERANCE);
    1603             : }
    1604             : 
    1605             : void
    1606        3823 : RayTracingStudy::resetUniqueRayIDs()
    1607             : {
    1608             :   libmesh_parallel_only(comm());
    1609             : 
    1610             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
    1611             : 
    1612             :   mooseAssert(!currentlyGenerating() && !currentlyPropagating(),
    1613             :               "Cannot be reset during generation or propagation");
    1614             : 
    1615        8368 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
    1616        4545 :     _threaded_next_ray_id[tid] = (RayID)_pid * (RayID)libMesh::n_threads() + (RayID)tid;
    1617        3823 : }
    1618             : 
    1619             : RayID
    1620     4053853 : RayTracingStudy::generateUniqueRayID(const THREAD_ID tid)
    1621             : {
    1622             :   // Get the current ID to return
    1623     4053853 :   const auto id = _threaded_next_ray_id[tid];
    1624             : 
    1625             :   // Advance so that the next call has the correct ID
    1626     4053853 :   _threaded_next_ray_id[tid] += (RayID)n_processors() * (RayID)libMesh::n_threads();
    1627             : 
    1628     4053853 :   return id;
    1629             : }
    1630             : 
    1631             : void
    1632        3823 : RayTracingStudy::resetReplicatedRayIDs()
    1633             : {
    1634             :   libmesh_parallel_only(comm());
    1635             : 
    1636             :   Threads::spin_mutex::scoped_lock lock(_spin_mutex);
    1637             : 
    1638             :   mooseAssert(!currentlyGenerating() && !currentlyPropagating(),
    1639             :               "Cannot be reset during generation or propagation");
    1640             : 
    1641        3823 :   _replicated_next_ray_id = 0;
    1642        3823 : }
    1643             : 
    1644             : RayID
    1645        1106 : RayTracingStudy::generateReplicatedRayID()
    1646             : {
    1647        1106 :   return _replicated_next_ray_id++;
    1648             : }
    1649             : 
    1650             : bool
    1651     3501880 : RayTracingStudy::sideIsIncoming(const Elem * const elem,
    1652             :                                 const unsigned short side,
    1653             :                                 const Point & direction,
    1654             :                                 const THREAD_ID tid)
    1655             : {
    1656     3501880 :   const auto & normal = getSideNormal(elem, side, tid);
    1657             :   const auto dot = normal * direction;
    1658     3501880 :   return dot < TraceRayTools::TRACE_TOLERANCE;
    1659             : }
    1660             : 
    1661             : std::shared_ptr<Ray>
    1662     3955573 : RayTracingStudy::acquireRay()
    1663             : {
    1664             :   mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
    1665             : 
    1666     3955573 :   return _parallel_ray_study->acquireParallelData(
    1667             :       /* tid = */ 0,
    1668     7911146 :       this,
    1669     3955573 :       generateUniqueRayID(/* tid = */ 0),
    1670     7911146 :       rayDataSize(),
    1671     3955573 :       rayAuxDataSize(),
    1672     3955573 :       /* reset = */ true,
    1673     3955573 :       Ray::ConstructRayKey());
    1674             : }
    1675             : 
    1676             : std::shared_ptr<Ray>
    1677       15840 : RayTracingStudy::acquireUnsizedRay()
    1678             : {
    1679             :   mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
    1680             : 
    1681       15840 :   return _parallel_ray_study->acquireParallelData(/* tid = */ 0,
    1682       31680 :                                                   this,
    1683       15840 :                                                   generateUniqueRayID(/* tid = */ 0),
    1684       31680 :                                                   /* data_size = */ 0,
    1685       31680 :                                                   /* aux_data_size = */ 0,
    1686       15840 :                                                   /* reset = */ true,
    1687       15840 :                                                   Ray::ConstructRayKey());
    1688             : }
    1689             : 
    1690             : std::shared_ptr<Ray>
    1691        1106 : RayTracingStudy::acquireReplicatedRay()
    1692             : {
    1693             :   mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
    1694             :   libmesh_parallel_only(comm());
    1695             : 
    1696        1106 :   return _parallel_ray_study->acquireParallelData(
    1697             :       /* tid = */ 0,
    1698        2212 :       this,
    1699        1106 :       generateReplicatedRayID(),
    1700        2212 :       rayDataSize(),
    1701        1106 :       rayAuxDataSize(),
    1702        1106 :       /* reset = */ true,
    1703        1106 :       Ray::ConstructRayKey());
    1704             : }
    1705             : 
    1706             : std::shared_ptr<Ray>
    1707        2519 : RayTracingStudy::acquireRegisteredRay(const std::string & name)
    1708             : {
    1709             :   mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
    1710             : 
    1711             :   // Either register a Ray or get an already registered Ray id
    1712        2519 :   const RayID id = registerRay(name);
    1713             : 
    1714             :   // Acquire a Ray with the properly sized data initialized to zero
    1715        2517 :   return _parallel_ray_study->acquireParallelData(
    1716             :       /* tid = */ 0,
    1717        5034 :       this,
    1718             :       id,
    1719        5034 :       rayDataSize(),
    1720        2517 :       rayAuxDataSize(),
    1721        2517 :       /* reset = */ true,
    1722        2517 :       Ray::ConstructRayKey());
    1723             : }
    1724             : 
    1725             : std::shared_ptr<Ray>
    1726     4035627 : RayTracingStudy::acquireCopiedRay(const Ray & ray)
    1727             : {
    1728             :   mooseAssert(currentlyGenerating(), "Can only use during generateRays()");
    1729     4035627 :   return _parallel_ray_study->acquireParallelData(
    1730     8071254 :       /* tid = */ 0, &ray, Ray::ConstructRayKey());
    1731             : }
    1732             : 
    1733             : std::shared_ptr<Ray>
    1734       82440 : RayTracingStudy::acquireRayDuringTrace(const THREAD_ID tid, const AcquireMoveDuringTraceKey &)
    1735             : {
    1736             :   mooseAssert(currentlyPropagating(), "Can only use during propagation");
    1737       82440 :   return _parallel_ray_study->acquireParallelData(tid,
    1738      164880 :                                                   this,
    1739       82440 :                                                   generateUniqueRayID(tid),
    1740      164880 :                                                   rayDataSize(),
    1741       82440 :                                                   rayAuxDataSize(),
    1742       82440 :                                                   /* reset = */ true,
    1743       82440 :                                                   Ray::ConstructRayKey());
    1744             : }

Generated by: LCOV version 1.14