Files
Jungfraujoch/tests/CrystalLatticeTest.cpp
2025-10-20 20:43:44 +02:00

174 lines
6.7 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <catch2/catch_all.hpp>
#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<CrystalLattice> 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));
}