Commit Graph
1565 Commits
Author SHA1 Message Date
leonarski_fandClaude Opus 5.5 76adf3f0fa Merge origin/rc175 (macOS signing and notarization) into rc175
Build Packages / Create release (push) Successful in 8s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 8m53s
Build Packages / build:portable:macos-arm64:nocuda (push) Failing after 10m37s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 14m38s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 19m15s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 19m49s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m41s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 23m5s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 23m47s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m48s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 19m39s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 28m52s
Build Packages / Generate python client (push) Successful in 26s
Build Packages / Build documentation (push) Successful in 1m0s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 20m25s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 17m44s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 21m46s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 23m32s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 22m48s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 18m46s
Build Packages / Unit tests (push) Successful in 1h37m57s
Build Packages / Validation Battery (push) Successful in 37m52s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:47:07 +02:00
leonarski_fandClaude Opus 5.5 082781490a Merge stillsint into rc175: the stills pre-scan masks pixels holding the error value and dead pixels (no false overloads; +4% observations at high resolution)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:32:34 +02:00
leonarski_fandClaude Opus 5.5 3f25bbf003 Merge fftpeak exact commits into rc175: GPU peak search four threads per direction; CudaFFTPlan waits for cuFFT's NULL-stream plan setup (latent race)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 17:37:54 +02:00
leonarski_fandClaude Opus 5.5 90ba15871f CudaFFTPlan: wait for the plan's NULL-stream setup before it is used, output unchanged
A transform launched on a non-blocking stream straight after cufftPlanMany could read the
plan before its setup, still queued on the NULL stream, had finished: Xid 31, a read at a
fixed near-null address, "an illegal memory access" in the indexer threads. It showed at
transform length 3072, in 3 of 4 runs of one rotation set where a pool built its FFT
indexers at the start of integration (6 of 28 ci-tier sets, 9 of 25 null-model runs).
It went away under CUDA_LAUNCH_BLOCKING=1 and with a cudaStreamSynchronize(NULL) after
the plan (0 of 11 runs). The 3142 length the indexer used so far never hit it, but
nothing ordered it either.

