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>
166 lines
8.2 KiB
C++
166 lines
8.2 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||
// SPDX-License-Identifier: GPL-3.0-only
|
||
|
||
#pragma once
|
||
|
||
#include <QWidget>
|
||
#include <memory>
|
||
|
||
#include "../../common/DiffractionExperiment.h"
|
||
#include "../../common/IndexingSettings.h"
|
||
#include "../../common/AzimuthalIntegrationSettings.h"
|
||
#include "../../common/BraggIntegrationSettings.h"
|
||
#include "../../common/ScalingSettings.h"
|
||
#include "../../image_analysis/spot_finding/SpotFindingSettings.h"
|
||
#include "../../reader/JFJochReaderDataset.h"
|
||
#include "../../reader/JFJochReaderImage.h"
|
||
#include "../../rugnux/Rugnux.h" // ProcessMode
|
||
#include "../ReferenceMtzInfo.h"
|
||
#include "PowderCalibrationWidget.h" // CalibrationSelection
|
||
|
||
class QStackedWidget;
|
||
class QCheckBox;
|
||
class QComboBox;
|
||
class QLabel;
|
||
class QLineEdit;
|
||
class QPushButton;
|
||
class QRadioButton;
|
||
class SliderPlusBox;
|
||
class NumberLineEdit;
|
||
|
||
// The single, always-visible settings panel: an MX / AzInt / Calib toggle picks the page, and the panel
|
||
// is the one place all processing settings live (there is no separate window, so no synchronisation).
|
||
// "Analyze image" / "Analyze dataset" sit on top; the page choice decides what a dataset run does.
|
||
// Edits feed straight back into the running analysis via the worker.
|
||
class JFJochViewerSettingsDock : public QWidget {
|
||
Q_OBJECT
|
||
public:
|
||
JFJochViewerSettingsDock(const SpotFindingSettings &spot, const IndexingSettings &indexing,
|
||
const AzimuthalIntegrationSettings &azint, const BraggIntegrationSettings &bragg,
|
||
const ScalingSettings &scaling, QWidget *parent = nullptr);
|
||
|
||
public slots:
|
||
void datasetLoaded(std::shared_ptr<const JFJochReaderDataset> dataset);
|
||
void loadImage(std::shared_ptr<const JFJochReaderImage> image);
|
||
void referenceLoaded(ReferenceMtzInfo info); // worker reports a (un)loaded reference MTZ
|
||
// Dataset re-processing reads a stored HDF5 file, so it is unavailable on a live HTTP stream:
|
||
// grey out "Analyze dataset" while connected rather than failing with a dialog afterwards.
|
||
void setHttpConnection(bool connected, QString addr);
|
||
|
||
signals:
|
||
void spotFindingChanged(const SpotFindingSettings &spot, const IndexingSettings &indexing, int64_t max_spots);
|
||
void azintChanged(const AzimuthalIntegrationSettings &settings);
|
||
void braggChanged(BraggIntegrationSettings settings);
|
||
void scalingChanged(ScalingSettings settings);
|
||
void experimentChanged(const DiffractionExperiment &experiment);
|
||
void findBeamCenter(QVector<float> ring_q, bool guess, bool refine_tilt);
|
||
void ringsFromCalibration(QVector<float> rings);
|
||
void referenceSelected(QString path, QString column); // user picked a reference MTZ / SF-mmCIF / column
|
||
void modelSelected(QString path); // user picked (or cleared) the atomic model for validation
|
||
void reanalyzeImage(bool armed); // "Analyze image" toggle
|
||
// "Analyze dataset". The mode is the selected page; the calibrant selection is only read for
|
||
// ProcessMode::Calibration and is carried along so the job needs no second copy of the controls.
|
||
void analyzeDataset(ProcessMode mode, CalibrationSelection calibration);
|
||
|
||
private:
|
||
SpotFindingSettings spot_;
|
||
IndexingSettings indexing_;
|
||
AzimuthalIntegrationSettings azint_;
|
||
BraggIntegrationSettings bragg_;
|
||
ScalingSettings scaling_;
|
||
DiffractionExperiment experiment_;
|
||
bool have_experiment_ = false; // geometry edits only take effect once a dataset is loaded
|
||
int64_t max_spots_ = 1000;
|
||
// Min-pix is kept as the pair the UI shows - the fixed value AND whether to choose it per image -
|
||
// because spot_.min_pix_per_spot can only hold one of the two (unset = per image). SyncMinPix()
|
||
// turns the pair into that field, and per-image only ever applies to stills.
|
||
bool adaptive_min_pix_ = false;
|
||
int64_t min_pix_value_ = 2;
|
||
ProcessMode mode_ = ProcessMode::FullAnalysis; // the selected page, and what "Analyze dataset" runs
|
||
|
||
// "Analyze dataset" hero button, disabled while a live HTTP source is connected.
|
||
QPushButton *analyzeDatasetBtn_ = nullptr;
|
||
|
||
// Indexing-algorithm combo + the description line under it (kept so the Auto resolution refreshes).
|
||
QComboBox *algo_ = nullptr;
|
||
QLabel *algoDesc_ = nullptr;
|
||
|
||
// Geometry fields (shared by both MX and AzInt, since both need the same geometry)
|
||
NumberLineEdit *energy_ = nullptr;
|
||
NumberLineEdit *distance_ = nullptr;
|
||
NumberLineEdit *beamX_ = nullptr;
|
||
NumberLineEdit *beamY_ = nullptr;
|
||
NumberLineEdit *rot1_ = nullptr; // detector tilt (PONI rot1), deg
|
||
NumberLineEdit *rot2_ = nullptr; // detector tilt (PONI rot2), deg
|
||
// Polarization factor, also shared: it corrects the azimuthal profile and the Bragg intensities
|
||
// alike. Unchecked = no polarization correction (the experiment's factor is unset).
|
||
QCheckBox *polarizationOn_ = nullptr;
|
||
NumberLineEdit *polarization_ = nullptr;
|
||
|
||
// How the sample moves between images — the three cases a dataset can be, exactly as a file
|
||
// stores them (a rotation axis, a grid scan, or neither). The choice sets or clears the
|
||
// experiment's goniometer / grid scan, and ApplyProcessingMode() turns it into the
|
||
// indexing/scaling mode. Each group keeps its values while another mode is selected.
|
||
QRadioButton *modeStill_ = nullptr;
|
||
QRadioButton *modeRotation_ = nullptr;
|
||
QRadioButton *modeGrid_ = nullptr;
|
||
QLineEdit *axisName_ = nullptr;
|
||
NumberLineEdit *axisX_ = nullptr, *axisY_ = nullptr, *axisZ_ = nullptr;
|
||
NumberLineEdit *rotStart_ = nullptr, *rotIncrement_ = nullptr;
|
||
// Not editable here, but part of the file's goniometer — kept so rebuilding the axis from the
|
||
// fields does not drop them.
|
||
std::optional<Coord> gonioHelicalStep_;
|
||
std::optional<float> gonioScreeningWedge_;
|
||
// Grid scan. Only the fast-axis point count is a free parameter: the number of slow rows follows
|
||
// from the image count (GridScanSettings::ImageNum), which is why there is no second count field
|
||
// and why gridSummary_ spells out the grid the settings actually make.
|
||
NumberLineEdit *gridNFast_ = nullptr;
|
||
NumberLineEdit *gridStepX_ = nullptr, *gridStepY_ = nullptr;
|
||
QCheckBox *gridVertical_ = nullptr, *gridSnake_ = nullptr;
|
||
QLabel *gridSummary_ = nullptr;
|
||
// Crystal fields
|
||
QCheckBox *cellKnown_ = nullptr;
|
||
NumberLineEdit *cellA_ = nullptr, *cellB_ = nullptr, *cellC_ = nullptr;
|
||
NumberLineEdit *cellAlpha_ = nullptr, *cellBeta_ = nullptr, *cellGamma_ = nullptr;
|
||
NumberLineEdit *spaceGroup_ = nullptr;
|
||
QLabel *spaceGroupName_ = nullptr; // resolved Hermann–Mauguin symbol
|
||
|
||
// Reference dataset (MTZ) for scaling / CCref. The chosen file is remembered so a
|
||
// column change re-loads the same file; the worker owns the reflections, this just drives it.
|
||
QPushButton *refButton_ = nullptr;
|
||
QComboBox *refColumn_ = nullptr;
|
||
QLabel *refSummary_ = nullptr;
|
||
QLabel *refWarning_ = nullptr;
|
||
QString refPath_;
|
||
QPushButton *modelButton_ = nullptr;
|
||
QPushButton *modelClear_ = nullptr;
|
||
QLabel *modelLabel_ = nullptr;
|
||
QString modelPath_;
|
||
|
||
PowderCalibrationWidget *powder_ = nullptr;
|
||
// Azimuthal integration: one section, shared by the AzInt and Calib pages (a calibration by rings
|
||
// integrates the run in azimuthal sectors), so it sits outside the page stack and is hidden on MX.
|
||
QWidget *azintSection_ = nullptr;
|
||
|
||
QWidget *BuildGeometrySection();
|
||
QWidget *BuildMXPage();
|
||
QWidget *BuildGoniometerSection();
|
||
QWidget *BuildBraggSection();
|
||
QWidget *BuildScalingSection();
|
||
QWidget *BuildReferenceSection();
|
||
QWidget *BuildAzIntSection();
|
||
QWidget *BuildCalibrationPage();
|
||
void SyncMinPix();
|
||
void EmitSpotFinding();
|
||
void EmitExperiment();
|
||
void ApplyProcessingMode(); // sample-motion mode -> indexing + scaling rotation/stills mode
|
||
void UpdateAlgorithmDescription();
|
||
void RefreshGeometryFields();
|
||
void UpdateSpaceGroupName();
|
||
void EnableRotationFields(bool on);
|
||
void EnableGridFields(bool on);
|
||
void UpdateGridSummary();
|
||
[[nodiscard]] std::optional<GoniometerAxis> GoniometerFromFields() const;
|
||
[[nodiscard]] std::optional<GridScanSettings> GridScanFromFields() const;
|
||
};
|