rugnux: sixteen-bit Miller indices in the ingest sort key and the post-refine partial
Both arrays are one record per integrated observation - tens of millions on a fine-sliced long axis, gigabytes each - and both carry the raw hkl only to sort and group on. A Miller index needs sixteen bits (|h| <= a / d_min, in the hundreds even on the longest axis at atomic resolution), which takes the ingest sort key from 24 to 20 bytes and the post-refine partial from 32 to 28. Same comparisons, same order; merged output byte-identical. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nW6FNRP1bBJJ8pfHiByAT
This commit is contained in:
@@ -219,7 +219,9 @@ PostRefineObservations GatherPostRefineObservations(std::vector<IntegrationOutco
|
||||
if (!std::isfinite(r.I) || !std::isfinite(r.sigma) || r.sigma <= 0.0f) continue;
|
||||
const float ox = std::isfinite(r.observed_x) ? r.observed_x : NAN;
|
||||
const float oy = std::isfinite(r.observed_y) ? r.observed_y : NAN;
|
||||
pts[fill[r.h - h_lo]++] = Partial{r.h, r.k, r.l, r.image_number, r.I, r.sigma, ox, oy};
|
||||
pts[fill[r.h - h_lo]++] = Partial{static_cast<int16_t>(r.h), static_cast<int16_t>(r.k),
|
||||
static_cast<int16_t>(r.l), r.image_number,
|
||||
r.I, r.sigma, ox, oy};
|
||||
}
|
||||
// The caller said it will never read this image's reflections again, so hand the
|
||||
// vector back the moment it is consumed: the payload shrinks image by image as it
|
||||
|
||||
@@ -94,7 +94,10 @@ struct PostRefineSettings {
|
||||
// per-bucket sort move all of it. The goniometer angle is not stored - it is a function of the
|
||||
// image number alone, and is rebuilt from it where it is needed.
|
||||
struct PostRefinePartial {
|
||||
int h, k, l;
|
||||
// Sixteen bits are plenty for a Miller index - |h| <= a / d_min, in the hundreds even on the
|
||||
// longest axis at atomic resolution - and here four bytes of every partial are four bytes of an
|
||||
// array that is gigabytes on a large cell.
|
||||
int16_t h, k, l;
|
||||
float img;
|
||||
float I, sigma;
|
||||
float obs_x, obs_y; // observed spot centroid (pixels); NAN if the box sum found no centroid
|
||||
|
||||
@@ -432,7 +432,8 @@ void RotationScaleMerge::Ingest() {
|
||||
for (int o = lo; o < hi; ++o) {
|
||||
int32_t at = src_start[o];
|
||||
for (const auto &r : partials_out[o].reflections) {
|
||||
keys[fill[r.h - h_min]++] = SortKey{r.h, r.k, r.l, r.image_number, at, r.d};
|
||||
keys[fill[r.h - h_min]++] = SortKey{static_cast<int16_t>(r.h), static_cast<int16_t>(r.k),
|
||||
static_cast<int16_t>(r.l), r.image_number, at, r.d};
|
||||
++at;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,10 +142,12 @@ private:
|
||||
|
||||
// The narrow per-observation record the ingest sort orders: the raw hkl the runs are cut on, the
|
||||
// frame position that breaks a tie inside one, the observation's own index (which makes the order
|
||||
// total - see the .cpp), and the resolution the range test reads. Twenty-four bytes against the
|
||||
// Obs's eighty, and it is all the ingest needs before it knows which observations survive.
|
||||
// total - see the .cpp), and the resolution the range test reads. Twenty bytes against the Obs's
|
||||
// eighty, and it is all the ingest needs before it knows which observations survive. Sixteen bits
|
||||
// are plenty for a Miller index - |h| <= a / d_min, in the hundreds even on the longest axis at
|
||||
// atomic resolution - and on a long axis this array sets the sort's memory high-water mark.
|
||||
struct SortKey {
|
||||
int32_t h, k, l;
|
||||
int16_t h, k, l;
|
||||
float image_number;
|
||||
int32_t idx;
|
||||
float d;
|
||||
|
||||
Reference in New Issue
Block a user