LCOV - code coverage report
Current view: top level - src/raytracing - ClaimRays.C (source / functions) Hit Total Coverage
Test: idaholab/moose ray_tracing: #30155 (42ebdb) with base 5e2432 Lines: 117 120 97.5 %
Date: 2025-07-02 02:06:27 Functions: 13 13 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 "ClaimRays.h"
      11             : 
      12             : // Local includes
      13             : #include "RayTracingStudy.h"
      14             : 
      15             : // libMesh includes
      16             : #include "libmesh/elem.h"
      17             : #include "libmesh/parallel_algebra.h"
      18             : #include "libmesh/parallel_sync.h"
      19             : #include "libmesh/enum_point_locator_type.h"
      20             : #include "libmesh/mesh_tools.h"
      21             : 
      22             : using namespace libMesh;
      23             : 
      24        3424 : ClaimRays::ClaimRays(RayTracingStudy & study,
      25             :                      const std::vector<std::shared_ptr<Ray>> & rays,
      26             :                      std::vector<std::shared_ptr<Ray>> & local_rays,
      27        3424 :                      const bool do_exchange)
      28             :   : ParallelObject(study.comm()),
      29             :     MeshChangedInterface(study.parameters()),
      30        3424 :     _mesh(study.mesh()),
      31        3424 :     _pid(comm().rank()),
      32        3424 :     _do_exchange(do_exchange),
      33        3424 :     _study(study),
      34        3424 :     _parallel_study(*_study.parallelStudy()),
      35        3424 :     _rays(rays),
      36        3424 :     _local_rays(local_rays),
      37        3424 :     _needs_init(true)
      38             : {
      39        3424 : }
      40             : 
      41             : void
      42         841 : ClaimRays::claim()
      43             : {
      44         841 :   if (_needs_init)
      45             :   {
      46         841 :     init();
      47         841 :     _needs_init = false;
      48             :   }
      49             : 
      50         841 :   preClaim();
      51             : 
      52             :   // Clear these as we're about to fill
      53         841 :   _local_rays.clear();
      54             : 
      55             :   // Grab the point locator
      56        1682 :   _point_locator = PointLocatorBase::build(TREE_LOCAL_ELEMENTS, _mesh.getMesh());
      57         841 :   _point_locator->enable_out_of_mesh_mode();
      58             : 
      59             :   // Exchange: filter Rays into processors that _may_ claim them
      60             :   std::unordered_map<processor_id_type, std::vector<std::shared_ptr<Ray>>> rays_to_send;
      61         841 :   if (_do_exchange)
      62         576 :     for (processor_id_type pid = 0; pid < comm().size(); ++pid)
      63         368 :       if (_pid != pid)
      64             :       {
      65             :         const BoundingBox & pid_bbox = inflatedBoundingBox(pid);
      66       26080 :         for (auto & ray : _rays)
      67       25920 :           if (pid_bbox.contains_point(ray->currentPoint()))
      68       14468 :             rays_to_send[pid].push_back(ray);
      69             :       }
      70             : 
      71             :   // Functor for possibly claiming a vector of Rays
      72             :   auto claim_functor =
      73         991 :       [&](processor_id_type /* pid */, const std::vector<std::shared_ptr<Ray>> & rays)
      74             :   {
      75       60254 :     for (auto & ray : rays)
      76       59263 :       possiblyClaim(ray);
      77        1832 :   };
      78             : 
      79             :   // Send the relevant Rays to everyone and then attempt to claim the ones that we receive
      80         841 :   if (_do_exchange)
      81         208 :     Parallel::push_parallel_packed_range(comm(), rays_to_send, &_parallel_study, claim_functor);
      82             : 
      83             :   // Attempt to claim the locally generated rays in _rays
      84         841 :   claim_functor(_pid, _rays);
      85             : 
      86             :   // Verify the claiming if the study so desires
      87         841 :   if (_study.verifyRays())
      88         841 :     verifyClaiming();
      89             : 
      90         841 :   postClaim();
      91         841 : }
      92             : 
      93             : void
      94       59263 : ClaimRays::possiblyClaim(const std::shared_ptr<Ray> & ray)
      95             : {
      96       59263 :   prePossiblyClaimRay(ray);
      97             : 
      98             :   const auto elem =
      99       59263 :       claimPoint(ray->currentPoint(), getID(ray), (*_point_locator)(ray->currentPoint()));
     100       59263 :   if (elem)
     101             :   {
     102       43441 :     _local_rays.push_back(ray);
     103       43441 :     postClaimRay(_local_rays.back(), elem);
     104             :   }
     105       59263 : }
     106             : 
     107             : const Elem *
     108       59263 : ClaimRays::claimPoint(const Point & point, const RayID id, const Elem * elem)
     109             : {
     110       59263 :   if (elem)
     111             :   {
     112             :     // Looking for smallest (even ID Ray) or largest (odd ID Ray) elem id
     113       45257 :     const bool smallest = id % 2 == 0;
     114             : 
     115             :     // Start with the element we found, as it is a valid candidate
     116             :     const Elem * extremum_elem = elem;
     117             : 
     118             :     // All point neighbors for this element
     119             :     mooseAssert(_elem_point_neighbors.count(elem->id()), "Not in point neighbor map");
     120       45257 :     const auto & neighbors = _elem_point_neighbors.at(elem->id());
     121             : 
     122             :     // Find element that matches the extremum criteria
     123      367961 :     for (const auto & neighbor : neighbors)
     124             :     {
     125             :       mooseAssert(neighbor->active(), "Inactive neighbor");
     126             : 
     127      322704 :       if ((smallest && neighbor->id() < extremum_elem->id()) || // satisfies
     128      123934 :           (!smallest && neighbor->id() > extremum_elem->id()))  // ...one of the id checks
     129      130675 :         if (neighbor->contains_point(point))                    // and also contains the point
     130       18095 :           extremum_elem = neighbor;
     131             :     }
     132             : 
     133             :     // Claim the object if we own the extremum elem
     134       45257 :     if (extremum_elem->processor_id() == _pid)
     135             :     {
     136             :       mooseAssert(extremum_elem->active(), "Inactive element");
     137             :       return extremum_elem;
     138             :     }
     139             :   }
     140             : 
     141             :   return nullptr;
     142             : }
     143             : 
     144             : void
     145       43441 : ClaimRays::postClaimRay(std::shared_ptr<Ray> & ray, const Elem * elem)
     146             : {
     147             :   mooseAssert(_mesh.queryElemPtr(elem->id()) == elem, "Mesh doesn't contain elem");
     148             :   mooseAssert(elem->active(), "Inactive element");
     149             : 
     150             :   // If the incoming side is set and is not incoming, or if it is not set at all, see
     151             :   // if we can find an incoming side that is valid.
     152             :   auto starting_incoming_side = RayTracingCommon::invalid_side;
     153       43441 :   if (!(!ray->invalidCurrentIncomingSide() &&
     154           0 :         _study.elemSide(*elem, ray->currentIncomingSide()).contains_point(ray->currentPoint()) &&
     155           0 :         _study.sideIsIncoming(elem, ray->currentIncomingSide(), ray->direction(), /* tid = */ 0)))
     156       98729 :     for (const auto s : elem->side_index_range())
     157      144988 :       if (_study.elemSide(*elem, s).contains_point(ray->currentPoint()) &&
     158       46614 :           _study.sideIsIncoming(elem, s, ray->direction(), /* tid = */ 0))
     159             :       {
     160             :         starting_incoming_side = s;
     161             :         break;
     162             :       }
     163             : 
     164       43441 :   ray->setStart(ray->currentPoint(), elem, starting_incoming_side);
     165       43441 : }
     166             : 
     167             : void
     168         841 : ClaimRays::init()
     169             : {
     170         841 :   buildBoundingBoxes();
     171         841 :   buildPointNeighbors();
     172         841 : }
     173             : 
     174             : void
     175         208 : ClaimRays::meshChanged()
     176             : {
     177         208 :   _needs_init = true;
     178         208 : }
     179             : 
     180             : void
     181         841 : ClaimRays::buildBoundingBoxes()
     182             : {
     183             :   // Local bounding box
     184         841 :   const auto bbox = MeshTools::create_local_bounding_box(_mesh.getMesh());
     185             : 
     186             :   // Gather the bounding boxes of all processors
     187         841 :   std::vector<std::pair<Point, Point>> bb_points = {static_cast<std::pair<Point, Point>>(bbox)};
     188         841 :   comm().allgather(bb_points, true);
     189             : 
     190             :   // Inflate the local bboxes by a bit and store
     191         841 :   _inflated_bboxes.resize(comm().size());
     192        2362 :   for (processor_id_type pid = 0; pid < comm().size(); ++pid)
     193             :   {
     194        1521 :     BoundingBox pid_bbox = static_cast<BoundingBox>(bb_points[pid]);
     195        1521 :     pid_bbox.scale(0.01);
     196             :     _inflated_bboxes[pid] = pid_bbox;
     197             :   }
     198         841 : }
     199             : 
     200             : void
     201         841 : ClaimRays::buildPointNeighbors()
     202             : {
     203             :   _elem_point_neighbors.clear();
     204         841 :   const auto & node_to_elem_map = _mesh.nodeToElemMap();
     205             : 
     206      132194 :   for (const auto & elem : _mesh.getMesh().active_element_ptr_range())
     207             :   {
     208       65256 :     auto & fill = _elem_point_neighbors[elem->id()];
     209      368530 :     for (unsigned int v = 0; v < elem->n_vertices(); ++v)
     210             :     {
     211      303274 :       const auto & node = elem->node_ptr(v);
     212     4429300 :       for (const auto & neighbor_id : node_to_elem_map.at(node->id()))
     213             :       {
     214     4126026 :         if (neighbor_id == elem->id())
     215      303274 :           continue;
     216             : 
     217     3822752 :         const auto & neighbor = _mesh.elemPtr(neighbor_id);
     218     3822752 :         if (std::count(fill.begin(), fill.end(), neighbor) == 0)
     219     2857192 :           fill.emplace_back(neighbor);
     220             :       }
     221             :     }
     222         841 :   }
     223         841 : }
     224             : 
     225             : void
     226         841 : ClaimRays::verifyClaiming()
     227             : {
     228             :   // NOTE for all of the following: we use char here in place of bool.
     229             :   // This is because bool is not instantiated as a StandardType in
     230             :   // TIMPI due to the fun of std::vector<bool>
     231             : 
     232             :   // Map from Ray ID -> whether or not it was generated (false) or
     233             :   // claimed/possibly also generated (true)
     234             :   std::map<RayID, char> local_map;
     235             :   auto add_to_local_map =
     236        1682 :       [this, &local_map](const std::vector<std::shared_ptr<Ray>> & rays, const bool claimed_rays)
     237             :   {
     238       89918 :     for (const auto & ray : rays)
     239             :     {
     240       88236 :       const auto id = getID(ray);
     241             : 
     242             :       // Try to insert into the map
     243       88236 :       auto emplace_pair = local_map.emplace(id, claimed_rays);
     244             : 
     245             :       // If it already exists but has not been claimed yet, set it to being claimed
     246       88236 :       if (!emplace_pair.second && claimed_rays)
     247             :       {
     248             :         mooseAssert(!emplace_pair.first->second,
     249             :                     "Ray was claimed more than once on a single processor");
     250       33524 :         emplace_pair.first->second = true;
     251             :       }
     252             :     }
     253        2523 :   };
     254             : 
     255             :   // Build the local_map
     256         841 :   add_to_local_map(_rays, false);
     257         841 :   add_to_local_map(_local_rays, true);
     258             : 
     259             :   // Build the structure to send the local generation/claiming information to rank 0
     260             :   std::map<processor_id_type, std::vector<std::pair<RayID, char>>> send_info;
     261         841 :   if (local_map.size())
     262             :     send_info.emplace(std::piecewise_construct,
     263         841 :                       std::forward_as_tuple(0),
     264         841 :                       std::forward_as_tuple(local_map.begin(), local_map.end()));
     265             : 
     266             :   // The mapping (filled on rank 0) from Ray ID -> (processor id, claiming status)
     267             :   std::map<RayID, std::vector<std::pair<processor_id_type, char>>> global_map;
     268             : 
     269             :   // Functor for receiving the generation/claiming information
     270         841 :   auto receive_functor = [&global_map](processor_id_type pid,
     271       54712 :                                        const std::vector<std::pair<RayID, char>> & id_claimed_pairs)
     272             :   {
     273       55553 :     for (const auto & id_claimed_pair : id_claimed_pairs)
     274       54712 :       global_map[id_claimed_pair.first].emplace_back(pid, id_claimed_pair.second);
     275         841 :   };
     276             : 
     277             :   // Send claiming information to rank 0
     278         841 :   Parallel::push_parallel_vector_data(comm(), send_info, receive_functor);
     279             : 
     280             :   // Rank 0 will make sure everything looks good
     281         841 :   if (_pid == 0)
     282       43962 :     for (const auto & id_pairs_pair : global_map)
     283             :     {
     284       43441 :       const RayID id = id_pairs_pair.first;
     285             :       const std::vector<std::pair<processor_id_type, char>> & pid_claimed_pairs =
     286             :           id_pairs_pair.second;
     287             : 
     288             :       std::vector<processor_id_type> claimed_pids;
     289       98153 :       for (const auto & pid_claimed_pair : pid_claimed_pairs)
     290       54712 :         if (pid_claimed_pair.second)
     291       43441 :           claimed_pids.push_back(pid_claimed_pair.first);
     292             : 
     293       43441 :       if (claimed_pids.size() == 0)
     294           0 :         _study.mooseError("Failed to claim the Ray with ID ", id);
     295             :       mooseAssert(claimed_pids.size() == 1, "Ray was claimed on multiple processors");
     296             :     }
     297         841 : }

Generated by: LCOV version 1.14