13#include "libmesh/ignore_warnings.h"
15#include "libmesh/restore_warnings.h"
22#include "libmesh/mesh_tools.h"
23#include "libmesh/parallel_algebra.h"
24#include "libmesh/parallel_sync.h"
25#include "libmesh/remote_elem.h"
32 params.
addRequiredParam<UserObjectName>(
"study",
"The RayTracingStudy to get the segments from");
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",
39 "Whether or not to output the Ray's data in a nodal sense, in which the "
40 "data is interpolated linearly across segments");
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");
47 MultiMooseEnum props(
"ray_id intersections pid processor_crossings trajectory_changes",
48 "ray_id intersections");
58 _output_data(getParam<bool>(
"output_data")),
59 _output_data_names(isParamValid(
"output_data_names")
60 ? &getParam<
std::vector<
std::string>>(
"output_data_names")
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")
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)
75 mooseError(
"In order to output Ray data in output '",
77 "', the RayTracingStudy '",
79 "' must set data_on_cache_traces = true");
81 mooseError(
"In order to output Ray aux data in output '",
83 "', the RayTracingStudy '",
85 "' must set aux_data_on_cache_traces = true");
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");
96 "Cannot be used in addition to 'output_aux_data_names'; choose one");
103 std::ostringstream
output;
108 output <<
"-s" << std::setw(
_padding) << std::setprecision(0) << std::setfill(
'0') << std::right
150 TIME_SECTION(
"buildIDMap", 3,
"Building RayTracing ID Map");
153 std::map<RayID, dof_id_type> local_ray_needed_nodes;
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);
161 auto & value = find->second;
167 std::map<processor_id_type, std::vector<std::pair<RayID, dof_id_type>>> send_needed_nodes;
168 if (local_ray_needed_nodes.size())
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);
176 std::map<RayID, dof_id_type> global_needed_nodes;
178 std::unordered_map<processor_id_type, std::set<RayID>> pid_received_ids;
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)
185 auto & pid_received_entry = pid_received_ids[pid];
186 for (
const auto & id_max_pair : pairs)
188 const RayID ray_id = id_max_pair.first;
189 const dof_id_type max = id_max_pair.second;
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);
196 auto & current_max = find->second;
197 current_max = std::max(current_max, max);
200 pid_received_entry.insert(ray_id);
203 Parallel::push_parallel_vector_data(
comm(), send_needed_nodes, append_global_max);
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)
211 const RayID ray_id = pair.first;
212 const dof_id_type max_nodes = pair.second;
214 global_ids.emplace(ray_id, std::make_pair(current_node_id, current_elem_id));
216 current_node_id += max_nodes;
218 current_elem_id += max_nodes - 1;
220 current_elem_id += 1;
228 std::unordered_map<processor_id_type, std::vector<std::tuple<RayID, dof_id_type, dof_id_type>>>
230 for (
auto & pid_ids_pair : pid_received_ids)
232 const processor_id_type pid = pid_ids_pair.first;
233 const std::set<RayID> & ray_ids = pid_ids_pair.second;
235 auto & pid_send = send_ids[pid];
236 for (
const RayID ray_id : ray_ids)
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);
246 const auto append_ids =
247 [&](processor_id_type,
248 const std::vector<std::tuple<RayID, dof_id_type, dof_id_type>> & tuples)
250 for (
const auto & tuple : tuples)
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);
260 Parallel::push_parallel_vector_data(
comm(), send_ids, append_ids);
266 TIME_SECTION(
"buildSegmentMesh", 3,
"Building RayTracing Mesh Output");
272 dof_id_type num_nodes = 0;
273 dof_id_type num_elems = 0;
276 const auto num_segments = entry.numSegments();
277 num_nodes += num_segments + 1;
278 if (num_segments >= 1)
281 num_elems += num_segments;
283 else if (entry.stationary())
317 if (trace_data.numSegments() == 0 && !trace_data.stationary())
320 dof_id_type node_id, elem_id;
324 mooseAssert(!
_segment_mesh->query_node_ptr(node_id),
"Node already exists");
327 last_node->set_unique_id(node_id++);
330 if (trace_data.stationary())
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));
336 elem->set_node(0, last_node);
341 Elem * last_elem =
nullptr;
343 for (std::size_t i = 1; i < trace_data._point_data.size(); ++i)
345 const auto & point = trace_data._point_data[i]._point;
348 mooseAssert(!
_segment_mesh->query_node_ptr(node_id),
"Node already exists");
350 node->set_unique_id(node_id++);
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));
357 elem->set_node(0, last_node);
358 elem->set_node(1, node);
363 elem->set_neighbor(0, last_elem);
364 last_elem->set_neighbor(1, elem);
384 std::vector<Node *> need_node_owners;
387 const auto num_segments = trace_data.numSegments();
389 if (num_segments == 0)
392 dof_id_type start_node_id, start_elem_id;
393 startingIDs(trace_data, start_node_id, start_elem_id);
397 : (
unsigned long int)(trace_data._processor_crossings +
398 trace_data._trajectory_changes)) != 0)
401 Elem * previous_elem =
_segment_mesh->query_elem_ptr(start_elem_id - 1);
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));
419 if (!trace_data._last)
422 Elem * next_elem =
_segment_mesh->query_elem_ptr(start_elem_id + num_segments);
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));
437 std::unordered_map<processor_id_type, std::vector<dof_id_type>> need_node_owners_sends;
438 for (
const Node * node : need_node_owners)
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());
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)
457 auto & confirm_pid = confirm_node_owners_sends[pid];
458 for (
const auto & node_id : incoming_node_ids)
463 mooseAssert(!node->valid_processor_id(),
"Should be invalid");
465 confirm_pid.push_back(node_id);
471 Parallel::push_parallel_vector_data(
472 _communicator, need_node_owners_sends, decide_node_owners_functor);
477 for (Node * node : need_node_owners)
478 if (!node->valid_processor_id())
488 TIME_SECTION(
"setupEquationSystem", 3,
"Setting Up Ray Tracing MeshOutput Equation System");
526 const auto get_data_vars = [
this](
const bool aux)
529 std::vector<std::pair<RayDataIndex, unsigned int>>
vars;
531 const auto from_names =
541 for (
const auto &
name : *from_names)
543 const auto data_index =
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");
552 const std::string var_prefix = aux ?
"aux_" :
"";
553 if (output_data_nodal)
558 vars.emplace_back(data_index, var_num);
574 TIME_SECTION(
"fillFields", 3,
"Filling RayTracing MeshOutput Fields");
577 const auto set_solution =
578 [
this](
const DofObject *
const dof,
const unsigned int var,
const Real value)
580 mooseAssert(dof,
"Nullptr dof");
581 if (var != invalid_uint)
583 const auto dof_number = dof->dof_number(
_sys->
number(), var, 0);
590 [&set_solution](
const DofObject *
const dof,
const auto &
vars,
const auto & data)
592 mooseAssert(dof,
"Nullptr dof");
593 for (
const auto & [data_index, var_num] :
vars)
594 set_solution(dof, var_num, data[data_index]);
599 auto intersection = trace_data._intersections;
603 if (!trace_data.numSegments() && !trace_data.stationary())
606 dof_id_type node_id, elem_id;
616 const std::size_t start = trace_data.stationary() ? 0 : 1;
617 for (
const auto i : make_range(start, trace_data._point_data.size()))
619 mooseAssert(elem,
"Nullptr elem");
622 set_solution(elem,
_ray_id_var, trace_data._ray_id);
625 if (!trace_data.stationary())
631 const auto & point_data = trace_data._point_data[i];
641 if (!trace_data.stationary())
642 elem = elem->neighbor_ptr(1);
653 TIME_SECTION(
"buildBoundingBoxes", 3,
"Building Bounding Boxes for RayTracing Mesh Output");
663 std::vector<std::pair<Point, Point>> bb_points = {
static_cast<std::pair<Point, Point>
>(
_bbox)};
668 BoundingBox pid_bbox =
static_cast<BoundingBox
>(bb_points[pid]);
669 pid_bbox.scale(0.01);
680 if (
_bbox.intersects(pid_bbox))
687 dof_id_type & start_node_id,
688 dof_id_type & start_elem_id)
const
699 start_node_id = begin_node_id + offset;
700 start_elem_id = begin_elem_id + offset;
unsigned long int RayID
Type for a Ray's ID.
void ErrorVector unsigned int
static InputParameters validParams()
const std::string & name() const
void paramError(const std::string ¶m, Args... args) const
void mooseError(Args &&... args) const
const InputParameters & _pars
virtual unsigned int dimension() const
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 ¶meters)
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.
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
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.
std::vector< TracePointData > _point_data
The data for each point along the track.
const unsigned int _processor_crossings
Number of processor crossings thus far.
const unsigned long int _intersections
The number of intersections thus far.
const unsigned int _trajectory_changes
Number of trajectory changes thus far.
const RayID _ray_id
The Ray ID.