Plans are built once per indexer, so the wait costs nothing measurable. The other cuFFT
users (beam-centre FFT, model structure factors, rigid body) create raw plans and are not
covered by this wrapper.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 16:40:34 +02:00
leonarski_fandClaude Opus 5.5 eb53b11b88 Merge stillsmerge into rc175: stills crystals indexed in a lattice containing the run's are relabelled onto its cell; unmerged MTZ only for crystals on the run's cell; French-Wilson on the merge's workers
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 15:34:40 +02:00
leonarski_fandClaude Opus 5.5 79b82aae7b Merge stillsidx into rc175: the stills min-pix choice searches the lattice once per frame (known cell: same frames, 1.1-1.6x faster; de novo: noise lattices dropped)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 15:03:50 +02:00
Filip LeonarskiandClaude Fable 5.1 82e5de1261 macOS: sign jfjoch_viewer and rugnux with a Developer ID and notarize them
Build Packages / Create release (push) Successful in 7s
Build Packages / build:portable:macos-arm64:nocuda (push) Failing after 3m56s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 9m51s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 14m6s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 17m41s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 20m52s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m19s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 21m26s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 12m53s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 24m2s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m48s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 14m55s
Build Packages / Generate python client (push) Successful in 28s
Build Packages / Build documentation (push) Successful in 1m4s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 26m8s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 22m46s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 29m3s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 27m12s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 25m36s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 28m42s
Build Packages / Unit tests (push) Failing after 1h23m15s
Build Packages / Validation Battery (push) Successful in 33m26s
JFJOCH_MACOS_SIGNING_IDENTITY (the certificate's SHA-1) makes the install step sign the shipped
program with hardened runtime and secure timestamp: macdeployqt's -sign-for-notarization for the
viewer bundle, a codesign INSTALL(CODE) for rugnux. tools/notarize_macos.sh then submits the cpack
artefact to Apple, waits for the verdict and staples the ticket to the .dmg; the rugnux binary
inside the .tgz is submitted as a zip, as a bare executable cannot carry a ticket. Both macOS CI
jobs resolve the identity from the runner's keychain, check for the hardened runtime and notarize
on every push, so a rejection shows up with the change that caused it.

The identity is passed as the hash, not the name: Qt's generated deploy script writes
DEPLOY_TOOL_OPTIONS unquoted, so a name with spaces splits into several macdeployqt arguments. The
notarytool profile has to live in the login keychain file (store-credentials --keychain): the
default data-protection keychain becomes unreadable once the screen locks, which an unattended
runner's does.

Verified end to end on the runner: both artefacts accepted by Apple, the stapled image and a
quarantined copy of the app and of rugnux pass Gatekeeper as Notarized Developer ID, signing and
the credential lookup work from a launchd-started process with the console locked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 15:01:30 +02:00
leonarski_fandClaude Opus 5.5 b574a0e600 docs: the min-pix 1 search gate is de novo only
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 14:59:04 +02:00
leonarski_fandClaude Opus 5.5 92356a8451 Revert "PROTOTYPE (owner decision): ffbidx stills index each frame with one call on all spots"
This reverts commit 64a8d8234. Measured on non-lysozyme sets (one search per frame scheme, 5000
frames each, with -C -S): on a body-centred orthorhombic crystal with a 235 A axis the single call
indexed 14.4% of frames against 15.9% with the 30/80/all escalation (79.7k -> 63.1k observations);
the escalation restored the frame count exactly (15.9%) on that set, on a P212121 set (73.6%) and on
lysozyme (55.0%). The lean seed matters for ffbidx on a long cell.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 14:58:34 +02:00
leonarski_fandClaude Opus 5.5 98fbe0c2c9 PROTOTYPE: the min-pix 1 search gate applies only to de-novo indexing
With a known cell the min-pix 1 list is searched again whatever the min-pix 2 list holds. On sparse
serial data the frames with almost no spot of two pixels are real hits: a weakly diffracting
hexagonal run (20000 frames, -C -S) lost half its indexed frames and observations to the gate
(6.5 -> 3.4%, 273k -> 120k observations, CC1/2 .83 -> .74); with the gate off for a known cell it
matches the original scheme (6.5%, 274k, .83). De novo the gate stays: there the cells a free search
fits to single pixels were unrelated to the crystal's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 14:58:34 +02:00
leonarski_fandClaude Opus 5.5 31ec9f8d9c PROTOTYPE fixes: a carried lattice keeps the spindle severity; the min-pix 2 gate counts spots as the indexer does
The carried indexer answer now brings its spindle severity along, so with a goniometer (--force-still)
no severity-only pass runs per carried list. The min-pix 1 search gate counted ice-ring spots the
indexer leaves out; it now counts the spots indexing would use.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:48:45 +02:00
leonarski_fandClaude Opus 5.5 c3cefebe8f FFT indexer GPU peak search: four threads per direction, output unchanged
The peak search gave each of the 16384 directions to one thread, a chain of dependent
FP64 additions over 1572 bins - latency-bound on a card that holds far more threads.
Each direction's spectrum is now split into four consecutive runs of bins, one thread
each, and the runs' first-best peaks are combined in order with the same strict
comparison, so the first bin of highest prominence is still the one returned.

A run after the first sums its starting window afresh rather than carrying the running
sum in. The double sums of float magnitudes are exact unless a magnitude is below
~1e-9 of its window sum, so the background is the same double: 0 of 491520
directions differ in a synthetic still/rotation harness, and the hkl of 3000 stills
frames (-X fft) is byte-identical.

Kernel (isolated, RTX 5080): 0.87 -> 0.65 ms per call. The stills de-novo loop gains
~4% (128 -> 133 frames/s, ABAB, load ~10): the FFT chain as a whole is bound by GPU
memory bandwidth under the indexer pool's concurrency, so even a background in float
(6x faster kernel, not adopted) gives only ~9%. Rotation first pass: FFT kernels are
~3% of the indexing step.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:48:39 +02:00
leonarski_fandClaude Opus 5.5 aca4dd3cca Merge origin/rc175 (server battery results, 4nwv in the ci tier) into rc175
Build Packages / Create release (push) Successful in 8s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 9m13s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 14m55s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 17m38s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 19m39s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 20m1s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m21s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 23m14s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 24m8s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m59s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 28m21s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 20m15s
Build Packages / Generate python client (push) Successful in 34s
Build Packages / Build documentation (push) Successful in 1m20s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 21m20s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 22m47s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 22m5s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 18m18s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 22m31s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 18m42s
Build Packages / Unit tests (push) Failing after 1h38m41s
Build Packages / Validation Battery (push) Successful in 41m45s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:42:25 +02:00
leonarski_fandClaude Opus 5.5 c15ee18782 Merge rc175 86f17740f into stillsmerge
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:41:52 +02:00
leonarski_f cb044459ba Merge commit '86f17740f' into stillsidx 2026-10-14 13:41:39 +02:00
leonarski_f 1582a7f024 Merge commit '86f17740f' into stillsint 2026-10-14 13:41:34 +02:00
leonarski_fandClaude Opus 5.5 86f17740f6 ACKNOWLEDGEMENT: Markstein reference without the working note
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:15:22 +02:00
leonarski_fandClaude Opus 5.5 51397c15f4 Merge FFT indexer GPU peak search rewrite into rc175: ~45% faster per call, output unchanged
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:15:17 +02:00
leonarski_fandClaude Opus 5.5 439ca10db1 RESULT-CHANGING (owner approved): stills crystals indexed in a lattice containing the run's are put on the run's cell
A still indexed on few spots often gets a lattice that explains them as well as the true one: an axis
multiple, whose reciprocal lattice holds every true node plus the nodes between, or the true lattice in
another setting. Its cell is then not the run's, the merge leaves it out, and its reflections at the true
nodes - as good as any - are lost. On two 25k-frame XFEL runs of a tetragonal crystal these were 26-30%
of the indexed frames; for those holding the true cell as a sublattice, the reflections at true nodes had
the I/sigma of accepted frames and those between them none.

RelabelOnSublattice (new, scale_merge/SublatticeRelabel) compares lattices, not cell parameters: it
looks for integer combinations of the frame's reciprocal axes whose full metric is the run's cell within
the merge's own tolerances, volume ratio 1-4 (another setting, or a 2x-4x supercell). Where several such
bases exist - a nearly symmetric supercell fits along more than one axis - the one with the strongest
mean I/sigma at its nodes is taken, since only the true lattice has signal there. The frame's indices
are re-derived on that basis (h = h' M^-1), reflections that are not nodes are dropped, as are those
finer than any frame already on the run's cell reaches (the wrong lattice was also predicted to a wrong
resolution), and the frame gets the run's basis in its own orientation. It runs before the first stills
merge, and in --mode scale before the resolutions are recomputed from the cell.

--mode scale, both runs (indicative; the mx pairs are queued): 1060 and 920 crystals put back,
observations +8%, I/sigma 10.3 -> 10.6 and 10.7 -> 11.0, CC1/2 0.966 -> 0.969 and 0.970 -> 0.966.
Tests: SublatticeRelabel_DoubledAxisGoesBackToTheTrueCell, _OtherSettingAndUnrelatedLattice,
_SignalPicksTheAxisThatWasDoubled.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:15:13 +02:00
leonarski_fandClaude Opus 5.5 a574e97b10 RESULT-CHANGING (owner approved): stills unmerged MTZ written only for the crystals on the run's cell
The unmerged MTZ took every integrated still. A still whose cell is not close to the run's - the same
test, with the same tolerances, the merge applies - was indexed in another lattice (on few spots,
typically an axis multiple), so its H K L do not name the reflections a reading program takes them
for: aimless or careless would scale them in as wrong reflections. On two 25k-frame XFEL runs such
frames carried 60% of the integrated reflections, and the 824 MB file was the largest single cost
after the merge (about 7 s in --mode scale). Rotation runs are unchanged (one lattice).

The orientation matrix in the batch headers is likewise taken from the first crystal on the run's cell.
Test: Unmerged MTZ: on stills, only the crystals on the run's cell.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:15:13 +02:00
leonarski_fandClaude Opus 5.5 02ea56b2d3 Merge broker-fft-nogpu into rc175: FFT without a GPU turns indexing off instead of cancelling the collection
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:14:25 +02:00
leonarski_fandClaude Opus 5.5 bdb5aaa44b Indexing algorithm FFT without a GPU resolves to None: live indexing is off instead of the collection cancelled
The broker cancelled the whole data collection on the first indexed image when its algorithm was FFT and no
GPU was visible (the indexer pool refuses FFT there, and the receiver cancels on the exception). FFTW is too
slow for live stills, so it is not switched to; with None the pool reports the frame as not indexed and the
collection runs on. rugnux resolves -X FFT itself (cuFFT or FFTW) and never reaches this case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:14:25 +02:00
leonarski_fandClaude Opus 5.5 b92afe1b3e Merge gpu8gb into rc175: rotation runs on an 8 GB card - probe and image passes take turns below 12 GB, workers bounded by card memory, rotation scaling on the CPU where its GPU footprint is over 80% of the card
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:12:04 +02:00
leonarski_fandClaude Opus 5.5 d0c9ee0410 Merge nogpu into rc175: rugnux --no-gpu keeps the process off the GPU; -X FFT is the algorithm (cuFFT on a GPU, FFTW otherwise), -X cuFFT the GPU implementation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 13:07:08 +02:00
leonarski_fandClaude Opus 5.5 3d48b76b49 Merge stillsmerge into rc175: stills merge 2.3-2.5x faster (exact); scaling reference and tilt refinement over the merged crystals only; tilt post-refinement solved directly
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 12:46:11 +02:00
leonarski_fandClaude Opus 5.5 2d118f6df1 stills merge export: French-Wilson and free-R flags on the merge's workers, output unchanged
ExportReflections ran French-Wilson single-threaded: about 1 s on the P1 search merge of a 25k-frame
stills run (180k reflections). It now uses the merge's thread count, as AssignRfreeFlags does; both
give the same result at any worker count by construction. mtz/unmerged-mtz data, hkl, cif and report
identical (--mode scale, 25k-frame XFEL run).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 12:14:25 +02:00
leonarski_fandClaude Opus 5.5 4b92ec25a6 On a card under 12 GB probe passes also take turns with each other
With the main image pass already waited for, a simulated 8 GB card with 7.0 GB free (the display's
share taken) still ran out of memory: the rotation-scale walk's index-only probe and the speculative
geometry probe ran beside each other during the merge, and the walk's cuFFT plan failed. On a small
card a probe pass now also waits, before it starts, for every other probe pass to end. Scheduling
only; nothing changes on a 16 GB card.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 12:13:17 +02:00
leonarski_fandClaude Opus 5.5 a509fd8022 rugnux stills: mask the pixels holding the detector's error value (and dead pixels) in the pre-scan
The defective-pixel finder ran only on scanning rotation data. On stills, a pixel that holds the
detector's error value on every frame but is missing from the file's static mask is read by the
integrator as a per-frame unreadable pixel, so every reflection whose signal disk touches it is
flagged overloaded and the merge drops it. On a SwissFEL JUNGFRAU stills run, 14k such pixels made
3.9% of all integrated reflections "overloaded" (mostly finer than 1.6 A, where they are scarcest)
and raised a false OVERLOADS warning; with them masked, 0.05% remain flagged and the profile fit
keeps the reflections whose disk stays at least MINPK readable.

On stills only the error-value and dead rules apply: the hot-pixel rule counts a reflection's stay on
a pixel in rotation, so spacing is passed as 0 there, which HotPixelFinder already treats as "no
hot-pixel test". Rotation is unchanged (same gate, same oscillation and spacing).

Result-changing for stills; merged-statistics A/B on full runs and the battery ci check still to do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 12:05:05 +02:00
leonarski_fandClaude Opus 5.5 c1b745fdfc Image-loop workers per card bounded by the card's memory
A worker of the full-analysis image loop holds about 24 bytes a detector pixel on its card (sixteen
held 6.3 GB on a 16.8 Mpx detector, measured with nsys). The workers now keep to half of the card's
total memory: on a 16 GB card that is still the sixteen of before for detectors up to ~21 Mpx; on
an 8 GB card with a 16.8 Mpx detector it is ten, where sixteen ran out of memory with ~7 GB free.
How many workers there are changes only the loop's speed. An explicit -N is obeyed as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 12:01:57 +02:00
leonarski_fandClaude Opus 5.5 beba8f0301 Rotation scaling runs on the CPU where its GPU footprint is over 80% of the card
The device scaling's peak, measured with nsys on four large sets (33-123 M observations, 4-16 M
distinct hkl), is 81 bytes an observation plus 291 bytes a distinct hkl to within 5%. Once the
ingest has counted both, an engine whose estimate is over 80% of the card's total memory scales on
the CPU instead, with one warning line saying why; a resident ingest is made again on the host,
which the CPU path needs. Decided once, before anything is scaled, on the card's total memory: the
answer depends on the card, not on the moment.

