Build Packages / Create release (push) Successful in 26s
Build Packages / Build documentation (push) Successful in 1m43s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 5m7s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 11m48s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 7m22s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 12m24s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 25m30s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 25m29s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 29m41s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 21m44s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 24m41s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 26m9s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 26m3s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 28m28s
Build Packages / Generate python client (push) Successful in 27s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 31m16s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 20m44s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 26m27s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 22m52s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 27m44s
Build Packages / Unit tests (push) Successful in 1h46m55s
Build Packages / Validation Battery (push) Successful in 52m50s
* Rugnux: Several rotation sweeps of one crystal are merged as one dataset (`rugnux a_master.h5 b_master.h5`), on a common indexing and cell, with a non-isomorphous sweep left out and reported; `--sweeps separate` processes each sweep on its own. Bruker `.sfrm` frames and imgCIF files with a full goniometer axis chain are read. * Rugnux: More robust rotation processing - a retry with a more sensitive spot finder when no lattice is found, a self-consistent detector distance, long axes and doubled cells tested as hypotheses, a `-C` supercell multiple stepped down, and each ice ring merged, down-weighted or rejected on its own. * Rugnux: Serial stills are merged with a partiality-aware error model, reported as `ERROR_MODEL_A/B/C` with no ISa or R_merge/R_meas/R_pim; runs without `-S` have an overall intensity scale about 0.86-0.88 times that of earlier versions, with unchanged statistics. A second crystal form is left out of the merge and reported, the indexing ambiguity is resolved more reliably, and `--model` no longer influences processing. * Rugnux: The reported CC1/2 is the standard unweighted one, which can be lower than before on sweeps with weak frames; ISa comes with a 95% interval (`ISA_LOW`/`ISA_HIGH`). * Rugnux: Model refinement adds anisotropic/TLS, local NCS and twin refinement where R-free supports them; building waters and relabelling ions is now off by default (`--refine-solvent` turns it on). * NXmx: A detector of several modules is written as `module_0` ... `module_N` only, so DIALS reads each module as its own panel. * Other: JPEG previews use a bundled encoder (libjpeg-turbo no longer needed), Rugnux prints how to cite it, and the release archives contain the HTML documentation. Reviewed-on: #92 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
104 lines
6.0 KiB
C++
104 lines
6.0 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "gemmi/model.hpp"
|
|
|
|
#include "SolventSites.h" // SolventSite, SolventMaps
|
|
|
|
// One round of the solvent build that RefineModel runs within its weight trajectory (Refinement.h): waters placed at the
|
|
// peaks of the difference map a refined model leaves, the model's waters the maps no longer support taken out, and
|
|
// waters and ions relabelled.
|
|
//
|
|
// A peak of mFo-DFc becomes a water where it has a hydrogen-bond partner and nothing too close - on a special
|
|
// position exactly on it, at the occupancy that makes its coinciding copies one water; a peak that
|
|
// cannot be a single solvent atom - one bonded to the model, beside a negative peak on a model atom, where a
|
|
// truncated side chain's missing atoms would be, one of a cluster of peaks at bonded distances, elongated, or
|
|
// denser than the model's S and P - is reported as not solvent and left alone. A water or a single-atom ion whose
|
|
// surroundings and density support another species, not ambiguously (SolventSites.h), is relabelled as it: a water
|
|
// becomes an ion, an ion water or another ion.
|
|
struct SolventEdit {
|
|
enum class Kind { Added, Removed, NotSolvent, Relabelled };
|
|
Kind kind = Kind::NotSolvent;
|
|
int round = 0;
|
|
std::string site; // the water as modelled ("HOH A401"); empty for a peak not taken
|
|
std::string chain; // where an added water goes: the chain of the atom nearest to it
|
|
gemmi::SeqId seqid; // a removed water's residue
|
|
char altloc = '\0';
|
|
gemmi::Position pos;
|
|
double difference_sigma = 0; // mFo-DFc at the site, in its rms
|
|
double map_sigma = 0; // 2mFo-DFc
|
|
double b_iso = 0; // an added water's starting B, a removed one's
|
|
double occupancy = 1; // an added water's: 1 over its site's multiplicity on a special position
|
|
double elongation = 0; // a peak's density: the longest axis over the shortest, rms
|
|
std::string species; // a relabelled site's new residue name (HOH, MG, NA, K, CA, CL); its old one in `site`
|
|
std::string reason; // why: the partners an added water has, what a removed or refused one lacks, the
|
|
// scores a relabelled one has
|
|
};
|
|
|
|
const char *SolventEditKindName(SolventEdit::Kind kind);
|
|
|
|
struct SolventRound {
|
|
std::vector<SolventEdit> edits;
|
|
int added = 0, removed = 0, not_solvent = 0, relabelled = 0;
|
|
std::map<std::string, int> refused; // the peaks neither taken nor reported, by what they lack
|
|
};
|
|
|
|
// The resolution waters are built to at most, following the ordered-solvent defaults of phenix.refine
|
|
// (Afonine et al. (2012) Acta Cryst. D68, 352-367): no picking where the data stop short of 2.8 A.
|
|
constexpr double SOLVENT_BUILD_D_MIN = 2.8;
|
|
|
|
// At most this many rounds, each judged by refining the solvent it changed (a third one gained at most 0.0004 in
|
|
// R-free where it was kept, on twelve sets, and was refused on the others).
|
|
constexpr int SOLVENT_BUILD_ROUNDS = 2;
|
|
|
|
// What the build did, over the rounds kept.
|
|
struct SolventBuildResult {
|
|
bool ran = false;
|
|
int rounds = 0; // the rounds kept
|
|
int added = 0; // waters added and still in the model
|
|
int removed = 0; // waters of the model as given taken out
|
|
int relabelled = 0; // sites relabelled, in the rounds kept
|
|
int not_solvent = 0; // peaks of the final model's difference map that are no solvent
|
|
std::vector<SolventEdit> edits; // the kept rounds' additions and removals, then the final not-solvent peaks
|
|
std::string stop; // why it stopped
|
|
double seconds = 0;
|
|
};
|
|
|
|
// A round on `model` (the refined model the maps were made from, in their cell and space group): `maps` its
|
|
// 2mFo-DFc and mFo-DFc (the anomalous one unused), `sites` its solvent audit (AnalyseSolventSites on the same
|
|
// maps), `built` the waters added by earlier rounds (by name, "HOH A401"), `b_ceiling` the highest B its refinement
|
|
// allowed; `relabelled` the sites relabelled by earlier rounds (by their new name), which are not relabelled again.
|
|
SolventRound PickSolvent(const gemmi::Structure &model, const SolventMaps &maps, const std::vector<SolventSite> &sites,
|
|
const std::set<std::string> &built, const std::set<std::string> &relabelled, double b_ceiling,
|
|
int round);
|
|
|
|
// The N, O and S of a metal's inner sphere in the model's own copy (no symmetry mate): within the library's distance
|
|
// plus INNER_SHELL_MARGIN, at the coordination of those within an octahedral site's - the atoms a metal's records
|
|
// name. `own` is the metal's residue.
|
|
struct InnerSphereDonor {
|
|
int chain = 0, residue = 0, atom = 0; // indices into the model
|
|
double distance = 0;
|
|
};
|
|
std::vector<InnerSphereDonor> InnerSphereDonors(const gemmi::Model &model, const gemmi::Residue &own,
|
|
const gemmi::Atom &metal);
|
|
|
|
// The element of a species by its residue name (HOH: O, MG: Mg, ...).
|
|
gemmi::El SolventSpeciesElement(const std::string &species);
|
|
|
|
// A record (struct_conn metalc, named rugnux_metalcN with N on from `record`) for each of InnerSphereDonors() of the
|
|
// single-atom ion `res` of `chain`, where it is a metal.
|
|
void AddMetalRecords(gemmi::Structure &st, const gemmi::Chain &chain, const gemmi::Residue &res, int &record);
|
|
|
|
// The round's waters taken out and put in: each added one to the chain of its nearest atom, numbered on from that
|
|
// chain's last residue (its `site` is set to the name it gets). A relabelled site gets its new residue name,
|
|
// element and atom name; its metal records go, and a metal gets one for each N, O or S of its inner sphere (within
|
|
// the library's distance plus 0.33 A, as the records of the model as given are taken).
|
|
void ApplySolventEdits(gemmi::Structure &st, std::vector<SolventEdit> &edits);
|