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