On a simulated 8 GB card (total clamped to 8.2e9, 7.75 GB free) a 33 M-observation, 15 M-hkl set
whose final merge alone needs ~7.5 GB on the GPU ran out of memory there; it now completes, and its
merged MTZ matches the 16 GB GPU run to float rounding (max relative difference 9e-7 on any
intensity column, same reflections, cell, space group and resolution cut). The largest estimate on
the battery is 12.45 GB, under 80% of a 16 GB card, so nothing changes there.

The all-observations arm is copied from the engine's card arrays only when that engine is on the
GPU.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 12:01:57 +02:00
leonarski_fandClaude Opus 5.5 3c4d730f89 RESULT-CHANGING (owner decision): stills tilt post-refinement solved directly instead of by Ceres
Each crystal's (psi_x, psi_y) tilt was refined by a Ceres problem rebuilt for every inner
iteration: one auto-differentiated residual block and one Cauchy loss per reflection. With the
scaling reference cleaned up it was 70% of the stills scale/merge. The same objective - Cauchy
on the sigma-weighted intensity residuals plus the Gaussian prior on the tilt, the tilt bounded -
is now minimised by a small Levenberg-Marquardt on the rho'-reweighted 2x2 normal equations,
the tilt applied to all reflections through one rotation matrix and the Jacobian taken by
central differences.

