v1.0.0-rc.168 #78
Merged
leonarski_f
merged 18 commits from 2026-09-10 13:51:16 +02:00
rc168 into main
18
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0a349026bb |
rotation passes: a pass already known to be superseded does not write the report
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 8m42s
Build Packages / build:windows:nocuda (push) Successful in 17m6s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 18m1s
Build Packages / build:windows:cuda (push) Successful in 20m1s
Build Packages / build:viewer-tgz:cpu (push) Successful in 21m39s
Build Packages / build:viewer-tgz:cuda (push) Successful in 22m49s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 22m53s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 27m53s
Build Packages / build:rugnux:windows (push) Successful in 11m4s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 29m2s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 20m37s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 27m51s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 25m15s
Build Packages / build:rpm (rocky9) (push) Successful in 24m8s
Build Packages / build:rpm (rocky8) (push) Successful in 29m5s
Build Packages / Generate python client (push) Successful in 33s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 24m8s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 1m23s
Build Packages / DIALS test (push) Successful in 25m55s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 27m58s
Build Packages / XDS test (durin plugin) (push) Successful in 10m43s
Build Packages / XDS test (neggia plugin) (push) Successful in 9m44s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 10m24s
Build Packages / Unit tests (push) Successful in 1h25m57s
Build Packages / build:rugnux:aarch64 (cross) (pull_request) Successful in 8m33s
Build Packages / build:windows:nocuda (pull_request) Successful in 17m45s
Build Packages / build:rugnux-tgz (x86_64) (pull_request) Successful in 19m3s
Build Packages / build:windows:cuda (pull_request) Successful in 20m6s
Build Packages / build:viewer-tgz:cpu (pull_request) Successful in 20m43s
Build Packages / build:viewer-tgz:cuda (pull_request) Successful in 22m49s
Build Packages / build:rpm (rocky9_nocuda) (pull_request) Successful in 23m46s
Build Packages / build:rpm (ubuntu2204_nocuda) (pull_request) Successful in 27m57s
Build Packages / build:rugnux:windows (pull_request) Successful in 11m12s
Build Packages / build:rpm (ubuntu2404_nocuda) (pull_request) Successful in 20m30s
Build Packages / build:rpm (rocky8_nocuda) (pull_request) Successful in 29m1s
Build Packages / build:rpm (rocky9_sls9) (pull_request) Successful in 21m5s
Build Packages / build:rpm (rocky8_sls9) (pull_request) Successful in 26m21s
Build Packages / build:rpm (rocky9) (pull_request) Successful in 23m50s
Build Packages / build:rpm (rocky8) (pull_request) Successful in 29m45s
Build Packages / build:rpm (ubuntu2404) (pull_request) Successful in 23m38s
Build Packages / XDS test (durin plugin) (pull_request) Successful in 11m4s
Build Packages / Create release (pull_request) Skipped
Build Packages / Generate python client (pull_request) Successful in 43s
Build Packages / Build documentation (pull_request) Successful in 1m8s
Build Packages / DIALS test (pull_request) Successful in 26m36s
Build Packages / build:rpm (ubuntu2204) (pull_request) Successful in 27m42s
Build Packages / XDS test (JFJoch plugin) (pull_request) Successful in 10m34s
Build Packages / XDS test (neggia plugin) (pull_request) Successful in 8m52s
Build Packages / Unit tests (pull_request) Successful in 1h27m19s
A rotation run can integrate more than twice: a pass whose post-refined geometry loses the quality comparison, or whose integration radius has to be refixed, is followed by another that overwrites it. The superseded pass nonetheless computed a Wilson B, an anisotropy and twinning verdict, a radiation-damage and sweep-quality report, ran model validation and wrote the P1 cross-check file - all of it replaced minutes later. On a 100-dataset corpus that is 19 superseded passes over 18 datasets and about 100 s. A pass cannot know it is the last one, but it can know it is not: every trigger that forces another pass is settled before the report is built - background starvation at integration, a lattice that conflicts with a fixed space group at indexing, and the quality comparison at the pass's own first merge. The one exception compares cells rather than the pass's own numbers, so it is left out; it fires on none of the corpus. The quality comparison is hoisted into one function with two callers rather than being written twice. The measurement the error model and the correction surfaces are fitted on is not skipped: full_stats also gates the surface refinement, so declining it would change the merged intensities rather than only the report. Verified on nine datasets spanning seven space groups, two of them with a superseded pass and two with none: the merged and unmerged files, the P1 cross-check and the image statistics are byte for byte identical, and the report differs only in its date, prefix, command line and wall time. |
||
|
|
672840dfb8 |
beam centre: a walk that adopts nothing is a walk not worth taking
The beam-centre hypothesis ladder runs up to 48 full first-pass indexing attempts, a pixel at a time, and adopts the first rung that indexes half the validation frames. When it adopts nothing the centre is restored and the run is bit-identical to one that never searched, so on those datasets the whole walk is pure cost - measured at 347 s of the 429 s the loop spends over a 100-dataset corpus. Two exits, both measured against the six corpus datasets that enter the walk. The walk is skipped outright when the background already measures a centre that is significant at three sigma and further away than the ladder can reach: it cannot get there, so it only spends the time finding that out. And it stops after the second ring when the running maximum, the starting centre included, is below a third of the adoption bar, which separates cleanly on this corpus - the three walks that adopt nothing peak at 0, 2 and 4 frames, the two that succeed sit at 17 and 26 by that point. Every dataset that adopted a centre before still adopts the same one, with the same cell and the same reflection count; the two that fail still fail with the same message. The measured saving is 226 s of the 429. |
||
|
|
31422ddf54 |
release: version 1.0.0-rc.168
Build Packages / build:windows:nocuda (push) Successful in 16m20s
Build Packages / build:rugnux:windows (push) Failing after 8m51s
Build Packages / build:windows:cuda (push) Successful in 18m47s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 9m28s
Build Packages / build:viewer-tgz:cpu (push) Successful in 14m30s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 16m0s
Build Packages / build:viewer-tgz:cuda (push) Successful in 18m13s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 18m17s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 20m46s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 18m23s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 24m51s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 22m24s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 26m37s
Build Packages / build:rpm (rocky9) (push) Successful in 22m8s
Build Packages / build:rpm (rocky8) (push) Successful in 24m37s
Build Packages / XDS test (durin plugin) (push) Successful in 10m27s
Build Packages / Generate python client (push) Successful in 34s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 24m39s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 46s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 19m39s
Build Packages / XDS test (neggia plugin) (push) Successful in 10m21s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 11m7s
Build Packages / DIALS test (push) Successful in 17m28s
Build Packages / Unit tests (push) Successful in 1h26m25s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
bc816eeccb |
preprocessing: a buffer someone else page-locked is already what we wanted
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 9m18s
Build Packages / build:windows:nocuda (push) Successful in 16m55s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 19m16s
Build Packages / build:windows:cuda (push) Successful in 20m1s
Build Packages / build:viewer-tgz:cpu (push) Successful in 22m5s
Build Packages / build:viewer-tgz:cuda (push) Successful in 22m7s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 23m4s
Build Packages / build:rugnux:windows (push) Successful in 11m28s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 28m15s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 28m25s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 19m49s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 22m27s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 26m45s
Build Packages / build:rpm (rocky9) (push) Successful in 23m23s
Build Packages / build:rpm (rocky8) (push) Successful in 28m7s
Build Packages / Generate python client (push) Successful in 32s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 23m5s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 58s
Build Packages / XDS test (durin plugin) (push) Successful in 10m23s
Build Packages / DIALS test (push) Successful in 25m58s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 27m30s
Build Packages / XDS test (neggia plugin) (push) Successful in 10m3s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 10m53s
Build Packages / Unit tests (push) Successful in 2h7m2s
The uncompressed-image upload page-locks the caller's bytes, which is right when the caller allocated them for us and wrong when it did not: the online receiver registers its image buffer once and hands out ranges inside it, and registering a range a second time is an error rather than a no-op, so the first frame aborted the collection. An already-registered range needs nothing done to it - the upload is a DMA either way - and it is not ours to unregister when this object is destroyed, so it is deliberately not remembered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
73e2382be5 |
beam centre: the metric symmetry the two centres disagree about is decided on the merge
Build Packages / build:windows:nocuda (push) Successful in 17m27s
Build Packages / build:windows:cuda (push) Successful in 19m50s
Build Packages / build:viewer-tgz:cpu (push) Successful in 21m7s
Build Packages / build:viewer-tgz:cuda (push) Successful in 22m58s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 19m10s
Build Packages / build:rugnux:windows (push) Successful in 10m57s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 8m17s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 28m18s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 22m14s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 28m19s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 20m40s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 26m12s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 23m9s
Build Packages / build:rpm (rocky8) (push) Successful in 27m37s
Build Packages / build:rpm (rocky9) (push) Successful in 23m49s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 27m28s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 23m19s
Build Packages / DIALS test (push) Successful in 25m53s
Build Packages / XDS test (durin plugin) (push) Successful in 10m28s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 10m34s
Build Packages / XDS test (neggia plugin) (push) Successful in 9m23s
Build Packages / Generate python client (push) Successful in 39s
Build Packages / Build documentation (push) Successful in 1m3s
Build Packages / Create release (push) Skipped
Build Packages / Unit tests (push) Failing after 46m41s
The beam-centre check indexes a second first pass at the centre the scattered background measures. Where the two centres return the same cell but a different metric symmetry read off it, the run kept the file's and said the difference did not decide the cell. It does not decide the cell; it decides the CLASS, and the class is handed to GeometryRefiner, which holds the cell to it for the whole run - so a tetragonal promotion the centre manufactured averages two axes that differ, and the run refines, predicts, integrates and merges on an equality the crystal does not have. Measured on a small-molecule rotation set whose two in-plane axes differ by 2.35 %: 3.06 px of centre error, one edge 1.6 % wrong, R_meas 57 %, ISa 1.2, and the axial row that carries the third screw never measured. Whether the centre could have decided it is arithmetic, and needs no new constant. The promotion holds a pair of reduced-cell axes equal to LatticeSearch's dist_tolerance; a centre wrong by d px moves an axis of length a by d*p*a/(D*lambda) relative, and both axes of the pair carry their own error. Where their sum reaches the tolerance the higher class is a statement about where the beam was assumed to be, not about the crystal. Nothing before integration can settle which centre is right - both index the same frames, and the frame count is the one statistic this check already refuses to arbitrate on - and the disagreement is two-sided, the file's centre finding the higher class about as often as the measured one does over the corpus. So both lattices are carried forward: the first pass is run again at the other centre and the run adopts whichever arm MERGES better, on each arm's own search merge (P1, whole range, before any correction surface - the terms the two passes are already compared in). The alternative arm is a whole first pass, so it gets the short-axis pass and the geometry post-refinement of its own; on a crystal with an axis under the FFT floor the check runs before that pass and compares supercells, and the arbitration has to be carried past it. Moving off the geometry the file gave takes the same CC1/2 band the pass-2 quality guard uses. On the target set the rescue is outright: cell 5.787 5.940 12.263 against 5.838 5.838 12.251, 100 % indexed, R_meas 57.0 -> 2.3 %, CC1/2 0.865 -> 0.993, I/sigma 16.2 -> 84.1, P212121 adopted on its own axial rows rather than on a tie between two empty zones. Of the eight other corpus runs that reach this branch, one does not trigger and six keep the file's centre with bit-identical output; one adopts the measured centre and returns the same space group and cell to 0.07 % with R_meas 11.6 -> 11.5 % and ISa 25.1 -> 25.2. The branch costs one extra first pass on the runs that trigger. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
10ccc26db2 |
broker: the second use of the removed indexing capability flag goes too
Removing the flag left one caller behind, so the broker did not compile. The setter's gate was the same one the constructor had already dropped: with the CPU indexer always compiled in there is no configuration where indexing is unavailable, so a setting that asked for it never has to be refused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
03a534fb66 |
miniCBF: write the decoded pixels past the cache
Nothing on the host reads a decoded image back: it is DMAed to the GPU, or a CPU engine walks it once and drops it. An ordinary store pays for that twice - it reads every cache line before overwriting it, and it evicts 72 MB of live cache to make room - and the decode is bound by exactly that traffic, which is why neither an eight-pixel-at-a-time scalar loop nor an SSE2 prefix sum moved it at all. Storing the pixels non-temporally takes an 18 Mpx frame from 27 ms to 11 ms, and 240 frames from six sweeps decode byte for byte as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
10435ef588 |
miniCBF: read the sweep's rotation angles on several threads
Opening a sweep parses the header of every file to get its start angle, and one header is two dozen std::regex searches: measured at 0.33 ms on a 16 Mpx sweep and 1.0 ms on one with a longer header, so a 7200-frame sweep spent 7.2 s on one thread before it read an image. The files are independent, so the scan is shared out over up to eight of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ffbdaa71e4 |
preprocessing: page-lock the reader's bytes for an uncompressed image
PinInputBuffer() page-locks the buffer an image is decompressed into, which an uncompressed image never uses: it is read straight out of the reader's own buffer, so the upload came from pageable memory. Measured on this card, 72.6 MB crosses at 5.6 GB/s pageable and 14.6 GB/s registered - 12.9 ms against 5.0 ms, on every image of a sweep that stores its frames uncompressed. PinInputRegion() page-locks a region the caller owns and remembers it, so a worker that reads every frame into the same buffer registers it once. The registration is dropped with the engine, so the three workers that build one declare their raw image before it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
391736425f |
miniCBF: read the compressed file through a buffer the caller keeps
Slurp() returned a fresh std::vector per call, so every frame allocated the whole compressed file - 18 MB on a 16 Mpx detector - and value-initialised it before the read overwrote every byte. Measured on one such frame that is 9.2 ms against 5.4 ms into a buffer that is already there. ReadInto() now takes the scratch, and the raw image carries it, so a worker reading a sweep reads every file into the same bytes. Slurp() fills a buffer instead of returning one, which is also what makes the resize cost nothing when the next file is the same size as the last. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4bac6d3f21 |
reader: a worker reads every frame into the same raw image
GetRawImage() allocated a fresh JFJochReaderRawImage per frame, and on a miniCBF sweep its buffer holds the whole decoded image: 72.6 MB on a 4150 x 4371 detector. That is over glibc's 32 MB mmap ceiling, so the block is mmapped and munmapped every image and the decoder faults in 17 700 untouched pages as it writes them. Measured on one 18.1 Mpx frame, decoding into a fresh buffer takes 61 ms against 16 ms into one that has been written before - the allocation costs nearly three times what the decode does, and the per-image loop pays it twice per sweep. ReadRawImage() fills a raw image the caller owns, so a worker declares one outside its loop and keeps the pages. GetRawImage() stays as the allocating wrapper for the single-image callers. The two loops that hand the image to the process-file writer keep a shared_ptr and recycle it only while the writer thread is not still reading the previous frame's pixels. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
dcc6d5e4fc |
JFJochStateMachine: remove confusing indexing_possible setting
Build Packages / build:windows:nocuda (push) Failing after 9m45s
Build Packages / build:rugnux:windows (push) Successful in 17m27s
Build Packages / build:windows:cuda (push) Successful in 20m46s
Build Packages / Unit tests (push) Failing after 6m36s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 8m18s
Build Packages / build:rpm (rocky8_nocuda) (push) Failing after 12m18s
Build Packages / build:rpm (rocky9_nocuda) (push) Failing after 11m31s
Build Packages / build:viewer-tgz:cpu (push) Successful in 15m21s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 15m6s
Build Packages / build:viewer-tgz:cuda (push) Successful in 16m59s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Failing after 12m14s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Failing after 9m55s
Build Packages / build:rpm (rocky9_sls9) (push) Failing after 9m24s
Build Packages / build:rpm (rocky8_sls9) (push) Failing after 11m18s
Build Packages / build:rpm (ubuntu2404) (push) Failing after 10m46s
Build Packages / build:rpm (rocky9) (push) Failing after 12m2s
Build Packages / Generate python client (push) Successful in 43s
Build Packages / Build documentation (push) Successful in 1m9s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky8) (push) Failing after 14m47s
Build Packages / build:rpm (ubuntu2204) (push) Failing after 15m40s
Build Packages / XDS test (durin plugin) (push) Successful in 10m35s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 9m39s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m56s
Build Packages / DIALS test (push) Successful in 18m21s
|
||
|
|
2e5c0df0a5 |
scaling: the decay / relative-B passes read a compact array, and their per-group references run on all threads
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 8m58s
Build Packages / build:windows:nocuda (push) Successful in 17m17s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 18m48s
Build Packages / build:windows:cuda (push) Successful in 19m38s
Build Packages / build:viewer-tgz:cpu (push) Successful in 20m51s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 22m30s
Build Packages / build:viewer-tgz:cuda (push) Successful in 23m33s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 27m3s
Build Packages / build:rugnux:windows (push) Successful in 11m35s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 28m49s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 20m3s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 22m30s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 26m57s
Build Packages / build:rpm (rocky9) (push) Successful in 25m1s
Build Packages / build:rpm (rocky8) (push) Successful in 29m28s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 23m36s
Build Packages / Generate python client (push) Successful in 42s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 1m54s
Build Packages / XDS test (durin plugin) (push) Successful in 11m11s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 28m9s
Build Packages / DIALS test (push) Successful in 26m17s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 10m33s
Build Packages / XDS test (neggia plugin) (push) Successful in 8m45s
Build Packages / Unit tests (push) Successful in 1h50m12s
The three step-4b passes - the global decay slope, the per-batch relative-B and the radiation-damage monitor - were plain serial walks over the eighty-byte observation records, a dozen of them, each reading twenty bytes of each record and each allocating and zeroing a pair of per-group vectors. The decay fit alone runs seven of those passes (three slope fits, four held-out scores) on one thread while the workers and the GPU idle. Give them the treatment the correction surfaces below them already have: pull the fields they read out of the records once into a compact array in fulls order, and keep a second copy of the same terms in ASU-group order (stable counting sort) so a thread can own whole groups. The per-group inverse-variance references then run on all threads and still add each group's terms in fulls order, and the remaining scalar reductions keep their serial order over the compact array - so every sum is formed from the same terms in the same order as before, and the numbers are unchanged rather than merely close. The build is shared by the three curve fits and the three held-out scores of the relative-B pass instead of being repeated per call. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
58a9e24484 |
merge: the report-only error-model diagnostics are measured only where they are read
The strong-reflection ISa asymptote and the reported chi2 median are both report-only - the header already states that a merge run with full_stats false skips the diagnostics, because its numbers are never read - yet both ran on every merge, including the geometry pre-pass and the pre-correction CC1/2 merge that is discarded. The asymptote is the expensive one: a per-group scatter array value-initialised on one thread (hundreds of megabytes at a million ASU groups) plus a serial random-scatter walk over every full. Gate both on full_stats, and leave them out of the error-model line on the merges that no longer measure them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b43f72a676 |
merge: the half-set cell array is the one the flat fields already hold
The merge built a per-observation cell array with the same rule the flat merge fields had just been filled with - `usable_merge(o) ? o.group : -1` - but serially, over the eighty-byte observation records, so a large P1 pass dragged more than a gigabyte through the cache to rebuild an array that was already in hand. Hand the existing one to the half-set assignment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
7db76560a6 |
beam centre: the background fit stops at its fixed point, and reads the pixels on every thread
The background ring fit is the largest single phase of an offline run on a large detector: 626 s of a 5403 s hundred-dataset battery, against 127 s for the beam-stop projection it reads. The cost is not spread over its invocations - the worst single invocation per dataset accounts for 516 s of that 626 s - and the worst one is always the second pass, at the post-refined geometry, where the fit starts essentially at the answer. Instrumented per iteration, that invocation is not a truncated walk. It reaches its answer at iteration 4 and then enters a period-2 limit cycle: the step alternates in sign every iteration at a fixed 0.30 px, and the centre oscillates by 0.08 px about a value it knows to 1.75 px, for the 96 iterations left in the budget. CONVERGED_PXL sits below the cycle amplitude, so the run can never meet it; whether a dataset costs 5 s or 55 s is decided by whether its cycle happens to fall under 0.02 px. Travel looks nothing like that - a fit forced to walk 339 px keeps its step direction through all 22 of its travelling iterations and takes steps of 75 px down to 0.1 px - so the sign of the step against the one before it separates the two without any length to compare against. Two changes, measured apart. The walk now also stops when its step reverses the previous one twice running, which is a crossing of the fixed point rather than a step toward it. MAX_ITERATIONS stays at 100, so the long-travel budget is untouched: the forced 339 px walk arrives as before, in 27 iterations instead of 29, 0.013 px away. A monotone fit is bit-identical. The two cycling invocations measured go from 100 iterations to 7 and 9, moving 0.11 and 0.16 px - under 6% of the sigma each reports, and the hundredth iteration was itself an arbitrary point on the cycle. The per-pixel passes - a rotation, a sqrt, two atan2 and two divisions each, over 18 Mpx, three times an iteration - now run on the threads the caller was given, split over a fixed 64 row blocks folded in block order, so the grouping of the sums is a property of the image and not of the machine. Serial against six threads on the same projection in the same process: 4.2-5.3x, with beam_x, beam_y and sigma identical to four decimals on all five fits measured, and a test that asserts one thread and eight give the same answer. Together, measured on the same phase boundary the battery numbers use: 57.7 s to 2.4 s and 54.5 s to 3.0 s on the two worst datasets, 0.23 s to 0.10 s on a small one. The beam-stop mask is untouched - ShadowFinder is not modified and the fit runs after it and writes nothing back - and both masks come out at exactly the pixel counts the unmodified binary logged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e74517c2fb |
performance: the French-Wilson quadrature stops recomputing what its grid already fixes
The posterior is integrated on j = (i + 1/2) * dj, so sqrt(j) and log(j) each split into a per-reflection factor and a term that depends only on i: sqrt(j) = sqrt(dj) * sqrt(i + 1/2) and log(j) = log(dj) + log(i + 1/2). The i-dependent halves are the same for every reflection and every intensity, so they are tabulated once for the run rather than recomputed at each of the 400 grid points. The exponential in the second pass is left alone, but a point more than 37 below the peak carries a weight under 1e-16 and cannot move a normalised sum of doubles, so it is skipped instead of exponentiated - and the posterior is sharply peaked, so most of the grid is skipped. Finally the Wilson prior and the centric flag belong to the reflection rather than to one of its three intensities, so the two symmetry lookups are made once and shared instead of three times over. None of this changes the arithmetic: on five datasets spanning the corpus's speed range the space group, the unique-reflection count, R_meas and CC1/2 are identical to the digit. Measured over those five, each run twice and the two binaries interleaved so machine load cannot favour one arm: 771.2 s to 730.7 s, 5.3 % of total wall, between 3.2 % and 7.8 % per dataset and in the same direction on all five. The work removed is largest where the run merges most - the quadrature runs on every weak intensity of every merge, and a large cell at high resolution reaches a few million of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
11c15837c6 |
rugnux: acknowledge the field and the open-source stack
The crystallographic methods rugnux implements were developed and published by the X-ray research community, and the program is built on other people's open-source software. Both were credited only in files that never left the source tree: jfjoch_viewer shows LICENSE and ACKNOWLEDGEMENT.md from its own resources, but rugnux said nothing and the packages shipped no ACKNOWLEDGEMENT.md at all. One text, defined once, printed by the CLI after the licence banner and written at the foot of every <prefix>_report.txt, so a result carries the acknowledgement wherever it travels and names the files that hold the detail. ACKNOWLEDGEMENT.md joins LICENSE and THIRD_PARTY_NOTICES.md in JFJOCH_NOTICE_FILES, which is installed per component - so it reaches the Linux .tgz, the Windows .zip, the NSIS installer, the macOS image and the DEB/RPMs alike, next to the two files it links to. Also drops the SUMMARY block's explanatory paragraph. It was written to whoever maintains the report format, not to the crystallographer reading their results, and the greppability contract it described is already in docs/RUGNUX_REPORT.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018FeM2CtemFrbhoK1G9n7J5 |