141 lines
5.3 KiB
C++
141 lines
5.3 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "BraggPredictionRot.h"
|
|
#include "../bragg_integration/SystematicAbsence.h"
|
|
|
|
|
|
int BraggPredictionRot::Calc(const DiffractionExperiment &experiment, const CrystalLattice &lattice,
|
|
const BraggPredictionSettings &settings) {
|
|
|
|
const auto geom = experiment.GetDiffractionGeometry();
|
|
const auto det_width_pxl = static_cast<float>(experiment.GetXPixelsNum());
|
|
const auto det_height_pxl = static_cast<float>(experiment.GetYPixelsNum());
|
|
|
|
const float one_over_dmax = 1.0f / settings.high_res_A;
|
|
const float one_over_dmax_sq = one_over_dmax * one_over_dmax;
|
|
|
|
float one_over_wavelength = 1.0f / geom.GetWavelength_A();
|
|
|
|
const Coord Astar = lattice.Astar();
|
|
const Coord Bstar = lattice.Bstar();
|
|
const Coord Cstar = lattice.Cstar();
|
|
const Coord S0 = geom.GetScatteringVector();
|
|
|
|
std::vector<float> rot = geom.GetPoniRotMatrix().transpose().arr();
|
|
|
|
// Precompute detector geometry constants
|
|
float beam_x = geom.GetBeamX_pxl();
|
|
float beam_y = geom.GetBeamY_pxl();
|
|
float det_distance = geom.GetDetectorDistance_mm();
|
|
float pixel_size = geom.GetPixelSize_mm();
|
|
float F = det_distance / pixel_size;
|
|
|
|
const auto gon_opt = experiment.GetGoniometer();
|
|
if (!gon_opt.has_value())
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"BraggPredictionRotationCPU requires a goniometer axis");
|
|
const GoniometerAxis& gon = *gon_opt;
|
|
|
|
const Coord m2 = gon.GetAxis().Normalize();
|
|
const Coord m1 = (m2 % S0).Normalize();
|
|
const Coord m3 = (m1 % m2).Normalize();
|
|
|
|
const float m2_S0 = m2 * S0;
|
|
const float m3_S0 = m3 * S0;
|
|
|
|
int i = 0;
|
|
const float max_angle_rad = settings.max_angle * static_cast<float>(M_PI) / 180.f;
|
|
|
|
for (int h = -settings.max_hkl; h <= settings.max_hkl; h++) {
|
|
// Precompute A* h contribution
|
|
|
|
for (int k = -settings.max_hkl; k <= settings.max_hkl; k++) {
|
|
// Accumulate B* k contribution
|
|
|
|
for (int l = -settings.max_hkl; l <= settings.max_hkl; l++) {
|
|
if (systematic_absence(h, k, l, settings.centering))
|
|
continue;
|
|
|
|
if (i >= max_reflections)
|
|
continue;
|
|
Coord p0 = Astar * h + Bstar * k + Cstar * l;
|
|
|
|
float p0_sq = p0 * p0;
|
|
if (p0_sq <= 0.0f || p0_sq > one_over_dmax_sq)
|
|
continue;
|
|
|
|
const float p0_m1 = p0 * m1;
|
|
const float p0_m2 = p0 * m2;
|
|
const float p0_m3 = p0 * m3;
|
|
|
|
const float rho_sq = p0_sq - (p0_m2 * p0_m2);
|
|
|
|
const float p_m3 = (- p0_sq / 2 - p0_m2 * m2_S0) / m3_S0;
|
|
const float p_m2 = p0_m2;
|
|
const float p_m1_opt[2] = {
|
|
std::sqrt(rho_sq - p_m3 * p_m3),
|
|
-std::sqrt(rho_sq - p_m3 * p_m3)
|
|
};
|
|
|
|
// No solution for Laue equations
|
|
if ((rho_sq < p_m3 * p_m3) || (p0_sq > 4 * S0 * S0))
|
|
continue;
|
|
|
|
for (const auto& p_m1 : p_m1_opt) {
|
|
if (i >= max_reflections)
|
|
continue;
|
|
|
|
const float cosphi = (p_m1 * p0_m1 + p_m3 * p0_m3) / rho_sq;
|
|
const float sinphi = (p_m1 * p0_m3 - p_m3 * p0_m1) / rho_sq;
|
|
Coord p = m1 * p_m1 + m2 * p_m2 + m3 * p_m3; // p0 vector "rotated" to diffracting condition
|
|
Coord S = S0 + p;
|
|
|
|
float phi = -1.0f * std::atan2(sinphi, cosphi) * 180.0f / static_cast<float>(M_PI);
|
|
|
|
if (phi > max_angle_rad || phi < -max_angle_rad)
|
|
continue;
|
|
|
|
const float lorentz_reciprocal = std::fabs(m2 * (S % S0))/(S*S0);
|
|
|
|
Coord S_local = S0 + p0;
|
|
|
|
// Inlined RecipToDector with rot1 and rot2 (rot3 = 0)
|
|
// Apply rotation matrix transpose
|
|
float S_rot_x = rot[0] * S_local.x + rot[1] * S_local.y + rot[2] * S_local.z;
|
|
float S_rot_y = rot[3] * S_local.x + rot[4] * S_local.y + rot[5] * S_local.z;
|
|
float S_rot_z = rot[6] * S_local.x + rot[7] * S_local.y + rot[8] * S_local.z;
|
|
|
|
if (S_rot_z <= 0)
|
|
continue;
|
|
|
|
float x = beam_x + F * S_rot_x / S_rot_z;
|
|
float y = beam_y + F * S_rot_y / S_rot_z;
|
|
|
|
if ((x < 0) || (x >= det_width_pxl) || (y < 0) || (y >= det_height_pxl))
|
|
continue;
|
|
|
|
float dist_ewald_sphere = std::fabs(p0_sq - one_over_wavelength);
|
|
|
|
float d = 1.0f / sqrtf(p0_sq);
|
|
reflections[i] = Reflection{
|
|
.h = h,
|
|
.k = k,
|
|
.l = l,
|
|
.predicted_x = x,
|
|
.predicted_y = y,
|
|
.d = d,
|
|
.dist_ewald = dist_ewald_sphere,
|
|
.lp = lorentz_reciprocal,
|
|
.S_x = S.x,
|
|
.S_y = S.y,
|
|
.S_z = S.z
|
|
};
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return i;
|
|
}
|