Rotation merge: partial corr carries the recomputed partiality, not the predictor's
Every partial's corr is formed at integration as image_scale_corr = LP*QE*flight / partiality with the predictor's per-frame partiality. Ingest then recomputes every partiality from the frame-order-smoothed mosaicity (and the one exact-Bragg angle per rocking event), but corr_ingested kept the predictor's 1/p. Where partials are scaled every fitted frame's corr is rebuilt from the recomputed partiality, so that path only saw it in the first reference. Where they are not - fewer than 50 rocking events per frame: small molecules, and long-wavelength protein sweeps - corr is what the 3D combine divides each partial by, and the full became a weighted mean of I/p_pred while the event's recomputed fractions summed to 1.00: the combined fulls tracked 1/sum(p_pred) (r = -0.96) and scattered 13% rms about the plain sum of the same partials. corr_ingested is now rescaled by p_pred/p_recomputed once at ingest. Targeted battery against rc174-cand: every set on the partial-scaling path is bit-identical (5reo, 9qw8, lyso_x06da_ref, thau_x10sa_0p1deg, lyso_x06da_atten_wedge, myob_x06da_split, cytc_x06da_1, insu_I_x06da_5keV/6keV). Small molecules, SHELXL R1 against the COD model: aspirin 20 keV 0.0515 -> 0.0432 (ISa 10.3 -> 30.9), aspirin 25 keV 0.0451 -> 0.0396, HEPES 0.0452 -> 0.0420, citric acid 0.0747 -> 0.0726, dnba 0.0472 -> 0.0273, metformin 0.0405 -> 0.0326, nidppe 0.0498 -> 0.0426, lalanine 0.081 -> 0.057, cytidine 0.077 -> 0.071. Long-wavelength proteins on the fulls-alone path: lyso_x06da_5keV ISa 17.9 -> 27.5, thau_bl1a_3p8keV 21.7 -> 28.4, thau_bl1a_4p6keV 26.4 -> 33.5. One regression: lcystine_x10sa_25keV (a polycrystalline aggregate) now refuses 622 and reports P31 instead of P6122. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5K8jvPPbmCrbqnWkddTuB
This commit is contained in:
@@ -507,6 +507,9 @@ void RotationScaleMerge::Ingest() {
|
||||
logger.Info("RotationScaleMerge: ingested {} partial observations from {} frames ({} distinct hkl)",
|
||||
n_partials_obs, n_frames, rawrun_start.size());
|
||||
|
||||
// The partiality the predictor gave each partial, which its corr was formed with (image_scale_corr
|
||||
// divides by it); the recompute below replaces it.
|
||||
const std::vector<float> partiality_predicted(ingest_partiality.get(), ingest_partiality.get() + n_partials_obs);
|
||||
SmoothMosaicityAndPartiality();
|
||||
if (!resident_ingest) {
|
||||
ParallelChunks(static_cast<int>(partials.size()), ThreadsForWork(partials.size(), nthreads),
|
||||
@@ -529,6 +532,20 @@ void RotationScaleMerge::Ingest() {
|
||||
for (int i = lo; i < hi; ++i) corr_ingested[i] = partials[i].corr;
|
||||
});
|
||||
}
|
||||
// corr carries 1 / partiality, and it has to be the partiality everything downstream uses - the
|
||||
// recomputed one. Where the partials are scaled, every fitted frame's corr is rebuilt from it
|
||||
// anyway; where they are not (too few rocking events per frame, small molecules), corr is what
|
||||
// the combine divides each partial by, and with the predictor's per-frame partiality left in it
|
||||
// the parts of one rocking curve no longer add up to the reflection: measured on a 0.2 deg
|
||||
// small-molecule sweep, the combined fulls scattered 13% rms about the plain sum of the same
|
||||
// partials, and ISa was 10 where it is 31 with the recomputed partiality.
|
||||
ParallelChunks(n_partials_obs, ThreadsForWork(n_partials_obs, nthreads), [&](int lo, int hi) {
|
||||
for (int i = lo; i < hi; ++i) {
|
||||
const float p_old = partiality_predicted[i], p_new = ingest_partiality[i];
|
||||
if (p_old > 0.0f && p_new > 0.0f)
|
||||
corr_ingested[i] *= p_old / p_new;
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
// Feed the device (probed at the top of Ingest). On the resident path the layout and every
|
||||
|
||||
Reference in New Issue
Block a user