// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "BraggPrediction.h" #include "../bragg_integration/SystematicAbsence.h" BraggPrediction::BraggPrediction(int max_reflections) : max_reflections(max_reflections), reflections(max_reflections) {} const std::vector &BraggPrediction::GetReflections() const { return reflections; } int BraggPrediction::Calc(const DiffractionExperiment &experiment, const CrystalLattice &lattice, const BraggPredictionSettings &settings) { const auto geom = experiment.GetDiffractionGeometry(); const auto det_width_pxl = static_cast(experiment.GetXPixelsNum()); const auto det_height_pxl = static_cast(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 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; int i = 0; for (int h = -settings.max_hkl; h <= settings.max_hkl; h++) { // Precompute A* h contribution const float Ah_x = Astar.x * h; const float Ah_y = Astar.y * h; const float Ah_z = Astar.z * h; for (int k = -settings.max_hkl; k <= settings.max_hkl; k++) { // Accumulate B* k contribution const float AhBk_x = Ah_x + Bstar.x * k; const float AhBk_y = Ah_y + Bstar.y * k; const float AhBk_z = Ah_z + Bstar.z * k; 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; float recip_x = AhBk_x + Cstar.x * l; float recip_y = AhBk_y + Cstar.y * l; float recip_z = AhBk_z + Cstar.z * l; float recip_sq = recip_x * recip_x + recip_y * recip_y + recip_z * recip_z; if (recip_sq > one_over_dmax_sq) continue; float S_x = recip_x + S0.x; float S_y = recip_y + S0.y; float S_z = recip_z + S0.z; float S_len = sqrtf(S_x * S_x + S_y * S_y + S_z * S_z); float dist_ewald_sphere = std::fabs(S_len - one_over_wavelength); if (dist_ewald_sphere <= settings.ewald_dist_cutoff ) { // Inlined RecipToDector with rot1 and rot2 (rot3 = 0) // Apply rotation matrix transpose float S_rot_x = rot[0] * S_x + rot[1] * S_y + rot[2] * S_z; float S_rot_y = rot[3] * S_x + rot[4] * S_y + rot[5] * S_z; float S_rot_z = rot[6] * S_x + rot[7] * S_y + rot[8] * S_z; if (S_rot_z <= 0) continue; // Project to detector coordinates // Assume detector is along x,y,z coordinates after rotation 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 d = 1.0f / sqrtf(recip_sq); reflections[i] = Reflection{ .h = h, .k = k, .l = l, .delta_phi_deg = NAN, .predicted_x = x, .predicted_y = y, .d = d, .dist_ewald = dist_ewald_sphere, .rlp = 1.0, .partiality = 1.0, .zeta = 1.0 }; ++i; } } } } return i; }