https://mooseframework.inl.gov
Loading...
Searching...
No Matches
RayTracingMeshOutput.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10// TODO: remove ignore warnings once std::tuple<> is instantiated as a StandardType
11// Using push_parallel_vector_data with std::tuple<> leads to a -Wextra error
12// https://github.com/libMesh/TIMPI/issues/52
13#include "libmesh/ignore_warnings.h"
15#include "libmesh/restore_warnings.h"
16
17// Local Includes
18#include "RayTracingStudy.h"
19#include "TraceData.h"
20
21// libMesh includes
22#include "libmesh/mesh_tools.h"
23#include "libmesh/parallel_algebra.h"
24#include "libmesh/parallel_sync.h"
25#include "libmesh/remote_elem.h"
26
29{
31
32 params.addRequiredParam<UserObjectName>("study", "The RayTracingStudy to get the segments from");
33
34 params.addParam<bool>("output_data", false, "Whether or not to also output the Ray's data");
35 params.addParam<std::vector<std::string>>("output_data_names",
36 "The names of specific data to output");
37 params.addParam<bool>("output_data_nodal",
38 false,
39 "Whether or not to output the Ray's data in a nodal sense, in which the "
40 "data is interpolated linearly across segments");
41
42 params.addParam<bool>(
43 "output_aux_data", false, "Whether or not to also output the Ray's aux data");
44 params.addParam<std::vector<std::string>>("output_aux_data_names",
45 "The names of specific aux data to output");
46
47 MultiMooseEnum props("ray_id intersections pid processor_crossings trajectory_changes",
48 "ray_id intersections");
49 params.addParam<MultiMooseEnum>("output_properties", props, "Which Ray properties to output");
50
51 return params;
52}
53
55 : FileOutput(params),
57 _study(getUserObject<RayTracingStudy>("study")),
58 _output_data(getParam<bool>("output_data")),
59 _output_data_names(isParamValid("output_data_names")
60 ? &getParam<std::vector<std::string>>("output_data_names")
61 : nullptr),
62 _output_data_nodal(getParam<bool>("output_data_nodal")),
63 _output_aux_data(getParam<bool>("output_aux_data")),
64 _output_aux_data_names(isParamValid("output_aux_data_names")
65 ? &getParam<std::vector<std::string>>("output_aux_data_names")
66 : nullptr),
67 _ray_id_var(invalid_uint),
68 _intersections_var(invalid_uint),
69 _pid_var(invalid_uint),
70 _processor_crossings_var(invalid_uint),
71 _trajectory_changes_var(invalid_uint),
72 _segmented_rays(false)
73{
75 mooseError("In order to output Ray data in output '",
76 name(),
77 "', the RayTracingStudy '",
78 _study.name(),
79 "' must set data_on_cache_traces = true");
81 mooseError("In order to output Ray aux data in output '",
82 name(),
83 "', the RayTracingStudy '",
84 _study.name(),
85 "' must set aux_data_on_cache_traces = true");
87 paramError("output_data_nodal",
88 "Cannot be used unless there is data to output; in addition set either "
89 "'output_data' or 'output_data_names");
91 paramError("output_data_nodal", "Not supported when study segments_on_cache_traces = false");
93 paramError("output_data", "Cannot be used in addition to 'output_data_names'; choose one");
95 paramError("output_aux_data",
96 "Cannot be used in addition to 'output_aux_data_names'; choose one");
97}
98
99std::string
101{
102 // Append the file extension on the base file name
103 std::ostringstream output;
105
106 // Add the _000x extension to the file
107 if (_file_num > 0)
108 output << "-s" << std::setw(_padding) << std::setprecision(0) << std::setfill('0') << std::right
109 << _file_num;
110
111 // Return the filename
112 return output.str();
113}
114
115void
117{
118 // Do we even have any traces?
119 auto num_segments = _study.getCachedTraces().size();
120 _communicator.sum(num_segments);
121 if (!num_segments)
122 mooseError("No cached trace segments were found in the study '", _study.name(), "'.");
123
124 // Build the _inflated_neighbor_bboxes
126
127 // Build the _segment_mesh
129
130 // Setup the system to store the Ray field data
132
133 // Fill the field data
134 fillFields();
135
136 // And output
137 outputMesh();
138
139 // Done with these
140 // We don't necessarily need to create a new mesh every time, but it's easier than
141 // checking if the Rays have changed from last time we built a mesh
142 _es = nullptr;
143 _sys = nullptr;
144 _segment_mesh = nullptr;
145}
146
147void
149{
150 TIME_SECTION("buildIDMap", 3, "Building RayTracing ID Map");
151
152 // Build the maximum number of nodes required to represent each one of my local Rays
153 std::map<RayID, dof_id_type> local_ray_needed_nodes;
154 for (const auto & trace_data : _study.getCachedTraces())
155 {
156 const auto find = local_ray_needed_nodes.find(trace_data._ray_id);
157 if (find == local_ray_needed_nodes.end())
158 local_ray_needed_nodes[trace_data._ray_id] = neededNodes(trace_data);
159 else
160 {
161 auto & value = find->second;
162 value = std::max(value, neededNodes(trace_data));
163 }
164 }
165
166 // Fill all of my local Ray maxima to be sent to processor 0
167 std::map<processor_id_type, std::vector<std::pair<RayID, dof_id_type>>> send_needed_nodes;
168 if (local_ray_needed_nodes.size())
169 {
170 auto & root_entry = send_needed_nodes[0];
171 for (const auto & id_nodes_pair : local_ray_needed_nodes)
172 root_entry.emplace_back(id_nodes_pair);
173 }
174
175 // The global map of ray -> required nodes needed to be filled on processor 0
176 std::map<RayID, dof_id_type> global_needed_nodes;
177 // Keep track of what Ray IDs we received from what processors so we know who to send back to
178 std::unordered_map<processor_id_type, std::set<RayID>> pid_received_ids;
179 // Take the required nodes for each Ray and determine on processor 0 the global maximum
180 // nodes needed to represent each Ray
181 const auto append_global_max =
182 [&global_needed_nodes, &pid_received_ids](
183 processor_id_type pid, const std::vector<std::pair<RayID, dof_id_type>> & pairs)
184 {
185 auto & pid_received_entry = pid_received_ids[pid];
186 for (const auto & id_max_pair : pairs)
187 {
188 const RayID ray_id = id_max_pair.first;
189 const dof_id_type max = id_max_pair.second;
190
191 const auto find = global_needed_nodes.find(ray_id);
192 if (find == global_needed_nodes.end())
193 global_needed_nodes.emplace(ray_id, max);
194 else
195 {
196 auto & current_max = find->second;
197 current_max = std::max(current_max, max);
198 }
199
200 pid_received_entry.insert(ray_id);
201 }
202 };
203 Parallel::push_parallel_vector_data(comm(), send_needed_nodes, append_global_max);
204
205 // Decide on the starting representative starting node and elem ID for each Ray
206 std::map<RayID, std::pair<dof_id_type, dof_id_type>> global_ids;
207 dof_id_type current_node_id = 0;
208 dof_id_type current_elem_id = 0;
209 for (auto & pair : global_needed_nodes)
210 {
211 const RayID ray_id = pair.first;
212 const dof_id_type max_nodes = pair.second;
213
214 global_ids.emplace(ray_id, std::make_pair(current_node_id, current_elem_id));
215
216 current_node_id += max_nodes;
217 if (max_nodes > 1)
218 current_elem_id += max_nodes - 1;
219 else // stationary
220 current_elem_id += 1;
221 }
222
223 // Share the max node IDs so that we have a starting point for unique IDs for elems
224 _max_node_id = current_node_id;
226
227 // Fill the starting ID information to each processor that needs it from processor 0
228 std::unordered_map<processor_id_type, std::vector<std::tuple<RayID, dof_id_type, dof_id_type>>>
229 send_ids;
230 for (auto & pid_ids_pair : pid_received_ids)
231 {
232 const processor_id_type pid = pid_ids_pair.first;
233 const std::set<RayID> & ray_ids = pid_ids_pair.second;
234
235 auto & pid_send = send_ids[pid];
236 for (const RayID ray_id : ray_ids)
237 {
238 const auto & global_entry = global_ids.at(ray_id);
239 const dof_id_type node_id = global_entry.first;
240 const dof_id_type elem_id = global_entry.second;
241 pid_send.emplace_back(ray_id, node_id, elem_id);
242 }
243 }
244
245 // Take the starting ID information from processor 0 and store it locally
246 const auto append_ids =
247 [&](processor_id_type,
248 const std::vector<std::tuple<RayID, dof_id_type, dof_id_type>> & tuples)
249 {
250 for (const auto & tuple : tuples)
251 {
252 const RayID ray_id = std::get<0>(tuple);
253 const dof_id_type node_id = std::get<1>(tuple);
254 const dof_id_type elem_id = std::get<2>(tuple);
255 _ray_starting_id_map.emplace(ray_id, std::make_pair(node_id, elem_id));
256 }
257 };
258
259 _ray_starting_id_map.clear();
260 Parallel::push_parallel_vector_data(comm(), send_ids, append_ids);
261}
262
263void
265{
266 TIME_SECTION("buildSegmentMesh", 3, "Building RayTracing Mesh Output");
267
268 _segmented_rays = false;
269
270 // Tally nodes and elems for the local mesh ahead of time so we can reserve
271 // Each segment requires an element, and we need one more node than elems
272 dof_id_type num_nodes = 0;
273 dof_id_type num_elems = 0;
274 for (const auto & entry : _study.getCachedTraces())
275 {
276 const auto num_segments = entry.numSegments();
277 num_nodes += num_segments + 1;
278 if (num_segments >= 1)
279 {
280 _segmented_rays = true;
281 num_elems += num_segments;
282 }
283 else if (entry.stationary())
284 num_elems += 1;
285 }
286
288
289 // Build the segment mesh
290 mooseAssert(!_segment_mesh, "Not cleared");
291 if (_communicator.size() == 1)
292 _segment_mesh = std::make_unique<ReplicatedMesh>(_communicator, _mesh_ptr->dimension());
293 else
294 {
295 _segment_mesh = std::make_unique<DistributedMesh>(_communicator, _mesh_ptr->dimension());
296 _segment_mesh->set_distributed();
297 }
298 // We set neighbor links
299 _segment_mesh->allow_find_neighbors(false);
300 // Don't renumber so that we can remain consistent between processor counts
301 _segment_mesh->allow_renumbering(false);
302 // As we're building segments for just this partiton, we're partitioning on our own
303 _segment_mesh->skip_partitioning(true);
304 // Reserve nodes and elems
305 _segment_mesh->reserve_nodes(num_nodes);
306 _segment_mesh->reserve_elem(num_elems);
307
308 buildIDMap();
309
310 // Decide on a starting ID for each processor
311 // Build a mesh segment for each local Ray segment
312 // Each one of these objects represents a single Ray's path through this processor
313 for (const auto & trace_data : _study.getCachedTraces())
314 {
315 // If we have no segments, this is a trace that skimmed the corner of a processor boundary and
316 // didn't contribute anything
317 if (trace_data.numSegments() == 0 && !trace_data.stationary())
318 continue;
319
320 dof_id_type node_id, elem_id;
321 startingIDs(trace_data, node_id, elem_id);
322
323 // Add the start point
324 mooseAssert(!_segment_mesh->query_node_ptr(node_id), "Node already exists");
325 Node * last_node =
326 _segment_mesh->add_point(trace_data._point_data[0]._point, node_id, processor_id());
327 last_node->set_unique_id(node_id++);
328
329 // Stationary, add a NodeElem
330 if (trace_data.stationary())
331 {
332 mooseAssert(!_segment_mesh->query_elem_ptr(elem_id), "Elem already exists");
333 auto elem = _segment_mesh->add_elem(Elem::build_with_id(NODEELEM, elem_id));
334 elem->processor_id(processor_id());
335 elem->set_unique_id(_max_node_id + elem_id++);
336 elem->set_node(0, last_node);
337 }
338 // Not stationary; add a point and element for each segment
339 else
340 {
341 Elem * last_elem = nullptr;
342
343 for (std::size_t i = 1; i < trace_data._point_data.size(); ++i)
344 {
345 const auto & point = trace_data._point_data[i]._point;
346
347 // Add next point on the trace
348 mooseAssert(!_segment_mesh->query_node_ptr(node_id), "Node already exists");
349 Node * node = _segment_mesh->add_point(point, node_id, processor_id());
350 node->set_unique_id(node_id++);
351
352 // Build a segment from this point to the last
353 mooseAssert(!_segment_mesh->query_elem_ptr(elem_id), "Elem already exists");
354 Elem * elem = _segment_mesh->add_elem(Elem::build_with_id(EDGE2, elem_id));
355 elem->processor_id(processor_id());
356 elem->set_unique_id(_max_node_id + elem_id++);
357 elem->set_node(0, last_node);
358 elem->set_node(1, node);
359
360 // Set neighbor links
361 if (last_elem)
362 {
363 elem->set_neighbor(0, last_elem);
364 last_elem->set_neighbor(1, elem);
365 }
366
367 last_elem = elem;
368 last_node = node;
369 }
370 }
371 }
372
373 // If the mesh is replicated, everything that follows is unnecessary. Prepare and be done.
374 if (_segment_mesh->is_replicated())
375 {
376 _segment_mesh->prepare_for_use();
377 return;
378 }
379
380 // Find the Nodes on processor boundaries that we need to decide on owners for. Also set up
381 // remote_elem links for neighbors we clearly don't have. We don't build the neighbor maps at
382 // all, so all we have are nullptr and remote_elem. We can only do all of this after the mesh is
383 // built because the mesh isn't necessarily built in the order Rays are traced
384 std::vector<Node *> need_node_owners;
385 for (const auto & trace_data : _study.getCachedTraces())
386 {
387 const auto num_segments = trace_data.numSegments();
388
389 if (num_segments == 0)
390 continue;
391
392 dof_id_type start_node_id, start_elem_id;
393 startingIDs(trace_data, start_node_id, start_elem_id);
394
395 // Another part of the trace for this Ray happened before this one
396 if ((_study.segmentsOnCacheTraces() ? trace_data._intersections
397 : (unsigned long int)(trace_data._processor_crossings +
398 trace_data._trajectory_changes)) != 0)
399 {
400 // The element before start_elem (may be nullptr, meaning it didn't trace on this proc)
401 Elem * previous_elem = _segment_mesh->query_elem_ptr(start_elem_id - 1);
402
403 // We don't have the previous element segment, so it exists on another processor
404 // Set the remote_elem and mark that we need to find out who owns the first node on this
405 // trace
406 if (!previous_elem)
407 {
408 Elem * start_elem = _segment_mesh->elem_ptr(start_elem_id);
409 start_elem->set_neighbor(0, const_cast<RemoteElem *>(remote_elem));
410 start_elem->node_ptr(0)->invalidate_processor_id();
411 need_node_owners.push_back(start_elem->node_ptr(0));
412 }
413 }
414
415 // If you have multiple sets of segments on a single processor, it is possible
416 // to have part of the trace on another processor within the segments we know about.
417 // In this case, we have to do some magic to the neighbor links so that the mesh
418 // doesn't fail in assertions.
419 if (!trace_data._last)
420 {
421 // The element after end_elem (may be nullptr, meaning it didn't happen on this proc)
422 Elem * next_elem = _segment_mesh->query_elem_ptr(start_elem_id + num_segments);
423
424 // We have the next element from another trace, set neighbors as we own both
425 if (!next_elem)
426 {
427 Elem * end_elem = _segment_mesh->elem_ptr(start_elem_id + num_segments - 1);
428 end_elem->set_neighbor(1, const_cast<RemoteElem *>(remote_elem));
429 end_elem->node_ptr(1)->invalidate_processor_id();
430 need_node_owners.push_back(end_elem->node_ptr(1));
431 }
432 }
433 }
434
435 // Sort through the neighboring bounding boxes and prepare requests to each processor that /may/
436 // also have one of the nodes that we need to decide on an owner for
437 std::unordered_map<processor_id_type, std::vector<dof_id_type>> need_node_owners_sends;
438 for (const Node * node : need_node_owners)
439 for (const auto & neighbor_pid_bbox_pair : _inflated_neighbor_bboxes)
440 {
441 const auto neighbor_pid = neighbor_pid_bbox_pair.first;
442 const auto & neighbor_bbox = neighbor_pid_bbox_pair.second;
443 if (neighbor_bbox.contains_point(*node))
444 need_node_owners_sends[neighbor_pid].push_back(node->id());
445 }
446
447 // Functor that takes in a set of incoming node IDs from a processor and decides on an owner.
448 // For every node that we need an owner for, this should be called for said node on both
449 // processors. Therefore, just pick the minimum processor ID as the owner. This should never
450 // happen more than once for a single node because we're building 1D elems and we can only ever
451 // have two procs that touch a node.
452 std::unordered_map<processor_id_type, std::vector<dof_id_type>> confirm_node_owners_sends;
453 auto decide_node_owners_functor =
454 [this, &confirm_node_owners_sends](processor_id_type pid,
455 const std::vector<dof_id_type> & incoming_node_ids)
456 {
457 auto & confirm_pid = confirm_node_owners_sends[pid];
458 for (const auto & node_id : incoming_node_ids)
459 {
460 Node * node = _segment_mesh->query_node_ptr(node_id);
461 if (node)
462 {
463 mooseAssert(!node->valid_processor_id(), "Should be invalid");
464 node->processor_id() = std::min(pid, processor_id());
465 confirm_pid.push_back(node_id);
466 }
467 }
468 };
469
470 // Ship the nodes that need owners for and pick an owner when we receive
471 Parallel::push_parallel_vector_data(
472 _communicator, need_node_owners_sends, decide_node_owners_functor);
473
474 // At this point, any nodes that we actually need ghosts for should have their processor_ids
475 // satisfied. Anything that is left isn't actually ghosted and is ours. Therefore, set
476 // everything left that is invalid to be owned by this proc.
477 for (Node * node : need_node_owners)
478 if (!node->valid_processor_id())
479 node->processor_id() = processor_id();
480
481 // We're done!
482 _segment_mesh->prepare_for_use();
483}
484
485void
487{
488 TIME_SECTION("setupEquationSystem", 3, "Setting Up Ray Tracing MeshOutput Equation System");
489
490 _es = std::make_unique<EquationSystems>(*_segment_mesh);
491 _sys = &_es->add_system<libMesh::ExplicitSystem>("sys");
493 _aux_data_vars.clear();
494
495 // Add variables for the basic properties if enabled
496 _ray_id_var = invalid_uint;
497 _intersections_var = invalid_uint;
498 _pid_var = invalid_uint;
499 _processor_crossings_var = invalid_uint;
500 _trajectory_changes_var = invalid_uint;
501 for (auto & prop : _pars.get<MultiMooseEnum>("output_properties"))
502 switch (prop)
503 {
504 case 0: // ray_id (stationary and segments)
505 _ray_id_var = _sys->add_variable("ray_id", CONSTANT, MONOMIAL);
506 break;
507 case 1: // intersections (segments only)
508 if (_segmented_rays)
509 _intersections_var = _sys->add_variable("intersections", CONSTANT, MONOMIAL);
510 break;
511 case 2: // pid (stationary and segments)
512 _pid_var = _sys->add_variable("pid", CONSTANT, MONOMIAL);
513 break;
514 case 3: // processor_crossings (segments only)
515 if (_segmented_rays)
516 _processor_crossings_var = _sys->add_variable("processor_crossings", CONSTANT, MONOMIAL);
517 break;
518 case 4: // trajectory_changes (segments only)
519 if (_segmented_rays)
520 _trajectory_changes_var = _sys->add_variable("trajectory_changes", CONSTANT, MONOMIAL);
521 break;
522 default:
523 mooseError("Invalid property");
524 }
525
526 const auto get_data_vars = [this](const bool aux)
527 {
528 // The data index -> variable result
529 std::vector<std::pair<RayDataIndex, unsigned int>> vars;
530
531 const auto from_names =
534
535 // Nothing to output
536 if (!from_names)
537 return vars;
538
539 const auto output_data_nodal = aux ? false : _output_data_nodal;
540
541 for (const auto & name : *from_names)
542 {
543 const auto data_index =
545 if (data_index == Ray::INVALID_RAY_DATA_INDEX)
546 {
547 const std::string names_param = aux ? "output_aux_data_names" : "output_data_names";
548 const std::string data_prefix = aux ? "aux " : "";
549 paramError(names_param, "The ray ", data_prefix, "data '", name, "' is not registered");
550 }
551
552 const std::string var_prefix = aux ? "aux_" : "";
553 if (output_data_nodal)
554 _sys->add_variable(var_prefix + name, FIRST, LAGRANGE);
555 else
556 _sys->add_variable(var_prefix + name, CONSTANT, MONOMIAL);
557 const auto var_num = _sys->variable_number(var_prefix + name);
558 vars.emplace_back(data_index, var_num);
559 }
560
561 return vars;
562 };
563
564 _data_vars = get_data_vars(false);
565 _aux_data_vars = get_data_vars(true);
566
567 // All done
568 _es->init();
569}
570
571void
573{
574 TIME_SECTION("fillFields", 3, "Filling RayTracing MeshOutput Fields");
575
576 // Helper for setting the solution (if enabled)
577 const auto set_solution =
578 [this](const DofObject * const dof, const unsigned int var, const Real value)
579 {
580 mooseAssert(dof, "Nullptr dof");
581 if (var != invalid_uint)
582 {
583 const auto dof_number = dof->dof_number(_sys->number(), var, 0);
584 _sys->solution->set(dof_number, value);
585 }
586 };
587
588 // Helper for filling data and aux data (if enabled)
589 const auto fill_data =
590 [&set_solution](const DofObject * const dof, const auto & vars, const auto & data)
591 {
592 mooseAssert(dof, "Nullptr dof");
593 for (const auto & [data_index, var_num] : vars)
594 set_solution(dof, var_num, data[data_index]);
595 };
596
597 for (const auto & trace_data : _study.getCachedTraces())
598 {
599 auto intersection = trace_data._intersections;
600
601 // No segments and not stationary; means this ray bounced off
602 // this processor and never actually moved on this processor
603 if (!trace_data.numSegments() && !trace_data.stationary())
604 continue;
605
606 dof_id_type node_id, elem_id;
607 startingIDs(trace_data, node_id, elem_id);
608
609 const Elem * elem = _segment_mesh->elem_ptr(elem_id);
610
611 // Fill first node's nodal data if we need it; the loop that follows will handle
612 // the rest of the nodes
613 if (_output_data_nodal && _study.hasRayData() && !trace_data.stationary())
614 fill_data(elem->node_ptr(0), _data_vars, trace_data._point_data[0]._data);
615
616 const std::size_t start = trace_data.stationary() ? 0 : 1;
617 for (const auto i : make_range(start, trace_data._point_data.size()))
618 {
619 mooseAssert(elem, "Nullptr elem");
620
621 // Elemental ID and pid
622 set_solution(elem, _ray_id_var, trace_data._ray_id);
623 set_solution(elem, _pid_var, processor_id());
624 // Elemental properties that only apply to segments
625 if (!trace_data.stationary())
626 {
627 set_solution(elem, _intersections_var, intersection++);
628 set_solution(elem, _processor_crossings_var, trace_data._processor_crossings);
629 set_solution(elem, _trajectory_changes_var, trace_data._trajectory_changes);
630 }
631 const auto & point_data = trace_data._point_data[i];
632 // Data fields
633 if (_output_data_nodal && !trace_data.stationary())
634 fill_data(elem->node_ptr(1), _data_vars, point_data._data);
635 else
636 fill_data(elem, _data_vars, point_data._data);
637 // Aux data fields
638 fill_data(elem, _aux_data_vars, point_data._aux_data);
639
640 // Advance to the next element
641 if (!trace_data.stationary())
642 elem = elem->neighbor_ptr(1);
643 }
644 }
645
646 _sys->solution->close();
647 _sys->update();
648}
649
650void
652{
653 TIME_SECTION("buildBoundingBoxes", 3, "Building Bounding Boxes for RayTracing Mesh Output");
654
655 // Not used in the one proc case
656 if (_communicator.size() == 1)
657 return;
658
659 // Local bounding box
660 _bbox = MeshTools::create_local_bounding_box(_mesh_ptr->getMesh());
661
662 // Gather the bounding boxes of all processors
663 std::vector<std::pair<Point, Point>> bb_points = {static_cast<std::pair<Point, Point>>(_bbox)};
664 _communicator.allgather(bb_points, true);
666 for (processor_id_type pid = 0; pid < _communicator.size(); ++pid)
667 {
668 BoundingBox pid_bbox = static_cast<BoundingBox>(bb_points[pid]);
669 pid_bbox.scale(0.01);
670 _inflated_bboxes[pid] = pid_bbox;
671 }
672
673 // Find intersecting (neighbor) bounding boxes
675 for (processor_id_type pid = 0; pid < _communicator.size(); ++pid)
676 if (pid != processor_id())
677 {
678 // Insert if the searched processor's bbox intersects my bbox
679 const auto & pid_bbox = _inflated_bboxes[pid];
680 if (_bbox.intersects(pid_bbox))
681 _inflated_neighbor_bboxes.emplace_back(pid, pid_bbox);
682 }
683}
684
685void
687 dof_id_type & start_node_id,
688 dof_id_type & start_elem_id) const
689{
690 const auto [begin_node_id, begin_elem_id] = _ray_starting_id_map.at(trace_data._ray_id);
691
692 const auto offset =
693 trace_data.stationary()
694 ? 1
696 ? trace_data._intersections
697 : (trace_data._processor_crossings + trace_data._trajectory_changes));
698
699 start_node_id = begin_node_id + offset;
700 start_elem_id = begin_elem_id + offset;
701}
702
703dof_id_type
705{
707 return trace_data._intersections + trace_data._point_data.size();
708 return trace_data._processor_crossings + trace_data._trajectory_changes +
709 trace_data._point_data.size();
710}
char ** vars
unsigned long int RayID
Type for a Ray's ID.
Definition Ray.h:44
void ErrorVector unsigned int
unsigned int _padding
static InputParameters validParams()
std::string _file_base
unsigned int & _file_num
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
const InputParameters & _pars
virtual unsigned int dimension() const
MeshBase & getMesh()
MooseMesh * _mesh_ptr
unsigned int _intersections_var
The variable index in _sys for the intersection ID (if any)
static InputParameters validParams()
void buildBoundingBoxes()
Build the inflated neighbor bounding boxes stored in _inflated_neighbor_bboxes for the purposes of id...
std::unique_ptr< libMesh::EquationSystems > _es
The EquationSystems.
void fillFields()
Fill the Ray field data.
RayTracingMeshOutput(const InputParameters &parameters)
unsigned int _ray_id_var
The variable index in _sys for the Ray's ID (if any)
const std::vector< std::string > *const _output_aux_data_names
Specific Ray Aux data to output.
std::vector< BoundingBox > _inflated_bboxes
The inflated bounding boxes for all processors.
const bool _output_aux_data
Whether or not to output the Ray's aux data.
bool _segmented_rays
Whether or not we have segmented rays.
unsigned int _pid_var
The variable index in _sys for the Ray's processor id (if any)
const bool _output_data
Whether or not to output all of the Ray's data.
const bool _output_data_nodal
Whether or not to output the Ray's data in a nodal, linear sense.
void setupEquationSystem()
Setup the equation system that stores the segment-wise field data.
unsigned int _processor_crossings_var
The variable index in _sys for the Ray's processor crossings (if any)
dof_id_type neededNodes(const TraceData &trace_data) const
Gets the number of nodes needed to represent a given trace.
std::unique_ptr< MeshBase > _segment_mesh
The mesh that contains the segments.
void startingIDs(const TraceData &trace_data, dof_id_type &start_node_id, dof_id_type &start_elem_id) const
Gets the starting node and element IDs in the EDGE2 mesh for a given trace.
libMesh::ExplicitSystem * _sys
The system that stores the field data.
virtual std::string fileExtension() const =0
Return the file extension.
virtual std::string filename() override
const RayTracingStudy & _study
The RayTracingStudy.
const std::vector< std::string > *const _output_data_names
Specific Ray data to output.
void buildIDMap()
Builds a map for each Ray to starting element and node ID for the EDGE2 mesh that will represent said...
void buildSegmentMesh()
Build the mesh that contains the ray tracing segments.
std::unordered_map< RayID, std::pair< dof_id_type, dof_id_type > > _ray_starting_id_map
The map from RayID to the starting element and node ID of the mesh element for said Ray.
std::vector< std::pair< processor_id_type, BoundingBox > > _inflated_neighbor_bboxes
Inflated bounding boxes that are neighboring to this processor (pid : bbox for each entry)
BoundingBox _bbox
The bounding box for this processor.
std::vector< std::pair< RayDataIndex, unsigned int > > _data_vars
The ray data index -> variable index map.
virtual void outputMesh()=0
Output the mesh - to be overridden.
dof_id_type _max_node_id
The max node ID for the ray tracing mesh for creating unique elem IDs.
unsigned int _trajectory_changes_var
The variable index in _sys for the Ray's trajectory changes (if any)
std::vector< std::pair< RayDataIndex, unsigned int > > _aux_data_vars
The ray aux data index -> variable index map.
Base class for Ray tracing studies that will generate Rays and then propagate all of them to terminat...
const std::vector< TraceData > & getCachedTraces() const
Get the cached trace data structure.
const std::vector< std::string > & rayAuxDataNames() const
The Ray aux data names.
const std::vector< std::string > & rayDataNames() const
The Ray data names.
bool segmentsOnCacheTraces() const
Whether or not to cache individual element segments when _cache_traces = true.
RayDataIndex getRayDataIndex(const std::string &name, const bool graceful=false) const
Gets the index associated with a registered value in the Ray data.
bool auxDataOnCacheTraces() const
Whether or not to store the Ray aux data on the cached Ray traces.
bool hasRayData() const
Whether or not any Ray data are registered.
RayDataIndex getRayAuxDataIndex(const std::string &name, const bool graceful=false) const
Gets the index associated with a registered value in the Ray aux data.
bool dataOnCacheTraces() const
Whether or not to store the Ray data on the cached Ray traces.
static const RayDataIndex INVALID_RAY_DATA_INDEX
Invalid index into a Ray's data.
Definition Ray.h:212
void max(const T &r, T &o, Request &req) const
processor_id_type size() const
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
virtual void clear() override
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
std::unique_ptr< NumericVector< Number > > solution
virtual void update()
unsigned int variable_number(std::string_view var) const
unsigned int number() const
void fill_data(std::map< processor_id_type, std::vector< std::set< unsigned int > > > &data, int M)
Data structure that stores information for output of a partial trace of a Ray on a processor.
Definition TraceData.h:43
std::vector< TracePointData > _point_data
The data for each point along the track.
Definition TraceData.h:78
bool stationary() const
Definition TraceData.h:59
const unsigned int _processor_crossings
Number of processor crossings thus far.
Definition TraceData.h:72
const unsigned long int _intersections
The number of intersections thus far.
Definition TraceData.h:70
const unsigned int _trajectory_changes
Number of trajectory changes thus far.
Definition TraceData.h:74
const RayID _ray_id
The Ray ID.
Definition TraceData.h:68