RayTracingStudy
The RayTracingStudy
is the base object for generating and tracing of Rays with the Ray Tracing Module. For information about the manipulation of Rays once they have began tracing, see RayKernels and RayBCs.
The majority of the information presented here is for advanced users only. The use of this module for simple cases like line sources (see Using Line Sources) and line integrals (see Computing Line Integrals) utilize the RepeatableRayStudy. Please see RepeatableRayStudy first for a more general understanding. If the RepeatableRayStudy is not sufficient for your use case, please then reference the RepeatableRayStudyBase instead.
Execution Phases
The execution of the RayTracingStudy
is split up into two phases:
Generation - The Rays to be traced are defined and moved into the buffer to be traced
Propagation - The Rays are traced until completion
Generation
To use the RayTracingStudy
, you are to derive from it and override the generateRays()
method. Within generateRays()
, you are to create Rays (using the Ray Pool), set their trajectories (for more information, see Defining a Ray Trajectory), set their data (if any, see Using Ray Data for more information), and move them into the buffer to be traced with moveRayToBuffer()
or moveRaysToBuffer()
.
If you know in advance the number of Rays that you are adding to the buffer to be traced during generation, it is advised call reserveRayBuffer()
with the number of Rays to be moved to the buffer before moving them into the buffer.
Propagation
The Ray propagation phase traces the Rays that were added to the buffer to be traced. This is done internally by the propagateRays()
method.
Note that additional Ray objects may be added to be traced during the propagation phase via RayKernels and RayBCs. For more information, see Creating Additional Rays for creating a Ray within RayKernels and Creating a New Ray for creating a Ray within RayBCs.
Specialization
There are many methods that can be overridden in your derived class. A summary of such methods follows:
generateRays()
- MUST override to define and move the Rays into the buffer to be traced (see Generation)preExecuteStudy()
- Called before GenerationpostExecuteStudy()
- Called after PropagationpostOnSegment()
- Called after each segment of a RayonCompleteRay()
- Called when the trace of a Ray has endedbuildSegmentQuadrature()
Called to generate the 1D quadrature across a Ray segment
Helper Systems
The RayTracingStudy
contains many systems that aid in the generation and manipulation of Rays:
Ray Data Registration: Registers indices into data and auxiliary data to be stored on each Ray
Ray Registration: Registers names to be associated with each Ray and requires RayBCs and RayKernels to provide which Rays they are applied to
Ray Banking: Banks Rays once they end to be accessed after tracing
Ray Pool: A shared pool of Rays that allows for their reuse after they are called to be destructed
Side Normal Caching: Generates and caches (for future use) outward normals for an element
Ray Data Registration
The data and auxiliary data associated with a Ray is stored on the Ray itself in the form of two std::vector<RayData>
(RayData
is typically a Real
), which are accessed via Ray::data()
and Ray::auxData()
. A data registration system exists that allows for the study, RayKernels, and RayBCs to request the data and auxiliary data that they need upon construction.
To register value(s), use the methods registerRayData()
and registerRayAuxData()
, which return either a single index or a vector of indices into the Ray::data()
and Ray::auxData()
vectors that you should utilize. If the same value is registered (uses the same name) within multiple objects, the same index will be returned.
For example, the IntegralRayKernel is the base object for RayKernels that integrate a field across a line (variables, materials, etc). In order to accumulate the integrated value, these kernels need a data value on the Ray. As such, they register the need for Ray data in the constructor and store the index for access:
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)While you can acquire Rays that do not necessarily have the data size set according to the registration using the Ray Pool, any time the data is accessed on a Ray (via Ray::data()
and Ray::auxData()
), if it is not sized properly as required by the registration, it will be resized to the registered size with zeros.
Ray Registration
For cases in which only a few Rays are generated, it is beneficial to have a registration system that allows for the user to access a specific Ray by name instead of an ID. Specific examples are line sources (see Using Line Sources) and line integrals (see Computing Line Integrals), which utilize the RepeatableRayStudy.
The enabling and disabling of ray registration is handled by the _use_ray_registration
private parameter, for which the default is true. When ray registration is enabled, all RayKernels and RayBCs must have the parameter "rays" set, which identifies the Rays by name that said objects are executed on. When ray registration is disabled, the "rays" parameter cannot be used and RayKernels and RayBCs will be executed for all Rays.
When using Ray registration, each Ray must be constructed within the study using the acquireRegisteredRay()
method, which takes as an argument the name for the Ray. For more information, see Ray Pool.
The public methods registeredRayID()
and registeredRayName()
can be utilized to map a registered Ray name to its ID and an ID to its registered name, respectively.
Ray Banking
It is often useful to examine a Ray after it has completed tracing. An example use case is for obtaining the accumulated integral for a Ray that is integrating a field along a line (see Computing Line Integrals and IntegralRayKernel). Within line integrals, the Ray banks will be accessed to obtain the final accumulated value in a Ray.
With the _bank_rays_on_completion
private parameter set to true, all Rays that complete on a given processor are stored in the private member variable _ray_bank
. This bank can be accessed through the rayBank()
method or via getBankedRay()
, which will return the Ray with the requested ID on the processor that ended the Ray.
In addition, the methods getBankedRayData()
and getBankedRayAuxData()
are available to get a single data value from a Ray from the banks after it has completed. For these methods, the resulting value is replicated across all processors.
Ray Pool
For simulations with the Ray Tracing Module that generate a significant number of Ray objects, it becomes advantageous to minimize the construction of new Ray objects. This is more important whenever the size of the data on the Ray is significant.
Because of this, construction Ray objects is handled via a shared pool. The pool ensures that, internally, any Rays that are no-longer used can be reset and used again without extraneous allocation. The construction of new Rays can only occur in three places:
Within
generateRays()
in a study, to create Rays to be inserted into the buffer for tracing, via theacquireRay{}()
methods (more discussion follows).Within
onSegment()
in RayKernels, via theacquireRay()
method.Within
onBoundary()
in RayBCs, via theacquireRay()
method.
Multiple methods exist for acquiring rays within the study, which are:
acquireRay()
- Acquires a Ray with a generated unique ID and with data sized according to the Ray Registration.acquireUnsizedRay()
- Acquires a Ray with a generated unique ID and data sized to zero. Note, whenever the unsized data is accessed within the Ray, it will be automatically resized to ensure data consistency.acquireReplicatedRay()
- Acquires a Ray with an ID that is replicated across all processors and with data sized according to the Ray Registration. This must be called on all processors at the same time.acquireRegisteredRay()
- Acquires a Ray that is replicated across all processors with a given name and utilizes the Ray Registration. For example use, see RepeatableRayStudy.acquireCopiedRay()
- Acquires a Ray that is initialized from another Ray. This is the only acquire method that does not generate a new ID. For example use, see RepeatableRayStudyBase.
Ray storage is wrapped as a std::shared_ptr<Ray>
. The significance of using a shared_ptr
is that the destructor is called when the use count reaches zero, that is, when nobody is holding onto the Ray. The internal tracing algorithm will decrease the use count for a traced Ray after it has completed tracing. That is, if no other shared ownership of the Ray exists, the use count will reach zero. This is why Ray Banking exists—the Ray bank will increase the use count of the Ray so that it is available for use after tracing and is not destructed.
As all Rays must be constructed using the shared pool, whenever the use count for a Ray goes to zero, the Ray will be returned to the pool for future use.
Side Normal Caching
There is often a need to obtain the outward normal for an element's side for many use cases of the Ray Tracing Module.
The RayTracingStudy
can provide outward side normals for an element on the fly. It also caches the generated normal for future requests for the same side normal. This is accessed through the getSideNormal()
method.
Note that the side normals obtained through this caching are evaluated at the side centroid. Therefore, if the element side is non-planar, the returned normal will be an approximation.