RayKernels

A RayKernel operates on a line segment of a Ray within the Ray Tracing Module, which is defined by the entry and exit point of the Ray within an element as it is traced through the mesh. The base object is the RayKernelBase.

Standard MOOSE convention denotes that a "kernel" is an object that contributes to the residual and the Jacobian. To remain consistent with this nomenclature, the RayKernel and ADRayKernel objects contribute to residuals and Jacobians from a Ray.

A summary of the objects that should be derived based on desired operation on segments are as follows:

  • RayKernel: Segments contribute to residuals and Jacobians (example: line sources, see LineSourceRayKernel)

  • ADRayKernel: Segments contribute to residuals and use automatic-differentiation to compute the Jacobian

  • GenericRayKernel: Templated object that enables segment contribution to residuals and Jacobians with and without automatic differentiation (serves a similar purpose as GenericKernel)

  • IntegralRayKernel: Performs an integral on each segment and accumulates the integrated result into the Ray (example: integral of a variable along a line, see VariableIntegralRayKernel)

  • AuxRayKernel: Segments contribute to an AuxVariable in a user-defined manner (example: segment distances are accumulated into an AuxVariable, see RayDistanceAux)

  • GeneralRayKernel: General purpose object that can be adapted to operate on segments in a manner that is not covered by the classes listed above

The remainder of the discussion focuses on the use of the functionality offered within the base object, RayKernelBase. Refer to the objects above for a more specific discussion.

Using a RayKernel

The method that is called on each segment of a Ray in a RayKernel is onSegment(). This method is to be overridden to specialize the on-segment operation. The preTrace() method is also available to be overridden and is called before a trace begins on a processor/thread. After the ray has finished its trace the postTrace() method is called, this method is also available to be overridden.

The significant information pertaining to the trace that is available within onSegment() is as follows:

  • currentRay() - The current Ray that is being traced on the segment.

  • _current_elem - The current element that the Ray is being traced in.

  • _current_segment_start - The start point of the current segment being operated on. This is not necessarily on the element periphery (a side of _current_elem) in the case that a Ray starts within an element.

  • _current_segment_end - The end point of the current segment being operated on. This is not necessarily on the element periphery in the case that a Ray has ended within an element.

  • _current_segment_length - The length of the current segment being operated on.

  • _current_intersected_side - The side intersected on _current_elem at _current_segment_end, if any.

  • _current_incoming_side - The side intersected on _current_elem at _current_segment_end, if any.

  • _current_intersected_extrema - The extrema (element vertex or edge, see ElemExtrema for more information) intersected on _current_intersected_side at _current_segment_end, if any.

  • _current_subdomain_id - The subdomain ID of the _current_elem.

See the Ray documentation for what members are available for use during tracing.

Many standard MOOSE interfaces are also available within RayKernels to do things like access coupled variables, access materials, access UserObjects, access Postprocessors, etc.

Ending the Ray

Ray::shouldContinue() denotes whether or not a Ray will continue to be traced after execution of current objects on the Ray. In the case of a RayKernel, when currentRay()->shouldContinue() == false, the Ray will cease tracing after execution of RayKernels. Internally, the following will set the state of the Ray to not continue before RayKernels are executed but will still allow them to be executed on the segment that contains the final end point:

  • If it has reached the user-set end point (when the Ray trajectory is set via Ray::setStartingEndPoint()). In this specific case, you can tell if the Ray has hit its end point by currentRay()->atEnd().

  • If it has reached the user-set maximum distance for the Ray (currentRay()->distance() == currentRay()->maxDistance()).

  • If it has reached the global maximum distance set by the study parameter ray_distance (currentRay()->distance() == _study.maxRayDistance())

To stop a Ray from being traced, call:


currentRay().setShouldContinue(false);

After a Ray has been set to not continue by any RayKernel, it cannot ever be set to continue again for the current trace.

Changing the Ray Trajectory

