Refinement: the final maps' missing reflections beside the map transforms

MakeMaps filled FWT at the reflections the data do not have only after the three map
transforms, and MissingReflections computed the atoms' Fcalc (CPU, anisotropic atoms
included) and the solvent mask's Fmask one after the other. Neither reads the other:
MissingReflections now runs beside the difference and 2mFo-DFc transforms and the
anomalous map, and inside it Fcalc runs beside Fmask. Both are serial-heavy (a
single-threaded FFTW transform each, the mask's flood fill), so the tail shortens to
about the mask alone.

Measured on one RTX 5080, -N 28, two runs each (Step 11 wall, s; with the twinned arm
beside the untwinned one):
  thaumatin (anisotropic U chosen):  3.24 / 3.23 -> 3.04 / 3.08
  lysozyme:                          2.38 / 2.38 -> 2.25 / 2.28
  cytochrome C (two arms):           4.53 / 4.58 -> 4.49 / 4.57
Every output (model, maps, MTZ, report, refinement log lines) is identical to the
rc184-port binary on all three sets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-11-08 21:49:24 +01:00
1 parent 40e2462bde
commit 8e2d656b48
1 file changed
+9 -3
@@ -1201,9 +1201,11 @@ std::vector<MissingReflection> MissingReflections(Context &ctx, const gemmi::SMa
std::unique_ptr<RefinementEngine> engine = MakeRefinementEngineCPU(ctx.cell, *ctx.sg, ctx.d_min, missing, ctx.nthreads);
engine->SetBlur(RefinementBlur(ctx.cell, *ctx.sg, ctx.d_min, ctx.b_floor));
engine->SetAniso(&ctx.aniso);
// the atoms and the solvent side by side: neither reads the other
std::vector<std::complex<float>> fcalc, fmask;
engine->Fcalc(ctx.Model(), fcalc);
std::future<void> atoms = std::async(std::launch::async, [&] { engine->Fcalc(ctx.Model(), fcalc); });
ctx.mask->Fmask(ctx.Model(), missing.hkl, fmask);
atoms.get();
std::vector<std::pair<double, double>> d_by_s; // (1/d^2, D) of the observed reflections, by resolution
for (size_t m = 0; m < ctx.refl.Size(); m++)
@@ -1298,7 +1300,11 @@ void MakeMaps(Context &ctx, const std::vector<MergedReflection> &merged, double
fwt_data.v.push_back({ctx.refl.hkl[m], std::polar(static_cast<float>(fwt), static_cast<float>(phi))});
}
result.maps = maps;
// The difference map and the 2mFo-DFc map beside the anomalous one below: none reads another.
// The reflections the data do not have, the difference map and the 2mFo-DFc map beside the anomalous one below:
// none reads another.
std::future<std::vector<MissingReflection>> missing = std::async(std::launch::async, [&ctx, &b_aniso] {
return MissingReflections(ctx, b_aniso);
});
const std::array<int, 3> size = gemmi::get_size_for_hkl(diff, {{0, 0, 0}}, 3.0);
std::future<void> difference_map = std::async(std::launch::async, [&diff, &size, &result] {
result.difference_map = std::make_shared<gemmi::Grid<float>>(MapFromFPhi(gemmi::get_f_phi_on_grid<float>(diff, size, true)));
@@ -1354,7 +1360,7 @@ void MakeMaps(Context &ctx, const std::vector<MergedReflection> &merged, double
}
// The filled reflections after the observed ones: FWT/PHWT and the model's FC/PHIC, every other column missing.
const float nan = std::numeric_limits<float>::quiet_NaN();
for (const MissingReflection &r : MissingReflections(ctx, b_aniso)) {
for (const MissingReflection &r : missing.get()) {
const double phi_deg = std::arg(r.f_model) * 180.0 / PI;
const double k_aniso = std::exp(-0.25 * b_aniso.r_u_r(r.hkl));
const float row[] = {static_cast<float>(r.hkl[0]), static_cast<float>(r.hkl[1]), static_cast<float>(r.hkl[2]), nan,