rugnux --af-model: anomalous veto on the enantiomorph, search on the GPU, cheaper rescoring
- An MR ENANTIOMORPH decision is taken into the answer's label only as --model takes a model's enantiomorph: ValidateAgainstModel runs on the placed model (in a scratch directory, removed after) and its adopted_model_enantiomorph - fit over its null, anomalous map at the atoms not inverted - decides; otherwise ENANTIOMORPH_VETOED. The hand-check numbers go to the MODEL PRIOR section. - Rotation and translation functions on the GPU (MolecularReplacementGPU.cu): one block per orientation for the rotation function; per orientation the copies, the bin weights, a gather of the Crowther-Blow coefficients (CSR lists, fixed order, no atomics) and a cuFFT C2R, block-reduced statistics finished on the host in a fixed order. The CPU path (FFTW) is unchanged in what it computes; the orientation-independent part of the translation function (TranslationSetup) is now built once per group on either path. - Rescoring: a placement clashing with its mates over twice the limit is not refined, one rigid-body engine per group, and the null is built only for the candidate the choice falls on. Measured MR step on the RTX 5080 (before -> after, seconds; hand check extra where it runs): 3r6o 1.77 -> 0.67, neg 2.14 -> 0.37, 11if 1.20 -> 0.51 (+0.6 hand check), 7ris 1.46 -> 0.56 (+0.6), 9zmu 4.71 -> 1.07 (+0.9). Rotation 0.50 -> 0.02 s and translation 1.17 -> 0.13 s on 9zmu. Mirrored-detector runs of two anomalous lysozyme sets: the hand check vetoes (map inverted at the model's atoms); the unmirrored runs adopt. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,10 @@
|
||||
|
||||
#include "../image_analysis/structure_refinement/ModelFFT.h"
|
||||
#include "../image_analysis/structure_refinement/MolecularReplacement.h"
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
#include "../common/CUDAWrapper.h"
|
||||
#include "../image_analysis/structure_refinement/MolecularReplacementGPU.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
// A synthetic body: carbon atoms scattered in a 12 A sphere, centroid at the origin.
|
||||
@@ -156,7 +160,7 @@ TEST_CASE("MolecularReplacement_SyntheticPlacement", "[MolecularReplacement]") {
|
||||
|
||||
// At the true orientation the translation function peaks at the true position, up to the
|
||||
// origins P 21 21 21 allows (a half in each coordinate), and stands far out of its own noise.
|
||||
const TranslationPeak peak = TranslationFunction(ft, refl, cell, sg, truth);
|
||||
const TranslationPeak peak = TranslationFunction(ft, MakeTranslationSetup(refl, cell, sg), truth);
|
||||
CHECK(peak.z > 10.0);
|
||||
for (double d : {peak.t.x - t.x, peak.t.y - t.y, peak.t.z - t.z}) {
|
||||
const double half = 2 * d - std::round(2 * d);
|
||||
@@ -164,3 +168,39 @@ TEST_CASE("MolecularReplacement_SyntheticPlacement", "[MolecularReplacement]") {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
TEST_CASE("MolecularReplacement_GPUMatchesCPU", "[MolecularReplacement]") {
|
||||
if (get_gpu_count() == 0)
|
||||
SKIP("no GPU");
|
||||
const SearchModel model = synthetic_model();
|
||||
const gemmi::UnitCell cell(42, 50, 58, 90, 90, 90);
|
||||
const gemmi::SpaceGroup &sg = gemmi::get_spacegroup_by_name("P 21 21 21");
|
||||
const gemmi::Mat33 truth = axis_angle({1, 2, 3}, 50);
|
||||
const gemmi::Fractional t(0.17, 0.31, 0.09);
|
||||
const auto merged = observed(PlaceSearchModel(model, truth, t, cell, sg));
|
||||
const SearchReflections refl = MakeSearchReflections(merged, cell, sg, 4.0, 12.0);
|
||||
const MolecularTransform ft(model, 4.0, 4);
|
||||
MolecularReplacementGPU gpu(ft);
|
||||
|
||||
const auto rotations = RotationAsu(UniformRotations(15.0), CartesianPointGroup(sg, cell));
|
||||
const auto rf_cpu = RotationFunction(ft, refl, cell, sg, rotations, 4);
|
||||
const auto rf_gpu = gpu.RotationFunction(refl, cell, sg, rotations);
|
||||
REQUIRE(rf_gpu.size() == rf_cpu.size());
|
||||
for (size_t i = 0; i < rf_cpu.size(); i++)
|
||||
CHECK(std::fabs(rf_gpu[i] - rf_cpu[i]) < 1e-4);
|
||||
|
||||
const TranslationSetup setup = MakeTranslationSetup(refl, cell, sg);
|
||||
std::vector<gemmi::Mat33> orientations{truth, rotations[3], rotations[17]};
|
||||
const auto tf_gpu = gpu.TranslationFunction(setup, orientations);
|
||||
for (size_t i = 0; i < orientations.size(); i++) {
|
||||
const TranslationPeak cpu = TranslationFunction(ft, setup, orientations[i]);
|
||||
CHECK(std::fabs(tf_gpu[i].z - cpu.z) < 1e-3);
|
||||
if (i == 0) { // the solution's peak is unique; noise peaks can tie between grid points
|
||||
CHECK(tf_gpu[i].t.x == Catch::Approx(cpu.t.x));
|
||||
CHECK(tf_gpu[i].t.y == Catch::Approx(cpu.t.y));
|
||||
CHECK(tf_gpu[i].t.z == Catch::Approx(cpu.t.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user