Rebrand offline processing as rugnux (jfjoch_process -> rugnux)
Build Packages / Unit tests (push) Successful in 1h1m46s
Build Packages / build:windows:nocuda (push) Successful in 10m19s
Build Packages / build:viewer-tgz:cpu (push) Successful in 8m22s
Build Packages / build:viewer-tgz:cuda (push) Successful in 9m18s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 13m23s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 13m47s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 13m25s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 12m53s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 13m27s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 11m59s
Build Packages / build:rpm (rocky8) (push) Successful in 10m50s
Build Packages / build:rpm (rocky9) (push) Successful in 12m26s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 13m3s
Build Packages / XDS test (durin plugin) (push) Successful in 7m58s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 12m26s
Build Packages / DIALS test (push) Successful in 13m35s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m22s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m47s
Build Packages / Generate python client (push) Successful in 30s
Build Packages / Build documentation (push) Successful in 1m5s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:cuda (push) Successful in 11m55s

Split the naming: rugnux = data-processing subsystem, Jungfraujoch = streaming/acquisition. Executables jfjoch_process -> rugnux (source tools/rugnux_cli.cpp) and jfjoch_scale -> rugnux_scale; the processing library process/ -> rugnux/ with class/target JFJochProcess -> Rugnux (JFJochProcessObserver -> RugnuxObserver, JFJochProcessCommandLine -> RugnuxCommandLine).

Doc JFJOCH_PROCESS.md -> RUGNUX.md, reconciled with the live usage message (drop dead -P/--partiality, -w/--wedge; --process-as-stills -> --force-still; add the real rot3d scaling knobs). New docs/NAMING.md explains both names, with pronunciation and a note on Romansh.

rugnux now scales and merges rotation data automatically (implicit -M); stills still require an explicit -M.