It reaches the same answer to optimizer precision: on the new synthetic test
(StillsPartialityRefine_RecoversTheTilt) partiality error 0.0559 -> 0.03019 with Ceres and
0.03016 with this, mean tilt 0.02065 vs 0.02067 deg; on two 25k-frame XFEL runs (--mode scale)
CC1/2, I/sigma, R_pim and the CC against a deposited lysozyme model agree to 0.0005, mean
|dpsi| 0.023 deg on both. Scale+merge 6.9 -> 3.0 s (--mode scale, loaded box).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 11:59:09 +02:00
leonarski_fandClaude Opus 5.5 4936f9b408 RESULT-CHANGING (owner decision): stills scaling reference and tilt refinement only over the crystals the merge keeps
The final stills merge drops every crystal whose cell is not close to the consensus cell
(MergeOnTheFly::Mask), but the scaling reference (MergeAll) and the partiality post-refinement
were made over all of them. On two 25k-frame XFEL runs of a tetragonal crystal those were 26-30%
of the indexed frames and 60% of the integrated reflections: weak frames (median 8 indexed
spots) indexed in a 2x/3x/4x supercell or an unrelated lattice, whose supercell-only reflections
are pure noise and whose indices do not name the true reflections. They went into the
reference every merged crystal was scaled against, and through the tilt refinement.

MergeAll and StillsPartialityRefine::Run now take the reference cell and use only the crystals
it accepts - the same test, with the same tolerances, as the merge.

