Merge docs-rc175 into rc175: docs updated for this round (GPU memory decisions, Metal on macOS, viewer defaults and processed sweeps, tilted-detector beam stop, stills with a stationary axis, MR tie rule)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXc1zD1yxHqaR1DGdCTHL4
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-16 01:07:38 +02:00
commit 62c4aa933a
12 files changed
+109 -45

No files matched your search

+9 -2
View File
@@ -317,7 +317,12 @@ reflections it modulates.
The choice between groups and merges is a hypothesis comparison like §14.5: the candidate the choice
falls on has to beat a null of the same model placed from random orientations, and lead the other
groups by a margin in R-free. What the choice may do is limited exactly as for the rigid body, and
groups by a margin in R-free. Where another group is within that margin with as many copies placed,
the two have as many parameters, so the working reflections compare them as fairly as the free ones
and there are some twenty times as many: the other group is ruled out where the chosen placement's
$|F_\mathrm{obs}-F_\mathrm{calc}|$ is lower by 3 standard errors of the paired per-reflection
difference over every reflection to the scoring limit, and the choice is left undecided otherwise.
What the choice may do is limited exactly as for the rigid body, and
less: it may relabel the **enantiomorph** of the answer, past the anomalous check of §14.7, and never
reindexes; another group with the answer's rotations, or the twin hypothesis' subgroup, is written
beside the answer as `<prefix>_mr.mtz`/`.cif` with the model as placed there, and the answer's own
@@ -327,5 +332,7 @@ the budget of §14.8 (80% of the card's total memory, less what a merge still at
molecular transform, one rigid-body engine shared by every fit of the merge and the largest score engine
take their place in that order where they still fit, more rigid-body engines are added only within half
of the budget, and the score engines and the functions' working buffers share what is left, waiting for
each other rather than spilling past it. The keys, thresholds and table are described in
each other rather than spilling past it. On the CPU the molecular transform's FFT is split over the
threads by columns and planes, each done by one plan, so its result does not depend on the thread
count. The keys, thresholds and table are described in
[the results report](RUGNUX_REPORT.md#molecular-replacement).
+16 -7
View File
@@ -182,7 +182,8 @@ margin, not a centre: the surface can be locally flat over tens of pixels, and t
peak-to-runner-up margin says so. A **local walk** is then seeded at the capture and refines it; where
it declines there it is started again from the file's centre, and only where neither start gives it
something to fit does the capture stand alone. The transforms run on the GPU where one is present and
on FFTW otherwise, with everything that decides anything shared between the two.
they fit half of the card's total memory, and on FFTW otherwise, with everything that decides anything
shared between the two.
The run log then carries:
@@ -315,10 +316,10 @@ frames) projects those frames to a per-pixel mean and maximum, and writes the re
the pixel mask as bit 9, from where it excludes those pixels from every later stage.
The shadow is a place where the background is *missing*, so it is found by comparing each pixel's
background against the background at the same radius. The mean is pooled over a small box first —
background against the background at the same $2\theta$. The mean is pooled over a small box first —
a single pixel of a sparse background carries too few counts to tell a shadow from a Poisson hole,
and the stop is much wider than the box — and the comparison is against the median of the pixel's own
radius ring, taken over the pixels not already excluded and iterated a few times so the shadow stays
ring, taken over the pixels not already excluded and iterated a few times so the shadow stays
out of the baseline it is measured against. A pixel is shadow when the ratio falls below 0.50 **and**
the deficit is significant against its own Poisson scatter, $\sqrt{2\,(E-N+N\ln(N/E))}$ over the pooled
counts. A ratio says nothing when the background behind it is a handful of photons, and it is that
@@ -328,6 +329,11 @@ index of dispersion does *not* do this job — it is 1.0 inside the shadow and 1
shadowed pixel being Poisson at a low rate and a lit one Poisson at a high rate — only the rate
relative to the ring separates them.
A ring is a cone of constant $2\theta$, indexed by $D\tan 2\theta$ in pixels. On an untilted detector
that is a circle about the beam centre; on a tilted one — a swung $2\theta$ arm — it is not, and a circle
about the centre runs across the background's own fall-off with $2\theta$: at a 5° swing and 50 mm that
put a quarter of the detector in the shadow.
Nothing assumes the stop and the beam are concentric, because only the per-ring comparison is used; a
ring lying wholly inside the stop has no unshadowed pixel for its median to find, which is exactly the
case where the comparison must fail. Such a ring is judged against what this detector's background
@@ -357,10 +363,13 @@ in-plane lobes of every outer ring read as shadow (measured: 5 % and 11 % of two
nothing visible under either mask). The mean projection is therefore divided by that factor before the
ring comparison, and the Poisson deficit multiplies it back in so the significance is still the
significance of the counts that were recorded. The factor is the geometry's own, evaluated about the
centre just fitted rather than the one in the file: polarization is the only correction a ring carries
that varies *along* it — solid angle, detector response and air absorption are functions of $2\theta$
and the ring median absorbs them — and the geometry is what knows where the polarization plane lies
once the detector is tilted, the stored image quarter-turned or the detector rotated in its own plane.
centre just fitted rather than the one in the file, because the geometry is what knows where the
polarization plane lies once the detector is tilted, the stored image quarter-turned or the detector
rotated in its own plane. On a tilted detector the **solid angle** of a pixel varies along a ring too —
by tens of per cent at a 5° swing and 50 mm — so the mean is divided as well by the pixel's solid angle
over the one an untilted detector has at the same $2\theta$, $(D/l_z)^3$ with $l_z$ the pixel's distance along the beam, which is exactly 1 untilted;
the Poisson deficit multiplies that back in as it does the polarization. Detector response and air
absorption are functions of $2\theta$ alone, nearly enough, and the ring median absorbs them.
What survives is then shaped into a region, and what makes it specific is **size**, not connection to
the beam. A shadow is cast by something physical and is correspondingly large, while the background
+28 -9
View File
@@ -73,14 +73,18 @@ nothing.
stays on the host. [§1.4](CPU_DATA_ANALYSIS_IMAGE.md#14-the-beam-centre)
- **Beam centre by FFT** (`BeamCenterFFTGPU.cu`) - the self-convolutions that score every candidate
centre at once, their combination into one score surface, and the shortlist of its peaks, with only
the shortlist brought back. Falls back to FFTW on the host where the card has no room.
the shortlist brought back. Runs on FFTW on the host instead where it would need more than half of
the card's total memory, decided before the card is touched and logged.
[§1.4](CPU_DATA_ANALYSIS_IMAGE.md#14-the-beam-centre)
## Indexing
- **FFT indexer** (`FFTIndexerGPU.cu`) - projects the spots' reciprocal-space vectors onto a dense set
of directions, histograms each projection and transforms it with cuFFT, and picks each direction's
most prominent lattice peak; the candidate vectors then go to the host's lattice reduction.
of directions, histograms each projection and transforms it with cuFFT, 4096 directions at a time,
and picks each direction's most prominent lattice peak; the candidate vectors then go to the host's
lattice reduction. cuFFT gives the same bits at any batch size, so the batching changes nothing but
the memory held. A rotation run gives its indexers' memory back to the card once a pass has done its
last indexing.
[§5.1](CPU_DATA_ANALYSIS_INDEXING.md#51-directional-projections-and-histograms),
[§5.2](CPU_DATA_ANALYSIS_INDEXING.md#52-fft-peak-picking-and-candidate-vectors)
- **Fast-feedback indexer** (`ffbidx`) - third-party, from the fast-feedback-indexer project (PSI); a
@@ -99,7 +103,8 @@ nothing.
- **Bragg integration** (`BraggIntegrationEngineGPU.cu`) - for every predicted reflection: the
three-ring regions and neighbour ownership, box summation, the per-shell profile learned from the
strong spots and rebuilt per reflection, the radial background-curvature correction, and the profile
fit. Matches the CPU engine to floating-point precision.
fit. The profile fit is in single precision on both engines, in the same arithmetic, so the two
differ only in summation order.
[§9.2](CPU_DATA_ANALYSIS_INTEGRATION.md#92-box-summation-seed-and-fallback),
[§9.3](CPU_DATA_ANALYSIS_INTEGRATION.md#93-profile-fitted-extraction-default)
@@ -107,8 +112,15 @@ nothing.
One engine (`RotationScaleMergeGPU.cu`) keeps every observation resident on the card for the whole
scaling run; each per-group sum is a segmented reduction in a fixed order, so it is deterministic.
Whether the scaling runs there at all is decided before any observation is on the card, from the
card's total memory: a set whose sort alone, or whose scaling (estimated from its observation and
distinct-hkl counts once the sort has counted them), would take more than 80% of it scales on the CPU
instead, with a warning in the log (`RotationScaleMerge: ... scaling on the CPU instead`). Judged on the
total and not on what is free, the same input on the same card always takes the same path.
- **Ingest** - the integrated reflections uploaded frame by frame and taken apart on the card; the
- **Ingest** - the integrated reflections uploaded frame by frame, twice: first only what the sort and
the resolution cut need, then, once the scaling is known to fit, the observations the cut kept,
written straight to their place (the predicted detector positions stay on the host); the
frame flux applied; the sort into raw-hkl runs and the resolution cut; the recomputed rotation angles
(delta-phi), the rocking events relaid, and the partiality recomputed from the smoothed mosaicity.
The same sort and cut as the host, to the bit.
@@ -140,7 +152,9 @@ scaling run; each per-group sum is a segmented reduction in a fixed order, so it
## Model validation
The model's structure factors and the rigid body run on the card where there is one; the GPU and CPU
The model's structure factors and the rigid body run on the card where there is one, every part
placed on it or on the CPU up front against one budget, 80% of the card's total memory
([§14.8](CPU_DATA_ANALYSIS_DECISIONS.md#148-rigid-body-placement-of-the-model)); the GPU and CPU
agree to rounding (float distances, cuFFT for FFTW, the crystal's symmetry composed in reciprocal
space instead of by symmetrizing the grid).
@@ -157,8 +171,8 @@ space instead of by symmetrizing the grid).
[§14.4](CPU_DATA_ANALYSIS_DECISIONS.md#144-electron-density-maps)
- **Scale fit** (`ModelScaleGPU.cu`) - the sums of the overall-scale, anisotropic-B and bulk-solvent
fit over the reflections, in double and a fixed order, for many fits at once (the solvent grid, the
null replicates); the Levenberg-Marquardt control stays on the host. Used where the pool fits an
eighth of the card, the CPU fit otherwise.
null replicates); the Levenberg-Marquardt control stays on the host. Used where its engine fits the
validation's budget, the CPU fit otherwise.
[§14.2](CPU_DATA_ANALYSIS_DECISIONS.md#142-bulk-solvent-and-scaling)
- **Rigid body** (`RigidBodyGPU.cu`) - the rigid-body target for a placement: the model moved, its
F_calc and mask, the scale, the residuals and their Jacobian; used for the placement itself and for
@@ -177,6 +191,10 @@ space instead of by symmetrizing the grid).
every position in the cell, beside the copies already placed where there are any, and its mean,
spread and highest point.
What of the search goes on the card is planned once per merge on the same budget as model
validation's, before the search starts
([§14.10](CPU_DATA_ANALYSIS_DECISIONS.md#1410-molecular-replacement-placing-a-model-the-rigid-body-cannot)).
The placements are then refined, scored and checked against their null with the rigid body and the
structure factors above; the likelihood scoring runs on the host. The search is described in
[§14.10](CPU_DATA_ANALYSIS_DECISIONS.md#1410-molecular-replacement-placing-a-model-the-rigid-body-cannot), its keys and decision rules in
@@ -192,5 +210,6 @@ A macOS build has no CUDA, and one engine runs on the Apple GPU instead:
Everything computed in double and the profile learning stay on the CPU, as Apple GPUs have no
double precision. The fit's Gaussian uses an exp of our own (`BraggExp.h`) on both, as no GPU exp
matches the CPU's. The same result as the CPU engine **to the bit**, which a macOS CI step checks on
every push; `rugnux --no-gpu` keeps it off the GPU.
every push; `rugnux --no-gpu` keeps it off the GPU, and so does `--background-trim`, whose trimmed
mean has no Metal kernel.
[§9.2](CPU_DATA_ANALYSIS_INTEGRATION.md#92-box-summation-seed-and-fallback)
+18 -4
View File
@@ -9,7 +9,7 @@ It is a standalone Qt 6 application, distributed pre-built for **Linux, Windows
Gitea release page, and for Linux also in the Jungfraujoch RPM/APT repositories — see
[Release contents](RELEASE_CONTENTS.md) for what each package contains and what it requires, and
[Deployment](DEPLOYMENT.md) for how to install it. The macOS build needs **macOS 13 (Ventura) or
newer on an Apple Silicon Mac** (M1 and newer; Intel Macs are not supported) and is CPU-only — see
newer on an Apple Silicon Mac** (M1 and newer; Intel Macs are not supported) and has no CUDA — see
[Release contents ▸ macOS](RELEASE_CONTENTS.md#macos).
## Where it fits among the three analysis tools
@@ -29,6 +29,8 @@ newer on an Apple Silicon Mac** (M1 and newer; Intel Macs are not supported) and
[What Rugnux reads](RUGNUX_FORMATS.md)). These store one frame per file, so naming any frame opens
the whole sweep it belongs to. A raw frame carries the images and the geometry but no analysis
results, so the spot, reflection and per-image plot panels stay empty until something is computed.
A processing job on such a sweep writes the images into its `_process.h5`, and its run is shown from
that file like a run of an HDF5 dataset; *Original* goes back to the sweep.
- Runs an **embedded data-processing pipeline** — the same analysis code as the rest of
Jungfraujoch — performing spot finding, indexing and integration on the displayed image, with the
result drawn over it. This interactive analysis is not written anywhere.
@@ -128,6 +130,15 @@ indexing, Bragg integration, scaling, and the reference dataset (MTZ or structur
with an optional atomic model to validate the merged data against. The MX / AzInt / Calib switch
selects the kind of analysis and its page.
The panel starts from the defaults of a bare `rugnux <file>`, which the viewer and the command line
share, so an *Analyze dataset* run with nothing changed processes the data as `rugnux` would; the one
intended difference is that the viewer writes `_process.h5`. **Known unit cell** starts unticked when
a file is opened, and again when a run's results are shown: the cell and space group the file or the
run carries are filled in but not used until it is ticked (`-C`/`-S`), so a run determines the crystal
from the data, as `rugnux` does. The azimuthal-integration settings apply to AzInt jobs only; an MX job
integrates its profile (for the ice-ring score and the background estimate) with `rugnux --mode mx`'s
defaults.
![The Processing dock with the spot finding, indexing and reference sections open](images/viewer_processing_settings.png)
**Analyze image** re-analyses the current image with these settings, now and on every change
@@ -137,7 +148,9 @@ while it stays pressed; the inspector and the image overlays update at once.
**Analyze dataset** runs the whole sweep through the same pipeline as `rugnux` (a job dialog
takes the image range, the thread count and which files to write, and **Copy command** gives the
equivalent command line for a cluster). The **Jobs** dock follows the run.
equivalent command line for a cluster, leaving out `-N`, `--max-spots` and `--integration-radius`
where the run left them to `rugnux`, so that the copy makes the same automatic choices). The
**Jobs** dock follows the run.
![A finished job: the jobs table and the merge statistics window](images/viewer_processing_results.png)
@@ -164,8 +177,9 @@ installer and the `.tgz` are CUDA 13 and CUDA 12 respectively, which also decide
they run on — a V100 needs the CUDA 12 `.tgz`. See
[Release contents ▸ GPU generations and the NVIDIA driver](RELEASE_CONTENTS.md#gpu-generations-and-the-nvidia-driver).
On a Mac there is no CUDA: the viewer always runs the same pipeline on the CPU, with the FFTW
indexer, like the non-CUDA build elsewhere.
On a Mac there is no CUDA: the viewer runs the same pipeline on the CPU, with the FFTW indexer, like
the non-CUDA build elsewhere, except Bragg integration, which runs on the Apple GPU through Metal with
the same result as the CPU engine to the bit.
### Remote displays
+3 -2
View File
@@ -161,8 +161,9 @@ To build the viewer yourself on Windows, see
## macOS
The macOS artefacts are the `jfjoch_viewer` disk image and the `rugnux` `.tgz`; as on Windows, the
rest of Jungfraujoch, including the XDS plugin, is Linux-only. Both are **CPU-only** — macOS has no CUDA, so indexing uses the
FFTW indexer and the whole pipeline runs on the CPU — and both are built for **Apple Silicon only**.
rest of Jungfraujoch, including the XDS plugin, is Linux-only. Both are **without CUDA** — macOS has none, so indexing uses the FFTW indexer and the pipeline runs
on the CPU, except Bragg integration, which runs on the Apple GPU through Metal with the same result
as on the CPU to the bit — and both are built for **Apple Silicon only**.
They need **macOS 13 (Ventura) or newer**.
**Intel Macs are not supported.** No Intel (`x86_64`) code is built for macOS, and Rosetta cannot
+3 -2
View File
@@ -60,8 +60,9 @@ rugnux -o myrun -S P43212 -C 79,79,38,90,90,90 dataset_master.h5
Parallelism needs no asking for: a run already uses the machine's threads. `-N` is there to *limit*
that, or to lift the per-image loop's default ceiling of 16 workers per GPU.
Nothing more is needed to pick the workflow: a dataset carrying a **goniometer axis** is processed
as a rotation sweep, one without as **independent stills**, and scaling and merging run by default
Nothing more is needed to pick the workflow: a dataset carrying a **goniometer axis** that turns is
processed as a rotation sweep, one without (or with an axis that stands still) as **independent
stills**, and scaling and merging run by default
in both. A rotation run that merges — the default — leaves eight files next to each other:
```
+3 -3
View File
@@ -325,12 +325,12 @@ General:
| Option | Description |
| --- | --- |
| `-o, --output-prefix <txt>` | Output file prefix (default: `output`) |
| `-N, --threads <num>` | Number of worker threads (default, and for any value ≤ 0: every CPU the run may use - its affinity and cgroup quota). Some stages take fewer, because past a point more workers make them slower: the per-image loop of `--mode mx` uses at most 16 per GPU unless `-N` was given a positive value, and first-pass spot finding and the beam-stop pre-scan have ceilings of their own that `-N` does not lift. Scaling, merging and the space-group search use the full count |
| `-N, --threads <num>` | Number of worker threads (default, and for any value ≤ 0: every CPU the run may use - its affinity and cgroup quota). Some stages take fewer, because past a point more workers make them slower: the per-image loop of `--mode mx` uses at most 16 per GPU unless `-N` was given a positive value, and first-pass spot finding and the beam-stop pre-scan have ceilings of their own that `-N` does not lift. On a small card the two ceilings, and the per-image loop's 16 where `-N` was left at its default, come down to as many workers as keep their buffers (about 24 bytes per detector pixel each) within half of the card's total memory. Scaling, merging and the space-group search use the full count |
| `-s, --start-image <num>` | First image to process (default: 0) |
| `-e, --end-image <num>` | Last image to process (default: all) |
| `-t, --stride <num>` | Process every *n*-th image (default: 1) |
| `-v, --verbose` | Show the whole log on the terminal in place of the steps; `<prefix>_log.txt` is written either way |
| `--no-gpu` | Run on the CPU only, as on a machine without a GPU, even where one is present: CUDA is not touched and every GPU step takes its CPU path (`-X Auto` and `-X FFT` index with FFTW; `-X cuFFT` and `-X FFBIDX` are refused) |
| `--no-gpu` | Run on the CPU only, as on a machine without a GPU, even where one is present: CUDA is not touched (nor, on macOS, the Apple GPU that Bragg integration otherwise runs on) and every GPU step takes its CPU path (`-X Auto` and `-X FFT` index with FFTW; `-X cuFFT` and `-X FFBIDX` are refused) |
Mode — `--mode <name>` (default `mx`):
@@ -419,7 +419,7 @@ For very long axes the direction grid's angular resolution binds well below 500
| `--force-rotation-lattice <vec>` | Force rotation lattice (9 floats, Å), skipping the first pass |
| `--rotation-no-postrefine` | Rotation: disable the default-on two-pass geometry post-refine (see the rotation section) |
| `--prepass-fraction <f>` | Rotation: the two-pass geometry pre-pass integrates the first fraction `f` of the sweep, but at least 120° of it (0.05-1, default **0.5**; `1` integrates the whole sweep twice). The second pass always integrates every frame. The pre-scan and the first-pass indexing sample the same part of the sweep |
| `--refine-geometry[=N\|off]` | Stills: extra first pass that bundle-adjusts the shared beam/distance/cell from N strongly-indexed frames (default 200) then re-indexes; default ON for stills with a reference cell (`-C` / `-z`), `=off` disables |
| `--refine-geometry[=N\|off]` | Stills: extra first pass that bundle-adjusts the shared beam/distance/cell from N strongly-indexed frames (default 200) then re-indexes; default ON for stills with a reference cell (`-C` / `-z`), a stationary goniometer axis counting as stills, `=off` disables |
| `--index-ice-rings[=on\|off]` | Index on the spots flagged as sitting on an ice ring too, instead of setting them aside (default: **off**; no effect without `--detect-ice-rings`, which does the flagging) |
Indexer choice in brief: `ffbidx` (GPU) refines toward a **known cell** and is best for sparse
+17 -7
View File
@@ -38,7 +38,7 @@ take the archive for your architecture from the Gitea release page:
| `rugnux-<version>-linux-x86_64-cuda12.tgz` | 64-bit Intel/AMD Linux. Built on RHEL 8, so it runs on any newer Linux |
| `rugnux-<version>-linux-aarch64-cuda13.tgz` | 64-bit Arm Linux — NVIDIA GH200 and DGX Spark. Built on Ubuntu 24.04, so it needs glibc 2.39 or newer. Cross-compiled and **not yet exercised on Arm hardware** |
| `rugnux-<version>-win64-cuda13.zip` | 64-bit Windows |
| `rugnux-<version>-macos-arm64-cpu.tgz` | macOS 13 (Ventura) or newer on Apple Silicon (M1 and newer). CPU only — there is no CUDA on macOS. Intel Macs are not supported |
| `rugnux-<version>-macos-arm64-cpu.tgz` | macOS 13 (Ventura) or newer on Apple Silicon (M1 and newer). No CUDA on macOS: CPU, with Bragg integration on the Apple GPU (Metal). Intel Macs are not supported |
**The archive has no top-level directory** — it unpacks straight into `bin/` and `share/`. Always
give `tar` a destination of its own, or it will scatter those into whatever directory you are in:
@@ -101,7 +101,9 @@ answer.
On a Mac (Apple Silicon), leave the two `-march` flags out — they name an x86 level; the Apple
compiler's default target is already the M1 — and use `sysctl -n hw.ncpu` for the job count. The
build has no CUDA there and needs nothing installed beyond Xcode and CMake.
build has no CUDA there and needs nothing installed beyond Xcode and CMake; its Bragg integration runs
on the Apple GPU through Metal, with the same result as the CPU engine to the bit
([GPU data analysis ▸ Apple GPUs](GPU_DATA_ANALYSIS.md#apple-gpus-metal)).
## Hardware
@@ -145,6 +147,8 @@ count rather than on the detector. As a rule, **0.6 GB + 0.3 GB of host memory p
observations, and 2.4 GB + 0.1 GB of device memory per million**. The run reports what it actually
integrated — `RotationScaleMerge: ingested ... partial observations`. **32 GB of host RAM and an
8 GB card** cover every dataset measured here at `-N 6`; an ordinary sweep needs 16 GB and 6 GB.
Rotation scaling holds less on the card since rc.175 than when these were measured, so the device
column is an upper bound.
Both peaks fall in **scaling and merging**, not in the per-image loop: the loop alone stays under
3.2 GB on the device at `-N 6`, whatever the detector. The stage that needs the most is the 3D combine.
@@ -164,10 +168,16 @@ To use less:
- **`--no-export-unmerged`** does **not** save memory. `<prefix>_unmerged.mtz` is the largest file a
run writes, but its observations are resident either way; the flag saves disk, not RAM.
Nothing measures the free memory on the card before allocating. A run that does not fit degrades
Nothing measures the free memory on the card before allocating. The steps that can be too large for
a card decide between GPU and CPU before they touch it, from the card's **total** memory, so the same
input on the same card always takes the same path: rotation scaling and merging runs on the CPU where
it would take more than 80% of the card (and logs `... scaling on the CPU instead`), `--model`
validation and molecular replacement place each of their parts on the GPU or the CPU against a budget
of 80% of the card, the beam-centre FFT runs on the CPU where it would take more than half of it, and
on a small card the per-image workers are fewer. Past those decisions a run that does not fit degrades
where it can — device decompression falls back to the host, the beam-stop projection falls back to
the host, an indexer thread that cannot allocate reports the frame as not indexed, the `--model`
rigid-body placement moves to the CPU — and **fails the run otherwise**, with `Processing failed: CUDA (GPU) error (Failed to allocate device memory)` and a
the host, an indexer thread that cannot allocate reports the frame as not indexed — and **fails the
run otherwise**, with `Processing failed: CUDA (GPU) error (Failed to allocate device memory)` and a
non-zero exit status. That is deliberate: a CUDA out-of-memory says nothing about the frame in
flight and everything about the machine, so skipping frames would leave a run that looks complete
with quietly different merged numbers. A run is never silently short; it either completes or fails.
@@ -181,8 +191,8 @@ reflection is split over more frames, and **tens of millions of partial observat
a big cell. One sweep of a crystal with an axis of about 640 Å predicted 236 million reflections and
peaked at **17.5 GB of host memory on a GPU build and 72.5 GB on a CPU-only build**. A CPU-only build
keeps in host RAM what a GPU build keeps on the card, so it is the one that needs the large machine.
When such a set is too large for GPU scaling on the card, the run stops and says to run it on the
CPU (`rugnux --no-gpu ...`, or a CPU-only build) — which then wants that much host memory.
When such a set is too large for GPU scaling on the card, the run scales and merges it on the CPU
instead, with a warning in the log — and then wants that much host memory for that step.
### When host memory runs out
+2 -2
View File
@@ -7,8 +7,8 @@ rotation run with the defaults; stills differences are at the end.
![The rotation pipeline: pass 1 integrates half the sweep at the file's geometry, pass 2 the whole sweep at the refined one, and the better of the two goes on to the space group and the written merge](images/rugnux_pipeline.svg)
**Open the dataset.** The geometry, wavelength and goniometer come from the file
([What Rugnux reads](RUGNUX_FORMATS.md)). A goniometer axis makes it a rotation run, none makes it
serial stills — nothing is asked of the user.
([What Rugnux reads](RUGNUX_FORMATS.md)). A goniometer axis that turns makes it a rotation run; none, or one
that stands still from frame to frame, makes it serial stills — nothing is asked of the user.
**Pre-scan.** A projection of frames (60 by default) spread over the first part of the sweep, the part the geometry pre-pass integrates, finds the beam-stop shadow and masks
it ([§1.5](CPU_DATA_ANALYSIS_IMAGE.md)), masks pixels its frames show to be defective
+2
View File
@@ -105,6 +105,8 @@ The header block above section 1 records **how the result was produced**: `RUGNU
`GPU_COUNT= 0` appears on its own, with no `GPU=` line, when nothing was visible — which is the
first thing to check when a run took far longer than expected. Rugnux prints the GPU at startup
(`Compute`), before the run, so a missing GPU can be caught while there is still time to stop.
On macOS, where only the Bragg integration uses the Apple GPU (Metal), the report has
`GPU_COUNT= 0` and the startup line and the log name the Apple GPU instead.
Rates, per-image costs and progress remain on stdout only.
+2 -2
View File
@@ -161,8 +161,8 @@ disturbance of the sweep, not dose, and the report says so and points at the swe
## Still / serial data
A dataset with **no goniometer axis** (e.g. a serial grid scan) is processed as **independent
stills automatically** — no flag needed. Known-cell indexing with the GPU fast-feedback indexer,
A dataset with **no goniometer axis**, or one whose axis does not turn (e.g. a serial grid scan), is
processed as **independent stills automatically** — no flag needed. Known-cell indexing with the GPU fast-feedback indexer,
then merge against a reference structure:
```
+6 -5
View File
@@ -410,11 +410,12 @@ must be found on `PATH`). Neither has been tried yet.
Measured on the workstation (RTX 5080, rc175): myoglobin 10 s, cytochrome c 13 s, 4NWV 106 s
(of which MR 34 s and model validation 8 s; about 16 GB peak resident memory, the most of the
three by far). **cytochrome c fails today**: the prediction is a monomer and the crystal holds
three copies related by a pseudo-translation; MR places three copies in both P 32 2 1 (R-free 0.427)
and P 31 2 1 (0.437), the margin is under the 0.03 it needs to choose, and so it places neither
(`MR_DECISION= UNDECIDED`). The battery's own three-copy reference model, given to `--mr`, decides
the hand at once. The set stays in the benchmark and fails until MR can decide this case.
three by far). Cytochrome c is the hard one: the prediction is a monomer and the crystal holds
three copies related by a pseudo-translation, and MR places three copies in both P 32 2 1 (R-free
0.427) and P 31 2 1 (0.437), under the 0.03 R-free margin. It was `MR_DECISION= UNDECIDED` when the
benchmark was added; with as many copies in both groups, MR now compares them on every reflection
(paired residuals, [RUGNUX_REPORT ▸ Molecular replacement](../../docs/RUGNUX_REPORT.md#molecular-replacement))
and decides P 32 2 1.
### Stress: hunting crashes that do not reproduce