Gemmi C++ API
Loading...
Searching...
No Matches
grid.hpp
Go to the documentation of this file.
1// Copyright 2017 Global Phasing Ltd.
2//
3// 3d grids used by CCP4 maps, cell-method search and hkl data.
4
5#ifndef GEMMI_GRID_HPP_
6#define GEMMI_GRID_HPP_
7
8#include <cassert>
9#include <cstddef> // for ptrdiff_t
10#include <complex>
11#include <algorithm> // for fill
12#include <numeric> // for accumulate
13#include <type_traits>
14#include <vector>
15#include "unitcell.hpp"
16#include "symmetry.hpp"
17#include "stats.hpp" // for DataStats
18#include "fail.hpp" // for fail
19
20namespace gemmi {
21
24enum class AxisOrder : unsigned char {
25 Unknown,
26 XYZ, // default, corresponds to CCP4 map with axis order XYZ,
27 // i.e. index X (H in reciprocal space) is fast and Z (or L) is slow
28 ZYX // fast Z (or L), may not be fully supported everywhere
29};
30
31enum class GridSizeRounding {
32 Nearest,
33 Up,
34 Down
35};
36
37inline int modulo(int a, int n) {
38 if (a >= n)
39 a %= n;
40 else if (a < 0)
41 a = (a + 1) % n + n - 1;
42 return a;
43}
44
45inline bool has_small_factorization(int n) {
46 while (n % 2 == 0)
47 n /= 2;
48 for (int k : {3, 5})
49 while (n % k == 0)
50 n /= k;
51 return n == 1 || n == -1;
52}
53
55 int n;
57 n = int(std::ceil(exact));
58 while (!has_small_factorization(n))
59 ++n;
60 } else if (rounding == GridSizeRounding::Down) {
61 n = std::max(int(std::floor(exact)), 1);
62 while (!has_small_factorization(n))
63 --n;
64 } else { // GridSizeRounding::Nearest
65 n = int(std::round(exact));
66 int sign = n > exact ? -1 : 1;
67 int k = 1;
68 while (n <= 0 || !has_small_factorization(n)) {
69 // for sign=1 we want sequence n+1, n-1, n+2, n-2, n+3, ...
70 n += sign * k;
71 sign = -sign;
72 ++k;
73 }
74 }
75 return n;
76}
77
78inline std::array<int, 3> good_grid_size(std::array<double, 3> limit,
80 const SpaceGroup* sg) {
81 std::array<int, 3> m = {{0, 0, 0}};
82 GroupOps gops;
83 if (sg)
84 gops = sg->operations();
85 std::array<int, 3> sg_fac = gops.find_grid_factors();
86
87 // If two dimensions are symmetry-related, they must have the same size.
88 // Otherwise, if two dimensions are almost equal, the same grid size
89 // looks better and is preferred.
90 for (int i = 1; i < 3; ++i)
91 for (int j = 0; j < i; ++j)
93 (std::fabs(limit[i] - limit[j]) < 0.5 && sg_fac[i] == sg_fac[j])) {
95 limit[j] = std::max(limit[i], limit[j]);
97 limit[j] = std::min(limit[i], limit[j]);
98 else // GridSizeRounding::Nearest
99 limit[j] = 0.5 * (limit[i] + limit[j]);
100 sg_fac[i] = -j; // mark the dimension with higher index
101 }
102
103 for (int i = 0; i < 3; ++i) {
104 int f = sg_fac[i];
105 if (f > 0) { // need to calculate the size
106 if (f % 2 != 0)
107 f *= 2; // always use even sizes - it simplifies things
109 } else { // the same size was already calculated
110 m[i] = m[-f];
111 }
112 }
113 return m;
114}
115
116struct GridOp {
118
119 std::array<int, 3> apply(int u, int v, int w) const {
120 std::array<int, 3> t;
121 const Op::Rot& rot = scaled_op.rot;
122 for (int i = 0; i != 3; ++i)
123 t[i] = rot[i][0] * u + rot[i][1] * v + rot[i][2] * w + scaled_op.tran[i];
124 return t;
125 }
126};
127
128inline void check_grid_factors(const SpaceGroup* sg, std::array<int,3> size) {
129 if (sg) {
130 GroupOps gops = sg->operations();
131 auto factors = gops.find_grid_factors();
132 for (int i = 0; i != 3; ++i)
133 if (size[i] % factors[i] != 0)
134 fail("Grid not compatible with the space group " + sg->xhm());
135 for (int i = 1; i != 3; ++i)
136 for (int j = 0; j != i; ++j)
137 if (gops.are_directions_symmetry_related(i, j) && size[i] != size[j])
138 fail("Grid must have the same size in symmetry-related directions");
139 }
140}
141
142inline double lerp_(double a, double b, double t) {
143 return a + (b - a) * t;
144}
145template<typename T>
146std::complex<T> lerp_(std::complex<T> a, std::complex<T> b, double t) {
147 return a + (b - a) * (T) t;
148}
149
153inline double cubic_interpolation(double u, double a, double b, double c, double d) {
154 //return 0.5 * u * (u * (u * (3*b - 3*c + d - a) + (2*a - 5*b + 4*c - d)) + (c - a)) + b;
155 // equivalent form that is faster on my computer:
156 return -0.5 * (a * u * ((u-2)*u + 1) - b * ((3*u - 5) * u*u + 2) +
157 u * (c * ((3*u - 4) * u - 1) - d * (u-1) * u));
158}
159
161inline double cubic_interpolation_der(double u, double a, double b, double c, double d) {
162 return a * (-1.5*u*u + 2*u - 0.5) + c * (-4.5*u*u + 4*u + 0.5)
163 + u * (4.5*b*u - 5*b + 1.5*d*u - d);
164}
165
166
168struct GridMeta {
170 const SpaceGroup* spacegroup = nullptr;
171 int nu = 0, nv = 0, nw = 0;
173
174 size_t point_count() const { return (size_t)nu * nv * nw; }
176 Fractional get_fractional(int u, int v, int w) const {
177 return {u * (1.0 / nu), v * (1.0 / nv), w * (1.0 / nw)};
178 }
179 Position get_position(int u, int v, int w) const {
180 return unit_cell.orthogonalize(get_fractional(u, v, w));
181 }
182
183 // operations re-scaled for faster later calculations; identity not included
184 std::vector<GridOp> get_scaled_ops_except_id() const {
185 std::vector<GridOp> grid_ops;
186 if (!spacegroup || spacegroup->number == 1)
187 return grid_ops;
189 fail("grid can use symmetries only if it is setup in the XYZ order");
190 GroupOps gops = spacegroup->operations();
191 grid_ops.reserve(gops.order());
192 for (const Op& so : gops.sym_ops)
193 for (const Op::Tran& co : gops.cen_ops) {
194 Op op = so.add_centering(co);
195 if (op != Op::identity()) {
196 // Rescale. Rotations are expected to be integral.
197 op.tran[0] = op.tran[0] * nu / Op::DEN;
198 op.tran[1] = op.tran[1] * nv / Op::DEN;
199 op.tran[2] = op.tran[2] * nw / Op::DEN;
200 for (int i = 0; i != 3; ++i)
201 for (int j = 0; j != 3; ++j)
202 op.rot[i][j] /= Op::DEN;
203 grid_ops.push_back({op});
204 }
205 }
206 return grid_ops;
207 }
208
210 size_t index_q(int u, int v, int w) const {
211 return size_t(w * nv + v) * nu + u;
212 }
213 size_t index_q(size_t u, size_t v, size_t w) const {
214 return (w * nv + v) * nu + u;
215 }
216
218 size_t index_n(int u, int v, int w) const { return index_n_ref(u, v, w); }
219
221 size_t index_n_ref(int& u, int& v, int& w) const {
222 if (u >= nu) u -= nu; else if (u < 0) u += nu;
223 if (v >= nv) v -= nv; else if (v < 0) v += nv;
224 if (w >= nw) w -= nw; else if (w < 0) w += nw;
225 return this->index_q(u, v, w);
226 }
227
229 size_t index_near_zero(int u, int v, int w) const {
230 return this->index_q(u >= 0 ? u : u + nu,
231 v >= 0 ? v : v + nv,
232 w >= 0 ? w : w + nw);
233 }
234};
235
237template<typename T>
240 struct Point {
241 int u, v, w;
243 };
244
245 std::vector<T> data;
246
247 void check_not_empty() const {
248 if (data.empty())
249 fail("grid is empty");
250 }
251
252 void set_size_without_checking(int nu_, int nv_, int nw_) {
253 nu = nu_, nv = nv_, nw = nw_;
254 data.resize((size_t)nu_ * nv_ * nw_);
255 }
256
257 T get_value_q(int u, int v, int w) const { return data[index_q(u, v, w)]; }
258
259 size_t point_to_index(const Point& p) const { return p.value - data.data(); }
260
261 Point index_to_point(size_t idx) {
262 auto d1 = std::div((ptrdiff_t)idx, (ptrdiff_t)nu);
263 auto d2 = std::div(d1.quot, (ptrdiff_t)nv);
264 int u = (int) d1.rem;
265 int v = (int) d2.rem;
266 int w = (int) d2.quot;
267 assert(index_q(u, v, w) == idx);
268 return {u, v, w, &data.at(idx)};
269 }
270
271 void fill(T value) {
272 data.resize(point_count());
273 std::fill(data.begin(), data.end(), value);
274 }
275
276 using Tsum = typename std::conditional<std::is_integral<T>::value,
277 std::ptrdiff_t, T>::type;
278 Tsum sum() const { return std::accumulate(data.begin(), data.end(), Tsum()); }
279
280
281 struct iterator {
283 size_t index;
284 int u = 0, v = 0, w = 0;
288 ++index;
289 if (++u == parent.nu) {
290 u = 0;
291 if (++v == parent.nv) {
292 v = 0;
293 ++w;
294 }
295 }
296 return *this;
297 }
299 return {u, v, w, &parent.data[index]};
300 }
301 bool operator==(const iterator &o) const { return index == o.index; }
302 bool operator!=(const iterator &o) const { return index != o.index; }
303 };
304 iterator begin() { return {*this, 0}; }
305 iterator end() { return {*this, data.size()}; }
306};
307
311template<typename T=float>
312struct Grid : GridBase<T> {
313 using Point = typename GridBase<T>::Point;
314 using GridBase<T>::nu;
315 using GridBase<T>::nv;
316 using GridBase<T>::nw;
317 using GridBase<T>::unit_cell;
318 using GridBase<T>::spacegroup;
319 using GridBase<T>::data;
320
322 double spacing[3] = {0., 0., 0.};
325
328 unit_cell = g.unit_cell;
329 spacegroup = g.spacegroup;
330 nu = g.nu;
331 nv = g.nv;
332 nw = g.nw;
333 this->axis_order = g.axis_order;
335 }
336
339 spacing[0] = 1.0 / (nu * unit_cell.ar);
340 spacing[1] = 1.0 / (nv * unit_cell.br);
341 spacing[2] = 1.0 / (nw * unit_cell.cr);
342 Vec3 inv_n(1.0 / nu, 1.0 / nv, 1.0 / nw);
345 fail("Grids work only with the standard orientation of crystal frame (SCALEn)");
346 }
347
353
354 void set_size(int nu_, int nv_, int nw_) {
357 }
358
366
367 void set_unit_cell(double a, double b, double c,
368 double alpha, double beta, double gamma) {
369 unit_cell.set(a, b, c, alpha, beta, gamma);
371 }
372
373 void set_unit_cell(const UnitCell& cell) {
374 unit_cell = cell;
376 }
377
378 template<typename S>
379 void setup_from(const S& st, double approx_spacing=0) {
380 spacegroup = st.find_spacegroup();
381 unit_cell = st.cell;
382 if (approx_spacing > 0)
384 }
385
387 size_t index_s(int u, int v, int w) const {
388 this->check_not_empty();
389 return this->index_q(modulo(u, nu), modulo(v, nv), modulo(w, nw));
390 }
391
393 T get_value(int u, int v, int w) const {
394 return data[index_s(u, v, w)];
395 }
396
397 void set_value(int u, int v, int w, T x) {
398 data[index_s(u, v, w)] = x;
399 }
400
402 Point get_point(int u, int v, int w) {
403 u = modulo(u, nu);
404 v = modulo(v, nv);
405 w = modulo(w, nw);
406 return {u, v, w, &data[this->index_q(u, v, w)]};
407 }
408
410 if (this->axis_order != AxisOrder::XYZ)
411 fail("grid is not fully setup");
412 return get_point(iround(f.x * nu), iround(f.y * nv), iround(f.z * nw));
413 }
417
418 size_t get_nearest_index(const Fractional& f) {
419 return index_s(iround(f.x * nu), iround(f.y * nv), iround(f.z * nw));
420 }
421
424 return this->get_fractional(p.u, p.v, p.w);
425 }
427 return this->get_position(p.u, p.v, p.w);
428 }
429
430 static double grid_modulo(double x, int n, int* iptr) {
431 double f = std::floor(x);
432 *iptr = modulo((int)f, n);
433 return x - f;
434 }
435
438 T trilinear_interpolation(double x, double y, double z) const {
439 this->check_not_empty();
440 int u, v, w;
441 double xd = grid_modulo(x, nu, &u);
442 double yd = grid_modulo(y, nv, &v);
443 double zd = grid_modulo(z, nw, &w);
444 assert(u >= 0 && v >= 0 && w >= 0);
445 assert(u < nu && v < nv && w < nw);
446 T avg[2];
447 for (int i = 0; i < 2; ++i) {
448 int wi = (i == 0 || w + 1 != nw ? w + i : 0);
449 size_t idx1 = this->index_q(u, v, wi);
450 int v2 = v + 1 != nv ? v + 1 : 0;
451 size_t idx2 = this->index_q(u, v2, wi);
452 int u_add = u + 1 != nu ? 1 : -u;
453 avg[i] = (T) lerp_(lerp_(data[idx1], data[idx1 + u_add], xd),
454 lerp_(data[idx2], data[idx2 + u_add], xd),
455 yd);
456 }
457 return (T) lerp_(avg[0], avg[1], zd);
458 }
460 return trilinear_interpolation(fctr.x * nu, fctr.y * nv, fctr.z * nw);
461 }
465
468 double tricubic_interpolation(double x, double y, double z) const {
469 std::array<std::array<std::array<T,4>,4>,4> copy;
470 copy_4x4x4(x, y, z, copy);
471 auto s = [&copy](int i, int j, int k) { return copy[i][j][k]; };
472 double a[4], b[4];
473 for (int i = 0; i < 4; ++i) {
474 for (int j = 0; j < 4; ++j)
475 a[j] = cubic_interpolation(z, s(i,j,0), s(i,j,1), s(i,j,2), s(i,j,3));
476 b[i] = cubic_interpolation(y, a[0], a[1], a[2], a[3]);
477 }
478 return cubic_interpolation(x, b[0], b[1], b[2], b[3]);
479 }
481 return tricubic_interpolation(fctr.x * nu, fctr.y * nv, fctr.z * nw);
482 }
487 std::array<double,4> tricubic_interpolation_der(double x, double y, double z) const {
488 std::array<std::array<std::array<T,4>,4>,4> copy;
489 copy_4x4x4(x, y, z, copy);
490 auto s = [&copy](int i, int j, int k) { return copy[i][j][k]; };
491 double a[4][4];
492 double b[4];
493 for (int i = 0; i < 4; ++i)
494 for (int j = 0; j < 4; ++j)
495 a[i][j] = cubic_interpolation(z, s(i,j,0), s(i,j,1), s(i,j,2), s(i,j,3));
496 for (int i = 0; i < 4; ++i)
497 b[i] = cubic_interpolation(y, a[i][0], a[i][1], a[i][2], a[i][3]);
498 std::array<double, 4> ret;
499 ret[0] = cubic_interpolation(x, b[0], b[1], b[2], b[3]);
500 ret[1] = cubic_interpolation_der(x, b[0], b[1], b[2], b[3]);
501 for (int i = 0; i < 4; ++i)
502 b[i] = cubic_interpolation(x, a[0][i], a[1][i], a[2][i], a[3][i]);
503 ret[2] = cubic_interpolation_der(y, b[0], b[1], b[2], b[3]);
504 for (int i = 0; i < 4; ++i)
505 for (int j = 0; j < 4; ++j)
506 a[i][j] = cubic_interpolation(y, s(i,0,j), s(i,1,j), s(i,2,j), s(i,3,j));
507 for (int i = 0; i < 4; ++i)
508 b[i] = cubic_interpolation(x, a[0][i], a[1][i], a[2][i], a[3][i]);
509 ret[3] = cubic_interpolation_der(z, b[0], b[1], b[2], b[3]);
510 return ret;
511 }
512 std::array<double,4> tricubic_interpolation_der(const Fractional& fctr) const {
513 auto r = tricubic_interpolation_der(fctr.x * nu, fctr.y * nv, fctr.z * nw);
514 return {r[0], r[1] * nu, r[2] * nv, r[3] * nw};
515 }
517 void copy_4x4x4(double& x, double& y, double& z,
518 std::array<std::array<std::array<T,4>,4>,4>& copy) const {
519 this->check_not_empty();
520 auto prepare_indices = [](double& r, int nt, int (&indices)[4]) {
521 int t;
522 r = grid_modulo(r, nt, &t);
523 indices[0] = (t != 0 ? t : nt) - 1;
524 indices[1] = t;
525 if (t + 2 < nt) {
526 indices[2] = t + 1;
527 indices[3] = t + 2;
528 } else {
529 indices[2] = t + 2 == nt ? t + 1 : 0;
530 indices[3] = t + 2 == nt ? 0 : 1;
531 }
532 };
533 int u_indices[4], v_indices[4], w_indices[4];
534 prepare_indices(x, nu, u_indices);
535 prepare_indices(y, nv, v_indices);
536 prepare_indices(z, nw, w_indices);
537 for (int i = 0; i < 4; ++i)
538 for (int j = 0; j < 4; ++j)
539 for (int k = 0; k < 4; ++k)
540 copy[i][j][k] = this->get_value_q(u_indices[i], v_indices[j], w_indices[k]);
541 }
542
544 T interpolate_value(const Fractional& f, int order=1) const {
545 switch (order) {
546 case 0: return *const_cast<Grid<T>*>(this)->get_nearest_point(f).value;
547 case 1: return trilinear_interpolation(f);
548 case 3: return (T) tricubic_interpolation(f);
549 }
550 throw std::invalid_argument("interpolation \"order\" must 0, 1 or 3");
551 }
552 T interpolate_value(const Position& ctr, int order=1) const {
554 }
555
556 void get_subarray(T* dest, std::array<int,3> start, std::array<int,3> shape) const {
557 this->check_not_empty();
558 if (this->axis_order != AxisOrder::XYZ)
559 fail("get_subarray() is for Grids in XYZ order");
560 const int u_start0 = modulo(start[0], nu);
561 for (int w = 0; w < shape[2]; w++) {
562 const int w0 = modulo(start[2] + w, nw);
563 for (int v = 0; v < shape[1]; v++) {
564 const int v0 = modulo(start[1] + v, nv);
565 int u_start = u_start0;
566 const T* src0 = &data[this->index_q(u_start, v0, w0)];
567 int len = shape[0];
568 while (len > nu - u_start) {
569 int elem = nu - u_start;
570 std::copy(src0, src0 + elem, dest);
571 src0 -= u_start;
572 dest += elem;
573 len -= elem;
574 u_start = 0;
575 }
576 std::copy(src0, src0 + len, dest);
577 dest += len;
578 }
579 }
580 }
581
582 void set_subarray(const T* src, std::array<int,3> start, std::array<int,3> shape) {
583 this->check_not_empty();
584 if (this->axis_order != AxisOrder::XYZ)
585 fail("set_subarray() is for Grids in XYZ order");
586 const int u_start0 = modulo(start[0], nu);
587 for (int w = 0; w < shape[2]; w++) {
588 const int w0 = modulo(start[2] + w, nw);
589 for (int v = 0; v < shape[1]; v++) {
590 const int v0 = modulo(start[1] + v, nv);
591 int u_start = u_start0;
592 T* dst0 = &data[this->index_q(u_start, v0, w0)];
593 int len = shape[0];
594 while (len > nu - u_start) {
595 int elem = nu - u_start;
596 std::copy(src, src + elem, dst0);
597 dst0 -= u_start;
598 src += elem;
599 len -= elem;
600 u_start = 0;
601 }
602 std::copy(src, src + len, dst0);
603 src += len;
604 }
605 }
606 }
607
608 template <bool UsePbc>
609 void check_size_for_points_in_box(int& du, int& dv, int& dw,
610 bool fail_on_too_large_radius) const {
611 if (UsePbc) {
613 if (2 * du >= nu || 2 * dv >= nv || 2 * dw >= nw)
614 fail("grid operation failed: radius bigger than half the unit cell?");
615 } else {
616 // If we'd use the minimum image convention the max would be (nu-1)/2.
617 // The limits set here are necessary for index_n() that is used below.
618 du = std::min(du, nu - 1);
619 dv = std::min(dv, nv - 1);
620 dw = std::min(dw, nw - 1);
621 }
622 }
623 }
624
625 template <bool UsePbc, typename Func>
626 void do_use_points_in_box(const Fractional& fctr, int du, int dv, int dw, Func&& func,
627 double radius=INFINITY) {
628 double max_dist_sq = radius * radius;
629 const Fractional nctr(fctr.x * nu, fctr.y * nv, fctr.z * nw);
630 int u0 = iround(nctr.x);
631 int v0 = iround(nctr.y);
632 int w0 = iround(nctr.z);
633 int u_lo = u0 - du;
634 int u_hi = u0 + du;
635 int v_lo = v0 - dv;
636 int v_hi = v0 + dv;
637 int w_lo = w0 - dw;
638 int w_hi = w0 + dw;
639 if (!UsePbc) {
640 u_lo = std::max(u_lo, 0);
641 u_hi = std::min(u_hi, nu - 1);
642 v_lo = std::max(v_lo, 0);
643 v_hi = std::min(v_hi, nv - 1);
644 w_lo = std::max(w_lo, 0);
645 w_hi = std::min(w_hi, nw - 1);
646 }
647 int u_0 = UsePbc ? modulo(u_lo, nu) : u_lo;
648 int v_0 = UsePbc ? modulo(v_lo, nv) : v_lo;
649 int w_0 = UsePbc ? modulo(w_lo, nw) : w_lo;
650 auto wrap = [](int& q, int nq) { if (UsePbc && q == nq) q = 0; };
651 Fractional fdelta(nctr.x - u_lo, 0, 0);
652 for (int w = w_lo, w_ = w_0; w <= w_hi; ++w, wrap(++w_, nw)) {
653 fdelta.z = nctr.z - w;
654 for (int v = v_lo, v_ = v_0; v <= v_hi; ++v, wrap(++v_, nv)) {
655 fdelta.y = nctr.y - v;
657 T* t = &data[this->index_q(u_0, v_, w_)];
658 double dist_sq0 = sq(delta.y) + sq(delta.z);
659 if (dist_sq0 > max_dist_sq)
660 continue;
661 for (int u = u_lo, u_ = u_0;;) {
662 double dist_sq = dist_sq0 + sq(delta.x);
663 if (!(dist_sq > max_dist_sq))
664 func(*t, dist_sq, delta, u, v, w);
665 if (u >= u_hi)
666 break;
667 ++u;
668 ++u_;
669 ++t;
670 if (UsePbc && u_ == nu) {
671 u_ = 0;
672 t -= nu;
673 }
674 delta.x -= orth_n.a11;
675 }
676 }
677 }
678 }
679
680 template <bool UsePbc, typename Func>
687
688 template <bool UsePbc, typename Func>
689 void use_points_around(const Fractional& fctr, double radius, Func&& func,
690 bool fail_on_too_large_radius=true) {
691 int du = (int) std::ceil(radius / spacing[0]);
692 int dv = (int) std::ceil(radius / spacing[1]);
693 int dw = (int) std::ceil(radius / spacing[2]);
695 fctr, du, dv, dw,
696 [&](T& ref, double d2, const Position&, int, int, int) { func(ref, d2); },
698 radius);
699 }
700
701 void set_points_around(const Position& ctr, double radius, T value, bool use_pbc=true) {
703 if (use_pbc)
704 use_points_around<true>(fctr, radius, [&](T& ref, double) { ref = value; }, false);
705 else
706 use_points_around<false>(fctr, radius, [&](T& ref, double) { ref = value; }, false);
707 }
708
711 for (auto& d : data)
712 if (impl::is_same(d, old_value))
713 d = new_value;
714 }
715
719 template<typename Func>
720 void symmetrize(Func func) {
722 }
723
724 template<typename Func>
725 void symmetrize_using_ops(const std::vector<GridOp>& ops, Func func) {
726 if (ops.empty())
727 return;
728 std::vector<size_t> mates(ops.size(), 0);
729 std::vector<signed char> visited(data.size(), 0); // faster than vector<bool>
730 size_t idx = 0;
731 for (int w = 0; w != nw; ++w)
732 for (int v = 0; v != nv; ++v)
733 for (int u = 0; u != nu; ++u, ++idx) {
734 assert(idx == this->index_q(u, v, w));
735 if (visited[idx])
736 continue;
737 for (size_t k = 0; k < ops.size(); ++k) {
738 std::array<int,3> t = ops[k].apply(u, v, w);
739 mates[k] = this->index_n(t[0], t[1], t[2]);
740 }
741 T value = data[idx];
742 for (size_t k : mates) {
743 if (visited[k])
744 fail("grid size is not compatible with space group");
745 value = func(value, data[k]);
746 }
747 data[idx] = value;
748 visited[idx] = 1;
749 for (size_t k : mates) {
750 data[k] = value;
751 visited[k] = 1;
752 }
753 }
754 assert(idx == data.size());
755 }
756
757 // most common symmetrize functions
759 symmetrize([](T a, T b) { return (a < b || !(b == b)) ? a : b; });
760 }
762 symmetrize([](T a, T b) { return (a > b || !(b == b)) ? a : b; });
763 }
765 symmetrize([](T a, T b) { return (std::abs(a) > std::abs(b) || !(b == b)) ? a : b; });
766 }
769 symmetrize([](T a, T b) { return a + b; });
770 }
772 symmetrize([default_](T a, T b) { return impl::is_same(a, default_) ? b : a; });
773 }
776 if (spacegroup && spacegroup->number != 1) {
777 int n_ops = spacegroup->operations().order();
778 for (T& x : data)
779 x /= n_ops;
780 }
781 }
782
784 void normalize() {
786 for (T& x : data)
787 x = static_cast<T>((x - stats.dmean) / stats.rms);
788 }
789};
790
791// TODO: add argument Box<Fractional> src_extent
792// cf. interpolate_grid_around_model() in solmask.hpp
793// cf interpolate_values in python/grid.cpp
794template<typename T>
795void interpolate_grid(Grid<T>& dest, const Grid<T>& src, const Transform& tr, int order=1) {
796 FTransform frac_tr = src.unit_cell.frac.combine(tr).combine(dest.unit_cell.orth);
797 size_t idx = 0;
798 for (int w = 0; w != dest.nw; ++w)
799 for (int v = 0; v != dest.nv; ++v)
800 for (int u = 0; u != dest.nu; ++u, ++idx) {
801 Fractional dest_fr = dest.get_fractional(u, v, w);
803 dest.data[idx] = src.interpolate_value(src_fr, order);
804 }
805}
806
807template<typename T>
809 if (a.data.size() != b.data.size() || a.nu != b.nu || a.nv != b.nv || a.nw != b.nw)
810 fail("calculate_correlation(): grids have different sizes");
811 Correlation corr;
812 for (size_t i = 0; i != a.data.size(); ++i)
813 if (!std::isnan(a.data[i]) && !std::isnan(b.data[i]))
814 corr.add_point(a.data[i], b.data[i]);
815 return corr;
816}
817
818} // namespace gemmi
819#endif
fail(), unreachable() and __declspec/__attribute__ macros
AxisOrder
Order of grid axis.
Definition grid.hpp:24
double cubic_interpolation_der(double u, double a, double b, double c, double d)
df/du (from Wolfram Alpha)
Definition grid.hpp:161
GridSizeRounding
Definition grid.hpp:31
int iround(double d)
Definition math.hpp:44
void interpolate_grid(Grid< T > &dest, const Grid< T > &src, const Transform &tr, int order=1)
Definition grid.hpp:795
bool has_small_factorization(int n)
Definition grid.hpp:45
double cubic_interpolation(double u, double a, double b, double c, double d)
Catmull–Rom spline interpolation.
Definition grid.hpp:153
int round_with_small_factorization(double exact, GridSizeRounding rounding)
Definition grid.hpp:54
DataStats calculate_data_statistics(const std::vector< T > &data)
Definition stats.hpp:107
constexpr float sq(float x)
Definition math.hpp:34
std::array< int, 3 > good_grid_size(std::array< double, 3 > limit, GridSizeRounding rounding, const SpaceGroup *sg)
Definition grid.hpp:78
Vec3_< double > Vec3
Definition math.hpp:113
int modulo(int a, int n)
Definition grid.hpp:37
void check_grid_factors(const SpaceGroup *sg, std::array< int, 3 > size)
Definition grid.hpp:128
void fail(const std::string &msg)
Definition fail.hpp:59
Correlation calculate_correlation(const GridBase< T > &a, const GridBase< T > &b)
Definition grid.hpp:808
Statistics utilities: classes Covariance, Correlation, DataStats.
void add_point(double x, double y)
Definition stats.hpp:53
Like Transform, but apply() arg is Fractional (not Vec3 - for type safety).
Definition unitcell.hpp:112
Fractional coordinates.
Definition unitcell.hpp:50
grid coordinates (modulo size) and a pointer to value
Definition grid.hpp:240
GridBase< T >::Point operator*()
Definition grid.hpp:298
iterator(GridBase &parent_, size_t index_)
Definition grid.hpp:285
iterator & operator++()
Definition grid.hpp:287
bool operator!=(const iterator &o) const
Definition grid.hpp:302
bool operator==(const iterator &o) const
Definition grid.hpp:301
A common subset of Grid and ReciprocalGrid.
Definition grid.hpp:238
std::vector< T > data
Definition grid.hpp:245
void fill(T value)
Definition grid.hpp:271
T get_value_q(int u, int v, int w) const
Definition grid.hpp:257
void check_not_empty() const
Definition grid.hpp:247
size_t point_to_index(const Point &p) const
Definition grid.hpp:259
Tsum sum() const
Definition grid.hpp:278
typename std::conditional< std::is_integral< T >::value, std::ptrdiff_t, T >::type Tsum
Definition grid.hpp:277
iterator end()
Definition grid.hpp:305
void set_size_without_checking(int nu_, int nv_, int nw_)
Definition grid.hpp:252
Point index_to_point(size_t idx)
Definition grid.hpp:261
iterator begin()
Definition grid.hpp:304
The base of Grid classes that does not depend on stored data type.
Definition grid.hpp:168
Position get_position(int u, int v, int w) const
Definition grid.hpp:179
size_t index_n(int u, int v, int w) const
Faster than index_s(), but works only if `-nu <= u < 2*nu`, etc.
Definition grid.hpp:218
size_t index_q(size_t u, size_t v, size_t w) const
Definition grid.hpp:213
size_t index_near_zero(int u, int v, int w) const
Faster than index_n(), but works only if -nu <= u < nu, etc.
Definition grid.hpp:229
size_t index_q(int u, int v, int w) const
Quick(est) index function, but works only if `0 <= u < nu`, etc.
Definition grid.hpp:210
size_t index_n_ref(int &u, int &v, int &w) const
The same as index_n(), but modifies arguments.
Definition grid.hpp:221
AxisOrder axis_order
Definition grid.hpp:172
const SpaceGroup * spacegroup
Definition grid.hpp:170
Fractional get_fractional(int u, int v, int w) const
u,v,w are not normalized here
Definition grid.hpp:176
std::vector< GridOp > get_scaled_ops_except_id() const
Definition grid.hpp:184
size_t point_count() const
Definition grid.hpp:174
UnitCell unit_cell
Definition grid.hpp:169
std::array< int, 3 > apply(int u, int v, int w) const
Definition grid.hpp:119
Real-space grid.
Definition grid.hpp:312
T trilinear_interpolation(const Fractional &fctr) const
Definition grid.hpp:459
T interpolate_value(const Fractional &f, int order=1) const
Definition grid.hpp:544
void set_value(int u, int v, int w, T x)
Definition grid.hpp:397
Point get_nearest_point(const Position &pos)
Definition grid.hpp:414
T trilinear_interpolation(double x, double y, double z) const
https://en.wikipedia.org/wiki/Trilinear_interpolation x,y,z are grid coordinates (x=1....
Definition grid.hpp:438
void set_unit_cell(double a, double b, double c, double alpha, double beta, double gamma)
Definition grid.hpp:367
void set_size_from_spacing(double approx_spacing, GridSizeRounding rounding)
Definition grid.hpp:359
void symmetrize_sum()
multiplies grid points on special position
Definition grid.hpp:768
size_t get_nearest_index(const Fractional &f)
Definition grid.hpp:418
typename GridBase< T >::Point Point
Definition grid.hpp:313
T get_value(int u, int v, int w) const
returns `data[index_s(u, v, w)]`
Definition grid.hpp:393
T trilinear_interpolation(const Position &ctr) const
Definition grid.hpp:462
void symmetrize_using_ops(const std::vector< GridOp > &ops, Func func)
Definition grid.hpp:725
double spacing[3]
spacing between virtual planes, not between points
Definition grid.hpp:322
void copy_metadata_from(const GridMeta &g)
copy unit_cell, spacegroup, nu, nv, nw, axis_order and set spacing
Definition grid.hpp:327
void do_use_points_in_box(const Fractional &fctr, int du, int dv, int dw, Func &&func, double radius=INFINITY)
Definition grid.hpp:626
Point get_point(int u, int v, int w)
Point stores normalizes indices (not the original u,v,w).
Definition grid.hpp:402
void set_size_without_checking(int nu_, int nv_, int nw_)
Definition grid.hpp:348
void calculate_spacing()
set spacing and orth_n
Definition grid.hpp:338
static double grid_modulo(double x, int n, int *iptr)
Definition grid.hpp:430
void set_points_around(const Position &ctr, double radius, T value, bool use_pbc=true)
Definition grid.hpp:701
void get_subarray(T *dest, std::array< int, 3 > start, std::array< int, 3 > shape) const
Definition grid.hpp:556
Fractional point_to_fractional(const Point &p) const
Point stores normalized indices, so fractional coordinates are in [0,1).
Definition grid.hpp:423
void use_points_in_box(const Fractional &fctr, int du, int dv, int dw, Func &&func, bool fail_on_too_large_radius=true, double radius=INFINITY)
Definition grid.hpp:681
void setup_from(const S &st, double approx_spacing=0)
Definition grid.hpp:379
void normalize()
scale the data to get mean == 0 and rmsd == 1 (doesn't work for T=complex)
Definition grid.hpp:784
Point get_nearest_point(const Fractional &f)
Definition grid.hpp:409
std::array< double, 4 > tricubic_interpolation_der(const Fractional &fctr) const
Definition grid.hpp:512
void symmetrize_avg()
Definition grid.hpp:774
Position point_to_position(const Point &p) const
Definition grid.hpp:426
void symmetrize_max()
Definition grid.hpp:761
UpperTriangularMat33 orth_n
unit_cell.orth.mat columns divided by nu, nv, nw
Definition grid.hpp:324
double tricubic_interpolation(const Position &ctr) const
Definition grid.hpp:483
std::array< double, 4 > tricubic_interpolation_der(double x, double y, double z) const
returns the same as above + derivatives df/dx, df/dy, df/dz
Definition grid.hpp:487
void symmetrize_abs_max()
Definition grid.hpp:764
void set_unit_cell(const UnitCell &cell)
Definition grid.hpp:373
void symmetrize(Func func)
Use.
Definition grid.hpp:720
double tricubic_interpolation(const Fractional &fctr) const
Definition grid.hpp:480
double tricubic_interpolation(double x, double y, double z) const
https://en.wikipedia.org/wiki/Tricubic_interpolation x,y,z are grid coordinates (x=1....
Definition grid.hpp:468
T interpolate_value(const Position &ctr, int order=1) const
Definition grid.hpp:552
void symmetrize_min()
Definition grid.hpp:758
void change_values(T old_value, T new_value)
Change all occurrences of old_value to new_value.
Definition grid.hpp:710
void set_size(int nu_, int nv_, int nw_)
Definition grid.hpp:354
void set_subarray(const T *src, std::array< int, 3 > start, std::array< int, 3 > shape)
Definition grid.hpp:582
void use_points_around(const Fractional &fctr, double radius, Func &&func, bool fail_on_too_large_radius=true)
Definition grid.hpp:689
size_t index_s(int u, int v, int w) const
Returns index in data array for (u,v,w). Safe but slower than index_q().
Definition grid.hpp:387
void check_size_for_points_in_box(int &du, int &dv, int &dw, bool fail_on_too_large_radius) const
Definition grid.hpp:609
void symmetrize_nondefault(T default_)
Definition grid.hpp:771
std::vector< Op > sym_ops
Definition symmetry.hpp:255
int order() const
Definition symmetry.hpp:258
std::array< int, 3 > find_grid_factors() const
Definition symmetry.hpp:444
std::vector< Op::Tran > cen_ops
Definition symmetry.hpp:256
bool are_directions_symmetry_related(int u, int v) const
Definition symmetry.hpp:454
bool is_upper_triangular() const
Definition math.hpp:242
Mat33 multiply_by_diagonal(const Vec3 &p) const
Definition math.hpp:179
std::array< std::array< int, 3 >, 3 > Rot
Definition symmetry.hpp:32
Tran tran
Definition symmetry.hpp:36
static constexpr int DEN
Definition symmetry.hpp:31
std::array< int, 3 > Tran
Definition symmetry.hpp:33
static constexpr Op identity()
Definition symmetry.hpp:176
Coordinates in Angstroms - orthogonal (Cartesian) coordinates.
Definition unitcell.hpp:32
Unit cell.
Definition unitcell.hpp:165
double ar
reciprocal parameters a*, b*, c*, alpha*, beta*, gamma*
Definition unitcell.hpp:177
Fractional fractionalize(const Position &o) const
Definition unitcell.hpp:394
void set(double a_, double b_, double c_, double alpha_, double beta_, double gamma_)
Definition unitcell.hpp:291
Position orthogonalize(const Fractional &f) const
Definition unitcell.hpp:391
Transform orth
Definition unitcell.hpp:173
Vec3 multiply(const Vec3 &p) const
Definition math.hpp:265
Crystallographic Symmetry. Space Groups. Coordinate Triplets.
Unit cell.