Measured (both runs, mx path): CC1/2 0.9599 -> 0.9598 and 0.9645 -> 0.9644, I/sigma +0.05 and
+0.02, R_pim unchanged, space group and resolution unchanged; --mode scale against a deposited
tetragonal lysozyme model CC 0.9452 -> 0.9477 and 0.9461 -> 0.9473. Scale+merge 2.1x faster on
--mode scale (14.6 -> 6.9 s, loaded box).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 11:32:51 +02:00
leonarski_fandClaude Opus 5.5 39b488e323 Probe passes and the main image pass take turns on a card under 12 GB
On a 16 GB card a probe pass (GPUWorkBeside) runs its image work beside the main line's image pass
(GPUImagePass). Measured on a 16 Mpx detector with nsys, the two together held 8.6 GB of device
buffers (two sets of 24 per-worker 67 MB image buffers plus five 0.6 GB FFT indexers), so on an
8 GB card the refined-geometry pass ran out of memory ("Allocating GPU memory (53.0 MB) failed").

Below 12 GB of card memory each of the two now waits, before it starts, for the other kind to end.
Only when each runs depends on the card, never what it computes; neither waits once running, so
nothing waits in a circle. On a 16 GB card nothing changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:50:56 +02:00
leonarski_fandClaude Opus 5.5 64a8d82341 PROTOTYPE (owner decision): ffbidx stills index each frame with one call on all spots
Changes results - not for merging without the owner's approval.

The 30/80/all seed escalation exists for the de-novo search, which a flooded frame's full spot list
derails. ffbidx fits a known cell with a trimmed score that already discounts off-lattice spots, so
for it the escalation only multiplied calls: up to three per searched list, ~2.2 per indexed frame
with a fixed min-pix 2. The ffbidx path is GPU-bound (nsys: card 98.5% busy, ffbidx kernels 86%), so
calls are the loop rate. Stills only: rotation never resolves to ffbidx.

Measured on top of the one-search min-pix scheme (parent), two full 24960-frame serial runs, P41212
merge (box loaded by other agents, load 13-17):
  run A: 1130 -> 1532 frames/s; obs 5.30M -> 5.47M; I/sig 13.02 -> 13.12; R_pim 6.46 -> 6.43%;
         CC1/2 .973 -> .976; indexing rate 56.9 -> 57.3%  (original scheme: 785 frames/s, 5.37M,
         13.09, 6.48%, .970)
  run B: 1017 -> 1488 frames/s; obs 4.89M -> 4.99M; I/sig 11.95 -> 11.94; R_pim 6.47 -> 6.42%;
         CC1/2 .970 -> .972; indexing rate 52.4 -> 52.9%  (original: 750, 4.91M, 12.00, 6.50%, .971)
Warm 6000-frame ABAB in one lock hold: 1453/1419 -> 1734/1810 frames/s, same indexing rate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:49:21 +02:00
leonarski_fandClaude Opus 5.5 804206155b PROTOTYPE (owner decision): stills min-pix choice searches the lattice once per frame, ~2x faster
Changes results - not for merging without the owner's approval.

The per-image min-pix scheme indexed every frame at min-pix 3, 2 and 1, each with the 30/80/all seed
escalation, then indexed the winner again: 3.9 indexer calls per frame. Measured per frame on a
serial lysozyme run (2000 frames, de novo and against the known cell):
- where several lists index, the searches return the same lattice (97-98% of pairs);
- the gain over a fixed min-pix 2 has two sources: (A) frames min-pix 2 indexes but whose min-pix
  1/3 list indexes ~26 more spots (a better per-frame refinement), and (B) frames with almost no
  spot of two pixels (min-pix 3/2 lists of 1-3 spots, ~50 single pixels at min-pix 1) on which the
  min-pix 1 search fits 8-9 spots. De novo, 189 of 194 such B lattices had cells unrelated to the
  crystal's; against the known cell they index, but leaving them out raises I/sigma and lowers R_pim.

Now: the lattice is searched for on the min-pix 2 list (min-pix 3 where that does not index); the
first list that indexes hands its indexer answer to the others, which are only refined and scored
(FrameIndexerCalls::lattice_found). The min-pix 1 list is searched on its own only where the min-pix
2 list holds at least ViableCellMinSpots spots. Ties keep the larger min-pix, as before.

Measured on two full 24960-frame serial runs (RTX 5080, box loaded by other agents, load 9-20; loop
rate is frames/s of the image loop; I/sig, R_pim, observations of the P41212 merge):
  known cell (ffbidx, -C):   base 785 / 750 -> 1130 / 1017 frames/s (warm 6000-frame ABAB 860 -> 1453)
    run A: obs 5.37M -> 5.30M, I/sig 13.09 -> 13.02, R_pim 6.48 -> 6.46%, CC1/2 .970 -> .973
    run B: obs 4.91M -> 4.89M, I/sig 12.00 -> 11.95, R_pim 6.50 -> 6.47%, CC1/2 .971 -> .970
  de novo (FFT):             base 89 / 91 -> 174 / 195 frames/s
    run A: obs 3.96M -> 3.91M, I/sig 10.18 -> 10.12, R_pim 6.94 -> 6.98%, CC1/2 .960 -> .967
    run B: obs 3.91M -> 3.89M, I/sig 10.53 -> 10.52, R_pim 7.01 -> 7.03%, CC1/2 .965 -> .961
  fixed min-pix 2, for scale: ffbidx 1496 / 1316 frames/s, obs 4.94M / 4.54M, I/sig 12.62 / 11.77.
