// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include #include "../common/DiffractionGeometry.h" #include "../common/DiffractionExperiment.h" TEST_CASE("RecipToDetector_1", "[LinearAlgebra][Coord]") { DiffractionExperiment x(DetJF(8, 2)); x.BeamX_pxl(1024).BeamY_pxl(1024).DetectorDistance_mm(120); DiffractionGeometry geom = x.GetDiffractionGeometry(); float pos_x = 512, pos_y = 512; auto recip = geom.DetectorToRecip(pos_x, pos_y); auto [proj_x, proj_y] = geom.RecipToDector(recip); REQUIRE(proj_x == Catch::Approx(pos_x)); REQUIRE(proj_y == Catch::Approx(pos_y)); REQUIRE((recip - geom.DetectorToRecip(proj_x, proj_y)).Length() < 0.00000001f); REQUIRE(std::fabs(geom.DistFromEwaldSphere(recip)) < 4e-4); } TEST_CASE("RecipToDetector_2", "[LinearAlgebra][Coord]") { DiffractionExperiment x(DetJF(8, 2)); x.BeamX_pxl(1024).BeamY_pxl(1024).DetectorDistance_mm(120); float pos_x = 1023, pos_y = 1023; DiffractionGeometry geom = x.GetDiffractionGeometry(); auto recip = geom.DetectorToRecip(pos_x, pos_y); auto [proj_x, proj_y] = geom.RecipToDector(recip); REQUIRE(proj_x == Catch::Approx(pos_x)); REQUIRE(proj_y == Catch::Approx(pos_y)); REQUIRE((recip - geom.DetectorToRecip(proj_x, proj_y)).Length() < 0.00000001f); REQUIRE(std::fabs(geom.DistFromEwaldSphere(recip)) < 4e-4); } TEST_CASE("RecipToDetector_3", "[LinearAlgebra][Coord]") { DiffractionExperiment x(DetJF(8, 2)); x.BeamX_pxl(1024).BeamY_pxl(1024).DetectorDistance_mm(120); float pos_x = 30, pos_y = 30; DiffractionGeometry geom = x.GetDiffractionGeometry(); auto recip = geom.DetectorToRecip(pos_x, pos_y); auto [proj_x, proj_y] = geom.RecipToDector(recip); REQUIRE(proj_x == Catch::Approx(pos_x)); REQUIRE(proj_y == Catch::Approx(pos_y)); REQUIRE((recip - geom.DetectorToRecip(proj_x, proj_y)).Length() < 0.00000001f); REQUIRE(std::fabs(geom.DistFromEwaldSphere(recip)) < 4e-4); } TEST_CASE("DiffractionGeometry_Phi","") { DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(75).IncidentEnergy_keV(WVL_1A_IN_KEV); x.BeamX_pxl(1000).BeamY_pxl(1000); DiffractionGeometry geom = x.GetDiffractionGeometry(); CHECK(geom.Phi_rad(2000, 1000) * (180.0 / M_PI) == Catch::Approx(0.0)); CHECK(geom.Phi_rad(2000, 0) * (180.0 / M_PI) == Catch::Approx(315.0f)); CHECK(geom.Phi_rad(1000, 0) * (180.0 / M_PI) == Catch::Approx(270.0f)); CHECK(geom.Phi_rad(0, 0) * (180.0 / M_PI) == Catch::Approx(225.0f)); CHECK(geom.Phi_rad(0, 1000) * (180.0 / M_PI) == Catch::Approx(180.0f)); CHECK(geom.Phi_rad(1000, 2000) * (180.0 / M_PI) == Catch::Approx(90.f)); CHECK(geom.Phi_rad(2000, 2000) * (180.0 / M_PI) == Catch::Approx(45.0f)); } TEST_CASE("DiffractionGeometry_Cos2Theta","") { DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(75).IncidentEnergy_keV(WVL_1A_IN_KEV); x.BeamX_pxl(1000).BeamY_pxl(1000); DiffractionGeometry geom = x.GetDiffractionGeometry(); // det distance == 1000 pixel // theta = 30 deg // tan(2 * theta) = sqrt(3) REQUIRE(cosf(geom.TwoTheta_rad(1000, 1000 * (1.0 + sqrt(3)))) == Catch::Approx(0.5f)); } TEST_CASE("DiffractionGeometry_PxlToRes","") { DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(75).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); // sin(theta) = 1/2 // theta = 30 deg // tan(2 * theta) = sqrt(3) REQUIRE(geom.PxlToRes( 0, 1000 * sqrt(3)) == Catch::Approx(1.0)); // sin(theta) = 1/4 // theta = 14.47 deg // tan(2 * theta) = 0.55328333517 REQUIRE(geom.PxlToRes(1000 * 0.55328333517 * cosf(1), 1000 * 0.55328333517 * sinf(1)) == Catch::Approx(2.0)); } TEST_CASE("DiffractionGeometry_ResToPxl","") { DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(75).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); // sin(theta) = 1/2 // theta = 30 deg // tan(2 * theta) = sqrt(3) REQUIRE(geom.ResToPxl(1.0) == Catch::Approx(1000 * sqrt(3))); // sin(theta) = 1/4 // theta = 14.47 deg // tan(2 * theta) = 0.55328333517 REQUIRE(geom.ResToPxl(2.0) == Catch::Approx(1000 * 0.55328333517)); } TEST_CASE("DiffractionGeometry_SolidAngleCorrection","") { DiffractionExperiment x; x.IncidentEnergy_keV(WVL_1A_IN_KEV); x.BeamX_pxl(1000).BeamY_pxl(1000).DetectorDistance_mm(75); DiffractionGeometry geom = x.GetDiffractionGeometry(); REQUIRE(geom.CalcAzIntSolidAngleCorr(0.0) == 1.0f); REQUIRE(geom.CalcAzIntSolidAngleCorr(2 * M_PI) == Catch::Approx(0.5f * 0.5f * 0.5f)); // theta = 30 deg // cos (2 * theta) = 1/2 REQUIRE(geom.CalcAzIntSolidAngleCorr(1000, 1000) == 1.0f); REQUIRE(geom.CalcAzIntSolidAngleCorr(1000 * (1.0 + sqrt(3)), 1000) == Catch::Approx(0.5f * 0.5f * 0.5f)); } TEST_CASE("DiffractionGeometry_PolarizationCorrection","") { DiffractionExperiment x; x.IncidentEnergy_keV(WVL_1A_IN_KEV); x.BeamX_pxl(1000).BeamY_pxl(1000).DetectorDistance_mm(75); DiffractionGeometry geom = x.GetDiffractionGeometry(); // Circular polarization 0.5*(1+cos(2theta)^2) x.PolarizationFactor(0); REQUIRE(geom.CalcAzIntPolarizationCorr(1000 * (1.0 + sqrt(3)), 1000, 0) == Catch::Approx(0.5f * (1 + 0.5f * 0.5f))); REQUIRE(geom.CalcAzIntPolarizationCorr(1000, 1000 * (1.0 + sqrt(3)), 0) == Catch::Approx(0.5f * (1 + 0.5f * 0.5f))); // Horizontal polarization x.PolarizationFactor(1); // No correction in vertical direction REQUIRE(geom.CalcAzIntPolarizationCorr(1000, 1000 * (1.0 + sqrt(3)), 1) == Catch::Approx(1.0f)); REQUIRE(geom.CalcAzIntPolarizationCorr(1000, 1000 * (1.0 - sqrt(3)), 1) == Catch::Approx(1.0f)); // cos(2*theta)^2 in horizontal direction REQUIRE(geom.CalcAzIntPolarizationCorr(1000 * (1.0 + sqrt(3)), 1000, 1) == Catch::Approx(0.5f * 0.5f)); REQUIRE(geom.CalcAzIntPolarizationCorr(1000 * (1.0 - sqrt(3)), 1000, 1) == Catch::Approx(0.5f * 0.5f)); } TEST_CASE("DiffractionGeometry_AngleFromEwaldSphere") { DiffractionGeometry geom; geom.Wavelength_A(1.0); // Center of Ewald sphere == (0,0,-1) // Points on Ewald sphere REQUIRE(geom.AngleFromEwaldSphere_deg(Coord(1, 0, -1)) == 0.0f); REQUIRE(geom.AngleFromEwaldSphere_deg(Coord(1.0f / sqrtf(2.0f), 1.0f / sqrtf(2.0f), -1)) == 0.0f); REQUIRE(geom.AngleFromEwaldSphere_deg(Coord(1, 0, 1)) == Catch::Approx(90.0f)); REQUIRE(geom.AngleFromEwaldSphere_deg(Coord(-sqrtf(2.0f), 0, 0)) == Catch::Approx(45.0f)); REQUIRE(geom.AngleFromEwaldSphere_deg(Coord(-sqrtf(3.0f), 0, 0)) == Catch::Approx(60.0f)); float cos_1deg = cosf(1.0f * M_PI / 180.0f); float sin_1deg = sinf(1.0f * M_PI / 180.0f); REQUIRE(fabsf(geom.AngleFromEwaldSphere_deg((Coord(cos_1deg - sin_1deg, 0, -(cos_1deg + sin_1deg)))) - 1.0f) < 0.0005); // Cannot be rotated to fit into the Ewald sphere REQUIRE(isnanf(geom.AngleFromEwaldSphere_deg(Coord(0, 0, 1)))); } TEST_CASE("DiffractionGeometry_AngleFromEwaldSphere_Wvl2A") { DiffractionGeometry geom; geom.BeamX_pxl(1000).BeamY_pxl(1000).DetectorDistance_mm(100).Wavelength_A(2.0); CHECK(geom.AngleFromEwaldSphere_deg(geom.DetectorToRecip(300,300)) < 0.05f); CHECK(geom.AngleFromEwaldSphere_deg(geom.DetectorToRecip(200,1700)) < 0.05f); CHECK(geom.AngleFromEwaldSphere_deg(geom.DetectorToRecip(1200,1800)) < 0.05f); CHECK(geom.AngleFromEwaldSphere_deg(geom.DetectorToRecip(1500,100)) < 0.05f); } TEST_CASE("DiffractionGeometry_ProjectToEwaldSphere") { DiffractionGeometry geom; geom.BeamX_pxl(1000).BeamY_pxl(437).DetectorDistance_mm(100).Wavelength_A(2.0); Coord p0 = geom.DetectorToRecip(300,300); Coord p1 = geom.ProjectToEwaldSphere(p0); REQUIRE(p0.x == Catch::Approx(p1.x)); REQUIRE(p0.y == Catch::Approx(p1.y)); REQUIRE(p0.z == Catch::Approx(p1.z)); Coord p2 = Coord(1,0,0); REQUIRE(std::fabs(geom.DistFromEwaldSphere(p2) > 0.01)); REQUIRE(std::fabs(geom.DistFromEwaldSphere(geom.ProjectToEwaldSphere(p2))) < 0.0001); } TEST_CASE("DiffractionGeometry_DirectBeam") { DiffractionGeometry geom; geom.Wavelength_A(1.0); geom.BeamX_pxl(1230).BeamY_pxl(1450); auto [x, y] = geom.GetDirectBeam_pxl(); REQUIRE(x == Catch::Approx(1230.0f)); REQUIRE(y == Catch::Approx(1450.0f)); } TEST_CASE("DiffractionGeometry_DirectBeam_RotZ") { DiffractionGeometry geom; geom.Wavelength_A(1.0); geom.BeamX_pxl(1230).BeamY_pxl(1450); geom.PoniRot3_rad(-M_PI_2); auto [x, y] = geom.GetDirectBeam_pxl(); REQUIRE(x == Catch::Approx(1230.0f)); REQUIRE(y == Catch::Approx(1450.0f)); } TEST_CASE("DiffractionGeometry_DirectBeam_RotY") { DiffractionGeometry geom; geom.Wavelength_A(1.0); geom.DetectorDistance_mm(100); geom.PixelSize_mm(1.0); geom.BeamX_pxl(1230).BeamY_pxl(1450); geom.PoniRot2_rad(-M_PI_4); // 45 deg rotation auto [x, y] = geom.GetDirectBeam_pxl(); CHECK(x == Catch::Approx(1230.0f)); // no Change for X CHECK(y >1450.0f); } TEST_CASE("DiffractionGeometry_PONI","") { /* poni_version: 2 Detector: Eiger4M Detector_config: {} Distance: 1.0 Poni1: 0.075 Poni2: 0.150 Rot1: 0.0 Rot2: 0.0 Rot3: 0.0 Wavelength: 1e-10 */ // PyFAI uses nm^-1 for Q? DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(1000).BeamX_pxl(2000).BeamY_pxl(1000).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); float diff_800_400 = fabs(geom.PxlToQ( 800,400)*10.0 - 6.295358803860941); float diff_400_800 = fabs(geom.PxlToQ( 400,800)*10.0 - 7.554628215027982); float diff_1300_2000 = fabs(geom.PxlToQ( 1300,2000)*10.0 - 5.73479724964891); REQUIRE(diff_800_400 < 0.01); REQUIRE(diff_400_800 < 0.01); REQUIRE(diff_1300_2000 < 0.01); } TEST_CASE("DiffractionGeometry_PONI_phi","") { /* poni_version: 2 Detector: Eiger4M Detector_config: {} Distance: 1.0 Poni1: 0.075 Poni2: 0.150 Rot1: 0.0 Rot2: 0.0 Rot3: 0.0 Wavelength: 1e-10 */ // PyFAI uses nm^-1 for Q? DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(1000).BeamX_pxl(2000).BeamY_pxl(1000).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); float phi_2000_0 = fabs(geom.Phi_rad(2000,0) - 2 * M_PI + 1.5702959937284997); float phi_2000_2000 = fabs(geom.Phi_rad(2000,2000) - 1.5702964938446844); float phi_0_1000 = fabs(geom.Phi_rad(0,1000) - 3.1413425992666903); float phi_2000_1300 = fabs(geom.Phi_rad(1300,2000) - 2.1809518509415025); CHECK(phi_2000_0 < 0.001); CHECK(phi_2000_2000 < 0.001); CHECK(phi_0_1000 < 0.001); CHECK(phi_2000_1300 < 0.001); } TEST_CASE("DiffractionGeometry_PONI_phi_rot3","") { /* poni_version: 2 Detector: Eiger4M Detector_config: {} Distance: 1.0 Poni1: 0.075 Poni2: 0.150 Rot1: 0.0 Rot2: 0.0 Rot3: 0.5 Wavelength: 1e-10 */ // PyFAI uses nm^-1 for Q? DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(1000).BeamX_pxl(2000).BeamY_pxl(1000).IncidentEnergy_keV(WVL_1A_IN_KEV) .PoniRot3_rad(0.5); DiffractionGeometry geom = x.GetDiffractionGeometry(); REQUIRE(geom.GetPoniRot3_rad() == Catch::Approx(0.5f)); float phi_800_400 = fabs(geom.Phi_rad(800,400) - 3.105073518019684); float phi_2000_1300 = fabs(geom.Phi_rad(1300,2000) - 1.6809518509415027); CHECK(phi_800_400 < 0.001); CHECK(phi_2000_1300 < 0.001); } TEST_CASE("DiffractionGeometry_PONI_phi_rot1_rot2_rot3","") { /* poni_version: 2 Detector: Eiger4M Detector_config: {} Distance: 1.0 Poni1: 0.075 Poni2: 0.150 Rot1: 0.2 Rot2: 0.1 Rot3: 0.5 Wavelength: 1e-10 */ // PyFAI uses nm^-1 for Q? DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(1000).BeamX_pxl(2000).BeamY_pxl(1000).IncidentEnergy_keV(WVL_1A_IN_KEV) .PoniRot1_rad(0.2).PoniRot2_rad(-0.1).PoniRot3_rad(0.5); DiffractionGeometry geom = x.GetDiffractionGeometry(); REQUIRE(geom.GetPoniRot1_rad() == Catch::Approx(0.2f)); REQUIRE(geom.GetPoniRot2_rad() == Catch::Approx(-0.1f)); REQUIRE(geom.GetPoniRot3_rad() == Catch::Approx(0.5f)); float phi_800_400 = fabs(geom.Phi_rad(800,400) - 2 * M_PI + 1.4175001633470816); float phi_2000_1300 = fabs(geom.Phi_rad(1300,2000) - 2 * M_PI + 0.6630282166663707); CHECK(phi_800_400 < 0.001); CHECK(phi_2000_1300 < 0.001); } TEST_CASE("DiffractionGeometry_PONI_rot1","") { /* poni_version: 2 Detector: Eiger4M Detector_config: {} Distance: 1.0 Poni1: 0.075 Poni2: 0.150 Rot1: 0.2 Rot2: 0.0 Rot3: 0.0 Wavelength: 1e-10 */ // PyFAI uses nm^-1 for Q? DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(1000).BeamX_pxl(2000).BeamY_pxl(1000).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); geom.PoniRot1_rad(0.2); float diff_800_400 = fabs(geom.PxlToQ( 800,400)*10.0 - 7.471276390173706); float diff_400_800 = fabs(geom.PxlToQ( 400,800)*10.0 - 5.148411999405654); float diff_1300_2000 = fabs(geom.PxlToQ( 1300,2000)*10.0 - 10.37635963741911); CHECK(diff_800_400 < 0.01); CHECK(diff_400_800 < 0.01); CHECK(diff_1300_2000 < 0.01); } TEST_CASE("DiffractionGeometry_PONI_rot1_rot2","") { /* poni_version: 2 Detector: Eiger4M Detector_config: {} Distance: 1.0 Poni1: 0.075 Poni2: 0.150 Rot1: 0.2 Rot2: 0.1 Rot3: 0.0 Wavelength: 1e-10 */ // PyFAI uses nm^-1 for Q? DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(1000).BeamX_pxl(2000).BeamY_pxl(1000).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); geom.PoniRot1_rad(0.2).PoniRot2_rad(-0.1); float diff_800_400 = fabs(geom.PxlToQ( 800,400)*10.0 - 11.412737079654118); float diff_400_800 = fabs(geom.PxlToQ( 400,800)*10.0 - 8.805012278158177); float diff_1300_2000 = fabs(geom.PxlToQ( 1300,2000)*10.0 - 9.363455481328781); CHECK(diff_800_400 < 0.01); CHECK(diff_400_800 < 0.01); CHECK(diff_1300_2000 < 0.01); } TEST_CASE("DiffractionGeometry_PyFAI_Solid_angle","") { /* poni_version: 2 Detector: Eiger4M Detector_config: {} Distance: 0.2 Poni1: 0.075 Poni2: 0.150 Rot1: 0.0 Rot2: 0.0 Rot3: 0.0 Wavelength: 1e-10 */ // Not sure why, but PyFAI solidAngleArray doesn't take into account poni rotation (???) DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(200).BeamX_pxl(2000).BeamY_pxl(1000).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); float diff_100_100 = fabs(geom.CalcAzIntSolidAngleCorr( 100,100) - 0.4844596502755233); CHECK(diff_100_100 < 0.0002); float diff_400_800 = fabs(geom.CalcAzIntSolidAngleCorr( 400,800)- 0.6267921080721112); CHECK(diff_400_800 < 0.0002); } TEST_CASE("ResPhiToPxl") { DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(75).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); auto out = geom.ResPhiToPxl(1.0, 0); CHECK(geom.PxlToRes(out.first, out.second) == Catch::Approx(1.0)); CHECK(fabs(geom.Phi_rad(out.first, out.second)) < 0.001 ); out = geom.ResPhiToPxl(1.0, M_PI); CHECK(geom.PxlToRes(out.first, out.second) == Catch::Approx(1.0)); CHECK(fabs(geom.Phi_rad(out.first, out.second) - M_PI) < 0.001 ); out = geom.ResPhiToPxl(2.0, 0.7567); CHECK(geom.PxlToRes(out.first, out.second) == Catch::Approx(2.0)); CHECK(fabs(geom.Phi_rad(out.first, out.second) - 0.7567) < 0.001 ); } TEST_CASE("ResPhiToPxl_poni_rot") { DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(75).IncidentEnergy_keV(WVL_1A_IN_KEV); DiffractionGeometry geom = x.GetDiffractionGeometry(); geom.PoniRot3_rad(0.5).PoniRot2_rad(-0.1).PoniRot2_rad(0.3); auto out = geom.ResPhiToPxl(1.0, 0); CHECK(geom.PxlToRes(out.first, out.second) == Catch::Approx(1.0)); CHECK(fabs(geom.Phi_rad(out.first, out.second)) < 0.001 ); out = geom.ResPhiToPxl(1.0, M_PI); CHECK(geom.PxlToRes(out.first, out.second) == Catch::Approx(1.0)); CHECK(fabs(geom.Phi_rad(out.first, out.second) - M_PI) < 0.001 ); out = geom.ResPhiToPxl(2.0, 0.7567); CHECK(geom.PxlToRes(out.first, out.second) == Catch::Approx(2.0)); CHECK(fabs(geom.Phi_rad(out.first, out.second) - 0.7567) < 0.001 ); }