Gemmi C++ API
Loading...
Searching...
No Matches
scaling.hpp
Go to the documentation of this file.
1// Copyright 2020 Global Phasing Ltd.
2//
3// Anisotropic scaling of data (includes scaling of bulk solvent parameters)
4
5#ifndef GEMMI_SCALING_HPP_
6#define GEMMI_SCALING_HPP_
7
8#include "asudata.hpp"
9#include "levmar.hpp"
10
11namespace gemmi {
12
13using Vec6 = std::array<double, 6>;
14
15inline double vec6_dot(const Vec6& a, const SMat33<double>& s) {
16 return a[0] * s.u11 + a[1] * s.u22 + a[2] * s.u33
17 + a[3] * s.u12 + a[4] * s.u13 + a[5] * s.u23;
18}
19
24inline std::vector<Vec6> adp_symmetry_constraints(const SpaceGroup* sg) {
25 auto constraints = [](const std::initializer_list<int>& l) {
26 constexpr double K2 = 0.70710678118654752440; // sqrt(1/2)
27 constexpr double K3 = 0.57735026918962576451; // sqrt(1/3)
28 static const Vec6 vv[10] = {
29 {1, 0, 0, 0, 0, 0}, // 0
30 {0, 1, 0, 0, 0, 0}, // 1
31 {0, 0, 1, 0, 0, 0}, // 2
32 {0, 0, 0, 1, 0, 0}, // 3
33 {0, 0, 0, 0, 1, 0}, // 4
34 {0, 0, 0, 0, 0, 1}, // 5
35 {K2, K2, 0, 0, 0, 0}, // 6
36 {2/3., 2/3., 0, 1/3., 0, 0}, // 7
37 {K3, K3, K3, 0, 0, 0}, // 8
38 {0, 0, 0, K3, K3, K3} // 9
39 };
40 std::vector<Vec6> c;
41 c.reserve(l.size());
42 for (int i : l)
43 c.push_back(vv[i]);
44 return c;
45 };
47 switch (cr_system) {
49 return constraints({0, 1, 2, 3, 4, 5});
51 // the last index is: a->5, b->4, c->3
52 return constraints({0, 1, 2, 3 + 'c' - sg->monoclinic_unique_axis()});
54 return constraints({0, 1, 2});
56 return constraints({6, 2});
59 if (sg->ext == 'R')
60 return constraints({8, 9});
61 return constraints({7, 2});
63 return constraints({8});
64 }
66}
67
68template<typename Real>
69struct Scaling {
70 struct Point {
72 double stol2;
73 std::complex<Real> fcmol, fmask;
75
76 Miller get_x() const { return hkl; }
77 double get_y() const { return fobs; }
78 double get_weight() const { return 1.0 /* / sigma*/; }
79 };
80
82 // model parameters
83 double k_overall = 1.;
84 // b_star = F B_cart F^T, where F - fractionalization matrix
85 SMat33<double> b_star{0, 0, 0, 0, 0, 0};
86 std::vector<Vec6> constraint_matrix;
87 bool use_solvent = false;
88 bool fix_k_sol = false;
89 bool fix_b_sol = false;
90 // initialize with average values (Fokine & Urzhumtsev, 2002)
91 double k_sol = 0.35;
92 double b_sol = 46.0;
93 std::vector<Point> points;
94
95 // pre: calc and obs are sorted
98
99 // B_{overall} is stored as B* not B_{cartesian}.
100 // Use getter and setter to convert from/to B_{cartesian}.
102 b_star = b_overall.transformed_by(cell.frac.mat);
103 }
107
108 // Scale data, optionally adding bulk solvent correction.
109 void scale_data(AsuData<std::complex<Real>>& asu_data,
110 const AsuData<std::complex<Real>>* mask_data) const {
111 if (use_solvent && !(mask_data && mask_data->size() == asu_data.size()))
112 fail("scale_data(): mask data not prepared");
113 bool use_scaling = (k_overall != 1 || !b_star.all_zero());
114 for (size_t i = 0; i != asu_data.v.size(); ++i) {
116 if (use_solvent) {
117 if (hv.hkl != mask_data->v[i].hkl)
118 fail("scale_data(): data arrays don't match");
119 double stol2 = cell.calculate_stol_sq(hv.hkl);
120 hv.value += (Real)get_solvent_scale(stol2) * mask_data->v[i].value;
121 }
122 if (use_scaling)
124 }
125 }
126
127 std::complex<Real> scale_value(const Miller& hkl, std::complex<Real> f_value,
128 std::complex<Real> mask_value) {
129 if (use_solvent) {
130 double stol2 = cell.calculate_stol_sq(hkl);
132 }
133 return f_value * (Real) get_overall_scale_factor(hkl);
134 }
135
136 std::vector<double> get_parameters() const {
137 std::vector<double> ret;
138 ret.push_back(k_overall);
139 if (use_solvent) {
140 if (!fix_k_sol)
141 ret.push_back(k_sol);
142 if (!fix_b_sol)
143 ret.push_back(b_sol);
144 }
145 for (const Vec6& v : constraint_matrix)
146 ret.push_back(vec6_dot(v, b_star));
147 return ret;
148 }
149
151 void set_parameters(const std::vector<double>& p) {
152 k_overall = p[0];
153 int n = 0;
154 if (use_solvent) {
155 if (!fix_k_sol)
156 k_sol = p[++n];
157 if (!fix_b_sol)
158 b_sol = p[++n];
159 }
160 b_star = {0, 0, 0, 0, 0, 0};
161 for (const Vec6& row : constraint_matrix) {
162 double d = p[++n];
163 b_star.u11 += row[0] * d;
164 b_star.u22 += row[1] * d;
165 b_star.u33 += row[2] * d;
166 b_star.u12 += row[3] * d;
167 b_star.u13 += row[4] * d;
168 b_star.u23 += row[5] * d;
169 }
170 }
171
172 void prepare_points(const AsuData<std::complex<Real>>& calc,
174 const AsuData<std::complex<Real>>* mask_data) {
175 if (use_solvent && !(mask_data && mask_data->size() == calc.size()))
176 fail("prepare_points(): mask data not prepared");
177 std::complex<Real> fmask;
178 points.reserve(std::min(calc.size(), obs.size()));
179 auto c = calc.v.begin();
180 for (const HklValue<ValueSigma<Real>>& o : obs.v) {
181 if (c->hkl != o.hkl) {
182 while (*c < o.hkl) {
183 ++c;
184 if (c == calc.v.end())
185 return;
186 }
187 if (c->hkl != o.hkl)
188 continue;
189 }
190 if (use_solvent) {
191 const HklValue<std::complex<Real>>& m = mask_data->v[c - calc.v.begin()];
192 if (m.hkl != c->hkl)
193 fail("prepare_points(): unexpected data");
194 fmask = m.value;
195 }
196 double stol2 = cell.calculate_stol_sq(o.hkl);
197 if (!std::isnan(o.value.value) && !std::isnan(o.value.sigma))
198 points.push_back({o.hkl, stol2, c->value, fmask, o.value.value, o.value.sigma});
199 ++c;
200 if (c == calc.v.end())
201 break;
202 }
203 }
204
205
206 double get_solvent_scale(double stol2) const {
207 return k_sol * std::exp(-b_sol * stol2);
208 }
209
210 double get_overall_scale_factor(const Miller& hkl) const {
211 return k_overall * std::exp(-0.25 * b_star.r_u_r(hkl));
212 }
213
214 // quick linear fit (ignoring sigma) to get initial parameters
216 double sx = 0, sy = 0, sxx = 0, sxy = 0;
217 int n = 0;
218 for (const Point& p : points) {
219 if (p.fobs < 1 || p.fobs < p.sigma) // skip weak reflections
220 continue;
221 double x = p.stol2;
222 double fcalc = std::abs(use_solvent ? p.fcmol + (Real)get_solvent_scale(x) * p.fmask
223 : p.fcmol);
224 double y = std::log(static_cast<float>(p.fobs / fcalc));
225 sx += x;
226 sy += y;
227 sxx += x * x;
228 sxy += x * y;
229 n += 1;
230 }
231 if (n <= 5) // this is not expected to happen
232 return;
233 double slope = (n * sxy - sx * sy) / (n * sxx - sx * sx);
234 double intercept = (sy - slope * sx) / n;
235 double b_iso = -slope;
236 k_overall = std::exp(intercept);
237 set_b_overall({b_iso, b_iso, b_iso, 0, 0, 0});
238 }
239
242 levmar.fit(*this);
243 }
244
245
246 // interface for fitting
247 std::vector<double> compute_values() const {
248 std::vector<double> values;
249 values.reserve(points.size());
250 for (const Point& p : points) {
251 double fcalc = std::abs(p.fcmol + (Real)get_solvent_scale(p.stol2) * p.fmask);
252 values.push_back(fcalc * (Real) get_overall_scale_factor(p.hkl));
253 }
254 return values;
255 }
256
257 // the tile_* parameters allow tiling: computing derivatives from a span
258 // of points at one time, which limits memory usage.
260 std::vector<double>& yy,
261 std::vector<double>& dy_da) const {
262 assert(tile_size == yy.size());
263 size_t npar = dy_da.size() / tile_size;
264 assert(dy_da.size() == npar * tile_size);
265 int n = 1;
266 if (use_solvent)
267 n += int(!fix_k_sol) + int(!fix_b_sol);
268 for (size_t i = 0; i != tile_size; ++i) {
269 const Point& pt = points[tile_start+i];
270 Vec3 h(pt.hkl);
271 double kaniso = std::exp(-0.25 * b_star.r_u_r(h));
272 double fcalc_abs;
273 if (use_solvent) {
274 double solv_b = std::exp(-b_sol * pt.stol2);
275 double solv_scale = k_sol * solv_b;
276 auto fcalc = pt.fcmol + (Real)solv_scale * pt.fmask;
277 fcalc_abs = std::abs(fcalc);
278 size_t offset = i * npar + 1;
279 double dy_dsol = (fcalc.real() * pt.fmask.real() +
280 fcalc.imag() * pt.fmask.imag()) / fcalc_abs * k_overall * kaniso;
281 if (!fix_k_sol)
282 dy_da[offset++] = solv_b * dy_dsol;
283 if (!fix_b_sol)
284 dy_da[offset] = -pt.stol2 * solv_scale * dy_dsol;
285 } else {
286 fcalc_abs = std::abs(pt.fcmol);
287 }
288 double fe = fcalc_abs * kaniso;
289 yy[i] = k_overall * fe;
290 dy_da[i * npar + 0] = fe; // dy/d k_overall
292 -0.25 * yy[i] * (h.x * h.x),
293 -0.25 * yy[i] * (h.y * h.y),
294 -0.25 * yy[i] * (h.z * h.z),
295 -0.5 * yy[i] * (h.x * h.y),
296 -0.5 * yy[i] * (h.x * h.z),
297 -0.5 * yy[i] * (h.y * h.z),
298 };
299 double* dy_db = &dy_da[i * npar + n];
300 for (size_t j = 0; j < constraint_matrix.size(); ++j)
302 }
303 }
304};
305
306} // namespace gemmi
307#endif
double vec6_dot(const Vec6 &a, const SMat33< double > &s)
Definition scaling.hpp:15
std::array< double, 6 > Vec6
Definition scaling.hpp:13
std::array< int, 3 > Miller
A synonym for convenient passing of hkl.
Definition unitcell.hpp:128
Vec3_< double > Vec3
Definition math.hpp:101
std::vector< Vec6 > adp_symmetry_constraints(const SpaceGroup *sg)
Symmetry constraints of ADP.
Definition scaling.hpp:24
void fail(const std::string &msg)
Definition fail.hpp:59
void unreachable()
Definition fail.hpp:80
CrystalSystem
Definition symmetry.hpp:908
SMat33< Real > transformed_by(const Mat33 &m) const
Definition math.hpp:319
bool all_zero() const
Definition math.hpp:275
auto r_u_r(const Vec3_< VT > &r) const -> decltype(r.x+u11)
Definition math.hpp:295
double get_y() const
Definition scaling.hpp:77
std::complex< Real > fmask
Definition scaling.hpp:73
double get_weight() const
Definition scaling.hpp:78
Miller get_x() const
Definition scaling.hpp:76
std::complex< Real > fcmol
Definition scaling.hpp:73
Scaling(const UnitCell &cell_, const SpaceGroup *sg)
Definition scaling.hpp:96
void set_b_overall(const SMat33< double > &b_overall)
Definition scaling.hpp:101
void fit_isotropic_b_approximately()
Definition scaling.hpp:215
double get_overall_scale_factor(const Miller &hkl) const
Definition scaling.hpp:210
SMat33< double > get_b_overall() const
Definition scaling.hpp:104
void fit_parameters()
Definition scaling.hpp:240
UnitCell cell
Definition scaling.hpp:81
std::complex< Real > scale_value(const Miller &hkl, std::complex< Real > f_value, std::complex< Real > mask_value)
Definition scaling.hpp:127
double k_overall
Definition scaling.hpp:83
void scale_data(AsuData< std::complex< Real > > &asu_data, const AsuData< std::complex< Real > > *mask_data) const
Definition scaling.hpp:109
SMat33< double > b_star
Definition scaling.hpp:85
std::vector< double > get_parameters() const
Definition scaling.hpp:136
void prepare_points(const AsuData< std::complex< Real > > &calc, const AsuData< ValueSigma< Real > > &obs, const AsuData< std::complex< Real > > *mask_data)
Definition scaling.hpp:172
std::vector< Vec6 > constraint_matrix
Definition scaling.hpp:86
std::vector< double > compute_values() const
Definition scaling.hpp:247
double get_solvent_scale(double stol2) const
Definition scaling.hpp:206
std::vector< Point > points
Definition scaling.hpp:93
void compute_values_and_derivatives(size_t tile_start, size_t tile_size, std::vector< double > &yy, std::vector< double > &dy_da) const
Definition scaling.hpp:259
void set_parameters(const std::vector< double > &p)
set k_overall, k_sol, b_sol, b_star
Definition scaling.hpp:151
Unit cell.
Definition unitcell.hpp:139
Transform frac
Definition unitcell.hpp:151
double calculate_stol_sq(const Miller &hkl) const
Calculate (sin(theta)/lambda)^2 = d*^2/4.
Definition unitcell.hpp:540
Transform orth
Definition unitcell.hpp:150