The indexing rate drops (ffbidx 59.5 -> 56.9%, 55.2 -> 52.4%; FFT 60.5 -> 52.1%, 60.2 -> 47.9%):
the frames that go are the spurious B lattices; observations stay within 1.3%. Indexer calls per
frame 3.89 -> 1.67 (de novo, 2000 frames); the min-pix 1 search is skipped on 58% of frames.
De novo, 23 of 691 frames whose lattice was the crystal's now take a superlattice (13, same spots
indexed) or a cell fitted to 6-12 spots of a weak frame (10) from the min-pix 2 search.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:34:04 +02:00
leonarski_fandClaude Opus 5.5 ab645847ed FFT indexer GPU peak search ~45% faster per call, output unchanged
calculate_fft_result (the per-direction prominence peak search) was 61-65% of the GPU kernel time of
a de-novo stills run, and the card is what limits that loop. It is one dependent chain per direction,
latency-bound. Two exact changes, taken where the spectrum holds a whole background window (any
FFT max cell above ~30 A; the general loop stays for the rest):

- the sliding window written out as its three phases (clamped at the start, centred, clamped at the
  end): the same additions and subtractions in the same order, so the running double sum is bit for
  bit the same, but the loads no longer sit behind data-dependent while loops and are issued ahead;
- the window mean divides by a constant width, so the double division (a long FP64 iteration on a
  consumer card) is replaced by the reciprocal multiply plus one correction by the exact remainder,
  which returns the correctly rounded quotient (Markstein). Checked on the CPU against the division
  for 4e8 random dividends over all odd widths 3-401: 0 differences.

Measured (RTX 5080, box loaded by other builds, load 12-25): kernel minimum 1.50 -> 0.83 ms, mean
under the 8-stream pool 4.7 -> 2.8 ms; de-novo stills 2000 frames ABAB 113/120 -> 127/138 frames/s.
p.hkl byte-identical (2000 and 3000 frames, base and new min-pix schemes).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:32:03 +02:00
leonarski_fandClaude Opus 5.5 593590b357 rugnux -X FFT is the algorithm: cuFFT on a GPU, FFTW without one; -X cuFFT names the GPU implementation
In rugnux people choose the algorithm, not where it runs. -X fft (and the
old -F) now resolves at parse time to the GPU FFT indexer where a GPU is
visible and --no-gpu is not given, and to FFTW otherwise. -X cufft is the
GPU implementation alone, as -X fft was. -X cufft and -X ffbidx without a
GPU now stop at once with a clear message instead of failing later in the
indexer pool. Auto is unchanged and not redundant: on stills with a known
cell and a GPU it takes FFBIDX.

Only rugnux's CLI spelling changes. The internal IndexingAlgorithmEnum, the
broker and its OpenAPI enum keep FFT = the GPU implementation; the CLI
maps onto it, and the command line the viewer writes for a run (RugnuxCommandLine)
gives the enum's FFT back as -X cufft, which re-parses to the same thing.
A run without -X, and -X fft on a machine with a GPU, process exactly as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:13:23 +02:00
leonarski_fandClaude Opus 5.5 bfd715b64d rugnux --no-gpu: run on the CPU only, without touching CUDA, on a machine with a GPU
hide_gpus() in CUDAWrapper makes get_gpu_count() answer 0 without calling
cudaGetDeviceCount, so every GPU decision in the process (indexer Auto ->
FFTW, preprocessing/spot finding/decode, pre-scan, scaling engine, model
validation, MR) takes the path it takes on a machine without a GPU, and no
CUDA context is created. rugnux reads --no-gpu in main, ahead of the parser,
because set_gpu_blocking_sync() already asks CUDA for the devices. The
banner says 'CPU only (--no-gpu)'. The 'run it on the CPU' advice in the
out-of-GPU-memory messages and in the install docs now names --no-gpu
instead of CUDA_VISIBLE_DEVICES=.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:13:23 +02:00
leonarski_fandClaude Opus 5.5 34d5bb5631 stills merge 2.3-2.5x faster, output unchanged: parallel observation passes, hash accumulator, no amplitudes on scaling references
The stills scale/merge after the image loop was almost entirely serial:
- every scaling-reference merge (MergeAll: the per-image scaling reference and the two
  partiality post-refinement references) ran French-Wilson single-threaded on its export,
  about 3 s each, and assigned free-R flags - neither is read by a reference, which uses I
  only. MergeAll now exports intensities alone (MergeOnTheFly::ExportIntensities).
- the merge accumulated into a std::map; it is now an unordered_map exported in key order,
  which is the std::map order.
- AddImage, the error-model collection and the R_meas pass of MergeStats did the
  per-observation work (systematic-absence test, ASU key, filters, error-model sigma,
  outlier test, merged-I lookup) in one serial loop. They now reduce a block of images in
  parallel and consume it in image order (ReduceImagesInOrder), so every floating-point
  sum, half-set choice and hash-map insertion is made in the serial order.

