Files
Jungfraujoch/viewer/windows/JFJochCalibrationResultWindow.cpp
leonarski_f 84228bf8be
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
v1.0.0-rc.173 (#83)
* 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>
2026-09-29 15:57:32 +02:00

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);
}