jfjoch_viewer and its classes keep their names (rename deferred); only their references to the renamed library are updated. The _process.h5 output suffix and ProcessConfig/Mode/Result are kept.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 22:46:58 +02:00
co-authored by Claude Opus 4.8
parent 0a28da7c2e
commit 1caa57744a
32 changed files with 204 additions and 132 deletions
+1 -1
View File
@@ -135,7 +135,7 @@ jobs:
run: |
cd build
# Build the whole viewer-only tree, not just the GUI: the "viewer" CPack component also
# contains the portable CLI tools (jfjoch_process/scale/azint/recompress/extract_hkl),
# contains the portable CLI tools (rugnux/rugnux_scale/azint/recompress/extract_hkl),
# which must exist on disk before cpack installs the component.
ninja -j16
cpack
+4 -3
View File
@@ -122,8 +122,9 @@ generated API model and internal types.
`JFJochImageAnalysis`):
- `jfjoch_broker` — online, real-time (FPGA + GPU).
- `jfjoch_viewer` — interactive Qt desktop (`viewer/`), results not persisted.
- `jfjoch_process` (`tools/jfjoch_process.cpp`) — offline batch over a stored HDF5; writes
`_process.h5` and `.mtz`/`.cif`/`.hkl`. `jfjoch_scale` re-scales/merges already-integrated data.
- `rugnux` (`tools/rugnux_cli.cpp`, built on the `Rugnux` library in `rugnux/`) — offline batch over
a stored HDF5; writes `_process.h5` and `.mtz`/`.cif`/`.hkl`. `rugnux_scale` re-scales/merges
already-integrated data. (rugnux = the data-processing half of the system; see `docs/NAMING.md`.)
**`image_analysis/` pipeline** (subdirs): `spot_finding`, `indexing` (`ffbidx`/`fft` GPU,
`fftw` CPU), `lattice_search`, `geom_refinement`, `pixel_refinement`, `bragg_prediction`,
@@ -198,7 +199,7 @@ A per-image scalar (e.g. `ice_ring_score`, `bkg_estimate`, `mosaicity`) flows an
(`bkg_estimate` is a clean template) at every layer:
1. **Compute** where the azint profile is finalized: `image_analysis/MXAnalysisWithoutFPGA.cpp` (CPU),
`receiver/JFJochReceiverFPGA.cpp` (FPGA), and the offline azint worker in `process/JFJochProcess.cpp`.
`receiver/JFJochReceiverFPGA.cpp` (FPGA), and the offline azint worker in `rugnux/Rugnux.cpp`.
2. **Message** (`common/JFJochMessages.h`): `std::optional<float>` in `DataMessage`, `std::vector<float>`
in `EndMessage`.
3. **CBOR**: encode in `frame_serialize/CBORStream2Serializer.cpp` (DataMessage block *and* END block),
+3 -3
View File
@@ -277,9 +277,9 @@ IF (JFJOCH_VIEWER_ONLY)
ADD_SUBDIRECTORY(image_analysis)
ADD_SUBDIRECTORY(broker)
ADD_SUBDIRECTORY(reader)
ADD_SUBDIRECTORY(process)
ADD_SUBDIRECTORY(rugnux)
ADD_SUBDIRECTORY(viewer)
ADD_SUBDIRECTORY(tools) # builds only the portable analysis tools (process/scale/azint/extract_hkl)
ADD_SUBDIRECTORY(tools) # builds only the portable analysis tools (rugnux/rugnux_scale/azint/extract_hkl)
ELSE()
ADD_SUBDIRECTORY(jungfrau)
ADD_SUBDIRECTORY(compression)
@@ -302,7 +302,7 @@ ELSE()
ADD_SUBDIRECTORY(acquisition_device)
ADD_SUBDIRECTORY(receiver)
ADD_SUBDIRECTORY(image_analysis)
ADD_SUBDIRECTORY(process)
ADD_SUBDIRECTORY(rugnux)
ADD_SUBDIRECTORY(tests)
ADD_SUBDIRECTORY(tools)
ENDIF()
+2 -2
View File
@@ -93,7 +93,7 @@ bitshuffle + Zstd; signed integer image datasets use `INTx_MIN` as the HDF5 fill
### Reprocessing output: `<prefix>_process.h5`
The offline reprocessing tool [`jfjoch_process`](TOOLS.md) (`tools/jfjoch_process.cpp`) re-runs the
The offline reprocessing tool [`rugnux`](TOOLS.md) (`tools/rugnux_cli.cpp`) re-runs the
full analysis pipeline (spot finding, indexing, refinement, integration, scaling) on an existing
dataset and writes its results to a master file named **`<prefix>_process.h5`**. This file uses the
**integrated** format, but instead of copying the images its `/entry/data/data` is a *virtual
@@ -104,7 +104,7 @@ of the raw images — without duplicating terabytes of data.
This is a particularly FAIR-friendly artefact: it can be shared or archived alongside (or instead
of) the raw data to convey what is in a dataset and how it processed, while the `/entry/data/data`
VDS still resolves to the original images when they are available. `jfjoch_process` can also process
VDS still resolves to the original images when they are available. `rugnux` can also process
an equally-spaced *subset* of images (start/end/stride), producing a down-sampled reference set.
## 3. NXmx-standard content
+2 -2
View File
@@ -14,12 +14,12 @@ Jungfraujoch RPM/APT repositories (see [Deployment](DEPLOYMENT.md)).
| --- | --- | --- | --- |
| [`jfjoch_broker`](JFJOCH_BROKER.md) | Online, real-time streaming analysis on FPGA + GPU | HTTP/REST + ZeroMQ | Live results and statistics, images streamed to [`jfjoch_writer`](JFJOCH_WRITER.md) |
| **`jfjoch_viewer`** | **Interactive, on-screen exploration** | **Qt desktop application** | **Displayed on screen (results not saved to disk)** |
| [`jfjoch_process`](JFJOCH_PROCESS.md) | Offline batch processing of a stored dataset | Command-line interface | `_process.h5`, and `.mtz`/`.cif`/`.hkl` when merging |
| [`rugnux`](RUGNUX.md) | Offline batch processing of a stored dataset | Command-line interface | `_process.h5`, and `.mtz`/`.cif`/`.hkl` when merging |
## Functionality
- Opens HDF5 files written by [`jfjoch_writer`](JFJOCH_WRITER.md) (`*_master.h5`) and the
`*_process.h5` files produced by [`jfjoch_process`](JFJOCH_PROCESS.md). It also opens NXmx files
`*_process.h5` files produced by [`rugnux`](RUGNUX.md). It also opens NXmx files
written by DECTRIS detectors, though that path has had only limited testing.
- Runs an **embedded data-processing pipeline** — the same analysis code as the rest of
Jungfraujoch — performing spot finding, indexing and integration on the displayed images.
+60
View File
@@ -0,0 +1,60 @@
# Naming
The software is Swiss, and so are its names: both halves of the system are named after
places in the Alps that are, in one way or another, about moving a *lot* of something up a
steep mountain as efficiently as possible — usually by train. Throughput, in other words.
| Part | Name | What it does |
| --- | --- | --- |
| Streaming / acquisition | **Jungfraujoch** | Receives detector data at high data rates, runs the FPGA/GPU pipeline, and streams images out for writing. |
| Data processing | **Rugnux** | Offline crystallographic analysis of a stored dataset — indexing, integration, scaling and merging (the [`rugnux`](RUGNUX.md) tool). |
## Jungfraujoch
The **Jungfraujoch** is a high mountain col in the Bernese Alps, the saddle (*Joch* is German
for "yoke" or "col") between the peaks **Jungfrau** and **Mönch**, at 3,466 m. It is the site of
the [High Altitude Research Station Jungfraujoch](https://www.hfsjg.ch/), whose long-running
atmospheric measurements are **co-operated by the Paul Scherrer Institute** — the same institute
that develops this software and the JUNGFRAU detector.
The name is also a small piece of word-play. PSI's **JUNGFRAU** detector and DECTRIS's **EIGER**
detector are both named after Bernese Alps peaks (the famous trio is *Eiger*, *Mönch*, *Jungfrau*).
The Jungfraujoch — the pass *between* Jungfrau and Mönch — is where those two detector worlds meet.
And it fits the theme of the whole project: the Jungfraujoch is reached by the **Jungfraubahn**,
whose terminus is the **highest railway station in Europe** (3,454 m, the "Top of Europe"). It is
the closest you can get to that summit in a genuinely *high-throughput* way — by train, moving
crowds up the mountain — which is exactly what the streaming side of this software does with
detector frames.
**Pronunciation (German):** *Jungfraujoch***YUNG-frow-yokh**.
"Jung" as in *young*, "frau" rhymes with *cow*, and the final "joch" ends in the guttural *ch* of
Scottish *loch* or German *Bach* — not a hard *k*.
## Rugnux
**Piz Rugnux** is a mountain in the Rhaetian Alps of canton Graubünden, in south-eastern
Switzerland. (*Piz* is the Romansh word for "peak".) It rises above the **Albula line** of the
**Rhaetian Railway** (*Rhätische Bahn*), part of the "Rhaetian Railway in the Albula / Bernina
Landscapes" — a **UNESCO World Heritage Site** (*Welterbe*).
That stretch of line is a masterpiece of throughput engineering: to climb a great deal of altitude
in very little horizontal distance, it corkscrews through a series of **helical (spiral) tunnels**
looping back inside the mountains. It is, again, the Swiss art of getting an enormous amount up a
steep mountain efficiently — the same idea the data-processing side of this software is built
around: pushing a large volume of diffraction data through the analysis pipeline.
So the theme is consistent — **Swiss mountains, trains, and throughput** — while keeping the two
subsystems clearly distinct: *Jungfraujoch* streams, *Rugnux* processes.
**Pronunciation (Romansh):** *Piz Rugnux***peets roo-NYOOKS**.
The "gn" is a soft palatal *ñ*, as in *canyon* or Italian *gnocchi*, not two separate sounds.
## What is Romansh?
**Romansh** (*Rumantsch*) is the **fourth national language of Switzerland**, alongside German,
French and Italian. It is a Romance language — a direct descendant of the spoken Latin left behind
in the Alpine valleys — today spoken by only a few tens of thousands of people, almost all in the
canton of Graubünden. It survives in several regional idioms, brought together in a standard form
called *Rumantsch Grischun*. Naming the processing engine with a Romansh mountain is a small nod to
the least-spoken but no-less-Swiss corner of the country.
+27 -22
View File
@@ -1,6 +1,7 @@
# jfjoch_process
# rugnux
`jfjoch_process` is the **offline** crystallographic data-analysis tool of Jungfraujoch.
`rugnux` is the **offline** crystallographic data-analysis tool of Jungfraujoch — the
data-processing half of the system (see [Naming](NAMING.md) for where the name comes from).
It takes an existing HDF5 dataset, runs the full analysis pipeline — spot finding, indexing,
geometry refinement, Bragg integration and (optionally) scaling and merging — and writes the
results to a `_process.h5` file, plus reflection files (`.mtz`/`.cif`/`.hkl`) when merging is
@@ -9,9 +10,9 @@ requested.
It runs the *same* analysis code as the online and interactive tools, just driven from the
command line over a file rather than a live detector stream.
> **Note.** `jfjoch_process` is under very active development. This page describes the tool and
> **Note.** `rugnux` is under very active development. This page describes the tool and
> its options at a high level; the authoritative, always-current list of options is the program's
> own usage message — run `jfjoch_process` with no arguments.
> own usage message — run `rugnux` with no arguments.
## Where it fits among the three analysis tools
@@ -19,9 +20,9 @@ command line over a file rather than a live detector stream.
| --- | --- | --- | --- |
| [`jfjoch_broker`](JFJOCH_BROKER.md) | Online, real-time streaming analysis on FPGA + GPU | HTTP/REST + ZeroMQ | Live results and statistics, images streamed to [`jfjoch_writer`](JFJOCH_WRITER.md) |
| [`jfjoch_viewer`](JFJOCH_VIEWER.md) | Interactive, on-screen exploration | Qt desktop application | Displayed on screen (results not saved to disk) |
| **`jfjoch_process`** | **Offline batch processing of a stored dataset** | **Command-line interface** | **`_process.h5`, and `.mtz`/`.cif`/`.hkl` when merging** |
| **`rugnux`** | **Offline batch processing of a stored dataset** | **Command-line interface** | **`_process.h5`, and `.mtz`/`.cif`/`.hkl` when merging** |
Use `jfjoch_process` to re-analyse data after acquisition, to experiment with processing
Use `rugnux` to re-analyse data after acquisition, to experiment with processing
parameters, or to produce merged intensities for downstream structure solution.
## Hardware
@@ -51,11 +52,11 @@ the first pass.
Merged statistics (⟨I/σ⟩, CC1/2, completeness, …), the error model and timing are printed to the
console.
## Re-scaling and re-merging (`jfjoch_scale`)
## Re-scaling and re-merging (`rugnux_scale`)
The companion tool `jfjoch_scale` re-scales and merges the *already-integrated* reflections stored
The companion tool `rugnux_scale` re-scales and merges the *already-integrated* reflections stored
in one or more `_process.h5` files, without re-running spot finding or integration. Use it to
re-merge quickly with a different space group, partiality model, resolution limit or reference MTZ,
re-merge quickly with a different space group, resolution limit, anomalous setting or reference MTZ,
or to combine several processed runs into one set of merged intensities.
## Quick start
@@ -65,22 +66,23 @@ or to combine several processed runs into one set of merged intensities.
Index, integrate, scale and merge a rotation sweep, fully de novo:
```
jfjoch_process rotation_master.h5 \
rugnux rotation_master.h5 \
-o lyso_rot -N 32 \
-M --scaling-high-resolution 1.4
--scaling-high-resolution 1.4
```
Because the dataset carries a rotation goniometer axis, it is processed as **rotation data by
default**: two-pass rotation indexing (index the sweep once, then process every frame against that
lattice) with the **`rot3d`** partiality model (rotation partials combined into 3D fulls). `-M`
scales and merges; the unit cell is taken from the rotation indexer and the space group is
determined from systematic absences, and both are written into the merged `.cif`.
lattice) with the **`rot3d`** partiality model (rotation partials combined into 3D fulls). Rotation
data is **scaled and merged automatically** (as if `-M` were given); the unit cell is taken from the
rotation indexer and the space group is determined from systematic absences, and both are written
into the merged `.cif`.
Run **fully de novo** (no `-C`/`-S`) for the best result — supplying a cell or space group up front
tends to *degrade* low-symmetry cases. `--scaling-high-resolution` (set it to your expected
resolution) sharpens both the space-group search and the error model. To tune the first pass use
`--two-pass-rotation=100` (or `-R100` — the first-pass image count); to force the sweep to be
treated as independent stills use `--process-as-stills`.
treated as independent stills use `--force-still`.
### Still / serial data
@@ -89,7 +91,7 @@ stills automatically** — no flag needed. Known-cell indexing with the GPU fast
then merge against a reference structure:
```
jfjoch_process serial_master.h5 \
rugnux serial_master.h5 \
-o lyso_serial -N 32 \
-X ffbidx -C 79,79,38,90,90,90 -S 96 \
--spot-sigma 4 \
@@ -100,7 +102,7 @@ jfjoch_process serial_master.h5 \
`ffbidx` requires a known cell (`-C`) and is the indexer of choice for sparse serial stills. For
weak serial data, tightening spot finding with `--spot-sigma 4` typically raises the indexing rate
substantially. If a dataset *does* carry a goniometer axis but you want per-frame stills processing
anyway, add `--process-as-stills`.
anyway, add `--force-still`.
## Command-line options
@@ -123,6 +125,7 @@ Spot finding:
| `--spot-threshold <num>` | Photon-count threshold for spot finding (default: 10) |
| `--spot-high-resolution <num>` | High-resolution limit for spot finding, Å (default: 1.5) |
| `--max-spots <num>` | Maximum spot count (default: 250) |
| `--detect-ice-rings[=on\|off]` | Flag ice-ring spots and exclude ice-ring reflections from scaling/merging; overrides the dataset setting (default: use the dataset value) |
Azimuthal integration (the radial profile behind the per-image ice-ring score):
@@ -136,13 +139,13 @@ Azimuthal integration (the radial profile behind the per-image ice-ring score):
Indexing:
A dataset with a **rotation goniometer axis** is processed as rotation data (two-pass rotation
indexing) by default; a dataset without one is processed as independent stills. `--process-as-stills`
indexing) by default; a dataset without one is processed as independent stills. `--force-still`
overrides the former; the `-R` / `--single-pass-rotation` / `--force-rotation-lattice` flags request
rotation explicitly and pick the pass or lattice.
| Option | Description |
| --- | --- |
| `--process-as-stills` | Treat a rotation (goniometer) dataset as independent stills instead of rotation |
| `--force-still` | Treat a rotation (goniometer) dataset as independent stills instead of rotation |
| `-X, --indexing-algorithm <txt>` | `FFBIDX` \| `FFT` \| `FFTW` \| `Auto` \| `None` |
| `-C, --unit-cell <cell>` | Reference unit cell `"a,b,c,alpha,beta,gamma"` (required by `ffbidx`) |
| `-S, --space-group <num>` | Space group number (used for indexing and scaling) |
@@ -160,11 +163,12 @@ Scaling and merging:
| Option | Description |
| --- | --- |
| `-M, --scale-merge` | Scale and merge |
| `-P, --partiality <txt>` | Partiality model: `fixed` \| `rot` \| `rot3d` \| `unity` (default: `rot3d` for rotation data, `fixed` for stills). `rot3d` = `rot` + 3D combine of the per-frame partials into fulls |
| `-M, --scale-merge` | Scale and merge (automatic for rotation data; only needed to force scaling on stills) |
| `-A, --anomalous` | Anomalous mode (keep Friedel pairs separate) |
| `-B, --refine-bfactor` | Refine a per-image B-factor |
| `-w, --wedge[=num]` | Refine the per-image rotation wedge (optional starting value) |
| `--scale-fulls` / `--no-scale-fulls` | rot3d: refit a per-frame scale on the combined fulls (XDS order, Unity model); on by default for rotation data, off for stills |
| `--smooth-g[=deg]` | rot3d: smooth the per-frame scale *G* over a degree range before the 3D combine (XDS DELPHI-like; default 5° for rotation, 0 = off) |
| `--capture-uncertainty <num>` | rot3d: systematic sigma on under-captured fulls, ~num·(1captured_fraction)·I (default: 1.0 for rotation, 0 otherwise) |
| `--scaling-high-resolution <num>` | High-resolution limit for scaling, Å (default: no limit) |
| `--min-partiality <num>` | Minimum partiality to accept a reflection (default: 0.02) |
| `--reject-outliers <num>` | Per-observation outlier rejection, N σ from the per-reflection median (default: 6 for `rot3d`, off otherwise) |
@@ -173,6 +177,7 @@ Scaling and merging:
| `--scaling-iterations <num>` | Scaling iterations with no reference data (default: 3) |
| `--scaling-output <txt>` | Reflection output format: `cif` (mmCIF, default) \| `mtz` \| `txt` |
| `-z, --reference-mtz <file>` | Reference MTZ (enables reference-driven scaling) |
| `--write-process-h5` | Also write the (large) `_process.h5` when merging (default: only `.mtz`/`.cif`) |
Integration:
+4 -4
View File
@@ -6,15 +6,15 @@ number of command-line tools. Each prints its own usage when run with `-h` or wi
## Data analysis
### jfjoch_process
### rugnux
Offline CLI tool that runs the full crystallographic analysis pipeline (spot finding, indexing,
integration, scaling/merging) on a stored HDF5 dataset, producing a `_process.h5` file and, when
merging, reflection files. See [jfjoch_process](JFJOCH_PROCESS.md).
merging, reflection files. See [rugnux](RUGNUX.md).
### jfjoch_scale
### rugnux_scale
Re-scales and merges the already-integrated reflections from one or more `_process.h5` files
(no re-integration). Useful to re-merge with a different space group, partiality, resolution limit
or reference MTZ, or to combine several runs. See [jfjoch_process](JFJOCH_PROCESS.md).
or reference MTZ, or to combine several runs. See [rugnux](RUGNUX.md).
### jfjoch_extract_hkl
Extracts reflections (HKL list) from a Jungfraujoch master file; can sum the same HKL across
+2 -1
View File
@@ -24,6 +24,7 @@ Jungfraujoch is distributed under the GPLv3 license.
VERSIONING
DEPLOYMENT
REPOSITORIES
NAMING
CHANGELOG
.. toctree::
@@ -32,7 +33,7 @@ Jungfraujoch is distributed under the GPLv3 license.
JFJOCH_BROKER
JFJOCH_WRITER
JFJOCH_PROCESS
RUGNUX
JFJOCH_VIEWER
SOFTWARE_INTEGRATION
TOOLS
+1 -1
View File
@@ -55,7 +55,7 @@ threshold needs no per-detector tuning).
rings;}` with per-ring `{centre_q, half_width_q}`.
- Add an `IsOnIceRing(d, const vector<IceRing>&)` overload in `common/Definitions.h` (per-ring centre
+ width), alongside the existing fixed-table one.
- `tools/jfjoch_process.cpp`: change `--detect-ice-rings` to take `auto|on|off` (default `auto`); in
- `tools/rugnux_cli.cpp`: change `--detect-ice-rings` to take `auto|on|off` (default `auto`); in
`auto`, run `DetectIceRings` on the accumulated profile and set
`experiment.DetectIceRings(model.present)` + feed `model.rings` to the spot-finder
(`MarkIceRings`) and the scaling exclusion path.
@@ -44,7 +44,7 @@ Pass A / the seed of the two profile modes, so it always runs.
This is the same buffer `AzIntEngineGPU`/`ROIIntegrationGPU` consume. **Consequence:** the `±1`
special/saturation band that `ProfileIntegrate2D` rejects on the lossy `CompressedImage` is handled
upstream by the preprocessor instead — correct for the online path; sanity-check it for offline
`jfjoch_process` if that ever uses this engine on a lossy-compressed stored file.
`rugnux` if that ever uses this engine on a lossy-compressed stored file.
- Config uses raw detector dims — `xpixel = GetXPixelsNum()`, `npixel = GetPixelsNum()` — matching how
`MXAnalysisWithoutFPGA` sizes the `ImagePreprocessorBuffer` and the frame the predicted `predicted_x`
live in.
@@ -100,7 +100,7 @@ The engine is deliberately *not* called anywhere yet. Wiring it in:
`MXAnalysisWithoutFPGA` chooses `AzIntEngineGPU` vs `AzIntEngineCPU`). Construct with the same
`stream` the other GPU engines share.
3. **Keep the old functions** (`ProfileIntegrate2D`/`BraggIntegrate2D`) until the engine is validated
end-to-end against them on real data (`jfjoch_process --integrator ... --dump-observations`, A/B vs
end-to-end against them on real data (`rugnux --integrator ... --dump-observations`, A/B vs
`XDS_ASCII.HKL`), since the `±1`-band difference above is the one behavioural change.
The output `vector<Reflection>` is identical in shape (I, sigma, bkg, partiality, d, ...), so
@@ -55,7 +55,7 @@ per-frame 2D integration (BraggIntegrate2D | ProfileIntegrate2D) <-
-> merge + error model + stats (MergeOnTheFly: RefineErrorModel, CC1/2, R-meas, CCref, ISa)
```
Relevant knobs (`jfjoch_process`, mirrored in the viewer's Processing-settings tabs):
Relevant knobs (`rugnux`, mirrored in the viewer's Processing-settings tabs):
- `--integrator boxsum|gaussian|empirical` — default **gaussian** (`IntegratorMode::ProfileGaussian`).
- `-P fixed|rot|rot3d` — partiality model. `rot3d` = `PartialityModel::Rotation` **+** the orthogonal
`ScalingSettings::combine_3d` bool (see "Why rot3d is a bool" below).
@@ -205,7 +205,7 @@ goniometer sphere-of-confusion).
## Tooling (reusable instruments)
- **`--dump-observations <file>`** (`jfjoch_process`): `Combine3D` writes the unmerged fulls
- **`--dump-observations <file>`** (`rugnux`): `Combine3D` writes the unmerged fulls
(`h k l I σ d n_frames captured_fraction peak_frame`) for direct A/B vs `XDS_ASCII.HKL`.
- **`lyso_test/anomalous_scoreboard.sh <jfjoch_anomalous.hkl> [label]`**: one command → SHELXC + ANODE
under `qemu-x86_64-static` (the SHELX binaries hit the legacy vsyscall page; this WSL2 kernel is
-11
View File
@@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
# SPDX-License-Identifier: GPL-3.0-only
ADD_LIBRARY(JFJochProcess STATIC
JFJochProcess.cpp
JFJochProcess.h
JFJochProcessCommandLine.cpp
JFJochProcessCommandLine.h
)
TARGET_LINK_LIBRARIES(JFJochProcess JFJochReader JFJochImageAnalysis JFJochWriter)
+11
View File
@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
# SPDX-License-Identifier: GPL-3.0-only
ADD_LIBRARY(Rugnux STATIC
Rugnux.cpp
Rugnux.h
RugnuxCommandLine.cpp
RugnuxCommandLine.h
)
TARGET_LINK_LIBRARIES(Rugnux JFJochReader JFJochImageAnalysis JFJochWriter)
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochProcess.h"
#include "Rugnux.h"
#include <algorithm>
#include <atomic>
@@ -64,13 +64,13 @@ namespace {
}
JFJochProcess::JFJochProcess(JFJochHDF5Reader &reader, DiffractionExperiment experiment,
Rugnux::Rugnux(JFJochHDF5Reader &reader, DiffractionExperiment experiment,
PixelMask pixel_mask, ProcessConfig config)
: reader_(reader), experiment_(std::move(experiment)),
pixel_mask_(std::move(pixel_mask)), config_(std::move(config)) {}
ProcessResult JFJochProcess::Run(JFJochProcessObserver *observer) {
Logger logger("JFJochProcess");
ProcessResult Rugnux::Run(RugnuxObserver *observer) {
Logger logger("Rugnux");
ProcessResult result;
const auto dataset = reader_.GetDataset();
+8 -8
View File
@@ -20,7 +20,7 @@
class JFJochHDF5Reader;
// Offline reprocessing of a stored Jungfraujoch HDF5 dataset, shared by jfjoch_process,
// Offline reprocessing of a stored Jungfraujoch HDF5 dataset, shared by rugnux,
// jfjoch_azint and (later) the viewer. The full processing workflow lives here, not in the CLIs:
// setup, an optional two-pass rotation-indexing pre-pass, a parallel per-image loop (std::thread),
// an optional scaling/merging post-pass, and the _process.h5 output. The detector geometry and all
@@ -28,7 +28,7 @@ class JFJochHDF5Reader;
// carries run control. Cancellable from any thread (e.g. SIGINT or a GUI button) via Cancel().
enum class ProcessMode {
AzimuthalIntegration, // preprocess + azimuthal integration only (jfjoch_azint)
FullAnalysis // spot finding + indexing + refinement + integration (jfjoch_process)
FullAnalysis // spot finding + indexing + refinement + integration (rugnux)
};
struct ProcessConfig {
@@ -42,7 +42,7 @@ struct ProcessConfig {
// Output prefix for the _process.h5 (and scaled reflections). Empty => process without writing.
std::string output_prefix;
// Write the per-image _process.h5. Defaults true; jfjoch_process turns it off when merging
// Write the per-image _process.h5. Defaults true; rugnux turns it off when merging
// (the .mtz/.cif is the wanted output and the h5 is large) unless --write-process-h5 is given.
bool write_process_h5 = true;
@@ -95,15 +95,15 @@ struct ProcessResult {
// Callbacks for progress and live results. Methods may be called from worker threads, so an
// implementation must be thread-safe. The default no-ops suit the CLIs.
class JFJochProcessObserver {
class RugnuxObserver {
public:
virtual ~JFJochProcessObserver() = default;
virtual ~RugnuxObserver() = default;
virtual void OnPhase(const std::string &phase) {}
virtual void OnProgress(uint64_t done, uint64_t total) {}
virtual void OnImageProcessed(const DataMessage &msg) {}
};
class JFJochProcess {
class Rugnux {
JFJochHDF5Reader &reader_;
DiffractionExperiment experiment_;
PixelMask pixel_mask_;
@@ -111,11 +111,11 @@ class JFJochProcess {
std::atomic<bool> cancelled_{false};
public:
JFJochProcess(JFJochHDF5Reader &reader, DiffractionExperiment experiment,
Rugnux(JFJochHDF5Reader &reader, DiffractionExperiment experiment,
PixelMask pixel_mask, ProcessConfig config);
// Runs the configured workflow to completion or until Cancel(). Throws on setup failure.
ProcessResult Run(JFJochProcessObserver *observer = nullptr);
ProcessResult Run(RugnuxObserver *observer = nullptr);
// Request cancellation; safe to call from any thread (the worker loop checks between images).
void Cancel() { cancelled_ = true; }
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochProcessCommandLine.h"
#include "RugnuxCommandLine.h"
#include "../common/DiffractionExperiment.h"
#include <sstream>
@@ -48,12 +48,12 @@ namespace {
}
}
std::string JFJochProcessCommandLine(const ProcessConfig &config,
std::string RugnuxCommandLine(const ProcessConfig &config,
const DiffractionExperiment &experiment,
const std::string &input_file) {
std::vector<std::string> args;
const bool azint = (config.mode == ProcessMode::AzimuthalIntegration);
args.emplace_back(azint ? "jfjoch_azint" : "jfjoch_process");
args.emplace_back(azint ? "jfjoch_azint" : "rugnux");
auto add = [&](const std::string &flag, const std::string &val) {
args.push_back(flag);
@@ -5,14 +5,14 @@
#include <string>
#include "JFJochProcess.h" // ProcessConfig, ProcessMode
#include "Rugnux.h" // ProcessConfig, ProcessMode
class DiffractionExperiment;
// Reconstruct an equivalent jfjoch_process / jfjoch_azint command line for a configured run, so a
// Reconstruct an equivalent rugnux / jfjoch_azint command line for a configured run, so a
// job set up in the GUI can be handed off to a cluster. Covers the settings that matter for the
// run, not every obscure flag; geometry is taken from the input file, so geometry overrides are
// not emitted.
std::string JFJochProcessCommandLine(const ProcessConfig &config,
std::string RugnuxCommandLine(const ProcessConfig &config,
const DiffractionExperiment &experiment,
const std::string &input_file);
+3 -3
View File
@@ -47,8 +47,8 @@ ADD_EXECUTABLE(jfjoch_test
ModuleSummationTest.cpp
ZMQMetadataSocketTest.cpp
JFJochReaderTest.cpp
JFJochProcessTest.cpp
JFJochProcessLargeTest.cpp
RugnuxTest.cpp
RugnuxLargeTest.cpp
TestData.h
MovingAverageTest.cpp
ImageMetadataTest.cpp
@@ -81,7 +81,7 @@ ADD_EXECUTABLE(jfjoch_test
)
target_link_libraries(jfjoch_test Catch2WithMain JFJochBroker JFJochReceiver JFJochReader JFJochStreamWriter
JFJochProcess JFJochImageAnalysis JFJochCommon JFJochHLSSimulation JFJochPreview
Rugnux JFJochImageAnalysis JFJochCommon JFJochHLSSimulation JFJochPreview
jfjoch_xds_plugin)
target_include_directories(jfjoch_test PRIVATE .)
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
// End-to-end JFJochProcess runs over real JUNGFRAU datasets that are kept in git-LFS under
// End-to-end Rugnux runs over real JUNGFRAU datasets that are kept in git-LFS under
// tests/data. They are tagged [large] and SKIP() when the data is not present (e.g. LFS not
// pulled), so the default test run stays fast and CI without the data still passes.
@@ -16,7 +16,7 @@
#include "../common/DiffractionExperiment.h"
#include "../common/IndexingSettings.h"
#include "../reader/JFJochHDF5Reader.h"
#include "../process/JFJochProcess.h"
#include "../rugnux/Rugnux.h"
namespace {
// Start-up hook: report once whether the large datasets are available, so it is obvious why
@@ -40,7 +40,7 @@ namespace {
CATCH_REGISTER_LISTENER(LargeDataListener)
TEST_CASE("JFJochProcess_LysoRotation", "[large]") {
TEST_CASE("Rugnux_LysoRotation", "[large]") {
const auto master = jfjoch_test::LargeDataFile("lyso_rotation_master.h5");
if (!master)
SKIP("lyso_rotation_master.h5 not available (git-lfs data not pulled)");
@@ -67,7 +67,7 @@ TEST_CASE("JFJochProcess_LysoRotation", "[large]") {
config.two_pass_rotation = true;
config.reuse_rotation_spots = false; // redo spot finding (raw dataset may carry no spots)
JFJochProcess process(reader, experiment, dataset->pixel_mask, config);
Rugnux process(reader, experiment, dataset->pixel_mask, config);
ProcessResult result;
REQUIRE_NOTHROW(result = process.Run());
@@ -8,8 +8,8 @@
#include "../common/ScanResultGenerator.h"
#include "../writer/FileWriter.h"
#include "../reader/JFJochHDF5Reader.h"
#include "../process/JFJochProcess.h"
#include "../process/JFJochProcessCommandLine.h"
#include "../rugnux/Rugnux.h"
#include "../rugnux/RugnuxCommandLine.h"
namespace {
// Write a small VDS dataset of `n` flat images and return nothing (prefix_master.h5 +
@@ -44,7 +44,7 @@ namespace {
}
}
TEST_CASE("JFJochProcess_AzInt", "[HDF5][Full]") {
TEST_CASE("Rugnux_AzInt", "[HDF5][Full]") {
WriteTestDataset("process_azint_in", 8);
JFJochHDF5Reader reader;
@@ -57,7 +57,7 @@ TEST_CASE("JFJochProcess_AzInt", "[HDF5][Full]") {
config.nthreads = 2;
config.output_prefix = "process_azint_out";
JFJochProcess process(reader, dataset->experiment, dataset->pixel_mask, config);
Rugnux process(reader, dataset->experiment, dataset->pixel_mask, config);
ProcessResult result;
REQUIRE_NOTHROW(result = process.Run());
@@ -83,7 +83,7 @@ TEST_CASE("JFJochProcess_AzInt", "[HDF5][Full]") {
REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0);
}
TEST_CASE("JFJochProcess_NoOutput", "[HDF5][Full]") {
TEST_CASE("Rugnux_NoOutput", "[HDF5][Full]") {
WriteTestDataset("process_noout_in", 6);
JFJochHDF5Reader reader;
@@ -95,7 +95,7 @@ TEST_CASE("JFJochProcess_NoOutput", "[HDF5][Full]") {
config.mode = ProcessMode::AzimuthalIntegration;
config.nthreads = 3;
JFJochProcess process(reader, dataset->experiment, dataset->pixel_mask, config);
Rugnux process(reader, dataset->experiment, dataset->pixel_mask, config);
auto result = process.Run();
CHECK_FALSE(result.cancelled);
@@ -108,7 +108,7 @@ TEST_CASE("JFJochProcess_NoOutput", "[HDF5][Full]") {
REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0);
}
TEST_CASE("JFJochProcess_Cancel", "[HDF5][Full]") {
TEST_CASE("Rugnux_Cancel", "[HDF5][Full]") {
WriteTestDataset("process_cancel_in", 8);
JFJochHDF5Reader reader;
@@ -119,7 +119,7 @@ TEST_CASE("JFJochProcess_Cancel", "[HDF5][Full]") {
config.mode = ProcessMode::AzimuthalIntegration;
config.nthreads = 2;
JFJochProcess process(reader, dataset->experiment, dataset->pixel_mask, config);
Rugnux process(reader, dataset->experiment, dataset->pixel_mask, config);
process.Cancel(); // cancel before running: the worker loop stops immediately
auto result = process.Run();
@@ -133,7 +133,7 @@ TEST_CASE("JFJochProcess_Cancel", "[HDF5][Full]") {
REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0);
}
TEST_CASE("JFJochProcessCommandLine_Full", "[process]") {
TEST_CASE("RugnuxCommandLine_Full", "[process]") {
DiffractionExperiment x(DetJF(1));
IndexingSettings idx;
idx.Algorithm(IndexingAlgorithmEnum::FFT);
@@ -151,8 +151,8 @@ TEST_CASE("JFJochProcessCommandLine_Full", "[process]") {
config.rotation_indexing_image_count = 30;
config.spot_finding = DiffractionExperiment::DefaultDataProcessingSettings();
const std::string cmd = JFJochProcessCommandLine(config, x, "/data/lyso_master.h5");
CHECK(cmd.rfind("jfjoch_process", 0) == 0);
const std::string cmd = RugnuxCommandLine(config, x, "/data/lyso_master.h5");
CHECK(cmd.rfind("rugnux", 0) == 0);
CHECK(cmd.find("-N 8") != std::string::npos);
CHECK(cmd.find("-e 500") != std::string::npos);
CHECK(cmd.find("-o run1") != std::string::npos);
@@ -162,7 +162,7 @@ TEST_CASE("JFJochProcessCommandLine_Full", "[process]") {
CHECK(cmd.find("/data/lyso_master.h5") != std::string::npos);
}
TEST_CASE("JFJochProcessCommandLine_AzInt", "[process]") {
TEST_CASE("RugnuxCommandLine_AzInt", "[process]") {
DiffractionExperiment x(DetJF(1));
AzimuthalIntegrationSettings a;
a.AzimuthalBinCount(4);
@@ -173,7 +173,7 @@ TEST_CASE("JFJochProcessCommandLine_AzInt", "[process]") {
config.nthreads = 2;
config.output_prefix = "az";
const std::string cmd = JFJochProcessCommandLine(config, x, "in.h5");
const std::string cmd = RugnuxCommandLine(config, x, "in.h5");
CHECK(cmd.rfind("jfjoch_azint", 0) == 0);
CHECK(cmd.find("--azimuthal-bins 4") != std::string::npos);
CHECK(cmd.find("--min-q") != std::string::npos);
+1 -1
View File
@@ -23,7 +23,7 @@ git lfs pull # or: git lfs pull --include "tests/data/*.h5"
| `lyso_rotation_master.h5` | lysozyme rotation series (~1800 images) | yes (LFS) |
`lyso_rotation_master.h5` (plus its `_data_NNNNNN.h5` files) is fetched by `git lfs pull` and
drives `JFJochProcess_LysoRotation`. A separate serial dataset is intentionally **not** shipped
drives `Rugnux_LysoRotation`. A separate serial dataset is intentionally **not** shipped
to keep the repository small — the rotation series can be run in serial mode (full analysis
without rotation indexing) to exercise that path. To add your own dataset, drop the master + its
data files here as real files (not symlinks, if you intend to commit them via LFS); the master
+9 -9
View File
@@ -8,16 +8,16 @@ ADD_EXECUTABLE(jfjoch_extract_hkl jfjoch_extract_hkl.cpp
TARGET_LINK_LIBRARIES(jfjoch_extract_hkl JFJochReader)
INSTALL(TARGETS jfjoch_extract_hkl RUNTIME COMPONENT viewer)
ADD_EXECUTABLE(jfjoch_process jfjoch_process.cpp)
TARGET_LINK_LIBRARIES(jfjoch_process JFJochProcess JFJochReader JFJochImageAnalysis JFJochWriter)
INSTALL(TARGETS jfjoch_process RUNTIME COMPONENT viewer)
ADD_EXECUTABLE(rugnux rugnux_cli.cpp)
TARGET_LINK_LIBRARIES(rugnux Rugnux JFJochReader JFJochImageAnalysis JFJochWriter)
INSTALL(TARGETS rugnux RUNTIME COMPONENT viewer)
ADD_EXECUTABLE(jfjoch_scale jfjoch_scale.cpp)
TARGET_LINK_LIBRARIES(jfjoch_scale JFJochReader JFJochImageAnalysis JFJochWriter)
INSTALL(TARGETS jfjoch_scale RUNTIME COMPONENT viewer)
ADD_EXECUTABLE(rugnux_scale rugnux_scale.cpp)
TARGET_LINK_LIBRARIES(rugnux_scale JFJochReader JFJochImageAnalysis JFJochWriter)
INSTALL(TARGETS rugnux_scale RUNTIME COMPONENT viewer)
ADD_EXECUTABLE(jfjoch_azint jfjoch_azint.cpp)
TARGET_LINK_LIBRARIES(jfjoch_azint JFJochProcess JFJochReader JFJochImageAnalysis JFJochWriter)
TARGET_LINK_LIBRARIES(jfjoch_azint Rugnux JFJochReader JFJochImageAnalysis JFJochWriter)
INSTALL(TARGETS jfjoch_azint RUNTIME COMPONENT viewer)
# In-place re-compress /entry/data/data of a _data file from bitshuffle/LZ4 to bitshuffle/zstd.
@@ -28,8 +28,8 @@ INSTALL(TARGETS jfjoch_recompress RUNTIME COMPONENT viewer)
# On Windows these CLIs get getopt/getopt_long from the vendored wingetopt shim (libc has none).
IF (WIN32)
TARGET_LINK_LIBRARIES(jfjoch_extract_hkl wingetopt)
TARGET_LINK_LIBRARIES(jfjoch_process wingetopt)
TARGET_LINK_LIBRARIES(jfjoch_scale wingetopt)
TARGET_LINK_LIBRARIES(rugnux wingetopt)
TARGET_LINK_LIBRARIES(rugnux_scale wingetopt)
TARGET_LINK_LIBRARIES(jfjoch_azint wingetopt)
ENDIF()
+4 -4
View File
@@ -15,7 +15,7 @@
#include "../common/DiffractionExperiment.h"
#include "../common/PixelMask.h"
#include "../common/print_license.h"
#include "../process/JFJochProcess.h"
#include "../rugnux/Rugnux.h"
void print_usage() {
std::cout << "Usage ./jfjoch_azint {<options>} <input.h5>" << std::endl;
@@ -105,7 +105,7 @@ bool parse_on_off(const char *arg, bool &out) {
}
namespace {
std::atomic<JFJochProcess *> g_active_process{nullptr};
std::atomic<Rugnux *> g_active_process{nullptr};
void handle_sigint(int) {
if (auto *p = g_active_process.load())
p->Cancel();
@@ -232,7 +232,7 @@ int main(int argc, char **argv) {
logger.Info("Loaded dataset from {}", input_file);
// 2. Build experiment: defaults from the input file, overridden by command line. Output and
// runtime invariants are set inside JFJochProcess; here we only configure the geometry and
// runtime invariants are set inside Rugnux; here we only configure the geometry and
// azimuthal-integration settings.
DiffractionExperiment experiment(dataset->experiment);
if (beam_x.has_value()) experiment.BeamX_pxl(beam_x.value());
@@ -273,7 +273,7 @@ int main(int argc, char **argv) {
config.nthreads = nthreads;
config.output_prefix = output_prefix;
JFJochProcess process(reader, experiment, dataset->pixel_mask, config);
Rugnux process(reader, experiment, dataset->pixel_mask, config);
g_active_process = &process;
std::signal(SIGINT, handle_sigint);
@@ -16,7 +16,7 @@
#include "../common/PixelMask.h"
#include "../common/print_license.h"
#include "../image_analysis/LoadFCalcFromMtz.h"
#include "../process/JFJochProcess.h"
#include "../rugnux/Rugnux.h"
// Default rot3d per-frame scale-G smoothing range (XDS DELPHI-like), in degrees of rotation.
constexpr double SMOOTH_G_DEFAULT_DEG = 5.0;
@@ -26,7 +26,7 @@ constexpr double SMOOTH_G_DEFAULT_DEG = 5.0;
constexpr double REJECT_OUTLIERS_DEFAULT_NSIGMA = 6.0;
void print_usage() {
std::cout << "Usage jfjoch_process {<options>} <input.h5>" << std::endl;
std::cout << "Usage rugnux {<options>} <input.h5>" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -o, --output-prefix <txt> Output file prefix (default: output)" << std::endl;
std::cout << " -N, --threads <num> Number of threads (default: 1)" << std::endl;
@@ -58,7 +58,7 @@ void print_usage() {
std::cout << std::endl;
std::cout << " Scaling and merging" << std::endl;
std::cout << " -M, --scale-merge Scale and merge (refine mosaicity) and write scaled.hkl + image.dat" << std::endl;
std::cout << " -M, --scale-merge Scale and merge (refine mosaicity); automatic for rotation data" << std::endl;
std::cout << " --scale-fulls After -P rot3d combine, refit a per-frame scale on the fulls (XDS order, Unity model); implies -M. Default ON for rot3d" << std::endl;
std::cout << " --no-scale-fulls Disable the rot3d scale-fulls refit (it is on by default for rot3d)" << std::endl;
std::cout << " --write-process-h5 Also write the (large) _process.h5 when merging (default: only .mtz/.cif when merging)" << std::endl;
@@ -273,7 +273,7 @@ std::optional<CrystalLattice> parse_lattice_arg(const char *arg) {
}
namespace {
std::atomic<JFJochProcess *> g_active_process{nullptr};
std::atomic<Rugnux *> g_active_process{nullptr};
void handle_sigint(int) {
if (auto *p = g_active_process.load())
p->Cancel();
@@ -289,9 +289,9 @@ int main(int argc, char **argv) {
RegisterHDF5Filter();
print_license("jfjoch_process");
print_license("rugnux");
Logger logger("jfjoch_process");
Logger logger("rugnux");
std::string input_file;
std::string output_prefix = "output";
@@ -770,6 +770,11 @@ int main(int argc, char **argv) {
"indexing). Use --force-still to treat it as stills.");
}
// Rotation data is scaled and merged automatically (as if -M were given); stills still require
// an explicit -M. --force-still keeps rotation_indexing false, so stills stay opt-in.
if (rotation_indexing)
run_scaling = true;
// Configure Indexing
IndexingSettings indexing_settings;
indexing_settings.Algorithm(indexing_algorithm);
@@ -839,7 +844,7 @@ int main(int argc, char **argv) {
// Azimuthal integration (default q-spacing 0.01 1/A, from AzimuthalIntegrationSettings): the profile
// resolves the narrow ice rings for the ice-ring score. -q / --azim-* override. Applied before
// JFJochProcess builds the azint mapping from the experiment.
// Rugnux builds the azint mapping from the experiment.
{
AzimuthalIntegrationSettings azint_settings = experiment.GetAzimuthalIntegrationSettings();
if (min_q || max_q)
@@ -866,7 +871,7 @@ int main(int argc, char **argv) {
spot_settings.high_resolution_limit = d_min_spot_finding;
// Run the shared full-analysis workflow (rotation indexing + scaling/merging live in
// JFJochProcess; the experiment above carries all algorithm settings).
// Rugnux; the experiment above carries all algorithm settings).
ProcessConfig config;
config.mode = ProcessMode::FullAnalysis;
config.start_image = start_image;
@@ -888,7 +893,7 @@ int main(int argc, char **argv) {
// _process.h5 unless explicitly requested. Without merging, the _process.h5 is the only output.
config.write_process_h5 = run_scaling ? write_process_h5_flag : true;
JFJochProcess process(reader, experiment, dataset->pixel_mask, config);
Rugnux process(reader, experiment, dataset->pixel_mask, config);
g_active_process = &process;
std::signal(SIGINT, handle_sigint);
@@ -28,7 +28,7 @@
#include "../image_analysis/UpdateReflectionResolution.h"
void print_usage() {
std::cout << "Usage ./jfjoch_scale {<options>} <input.h5>" << std::endl;
std::cout << "Usage ./rugnux_scale {<options>} <input.h5>" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -o, --output-prefix <txt> Output file prefix (default: output)" << std::endl;
std::cout << " -N, --threads <num> Number of threads (default: 1)" << std::endl;
@@ -88,9 +88,9 @@ static option long_options[] = {
int main(int argc, char **argv) {
RegisterHDF5Filter();
print_license("jfjoch_scale");
print_license("rugnux_scale");
Logger logger("jfjoch_scale");
Logger logger("rugnux_scale");
std::string input_file;
std::string output_prefix = "output";
@@ -369,13 +369,13 @@ int main(int argc, char **argv) {
// Print resolution-shell statistics table
std::cout << merged_statistics;
// Space-group determination lives in the full jfjoch_process pipeline, where the lattice search
// Space-group determination lives in the full rugnux pipeline, where the lattice search
// and a consistent integration are available; this re-scaling tool only consumes a space group
// (from the file's /entry/sample/space_group_number or -S) and merges in it.
const bool fixed_space_group = space_group || experiment.GetGemmiSpaceGroup().has_value();
if (!fixed_space_group)
logger.Warning("No space group in the input file or on the command line - merged in P1. "
"Re-run jfjoch_process (which determines and stores the space group) or pass "
"Re-run rugnux (which determines and stores the space group) or pass "
"-S to scale and merge in the correct symmetry.");
const auto twin_sg_number = experiment.GetSpaceGroupNumber();
+1 -1
View File
@@ -110,7 +110,7 @@ ADD_EXECUTABLE(jfjoch_viewer jfjoch_viewer.cpp JFJochViewerWindow.cpp JFJochView
TARGET_LINK_LIBRARIES(jfjoch_viewer Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Charts Qt6::Concurrent
Qt6::OpenGL Qt6::OpenGLWidgets
JFJochReader JFJochLogger JFJochCommon JFJochWriter JFJochImageAnalysis JFJochProcess
JFJochReader JFJochLogger JFJochCommon JFJochWriter JFJochImageAnalysis Rugnux
fftw3f)
# Native GUI app per platform: a .app bundle on macOS, GUI subsystem (no console window) on
+1 -1
View File
@@ -57,7 +57,7 @@ void JFJochProcessController::run_(QString file_path, DiffractionExperiment expe
last_live_emit_ = {};
}
JFJochProcess process(reader, std::move(experiment), std::move(pixel_mask), std::move(config));
Rugnux process(reader, std::move(experiment), std::move(pixel_mask), std::move(config));
active_ = &process;
if (cancel_pending_)
process.Cancel();
+6 -6
View File
@@ -12,19 +12,19 @@
#include <mutex>
#include <thread>
#include "../process/JFJochProcess.h"
#include "../rugnux/Rugnux.h"
#include "../common/DiffractionExperiment.h"
#include "../common/PixelMask.h"
#include "../reader/JFJochReaderDataset.h"
Q_DECLARE_METATYPE(ProcessResult)
// Runs one JFJochProcess job off the GUI thread and reports back via queued Qt signals. The job
// Runs one Rugnux job off the GUI thread and reports back via queued Qt signals. The job
// opens its own private JFJochHDF5Reader on the file (HDF5 access is globally serialized, so this
// is safe alongside the interactive reader), so the viewer becomes a processing frontend without
// blocking the UI. Cancel() is forwarded to JFJochProcess::Cancel() (atomic) and works from any
// blocking the UI. Cancel() is forwarded to Rugnux::Cancel() (atomic) and works from any
// thread / at any point of the run.
class JFJochProcessController : public QObject, private JFJochProcessObserver {
class JFJochProcessController : public QObject, private RugnuxObserver {
Q_OBJECT
public:
explicit JFJochProcessController(QObject *parent = nullptr);
@@ -49,7 +49,7 @@ signals:
void liveDataset(std::shared_ptr<const JFJochReaderDataset> dataset);
private:
// JFJochProcessObserver - called from worker threads, forwarded as queued signals.
// RugnuxObserver - called from worker threads, forwarded as queued signals.
void OnPhase(const std::string &phase) override;
void OnProgress(uint64_t done, uint64_t total) override;
void OnImageProcessed(const DataMessage &msg) override;
@@ -58,7 +58,7 @@ private:
void joinWorker_();
std::thread worker_;
std::atomic<JFJochProcess *> active_{nullptr};
std::atomic<Rugnux *> active_{nullptr};
std::atomic<bool> running_{false};
std::atomic<bool> cancel_pending_{false};
+1 -1
View File
@@ -417,7 +417,7 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
connect(this, &JFJochViewerWindow::setAutoForeground,
viewer, &JFJochDiffractionImage::setAutoForeground);
// Processing jobs: run jfjoch_process locally / copy a cluster command line, and surface
// Processing jobs: run rugnux locally / copy a cluster command line, and surface
// finished runs as switchable dataset snapshots.
connect(processingJobsWindow, &JFJochProcessingJobsWindow::registerSnapshot,
reading_worker, &JFJochImageReadingWorker::RegisterProcessingSnapshot);
@@ -4,7 +4,7 @@
#include "JFJochProcessingJobsWindow.h"
#include "JFJochMergeStatsWindow.h"
#include "../widgets/ToolbarIcons.h"
#include "../../process/JFJochProcessCommandLine.h"
#include "../../rugnux/RugnuxCommandLine.h"
#include <QApplication>
#include <QCheckBox>
@@ -230,7 +230,7 @@ void JFJochProcessingJobsWindow::newJob(bool azint) {
if (action == 2) { // copy command line
const QString cmd = QString::fromStdString(
JFJochProcessCommandLine(config, experiment, inputs.file.toStdString()));
RugnuxCommandLine(config, experiment, inputs.file.toStdString()));
QApplication::clipboard()->setText(cmd);
QMessageBox::information(this, "Command line", cmd + "\n\n(copied to clipboard)");
return;
+1 -1
View File
@@ -19,7 +19,7 @@ class QToolButton;
// Makes processing a first-class GUI activity: a dockable panel with a table of processing jobs run
// on the current dataset. A job can be run locally (off the GUI thread, via JFJochProcessController)
// or its jfjoch_process command line copied for a cluster. A finished local run is registered as a
// or its rugnux command line copied for a cluster. A finished local run is registered as a
// reader snapshot so its results become a selectable view of the dataset. Re-processing reads a
// stored HDF5 file, so the panel shows an explanatory message while connected to a live HTTP stream.
class JFJochProcessingJobsWindow : public QWidget {