Build Packages / Create release (push) Successful in 24s
Build Packages / build:viewer:macos-arm64:nocuda (push) Successful in 3m29s
Build Packages / build:rugnux:macos-arm64:nocuda (push) Successful in 2m43s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 8m27s
Build Packages / build:rugnux:linux-x86_64:cuda (push) Successful in 9m53s
Build Packages / build:viewer:linux-x86_64:nocuda (push) Successful in 9m58s
Build Packages / build:viewer:linux-x86_64:cuda (push) Successful in 11m22s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 13m39s
Build Packages / build:viewer:windows-x86_64:nocuda (push) Successful in 18m37s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 16m32s
Build Packages / build:viewer:windows-x86_64:cuda (push) Successful in 24m11s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 25m30s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 19m3s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 20m23s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 19m41s
Build Packages / Generate python client (push) Successful in 50s
Build Packages / Build documentation (push) Successful in 1m16s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 21m0s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 18m38s
Build Packages / build:rugnux:windows-x86_64:cuda (push) Successful in 14m33s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 17m55s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 20m50s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 18m38s
Build Packages / Unit tests (push) Successful in 1h46m14s
* jfjoch_broker: Optional per-dataset authentication - statistics, images and plots can require a bearer token, which jfjoch_viewer supports. * jfjoch_viewer: Dark mode and a theme-matched colour scheme, a magnifier panel, and simpler contrast and background controls. * Rugnux: Multiple performance improvements on GPU and CPU (CPU-only processing up to 40% faster, faster image decoding on ARM), with unchanged results. * Rugnux: `--model` rigid-body refinement runs on the GPU, and the model-validation check is faster and more reliable. * Rugnux: Improved scaling and merging - error model, outlier rejection, absorption correction and French-Wilson amplitudes now agree more closely with XDS and ctruncate. * Rugnux: Improved integration - radial background on powder and ice rings, crowded rotation data keep their reflections, and CPU-only builds integrate large unit cells as GPU builds do. * Rugnux: More robust detector geometry - measured beam centre, X-ray bandwidth and goniometer rate, and geometry refinement accepted only on significant evidence. * Rugnux: Merged files are written in the standard setting, or in the setting of a reference MTZ, structure-factor mmCIF or model, with its free-R flags. * Rugnux: Richer report - ice and powder rings, further lattices, superstructure candidates and mosaicity, with warnings worded as prompts to check. * Rugnux: Clear error messages when a data set needs more GPU or host memory than is available. Reviewed-on: #83 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
107 lines
5.2 KiB
C++
107 lines
5.2 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||
// SPDX-License-Identifier: GPL-3.0-only
|
||
|
||
#include "JFJochCalibrationResultWindow.h"
|
||
#include "../ViewerTheme.h"
|
||
|
||
#include <QFrame>
|
||
#include <QHBoxLayout>
|
||
#include <QHeaderView>
|
||
#include <QLabel>
|
||
#include <QTableWidget>
|
||
#include <QVBoxLayout>
|
||
|
||
#include "../../common/JFJochMath.h" // PI
|
||
|
||
namespace {
|
||
constexpr double RAD_TO_DEG = 180.0 / PI;
|
||
|
||
// A big-number "card" for the hero row, as in the merge-statistics window.
|
||
QWidget *MakeCard(const QString &value, const QString &caption, QWidget *parent) {
|
||
auto *card = new QFrame(parent);
|
||
card->setFrameShape(QFrame::StyledPanel);
|
||
auto *l = new QVBoxLayout(card);
|
||
l->setContentsMargins(12, 8, 12, 8);
|
||
l->setSpacing(0);
|
||
auto *v = new QLabel(value, card);
|
||
QFont f = v->font();
|
||
f.setPointSizeF(f.pointSizeF() * 2.2);
|
||
f.setBold(true);
|
||
v->setFont(f);
|
||
SetThemedStyleSheet(v, [] { return "color: " + AccentTextColor().name() + ";"; });
|
||
v->setAlignment(Qt::AlignCenter);
|
||
auto *c = new QLabel(caption, card);
|
||
c->setStyleSheet("color: gray;");
|
||
c->setAlignment(Qt::AlignCenter);
|
||
l->addWidget(v);
|
||
l->addWidget(c);
|
||
return card;
|
||
}
|
||
}
|
||
|
||
JFJochCalibrationResultWindow::JFJochCalibrationResultWindow(const QString &title,
|
||
const CalibrationResult &calibration,
|
||
const DiffractionGeometry &header,
|
||
const QString &poni_path,
|
||
QWidget *parent)
|
||
: QWidget(parent, Qt::Window) {
|
||
setWindowTitle("Detector calibration — " + title);
|
||
setAttribute(Qt::WA_DeleteOnClose);
|
||
resize(640, 380);
|
||
|
||
const DiffractionGeometry &g = calibration.geometry;
|
||
const double pxl_mm = g.GetPixelSize_mm();
|
||
|
||
auto *layout = new QVBoxLayout(this);
|
||
|
||
auto *hero = new QHBoxLayout();
|
||
hero->addWidget(MakeCard(QString::number(calibration.rms_radial_pxl, 'f', 2), "Radial rms [px]", this));
|
||
hero->addWidget(MakeCard(QString::number(calibration.beam_sigma_pxl, 'f', 3), "Beam σ [px]", this));
|
||
hero->addWidget(MakeCard(QString::number(calibration.ring_points), "Ring points", this));
|
||
layout->addLayout(hero);
|
||
|
||
auto *table = new QTableWidget(7, 4, this);
|
||
table->setHorizontalHeaderLabels({"Quantity", "Fitted", "From header", "Change"});
|
||
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
table->verticalHeader()->setVisible(false);
|
||
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||
|
||
// The PONI is the point of normal incidence, which is what the .poni file stores; the direct beam
|
||
// is where the beam actually lands, which is what every other program calls the beam centre. They
|
||
// part company by distance*tan(rot) as soon as the detector is tilted, so show both.
|
||
const auto [beam_x, beam_y] = g.GetDirectBeam_pxl();
|
||
const auto [header_beam_x, header_beam_y] = header.GetDirectBeam_pxl();
|
||
|
||
const struct { QString name; double fitted; double header; int digits; } rows[] = {
|
||
{"PONI x [px]", g.GetBeamX_pxl(), header.GetBeamX_pxl(), 3},
|
||
{"PONI y [px]", g.GetBeamY_pxl(), header.GetBeamY_pxl(), 3},
|
||
{"Rot1 [°]", g.GetPoniRot1_rad() * RAD_TO_DEG, header.GetPoniRot1_rad() * RAD_TO_DEG, 4},
|
||
{"Rot2 [°]", g.GetPoniRot2_rad() * RAD_TO_DEG, header.GetPoniRot2_rad() * RAD_TO_DEG, 4},
|
||
{"Distance [mm]", g.GetDetectorDistance_mm(), header.GetDetectorDistance_mm(), 4},
|
||
{"Direct beam x [px]", beam_x, header_beam_x, 3},
|
||
{"Direct beam y [px]", beam_y, header_beam_y, 3},
|
||
};
|
||
|
||
for (int i = 0; i < 7; i++) {
|
||
const double change = rows[i].fitted - rows[i].header;
|
||
table->setItem(i, 0, new QTableWidgetItem(rows[i].name));
|
||
table->setItem(i, 1, new QTableWidgetItem(QString::number(rows[i].fitted, 'f', rows[i].digits)));
|
||
table->setItem(i, 2, new QTableWidgetItem(QString::number(rows[i].header, 'f', rows[i].digits)));
|
||
table->setItem(i, 3, new QTableWidgetItem(
|
||
QStringLiteral("%1%2").arg(change >= 0 ? "+" : "").arg(change, 0, 'f', rows[i].digits)));
|
||
}
|
||
layout->addWidget(table);
|
||
|
||
auto *note = new QLabel(this);
|
||
note->setWordWrap(true);
|
||
note->setStyleSheet("color: gray;");
|
||
// In mm these are what the .poni file carries, so they are measured pyFAI's way - from the edge of
|
||
// the sensor, which puts the centre of pixel i at (i + 0.5) * pixel size - not from the centre of
|
||
// the first pixel as the [px] rows above (docs/DETECTOR_GEOMETRY.md).
|
||
note->setText(QStringLiteral("PONI x = %1 mm, PONI y = %2 mm.%3")
|
||
.arg((g.GetBeamX_pxl() + 0.5) * pxl_mm, 0, 'f', 4)
|
||
.arg((g.GetBeamY_pxl() + 0.5) * pxl_mm, 0, 'f', 4)
|
||
.arg(poni_path.isEmpty() ? QString() : " Written to " + poni_path));
|
||
layout->addWidget(note);
|
||
}
|