Skip to content

File Crystal.h

File List > Intern > rayx-core > src > Shader > Crystal.h

Go to the documentation of this file

#pragma once

#include <cmath>

#include "Complex.h"
#include "Constants.h"
#include "Core.h"
#include "Utils.h"

namespace rayx {

// **********************************************************
// Function to calculate the local normal incidence angle theta
// from the direction cosines of the surface normal and the ray.
// The **negative** normal vector is used.
// Input:
//   al, am, an - direction cosines of the surface normal
//   fx, fy, fz - direction cosines of the incoming ray
// Output:
//   returns theta - the angle of incidence in radians
// **********************************************************
RAYX_FN_ACC
inline double getTheta(const glm::dvec3& __restrict rayDirection, const glm::dvec3& __restrict normal, double offsetAngle) {
    double al = normal[0];
    double am = normal[1];
    double an = normal[2];

    double fx = rayDirection[0];
    double fy = rayDirection[1];
    double fz = rayDirection[2];

    // Normalize incoming ray vector
    double fn = complex::sqrt(fx * fx + fy * fy + fz * fz);
    if (fn == 0.0) fn = 1.0;
    fx /= fn;
    fy /= fn;
    fz /= fn;

    // Dot product of normalized ray and surface normal
    double ar = fx * al + fy * am + fz * an;

    // Clamp ar to the range [-1.0, 1.0] since std::clamp is not supported in CUDA
    if (ar < -1.0) {
        ar = -1.0;
    } else if (ar > 1.0) {
        ar = 1.0;
    }

    double theta = complex::acos(ar) - PI / 2;
    theta        = theta + offsetAngle;
    return theta;  // TODO Fanny check how to correct this
}

RAYX_FN_ACC
inline double getBraggAngle(double energy, double dSpacing2) {
    int order           = 1;
    double wavelength   = energyToWaveLength(energy);
    double theta_factor = (order * wavelength) / dSpacing2;

    // Check for physical validity
    if (theta_factor > 1.0) {
        return -1.0;  // No reflection possible
    }

    double theta = asin(theta_factor);  // In radians
    return theta;
}

RAYX_FN_ACC
inline double getAsymmetryFactor(double braggAngle, double alpha) {
    double numerator   = complex::sin(braggAngle - alpha);
    double denominator = complex::sin(braggAngle + alpha);

    if (denominator == 0.0) return 0.0;  // avoid division by zero

    return numerator / denominator;
}

RAYX_FN_ACC
inline double getDiffractionPrefactor(double wavelength, double unitCellVolume) {
    // Avoid division by zero
    if (wavelength <= 0.0 || unitCellVolume <= 0.0) { return 0.0; }
    double result = (ELECTRON_RADIUS * wavelength * wavelength) / PI / unitCellVolume;
    return result;
}

RAYX_FN_ACC
inline complex::Complex computeEta(double theta, double bragg, double asymmetry, double structureFactorReFH, double structureFactorImFH,
                                   double structureFactorReFHC, double structureFactorImFHC, double structureFactorReF0, double structureFactorImF0,
                                   double polFactor, double gamma) {
    // Calculate numerator terms
    complex::Complex top_term1 = asymmetry * (theta - bragg) * sin(2.0 * theta);
    complex::Complex top_term2 = 0.5 * gamma * complex::Complex(structureFactorReF0, structureFactorImF0) * (1.0 - asymmetry);
    complex::Complex top       = top_term1 + top_term2;

    // Calculate denominator terms
    double bottom_term1 = gamma * polFactor;
    double bottom_term2 = sqrt(fabs(asymmetry));
    complex::Complex FH(structureFactorReFH, structureFactorImFH);
    complex::Complex FHC(structureFactorReFHC, structureFactorImFHC);
    complex::Complex bottom_term3 = sqrt(FH * FHC);

    complex::Complex bottom = bottom_term1 * bottom_term2 * bottom_term3;

    // Final eta calculation
    complex::Complex eta = top / bottom;
    return eta;
}

RAYX_FN_ACC
inline complex::Complex computeR(complex::Complex eta, double structureFactorReFH, double structureFactorImFH, double structureFactorReFHC,
                                 double structureFactorImFHC) {
    complex::Complex one(1.0, 0.0);
    complex::Complex FH(structureFactorReFH, structureFactorImFH);
    complex::Complex FHC(structureFactorReFHC, structureFactorImFHC);

    if (complex::real(eta) > 0.0) {
        return (eta - sqrt(eta * eta - one)) * sqrt(FH / FHC);
    } else {
        return (eta + sqrt(eta * eta - one)) * sqrt(FH / FHC);
    }
}

}  // namespace rayx