Files
Jungfraujoch/common/CrystalLattice.cpp
T
leonarski_f fc68a9baed
Build Packages / Unit tests (push) Skipped
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 8m34s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 10m0s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 10m23s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 10m23s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 11m16s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 11m49s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 8m32s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 9m15s
Build Packages / XDS test (durin plugin) (push) Successful in 7m16s
Build Packages / Generate python client (push) Successful in 16s
Build Packages / build:rpm (rocky9) (push) Successful in 10m12s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 47s
Build Packages / DIALS test (push) Successful in 10m18s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 5m46s
Build Packages / build:rpm (rocky8) (push) Successful in 1h41m2s
Build Packages / XDS test (neggia plugin) (push) Successful in 1h59m18s
v1.0.0-rc.146 (#56)
This is an UNSTABLE release. The release has significant modifications for data processing - in case of troubles go back to 1.0.0-rc.144.

jfjoch_process: Generate a dedicated file (_process.h5), which can be used as a replacement for the _master.h5 file for a reanalyzed dataset.
jfjoch_process: Improve the performance of scaling and merging, implement on the fly scaling.
jfjoch_writer: All final data analysis results are repopulated in the _master.h5 file.
jfjoch_scale: Dedicated tool for rescaling/merging existing data.
jfjoch_viewer: Fix bugs where pixel labels where displayed on a wrong pixel.

WARNING! Scaling and merging are experimental at the moment, and may not provide reasonable results for the time being.

Reviewed-on: #56
2026-05-28 18:48:35 +02:00

214 lines
5.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <cmath>
#include "CrystalLattice.h"
#include "JFJochException.h"
#include "gemmi/symmetry.hpp"
#include "gemmi/unitcell.hpp"
#define DEG_TO_RAD static_cast<float>(M_PI/180.0)
CrystalLattice::CrystalLattice(const UnitCell &cell) {
vec[0] = {cell.a, 0, 0};
vec[1] = {cell.b * cosf(cell.gamma * DEG_TO_RAD), cell.b * sinf(cell.gamma * DEG_TO_RAD), 0};
float cx = cell.c * cosf(cell.beta * DEG_TO_RAD);
float cy = cell.c
* (cosf(cell.alpha * DEG_TO_RAD) - cosf(cell.beta * DEG_TO_RAD) * cosf(cell.gamma * DEG_TO_RAD))
/ sinf(cell.gamma * DEG_TO_RAD);
vec[2] = {cx, cy, sqrtf(cell.c*cell.c-cx*cx-cy*cy)};
FixHandedness();
}
CrystalLattice::CrystalLattice(const Coord &a, const Coord &b, const Coord &c) {
vec[0] = a;
vec[1] = b;
vec[2] = c;
FixHandedness();
}
const Coord &CrystalLattice::Vec0() const {
return vec[0];
}
const Coord &CrystalLattice::Vec1() const {
return vec[1];
}
const Coord &CrystalLattice::Vec2() const {
return vec[2];
}
UnitCell CrystalLattice::GetUnitCell() const {
UnitCell cell{};
cell.a = vec[0].Length();
cell.b = vec[1].Length();
cell.c = vec[2].Length();
cell.alpha = angle_deg(vec[1], vec[2]);
cell.beta = angle_deg(vec[0], vec[2]);
cell.gamma = angle_deg(vec[0], vec[1]);
return cell;
}
std::vector<float> CrystalLattice::GetVector() const {
std::vector<float> output(9);
for (int i = 0; i < 3; i++) {
output[3 * i + 0] = vec[i].x;
output[3 * i + 1] = vec[i].y;
output[3 * i + 2] = vec[i].z;
}
return output;
}
float CrystalLattice::CalcVolume() const {
// Calculate the cell volume
// V = a · (b × c)
Coord cross_product = vec[1] % vec[2];
return vec[0] * cross_product;
}
void CrystalLattice::Sort() {
if (vec[0].Length() > vec[1].Length())
std::swap(vec[0], vec[1]);
if (vec[1].Length() > vec[2].Length())
std::swap(vec[1], vec[2]);
if (vec[0].Length() > vec[1].Length())
std::swap(vec[0], vec[1]);
}
void CrystalLattice::FixHandedness() {
if (CalcVolume() < 0)
vec[2] *= -1;
}
Coord CrystalLattice::Astar() const {
return (vec[1] % vec[2]) * (1.0f / CalcVolume());
}
Coord CrystalLattice::Bstar() const {
return (vec[2] % vec[0]) * (1.0f / CalcVolume());
}
Coord CrystalLattice::Cstar() const {
return (vec[0] % vec[1]) * (1.0f / CalcVolume());
}
CrystalLattice::CrystalLattice(float a, float b, float c, float alpha, float beta, float gamma)
: CrystalLattice(UnitCell{.a = a, .b = b, .c = c, .alpha = alpha, .beta = beta, .gamma = gamma}) {}
CrystalLattice::CrystalLattice(const std::vector<float> &input) {
if (input.size() != 9)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,"Wrong size of crystal lattice vector");
for (int i = 0; i < 3; i++) {
vec[i].x = input[3 * i + 0];
vec[i].y = input[3 * i + 1];
vec[i].z = input[3 * i + 2];
}
}
void CrystalLattice::ReorderABEqual() {
double la = vec[0].Length();
double lb = vec[1].Length();
double lc = vec[2].Length();
double dab = std::abs(la - lb);
double dbc = std::abs(lb - lc);
double dac = std::abs(la - lc);
Coord a = vec[0];
Coord b = vec[1];
Coord c = vec[2];
if (dbc < dab && dbc < dac) {
// b≈c → [b, c, a]
vec[0] = b;
vec[1] = c;
vec[2] = a;
} else if (dac < dab && dac < dbc) {
// a≈c → [a, c, b]
vec[0] = a;
vec[1] = c;
vec[2] = b;
} // else a≈b → keep [a, b, c]
FixHandedness();
}
void CrystalLattice::ReorderMonoclinic() {
// Enforce obtuse beta (>= 90°). Beta is the angle between a and c.
// Flip signs of a and b simultaneously to keep handedness and lengths unchanged,
// which maps beta -> 180° - beta.
float beta_now = angle_deg(vec[0], vec[2]);
if (beta_now < 90.0f) {
vec[0] *= -1.0f; // a -> -a
vec[1] *= -1.0f; // b -> -b (preserves cell volume sign)
// beta becomes 180 - beta_now (> 90°)
}
}
CrystalLattice CrystalLattice::Multiply(const RotMatrix &input) const {
CrystalLattice l;
l.vec[0] = input * vec[0];
l.vec[1] = input * vec[1];
l.vec[2] = input * vec[2];
l.FixHandedness();
return l;
}
CrystalLattice CrystalLattice::Multiply(const gemmi::Mat33 &c2p) const {
CrystalLattice l;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
l.vec[i][j] = c2p[i][0] * vec[0][j] + c2p[i][1] * vec[1][j] + c2p[i][2] * vec[2][j];
}
}
l.FixHandedness();
return l;
}
CrystalLattice CrystalLattice::FromPrimitive(char centering) const {
if (centering == 'P')
return *this;
return Multiply(gemmi::rot_as_mat33(gemmi::centred_to_primitive(centering)).inverse());
}
CrystalLattice CrystalLattice::ToPrimitive(char centering) const {
if (centering == 'P')
return *this;
return Multiply(gemmi::rot_as_mat33(gemmi::centred_to_primitive(centering)));
}
void CrystalLattice::Regularize(const gemmi::CrystalSystem &input) {
switch (input) {
case gemmi::CrystalSystem::Monoclinic:
ReorderMonoclinic();
break;
case gemmi::CrystalSystem::Tetragonal:
case gemmi::CrystalSystem::Hexagonal:
ReorderABEqual();
break;
default:
Sort();
FixHandedness();
break;
}
}
std::vector<float> CrystalLattice::GetUBMatrix() const {
const Coord astar = Astar();
const Coord bstar = Bstar();
const Coord cstar = Cstar();
return {
astar.x, bstar.x, cstar.x,
astar.y, bstar.y, cstar.y,
astar.z, bstar.z, cstar.z
};
}