rugnux: per-batch relative-B correction + radiation-damage B monitor
Rotation scaling modelled dose-dependent scattering power with a single global relative-B decay slope only (RefineDecay); per-frame refinement was scale-only. Add a per-batch relative Debye-Waller B, both as an opt-in correction and as an always-on measurement. - FitRelativeBCurve / SolveCurvatureSmoothedB: per rotation-range batch, fit the s^2 slope of ln(Iref/Iobs) that the resolution-flat per-frame G leaves, regularised by a second-difference (curvature) penalty so a genuine smooth relative-B is kept while resolution-correlated noise (which an unconstrained per-batch fit would chase into an oscillating curve) is suppressed. - Correction (--relative-b[=deg], default off): applied after RefineDecay, gated by a physical peak-to-peak floor and cross-validated by ASU-group parity (a per-batch parameter owns whole frames, so it is scored on held-out equivalents, not held-out frames). On a battery re-scaling identical stored reflections it is a no-op on well-behaved data and a small gain on radiation-damaged rotation data; never a regression. - Radiation-damage monitor (always on for rotation, report-only): measure the relative-B change from the pristine low-dose start of the run to its end (a whole-run-mean reference inverts on damaged data) plus the per-batch curve, and report them on the command line (in the radiation-damage report and the merge statistics) and in the mmCIF (_reflns.jfjoch_radiation_damage_ relative_B and a per-batch loop). A large magnitude flags radiation damage, complementing the existing per-image CC / mosaicity read. - --scale now reproduces the de-novo rotation merge (it previously omitted scale-fulls and smooth-G, silently using a weaker model), and honours --relative-b, so an offline re-scale matches the full pipeline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -170,8 +170,28 @@ void WriteMmcifReflections(const std::vector<MergedReflection> &reflections,
|
||||
if (twinning.moment_reflections > 0)
|
||||
out << "_reflns.jfjoch_second_moment_I " << Fmt(twinning.second_moment, 3)
|
||||
<< " # <I^2>/<I>^2 (untwinned 2.00, perfect twin 1.50)\n";
|
||||
// Radiation-damage monitor (rotation): the relative Debye-Waller B change from the first to the last
|
||||
// frame (A^2). A large magnitude flags a dose-dependent resolution-scale change = radiation damage;
|
||||
// positive is the typical direction (high-resolution intensity fades with dose). No standard mmCIF item.
|
||||
if (std::isfinite(statistics.radiation_damage_delta_b))
|
||||
out << "_reflns.jfjoch_radiation_damage_relative_B " << Fmt(statistics.radiation_damage_delta_b, 2)
|
||||
<< " # relative-B first->last over the run (A^2); + = high-res fades with dose\n";
|
||||
out << "#\n";
|
||||
|
||||
// Per-batch relative-B curve (the radiation-damage monitor, rotation): one relative Debye-Waller B
|
||||
// per rotation-range batch, measured before any correction. rotation_start_deg = id * batch_deg.
|
||||
if (!statistics.radiation_damage_b_batch.empty()) {
|
||||
out << "loop_\n";
|
||||
out << "_jfjoch_radiation_damage_batch.id\n";
|
||||
out << "_jfjoch_radiation_damage_batch.rotation_start_deg\n";
|
||||
out << "_jfjoch_radiation_damage_batch.relative_B\n";
|
||||
for (size_t i = 0; i < statistics.radiation_damage_b_batch.size(); ++i)
|
||||
out << " " << (i + 1) << " "
|
||||
<< Fmt(static_cast<double>(i) * statistics.radiation_damage_batch_deg, 1) << " "
|
||||
<< Fmt(statistics.radiation_damage_b_batch[i], 2) << "\n";
|
||||
out << "#\n";
|
||||
}
|
||||
|
||||
out << "loop_\n";
|
||||
out << "_reflns_shell.d_res_high\n";
|
||||
out << "_reflns_shell.d_res_low\n";
|
||||
|
||||
Reference in New Issue
Block a user