Measured on a 25k-frame XFEL stills run (box loaded, load 9-14): merge P1 19.0 -> 7.1 s,
final-group merge 27.7 -> 13.2 s; on a second run 23.4 -> 7.7 s and 29.7 -> 13.6 s.
mtz/unmerged mtz data, hkl, cif and report identical on both runs, mx and --mode scale.
New test MergeOnTheFly_AddImagesIsTheSerialMerge checks the parallel merge against the
serial loop across a block boundary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:09:23 +02:00
jungfrauandClaude Opus 5.5 8430d69731 battery: 4nwv joins the ci tier (29 sets)
Build Packages / Create release (push) Successful in 7s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 3m28s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 10m29s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 15m47s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 19m46s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 20m11s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m37s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 24m6s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 24m57s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m51s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 28m10s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 20m32s
Build Packages / Generate python client (push) Successful in 33s
Build Packages / Build documentation (push) Successful in 1m6s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 22m44s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 24m8s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 25m30s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 20m48s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 25m14s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 22m39s
Build Packages / Unit tests (push) Successful in 1h28m18s
Build Packages / Validation Battery (push) Successful in 33m41s
The virus capsid (I222, ~400 A cell, marCCD) passed with the deposited group in every rc175 full
run: 71 s on the workstation, 159 s and again in 20261014-0141 on the server. It adds a second
large set to the regression check, about a minute of rugnux on the RTX 5080.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 03:44:57 -04:00
jungfrauandClaude Opus 5.5 e7d5964e75 battery: server run of the rc175 full battery at 6e9b891eb
Open 196/203 (97%; the 20261011 server run had 195/205), inhouse 47/47, no crash. Kept as
results/server/20261014-0141_6e9b89_1.0.0-rc.175 (report.md, results.json, manifest.json as
written); no private arm on this site.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 03:44:41 -04:00
leonarski_fandClaude Opus 5.5 70276a69fe Merge stillskhz exact commit into rc175: stills image loop 3.6-3.9x faster, output unchanged
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 00:36:05 +02:00
leonarski_fandClaude Opus 5.5 9849154081 stills image loop 3.6-3.9x faster, output unchanged: indexer pool, candidate post-processing, peak search, per-frame indexer memo
The stills per-image loop spent 99% of its time indexing (28.4 of 28.7 ms per frame on a 16 Mpx
serial set; read, decode, spot finding and integration together 0.33 ms), and only four threads
were ever busy:

- The 16 image workers each block on IndexerThreadPool, which had the default 4 threads, while the
  FFT candidate post-processing is CPU work. Stills now size the pool to min(-N, 8); 8 gets 94% of
  what 16 does (83 / 125 / 133 frames/s at 4 / 8 / 16 with the changes below) at half the card
  memory - each GPU FFT indexer holds ~0.5 GB, 16 of them took the run to 12.7 GB.
- FFTIndexer::ReduceResults (widened path, taken on ~2/3 of calls) deduplicated ~2900 candidate
  cells per call by a linear is_close scan: 46% of all CPU cycles. The accepted cells are now kept
  ordered by a and only the 2%-window of a is asked; the answer (any close cell) is the same.
- FilterFFTResults built a 16k-entry multimap per call (11% of cycles); now a partial sort of
  indices in the multimap's reverse order (magnitude descending, later direction first on ties).
- calculate_fft_result reads magnitudes from a bin-major transposed copy (in the histogram buffer,
  no new memory): neighbouring lanes no longer read a spectrum apart. Bit-identical results
  (checked against the old kernel on 4000+ calls, 0 mismatches); not faster on an idle card, but
  67 -> 98 frames/s with the pool's streams sharing it.
- The stills min-pix choice indexes a frame at 3/2/1 and then the winner again; the seed of
  strongest spots is often the same list each time (31% of indexer calls repeated an earlier call
  of the same frame). IndexAndRefine answers a repeated list from a per-frame FrameIndexerCalls
  memo kept by MXAnalysisWithoutFPGA for the duration of one frame.

Measured (RTX 5080, 32 threads, data in page cache), first 2000 frames, ABAB: 35/35 -> 127/127
frames/s, p.hkl md5 identical. Full 24960-frame run: loop 1081 s -> 277 s, wall 1132 s -> 328 s,
p.hkl md5 identical (P41212, 33234 unique); with a pool of 16 the loop is 222 s (the full run has
more spots per frame than its first 2000 frames, so the CPU side weighs more there). With a known cell (ffbidx path) 715 -> 920 frames/s on
2000 frames, p.hkl identical. Tests: jfjoch_test [Indexing] pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
2026-10-13 23:58:46 +02:00
leonarski_fandClaude Opus 5.5 6e9b891eb4 MR score-engine cache and merge buffers: no hang, no doubled wait; near-tie doc follows offline decide-late
Build Packages / Create release (push) Successful in 8s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 3m24s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 9m14s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 14m43s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 19m8s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 19m30s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m44s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 22m47s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 24m6s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m47s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 27m47s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 19m48s
Build Packages / Generate python client (push) Successful in 30s
Build Packages / Build documentation (push) Successful in 1m9s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 21m10s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 22m34s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 23m52s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 18m52s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 23m41s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 19m32s
Build Packages / Unit tests (push) Successful in 1h25m53s
Build Packages / Validation Battery (push) Successful in 31m9s
- MolecularReplacement: an engine whose making throws anything (std::bad_alloc, a gemmi error), not
  only a JFJochException, is refused and its reservation given back, so a thread waiting for the
  same engine is woken instead of waiting forever.