A Ray that is currently being traced can have its trajectory changed mid-trace by a RayKernel. The following conditions are imposed on such a trajectory change:

  • The Ray must be continuing (currentRay()->shouldContinue() == true). That is, it cannot have reached its max distance and it cannot have been set to not continue by another RayKernel. See Ending the Ray for more information.

  • The new start point (if changed) must remain within the element of the object that changed it.

  • 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.

  • Only one trajectory change can be called by all RayKernels on each segment.

  • The Ray must have moved before its trajectory is changed.

To change the trajectory of a Ray within a RayKernel, the method changeRayStartDirection() is to be called with parameters being the new start point of the Ray and the new direction of travel. For example:


const Point some_point(1, 2, 3); // must be within _current_elem!
const Point some_direction(1, 0, 0);
changeRayStartDirection(some_point, some_direction);

If the start point of the Ray is changed within the element, the Ray distance will be adjusted accordingly as if the Ray hit the changed point instead. That is, the original incremented distance will be removed (_current_segment_length) and the new incremented distance for this segment will be the distance between currentRay()->currentPoint() and _current_segment_start.

Modifying/Registering Ray Data

If your RayKernel requires data or auxiliary data on a Ray that is unique to said object, you can register said requirement in the constructor using _study.registerRayData() and _study.registerAuxData(). For more information on Ray registration supplied by the RayTracingStudy, see Ray Data Registration.

An example of this exists in IntegralRayKernel, which accumulates integrals into a data member on the Ray. Each IntegralRayKernel requires its own value to accumulate into, therefore the IntegralRayKernel registers a value for its own use:

IntegralRayKernel::IntegralRayKernel(const InputParameters & params)
  : IntegralRayKernelBase(params),
    _integral_data_index(_study.registerRayData(integralRayDataName())),
    _average(getParam<bool>("average"))
{
}
(moose/modules/ray_tracing/src/raykernels/IntegralRayKernel.C)
class IntegralRayKernel : public IntegralRayKernelBase
{
public:
  IntegralRayKernel(const InputParameters & params);

  static InputParameters validParams();

  /**
   * Gets the name of the Ray data associated with the integral accumulated by this RayKernel
   */
  std::string integralRayDataName() const { return _name + "_value"; }

  void onSegment() override final;

protected:
  virtual Real computeQpIntegral() = 0;

  /// The index into the data on the Ray that this integral accumulates into
  const RayDataIndex _integral_data_index;

  /// Whether or not to compute the average (divide by the length)
  const bool _average;
};
(moose/modules/ray_tracing/include/raykernels/IntegralRayKernel.h)

By registering the data, the data is guaranteed to be available for all constructed Rays and can be accessed by currentRay()->data() and currentRay()->auxData().

In the case of the IntegralRayKernel, the Ray data is accessed as such:

void
IntegralRayKernel::onSegment()
{
  // Note that here we do not multiply by _coord[_qp]!
  //
  // The integral done here is the integral of a field variable/material/etc, and not
  // an integration that contributes to the residual/Jacobian. Hence: it is something like
  // a line integral. In RZ and RSPHERICAL, we want line integrals to still be line integrals.
  // Therefore, it does not make sense to multiply by the coordinate transformation.
  Real integral = 0;
  for (_qp = 0; _qp < _q_point.size(); ++_qp)
    integral += _JxW[_qp] * computeQpIntegral();

  // If we're computing the average, divide by the length
  if (_average)
    integral /= _current_segment_length;

  // Accumulate the integral into the Ray
  currentRay()->data(_integral_data_index) += integral;
}
(moose/modules/ray_tracing/src/raykernels/IntegralRayKernel.C)

Creating Additional Rays

It is possible to generate another Ray to be traced from within a RayKernel. This method is thread safe.

First, acquire a new Ray using the acquireRay() method (for more information on acquiring Rays, see Ray Pool), which takes as arguments the starting point and direction of travel for the new Ray. For example:


const Point some_point(1, 2, 3); // must be within _current_elem!
const Point some_direction(1, 0, 0);
std::shared_ptr<Ray> ray = acquireRay(some_point, some_direction);

The acquired Ray will be initialized with the following:

  • Zeroed data and aux data, sized as registered by the RayTracingStudy.

  • A starting point as set by the user.

  • A starting element that is _current_elem.

  • 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);