v1.0.0-rc.166 (#76)
Build Packages / Unit tests (push) Successful in 1h22m15s
Build Packages / build:windows:nocuda (push) Successful in 18m0s
Build Packages / build:windows:cuda (push) Successful in 20m30s
Build Packages / build:viewer-tgz:cpu (push) Successful in 10m32s
Build Packages / build:viewer-tgz:cuda (push) Successful in 11m39s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 8m55s
Build Packages / build:rugnux:windows (push) Successful in 11m25s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 20m6s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 16m27s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 20m19s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 15m34s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 20m25s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 19m36s
Build Packages / build:rpm (rocky8) (push) Successful in 17m43s
Build Packages / build:rpm (rocky9) (push) Successful in 13m34s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 21m28s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 18m19s
Build Packages / DIALS test (push) Successful in 12m36s
Build Packages / XDS test (durin plugin) (push) Successful in 6m56s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 6m48s
Build Packages / XDS test (neggia plugin) (push) Successful in 6m7s
Build Packages / Generate python client (push) Successful in 11s
Build Packages / Build documentation (push) Successful in 36s
Build Packages / Create release (push) Skipped
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 5m11s

* `rugnux --mode calibration` writes `<prefix>.json` beside the `.poni`, whose `dataset_settings` member is a `jfjoch_broker` `dataset_settings` body as it stands.
* `rugnux` and `jfjoch_viewer` read PILATUS miniCBF sweeps natively, without conversion.
* Masters written by other facilities open, including Eiger 1.x and third-party NXmx variants.
* `rugnux` measures the beam centre on every run, and indexes with it when the file's value indexes nothing.
* A detector swung out on a 2theta arm is placed where the file says it stands, and the calibration can hold the tilt fixed.
* `rugnux` writes the unmerged MTZ by default, and a P1 merge beside it, so a wrong space group can be re-merged without reprocessing.
* Significant improvements to symmetry handling in `rugnux`: the lattice, the point group, the setting and the systematic absences.
* The `rugnux` report gives the resolution the CC1/2 fit reached, beside the range the reflections were written to.
* The `rugnux` report gives the twinning statistics measured before the space group was decided, beside the ones measured after.
* The `rugnux` report gives the strong-direction diffraction limit, and warns when CC1/2 is not monotone with resolution.
* `rugnux` ranks screw axes on the evidence their absences carry, rather than on how many control reflections a candidate happens to have.
* Twinning is no longer reported when the L-test contradicts it.
* The `rugnux` report gives the detector tilt, the measured tilt and the direct beam beside the beam centre, and a post-refined beam centre is judged against the run's own measurement rather than the file's.
* `--no-refine-tilt` holds the detector tilt at the value in the file, instead of zeroing it, when the calibration starts from the spots.
* The `jfjoch_viewer` grid scan view draws the cells in the proportion of the scan steps, so the map has the shape of the scanned area.

Reviewed-on: #76
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
This commit was merged in pull request #76.
This commit is contained in:
2026-09-02 21:17:31 +02:00
committed by leonarski_f
parent 511be0c366
commit 680c36c20d
383 changed files with 20910 additions and 3936 deletions
+67 -11
View File
@@ -2,16 +2,39 @@
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochMath.h"
#include <algorithm>
#include <cmath>
#include "DiffractionGeometry.h"
#include "RawToConvertedGeometry.h"
RotMatrix PoniRotMatrix(float rot1, float rot2, float rot3) {
return RotMatrix(-rot3, {0,0,1})
* RotMatrix(-rot2, {1,0,0})
* RotMatrix(rot1, {0,1,0});
}
void PoniAnglesFromMatrix(const RotMatrix &rot_matrix, float &rot1, float &rot2, float &rot3) {
const Coord fast = rot_matrix.Column(0);
const Coord slow = rot_matrix.Column(1);
const Coord normal = rot_matrix.Column(2);
rot2 = asinf(std::clamp(-slow.z, -1.0f, 1.0f));
if (fabsf(cosf(rot2)) < 1e-6f) {
// Gimbal lock: only rot1 +- rot3 is determined, so put it all into rot1.
rot1 = atan2f(normal.x, fast.x);
rot3 = 0.0f;
} else {
rot1 = atan2f(-fast.z, normal.z);
rot3 = atan2f(slow.x, slow.y);
}
}
Coord DiffractionGeometry::LabCoord(float x, float y) const {
Coord detectorCoord = {(x - beam_x_pxl) * pixel_size_mm ,
(y - beam_y_pxl) * pixel_size_mm ,
det_distance_mm};
return poni_rot * detectorCoord;
return det_matrix * detectorCoord;
}
std::pair<float, float> DiffractionGeometry::GetDirectBeam_pxl() const {
@@ -28,7 +51,7 @@ Coord DiffractionGeometry::DetectorToRecip(float x, float y) const {
std::pair<float, float> DiffractionGeometry::RecipToDetector(const Coord &recip) const {
auto S_unrotated = recip + GetScatteringVector();
auto S = poni_rot.transpose() * S_unrotated;
auto S = det_matrix.transpose() * S_unrotated;
if (S.z <= 0)
return {NAN, NAN};
@@ -180,27 +203,25 @@ float DiffractionGeometry::AngleFromEwaldSphere_deg(const Coord &p0) const {
return angle_deg(p_star, p0);
}
void DiffractionGeometry::UpdatePoniRotMatrix() {
poni_rot = RotMatrix(-poni_rot_3, {0,0,1})
* RotMatrix(-poni_rot_2, {1,0,0})
* RotMatrix(poni_rot_1, {0,1,0});
void DiffractionGeometry::UpdateDetectorMatrix() {
det_matrix = PoniRotMatrix(poni_rot_1, poni_rot_2, poni_rot_3) * orientation.Matrix();
}
DiffractionGeometry &DiffractionGeometry::PoniRot1_rad(float input) {
poni_rot_1 = input;
UpdatePoniRotMatrix();
UpdateDetectorMatrix();
return *this;
}
DiffractionGeometry &DiffractionGeometry::PoniRot2_rad(float input) {
poni_rot_2 = input;
UpdatePoniRotMatrix();
UpdateDetectorMatrix();
return *this;
}
DiffractionGeometry &DiffractionGeometry::PoniRot3_rad(float input) {
poni_rot_3 = input;
UpdatePoniRotMatrix();
UpdateDetectorMatrix();
return *this;
}
@@ -216,6 +237,41 @@ float DiffractionGeometry::GetPoniRot3_rad() const {
return poni_rot_3;
}
DiffractionGeometry &DiffractionGeometry::Orientation(const DetectorOrientation &input) {
orientation = input;
UpdateDetectorMatrix();
return *this;
}
DetectorOrientation DiffractionGeometry::GetOrientation() const {
return orientation;
}
DiffractionGeometry &DiffractionGeometry::DetectorAxes(const Coord &fast, const Coord &slow) {
const Coord f = fast.Normalize();
const Coord s = slow.Normalize();
// The normal is not free: it is the sample->PONI direction, and whether it is +fast x slow or
// -fast x slow is exactly whether the stored image is mirrored, which the orientation already says.
const Coord n = orientation.IsMirrorY() ? -(f % s) : (f % s);
PoniAnglesFromMatrix(RotMatrix(f, s, n) * orientation.Matrix().transpose(),
poni_rot_1, poni_rot_2, poni_rot_3);
UpdateDetectorMatrix();
return *this;
}
Coord DiffractionGeometry::GetFastAxis() const {
return det_matrix.Column(0);
}
Coord DiffractionGeometry::GetSlowAxis() const {
return det_matrix.Column(1);
}
Coord DiffractionGeometry::GetNormalAxis() const {
return det_matrix.Column(2);
}
std::pair<float, float> DiffractionGeometry::ResPhiToPxl(float d_A, float phi_rad) const {
// Guard invalid inputs
if (wavelength_A <= 0.0f || d_A <= wavelength_A / 2.0f)
@@ -241,8 +297,8 @@ Coord DiffractionGeometry::ProjectToEwaldSphere(const Coord &p0) const {
return S - S0;
}
const RotMatrix &DiffractionGeometry::GetPoniRotMatrix() const {
return poni_rot;
const RotMatrix &DiffractionGeometry::GetDetectorMatrix() const {
return det_matrix;
}
std::optional<GoniometerAxis> DiffractionGeometry::GetRotation() const {