Build Packages / Create release (push) Successful in 9s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 5m19s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 10m26s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 19m33s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 23m17s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 23m28s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 24m38s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 27m30s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 29m4s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 29m19s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 29m31s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 23m19s
Build Packages / Generate python client (push) Successful in 28s
Build Packages / Build documentation (push) Successful in 1m22s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 24m39s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 29m40s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 28m31s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 24m33s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 27m45s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 29m25s
Build Packages / Unit tests (push) Successful in 1h35m24s
Build Packages / Validation Battery (push) Successful in 35m45s
* Rugnux: A placed model is refined by default - restrained maximum-likelihood refinement on the GPU (CPU without one), writing `<prefix>_refined_model.cif`, `<prefix>_refined_maps.mtz` and the difference-map blobs in `<prefix>_blobs.json`; `--no-refine` skips it. * Rugnux: `--mode refine` refines a model against merged structure factors from an MTZ or mmCIF file. * Rugnux: The embedded restraint dictionary covers amino acids, nucleotides including common modified RNA nucleotides, common cofactors, crystallisation additives, waters and ions, with metal sites restrained from the model's LINK records. * Rugnux: A model or reference file can be given among the inputs in any order, recognised by its contents. * Rugnux: The geometry refinement algorithm option is now `-r, --geom-refine` (was `--refine`). * Rugnux: Serial stills resolve the indexing ambiguity from the data when no model or reference is given, reject merge outliers by default, follow the run's measured rocking curve in prediction and partiality (also in `--mode scale`), and index reproducibly on the GPU. * Rugnux: Reflections touching the beam-stop shadow are not integrated, reflections coarser than the low-resolution limit are merged and written (but kept out of the scale fit), and the polarisation plane is measured more reliably. * Rugnux: Small-molecule runs report unit-cell e.s.d.s, in a SHELX `.ins` beside the `.hkl` and in the mmCIF. * jfjoch_broker: Live Rugnux refines the uploaded model after placement; the web frontend's Rugnux live page has a hero panel with resolution, ISa, space group, placement and refined R-work/R-free, and its Mol* viewer shows the refined model and maps. * Documentation: A new front page with download pages for Rugnux and the viewer, and the technical reference reorganised below it. * Build: The fast-feedback indexer is part of the source tree instead of being downloaded at build time. Reviewed-on: #91 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
95 lines
5.0 KiB
C++
95 lines
5.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 <condition_variable>
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
#include "gemmi/model.hpp"
|
|
#include "gemmi/scaling.hpp"
|
|
|
|
// Fit the overall scale, the anisotropic B and the flat bulk solvent of a model to observed
|
|
// amplitudes, writing the answer into `scaling` (k_overall, b_star, k_sol, b_sol) so that
|
|
// gemmi's own scale_data() can then apply it.
|
|
//
|
|
// The caller decides which reflections are fitted: FitModelScale reads scaling.points, which
|
|
// prepare_points() filled. Pass the WORKING set to keep the free reflections out of the scale
|
|
// model - that is a property of the call site, not of this function.
|
|
//
|
|
// Replaces gemmi's fit_isotropic_b_approximately() + fit_parameters(), which is an unbounded
|
|
// Levenberg-Marquardt: nothing there stops k_sol and b_sol reaching values a flat solvent model
|
|
// is meaningless at (b_sol of 1707 A^2 was measured on this corpus). Here k_sol and b_sol come
|
|
// from a grid search over the physical ranges, so an unphysical answer cannot be represented.
|
|
// The box the solvent parameters are searched in. The defaults are the physically reasonable
|
|
// range reported by Fokine & Urzhumtsev and adopted as phenix's default grid: k_sol in (0.1, 0.8)
|
|
// and b_sol in (10, 80) A^2, of which phenix searches b_sol 10-80 in steps of 5. A model with high
|
|
// atomic B gets a higher b_sol ceiling (SolventBMax).
|
|
struct ModelScaleBox {
|
|
double k_lo = 0.10, k_hi = 0.60;
|
|
double b_lo = 10.0, b_hi = 80.0;
|
|
// The overall scale and anisotropic B of the points of the fine pass by robust least squares
|
|
// (ModelScaling.cpp, RobustReweight) rather than plain least squares on |F|: what refinement scores a model with.
|
|
bool robust = false;
|
|
// Where a fit of the model as it was a moment ago found the solvent: the fine pass around (k_start, b_start)
|
|
// without the coarse pass. Negative: the coarse pass finds where to look.
|
|
double k_start = -1, b_start = -1;
|
|
};
|
|
|
|
// The b_sol ceiling for `model`: 80 A^2, or twice the model's mean atomic B where that is higher.
|
|
// b_sol is how far the solvent's edge is smeared, and the edge can be no sharper than the atoms it
|
|
// lies against: a model at B of 300 A^2 beside a solvent held at 80 gets a solvent term that reaches
|
|
// far too deep in resolution, which only a too-small k_sol can then offset. Fokine & Urzhumtsev's
|
|
// 10-80 A^2 is for models at the B of ordinary entries (their mean b_sol 46 A^2), where the ceiling
|
|
// stays at 80.
|
|
double SolventBMax(const gemmi::Model &model);
|
|
|
|
struct ModelScaleReport {
|
|
int n_points = 0; // reflections the fit actually used
|
|
int n_grid = 0; // grid points evaluated
|
|
double r_work_fit = 1.0; // R on the fitted reflections, at the chosen solution
|
|
};
|
|
|
|
// nthreads: the grid points are fitted in parallel on up to this many threads; the answer does not
|
|
// depend on it.
|
|
ModelScaleReport FitModelScale(gemmi::Scaling<float> &scaling, ModelScaleBox box = {}, size_t nthreads = 1);
|
|
|
|
#ifdef JFJOCH_USE_CUDA
|
|
// FitModelScale on a GPU (ModelScaleGPU::FitSolvent): the same grid over the same box, the same fits and
|
|
// the same winner, with the sums over the reflections taken in double on the device where the host takes
|
|
// them in long double - so the answer is FitModelScale's to rounding, not to the bit. A few engines,
|
|
// reserved once, each a stream and room for one fit at a time; a fit takes a free one and waits for one
|
|
// where all are taken. Every kernel is deterministic, so which engine a fit gets changes no number.
|
|
class ModelScaleGPUPool {
|
|
public:
|
|
// What `engines` engines for fits of up to `max_points` reflections take on the card.
|
|
static size_t DeviceBytes(size_t max_points, size_t engines, bool robust = false);
|
|
// On `device`. Throws JFJochException on a CUDA failure.
|
|
ModelScaleGPUPool(int device, size_t max_points, size_t engines);
|
|
~ModelScaleGPUPool();
|
|
|
|
// FitModelScale(scaling, {.b_hi = b_hi, .robust = robust}, nthreads). Where FitModelScale would walk the
|
|
// grid point to point (five or fewer reflections for the isotropic start), it is FitModelScale on the host.
|
|
// Safe to call from several threads. Throws JFJochException on a CUDA failure. The robust fit's reweighting
|
|
// runs on the device; it is FitModelScale's robust fit to rounding (float residuals and weights, the
|
|
// anisotropic factor of the steps in float), deterministic run to run.
|
|
// k_start, b_start: ModelScaleBox's.
|
|
ModelScaleReport Fit(gemmi::Scaling<float> &scaling, double b_hi, size_t nthreads, bool robust = false,
|
|
double k_start = -1, double b_start = -1);
|
|
// What the pool holds on the card.
|
|
size_t Bytes() const { return DeviceBytes(max_points_, engines_.size()); }
|
|
|
|
private:
|
|
struct Engine;
|
|
int device_;
|
|
size_t max_points_;
|
|
std::vector<std::unique_ptr<Engine>> engines_;
|
|
std::vector<Engine *> idle_;
|
|
std::mutex m_;
|
|
std::condition_variable cv_;
|
|
};
|
|
#endif
|