// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include "../common/CrystalLattice.h" TEST_CASE("CrystalLattice") { CrystalLattice l(50,60,80, 90, 90, 90); REQUIRE(l.Vec0().Length() == Catch::Approx(50)); REQUIRE(l.Vec1().Length() == Catch::Approx(60)); REQUIRE(l.Vec2().Length() == Catch::Approx(80)); REQUIRE(angle_deg(l.Vec0(), l.Vec2()) == 90); REQUIRE(angle_deg(l.Vec0(), l.Vec1()) == 90); REQUIRE(angle_deg(l.Vec1(), l.Vec2()) == 90); auto uc0 = l.GetUnitCell(); REQUIRE(uc0.a == Catch::Approx(50)); REQUIRE(uc0.b == Catch::Approx(60)); REQUIRE(uc0.c == Catch::Approx(80)); REQUIRE(uc0.alpha == Catch::Approx(90)); REQUIRE(uc0.beta == Catch::Approx(90)); REQUIRE(uc0.gamma == Catch::Approx(90)); l = CrystalLattice(30, 40, 70, 90, 95, 90); REQUIRE(l.Vec0().Length() == Catch::Approx(30)); REQUIRE(l.Vec1().Length() == Catch::Approx(40)); REQUIRE(l.Vec2().Length() == Catch::Approx(70)); REQUIRE(angle_deg(l.Vec0(), l.Vec2()) == 95); REQUIRE(angle_deg(l.Vec0(), l.Vec1()) == 90); REQUIRE(angle_deg(l.Vec1(), l.Vec2()) == 90); auto uc1 = l.GetUnitCell(); REQUIRE(uc1.a == Catch::Approx(30)); REQUIRE(uc1.b == Catch::Approx(40)); REQUIRE(uc1.c == Catch::Approx(70)); REQUIRE(uc1.alpha == Catch::Approx(90)); REQUIRE(uc1.beta == Catch::Approx(95)); REQUIRE(uc1.gamma == Catch::Approx(90)); l = CrystalLattice(45, 45, 70, 90, 90, 120); REQUIRE(l.Vec0().Length() == Catch::Approx(45)); REQUIRE(l.Vec1().Length() == Catch::Approx(45)); REQUIRE(l.Vec2().Length() == Catch::Approx(70)); REQUIRE(angle_deg(l.Vec0(), l.Vec2()) == Catch::Approx(90)); REQUIRE(angle_deg(l.Vec0(), l.Vec1()) == Catch::Approx(120)); REQUIRE(angle_deg(l.Vec1(), l.Vec2()) == Catch::Approx(90)); auto uc2 = l.GetUnitCell(); REQUIRE(uc2.a == Catch::Approx(45)); REQUIRE(uc2.b == Catch::Approx(45)); REQUIRE(uc2.c == Catch::Approx(70)); REQUIRE(uc2.alpha == Catch::Approx(90)); REQUIRE(uc2.beta == Catch::Approx(90)); REQUIRE(uc2.gamma == Catch::Approx(120)); } TEST_CASE("CrystalLattice_Sort") { CrystalLattice l(80,60,50, 120, 90, 90); l.Sort(); REQUIRE(l.Vec0().Length() == Catch::Approx(50)); REQUIRE(l.Vec1().Length() == Catch::Approx(60)); REQUIRE(l.Vec2().Length() == Catch::Approx(80)); REQUIRE(angle_deg(l.Vec0(), l.Vec2()) == Catch::Approx(90)); REQUIRE(angle_deg(l.Vec0(), l.Vec1()) == Catch::Approx(120)); REQUIRE(angle_deg(l.Vec1(), l.Vec2()) == Catch::Approx(90)); } TEST_CASE("CrystalLattice_ReorderMonoclinic") { std::vector latt = { {85,70,60, 85, 90, 90}, {60,70,85, 90, 90, 85}, {60,85,70, 90, 85, 90}, {70,60,85, 90, 90, 85} }; for (const auto &l_in :latt) { CrystalLattice l = l_in; l.ReorderMonoclinic(); CHECK(l.Vec0().Length() == Catch::Approx(60)); CHECK(l.Vec1().Length() == Catch::Approx(85)); CHECK(l.Vec2().Length() == Catch::Approx(70)); CHECK(l.GetUnitCell().beta == Catch::Approx(95.0)); } } TEST_CASE("CrystalLattice_ReorderTetragonal") { CrystalLattice l(40,60,60, 90, 90, 90); l.ReorderABEqual(); REQUIRE(l.Vec0().Length() == Catch::Approx(60)); REQUIRE(l.Vec1().Length() == Catch::Approx(60)); REQUIRE(l.Vec2().Length() == Catch::Approx(40)); } TEST_CASE("CrystalLattice_ReorderTetragonal_ForHexagonal") { CrystalLattice l(40,60,60, 120, 90, 90); l.ReorderABEqual(); REQUIRE(l.Vec0().Length() == Catch::Approx(60)); REQUIRE(l.Vec1().Length() == Catch::Approx(60)); REQUIRE(l.Vec2().Length() == Catch::Approx(40)); } TEST_CASE("CrystalLattice_Handedness") { CrystalLattice l(Coord(1,0,0), Coord(0,1,0), Coord(0,0,-1)); REQUIRE(l.Vec0().x == Catch::Approx(1)); REQUIRE(l.Vec1().y == Catch::Approx(1)); REQUIRE(l.Vec2().z == Catch::Approx(1)); } TEST_CASE("CrystalLattice_Volume") { CrystalLattice l(50, 60, 80, 90, 90, 90); REQUIRE(l.CalcVolume() == 50 * 60 * 80); CrystalLattice l2(50,60,80, 90, 120, 90); float sin120 = std::sqrt(3) / 2; REQUIRE(l2.CalcVolume() == Catch::Approx(50 * 60 * 80 * sin120)); } TEST_CASE("CrystalLattice_Recip") { CrystalLattice l(50,60,80, 90, 90, 90); REQUIRE(l.Astar().Length() == Catch::Approx(1/50.0)); REQUIRE(l.Astar().x == Catch::Approx(1/50.0)); REQUIRE(l.Bstar().Length() == Catch::Approx(1/60.0)); REQUIRE(l.Bstar().y == Catch::Approx(1/60.0)); REQUIRE(l.Cstar().Length() == Catch::Approx(1/80.0)); REQUIRE(l.Cstar().z == Catch::Approx(1/80.0)); } TEST_CASE("CrystalLattice_ToPrimitive") { // Conventional cubic (take as I-conventional basis): a=b=c=50, orthogonal CrystalLattice conv(50, 50, 50, 90, 90, 90); const float Vconv = conv.CalcVolume(); REQUIRE(Vconv == Catch::Approx(50.f * 50.f * 50.f).margin(1e-4f)); CrystalLattice prim = conv.ToPrimitive('I'); // Volume halves (index 2) CHECK(prim.CalcVolume() == Catch::Approx(Vconv * 0.5f).margin(1e-4f)); // bcc primitive lengths and angles auto uc_p = prim.GetUnitCell(); const float a0 = 50.f; const float expected_len = a0 * std::sqrt(3.f) * 0.5f; // a*sqrt(3)/2 const float expected_angle = 109.4712206f; // arccos(-1/3) in degrees CHECK(uc_p.a == Catch::Approx(expected_len).margin(1e-3f)); CHECK(uc_p.b == Catch::Approx(expected_len).margin(1e-3f)); CHECK(uc_p.c == Catch::Approx(expected_len).margin(1e-3f)); CHECK(uc_p.alpha == Catch::Approx(expected_angle).margin(1e-3f)); CHECK(uc_p.beta == Catch::Approx(expected_angle).margin(1e-3f)); CHECK(uc_p.gamma == Catch::Approx(expected_angle).margin(1e-3f)); } TEST_CASE("CrystalLattice_FromPrimitive") { // Conventional cubic (take as I-conventional basis): a=b=c=50, orthogonal const float a0 = 50.f; const float expected_len = a0 * std::sqrt(3.f) * 0.5f; // a*sqrt(3)/2 const float expected_angle = 109.4712206f; // arccos(-1/3) in degrees CrystalLattice conv(expected_len, expected_len, expected_len, expected_angle, expected_angle, expected_angle); auto prim = conv.FromPrimitive('I'); auto uc_c = prim.GetUnitCell(); // Back to conventional cube with original volume CHECK(prim.CalcVolume() == Catch::Approx(a0 * a0 * a0).margin(1e-4f)); CHECK(uc_c.a == Catch::Approx(a0).margin(1e-3f)); CHECK(uc_c.b == Catch::Approx(a0).margin(1e-3f)); CHECK(uc_c.c == Catch::Approx(a0).margin(1e-3f)); CHECK(uc_c.alpha == Catch::Approx(90.f).margin(1e-3f)); CHECK(uc_c.beta == Catch::Approx(90.f).margin(1e-3f)); CHECK(uc_c.gamma == Catch::Approx(90.f).margin(1e-3f)); }