- RotationScaleMergeGPU::Alloc waits for the GPU work beside itself (GPU_BUSY_TIMEOUT), so its
  buffers are allocated plainly Synchronous; SynchronousAfterWorkBeside added a second 10-minute wait
  in front of it.
- CPU_DATA_ANALYSIS_INDEXING: an offline run decides its near ties on the whole sweep from the start;
  only a live run (broker URL or --simulate-live) decides early and re-checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:25:15 +02:00
leonarski_fandClaude Opus 5.5 ea597507df battery: workstation reference is the rc175 full battery at a45135926
Replaces the kept f853596 run with 20261013-1944_a45135_rc175-a451359-full (open + inhouse; the
private arm is never kept). Against f853596: no verdict changes beyond 5ky6 fail -> unscored (screw
undeterminable), sum 4145 -> 4088 s; the other moves are the last bits of device code without FMA
contraction (8xtf ISa 7.7 -> 7.9) and anomalous sigma.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:17:39 +02:00
leonarski_fandClaude Opus 5.5 7ff2a55d65 Merge branch 'zmq-close-30s' into rc175: receivers wait 30 s (was 10 s) for the pusher to finalise its sends at the end of a collection
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 19:57:33 +02:00
leonarski_fandClaude Opus 5.5 186c78f56a receiver: allow 30 s instead of 10 s for the image pusher to return its buffers after END
After the END message the receiver waits for every image buffer the pusher still holds (for
ZeroMQ: zero-copy messages freed once libzmq has transmitted them) before calling Finalize();
on expiry the collection fails with "Send commands not finalized". The limit was a 10 s literal
repeated in the FPGA and Lite receivers; it is now one constant, SendFinalizeTimeout, in the
JFJochReceiver base class.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
2026-10-13 19:57:15 +02:00
leonarski_fandClaude Opus 5.5 a45135926c Merge branch 'pass-levers' into rc175: rotation-scale walk early stop; a replaced canonical pass stops after its search arms; the measured-centre beam-centre arm tries a block-strided third of the pre-pass first
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 19:43:02 +02:00
leonarski_fandClaude Opus 5.5 94a51ea1ae rugnux: the beam-centre arm at the measured centre integrates a block-strided third of the pre-pass first
The measured-centre arm is a whole geometry pre-pass. It now integrates first a third of the pre-pass
frames, in 3k + 1 blocks of about 10 deg over the whole pre-pass arc with every third kept, and the
file's centre merges the same frames (its outcomes for the other frames are moved out while that
engine ingests). Where the measured centre loses on that subset on every line MeasuredCentreWins draws
by more than NEAR_TIE_SIGMA times the line's noise (Poisson for the strong counts, the Fisher standard
error for a CC1/2), it has lost and no full arm is run. Anything else, a near tie or a win, puts the
run back as the file's centre left it and runs the arm over all the pre-pass frames, deciding as before.
The run made again (decide_all_late_) runs the arm on all frames.

The same subset was tried for the metric-symmetry demoted arm and the walk refits and is not used
there: the held-out residual's standard error does not grow on a third of the frames, so a subset
margin overstates the evidence (one set's metric call flipped at 2.07 sigma), and a subset refit moved
one walk to a worse fixed point.

Against rc175 edde4439c, ci + 49 extra + 9 private sets: p.hkl and MTZ data identical on all 86. The
arm lost on the subset on 22 sets (no full arm), was a tie/win on 9 (full arm as before, +0.3-1.7 s).
Alternated runs: one set 62.1 -> 55.2 s, another 46.2 -> 42.4 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
2026-10-13 19:42:33 +02:00
leonarski_fandClaude Opus 5.5 7bef8d743c rugnux: a canonical pass the geometry walk replaces stops after its space-group search arms
The walk's indexing probes start as the canonical pass's post-refinement fit ends and run beside its
merge and space-group search. The pass asked whether the walk takes a round only before the search
and before its analyses; it now also asks after the search's two arms, before the space-group
decisions and the merge in the adopted group, without waiting for anything. A pass the walk is going
to replace stops there instead of finishing the search. (Waiting for the probes before the search was
measured and dropped: it cost 0.2-3.6 s on sets where the walk took no round.)

Exact by construction (only a pass that is thrown away is cut short); p.hkl and MTZ identical on the
ci tier and 49 extra sets. Fired on one geometry-walk set, alternated runs 33.8 -> 31.6 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EizBKhTqrAYago9KJG3dA3
2026-10-13 19:42:25 +02:00