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:
commit
62c4aa933a
12 files changed
+109
-45
No files matched your search
@@ -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).
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||

|
||||
|
||||
**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.
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
```
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ rotation run with the defaults; stills differences are at the end.
|
||||

|
||||
|
||||
**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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user