Build Packages / Create release (push) Successful in 7s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 4m56s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 9m10s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 13m51s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 16m56s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 20m59s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m25s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 21m17s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 14m14s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 25m59s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m48s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 15m52s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 19m33s
Build Packages / Generate python client (push) Successful in 29s
Build Packages / Build documentation (push) Successful in 1m19s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 20m49s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 25m31s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 25m24s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 26m5s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 22m19s
Build Packages / Unit tests (push) Failing after 2h28m24s
Build Packages / Validation Battery (push) Successful in 40m33s
* Rugnux: Processes a collection live while jfjoch_broker writes it (`rugnux <master.h5> <broker URL>`). * Rugnux: Processing starts on the first images and confirms its choices once all are in, so analysis can begin during collection. * Rugnux: Faster processing, especially on servers with several GPUs and CPU sockets and on computers without a GPU (`--no-gpu`, macOS). * Rugnux: Uses much less GPU memory, so large data sets stay on the GPU of smaller cards, and a data set too large for the card is processed on the CPU instead. * Rugnux: On macOS, Bragg integration runs on the Apple GPU. * Rugnux: Better handling of twinning and pseudo-symmetry, with the twin hypothesis written as `<prefix>_twin_<SG>.mtz`, and a wrong `--model` no longer confirms the data or decides the hand. * Rugnux: `--model` accepts predicted (AlphaFold) models and falls back to molecular replacement when the model is not placed as a rigid body (experimental `--mr` forces it), settling space group, hand and twinning and writing a different answer as `<prefix>_mr.mtz`. * Rugnux: Molecular replacement is faster and settles the space group in more cases with several copies in the asymmetric unit. * Rugnux: Deposition-ready mmCIF, a richer report, and numbered steps on the terminal with the full log in `<prefix>_log.txt`; `_P1.mtz`, `--estimate-beam-center` and `--beam-center-search` are removed. * Rugnux: The beam-stop shadow is found correctly on tilted detectors. * Rugnux: Stills files that record a stationary goniometer axis get the default stills geometry refinement. * jfjoch_viewer: "Analyze dataset" uses the same defaults as a bare `rugnux <file>`, and shows the results of processed CBF, marCCD and SMV sweeps. * jfjoch_viewer: The Files panel refreshes on F5 and every few seconds, and the plot type and other selections stay put during live collection. * jfjoch_writer: Memory stays bounded and old data files are closed efficiently on long runs, any failure ends the run in an error state the broker is told about, and the master file appears only after its data files; the ZeroMQ pusher logs when a stalled writer makes it drop images. * Fixes: The TCP image pusher no longer blocks the broker's receiver, GPU out-of-memory failures stop with a clear message, and the XDS plugin masks module and chip edges. --------- Co-authored-by: Filip Leonarski <jungfrau@filips-mac-mini.home> Reviewed-on: #85 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
144 lines
5.7 KiB
C++
144 lines
5.7 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <QtCharts/QChartView>
|
|
#include <QtCharts/QValueAxis>
|
|
#include <QtCharts/QLineSeries>
|
|
#include <QtCharts/QScatterSeries>
|
|
#include <QMouseEvent>
|
|
#include <QGraphicsLineItem>
|
|
#include <QGraphicsSimpleTextItem>
|
|
#include <QTimer>
|
|
#include <QPointer>
|
|
#include <QColor>
|
|
|
|
#ifdef JFJOCH_USE_FFTW
|
|
#include <fftw3.h>
|
|
#endif
|
|
|
|
#include "../../common/GoniometerAxis.h"
|
|
#include "../../reader/JFJochReaderDataset.h"
|
|
|
|
class JFJochDatasetInfoChartView : public QChartView {
|
|
Q_OBJECT
|
|
|
|
QPointer<QLineSeries> series = nullptr;
|
|
QPointer<QScatterSeries> currentSeries = nullptr;
|
|
QGraphicsLineItem *m_hoverLine = nullptr; // vertical crosshair, at the hovered x
|
|
QGraphicsLineItem *m_hoverLineHorizontal = nullptr; // horizontal crosshair, at the hovered y
|
|
QTimer *m_hoverLoadTimer = nullptr;
|
|
int64_t m_hoverPendingIdx = -1;
|
|
|
|
std::vector<float> values;
|
|
QString primaryName_; // legend name of the active (primary) run
|
|
int64_t binning = 1;
|
|
int64_t curr_image = 0;
|
|
|
|
bool m_minYZeroEnabled = true;
|
|
bool m_yOneOverD = false;
|
|
bool m_xUseGoniometerAxis = true;
|
|
// Compact mode: this view is one of two stacked half-height plots. The y axis is reduced to
|
|
// its two range ends, named by a short bold coloured label; the upper plot hides its x labels
|
|
// (the lower one carries them for both).
|
|
QString compact_label_;
|
|
bool compact_hide_x_ = false;
|
|
int compact_y_label_px_ = 0; // width of this plot's own widest y label
|
|
int compact_y_pad_px_ = 0; // y label width to pad out to (0 = no padding)
|
|
int compact_min_h_ = 0; // height this plot needs for its own y-axis title
|
|
int compact_title_w_ = 0; // width of that title, reserved in the left margin
|
|
// The y-axis name, painted upright (one letter per line) in place of Qt's rotated axis title.
|
|
QGraphicsSimpleTextItem *compact_title_ = nullptr;
|
|
// The other plot of a stacked pair. Both are padded out to the wider of the two y labels, so
|
|
// both curves start at the same x.
|
|
QPointer<JFJochDatasetInfoChartView> compact_peer_;
|
|
|
|
void applyCompactMargins();
|
|
void pairCompactYLabels();
|
|
void updateCompactTitle(const QFont &title_font);
|
|
void placeCompactTitle();
|
|
|
|
// Place the horizontal crosshair at y (scene coords), snapping to snap_to_y when the cursor is
|
|
// already within a few pixels of the curve. A non-finite snap_to_y never snaps.
|
|
void setHoverLineY(double y, double snap_to_y);
|
|
|
|
std::optional<GoniometerAxis> goniometer_axis;
|
|
std::optional<double> image_time_s;
|
|
|
|
void updateChart();
|
|
|
|
// Build regular (index or goniometer) chart
|
|
void buildTimeDomainChart();
|
|
|
|
public:
|
|
// One overlay line (a non-active run), drawn alongside the primary series with a legend entry.
|
|
// x maps each value to an image number on the shared x-axis (empty => 0,1,2,... by index).
|
|
struct NamedSeries { QString name; std::vector<float> values; QColor color; std::vector<float> x; };
|
|
private:
|
|
std::vector<NamedSeries> overlays_;
|
|
QColor primary_color_; // colour of the active (primary) run, if assigned
|
|
std::vector<float> primary_x_; // x (image numbers) for the primary series; empty => index
|
|
int64_t full_range_ = 0; // total images in the dataset; fixes the x-axis to [0, n)
|
|
// Append (binned) finite points of vals to s at x positions (xs empty => index), extending [mn,mx].
|
|
void appendSeries(QLineSeries *s, const std::vector<float> &vals, const std::vector<float> &xs,
|
|
double &mn, double &mx) const;
|
|
|
|
// FFT view toggle
|
|
bool m_showFFT = false;
|
|
|
|
#ifdef JFJOCH_USE_FFTW
|
|
// Cached FFT data for hover/status bar etc.
|
|
void buildFFTChart();
|
|
std::vector<double> m_fftMagnitudes;
|
|
std::vector<double> m_fftFrequenciesHz;
|
|
#endif
|
|
|
|
signals:
|
|
void imageSelected(int64_t number);
|
|
void writeStatusBar(QString string, int timeout_ms = 0);
|
|
|
|
private slots:
|
|
void onHoverLoadTimeout();
|
|
void setBinning(int64_t val);
|
|
|
|
private:
|
|
void changeEvent(QEvent *event) override;
|
|
void mousePressEvent(QMouseEvent *event) override;
|
|
void mouseMoveEvent(QMouseEvent *event) override;
|
|
void leaveEvent(QEvent *event) override;
|
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
|
|
|
public:
|
|
explicit JFJochDatasetInfoChartView(QWidget *parent = nullptr);
|
|
void setImage(int64_t val);
|
|
// An empty label switches compact mode off. Takes effect on the next loadValues().
|
|
void SetCompact(const QString &axis_label, bool hide_x_labels);
|
|
|
|
// The other plot of a stacked pair; set once on both, it keeps their y labels the same width.
|
|
void SetCompactPeer(JFJochDatasetInfoChartView *peer) { compact_peer_ = peer; }
|
|
|
|
// Whether the y axis starts at zero. On by default: a spot count is read against zero, while a
|
|
// background level is not, and a plot of it this short needs the whole height for its shape.
|
|
void SetMinYZero(bool val) { m_minYZeroEnabled = val; }
|
|
|
|
public slots:
|
|
void resetZoom();
|
|
|
|
public:
|
|
// Load the primary (active) run's values plus any overlay runs. one_over_d2 applies the same
|
|
// d -> 1/d^2 transform to every series; primaryName/overlay names drive the legend.
|
|
void loadValues(const std::vector<float> &input,
|
|
int64_t image,
|
|
bool one_over_d2,
|
|
const JFJochReaderDataset *dataset = nullptr,
|
|
const QString &primaryName = QString(),
|
|
std::vector<NamedSeries> overlays = {},
|
|
const QColor &primaryColor = QColor(),
|
|
std::vector<float> primaryX = {},
|
|
int64_t fullRange = 0);
|
|
};
|
|
|
|
|
|
|