Document not found (404)
+This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ae1c3f770 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Directories +.vs/ +.vscode/ +bin/ +bin-int/ +build/ +out/ + +# Files +*.user +*.code-workspace +/CMakeSettings.json +/Tests/output/* +/Tests/oldray_files/unittestData +/Tests/oldray_files/spec1-first_rzp02/data +/Tests/oldray_files/spec1-first_rzp4mm/data +/Tests/oldray_files/spec1+first_rzp4mm/data +/Tests/.ipynb_checkpoints/* +CMakeCache.txt +cmake.check_cache \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 000000000..0a31b14e7 --- /dev/null +++ b/404.html @@ -0,0 +1,220 @@ + + +
+ + +This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +RAYX is a command-line tool developed to work in conjunction with RAYX-CORE for the purpose of accurately and efficiently tracing beamlines, as described by .rml files. RAYX-CORE is a specialized library with capabilities for simulating a variety of optical phenomena. The library adopts a modular architecture for the construction of optical elements, comprising three key components: Surface, Cutout, and Behavior. These components are responsible for calculating ray-object collisions and determining the subsequent interactions of the ray, such as reflection or absorption. The primary application of RAYX-CORE is to enable the precise tracing of synchrotron radiation in beamlines associated with electron storage rings.
+In terms of computational resources, RAYX utilizes the parallel processing capabilities of GPUs, facilitated through the Vulkan API. This approach allows for efficient and accurate ray tracing. For systems without GPU support, RAYX provides an alternative CPU-based tracing option to maintain compatibility across diverse hardware configurations.
+Further enhancing the project's capabilities, RAYX-UI has been introduced as a real-time rendering interface built upon RAYX-CORE. This interface provides immediate visual feedback on the interactions between rays and optical elements, serving as a valuable tool for adjusting element positions. While initially conceived as a debugging utility, the long-term objective for RAYX-UI is its development into a full-fledged 3D design tool for beamline construction and customization.
+ +Currently we only support a one over trace for a given beamline file. This means, +you can either use RAY-UI to generate a beamline file or alter an existing one.
+Here is an example for a simple beamline file "PlaneMirror.rml":
+<?xml version="1.0" encoding="UTF-8" ?>
+<lab>
+<version>1.1</version>
+<beamline>
+
+ <object name="Matrix Source" type="Matrix Source">
+ <param id="numberRays" enabled="T">100</param>
+ <param id="sourceWidth" enabled="T">0.065</param>
+ <param id="sourceHeight" enabled="T">0.04</param>
+ <param id="sourceDepth" enabled="T">0</param>
+ <param id="horDiv" enabled="T">1</param>
+ <param id="verDiv" enabled="T">1</param>
+ <param id="energyDistributionType" comment="Values" enabled="T">1</param>
+ <param id="photonEnergyDistributionFile" relative="" enabled="F"></param>
+ <param id="photonEnergy" enabled="T">100</param>
+ <param id="energySpreadType" comment="white band" enabled="T">0</param>
+ <param id="energySpread" enabled="T">0</param>
+ <param id="linearPol_0" enabled="T">1</param>
+ <param id="linearPol_45" enabled="T">0</param>
+ <param id="circularPol" enabled="T">0</param>
+ <param id="sourcePulseType" comment="all rays start simultaneously" enabled="T">0</param>
+ <param id="sourcePulseLength" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>1</y>
+ <z>0</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>1</z>
+ </param>
+ </object>
+
+ <object name="Plane Mirror" type="Plane Mirror">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="grazingIncAngle" auto="T" enabled="T">40</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" auto="T" enabled="T">0</param>
+ <param id="systemMount" comment="standalone" enabled="T">0</param>
+ <param id="premirrorShiftZ" enabled="F">0</param>
+ <param id="pimpaleAlpha1" enabled="F">1</param>
+ <param id="pimpaleAlpha2" enabled="F">2</param>
+ <param id="pimpaleAlpha3" enabled="F">3</param>
+ <param id="distancePremirrorGrating" enabled="T">0</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="elementSubstrate" enabled="F">Au</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="coatingFile" relative="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" relative="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.766044443118978</y>
+ <z>-0.6427876096865393</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.6427876096865393</y>
+ <z>0.766044443118978</z>
+ </param>
+ </object>
+
+</beamline>
+
+<ExtraData>
+</ExtraData>
+</lab>
+
+
+In it we have a matrix source that sends rays onto a plane mirror. +Assuming the beamline file is in the same directory as the binary +you can run the CLI of rayx as follows:
+./rayx -i PlaneMirror.rml
+
+Adding a -p
flag will output a footprint of the last element in
+in the beamline, which was hit by rays.
After a successful build, type -h
or --help
for a summary of all known commands.
++Hint:
+-c
or--command
are accepted. But-command
can result in errors.
Terminal application for rayx
+Usage: ./rayx [OPTIONS]
+
+Options:
+ -h,--help Print this help message and exit
+ -c,--ocsv Output stored as .csv file.
+ -b,--batch INT Batch size for Vulkan tracing
+ -B,--benchmark Benchmark application and output to stdout
+ -x,--cpu Tracing on CPU
+ -p,--plot Plot output footprints and histograms.
+ -l,--list List available devices
+ -d,--device INT Device ID
+ -i,--input TEXT Input RML File or Directory.
+ -v,--version
+ -f Fix the seed to RAYX::FIXED_SEED (Uses default)
+ -s,--seed INT Provided user seed
+ -S,--sequential trace sequentially
+ -V,--verbose Dump more information
+ -F,--format TEXT Format output CSV / H5 data
+
+Command name | description |
---|---|
--help | Prints the help message. |
--ocsv | To store the result as a .csv (defaults to .h5). This is not recommended with a massive ray amount. |
--batch | Tells the tracer how large a batch of rays should be. This is useful for optimizing compute times. |
--benchmark | Uses RAYX-CORE's profiling to benchmark the application. The results are printed at the end of the trace. |
--cpu | Run Tracing on CPU, instead of using the GPU. |
--plot | After a successful trace, the output data from the last Image Plane element gets plotted. The application will only exit once the plot window is closed. |
--list | Gives a list of devices in your computer that are supported but rayx. |
--device | Select a specific device for the computations. This is useful when your PC has multiple GPUs. |
--input | Path to the RML file to be used as imported beamline. |
--version | Prints the application's meta info. |
--dummy | Run a dummy beamline with some optical elements, useful for testing. |
--format | Specify the output format of the ray data. Example: |Ray-ID|Event-ID|X-position|Y-position|Z-position| |
Th window in the image above is accessible in the RAYX-UI application once it's opened. The window provides additional options for adjusting the scene you're viewing. Most notably, the "Open File Dialog" button (indicated by a red arrow) opens a file dialog that allows you to load your .rml beamline file. Currently, the application expects the h5 file (RAYX output) to be located next to it. In the future, tracing functionality will be directly integrated into the interface.
+The subsequent camera settings in the interface are intended for advanced users and will not be explained here. If you're unfamiliar with these settings, you likely won't need them. Further down, however, you'll find the "Save Camera" and "Load Camera" buttons, which enable you to save and load a single camera position. This feature can be useful if you wish to save your camera settings for future use or compare different beamlines from the same vantage point.
+RAYX-UI is an interface designed for real-time visualization of beamline simulations conducted by RAYX-CORE. The application employs keyboard and mouse inputs for navigation and control.
+W
, A
, S
, D
, Q
, and E
keys to move the camera through the 3D space.Shift
key to adjust movement speed.F11
and F10
keys, respectively.In the visual representation, various colors are used to indicate specific conditions or states:
+Optical Elements: Optical elements like slits or image planes are displayed in varying shades of blue, with the color gradient used to represent surface orientation.
+ +For building and running the project, we recommend using Visual Studio Code (VSCode) as your IDE, along with the C/C++ and CMake Tools extensions. These extensions significantly simplify the building process. However, you are free to use any IDE of your choice. If you are on a UNIX-like system, the compile.sh
script can be used for compilation.
Clone the git repository by running one of the following commands:
+git clone --recurse-submodules git@github.com:hz-b/rayx.git
git clone --recurse-submodules https://github.com/hz-b/rayx.git
To use a custom generator for CMake, such as Ninja for faster builds, you can set it in the .vscode/settings.json
file. The following is an example configuration for the generator in VSCode:
+
cmake -S . -B build -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=Release
apt update && apt -y install libblas-dev liblapack-dev libhdf5-dev libgtk-3-dev pkg-config libxi-dev libxcursor-dev libxinerama-dev libxrandr-dev
/usr/include/hdf5/serial
and /usr/lib/x86_64-linux-gnu/hdf5/serial
.Arch Linux users can obtain all necessary packages through pacman, yay, or other package managers. Specific instructions will be provided later.
+To install the required packages on Fedora, run the following command:
+sudo dnf install cmake gcc gdb vulkan vulkan-tools vulkan-validation-layers hdf5-devel ninja-build gcc-c++ vulkan-loader-devel glslc blas-devel lapack-devel gtk3-devel pkg-config libXi-devel libXcursor-devel libXinerama-devel libXrandr-devel
This document serves as a comprehensive style guide for the RAY project. It outlines the coding standards and best practices that have evolved over the course of the project's lifecycle. Adherence to these guidelines is crucial for maintaining code readability and facilitating effective collaboration among team members. Please read them attentively.
+Write code with collective ownership in mind; the primary audience is your teammates, not just the compiler. The KISS (Keep It Stupid Simple) principle should be applied whenever possible.
+To enhance readability, includes should be categorized as follows:
+#include "internalHeader.h"
#include <externalHeader.h>
Internal headers are those developed within the project, while external headers pertain to dependencies integrated into the project.
+Prioritize frequent and precise commenting. Comments should be tailored to newcomers to the project, who are the most likely to require and benefit from them.
+/**
+* a desctription (don't just repeat the function name)
+* @param value1 what the parameter value1 does
+* @param value2 what the parameter value2 does
+* @see related function
+* @return what the function returns
+*/
+
+The objective is for header files to provide high-level documentation on API usage, while source files should contain more detailed documentation about implementation specifics.
+As longer names can contain more than one word, it can help to have a visual divider. In the case of Ray-UI we use "camelCase" and "PascalCase". When to use what, will be explained in the "Classes, Function and Variables" subsection.
+The upper case letter indicates a new word and thus improves readability. Every name should be able to stand alone and describe the object, function or variable. Something like "int v;" does not achieve this. This also means to avoid using abbreviations, besides the most common ones ("val" for "value" or "dx" for a distance over x).
+Avoid using symbols or variable names from formulas for naming variables in the code. This makes the code a riddle to decipher, without further information. +The correct ways to do this:
+Should the name get too long or cryptic, write a comment to clarify what you mean with the name you chose for this variable.
+Boolean values should begin with is/can/has/etc. when possible.
+Class and object names are written in "PascalCase". Functions and Variables are written in "camelCase".
+e.g.:
+Member objects and variables of a class are indicated by an "m_", e.g. :
+Const correctness is the practice of using the const
keyword to ensure that objects and variables remain immutable. Use const
as the default. One exception: function parameters of trivial data types like int
, double
, etc.
void f1(const std::string& s);
// Pass by reference-to-const
void f2(const std::string* sptr);
// Pass by pointer-to-const
void f3(std::string s);
// Pass by valueEmploying const correctness from the start is advisable, as it can simplify code maintenance and improve overall code quality.
+Further reading on const correctness is highly recommended and can be found here.
+Raw pointers are discouraged except when interfacing with APIs that require them. Following are alternatives to raw pointers for specific use-cases.
+std::unique_ptr
and std::shared_ptr
for managing dynamic memory.std::optional
: Utilize std::optional
for optional parameters or return types to explicitly indicate the absence of a value.const char*
) are acceptable only when necessary for compatibility with C APIs. Ensure they are const
to prevent modification.std::string_view
: For performance-critical code, use std::string_view
to pass strings by reference without ownership or copy.In the following chaper you will find extensive Information on all the Beamline Obejcts RAYX supports.
+ +TBA
+In RAYX every ray is described by four properties. The values are randomly generated for the given distirubution. This guarantees that minimal systematic errors are impacting the simulation.
+light properties:
+The Origin for each Ray is described by x, y and z Position.
+To trace a Circle Source through an RML File, utilize an XML Object to encompass all pertinent information for the light source. The default configuration for the light source from RAY-UI is presented here for your ease of use. It is recommended to include an Imageplane at the end for clearer results.
+<object name="Circle Source" type="Circle Source">
+ <param id="numberRays" enabled="T">20000</param>
+ <param id="sourceWidth" enabled="T">0.065</param>
+ <param id="sourceHeight" enabled="T">0.04</param>
+ <param id="sourceDepth" enabled="T">1</param>
+ <param id="numberCircles" enabled="T">1</param>
+ <param id="maximumOpeningAngle" enabled="T">0</param>
+ <param id="minimumOpeningAngle" enabled="T">0</param>
+ <param id="deltaOpeningAngle" enabled="T">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>1.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>1.0000000000000000</z>
+ </param>
+ <param id="energyDistributionType" comment="Values" enabled="T">1</param>
+ <param id="photonEnergyDistributionFile" absolute="" enabled="F"></param>
+ <param id="photonEnergy" enabled="T">100</param>
+ <param id="energySpreadType" comment="white band" enabled="T">0</param>
+ <param id="energySpread" enabled="T">0</param>
+ <param id="linearPol_0" enabled="T">1</param>
+ <param id="linearPol_45" enabled="T">0</param>
+ <param id="circularPol" enabled="T">0</param>
+ <param id="sourcePulseType" comment="all rays start simultaneously" enabled="T">0</param>
+ <param id="sourcePulseLength" enabled="F">0</param>
+ </object>
+
+
+ The Dipole Source in RAYX is implemented to generate a realistic lightbeam. To simulate a realistic representation of behaviour, the natural spectral and spatial distributions of synchrotron radiation are used as a foundation. Specificly the distributions defined by the universal function for synchrotron radiation defined by Wiedemann, Synchrotron Radiation P. 159 ().
+Dipole Sources are among the most common lightsources used to generate synchrotron radiation. In synchrotrons dipole magnets are used to bend a charged particle beam. Depending on the strength of the magnetic field and other changeble parameters the act of bending the particle beam produces X-Rays. Highly energized photons traveling with relativistic speeds. These photons than travel throuh the beamline, colliding with optical elements, and hitting an experiment at the end of the line.
+In RAYX every ray is described by four properties. Each light source has a different approach to determine these, depending on which parts should be realisic and what should be syntheticaly generated. The values are randomly generated for the given distirubution. This guarantees that minimal systematic errors are impacting the simulation. The user gives a distribution window for the properties.
+The Origin for each Ray is described by a x, y and z Position. For the dipole source the position is dependent on the strength of the dipole magnet. +In the coordinates system for the Lightsource x is on the horizontal plane with z. The y direction is "up" and "down" looking from the source down the beamline. Which is the direction to the experiment, or at least to the next optical element and is described by z. +The user has limeted influence on the distribution of the position. It is determined by the magnetic field and the horizontal divergence window.
+The Direction is determined by the vertical and horizontal angle. Those are determind by the users input for the vertical and horizontal Divergence. The Direction is calculated by two variables the Phi and Psi angle. +Phi is determined randomly in a uniform spread on the given horizontal Divergence. +For Psi the calculation is a little more complex and dependent on the Polarisation. +Of the given vertical electron beam divergance a distribution window is calculated.
+The photon energy is dependant on the given mean-energy and the energyspread. Again the focus of the simulation lays in the distribution of the photonenergies between all generated rays. The energy can be understood as the equvalent to the wavelength of the photon. For the dipole source the energy is randomly distributed according to the universal function for synchrotron radiation (1). +The basis for the simulation lays in Helmut Wiedemanns description (Synchrotron Radiation P. 259 (D.21)). He gives edge cases which are usefull to simplyfy the simulation.
+The light polarisation impacts the reflective properties of the ray. The polarization is also important for the distiribution of the vertical direction value of the ray. As written by Helmut Wiedemanns (Synchrotron Radiation P. 155 (9.78)) the polarisation, direction and photon energy determine the distribution.
+In RAYX we transfer all the generated information from the light sources to the optical elements via the struct Ray.
+To trace a Dipole Source through an RML File, utilize an XML Object to encompass all pertinent information for the light source. The default configuration for the light source from RAY-UI is presented here for your ease of use. It is recommended to include an Imageplane at the end for clearer results.
+<object name="Dipole Source" type="Dipole Source">
+ <param id="numberRays" enabled="T">200</param>
+ <param id="sourceWidth" enabled="T">0.065</param>
+ <param id="sourceHeight" enabled="T">0.04</param>
+ <param id="verEbeamDiv" enabled="T">1</param>
+ <param id="horDiv" enabled="T">1</param>
+ <param id="electronEnergy" enabled="T">1.7</param>
+ <param id="electronEnergyOrientation" comment="clockwise" enabled="T">0</param>
+ <param id="bendingRadius" enabled="T">4.35</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>1.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>1.0000000000000000</z>
+ </param>
+ <param id="energyDistributionType" comment="Values" enabled="T">1</param>
+ <param id="photonEnergyDistributionFile" absolute="" enabled="F"></param>
+ <param id="photonEnergy" enabled="T">100</param>
+ <param id="energySpreadType" comment="white band" enabled="T">0</param>
+ <param id="energySpreadUnit" comment="eV" enabled="T">0</param>
+ <param id="energySpread" enabled="T">0</param>
+ <param id="sourcePulseType" comment="all rays start simultaneously" enabled="T">0</param>
+ <param id="sourcePulseLength" enabled="F">0</param>
+ <param id="photonFlux" enabled="T">2.76089e+12</param>
+ </object>
+
+
+
+ Light Sources are the starting point for each ray. Here the Properties of the light are set. Depending on the user input the direction, photon energy and light polarization are determined. In this section, you can find a description on +how the light sources are implemented and how to employ them for different beamlines. Depending on the needs it can be usefull to know which light source suits best. +The important part of the implementation of light sources in RAYX are the overall distributions of the values. Most of the Lightsources produce a spectrum of light rays.
+Currently there are six light sources implemented in RAYX. They are fundamentaly different and serve different purpuses.
+The Matrix and Point Sources are conceptual sources, wheras the implementation of the Dipole Source is aiming to be as close to the reality as possible.
+In RAYX every ray is described by four properties. Each light source has a different approach to determine these, depending on which parts should be realisic and what should be syntheticaly generated. The values are almost always in a given distribution-window and randomly generated. This guarantees that minimal systematic errors are impacting the ray generation. The user can choose a distribution window and a distribution type.
+Parameters:
+The user sets the following parameters:
+The origin for each ray is described by a x, y and z value.
+With repeatability in mind the Maxtrix Source has a completely deterministic aproach to set the origin of each ray. All rays are positioned in a uniform grid inside the given width and height. Depending on the number of rays this grid can look scarsly or completely filled.
+The Point Source has set distribution types for the positions. Every ray has a random origin but looking at all rays combined, they represent the chosen distribution type. The common choises are either hard-edge or soft-edge distributions.
+The Dipole Source additionaly takes the horizontal divergence and bending radius into account. The origin positions are in a 3 dimensual bend, representing the trajectory of the electrons in the synchrotron. The position has a natural distribution which is dependant on the horizontal divergence of the electron beam.
+The user sets the following parameters:
+The direction for each ray is calculated for phi and psi values. They are the horizontal and vertical angles of the direction. +A direction vector is calculated from these angles and used for the tracing process.
+And again there are differences for each source which are described in the respective chapters.
+The user sets the following parameters:
+The Photonenergy is understood as the mean energy in a given distribution. If the energyspread is defined as 0 all rays have the same energy. +All the iformation can also be
+The energy can be understood as the equivalent to the wavelength of the lightray. Different Distributiontypes are available to observe the behaiviour.
+The light polarisation impacts the reflective properties of the ray. Depending on the lightsource it is calculated or given by the user.
+For more information on how to implement and use light sources please read the dokumentation for the spesific light source (updated soon).
+TBA
+In RAYX every ray is described by four properties. The values are randomly generated for the given distirubution. This guarantees that minimal systematic errors are impacting the simulation.
+light properties:
+The Origin for each Ray is described by x, y and z Position.
+To trace a Pixel Source through an RML File, utilize an XML Object to encompass all pertinent information for the light source. The default configuration for the light source from RAY-UI is presented here for your ease of use. It is recommended to include an Imageplane at the end for clearer results.
+<object name="Pixel Source" type="Pixel Source">
+ <param id="numberRays" enabled="T">20000</param>
+ <param id="sourceWidth" enabled="T">0.065</param>
+ <param id="sourceHeight" enabled="T">0.04</param>
+ <param id="sourceDepth" enabled="T">1</param>
+ <param id="horDiv" enabled="T">1</param>
+ <param id="verDiv" enabled="T">1</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>1.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>1.0000000000000000</z>
+ </param>
+ <param id="energyDistributionType" comment="Values" enabled="T">1</param>
+ <param id="photonEnergyDistributionFile" absolute="" enabled="F"></param>
+ <param id="photonEnergy" enabled="T">100</param>
+ <param id="energySpreadType" comment="white band" enabled="T">0</param>
+ <param id="energySpread" enabled="T">0</param>
+ <param id="linearPol_0" enabled="T">1</param>
+ <param id="linearPol_45" enabled="T">0</param>
+ <param id="circularPol" enabled="T">0</param>
+ <param id="sourcePulseType" comment="all rays start simultaneously" enabled="T">0</param>
+ <param id="sourcePulseLength" enabled="F">0</param>
+ </object>
+
+
+
+
+ TBA
+In RAYX every ray is described by four properties. The values are randomly generated for the given distirubution. This guarantees that minimal systematic errors are impacting the simulation.
+light properties:
+The Origin for each Ray is described by x, y and z Position.
+To trace a Pixel Source through an RML File, utilize an XML Object to encompass all pertinent information for the light source. The default configuration for the light source from RAY-UI is presented here for your ease of use. It is recommended to include an Imageplane at the end for clearer results.
+<object name="Pixel Source" type="Pixel Source">
+ <param id="numberRays" enabled="T">20000</param>
+ <param id="sourceWidth" enabled="T">0.065</param>
+ <param id="sourceHeight" enabled="T">0.04</param>
+ <param id="sourceDepth" enabled="T">1</param>
+ <param id="horDiv" enabled="T">1</param>
+ <param id="verDiv" enabled="T">1</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>1.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>1.0000000000000000</z>
+ </param>
+ <param id="energyDistributionType" comment="Values" enabled="T">1</param>
+ <param id="photonEnergyDistributionFile" absolute="" enabled="F"></param>
+ <param id="photonEnergy" enabled="T">100</param>
+ <param id="energySpreadType" comment="white band" enabled="T">0</param>
+ <param id="energySpread" enabled="T">0</param>
+ <param id="linearPol_0" enabled="T">1</param>
+ <param id="linearPol_45" enabled="T">0</param>
+ <param id="circularPol" enabled="T">0</param>
+ <param id="sourcePulseType" comment="all rays start simultaneously" enabled="T">0</param>
+ <param id="sourcePulseLength" enabled="F">0</param>
+ </object>
+
+
+
+
+ TBA
+In RAYX every ray is described by four properties. The values are randomly generated for the given distirubution. This guarantees that minimal systematic errors are impacting the simulation.
+light properties:
+The Origin for each Ray is described by x, y and z Position.
+To trace a Point Source through an RML File, utilize an XML Object to encompass all pertinent information for the light source. The default configuration for the light source from RAY-UI is presented here for your ease of use. It is recommended to include an Imageplane at the end for clearer results.
+<object name="Point Source" type="Point Source">
+ <param id="numberRays" enabled="T">200</param>
+ <param id="sourceWidthDistribution" comment="gaussian (sigma)" enabled="T">1</param>
+ <param id="sourceWidth" enabled="T">0.065</param>
+ <param id="sourceHeightDistribution" comment="gaussian (sigma)" enabled="T">1</param>
+ <param id="sourceHeight" enabled="T">0.04</param>
+ <param id="sourceDepth" enabled="T">1</param>
+ <param id="horDivDistribution" comment="gaussian (sigma)" enabled="T">1</param>
+ <param id="horDiv" enabled="T">1</param>
+ <param id="verDivDistribution" comment="gaussian (sigma)" enabled="T">1</param>
+ <param id="verDiv" enabled="T">1</param>
+ <param id="alignmentError" comment="Yes" enabled="T">0</param>
+ <param id="translationXerror" enabled="F">5</param>
+ <param id="translationYerror" enabled="F">5</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="energyDistributionType" comment="Values" enabled="T">1</param>
+ <param id="photonEnergyDistributionFile" absolute="" enabled="F"></param>
+ <param id="photonEnergy" enabled="T">151</param>
+ <param id="energySpreadType" comment="white band" enabled="T">0</param>
+ <param id="energySpreadUnit" comment="eV" enabled="T">0</param>
+ <param id="energySpread" enabled="T">6</param>
+ <param id="linearPol_0" enabled="T">1</param>
+ <param id="linearPol_45" enabled="T">0</param>
+ <param id="circularPol" enabled="T">0</param>
+ <param id="sourcePulseType" comment="all rays start simultaneously" enabled="T">0</param>
+ <param id="sourcePulseLength" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>1</y>
+ <z>0</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>1</z>
+ </param>
+ </object>
+
+
+
+
+ The simple Undulator source mimics the physics of a real undulator source and allows the user to trace an approximated undulator lightbeam much faster. +This light source utilieses the Gaussian Beam Theorie which allows us to simplify the beam Properties a lot. We already treat the lightbeam as a group of individual rays. This gives us the possibility to substitute all complex wave theory laws on our light beam with approximations.[1] +Simply put the Gaussian Beam Theory says that some beam properties are distributed in gaussian patterns. And that the pattern flattens or sqishes from collisions with optical elements but generaly remains a gaussian bellcurve.
+In RAYX every ray is described by four properties. The values are randomly generated for the given distirubution. This guarantees that minimal systematic errors are impacting the simulation.
+light properties:
+The Origin for each Ray is described by x, y and z Position.
+To trace a simple Undulator Source through an RML File, utilize an XML Object to encompass all pertinent information for the light source. The default configuration for the light source from RAY-UI is presented here for your ease of use. It is recommended to include an Imageplane at the end for clearer results.
+<object name="Simple Undulator" type="Simple Undulator">
+ <param id="numberRays" enabled="T">20000</param>
+ <param id="sigmaType" comment="standard" enabled="T">0</param>
+ <param id="undulatorLength" enabled="T">4</param>
+ <param id="electronDistributionType" comment="manual" enabled="T">0</param>
+ <param id="electronSigmaX" enabled="T">216</param>
+ <param id="electronSigmaXs" enabled="T">24.8</param>
+ <param id="electronSigmaY" enabled="T">18.7</param>
+ <param id="electronSigmaYs" enabled="T">4.3</param>
+ <param id="sourceDepth" enabled="T">1</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>1.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>1.0000000000000000</z>
+ </param>
+ <param id="energyDistributionType" comment="Values" enabled="T">1</param>
+ <param id="photonEnergyDistributionFile" absolute="" enabled="F"></param>
+ <param id="photonEnergy" enabled="T">100</param>
+ <param id="energySpreadType" comment="white band" enabled="T">0</param>
+ <param id="energySpreadUnit" comment="eV" enabled="T">0</param>
+ <param id="energySpread" enabled="T">0</param>
+ <param id="linearPol_0" enabled="T">1</param>
+ <param id="linearPol_45" enabled="T">0</param>
+ <param id="circularPol" enabled="T">0</param>
+ <param id="sourcePulseType" comment="all rays start simultaneously" enabled="T">0</param>
+ <param id="sourcePulseLength" enabled="F">0</param>
+ </object>
+
+
+[1]Representation of a Gaussian beam by rays +P. P. Crooker,a兲 W. B. Colson, and J. Blau +Physics Department, Naval Postgraduate School, Monterey, California 93943 +Received 3 October 2005; accepted 7 April 2006兲
+ +Mandatory:
+Cone:
+Cutout:
+To track a Cone Mirror using an RML File, you'll require an XML Object to encompass all the mirror details. The default mirror setup from RAY-UI is provided here for your convenience. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Cone" type="Cone">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="grazingIncAngle" enabled="T">10</param>
+ <param id="entranceArmLength" enabled="T">10000</param>
+ <param id="exitArmLength" enabled="T">1000</param>
+ <param id="upstreamRadius" auto="T" enabled="T">346.85522293840654</param>
+ <param id="downstreamRadius" auto="T" enabled="T">284.67930120577341</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="materialSubstrate" enabled="F">Au</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">19.300000000000001</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="coatingFile" absolute="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" absolute="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.98480775301220802</y>
+ <z>-0.17364817766693033</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.17364817766693033</y>
+ <z>0.98480775301220802</z>
+ </param>
+ </object>
+
+
+ Mandatory:
+Cylinder:
+Cutout:
+To track a Cylindrical Mirror using an RML File, you'll require an XML Object to encompass all the mirror details. The default mirror setup from RAY-UI is provided here for your convenience. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Cylinder" type="Cylinder">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="grazingIncAngle" enabled="T">10</param>
+ <param id="entranceArmLength" enabled="T">10000</param>
+ <param id="exitArmLength" enabled="T">1000</param>
+ <param id="bendingRadius" comment="Long Radius R" enabled="T">0</param>
+ <param id="radius" auto="T" enabled="T">10470.4917875</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="materialSubstrate" enabled="F">Au</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="coatingFile" absolute="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" absolute="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.98480775301220802</y>
+ <z>-0.17364817766693033</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.17364817766693033</y>
+ <z>0.98480775301220802</z>
+ </param>
+ </object>
+
+
+ Mandatory:
+Ellipoid:
+Cutout:
+To track a Ellipsoid Mirror using an RML File, you'll require an XML Object to encompass all the mirror details. The default mirror setup from RAY-UI is provided here for your convenience. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Ellipsoid" type="Ellipsoid">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="grazingIncAngle" enabled="T">10</param>
+ <param id="entranceArmLength" enabled="T">10000</param>
+ <param id="exitArmLength" enabled="T">1000</param>
+ <param id="designGrazingIncAngle" auto="T" enabled="T">10</param>
+ <param id="longHalfAxisA" auto="T" enabled="T">5500</param>
+ <param id="shortHalfAxisB" auto="T" enabled="T">549.12375296508355</param>
+ <param id="figureRotation" comment="Yes" enabled="T">0</param>
+ <param id="parameter_a11" enabled="F">1</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="reflectivityType" comment="Derived by Material" enabled="T">1</param>
+ <param id="materialSubstrate" enabled="T">Au</param>
+ <param id="roughnessSubstrate" enabled="T">0</param>
+ <param id="densitySubstrate" auto="T" enabled="T">19.300000000000001</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="T">0</param>
+ <param id="coatingFile" absolute="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="misalignmentCoordinateSystem" comment="Ellipsoid" enabled="T">0</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" absolute="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.98480775301220802</y>
+ <z>-0.17364817766693033</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.17364817766693033</y>
+ <z>0.98480775301220802</z>
+ </param>
+ </object>
+
+
+ Optical elements are a central part of the tracing process. They define how light is guided through the beamline - where it is focused, redirected, or scattered. +In the context of RAYX a beamline consists of two types of elements:
+In this section, you can read up on the design choices we made when implementing OpticalElements in RAYX.
+Next to its position and orientation, an OpticalElement is classified by three parts: The Behaviour, Surface and Cutout.
+Behavior determines what happens to a ray once it hits the OpticalElement. +Such a "hit" might result in absorbtion, reflection or the ray might pass through the OpticalElement. +Typical examples of Behaviours are Mirror, Grating, Slit and RZP.
+Behaviours are defined in the Shared/Behaviour.h file. +Each Behaviour has a "behave" function (eg. behaveMirror, behaveSlit, ...) that translates the incoming ray to the outgoing ray. +These functions are gathered in the behave.comp file.
+Surfaces in RAYX are defined as either a plane, a quadric, or a toroid. +We use mathematical formulas to represent them internally, which means they are not necessarily bounded in size. +Optical elements are often subtly curved; to the human eye, they might appear indistinguishable from planar elements.
+There are two ways to describe the reflectivity of the Surface. The User can choose between reflectivity Type '100%' and 'derived by material'. +If you want the second option you need to specify the following parameter:
+An example of the RML input looks like this:
+ <param id="reflectivityType" comment="Derived by Material" enabled="T">1</param>
+ <param id="materialSubstrate" enabled="T">Au</param>
+ <param id="roughnessSubstrate" enabled="T">0</param>
+ <param id="densitySubstrate" auto="T" enabled="T">19.300000000000001</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="T">0</param>
+ <param id="coatingFile" absolute="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+
+The cutout defines the boundaries of the OpticalElement, by cutting a shape out of the Surface. +As the surfaces of OpticalElements often only slightly differ from the XZ plane, we implement Cutouts by a simple 2D shape applied to the coordinates X and Z.
+Cutouts come in different shapes:
+The central function is the bool inCutout(Cutout cutout, double x, double z);
.
+A given 3D point p
is within the cutout c, if inCutout(c, p.x, p.z)
returns true
.
Not all OpticalElements use exactly one Cutout. +The Slit for example uses three Cutouts, one for the ray-absorbing shape around the "opening", then one for the "opening" itself, and another one for the ray-absorbing beamstop within the opening.
+When checking whether a ray collides with an OpticalElement, we first convert the Ray to the element coordinate system of the ray. +This makes (0, 0, 0) the center of the element, which generally lies in the XZ plane. +Rays then come from negative or positive y.
+We then ask the Surface of our OpticalElement for a hitpoint using the findCollisionWith
function.
+And finally, if this hitpoint is in the cutout, we have found a collision.
In the following image, you can see a visualization of how the surface and cutout interact. +The surface is a quadric that defines a sphere. The cutout is a rectangle, defined by points \(A\), \(B\), \(C\), and \(D\).
+ +Coupled with the icurv
parameter, the cutout is mapped to the correct side of the sphere, visualized by points \(A_1\), \(B_1\), \(C_1\), and \(D_1\).
+If a ray intersects the element's surface within the bounds of the cutout, it will be counted as a hit.
+This is, where the behaviour comes into play to calculate the continuing path of the ray.
The cutout itself does not have a position; it is always at the origin of the element's coordinate system.
+For some quadrics, this rule might not adequately define the position.
+Therefore, we use the icurv
parameter to determine whether the quadric is concave or convex.
+This suffices since we calculate all intersection points with elements.
+When two intersections occur, the icurv
parameter informs us which intersection point to select.
Mandatory:
+Paraboloid:
+Cutout:
+To track a Paraboloid Mirror using an RML File, you'll require an XML Object to encompass all the mirror details. The default mirror setup from RAY-UI is provided here for your convenience. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Paraboloid" type="Paraboloid">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="secondSurface" comment="on" enabled="T">1</param>
+ <param id="grazingIncAngle" enabled="T">10</param>
+ <param id="armLength" enabled="T">10000</param>
+ <param id="parameter_P_type" comment="focusing" enabled="T">1</param>
+ <param id="parameter_P" auto="T" enabled="T">-603.0737921409161</param>
+ <param id="figureRotation" comment="Yes" enabled="T">0</param>
+ <param id="parameter_a11" enabled="F">1</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">10</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="materialSubstrate" enabled="F">Au</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="coatingFile" absolute="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="misalignmentCoordinateSystem" comment="Paraboloid" enabled="T">0</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>10000.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>0.9848077530122080</x>
+ <y>0.1736481776669303</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>-0.1736481776669303</x>
+ <y>0.9848077530122080</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>1.0000000000000000</z>
+ </param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" absolute="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ </object>
+
+
+ A Plane Grating is amongst the simplest Elements you can include in your beamline. The Surface is flat and it reflects light like an optical grating.
+Mandatory:
+Grating:
+Cutout:
+To track a Plane Grating using an RML File, you'll require an XML Object to encompass all the grating details. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Plane Grating" type="Plane Grating">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="gratingMount" comment="constant deviation" enabled="T">0</param>
+ <param id="systemMount" comment="standalone, none" enabled="T">0</param>
+ <param id="deviationAngle" enabled="T">10</param>
+ <param id="halfConeAngle" enabled="F">10</param>
+ <param id="pimpaleX0" enabled="F">10000</param>
+ <param id="pimpaleY0" enabled="F">10</param>
+ <param id="premirrorMountPsi0" enabled="F">0</param>
+ <param id="designEnergyMounting" auto="T" enabled="T">100</param>
+ <param id="lineDensity" enabled="T">1000</param>
+ <param id="orderDiffraction" enabled="T">1</param>
+ <param id="cFactor" enabled="F">2</param>
+ <param id="alpha" auto="T" enabled="T">5.35655050894</param>
+ <param id="beta" auto="T" enabled="T">-4.64344949106</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="entranceArmLength" enabled="F">10000</param>
+ <param id="lineSpacing" comment="constant" enabled="T">0</param>
+ <param id="vlsParameterB2" enabled="F">0</param>
+ <param id="vlsParameterB3" enabled="F">0</param>
+ <param id="vlsParameterB4" enabled="F">0</param>
+ <param id="vlsParameterB5" enabled="F">0</param>
+ <param id="vlsParameterB6" enabled="F">0</param>
+ <param id="vlsParameterB7" enabled="F">0</param>
+ <param id="lineProfile" comment="unknown" enabled="F">3</param>
+ <param id="gratingEfficiency" enabled="F">0.5</param>
+ <param id="blazeAngle" enabled="F">4</param>
+ <param id="aspectAngle" enabled="F">90</param>
+ <param id="grooveDepth" enabled="F">10</param>
+ <param id="grooveRatio" enabled="F">0.65</param>
+ <param id="multilayerFourierCoefficients" auto="T" enabled="F">11</param>
+ <param id="multilayerIntegrationSteps" auto="T" enabled="F">50</param>
+ <param id="reflectivitySenkrecht" enabled="T">1</param>
+ <param id="reflectivityParallel" enabled="T">1</param>
+ <param id="reflectivityPhase" enabled="T">0</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="materialSubstrate" enabled="F">Au</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" relative="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.766044443118978</y>
+ <z>-0.6427876096865393</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.6427876096865393</y>
+ <z>0.766044443118978</z>
+ </param>
+ </object>
+
+
+ A Plane Mirror is amongst the simplest Elements you can include in your beamline. The Surface is flat and reflective as described by the material.
+Mandatory:
+Cutout:
+To track a Plane Mirror using an RML File, you'll require an XML Object to encompass all the mirror details. The default mirror setup from RAY-UI is provided here for your convenience. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Plane Mirror" type="Plane Mirror">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="grazingIncAngle" auto="T" enabled="T">40</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" auto="T" enabled="T">0</param>
+ <param id="systemMount" comment="standalone" enabled="T">0</param>
+ <param id="premirrorShiftZ" enabled="F">0</param>
+ <param id="pimpaleAlpha1" enabled="F">1</param>
+ <param id="pimpaleAlpha2" enabled="F">2</param>
+ <param id="pimpaleAlpha3" enabled="F">3</param>
+ <param id="distancePremirrorGrating" enabled="T">0</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="elementSubstrate" enabled="F">Au</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="coatingFile" relative="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" relative="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.766044443118978</y>
+ <z>-0.6427876096865393</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.6427876096865393</y>
+ <z>0.766044443118978</z>
+ </param>
+ </object>
+
+
+ Information about RZP
+Literature:
+PhD_JensRehanek_2014.pdf
+PhD_HeikeLöchel_2016.pdf
Mandatory:
+RZP:
+Cutout:
+If you want to follow a RZP using an RML File, you'll need an XML Object that contains all the RZP details. The default RZP from RAY-UI is available for use. Just remember, tracing only works if you set up a Light Source first. It's a good idea to add an Imageplane Object at the end for clearer results.
+ <object name="Reflection Zoneplate" type="Reflection Zoneplate">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="gratingMount" comment="constant incidence angle" enabled="T">1</param>
+ <param id="grazingIncAngle" auto="T" enabled="T">1</param>
+ <param id="deviationAngle" enabled="F">170</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="elementOffsetZType" comment="manual" enabled="T">0</param>
+ <param id="elementOffsetZ" enabled="T">0</param>
+ <param id="meridionalIncidenceBeamDivergence" enabled="F">0</param>
+ <param id="meridionalIncidenceFocusDistance" enabled="F">0</param>
+ <param id="orderDiffraction" auto="T" enabled="T">-1</param>
+ <param id="betaDiffraction" auto="T" enabled="T">1</param>
+ <param id="derivationMethod" comment="Formulas" enabled="T">0</param>
+ <param id="coefficientsFile" relative="" enabled="F"></param>
+ <param id="designEnergy" enabled="T">100</param>
+ <param id="designOrderDiffraction" auto="T" enabled="T">-1</param>
+ <param id="entranceArmLengthSag" enabled="T">100</param>
+ <param id="entranceArmLengthMer" enabled="T">100</param>
+ <param id="designAlphaAngle" enabled="T">1</param>
+ <param id="exitArmLengthSag" enabled="T">500</param>
+ <param id="exitArmLengthMer" enabled="T">500</param>
+ <param id="curvatureType" comment="planar" enabled="T">0</param>
+ <param id="longRadius" enabled="F">0</param>
+ <param id="shortRadius" enabled="F">0</param>
+ <param id="designType" comment="use Fresnel Center Offset" enabled="T">0</param>
+ <param id="FresnelZOffset" enabled="T">0</param>
+ <param id="designBetaAngle" enabled="F">1</param>
+ <param id="imageType" comment="point to point" enabled="T">0</param>
+ <param id="stretchXdirection" enabled="F">1</param>
+ <param id="rzpType" comment="elliptical (standard)" enabled="T">0</param>
+ <param id="zDcalc" enabled="T">0</param>
+ <param id="xDcalc" enabled="T">0</param>
+ <param id="Dz" enabled="T">0</param>
+ <param id="Dx" enabled="T">0</param>
+ <param id="refracMethod" comment="2D (new)" enabled="T">1</param>
+ <param id="additionalOrder" comment="off" enabled="T">0</param>
+ <param id="lineProfile" comment="unknown" enabled="T">3</param>
+ <param id="fullEfficiency" comment="off" enabled="T">0</param>
+ <param id="gratingEfficiency" enabled="T">0.5</param>
+ <param id="blazeAngle" enabled="F">4</param>
+ <param id="aspectAngle" enabled="F">90</param>
+ <param id="grooveDepth" enabled="F">10</param>
+ <param id="grooveRatio" enabled="F">0.65</param>
+ <param id="multilayerFourierCoefficients" auto="T" enabled="F">11</param>
+ <param id="multilayerIntegrationSteps" auto="T" enabled="F">50</param>
+ <param id="reflectivityType" comment="Derived by Material" enabled="T">1</param>
+ <param id="materialSubstrate" enabled="T">Au</param>
+ <param id="roughnessSubstrate" enabled="T">0</param>
+ <param id="densitySubstrate" auto="T" enabled="T">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="T">0</param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" relative="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.99984769515639127</y>
+ <z>-0.017452406437283512</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.017452406437283512</y>
+ <z>0.99984769515639127</z>
+ </param>
+ </object>
+
+
+ TBA
+Mandatory:
+Sphere:
+Grating:
+Cutout:
+To track a Spherical Grating using an RML File, you'll require an XML Object to encompass all the grating details. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Spherical Grating" type="Spherical Grating">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="gratingMount" comment="constant deviation" enabled="T">0</param>
+ <param id="deviationAngle" enabled="T">10</param>
+ <param id="entranceArmLength" enabled="T">10000</param>
+ <param id="exitArmLength" auto="T" enabled="T">1000</param>
+ <param id="radius" auto="T" enabled="T">1825.126977351541</param>
+ <param id="designEnergy" auto="T" enabled="T">100</param>
+ <param id="lineDensity" enabled="T">1000</param>
+ <param id="orderDiffraction" enabled="T">1</param>
+ <param id="alpha" auto="T" enabled="T">5.356547628646595</param>
+ <param id="beta" auto="T" enabled="T">-4.643452371353405</param>
+ <param id="lineSpacing" comment="constant" enabled="T">0</param>
+ <param id="vlsParameter1" enabled="F">0</param>
+ <param id="vlsParameter2" enabled="F">0</param>
+ <param id="vlsParameter3" enabled="F">0</param>
+ <param id="vlsParameter4" enabled="F">0</param>
+ <param id="vlsParameter5" enabled="F">0</param>
+ <param id="vlsParameter6" enabled="F">0</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="lineProfile" comment="unknown" enabled="T">3</param>
+ <param id="gratingEfficiency" enabled="T">0.5</param>
+ <param id="blazeAngle" enabled="F">4</param>
+ <param id="aspectAngle" enabled="F">90</param>
+ <param id="grooveDepth" enabled="F">10</param>
+ <param id="grooveRatio" enabled="F">0.65</param>
+ <param id="multilayerFourierCoefficients" auto="T" enabled="F">11</param>
+ <param id="multilayerIntegrationSteps" auto="T" enabled="F">50</param>
+ <param id="reflectivityType" comment="Derived by Material" enabled="T">1</param>
+ <param id="materialSubstrate" enabled="T">Au</param>
+ <param id="roughnessSubstrate" enabled="T">0</param>
+ <param id="densitySubstrate" auto="T" enabled="T">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="T">0</param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>10000.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0933532651278599</y>
+ <z>-0.9956330488136518</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.9956330488136518</y>
+ <z>0.0933532651278599</z>
+ </param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" absolute="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ </object>
+
+
+ Mandatory:
+Sphere:
+Cutout:
+To track a Sphere Mirror using an RML File, you'll require an XML Object to encompass all the mirror details. The default mirror setup from RAY-UI is provided here for your convenience. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="SphereMirrorDefault" type="Sphere">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="grazingIncAngle" enabled="T">10</param>
+ <param id="entranceArmLength" enabled="T">10000</param>
+ <param id="exitArmLength" enabled="T">1000</param>
+ <param id="radius" auto="T" enabled="T">10470.4917875</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="elementSubstrate" enabled="T">Cu</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">8.94</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="coatingFile" relative="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" relative="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.98480775301220802</y>
+ <z>-0.17364817766693033</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.17364817766693033</y>
+ <z>0.98480775301220802</z>
+ </param>
+ </object>
+
+
+ TBA
+Mandatory:
+Toroid:
+Grating:
+Cutout:
+To track a Toroid Grating using an RML File, you'll require an XML Object to encompass all the grating details. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Toroidal Grating" type="Toroidal Grating">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="gratingMount" comment="constant deviation" enabled="T">0</param>
+ <param id="deviationAngle" enabled="T">10</param>
+ <param id="entranceArmLengthSag" enabled="T">10000</param>
+ <param id="exitArmLengthSag" enabled="T">1000</param>
+ <param id="entranceArmLengthMer" enabled="T">10000</param>
+ <param id="exitArmLengthMer" enabled="T">1000</param>
+ <param id="longRadius" auto="T" enabled="T">1823.537175159428</param>
+ <param id="shortRadius" auto="T" enabled="T">1811.228017099299</param>
+ <param id="designEnergy" auto="T" enabled="T">100</param>
+ <param id="lineDensity" enabled="T">1000</param>
+ <param id="orderDiffraction" enabled="T">1</param>
+ <param id="alpha" auto="T" enabled="T">5.356547628646594</param>
+ <param id="beta" auto="T" enabled="T">-4.643452371353406</param>
+ <param id="lineSpacing" comment="constant" enabled="T">0</param>
+ <param id="vlsParameter1" enabled="F">0</param>
+ <param id="vlsParameter2" enabled="F">0</param>
+ <param id="vlsParameter3" enabled="F">0</param>
+ <param id="vlsParameter4" enabled="F">0</param>
+ <param id="vlsParameter5" enabled="F">0</param>
+ <param id="vlsParameter6" enabled="F">0</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="lineProfile" comment="unknown" enabled="T">3</param>
+ <param id="gratingEfficiency" enabled="T">0.5</param>
+ <param id="blazeAngle" enabled="F">4</param>
+ <param id="aspectAngle" enabled="F">90</param>
+ <param id="grooveDepth" enabled="F">10</param>
+ <param id="grooveRatio" enabled="F">0.65</param>
+ <param id="reflectivityType" comment="Derived by Material" enabled="T">1</param>
+ <param id="materialSubstrate" enabled="T">Au</param>
+ <param id="roughnessSubstrate" enabled="T">0</param>
+ <param id="densitySubstrate" auto="T" enabled="T">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="T">0</param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>10000.0000000000000000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1.0000000000000000</x>
+ <y>0.0000000000000000</y>
+ <z>0.0000000000000000</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.0933532651278599</y>
+ <z>-0.9956330488136518</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0.0000000000000000</x>
+ <y>0.9956330488136518</y>
+ <z>0.0933532651278599</z>
+ </param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" absolute="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ </object>
+
+
+ TBA
+Mandatory:
+Toroid:
+Cutout:
+To track a Toroid Mirror using an RML File, you'll require an XML Object to encompass all the mirror details. The default mirror setup from RAY-UI is provided here for your convenience. Remember, successful tracing requires you to specify a Light Source first. We suggest adding an Imageplane at the end for clearer results.
+ <object name="Toroid" type="Toroid">
+ <param id="geometricalShape" comment="rectangle" enabled="T">0</param>
+ <param id="totalWidth" enabled="T">50</param>
+ <param id="totalLength" enabled="T">200</param>
+ <param id="grazingIncAngle" enabled="T">10</param>
+ <param id="entranceArmLengthSag" enabled="T">10000</param>
+ <param id="exitArmLengthSag" enabled="T">1000</param>
+ <param id="entranceArmLengthMer" enabled="T">10000</param>
+ <param id="exitArmLengthMer" enabled="T">1000</param>
+ <param id="longRadius" auto="T" enabled="T">10470.4917875</param>
+ <param id="shortRadius" auto="T" enabled="T">315.723959394</param>
+ <param id="distancePreceding" enabled="T">10000</param>
+ <param id="azimuthalAngle" enabled="T">0</param>
+ <param id="reflectivityType" comment="100%" enabled="T">0</param>
+ <param id="materialSubstrate" enabled="F">Au</param>
+ <param id="roughnessSubstrate" enabled="F">0</param>
+ <param id="densitySubstrate" auto="T" enabled="F">19.3</param>
+ <param id="surfaceCoating" comment="Substrate only" enabled="F">0</param>
+ <param id="coatingFile" absolute="" enabled="F"></param>
+ <param id="numberLayer" enabled="F">2</param>
+ <param id="materialCoating1" enabled="F"></param>
+ <param id="thicknessCoating1" enabled="F">0</param>
+ <param id="roughnessCoating1" enabled="F">0</param>
+ <param id="densityCoating1" auto="T" enabled="F">0</param>
+ <param id="materialCoating2" enabled="F"></param>
+ <param id="thicknessCoating2" enabled="F">0</param>
+ <param id="roughnessCoating2" enabled="F">0</param>
+ <param id="densityCoating2" auto="T" enabled="F">0</param>
+ <param id="materialTopLayer" enabled="F"></param>
+ <param id="thicknessTopLayer" enabled="F">0</param>
+ <param id="roughnessTopLayer" enabled="F">0</param>
+ <param id="densityTopLayer" auto="T" enabled="F">0</param>
+ <param id="lateralThicknessGradientCoating1" comment="No" enabled="F">0</param>
+ <param id="gradientC1B1" enabled="F">0</param>
+ <param id="gradientC1B2" enabled="F">0</param>
+ <param id="gradientC1B3" enabled="F">0</param>
+ <param id="gradientC1B4" enabled="F">0</param>
+ <param id="gradientC1B5" enabled="F">0</param>
+ <param id="gradientC1B6" enabled="F">0</param>
+ <param id="gradientC1B7" enabled="F">0</param>
+ <param id="gradientC1B8" enabled="F">0</param>
+ <param id="alignmentError" comment="No" enabled="T">1</param>
+ <param id="translationXerror" enabled="F">0</param>
+ <param id="translationYerror" enabled="F">0</param>
+ <param id="translationZerror" enabled="F">0</param>
+ <param id="rotationXerror" enabled="F">0</param>
+ <param id="rotationYerror" enabled="F">0</param>
+ <param id="rotationZerror" enabled="F">0</param>
+ <param id="slopeError" comment="No" enabled="T">1</param>
+ <param id="profileKind" comment="no Profile" enabled="F">2</param>
+ <param id="profileFile" absolute="" enabled="F"></param>
+ <param id="slopeErrorSag" enabled="F">0</param>
+ <param id="slopeErrorMer" enabled="F">0</param>
+ <param id="thermalDistortionAmp" enabled="F">0</param>
+ <param id="thermalDistortionSigmaX" enabled="F">0</param>
+ <param id="thermalDistortionSigmaZ" enabled="F">0</param>
+ <param id="cylindricalBowingAmp" enabled="F">0</param>
+ <param id="cylindricalBowingRadius" enabled="F">0</param>
+ <param id="worldPosition" enabled="F">
+ <x>0</x>
+ <y>0</y>
+ <z>10000</z>
+ </param>
+ <param id="worldXdirection" enabled="F">
+ <x>1</x>
+ <y>0</y>
+ <z>0</z>
+ </param>
+ <param id="worldYdirection" enabled="F">
+ <x>0</x>
+ <y>0.98480775301220802</y>
+ <z>-0.17364817766693033</z>
+ </param>
+ <param id="worldZdirection" enabled="F">
+ <x>0</x>
+ <y>0.17364817766693033</y>
+ <z>0.98480775301220802</z>
+ </param>
+ </object>
+
+
+ wiki for efficiency calculations
+A fraction of the light is reflected and another transmitted:
+
+\(\theta_i =\) (normal) incidence angle
+\(\theta_r =\) (normal) reflection angle (same as \(\theta_i\))
+\(\theta_t =\) (normal) transmittance angle
+\(N_1 =\) refraction index of material from which the ray is coming (left in image)
+\(N_2 =\) refraction index of material into which the ray is going (right in image)
all parameters are potentially complex numbers. The refractive indices are retrieved from files (Palik, Henke, Cromer..)
+Snell's law: +\[ +N_1 \sin \theta_i = N_2 \sin \theta_t \rightarrow \sin \theta_t = \frac{N_1}{N_2} \sin \theta_i +\]
+\(\theta_i\), \(N_1\), \(N_2\) are known, we are looking for \(\theta_t\).
+We do not calculate the angle specifically but only the cosinus, which is sufficient for further calculations and more efficient/precise than calculating the angle itself because we do not need to use more trigonometric functions.
+We can calculate the incidence angle \(\theta_i\) of each ray from its direction and the surface normal. Then we calculate \(\cos(\theta_i)\) and from that we can derive \(\cos(\theta_t)\) with snell's law:
\[ +(\sin \theta_i)^2 = 1 - (\cos \theta_i)^2 \\ +(\sin \theta_t)^2 = (\frac{N_1}{N_2})^2 (\sin \theta_i)^2 \\ +\cos \theta_t = \sqrt{1 - (\sin \theta_t)^2} = \sqrt{1 - \Big(\frac{N_1}{N_2} \sin \theta_i\Big)^2} +\]
+The cosine of both angles is then used in the Fresnel equations to calculate the s- and p-polarization
+Any polarization state can be described by two components: one vertical and one horizontal. Or - relative to the plane of incidence - s- and p-polarization. +p-polarization (parallel, left image) lies parallel in the plane of incidence and s-polarization (senkrecht, right image) is orthogonal to the plane of incidence.
+ + +the reflectance of both polarizations is calculated with the fresnel equations:
+\[r_s = \frac{N_1 \cdot \cos \theta_i - N_2 \cdot \cos \theta_t}{N_1 \cdot \cos \theta_i + N_2 \cdot \cos \theta_t}\] +\[r_p = \frac{N_2 \cdot \cos \theta_i - N_1 \cdot \cos \theta_t}{N_2 \cdot \cos \theta_i + N_1 \cdot \cos \theta_t}\]
+(The transmitted power is then "the rest": \(t_s = 1 - r_s\) and \(t_p = 1 -r_p\))
+ +When the tracing process is finished, our Tracer does not only return the final state of the rays; +additionally it provides us with a sequence of events - expressing what happened to each ray individually. +This allows us to fully understand what happened to each ray, and potentially where in the beamline it diverged from our expectations.
+Each of these events contains a "snapshot" of the ray at that point in time, +and information about what happened at this point (encoded as an "EventType").
+Some typical events include (for more, see the Doxygen documentation):
+Some EventTypes "finalize" the corresponding ray, preventing it from being processed further. +Examples for this are "Fly Off" and "Absorbed".
+For more details see the Shared/EventType.h file.
+ +In this section you can find formulas, methods and ideas used to +develop RAYX.
+ +For RAYX we found a good and performant pseudo random number generator after some research. We cannot use default C++ options as they are not supported by our Shader code. We decided to use Squares RNG, which is counter based and utilizes a version of the Middle Square Weyl Sequence. We tested the method with the TestU01 bigcrush test with different seeds and it passed all of them.
+We added a few more methods for creating random numbers with more variety. These methods are:
+uint64_t squares64RNG(inout uint64_t ctr)
, which generates 64-Bit random integers from two 32-Bit random integersdouble squaresDoubleRNG(inout uint64_t ctr)
, which generates uniformly distributed doubles between 0 and 1 from one 64-Bit random integerdouble squaresNormalRNG(inout uint64_t ctr, double mu, double sigma)
, which creates (via the Box-Muller transform) a normal distributed double with mean mu
and standard deviation sigma
. This takes three random doulbes, which takes six 32-Bit integers.Function for calculating the intersection of a ray with the surface of an optical element in 3-dimensional space.
+general equation for second order surfaces: +\[F(x,y,z) = a_{11}x^2 + a_{22}y^2 + a_{33}z^2 + 2a_{12}xy + 2a_{13}xz + 2a_{23}yz + 2a_{14}x + 2a_{24}y + 2a_{34}z + a_{44}\]
+The intersection is determined by inserting the x, y and z-coordinates of the ray in $F(x,y,z)$ and set to zero:
+\[F(x_{S'}+t \cdot l_{S'} ,y_{S'}+t \cdot m_{S'}, z_{S'}+t \cdot n_{S'}) = 0\]
We obtain a quadratic equation of the form \(0 = a \cdot t^2 + b \cdot t + c\) with variable \(t\) and the following coefficients:
+\[ +\begin{align*} +a &= a_{11}l_{S'}^2 + a_{22}m_{S'}^2 + a_{33}n_{S'}^2 + 2a_{13}l_{S'}n_{S'} + 2a_{12}l_{S'}m_{S'} + 2a_{23}m_{S'}n_{S'} \\ +b &= 2a_{11}x_{S'}l_{S'} + 2a_{22}y_{S'}m_{S'} + 2a_{33}z_{S'}n_{S'} \\ +&+ 2a_{12}y_{S'}l_{S'} + 2a_{12}x_{S'}m_{S'} ++ 2a_{13}z_{S'}l_{S'} + 2a_{13}x_{S'}n_{S'} \\ +&+ 2a_{23}z_{S'}m_{S'} + 2a_{23}y_{S'}n_{S'} ++ 2a_{14}l_{S'} + 2a_{24}m_{S'} + 2a_{34}n_{S'} \\ +c &= a_{11}x_{S'}^2 + a_{22}y_{S'}^2 + a_{33}z_{S'}^2 + 2a_{12}x_{S'}y_{S'} + 2a_{13}x_{S'}z_{S'} + 2a_{23}y_{S'}z_{S'} \\ +&+ 2a_{14}x_{S'} + 2a_{24}y_{S'} + 2a_{34}z_{S'} + a_{44} +\end{align*} +\]
+Since a, b and c can simply be calculated, we could solve the equation directly with \(t = \frac{-b+ICURV \cdot \sqrt{b^2-4ac}}{2a}\) and use \(t\) to find the intersection point. If ICURV is negative, we get the first intersection point with the object (smaller \(t\)). If ICURV is positive we get the second intersection point from when the ray exits the element (larger \(t\)). Some optimizations are applied to this formula in the code. Depending on the largest component in the direction of the ray, it is normalized in x, y or z direction to simplify the ray equation. Thus, there are three cases.
+Assume \(l_{S'} \geq m_{S'}\) and \(l_{S'} \geq n_{S'}\) (first case). Then, we can divide the direction by \(l_{S'}\), such that it is normalized in x and the y- and z-coordinates are within \([-1,1]\) (I). Moreover, we can translate the origin of the ray along the direction vector towards the origin of the coordinate system until the y-z-plane is hit \((x=0, II)\). Then, we end up with a normalized ray (III), where \(x=t\).
+\[ +\begin{align*} +ray +&\overset{\text{I}}{=} \begin{bmatrix} x_{S'} \\ y_{S'} \\z_{S'} \\ \end{bmatrix} + t \begin{bmatrix} 1 \\ m_{S'}/l_{S'}\\ n_{S'}/l_{S'} \\ \end{bmatrix} \\ +&\overset{\text{II}}{=} \begin{bmatrix} x_{S'}-1\cdot x_{S'} \\ y_{S'} - (m_{S'}/l_{S'}) \cdot y_{S'} \\z_{S'} - (n_{S'}/l_{S'}) \cdot z_{S'} \\ \end{bmatrix} + t \begin{bmatrix} 1 \\ m_{S'}/l_{S'}\\ n_{S'}/l_{S'} \\ \end{bmatrix} \\ +&\overset{\text{III}}{=} \begin{bmatrix} 0 \\ y \\ z \end{bmatrix} + t \begin{bmatrix} 1 \\ a_{ml} \\ a_{nl} \\ \end{bmatrix} +\end{align*} +\]
+When we plug this parameterization of the ray into \(F(x,y,z)=0\), some terms in a, b and c are removed (see code).
+Since every term in b contains the factor 2, the equation for calculating t can be simplified: +\[ +t = \frac{-2\frac{b}{2} +ICURV \cdot \sqrt{(2\frac{b}{2})^2 - 4ac}}{2a} \\ = \frac{-2\frac{b}{2} +ICURV \cdot 2\sqrt{(\frac{b}{2})^2 - ac}}{2a} \\ = \frac{-\frac{b}{2} +ICURV \cdot \sqrt{(\frac{b}{2})^2 - ac}}{a} +\]
+In the code the factor 2 is left out of the equation for \(b\) from the beginning. Thus, what is called \(b\) in the code is actually \(\frac{b}{2}\).
+If the term in the root is negative there is no intersection and weight is set to 0 (or III to -4 in fortran). +Otherwise it is checked whether the factor a is much smaller than \(c\). Then, the divisor is very small or zero which can cause problems with the division. In that case, \(a \cdot t^2\) is removed from the quadratic equation \(a \cdot t^2 + b \cdot t + c\) which thus yields \(t=x=- \frac{c}{b}\) or \(t=x= \frac{c}{2b}\) in the code since the factor \(2\) is excluded from \(b\) (see also (Ray Quadric Intersection)). +If \(a\) is not much smaller than \(c\), \(t\) is calculated with the simplified equation above. Subsequently, \(t (=x)\) is plugged into the modified ray equation and the intersection point is calculated.
+The other two cases for when \(y\) or \(z\) are the largest component of the direction of the ray are similar.
+The partial derivatives of \(F(x,y,z)\) form the normal vector \(f_{x,y,z}\) of the surface. Inserting the calculated intersection point into the normal vector yields the normal of the surface at this specific point. The intersection point is set to be the new origin of the ray. The ray direction \((l_{S'},m_{S'},n_{S'})\) remains unchanged.
+The equation can describe the surface of several elements in a similar way as a circle with radius 1 can be described in 2D as \(x^2 + y^2 -1 = 0\). Elements that can be described by this general equation include sphere, ellipsoid, plane, cone, cylinder, paraboloid. These are so called quadric surfaces. Surfaces that cannot be described by any quadratic function include for example the torus. +The equation refers to a right-handed coordinate system with the center of the optical element in the origin. The element's surface is the x-z-plane and the y-axis is the normal vector.
+\[ +\begin{align*} +F(x,y,z) &= \vec{x}^TA\vec{x} \\ +\vec{x} = \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} +A &= \begin{bmatrix} a_{11} & a_{12} & a_{13} & a_{14} \\ +a_{21} & a_{22} & a_{23} & a_{24} \\ +a_{31} & a_{32} & a_{33} & a_{34} \\ +a_{41} & a_{42} & a_{43} & a_{44} +\end{bmatrix} \\ +a_{ij} = a_{ji} &\Rightarrow A^T = A +\end{align*} +\]
+Literature:
+https://en.wikipedia.org/wiki/Quadric
+https://www.win.tue.nl/~sterk/Bouwkunde/2db60-chap3.pdf
Rays are described in tree-dimensional space with an origin and a direction. +The origin are described with a three dimensional vector. However, the direction can be interpreted as the cosines of two angles \(\phi\) and \(\psi\). Since the z-axis is the direction of the center ray, the direction of all other rays can be described as the angle between ray direction vector and z-y-plane (horizontal divergence \(\phi\)) and between vector and z-x-plane (vertical divergence \(\psi\)). +See also documentation p.18,19.
+\[ +ray = \begin{bmatrix} +x_s \\ y_s \\ z_s +\end{bmatrix} + t +\begin{bmatrix} +l_S \\ m_S \\ n_S +\end{bmatrix} += \begin{bmatrix} +x_s \\ y_s \\ z_s +\end{bmatrix} + t +\begin{bmatrix} +sin \phi_S \cdot cos \psi_S \\ cos \psi_S \\ cos \psi_S \cdot cos \phi_S +\end{bmatrix} +\]
+When the ray is created in the light source, its origin and direction angles are chosen randomly.
+The user sets the following parameters:
+The intensity distribution in the lightsource is understood as the probability distribution of the parameters position and angle. +The parameters \(x\), \(y\) and \(z\) of position and the angles \(\phi\) and \(\psi\) are chosen randomly but according to a probability distribution. Uniformly distributed random numbers within the range \([0,1]\) are obtained by FORTRAN's random_number function. +There are two options for the probability distribution in the light source: +soft edge (Gaussian) and hard edge (uniformly). +Afterwards, some predefined offset can be added to each component.
+The hard edge option is the simpler one since the random numbers are already uniformly distributed. Thus, the ray parameters \(x\), \(y\), \(z\), \(\phi\), \(\psi\) are chosen uniformly within the given extent (width, height, divergence..) of the point source by calculating a random number \(u\), subtracting 0.5 such that the number ranges in \([-0.5,+0.5]\) and multiplying with the given width/height/depth or horizontal/vertical extent of the point source.
+\(x_S = (u_x - 0.5) \cdot width\)
+\(y_S = (u_y - 0.5) \cdot height\)
+\(z_S = (u_z - 0.5) \cdot depth\)
+\(\phi_S = (u_\phi - 0.5) \cdot hor. div\)
+\(\psi_S = (u_\psi - 0.5) \cdot vert. div\)
Thus, the x-coordinate of the origin, for example, lies within \([-\frac{width}{2}, +\frac{width}{2}]\) and is picked uniformly.
+(See also documentation p.14)
+For the soft edge we have to transform the uniform distribution to a Gaussian distribution with \(\mu = 0\) and \(\sigma = extent\). The resulting values for a specific ray are not bound by the input parameters as for the hard edge. Instead, the more they deviate from \(\mu\) the more rare they are.
+The old implementation uses the following apporach:
+Here, 9 is used as a factor for \(\sigma\), since the Gaussian distribution is defined from \(-\infty\) to \(+\infty\) but a confidence interval \([-4.5 \sigma, +4.5 \sigma]\) contains already more than 99.99%.
+\(w(x_s)\) lies in \([0,1]\). It is \(1\) for \(x_s = 0\) and becomes smaller the more \(x_s\) deviates from \(0\) according to the gaussian bell curve. +Since \(w(x_s)\) is the probability that \(x_s\) occurs in our gaussian distribution, we want to use \(x_s\) for the origin of the ray with a probability of \(w(x_s)\). Thus, we get a second random number \(u_x'\) from FORTRAN's random_number function and if \(u_x' \leq w(x_s)\) holds we use \(x_S\) for the ray. Otherwise, \(x_S\) is discarded and re-calculated with a new random number \(u_x\). Thereby, we use \(x_S\) with a probability that corresponds to the Gaussian distribution.
+However, this is not very efficient as a lot of rays are thrown away. The following method uses a different approach which - although some values are discarded as well - is more efficient and creates two values in one iteration instead of one (more here Section 5.5.2):
+Now \(z_1\) and \(z_2\) are distributed according to the Gaussian distribution with \(\mu = 0\) and \(\sigma = extent\) as defined by the user.
+This calculation is faster since we get 2 values instead of 1. +(In python the first method took twice as long as this method to create the same amount of values)
+ +This explains how to get world coordinates (global position and orientation of an optical element) from the sequential setup of a beamline that is often used, where the position and orientation of an element is described with distances and rotations relative to its predecesor.
+First a small introduction to rotation matrices which is not super relevant for the transformations but might help to visualize the rotations and understand why some angles are positive and others negative.
+A rotation through an angle \(\theta\) can either be active or passive. +An active rotation around for example the z-axis through the angle \(\theta\) rotates the point within the coordinate system. Thereby, the coordinates of the point are changed whereas the coordinate system is left unchanged. When looking along the rotation axis in a right-handed coordinate system towards the origin, the rotation direction is counter-clockwise.
+\[ +R_a({\theta}) = +\begin{bmatrix} +\cos(\theta) & -\sin(\theta) & 0 \\ +\sin(\theta) & \cos(\theta) & \\ +0 & 0 & 1 +\end{bmatrix} +\]
+A passive rotation leaves the position of the vector unchanged and rotates the axes of the coordinate system relative to the vector i.e. rotates the basis vectors (change of basis). When looking along the rotation-axis towards the origin in a right-handed coordinate system, the rotation of the rotating axes is clockwise. Thus, it is defined as an active rotation (applied to the basis vectors) in the other direction i.e. through the negative angle (\(\cos(-\theta) = \cos(\theta)\) and \(-\sin(\theta) = \sin(-\theta))\):
+\[
+R_p({\theta}) = \begin{bmatrix}
+\cos(\theta) & \sin(\theta) & 0 \\
+-\sin(\theta) & \cos(\theta) & \\
+0 & 0 & 1
+\end{bmatrix} = \begin{bmatrix}
+\cos(-\theta) & -\sin(-\theta) & 0 \\
+\sin(-\theta) & \cos(-\theta) & \\
+0 & 0 & 1
+\end{bmatrix}
+\]
The relation between axes and the position of the point are the same after each of the rotations: After the passive rotation the basis vectors are different and the vector coordinates stay the same whereas after the active rotation the vector coordinates are different but the basis vectors are the same.
+Example for active (left) and passive (right) rotation through \(\alpha=25^\circ\): + +The relative position of the vector to the axes is the same after each rotation. +See also active vs passive transformation
+However, active and passive are in our case only an interpretation of the rotations that makes sense when looking at the beamline from a global point of view. Globally seen, the local coordinate system of each optical element is rotated and translated differently with respect to a global coordinate system whereas the vectors (the rays) only change by e.g. reflection when interacting with an element. +Thus, we have coordinate systems for optical elements, that are identical for each element in the sense that the y-axis is the normal and the x-z-plane is the tangent plane of the surface at the origin, and for rays where the center ray is the z-axis and a global system. The transformation between the systems is implemented by rotating and translating the vectors within the same coordinate system. +\(\rightarrow\) In the implementation only the vectors are transformed by active transformations and the axes of the coordinate system stay the same, although in the "real world", the beams remain unchanged and only the coordinate system is rotated around them.
+The following sections describe how to calculate the transformation matrices from beam coordinates to element coordinates and again to (new) beam coordinates based on the given distance and angles in the sequential setup. +This is relevant for RAYX if you need to calculate the world coordinates from the user parameters that describe the sequential setup yourself and cannot directly use the world coordinates from the rml file:
+In the RAY-UI rays are represented in a beam coordinate system. In that system the main ray always points from the origin towards the z-axis wheras the individual rays have slight deviations in their direction and origin.
+Elements are represented in an element coordinate system. +The elements are mostly located in the x-z-plane of their coordinate system. The y-axis is the normal in the center of the element (Visualized in the documentation).
+In order to calculate the intersection point with the "quad" function, we first need to transfer the incoming rays from beam coordinates to the object coordinates. The relation between these system is defined by two angles \(\alpha\) and \(\chi\) and a translation by \(z_0\). The transformation affects the position and direction of the ray.
+the offset \(z_0\) describes the distance between the previous element or the source and the current optical element.
+the main ray should have a specific incidence angle \(\alpha\) (angle between main ray and x-z-plane of the optical element). This rotation is counter-clockwise around the x-axis:
+\[ +R_x({\alpha}) = \begin{bmatrix} +1 & 0 & 0 \\ +0 & \cos(\alpha) & -\sin(\alpha) \\ +0 & \sin(\alpha) & \cos(\alpha) +\end{bmatrix} +\]
+After tracing we need to transform the ray back to the beam coordinate system. Therefore we rotate back around \(\chi\) and then rotate around the exit angle \(\beta\) All these parameters are given as "user" parameters. The following section describes how to calculate beam-element and element-beam transformation matrices.
+Translation by \(z_0\) in z direction = distance to preceeding element
+Rotation by azimuthal angle \(\chi\) around z-axis.
+\[ +R_z(-\chi) = \begin{bmatrix} \cos(-\chi) & -\sin(-\chi) & 0 \\ +\sin(-\chi) & \cos(-\chi) & 0 \\ +0 & 0 & 1 \end{bmatrix} = +\begin{bmatrix} \cos(\chi) & \sin(\chi) & 0 \\ +-\sin(\chi) & \cos(\chi) & 0 \\ +0 & 0 & 1 \end{bmatrix} +\]
+\[ +R_x(\alpha) = \begin{bmatrix} 1 & 0 & 0 \\ +0 & \cos(\alpha) & -\sin(\alpha) \\ +0 & \sin(\alpha) & \cos(\alpha) \end{bmatrix} +\]
+Putting it all together this is an affine transformation and can be written in homogeneous coordinates as one single matrix:
+\[ +\begin{align*} +M_{b2e} &= R_{x}(\alpha) R_z(-\chi) T_z(z_0) \\ +M_{b2e} &= \begin{bmatrix} 1 & 0 & 0 & 0\\ +0 & \cos(\alpha) & -\sin(\alpha) & 0 \\ 0 & \sin(\alpha) & \cos(\alpha) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} \cos(\chi) & \sin(\chi) & 0 & 0\\ -\sin(\chi) & \cos(\chi) & 0 & 0\\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & -z_0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \\ +&= \begin{bmatrix} \cos(\chi) & \sin(\chi) & 0 & 0 \\ +-\sin(\chi)\cos(\alpha) & \cos(\chi)\cos(\alpha) & -\sin(\alpha) & z_0 \sin(\alpha) \\ +-\sin(\chi) \sin(\alpha) & \sin(\alpha)\cos(\chi) & \cos(\alpha) & -z_0 \cos(\alpha) \\ 0 & 0 & 0 & 1 \end{bmatrix} +\end{align*} +\]
+After the interaction with the element, the reflected ray \(x_R\) is transformed back to a beam coordinate system. The rotations around the axes are applied in reverse order.
+\[ +R_x(\beta) = \begin{bmatrix} 1 & 0 & 0 \\ +0 & \cos(\beta) & -\sin(\beta) \\ 0 & \sin(\beta) & \cos(\beta) \end{bmatrix} +\]
+\[ +R_z(\chi) = R_z^{-1}(-\chi) = \begin{bmatrix} \cos(\chi) & -\sin(\chi) & 0 \\ +\sin(\chi) & \cos(\chi) & 0 \\ +0 & 0 & 1 \end{bmatrix} +\]
+In homogeneous coordinates:
+\[ +\begin{align*} +M_{e2b} &= R_z(\chi)R_{x}(\beta) \\ +M_{e2b} &= \begin{bmatrix} \cos(\chi) & -\sin(\chi) & 0 & 0\\ +\sin(\chi) & \cos(\chi) & 0 & 0\\ +0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & 0 & 0\\ +0 & \cos(\beta) & -\sin(\beta) & 0 \\ 0 & \sin(\beta) & \cos(\beta) & 0 \\ 0 & 0 & 0 & 1\end{bmatrix}\\ +&= \begin{bmatrix} \cos(\chi) & -\sin(\chi) \cos(\beta) & \sin(\chi)\sin(\beta) & 0 \\ +\sin(\chi) & \cos(\chi)\cos(\beta) & -\cos(\chi)\sin(\beta) & 0 \\ +0 & \sin(\beta) & \cos(\beta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} +\end{align*} +\]
+(Since there is no translation a 3x3 matrix would suffice)
+Misalignment is used when the optical element does not lie exactly where it should after applying the beam to element matrix. Therefore some rotation or translation might be necessary before the intersection point can be calculated.
+The misalignment transformation matrix \(M_{mis}\) is simply derived from the user parameters \(d_x\), \(d_y\), \(d_z\), \(d_{\phi}\), \(d_{\psi}\), \(d_{\chi}\). It can be calculated by spliting into a transformation matrix (from \(d_x\), \(d_y\), \(d_z\)) and multiplying with a rotation matrix (from \(d_{\phi}\), \(-d_{\psi}\), \(d_{\chi}\)):
+\[ +\begin{align*} +M_{mis} &= T_{x,y,z} R_{\phi, -\psi, \chi} \\ +&= \begin{bmatrix} 1 & 0 & 0 & -d_x\\ +0 & 1 & 0 & -d_y\\ +0 & 0 & 1 & -d_z \\ 0 & 0 & 0 & 1 \end{bmatrix} \cdot R^x_{-\psi} \cdot R^y_{\phi} \cdot R^z_{\chi} +\end{align*} +\]
+where e.g. \(R^x_{\psi}\) is the 4x4 homogeneous rotation matrix through \(\psi\) around the x-axis.
+The inverse misalignment matrix is then calculated as follows:
+\[ +\begin{align*} +M_{mis}^{-1} &= R_{-\psi, \phi, \chi}^{-1} \cdot T_{x,y,z}^{-1}\\ +&= (R^x_{-\psi} \cdot R^y_{\phi} \cdot R^z_{\chi}) ^{T} \cdot +\begin{bmatrix} +1 & 0 & 0 & d_x \\ +0 & 1 & 0 & d_y \\ +0 & 0 & 1 & d_z \\ +0 & 0 & 0 & 1 +\end{bmatrix} +\end{align*} +\]
+Since rotation matrices are orthogonal, the inverse of \((R^x_{-\psi} R^y_{\phi} R^z_{\chi})\) is the same as the transpose. The inverse of the translation matrix is the same but with negative offsets.
+\(M_{mis}\), \(M_{mis}^{-1}\) are multiplied with \(M_{b/g2e}\) and \(M_{e2g/b}\), respectively, to form the final transformation matrices which could be given to the shader if we would still use the sequential approach in RAYX. However, we use a global coordinate system instead of the beam coordinate system but don't worry you didn't just read all of that for nothing, it will be important in the derivation of the transformation from global to element coordinates and back.
+The next section describes how to replace the beam coordinate system that is used in the sequential approach with the global coordinate system and how to calculate the position and orientation.
+As explained in the previous sections, there is no global coordinate system in the sequential implementation but instead rays are transformed from beam coordinate system to element coordinate system and back to a different beam coordinate system such that the z-axis of the beam coordinate system always follows the main ray, which means that the main ray with \(pos=(0,0,0)\), \(dir=(0,0,1)\) in beam coordinates is always the same after each interaction with an optical element.
+In a global coordinate system this is different. The origin of the system is the (first) source. When the main ray hits the first element, it is transformed into the element's coordinate system, traced (e.g. reflected) and transformed back into the global coordinate system. Then it does no longer have the values \(pos=(0,0,0)\) and \(dir=(0,0,1)\). To achieve this for the first element (i=1) in the beamline, we can still use \(M_{b2e}\) that we defined previously \((M_{g2e}^{-1} = M_{b2e}^{-1})\) since for the first element the global coordinate system is the same as the beam coordinate system of the incoming rays (bc the source is in \((0,0,0)\) which is the origin of both the global coord system and the initial beam coord. system). However, we need a different element to global coordinate system transformation for this elemet \(M_{g2e}^{-1} \neq M_{b2e}^1\) and of course also for all following elements. Moreover, for all following elements we also need a different \(M_{g2e}^i \neq M_{b2e}^i\) for \(i>1\).
+global coordinates are sometimes given by the user directly via e.g. an rml file, which stores the global orientation as a 3x3 matrix and the position as a 3 element vector. Expanding both to homogeneous 4x4 rotation/translation matrices makes it possible to calculate \(M_{g2e}\) and \(M_{e2g}\) by multiplying them. +For the case that the beamline was still build sequentially, it was decided to first build the global position and orientation from \(\alpha\), \(\beta\), \(\chi\), the distance \(z_0\) and the misalignment and then derive the matrices \(M_{g2e}\) and \(M_{e2g}\) in the same way.
+The following calculations can be used for all optical elements. For the ellipsoid, however, the misalignment can be defined in the coordinate system of the mirror or of the curvation. The usual misalignment is in the coordinate system of the mirror. The coordinate system of the curvation differs by a rotation through the tangent angle \(\theta\) around the x-axis. This angle depends on the shape of the ellipsoid. Depending on the coordinate system, we add the rotation \(T_x(\theta)\) (in red), for all other elements this is irrelevant so \(\theta = 0\).
+As mentioned before, in the case that the element is the first in the beamline, it is simply placed at a certain distance on the z-axis. Therefore, the position (pos) is, in homogeneous coordinates:
+\[ +pos^0 = \begin{bmatrix} 0 \\ 0 \\ z_0^0 \\ 1 \end{bmatrix} + or^0 \cdot \color{red}{R_x^0(\theta)} \cdot \color{black}{\begin{bmatrix} d_x^0 \\ d_y^0 \\ d_z^0 \\ 1 \end{bmatrix}} +\]
+where or is the orientation of the element and \(d_x\), \(d_y\), \(d_z\) are the positional misalignment. +The orientation of the first element is calculated as follow:
+\[ +or^0 = R_x^0(\alpha) R_z^0(-\chi) \cdot \color{red}{R_x^0(\theta)} \cdot \color{black}{R_{\phi, -\psi, \chi}^0} \cdot \color{red}{R_x^0(\theta)^T} +\]
+where \(R_{\phi -\psi \chi}\) contains the orientational misalignment and \(R_x(\alpha) R_z(-\chi)\) is the rotational part of \(M_{b2e}\) (without the translation by \(z_0\) since the distance is not part of the orientation but of the position)
+When the element is not the first in the beamline, we need in addition to the ususal parameters of this element (\(\alpha\), \(\beta\), \(\chi\), the distance \(z_0\) and the misalignment) also the global position and orientation and the \(M_{e2b}\) matrix of the previous element. Unfortunately, we also have to remove the misalignment from the global position of the previous element (equation 1), then we can add the distance from the previous to new element to the position of the previous element following the direction of the outgoing ray (2). Finally, we can add the positional misalignment of element i to the position (3).
+\[ +\begin{align} +pos^{(i-1)} &= pos^{i-1} - or^{i-1} \cdot \color{red}{R_x^{i-1}(\theta)} \cdot \color{black}{\begin{bmatrix} d_x^{i-1} \\ d_y^{i-1} \\ d_z^{i-1} \\ 1 \end{bmatrix}} \\ +pos^i &= pos^{i-1} - or^{i-1} \cdot R_x^{i-1}(\theta) \cdot \begin{bmatrix} 0 \\ 0 \\ z_0^i \\ 1 \end{bmatrix} \\ +pos^i &= pos^i + or^{i} \cdot \color{red}{R_x^{i}(\theta)} \cdot \color{black}{\begin{bmatrix} d_x^i \\ d_y^i \\ d_z^i \\ 1 \end{bmatrix}} +\end{align} +\]
+The calculation of the orientation of the ith element is a bit simpler. The global orientation of element i is the global orientation of the previous element \((or^{i-1})\) without the rotational misalignment (1) multiplied with the rotation of the new element coordinate system with respect to the previous element coordinate system \((M_{e2b}, \text{eq 2})\) multiplied with the orientation of the new element in its own element coordinate system (which is calculated in the same way as for the first element: local orientation \(\cdot\) misalignment, eq. 3):
+\[ +\begin{align} +or^{(i-1)} &= or^{i-1} \cdot \color{red}{R_x^{i-1}(\theta)^T} \cdot \color{black}{(R_{\phi, -\psi, \chi}^{i-1})^{T}} \cdot \color{red}{R_x^{i-1}(\theta)} \\ +or^i &= or^{(i-1)} \cdot M_{e2b}^{i-1} \\ +or^i &= or^i \cdot (R_x^i(\alpha) R_z^i(-\chi) \cdot \color{red}{R_x^i(\theta)} \cdot \color{black}{R_{\phi, -\psi, \chi}^i} \color{red}{R_x^i(\theta)^T}) +\end{align} +\]
+These calculations are done in WorldUserParams.cpp. They have been tested but still there might be some mistake in there, so feel free to question the calculations if something is not working.
+Once you have the global position and orientation, you can derive the Transformation matrices by expanding the position p to a homogeneous translation matrix and the orientation to a homogeneous rotation matrix:
+\[ +M_{g2e} = +\begin{bmatrix} +1 & 0 & 0 & -p_x \\ +0 & 1 & 0 & -p_y \\ +0 & 0 & 1 & -p_z \\ +0 & 0 & 0 & 1 +\end{bmatrix} \cdot or +\] +\[ +M_{e2g} = M_{g2e}^{-1} = or^T \cdot \begin{bmatrix} +1 & 0 & 0 & p_x \\ +0 & 1 & 0 & p_y \\ +0 & 0 & 1 & p_z \\ +0 & 0 & 0 & 1 +\end{bmatrix} +\]
+ +In RAYX we intend to work on better modularity and readability in our code. In our opinion, one step to achieve this, to reevaluate and restructure the parameters of RAY-UI. Since a lot of the parameters had dependencies on each other, we decided that a distinction between them was needed. This is where our definition of User and Model Parameters comes into play.
+A Model Parameter is any parameter that is directly influencing the calculations of the tracer. Further, a Model Parameter can also be a parameter, which is required for the fundamental definition of our model.
+The position and direction Matrix are not directly used in the calculations of the Tracer, but they still are Model Parameters, since they are essential in directly defining our model.
+User Parameters are all the parameters that are only used to calculate Model Parameters. They only exist to improve the user experience of our application and are neither passed directly to the tracer nor are needed to define our model.
+ +The triangulation algorithm is based on the algorithm presented in Computational Geometry
+The polygons are represented as a collection of paths for faces and holes. +Faces are ordered in counter-clockwise order and holes in clockwise order.
+The edge list a data structure containing all edges of the polygons. +The edges are directed. +A polygon is represented in counter-clockwise order.
+Every edge contains:
+The data structure has a split method. +This method inserts two new edges and updates some incoming and outgoing references.
+References before splitting:
+References after splitting:
+Split returns uv and vu.
+Example:
+ +A polygon is monotone if every line in y direction intersects 0 or 2 edges.
+The algorithm is a sweep line algorithm. +The line sweeps from +y to -y. +The algorithm requires a binary search tree, a hash map and a priority queue. +The BST contains every edge that is currently intersecting the sweep line sorted by x position of the intersection. +The hash map maps downward facing edges intersecting the sweep line to edges used for splitting.
+y1 > y2 || (y1 == y2 && x1 < x2)
Start:
+Polygon begin
+ +End:
+Polygon end
+ +Regular:
+Standard edge
+ +Merge:
+Merge two monotone polygons in one
+ +Split:
+Split polygon in two monotone pieces
+ +le*
denotes an edge in the BST and helper[le*]
the corresponding edge in the HM.
+The left part of each diagram is the state before processing and the right part the state after processing.
+The dotted line is the current sweep line.
+The BST and HM can contain more entries than shown. These entries wont be modified.
Origin of helper is a merge vertex:
+ +Otherwise no new edges will be added.
+This works for every vertex type of the helpers origin, in this example the helpers origin is a merge vertex:
+ +New edges are only added if a helpers origin is a merge vertex. +In case le1s helper is not a merge vertex, the helper of le is set to the outgoing edge of the currently processed vertex.
+ +Origin of helper is merge vertex.
+ +Otherwise no new edge will be added.
+Origin of helper is merge vertex.
+ +Otherwise no new edge will be added. And the helper of le is the outgoing edge of the currently processed vertex.
+The newly added edges are colored blue.
+ +The algorithm requires a stack containing all not yet triangulated edges.
+The triangulation algorithm has the following steps:
+The green edge is the current edge. The blue edges are on the stack. (ordered by y coordinate)
+Analyzing your code Coverage is a necessary step in devloping complex big projects.
+Getting the code's coverage is simply generating a summary of how much a piece of code is executed and "covered".
+Code Coverage offers line, function, and branch analysis.
+Generally, the most reasonable method is to check how much code your Testsuites cover.
+Please Note: This has only been tested on UNIX System (Ubuntu)
+Code Coverage is supported by GNU GCC and LLVM. Make sure that one of these build tools is installed. Gcov, lcov and genhtml are packages that are also needed. For more information, you can check the usage of gcov (GCC) here.
+You can also use gcovr as an alternative to gcov.
+Code Coverage only works if the main function returns 0. (The Test Suite returns 0 if all testcases pass).
+The test suite should run only in DEBUG Mode and with all optimizations turned off "-O0" for Coverage. Warnings will pop if not.
+With the correct gcov and -fdump added to CXX_COMPILER_FLAGS
, running the Testsuite will generate *.gcno
and *.gch
files meant for lcov. The file generation and flags are handled by a CMAKE submodule. An *.info
file is then created and transformed afterwards to interactable index.html
page on the web browser. Configuration information on the CMAKE Output window show up as the submodule defaults to verbose.
#CodeCoverage
in the CMakeLists.txt on the project's root directory, to set the option BUILD_WITH_GCOV
.DBUILD_WITH_GCOV
instead of last step.cd build
and run:lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory coverage
build/
called coverage, with and index.html
can be opened to see Code Coverage.Rayx uses a number of languages but mainly C++ and GLSL.
+If you are using VS Code, you may create a new Debugging Setting to launch rayx through gdb. More here
+Once the setting file (launch.json) is ready, you can change and add arguments for VSCode/gdb to parse.
+If you are familiar with gdb, add more commands in setupCommands, eg. Disassembly Flavor for Intel
+The shader language (GLSL) does not offer live debugging and stepping with gdb or similar. If you run the CPU Tracer then you can step through the code as it was compiled into C code, since it's only running on the CPU. However when using Vulkan a few more steps needs attention.
+Vulkan offers a debug extension layer that can be activated with the combination of vkconfig to launch the application in debug mode.
+We need #extension GL_EXT_debug_printf : enable
in the shader file.
We also need to add VK_KHR_shader_non_semantic_info
as in extension in the Vulkan Instance.
Launch the application with vkconfig and chose the Debug Printf Preset in VK_LAYER_KHRONOS_validation. (Vkconfig should be packaged with Vulkan-SDK)
+Make sure to increase Printf buffer size under Debug Printf if you get a Validation Layer Warning.
+Note GraphViz installation: On windows you can install graphviz via MSYS2 since you might already have it to build the project. Simply call: pacman -S mingw-w64-x86_64-graphviz in the MSYS2 commandline window. The DOT_PATH muust then be set to {MSYS2}/mingw64/bin
+A generated Class Diagram can be found under "Classes->Class Hierarchy"
+Use this prompt and replace the header and cpp code in the lower part with your code. +Note: The first code is example code for ChatGPT so it knows the style. Do not replace it. Just replace the lower code after "Create a documentation for this header file:" and "Create a documentation for this cpp file:"
+Read and check all generated content. ChatGPT can easily make errors and assumptions. It is only a tool to save time writing text. The information needs to be approved by the developer.
+Prompt:
+I need to create a doxygen documentation for some code I wrote. You will help me with that. The comments in the header file should include the information what the function does/is for. In the cpp file, it's how the function is implemented. This is a good example how it should be:
+
+.h
+/**
+ * @brief Triangulates optical elements for rendering.
+ * @param elements A vector of optical elements to be triangulated.
+ * @param useMarchinCubes Flag to determine if Marching Cubes triangulation should be used.
+ * @return A vector of RenderObject, which are the triangulated version of the input elements.
+ */
+std::vector<RenderObject> triangulateObjects(const std::vector<RAYX::OpticalElement>& elements, bool useMarchinCubes = false);
+
+/**
+ * @brief Generates visual representations of rays based on bundle history and optical elements.
+ * @param bundleHist RAYX-Core type, providing details of ray interactions in the beamline.
+ * @param elements A vector of optical elements used for coordinate conversions.
+ * @return A vector of lines, which visually represents the paths of rays in the beamline.
+ */
+std::vector<Line> getRays(const RAYX::BundleHistory& bundleHist, const std::vector<RAYX::OpticalElement>& elements);
+
+.cpp
+/**
+ * This function processes the BundleHistory and determines the ray's path in the beamline.
+ * Depending on the event type associated with the ray, the function produces visual lines that represent
+ * ray segments, colored based on the event type.
+ */
+std::vector<Line> getRays(const RAYX::BundleHistory& bundleHist, const std::vector<RAYX::OpticalElement>& elements) {
+ std::vector<Line> rays;
+
+ for (const auto& rayHist : bundleHist) {
+ glm::vec3 rayLastPos = {0.0f, 0.0f, 0.0f};
+ for (const auto& event : rayHist) {
+ if (event.m_eventType == ETYPE_JUST_HIT_ELEM || event.m_eventType == ETYPE_ABSORBED) {
+ // Events where rays hit objects are in element coordinates
+ // We need to convert them to world coordinates
+ glm::vec4 worldPos = elements[(size_t)event.m_lastElement].m_element.m_outTrans * glm::vec4(event.m_position, 1.0f);
+
+ Vertex origin = {{rayLastPos.x, rayLastPos.y, rayLastPos.z, 1.0f}, YELLOW};
+ Vertex point = (event.m_eventType == ETYPE_JUST_HIT_ELEM) ? Vertex(worldPos, ORANGE) : Vertex(worldPos, RED);
+
+ rays.push_back(Line(origin, point));
+ rayLastPos = point.pos;
+ } else if (event.m_eventType == ETYPE_FLY_OFF) {
+ // Fly off events are in world coordinates
+ // The origin here is the position of the event
+ // The point is defined by the direction of the ray (default length)
+
+ glm::vec4 eventPos = glm::vec4(event.m_position, 1.0f);
+ glm::vec4 eventDir = glm::vec4(event.m_direction, 0.0f);
+ glm::vec4 pointPos = eventPos + eventDir * 1000.0f;
+
+ Vertex origin = {eventPos, GREY};
+ Vertex point = {pointPos, GREY};
+
+ rays.push_back(Line(origin, point));
+ }
+ }
+ }
+
+ return rays;
+}
+
+Create a documentation for this header file:
+
+// Marching Cubes
+std::vector<RenderObject> marchingCubeTriangulation(const std::vector<RAYX::OpticalElement>& elements);
+std::vector<Triangle> trianglesFromQuadric(const double* quadric, Cutout cutout); // TODO: make nicer
+
+glm::vec3 getPositionAtCorner(int cornerIndex);
+Vertex interpolateVertex(int edgeIndex, const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE], int offsetX, int offsetY, int offsetZ,
+ glm::vec3 scale);
+double evaluateQuadricAtPosition(const double surface[16], const glm::vec4& pos);
+int determineMarchingCubesCase(const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE], int x, int y, int z);
+std::vector<Triangle> lookupTrianglesForCase(int caseIndex, const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE], int offsetX, int offsetY,
+ int offsetZ, glm::vec3 scale);
+Vertex getVertexFromEdge(int edgeIndex);
+double getScalarValueAtCorner(int x, int y, int z, const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE]);
+
+// Marching Cubes
+const int edgeTable[256] = {...}
+const int triTable[256][16] = {...}
+
+Create a documentation for this cpp file:
+#include "MarchingCubes.h"
+
+#include "Colors.h"
+#include "Debug/Debug.h"
+
+namespace RAYX {
+namespace CPU_TRACER {
+bool RAYX_API inCutout(Cutout cutout, double x1, double x2);
+} // namespace CPU_TRACER
+} // namespace RAYX
+
+std::vector<RenderObject> marchingCubeTriangulation(const std::vector<RAYX::OpticalElement>& elements) {
+ std::vector<RenderObject> objects;
+
+ for (RAYX::OpticalElement element : elements) {
+ auto quadric = element.m_element.m_surface.m_params;
+ std::vector<Triangle> triangles = trianglesFromQuadric(quadric, element.m_element.m_cutout);
+ RenderObject object(glm::mat4(element.m_element.m_outTrans));
+ for (Triangle triangle : triangles) {
+ object.addTriangle(triangle);
+ }
+ objects.push_back(object);
+ }
+
+ return objects;
+}
+
+std::vector<Triangle> trianglesFromQuadric(const double* quadric, Cutout cutout) {
+ // Define the size and resolution of the grid
+ double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE];
+ if (cutout.m_type == CTYPE_UNLIMITED) {
+ RAYX_ERR << "Unlimited cutout not supported by marching cubes";
+ }
+ RAYX_LOG << "cutout: " << cutout.m_params[0] << ", " << cutout.m_params[1];
+
+ const double SCALE = 1; // Define your desired scaling factor here
+
+ const glm::vec3 bounding_box = glm::vec3(cutout.m_params[0], cutout.m_params[1], 1.0f);
+ const glm::vec3 scale = glm::vec3(cutout.m_params[0] * SCALE / GRIDSIZE, SCALE * 1.0f, cutout.m_params[1] * SCALE / GRIDSIZE);
+
+ // 1. Sample the 3D space
+ for (int x = 0; x < GRIDSIZE; x++) {
+ for (int y = 0; y < GRIDSIZE; y++) {
+ for (int z = 0; z < GRIDSIZE; z++) {
+ // Convert grid coordinate to centered & scaled space coordinate
+
+ double realX = ((double(x) / GRIDSIZE) - 0.5) * bounding_box.x;
+ double realY = ((double(y) / GRIDSIZE) - 0.5) * bounding_box.y;
+ double realZ = ((double(z) / GRIDSIZE) - 0.5) * bounding_box.z;
+
+ glm::vec4 pos(realX, realY, realZ, 1);
+ double value = evaluateQuadricAtPosition(quadric, pos);
+ scalarGrid[x][y][z] = value;
+ }
+ }
+ }
+
+ // 2. March through each voxel
+ std::vector<Triangle> triangles;
+ for (int x = 0; x < GRIDSIZE - 1; x++) {
+ for (int y = 0; y < GRIDSIZE - 1; y++) {
+ for (int z = 0; z < GRIDSIZE - 1; z++) {
+ double realX = ((double(x) / GRIDSIZE) - 0.5) * bounding_box.x;
+ double realZ = ((double(z) / GRIDSIZE) - 0.5) * bounding_box.y;
+ if (RAYX::CPU_TRACER::inCutout(cutout, realX, realZ)) {
+ int caseIndex = determineMarchingCubesCase(scalarGrid, x, y, z);
+ std::vector<Triangle> voxelTriangles = lookupTrianglesForCase(caseIndex, scalarGrid, x, y, z, scale);
+ triangles.insert(triangles.end(), voxelTriangles.begin(), voxelTriangles.end());
+ }
+ }
+ }
+ }
+
+ return triangles;
+}
+
+double evaluateQuadricAtPosition(const double surface[16], const glm::vec4& pos) {
+ double icurv = surface[0];
+ double a11 = surface[1];
+ double a12 = surface[2];
+ double a13 = surface[3];
+ double a14 = surface[4];
+ double a22 = surface[5];
+ double a23 = surface[6];
+ double a24 = surface[7];
+ double a33 = surface[8];
+ double a34 = surface[9];
+ double a44 = surface[10];
+ double result = a11 * pos.x * pos.x + a22 * pos.y * pos.y + a33 * pos.z * pos.z + a12 * pos.x * pos.y + a13 * pos.x * pos.z +
+ a23 * pos.y * pos.z + a14 * pos.x + a24 * pos.y + a34 * pos.z + a44;
+
+ return result;
+}
+
+int determineMarchingCubesCase(const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE], int x, int y, int z) {
+ // Based on the scalar values at the voxel corners, determine the index for the lookup tables.
+ int cubeIndex = 0;
+
+ auto test = scalarGrid[x][y][z];
+ if (scalarGrid[x][y][z] < 0) cubeIndex |= 1;
+ auto test2 = scalarGrid[x + 1][y][z];
+ if (scalarGrid[x + 1][y][z] < 0) cubeIndex |= 2;
+ if (scalarGrid[x + 1][y][z + 1] < 0) cubeIndex |= 4;
+ if (scalarGrid[x][y][z + 1] < 0) cubeIndex |= 8;
+ if (scalarGrid[x][y + 1][z] < 0) cubeIndex |= 16;
+ if (scalarGrid[x + 1][y + 1][z] < 0) cubeIndex |= 32;
+ if (scalarGrid[x + 1][y + 1][z + 1] < 0) cubeIndex |= 64;
+ if (scalarGrid[x][y + 1][z + 1] < 0) cubeIndex |= 128;
+
+ return cubeIndex;
+}
+
+std::vector<Triangle> lookupTrianglesForCase(int caseIndex, const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE], int offsetX, int offsetY,
+ int offsetZ, glm::vec3 scale) {
+ // Using the triTable to generate the triangles for the voxel.
+
+ std::vector<Triangle> triangles;
+
+ // triTable[caseIndex] provides the edges to be connected for the triangles.
+ // Every 3 indices in the table make up a triangle.
+ for (int i = 0; triTable[caseIndex][i] != -1; i += 3) {
+ Triangle triangle;
+
+ // Convert edge indices to vertices
+ triangle.v1 = interpolateVertex(triTable[caseIndex][i], scalarGrid, offsetX, offsetY, offsetZ, scale);
+ triangle.v2 = interpolateVertex(triTable[caseIndex][i + 1], scalarGrid, offsetX, offsetY, offsetZ, scale);
+ triangle.v3 = interpolateVertex(triTable[caseIndex][i + 2], scalarGrid, offsetX, offsetY, offsetZ, scale);
+ triangle.v1.color = DARKER_BLUE;
+ triangle.v2.color = BLUE;
+ triangle.v3.color = LIGHTER_BLUE;
+
+ triangles.push_back(triangle);
+ }
+
+ return triangles;
+}
+
+glm::vec3 getPositionAtCorner(int cornerIndex) {
+ glm::vec3 cornerPositions[8] = {
+ glm::vec3(0, 0, 0), // 0
+ glm::vec3(1, 0, 0), // 1
+ glm::vec3(1, 0, 1), // 2
+ glm::vec3(0, 0, 1), // 3
+ glm::vec3(0, 1, 0), // 4
+ glm::vec3(1, 1, 0), // 5
+ glm::vec3(1, 1, 1), // 6
+ glm::vec3(0, 1, 1) // 7
+ };
+ // Check for valid index
+ if (cornerIndex < 0 || cornerIndex >= 8) {
+ throw std::out_of_range("Invalid corner index");
+ }
+ return cornerPositions[cornerIndex];
+}
+Vertex interpolateVertex(int edgeIndex, const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE], int offsetX, int offsetY, int offsetZ,
+ glm::vec3 scale) {
+ int edgeToVertex[12][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}};
+
+ // Define the corner-to-voxel mapping
+ int cornerToVoxel[8][3] = {{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 1, 0}, {1, 1, 0}, {1, 1, 1}, {0, 1, 1}};
+
+ int v0Index = edgeToVertex[edgeIndex][0];
+ int v1Index = edgeToVertex[edgeIndex][1];
+
+ double value0 = getScalarValueAtCorner(cornerToVoxel[v0Index][0] + offsetX, cornerToVoxel[v0Index][1] + offsetY,
+ cornerToVoxel[v0Index][2] + offsetZ, scalarGrid);
+ double value1 = getScalarValueAtCorner(cornerToVoxel[v1Index][0] + offsetX, cornerToVoxel[v1Index][1] + offsetY,
+ cornerToVoxel[v1Index][2] + offsetZ, scalarGrid);
+ // Check for divide by zero
+
+ double t;
+ if (fabs(value1 - value0) < 1e-6) {
+ t = 0.5; // or choose a reasonable default
+ RAYX_LOG << "Divide by zero";
+ } else {
+ t = (0 - value0) / (value1 - value0);
+ }
+ double move = (GRIDSIZE / 2.0);
+ Vertex v;
+ v.pos = glm::vec4((glm::mix(getPositionAtCorner(v0Index), getPositionAtCorner(v1Index), t) + glm::vec3(offsetX, offsetY, offsetZ) -
+ glm::vec3(move, move, move)) *
+ scale,
+ 1.0f);
+
+ return v;
+}
+
+double getScalarValueAtCorner(int x, int y, int z, const double scalarGrid[GRIDSIZE][GRIDSIZE][GRIDSIZE]) {
+ if (x < 0 || x >= GRIDSIZE || y < 0 || y >= GRIDSIZE || z < 0 || z >= GRIDSIZE) {
+ throw std::out_of_range("Invalid corner index");
+ }
+ return scalarGrid[x][y][z];
+}
+
+
+ In RAYX we decided on using a formatter to keep our code clean and organized. Here you can read how to use it.
+We use clang-format for this purpose. The style is defined in the .clang-format file in the project root. As you can see in the file, our style is based on the Google C++ style. Remember: the formatter just completes our style guide and doesn't replace it.
+If you are using Visual Studio Code you can use the C/C++ extension for formatting. It includes the clang-format binary so you just need to change a few settings to configure it correctly. You can see the changes (highlighted in red) in the following screenshots:
+ +The following settings control, when the formatting should happen. This is up to you as long as the commited code is formatted.
+ +To format all the workspace at once, you can use the following Visual Studio Code's Extension.
+In your settings .json file, add the lines to go through the correct directories and only format C++ files:
+ "formatAll.includeFileExtensions": [
+ ".cpp",
+ ".h"
+ ],
+ "formatAll.excludeFolders": [
+ "build",
+ "Extern",
+ ],
+
+WARNING: Using this extension can take up some time, you'll see all targeted files opening in new tabs. Grab a cup of coffee and let it do the trick :)
+Here you can find an overview of the profiling tools used in rayx. Further reading can be done in the code (see Instrumentor.h file in "Debug"-folder).
+The code for this profiler, was taken from a tutorial by TheCherno on Youtube.
+The Profiling used in RAYX is a simple json export of the time each profiled function took. If you want to add profiling to a function, simply add:
+RAYX_PROFILE_FUNCTION;
at the beginning of the function.
+For profiling specific scopes, you can use RAYX_PROFILE_SCOPE
, which takes in a name for the scoped timer.
The data will be output to the directory, the executable was started in. You can use the tracing functionality of any chromium based browser to read the data.
+For example, use the URL "chrome://tracing" for the Chrome browser.
+The profiling can happen in multiple sessions. You can't have to sessions running at the same time though. This is functionality is mainly supposed to give the option to separate data for different parts of the program.
+An example would be seperating measurements into startup, runtime, shutdown.
+The Macros used to create and end sessions:
+RAYX_PROFILE_BEGIN_SESSION
RAYX_PROFILE_END_SESSION
There are several kinds of tests:
+testing only c++ code and not using the shader, to check if parameters of optical elements are calculated correctly
+check especially if the values that are derived from given user parameters and given to the shader are correct. These include the surface Parameters, the object parameters, the element parameters and the world to element and element to world coordinate transformation matrices, each of which are stored in an 16 element value array.
+The testing suite "Tracer" contains unit tests that check if the individual functions in the shader code are behaving as expected. As the functions that are tested are on the shader and in our current framework the only values that can be moved to the shader are mainly the Ray and Optical Element buffers, we cannot just call the functions with the required input values. +Instead we first store the test values in Rays on the C++ side and retrieve them from the Rays on the shader side. Then the test can be executed on the shader and the results are stored again in the Ray buffer (outputRays). Back on the C++ side this can then be compared with the expected values.
+Example: Testing the refraction function
+The refraction function on the shader calculates the direction and weight of the refracted ray from the direction of the incoming ray, the normal at the intersection and the line density. The input to the test should therefore be:
The output and therefore the values to verify are:
+after the refraction
+To move the test data to the shader and retrieve the results after applying the function we use the Ray buffer. Each test case is encoded in one Ray and we can add as many test cases as we want to the ray buffer. A ray consists of:
+We can for example encode the test values for the refraction test as:
+For this, we can use the function "addTestSetting" that receives the test values in the correct order, creates a ray and adds it to a given ray vector which is in this case the one that will be transferred to the shader (std::vector<RAYX::Ray> testValues). +To be able to verify the result that we will later retrieve from the shader, we need to store also the expected direction_out and weight_out. To make the comparison later easier we also store these in a Ray that corresponds to the test case and add it to std::vector<RAYX::Ray> correct using for example the following encoding:
+Now we have in both vectors one Ray for each test case, where testValues contains the values that we move to the shader and correct contains those that we expect to get back. Now, we can move testValues as the Ray buffer to the shader.
+Then, on the shader side we need to make sure that the test values are "unpacked" correctly from the ray buffer. We can execute the function and store the updated direction and weight in the output ray buffer in the same format as they are stored in correct on the C++ side which is:
+These Rays are returned to the C++ test code as outputRays where the can be compared with compareFromCorrect(correct, outputRays, tolerance); for a given tolerance.
+For other functions, like the approximation of sinus for example, we do not necessarily need the correct vector but we can simply apply the function sin() to the testValues and compare them directly with:
+auto sinfun = fn<double, double>([](double x) { return sin(x); });
+compareFromFunction(sinfun, testValues, outputRays, tolerance);
If a test has more test values than a ray has paramters, one could use the opticalElement buffer and add e.g. one opticalElement for each test case.
+To make things even more complicated, we also need to have an id for each test to distinguish on the shader side which test is run and how the rays should be interpreted. The id is set in the surfaceParams of an optical Element. Because we want to have only one main function on the shader we also have to distinguish between a test case and a normal run of a beamline. This is achieved by setting the id to 0 if it is a beamline and to the test id otherwise (there is no test with id=0).
+Tests from the testing suite opticalElements read a beamline from a given rml file, run the tracer on it and writes the returned rays to a csv file with the same name as the rml file. +If the beamlines give deterministic results, we can compare them with the output of RAY-UI using the test.py file. Therefore it is necessary to export the traced data from the same beamline traced in RAY-UI. Moreover, the beamline needs to end with an image plane because of the different coordinate systems that are used (Ray coodinates vs world coordinates).
+ +In this section, you can read up on all the tools used to develop RAYX.
+ +Please note that this page has been written from a Vulkan beginner's perspective. All information and facts are bound to change and tend to be misleading at times.
+We have followed an extensive research on RAYX Execution Model on the GPU side and found plenty of rabbit holes and possible bottlenecks that might lead to future misbehaviours or loss in performance. In this blog-like post, we explain some of the mentioned issues and possible solutions to overcome them. When needed, this post can be a starting point to a code refraction in RAYX.
+The GPU is a massive die on Chip that has plenty of processing cores (Similar to a CPU Core but only with very basic Arithmetic Operations FP/INT32). The GPU excels in SIMD (Single Instrct. Multiple Data) operations. It is basically a huge parallel machine that tries to execute the same operation (Pixel coloring, coordinate calculation etc.) on a wide range of data. For this purpose, GPUs are used in a more abstract field other than simply "graphics"; General Purpose Compute on GPU (GPGPU), which is mainly what RAYX on Vulkan relies on. Vulkan? WHO?
+Nvidia and AMD are the main GPU manufacturers. As both GPU architectures slightly differ and Nvidia ... isn't a fan of open-source... we decided to use an All-in-One compatible tool to talk to the GPU. Enter Vulkan. A "new" API used to control the GPU for all (Not really, we'll talk about this later) purposes developed to compete against OpenGL and alike. But most of all, open-source! Vulkan is increasingly gaining popularity and is the new state of the art for developing GPU Solutions. However it is the most NOT beginner-friendly API that you can learn. Vulkan is created to give the user the uttermost control over hardware at the price of complexity and low-level execution. It can be very fast and performant but to reach that level you need to cover all aspects and gotchas that vulkan(the dev) might fall into.
+We can talk about Vulkan and how it works for weeks and still have more topics that we haven't covered. By the way, a very effecient way to learn about it, is to simply read the Vulkan-Samples made by Khronos and others. But let's suppose that we have already created a working Vulkan Environment and we are ready to talk with the GPU... "GPU, do this!".
+To do as such, we need to record a Command in a Command Buffer. The Vulkan Pipeline will know which installement we need and which Shader Module we intend to run the GPU. The Command is then stored in a Queue. The Queue can be seen as a stack memory that accepts commands sequenctually... A then B then C... The Queue is submitted to the GPU. Finally we simply wait until idle i.e wait until our gpu compute code is terminated as we don't need any render workload or such from the GPU. So that we can proceed with further code.
+All of this is executed on the CPU-side. The GPU takes the heavy-lifting and the actual execution of Compute only once queue is submitted. This is a good example of async computation which means that two things are doing seperate operations and they Synchronize with each other at different times. The CPU proceeds with data analysis or plotting only when the GPU is idle.
+Through Vulkan, the GPU offers about 16 Queue Families (Check your vkinfo) to stack commands inside for execution. Hence the described procedure does not happen once in a standard GPU pipeline and does not have to be unique.
+You can find GA102 Architecture whitepaper at Nvidia's
+Before discussing the issues at hand, now is a good chance to dip our hands a bit in the Hardware Side of Nvidia's latest GPU high-end architecture. Although, the dies become faster, and more power demanding, the idea behind a gpu still remains the same throughout the last generations.
+A full GPU is mainly made out of memory and execution cores. +The execution cores can be seen as a hierachical abstraction. The GPU (GA102 as reference) has 7 GPCs. Each GPC consists of 12 TPCs. Each TPC is made out of 2 SMs. Each SM has a Warp Scheduler, a 64KB register File (Registers), Load/Store Units and 32 CUDA FP32/INT32 cores.
+This GPU has 84 SMs overall and that is the most important unit in understanding the GPU Design. An SM (Streaming Multiprocessor) is a scalable core and a Cuda Core is the arithmetic brain for operations inside the SM.
+ +Memory consists of a big VRAM (DRAM) outside of the SoC, L2 Cache and a smaller faster L1 Cache closer to every SM. The register file inside the SM is the fastest and it's used to store any temporary values or branch jump "pointers".
+L1 Cache can also be split into some shared memory that threads can concurrently share at the same time, depending on the configuration.
+In the Compute Model, we talk about Warps, hence the warp scheduler. Warps are a "virtual" budnel of threads (Also called Wavefronts at AMD) made out of a pack of 32 Threads that are executed together. The Warp Scheduler takes care of issueing and disaptching the warps from the Intstruction bank to the SMs and cuda cores. It's only up to the Dev to correctly set the size of the Workload and its scalability. The Warp Scheduler will handle the rest.
+SMs execute instructions in warps, each warp consists of 32 Threads. It is important to notice that all warps in one SM execute the same instruction. Once done the scheduler issues the next instruction or decides to schedule another warp if the current has to stall for e.g. (Priority scheduling). The GA102 can schedule 4 warps at one cycle as seen in the figure. The Warp Scheduler is still a black box and very few documents describe it's exact functional model. It's also a piece that changes from one architecture to another and handles data and execution differently. Furthermore, the Warps do not directly map to the local group size and global work size in Vulkan.
+A warp is considered active from the point its threads start executing until all threads have finished. SMs have a limit on the number of active warps, meaning the remaining inactive warps will need to wait until the current active ones are finished execuring. The ratio of active warps on an SM to the maximum number of active warps on the SM is called occupancy.
+If the code to be executed has if clauses then some threads inside the warp have to execute with masks (Not do anything as the if condition is false ) and the else branch would need to also execute AFTER the warp is done with the first part. This is called Warp Divergence. Even if modern GPUs are becoming better with handling the divergence. Compilers tend to also optimize the shader. It's still viewed as one of the DONT in parallel compute.
+The current RAYX Tracer uses 1 Vulkan Pipeline consisting of 1 Shader Module. The Shader module is dispatched once with the amount of needed threads (Rays) through 1 Compute Queue and the GPU would handle the rest.
+ +The fence shows that we are waiting for the GPU to become idle.
+The current shader code has many if elif elif branchs which would cause internally a huge amount of divergence. According to nvidia's devoloper guide for tuning:
+++The high-priority recommendations from those guides are as follows:
++
+- Find ways to parallelize sequential code.
+- Minimize data transfers between the host and the device.
+- Adjust kernel launch configuration to maximize device utilization.
+- Ensure global memory accesses are coalesced.
+- Minimize redundant accesses to global memory whenever possible.
+- Avoid long sequences of diverged execution by threads within the same warp.
+
The divergence and the need to schedule each divergence is a waste of SMs. Our main function is an if clause by itself, inside of it are more ifs. This is one of the major flows of writing GLSL that we seem to be trapped in for now. Even with compiler optimization and the fact that modern GPUs can easily handle warp divergence, it's still one of the main issues that need to be fixed. New architectures include better divergence methods but we shouldn't rely only on that for better performance.
+One thing to consider is that usually most ray bundles react the same way in very small finite surfaces. The divergence only happens at the extremeties of said surfaces/objects or wasteboxes.
+To create a pipeline for anything-vulkan, we need to first upload the shader code to the GPU. The shader code is created inside a shader module with the main function as it's entry point. The GLSL code files in RAYX are all packed together and uploaded once. This bundle is too big for standard shader codes and it causes setup speed-loss as vulkan usually hangs in this step waiting for all instructions to be streamed to the GPU.
+Nvidia Nsight Graphics is a developer profiling tool that can run GPU Traces (And many other Profiling traces). It supports Vulkan natively which makes it the perfect candidate. The only problem is that RAYX still does not have a Vulkan Window as we only do compute. So Nsight Graphics is very limited and cannot catch "frames" for tracing. The GPU Trace produces a resource usage timeline and can also name the possible bottlenecks and reasons for low throughput if any. So what does Nsight Graphics say about RAYX Application?
+Well, not good stuff.. +Below are the Metrics, recorded from a run of 1.5 million Rays:
+ +Notice:
+The timeline shows more detailed information about the execution:
+ +Notice the bottlneck in the Compute Warps, stopping at almost 25% and the constant CS Warp Can't launch.
+Aren't we supposed to get a higher SM Throughput?.. Shouldn't the compute warps reach 95% ?..
+The divergence and the massive amount of shader code to execute in one thread / Warp causes a register pressure. Furthermore we have plenty of loops inside the shader code that might have been unrolled which is causing stalled Warps and the SM being not able to issue to all available CUDA Cores at once. Per Cycle, we are only using 22% of Available Warps. The SM is only using very specific alu fonctions and not even close to hitting 10% mark of the different operations that it can do (FMA Pipe for Floating precision being 5%). This can be better improved by reducing the stalled Warps.
+Nsight also recommends that we solve the issue as the SM is "running out of register-file space" by moving to async compute.
+It's however worth mentioning that this is one of the most common SM-occupancy limiters for pixel and compute shaders. [blog]
+To solve this we use the queue in vulkan and send as many small known commands as possible with smaller shader codes instead of one big block of GLSL. We use multiple Compute Queues if needed and let the Warp Scheduler control the rest. We obviously need a few synchronization points as we are now out of the global scope i.e we need to wait until rays intersect before reflecting, to send more specific commands. This is getting closer an async ompute model! For each stage/compute step we dedicate a vkPipeline preloaded with the shader and all what's left is to correctly bind the Descriptor Sets and push the command at the right time into the Queue.
+The new Compute Pipeline Pass: +
+A look into a standard usage of queues: +
+One issue remains to be solved (or mainly discussed) is as now we are sending commands per Object and per run. We need to know what type of object the ray intersects to feed it the correct shader module. For that we propose these solutions:
+ +The solutions are straight-forward. Solution 2 and 5 are the top-picks as they rely on on async compute the most.
+This is still a bound-to-fail idea as we are not sure if it will solve the register and idle Warps problem. But it will surely change some the vulkan code structure.
+The main goal is that the pros and the wasted Unused Warps would overcome the cons mentioned.
+A big downset of the new implementation is that it's still being made inside Vulkan. For that we can replace all of Vulkan's API with a pure GPGPU API: OpenCL.
+As mentioned earlier Vulkan is not meant to be purely for compute as CUDA or OpenCL etc. but to include compute stages betweenn other render passes. Which makes the pure compute dev work even harder and more time consuming.
+OpenCL is lightweight and easier to understand and recreate than Vulkan. It's definitely not faster than OpenCL. It's also older. But still valid for such cases and most of all supported by nvidia and amd.
+ +The current compute task in RAYX is crammed into one main.comp
compute shader file. This shader file takes care of all the computation and runs all neccessary tracing operations (bounces, collision check, etc.). As expected though, this file is huge in size, even compared to the state of the art compute shaders. As a consequence, loading this file into the GPU as instructions through Vulkan takes a while.
Moreover, although including one file, launching the task once and idle waiting are easy, massive flexibility and granuanilty are lost. Once the compute task starts, the CPU receives messages from the GPU only once this task has finished or failed. In the meantime, the GPU is a blackbox! To ensure correct result storage, the VRAM needs to allocate enough space for all recorded events, which leads to the output buffer's exponential increase in memory usage.
+The Vulkan Engine needs to undergo a refactor, so that it can create multiple compute tasks coming from different shader files. We only traget "path tracing" and already have the whole procedure in main.comp. A good start would be to split this shader file into coherent smaller files, which might help make the engine's tasks easier to handle. This would first reduce the size of each task therby help the GPU Scheduler and reduce the register load inside every Core (Nvidia SM Core). By dispatching smaller similar tasks to the GPU, these tasks are bound to finish sooner and more likely to be executed in parallel, as opposed to having idle cores due to register bottlneck or missing cache as reported by Nvidia Nsight tools in singular shader file.
+Futhermore, this enables more debugging features as we gain more control over the control flow of the tracing algorithm. But also, the output buffer can be merged with the input buffer and read to fetch results every time a new "mini-task" has finished. The engine is supposed to treat a specific chunk of data, so it makes senes to send the data to GPU and read it back at every checkpoint, while the GPU can execute more tasks on the same data.
+AFAIK, a shader VkShaderModule
can only be bound through a unique pipeline VkPipeline
. Hence this refactoring will center itself around this principle in the goal of making an easy-to-understand API that still doesn't require much user input.
The Vulkan Engine is made out of multiple passes. A (compute) Pass is the main class for executing a compute Program(Shader). Every Pass can have a group of pipelines to be executed sequentially. Each Pipeline is bound to a shader file.
+Let's suppose, we have Task A, B, and C to be executed on the GPU. If A and B can be ran sequentially, but C needs some CPU intervention before being also executed on GPU, then a possible composition would be to createComputePipelinePass()
with A and B in the same Pass (containing 2 pipelines/shaders) and another Pass with only C. A Compute Command recorded for the first Pass will execute for both A and B. Another one is needed for C.
To follow vulkan usage styles, the vulkan engine is initiliazed once. Then, as we use a batching system, we prepare the Passes (descirptor updating, necessary buffer reallocation, etc. ), run the the required compute task, cleanup, and repeat.
+ +For buffer-descriptor-Pass binding, a new BufferHandler
is created. This manages and prepares the buffers needed for the compute tasks.
The BufferHandler
binds, following vulkan rules, the buffers to the correct pass, makes sure that the needed size is available and takes care of the transfer and write operation from and to the GPU.
A new buffer is created ray-meta
which contains unique data to each ray (seed, ctr or state). This data also persists between bounces.
Multiple shader files:
+The engine supports more than one shader file, which was the main goal at the beginning. It is now easy to introduce a new shader into the engine, for example ray generation-only tasks, sorting and ray marching.
+More flexibility and ease of debugging:
+The vulkan engine now has more features and much more function calls that makes implementing new ideas faster. It is easier now to fetch more information from the GPU, since the CPU gets more messages in return (every bounce).
+Less memory usage:
+The same buffer ray-buffer
is now input and output. The output-only buffer is removed. The size of this buffer is rayAmount
instead of being rayAmount*maxBounces
.
More openings for optimization and parallelism. [Looks at Vulkan-Beyond page]
+Most of the classes (Pipelines, shaders, buffers, descriptors) are written in a way that graphic pipelines can also be supported (With more code obviously)
+OOP, destruction, and vulkan cleanup
+The new engine moves away from the C-style Vulkan version and creates objects with constructs, smart-pointers and destructors. As a consequence, memory leaks are less bound to happen and syntax is clearer. For example:
+// Create a buffer with rayList content
+bufferHandler
+ ->createBuffer<Ray>({"ray-buffer", VKBUFFER_INOUT}, rayList);
+
+// @brief Use ComputePipelineCreateInfo to build a new computePipeline
+struct ComputePassCreateInfo {
+ const char* passName;
+ std::vector<Pass::PipelineCreateInfo> pipelineCreateInfos = {};
+ int descriptorSetAmount = 1;
+};
+
+// Explicit passes cleanup
+for (auto pass : m_computePasses) {
+ for (auto& pipeline : pass->getPipelines()) {
+ pipeline->cleanPipeline(m_Device);
+ }
+}
+
+This was rather a successful experiment. I (OS) learned much more about Vulkan and GPUs. Once it is time to work on the vulkan engine in RAYX. This is by far, a high priority.
+ +In this section we introduce our design decisions for Vulkan and how +we use it to get efficient tracing on the GPU.
+ +The VulkanTracer is a ray tracing module using VULKAN by KHRONOS GROUP to efficiently trace rays with hardware acceleration.
+In the current version the Vulkan Engine is a Compute Class that runs as much parallel as possible on the GPU.
+Current procedure:
+initVulkan():
+mainLoop():
+A uniform buffer (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
) is a descriptor type associated with a buffer resource directly, described in a shader as a structure with various members that load operations can be performed on. More here
A storage buffer (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
) is a descriptor type associated with a buffer resource directly, described in a shader as a structure with various members that load, store, and atomic operations can be performed on.
Buffers | Size ( vkDeviceSize) | Usage | Name |
---|---|---|---|
0 | NumverOfRays*RAY_DOUBLE_AMOUNT | Transfer_DST|STORAGE_BUFFER | Ray Buffer |
1 | NumberOfRays*RAY_DOUBLE_AMOUNT | Transfer_SRC|STORAGE_BUFFER | Output Buffer |
2 | Quadric_parm+beamlineSize | STORAGE_BUFFER | Quadric Buffer |
3 | 100 | STORAGE_BUFFER | Buffer for xyznull |
4 | xxxxxxxxxxxxxxx | STORAGE_BUFFER | materialIndexBuf |
5 | xxxxxxxxxxxxxxx | STORAGE_BUFFER | materialBuf |
6 | xxxxxxxxxxxxxxx | STORAGE_BUFFER | debugBuffer |
X | min(GPU_MAX_STAGING,numberOfRays) | STORAGE|DST|SRC | Staging Buffer |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT
specifies that the buffer can be used as the source of a transfer command (see the definition of VK_PIPELINE_STAGE_TRANSFER_BIT).
VK_BUFFER_USAGE_TRANSFER_DST_BIT
specifies that the buffer can be used as the destination of a transfer command.
RAYX is a simulation tool designed for beamlines at electron storage rings. Currently, it is intended to be used in conjunction with RAY-UI. Our primary focus with RAYX is on high-performance tracing, achieved through the utilization of GPUs via the Vulkan API.
+RAYX offers several advanced features, including:
+In contrast, RAY-UI provides a graphical user interface (GUI) for editing beamlines, a feature currently lacking in RAYX. This means that .rml files for beamlines must be edited manually or generated using RAY-UI. Integrating a beamline editor into rayx-ui is among our top priorities for future development.
+For additional information, please visit our Wiki. We are committed to delivering stable releases, which can be found here. Please note that the master
branch and other branches might be unstable, and building RAYX from the source could lead to unstable software. If you experience any issues with our distributed binaries, do not hesitate to open an issue. We are keen on providing assistance and offering alternative solutions as the need arises.