RayBCs
A RayBC operates on a Ray in the Ray Tracing Module that has intersected a boundary (both external and internal boundaries are supported). The base object is the RayBoundaryConditionBase.
Common use cases for a RayBC are as follows:
Killing a Ray on a boundary (see Killing the Ray and KillRayBC).
Changing the direction of a Ray on a boundary, for example, reflecting the Ray (see Changing the Ray Trajectory and ReflectRayBC).
Creating another Ray on a boundary mid-trace (see Creating a New Ray)
The use of RayBCs is considered advanced use of the Ray Tracing Module. For simple use cases like line sources (see Using Line Sources) and line integrals (see Computing Line Integrals), RayBCs are not required.
Support is not currently available for contributing to residuals and Jacobians with RayBCs, nor is support for accessing coupled variables and materials on element sides in a RayBC. This support is planned in the future and will be implemented as a need arises.
Using a RayBC
The method that is called on each intersected boundary along the trajectory of a Ray in a RayBC is onBoundary()
. This method is to be overridden to specialize the on-boundary operation. For information on the num_applying
parameter, see Hitting Multiple Boundaries.
The significant information pertaining to the trace that is available within onBoundary()
is as follows:
currentRay()
- The current Ray being operated on that has intersected the boundary._current_elem
- The element that the Ray has intersected on the boundary._current_intersected_side
- The side of_current_elem
that the Ray has intersected on the boundary._current_intersection_point
- The point on_current_elem
on_current_intersected_side
that the Ray has intersected on the boundary._current_intersected_extrema
- The extrema (element vertex or edge, see ElemExtrema for more information) intersected on_current_intersected_side
at_current_intersection_point
on_current_elem
, if any._current_bnd_id
- The ID of the boundary that the Ray has intersected._current_subdomain_id
- The subdomain ID of the_current_elem
.
See the Ray documentation for what members are available for use during tracing.
Killing the Ray
A RayBC can stop a Ray from being traced, as is done in KillRayBC:
void
KillRayBC::onBoundary(const unsigned int /* num_applying */)
{
// After RayBCs are completed, ray->shouldContinue() is checked and this will kill the Ray
currentRay()->setShouldContinue(false);
}
(moose/modules/ray_tracing/src/raybcs/KillRayBC.C)Similarly, you can check if another RayBC has set to kill a Ray after this segment with:
const bool ended = currentRay().shouldContinue();
After all RayBCs are executed at a point, if !shouldContinue()
, the trace for the Ray will end.
Changing the Ray Trajectory
A Ray that is currently being traced can have its trajectory changed on a boundary by a RayBC. The following conditions are imposed on a Ray with such a trajectory change:
It must be continuing (
currentRay()->shouldContinue() == true
).It must have moved some before hitting the boundary (
currentRay()->distance() > 0
).The Ray cannot have had its end point set by
Ray::setStartingEndPoint()
. That is, if the user set a specific end point for the Ray (which internally sets its maximum distance to the straight-line distance from the start to the end), its trajectory can never be changed.The new direction must be entrant into
_current_elem
on_current_intersected_side
(the dot product of the outward normal of the side and the direction of the Ray must be negative).
To change the trajectory of a Ray within a RayBC, the method changeRayDirection()
is to be called with the new direction of the Ray. For example:
const Point some_direction(1, 0, 0); // must be entrant!
changeRayDirection(some_direction);
Note on the optional parameter const bool skip_changed_check
in changeRayDirection()
. By default, only one RayBC is allowed to change the trajectory of a Ray that is being traced that intersects a boundary. Said optional parameter sets this requirement. There exist cases when it is appropriate to change the trajectory of a Ray multiple times at a point. See Hitting Multiple Boundaries for more information.
Hitting Multiple Boundaries
When hitting an element vertex in 2D and 3D or an element edge in 3D, it is possible to intersect multiple boundaries at the same point. This is supported and onBoundary()
will be called once on each of the boundaries that are hit for every RayBC that is defined on said boundaries.
The onBoundary()
method passes a single argument, num_applying
. This argument denotes how many of the same RayBC object are being applied at a point. To explain the necessity of num_applying
, consider the following problem:
Two-dimensional domain with the boundaries
left
,right
,top
, andbottom
.A single ReflectRayBC object is defined on all of the boundaries of the problem. That is, whenever a Ray hits any of the boundaries it will be reflected in a specular manner.
One of the traced rays perfectly hits the top-right corner of the domain, at which both the
top
andright
boundaries exist.
The correct specular reflection (inwards to the domain) can be achieved by applying a specular reflection on each of the boundaries, as seen below in Figure 1.
Recall that the act of changing a Ray direction, achieved by changeRayDirection()
, takes an optional parameter const bool skip_changed_check
. In this case, where we want to apply the same ReflectRayBC twice, we pass in num_applying > 1
as the argument to skip_changed_check
to allow the changing of a Ray trajectory multiple times if the same ReflectRayBC is applied more than once. This is done in ReflectRayBC as follows:
void
ReflectRayBC::onBoundary(const unsigned int num_applying)
{
if (_warn_non_planar && _study.sideIsNonPlanar(_current_elem, _current_intersected_side))
mooseWarning("A Ray is being reflected on a non-planar side.\n\n",
"Ray tracing on elements with non-planar faces is an approximation.\n\n",
"The normal used to compute the reflected direction is computed at\n",
"the side centroid and may not be valid for a non-planar side.\n\n",
"To disable this warning, set RayKernels/",
name(),
"/warn_non_planar=false.\n\n",
currentRay()->getInfo());
// No need to do anything if the Ray's gonna die anyway
if (!currentRay()->shouldContinue())
return;
// The direction this Ray reflects off this boundary
const auto & normal = _study.getSideNormal(_current_elem, _current_intersected_side, _tid);
const auto reflected_direction = reflectedDirection(currentRay()->direction(), normal);
// Change it! Note here the usage of num_applying: if we are at a corner with a reflecting
// boundary condition on both sides, we want to allow both boundary conditions to reflect the Ray.
// Therefore, we skip the check that another RayBC has changed the Ray's trajectory when we are
// applying multiple of the same ReflectRayBC at different boundaries at the same point to allow
// this. Note that this double (or triple in 3D) reflection will only be allowed when the same
// ReflectRayBC object is on both boundaries.
changeRayDirection(reflected_direction, /* skip_changed_check = */ num_applying > 1);
}
(moose/modules/ray_tracing/src/raybcs/ReflectRayBC.C)Creating a New Ray
It is possible to generate another Ray to be traced from within a RayBC.
First, acquire a new Ray using the acquireRay()
method (for more information on acquiring Rays, see Ray Pool), which takes an argument that is the direction for the new Ray. For example:
const Point some_direction(1, 0, 0); // must be entrant on _current_intersected_side!
std::shared_ptr<Ray> ray = acquireRay(some_direction);
The acquired Ray will be initialized with the following:
Zeroed data and aux data, sized as registered by the
RayTracingStudy
.A starting element that is
_current_elem
.An incoming side that is
_current_intersected_side
.A starting point that is
_current_intersected_point
.A direction as set by the user.
A unique ID.
After the Ray has been acquired, you may modify its data members as desired before setting it to be traced. Once the Ray is modified as desired, do the following to insert it into the buffer to be traced:
moveRayToBuffer(ray);