Refinement: an exception in a trial slot or in RefineModel no longer ends the run
A slot builder thread that throws (gemmi's topology or hydrogen placement, bad_alloc) would call std::terminate - rugnux or the broker gone. The slot is now left out as one the card cannot take is, with a warning, and MakeOwnSlot's GPU engine catches std::exception, not only JFJochException. Both callers of RefineModel (after the merge, and --mode refine) catch std::exception: the refinement is reported NOT_PERFORMED with "refinement failed: <what>" and the run writes everything else (cell e.s.d.s, unmerged MTZ, report), as the refinement is processing-inert. Test: --mode refine on a cell too large for the bulk-solvent grid comes back with REFINE= NOT_PERFORMED and the reason, not an exception. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu (cherry picked from commit dc4e99fe4b3da2a9a3fa4290b5f70bfcc8c7bdb8)
This commit is contained in:
1 parent
0143d57eb0
commit
3d382ce0a0
4 files changed
+98
-59
No files matched your search
+76
-54
@@ -45,6 +45,62 @@ gemmi::Mtz MakeMtz(const std::vector<std::pair<std::string, char>> &columns,
|
||||
|
||||
bool InTestSet(int h, int k, int l) { return (h * 7 + k * 13 + l * 17) % 10 == 0; }
|
||||
|
||||
// Waters scattered over the test cell, and their own |F|^2 as the data: the model as given scores well.
|
||||
void SyntheticRefineInput(gemmi::Structure &st, RugnuxReflections &data) {
|
||||
const gemmi::SpaceGroup *sg = gemmi::find_spacegroup_by_name("P 21 21 21");
|
||||
st.cell = test_cell;
|
||||
st.spacegroup_hm = sg->xhm();
|
||||
st.setup_cell_images();
|
||||
gemmi::Model model(1);
|
||||
gemmi::Chain chain("A");
|
||||
std::mt19937 rng(7);
|
||||
std::uniform_real_distribution<double> frac(0.0, 1.0);
|
||||
for (int i = 0; i < 120; i++) {
|
||||
gemmi::Residue res;
|
||||
res.name = "HOH";
|
||||
res.seqid = gemmi::SeqId(i + 1, ' ');
|
||||
res.het_flag = 'H';
|
||||
gemmi::Atom a;
|
||||
a.name = "O";
|
||||
a.element = gemmi::El::O;
|
||||
a.pos = test_cell.orthogonalize(gemmi::Fractional(frac(rng), frac(rng), frac(rng)));
|
||||
a.b_iso = 15.0f;
|
||||
a.occ = 1.0f;
|
||||
res.atoms.push_back(a);
|
||||
chain.residues.push_back(res);
|
||||
}
|
||||
model.chains.push_back(chain);
|
||||
st.models.push_back(model);
|
||||
|
||||
gemmi::DensityCalculator<gemmi::IT92<float>, float> dc;
|
||||
dc.d_min = 2.0;
|
||||
dc.grid.unit_cell = test_cell;
|
||||
dc.grid.spacegroup = sg;
|
||||
dc.set_refmac_compatible_blur(st.models[0]);
|
||||
dc.put_model_density_on_grid(st.models[0]);
|
||||
const gemmi::AsuData<std::complex<float>> f =
|
||||
gemmi::transform_map_to_f_phi(dc.grid, true).prepare_asu_data(2.0, dc.blur);
|
||||
|
||||
data.name = "synthetic.mtz";
|
||||
data.source = "MTZ";
|
||||
data.cell = test_cell;
|
||||
data.space_group = sg->xhm();
|
||||
data.amplitudes = false;
|
||||
for (const auto &hv : f.v) {
|
||||
if (hv.hkl == gemmi::Miller{{0, 0, 0}})
|
||||
continue;
|
||||
const double fc = std::abs(hv.value);
|
||||
MergedReflection m;
|
||||
m.h = hv.hkl[0];
|
||||
m.k = hv.hkl[1];
|
||||
m.l = hv.hkl[2];
|
||||
m.d = static_cast<float>(test_cell.calculate_d(hv.hkl));
|
||||
m.I = static_cast<float>(fc * fc);
|
||||
m.sigma = static_cast<float>(0.02 * fc * fc + 1.0);
|
||||
data.reflections.push_back(m);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("RugnuxReflectionsFromMtz takes observed amplitudes over map coefficients", "[refine_only]") {
|
||||
@@ -124,62 +180,9 @@ TEST_CASE("ParseRugnuxReflections reads an MTZ from its bytes", "[refine_only]")
|
||||
|
||||
TEST_CASE("RunRefineOnly scores a model against intensities, with amplitudes and free flags made for it",
|
||||
"[refine_only]") {
|
||||
// Atoms scattered over the cell; the data are their own |F|^2, so the model as given scores well.
|
||||
const gemmi::SpaceGroup *sg = gemmi::find_spacegroup_by_name("P 21 21 21");
|
||||
gemmi::Structure st;
|
||||
st.cell = test_cell;
|
||||
st.spacegroup_hm = sg->xhm();
|
||||
st.setup_cell_images();
|
||||
gemmi::Model model(1);
|
||||
gemmi::Chain chain("A");
|
||||
std::mt19937 rng(7);
|
||||
std::uniform_real_distribution<double> frac(0.0, 1.0);
|
||||
for (int i = 0; i < 120; i++) {
|
||||
gemmi::Residue res;
|
||||
res.name = "HOH";
|
||||
res.seqid = gemmi::SeqId(i + 1, ' ');
|
||||
res.het_flag = 'H';
|
||||
gemmi::Atom a;
|
||||
a.name = "O";
|
||||
a.element = gemmi::El::O;
|
||||
a.pos = test_cell.orthogonalize(gemmi::Fractional(frac(rng), frac(rng), frac(rng)));
|
||||
a.b_iso = 15.0f;
|
||||
a.occ = 1.0f;
|
||||
res.atoms.push_back(a);
|
||||
chain.residues.push_back(res);
|
||||
}
|
||||
model.chains.push_back(chain);
|
||||
st.models.push_back(model);
|
||||
|
||||
gemmi::DensityCalculator<gemmi::IT92<float>, float> dc;
|
||||
dc.d_min = 2.0;
|
||||
dc.grid.unit_cell = test_cell;
|
||||
dc.grid.spacegroup = sg;
|
||||
dc.set_refmac_compatible_blur(st.models[0]);
|
||||
dc.put_model_density_on_grid(st.models[0]);
|
||||
const gemmi::AsuData<std::complex<float>> f =
|
||||
gemmi::transform_map_to_f_phi(dc.grid, true).prepare_asu_data(2.0, dc.blur);
|
||||
|
||||
RugnuxReflections data;
|
||||
data.name = "synthetic.mtz";
|
||||
data.source = "MTZ";
|
||||
data.cell = test_cell;
|
||||
data.space_group = sg->xhm();
|
||||
data.amplitudes = false;
|
||||
for (const auto &hv : f.v) {
|
||||
if (hv.hkl == gemmi::Miller{{0, 0, 0}})
|
||||
continue;
|
||||
const double fc = std::abs(hv.value);
|
||||
MergedReflection m;
|
||||
m.h = hv.hkl[0];
|
||||
m.k = hv.hkl[1];
|
||||
m.l = hv.hkl[2];
|
||||
m.d = static_cast<float>(test_cell.calculate_d(hv.hkl));
|
||||
m.I = static_cast<float>(fc * fc);
|
||||
m.sigma = static_cast<float>(0.02 * fc * fc + 1.0);
|
||||
data.reflections.push_back(m);
|
||||
}
|
||||
|
||||
SyntheticRefineInput(st, data);
|
||||
RefinementSettings settings;
|
||||
settings.score_only = true;
|
||||
settings.gpu = false;
|
||||
@@ -205,3 +208,22 @@ TEST_CASE("RunRefineOnly scores a model against intensities, with amplitudes and
|
||||
CHECK(text.find("REFINE_FREE_FLAGS= GENERATED") != std::string::npos);
|
||||
CHECK(text.find("REFINE_FRENCH_WILSON= TRUE") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE("RunRefineOnly reports a refinement that throws as not performed, with the reason", "[refine_only]") {
|
||||
// The data's cell, a hundred metres on a side, makes the bulk-solvent mask's grid too large to allocate.
|
||||
gemmi::Structure st;
|
||||
RugnuxReflections data;
|
||||
SyntheticRefineInput(st, data);
|
||||
data.cell = gemmi::UnitCell(1e6, 1e6, 1e6, 90, 90, 90);
|
||||
RefinementSettings settings;
|
||||
settings.gpu = false;
|
||||
settings.nthreads = 2;
|
||||
Logger logger("RefineOnlyTest");
|
||||
RefineOnlyResult r;
|
||||
REQUIRE_NOTHROW(r = RunRefineOnly(data, st, 0.0, settings, logger));
|
||||
CHECK_FALSE(r.refinement.ok);
|
||||
CHECK(r.refinement.failure_reason.find("refinement failed") == 0);
|
||||
const std::string text = RenderReportText(BuildRefineReportDocument("out", "synthetic.mtz", "model.cif", r), true);
|
||||
CHECK(text.find("REFINE= NOT_PERFORMED") != std::string::npos);
|
||||
CHECK(text.find("REFINE_REASON= refinement failed") != std::string::npos);
|
||||
}
|
||||
Reference in new issue
Block a user