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