diff --git a/docs/CPU_DATA_ANALYSIS_REFINEMENT.md b/docs/CPU_DATA_ANALYSIS_REFINEMENT.md index 0c9d74b4f..7e0bf420f 100644 --- a/docs/CPU_DATA_ANALYSIS_REFINEMENT.md +++ b/docs/CPU_DATA_ANALYSIS_REFINEMENT.md @@ -86,7 +86,8 @@ gradient gather, whatever the number of atoms. The derivative of a riding hydrog **Scale and solvent.** The flat bulk-solvent mask is drawn with the REFMAC radii (probe 1.0 Å, shrink 0.8 Å) on a grid of its own, with a spacing of $d_\mathrm{min}/3$ or 0.4 Å, whichever is finer (coarser only where -a cell would need more than $2^{26}$ points). On the atoms' density grid the shrink is lost at low +a cell would need more than $2^{26}$ points, and then never coarser than the density grid, which a cell that +large uses for its mask). On the atoms' density grid the shrink is lost at low resolution: at 3 Å its 1 Å step leaves no grid point within the shrink radius. $k_\mathrm{sol}$ and $B_\mathrm{sol}$ are searched over their physical range on the working reflections, and the overall scale and anisotropic B are fitted at each point of that search (§14.2) - at the points of its fine pass by diff --git a/docs/GPU_DATA_ANALYSIS.md b/docs/GPU_DATA_ANALYSIS.md index 24a57b695..70a474c8e 100644 --- a/docs/GPU_DATA_ANALYSIS.md +++ b/docs/GPU_DATA_ANALYSIS.md @@ -269,7 +269,7 @@ and both gradients come back into pinned buffers. The minimiser (L-BFGS) stays o Once per macro-cycle ([§15.3](CPU_DATA_ANALYSIS_REFINEMENT.md#153-scale-bulk-solvent-and-the-likelihood-parameters)): - **F_calc and F_mask** - the forward path above without the target, and the bulk-solvent mask drawn on the card - (`ModelMaskGPU.cu`) on **its own fine grid** (d_min/3 or 0.4 A, whichever is finer, at most 2^26 points), + (`ModelMaskGPU.cu`) on **its own fine grid** (d_min/3 or 0.4 A, whichever is finer, at most 2^26 points but never coarser than the density's grid), with a grid, spectrum and cuFFT plan of its own where that grid is not the density's: one block per (atom, symmetry image) sets the points within the atom's REFMAC radius, the islands are removed by a union-find labelling (integer `atomicCAS`, rooted at each component's lowest index, so the partition does not diff --git a/image_analysis/structure_refinement/RefinementXray.cpp b/image_analysis/structure_refinement/RefinementXray.cpp index faba0a11d..ea1f8deea 100644 --- a/image_analysis/structure_refinement/RefinementXray.cpp +++ b/image_analysis/structure_refinement/RefinementXray.cpp @@ -255,14 +255,24 @@ RefinementReflections PrepareRefinementReflections(const std::vector BulkSolventMask::MaskGrid(const gemmi::UnitCell &cell, const gemmi::SpaceGroup &sg, double d_min) { + // the cap would coarsen it beyond the atoms' grid: that grid, whose points the density holds anyway + if (std::cbrt(cell.volume / MASK_POINTS_MAX) > d_min / 3.0) { + gemmi::Grid grid = RigidBodyZoneGrid(cell, sg, d_min); + std::vector().swap(grid.data); + return grid; + } gemmi::Grid grid; grid.unit_cell = cell; grid.spacegroup = &sg; - const double spacing = std::max(std::min(d_min / 3.0, MASK_SPACING_MAX), std::cbrt(cell.volume / MASK_POINTS_MAX)); + const double spacing = MaskSpacing(cell, d_min); // set_size_from_spacing()'s size, without allocating the values const std::array n = gemmi::good_grid_size({{cell.a / spacing, cell.b / spacing, cell.c / spacing}}, gemmi::GridSizeRounding::Up, &sg); diff --git a/image_analysis/structure_refinement/RefinementXray.h b/image_analysis/structure_refinement/RefinementXray.h index d7062afbf..b1d88d2a8 100644 --- a/image_analysis/structure_refinement/RefinementXray.h +++ b/image_analysis/structure_refinement/RefinementXray.h @@ -60,8 +60,11 @@ struct XrayRowModel { // probe and shrink radii (1.0 and 0.8 A), which a grid at d_min / 3 cannot resolve at low resolution - at 3 A // its 1 A step leaves no grid point within the shrink radius and the shrink is skipped. The step is therefore // d_min / 3 or MASK_SPACING_MAX, whichever is finer - where the mask grid stays within MASK_POINTS_MAX points; a -// cell too large for that gets the spacing that fills it. The R-factors of low-resolution models settle at a -// step of 0.4 A (within 0.001 of 0.3 A) and move by up to 0.005 between 0.4 and 0.8 A. +// cell too large for that gets the spacing that fills it, but never one coarser than the atoms' density grid +// (RigidBodyZoneGrid, d_min / 3), which it then is: the density's points are on the card anyway, and the mask's +// own grid only ever saved memory. The R-factors of low-resolution models settle at a step of 0.4 A (within 0.001 +// of 0.3 A) and move by up to 0.005 between 0.4 and 0.8 A; a cell coarsened by the cap past the shrink radius has +// no shrink at all, its macromolecule swollen by the whole probe, which costs far more. // The flat solvent model of Fokine & Urzhumtsev (2002) Acta Cryst. D58, 1387-1392 // The probe-and-shrink mask of Jiang & Brunger (1994) J. Mol. Biol. 243, 100-115 class BulkSolventMask { @@ -71,6 +74,8 @@ public: BulkSolventMask(const gemmi::UnitCell &cell, const gemmi::SpaceGroup &sg, double d_min, size_t nthreads); // The mask's grid (sizes and spacing set, values not allocated) - also the GPU engine's (RefinementGPU.cpp). static gemmi::Grid MaskGrid(const gemmi::UnitCell &cell, const gemmi::SpaceGroup &sg, double d_min); + // The step that grid is asked for. + static double MaskSpacing(const gemmi::UnitCell &cell, double d_min); // Fmask (unscaled) of `model` at `hkl`. void Fmask(const gemmi::Model &model, const std::vector &hkl, std::vector> &f) const;