Gemmi C++ API
Loading...
Searching...
No Matches
asumask.hpp
Go to the documentation of this file.
1// Copyright 2022 Global Phasing Ltd.
2//
3// AsuBrick and MaskedGrid that is used primarily as direct-space asu mask.
4
5#ifndef GEMMI_ASUMASK_HPP_
6#define GEMMI_ASUMASK_HPP_
7
8#include <cstdint>
9
10#include "util.hpp" // for in_vector
11#include "grid.hpp"
12
13namespace gemmi {
14
15struct AsuBrick {
16 // The brick is 0<=x<=size[0]/24, 0<=y<=size[1]/24, 0<=z<=size[2]/24
17 static constexpr int denom = 24;
18 std::array<int, 3> size;
19 std::array<bool, 3> incl;
20 int volume;
21
22 AsuBrick(int a, int b, int c)
23 : size({a,b,c}), incl({a < denom, b < denom, c < denom}), volume(a*b*c) {}
24
25 std::string str() const {
26 std::string s;
27 for (int i = 0; i < 3; ++i) {
28 if (i != 0)
29 s += "; ";
30 s += "0<=";
31 s += "xyz"[i];
32 s += incl[i] ? "<=" : "<";
34 }
35 return s;
36 }
37
39 double inv_denom = 1.0 / denom;
40 return Fractional(inv_denom * size[0] + (incl[0] ? 1e-9 : -1e-9),
41 inv_denom * size[1] + (incl[1] ? 1e-9 : -1e-9),
42 inv_denom * size[2] + (incl[2] ? 1e-9 : -1e-9));
43 }
44
45 // cf. Ccp4Base::get_extent()
48 box.minimum = Fractional(-1e-9, -1e-9, -1e-9);
49 box.maximum = get_upper_limit();
50 return box;
51 }
52
53 std::array<int,3> uvw_end(const GridMeta& meta) const {
54 if (meta.axis_order != AxisOrder::XYZ)
55 fail("grid is not fully setup");
57 // upper limit is positive and never exact integer
58 auto iceil = [](double x) { return int(x) + 1; };
59 return {iceil(f.x * meta.nu), iceil(f.y * meta.nv), iceil(f.z * meta.nw)};
60 }
61};
62
63// Returns asu brick upper bound. Lower bound is always (0,0,0).
64// Brute force method that considers 8^3 sizes.
66 if (sg == nullptr)
67 fail("Missing space group");
68
69 using Point = std::array<int, 3>;
70 static_assert(AsuBrick::denom == 24, "");
71 const int allowed_sizes[] = {3, 4, 6, 8, 12, 16, 18, 24};
72 const GroupOps gops = sg->operations();
73 const int n_ops = gops.order();
74
76 grid.spacegroup = sg;
77 // Testing with grid size 24 can't distinguish x<=1/8 and x<1/6,
78 // but it happens to give the same results as grid size 48 for all
79 // space groups tabulated in gemmi, so it's fine.
80 // M=1 -> grid size 24; M=2 -> grid size 48
81 constexpr int M = 1;
83 const std::vector<GridOp> ops = grid.get_scaled_ops_except_id();
84
85 auto is_asu_brick = [&](const AsuBrick& brick, bool check_size) -> bool {
86 // The most effective screening points for grid size 24.
87 // These points were determined by doing calculations for all space groups
88 // from gemmi::spacegroup_tables.
89 static const Point size_checkpoints[] = {
90 {7, 17, 7}, // eliminates 9866 out of 14726 wrong candidates
91 {11, 1, 23}, // eliminates 2208 out of the rest
92 {11, 10, 11}, // eliminates 1108
93 {19, 1, 1}, // eliminates 665
94 {3, 7, 19}, // eliminates 305
95 {13, 9, 3},
96 {23, 23, 23},
97 {21, 10, 7},
98 {11, 22, 1},
99 {9, 15, 3},
100 {5, 17, 23},
101 {1, 5, 23},
102 {5, 7, 17},
103 {7, 5, 15},
104 {20, 4, 5},
105 {9, 23, 23},
106 {9, 13, 13}, // eliminates the last wrong candidates
107 };
108 static const Point boundary_checkpoints[] = {
109 {6, 18, 18},
110 {12, 12, 12},
111 {8, 16, 0},
112 {0, 12, 3},
113 {12, 3, 2},
114 {0, 0, 12},
115 {3, 12, 6},
116 {16, 8, 9},
117 {9, 21, 21},
118 {8, 16, 6},
119 {12, 12, 7},
120 {20, 15, 0},
121 {12, 0, 1},
122 {16, 0, 0},
123 {0, 6, 18},
124 // ...
125 };
126 if (M == 1) {
127 auto is_in = [&](const Point& p) {
128 return p[0] < brick.size[0] + int(brick.incl[0])
129 && p[1] < brick.size[1] + int(brick.incl[1])
130 && p[2] < brick.size[2] + int(brick.incl[2]);
131 };
132 auto check_point = [&](const Point& point) {
133 if (is_in(point))
134 return true;
135 for (const GridOp& op : ops) {
136 Point t = op.apply(point[0], point[1], point[2]);
137 grid.index_n_ref(t[0], t[1], t[2]);
138 if (is_in(t))
139 return true;
140 }
141 return false;
142 };
143
144 auto it = check_size ? std::begin(size_checkpoints) : std::begin(boundary_checkpoints);
145 auto end = check_size ? std::end(size_checkpoints) : std::end(boundary_checkpoints);
146 for (; it != end; ++it)
147 if (!check_point(*it))
148 return false;
149 }
150
151 // full check (it could be skipped for M==1 and check_size)
152 grid.fill(0);
153 int w_lim = M * brick.size[2] + int(brick.incl[2]);
154 int v_lim = M * brick.size[1] + int(brick.incl[1]);
155 int u_lim = M * brick.size[0] + int(brick.incl[0]);
156 for (int w = 0; w < w_lim; ++w)
157 for (int v = 0; v < v_lim; ++v)
158 for (int u = 0; u < u_lim; ++u) {
159 size_t idx = grid.index_q(u, v, w);
160 if (grid.data[idx] == 0) {
161 grid.data[idx] = 1;
162 for (const GridOp& op : ops) {
163 Point t = op.apply(u, v, w);
164 size_t mate_idx = grid.index_n(t[0], t[1], t[2]);
165 grid.data[mate_idx] = 1;
166 }
167 }
168 }
169#if 0
170 // this code was used for determining checkpoints
171 bool found = false;
172 for (size_t n = 0; n != grid.data.size(); ++n)
173 if (grid.data[n] == 0) {
174 auto p = grid.index_to_point(n);
175 printf("[debug1] %d %d %d is missing\n", p.u, p.v, p.w);
176 found = true;
177 }
178 if (found)
179 printf("[debug2] checkpoints failed\n");
180#endif
181 return !in_vector(std::int8_t(0), grid.data);
182 };
183
184 std::vector<AsuBrick> possible_bricks;
185 for (int a : allowed_sizes)
186 for (int b : allowed_sizes)
187 for (int c : allowed_sizes) {
188 AsuBrick brick(a, b, c);
190 possible_bricks.push_back(brick);
191 }
192 // the last item is the full unit cell, no need to check it
193 possible_bricks.pop_back();
194 // if two bricks have the same size, prefer the more cube-shaped one
195 std::stable_sort(possible_bricks.begin(), possible_bricks.end(),
196 [](const AsuBrick& a, const AsuBrick& b) {
197 return a.volume < b.volume ||
198 (a.volume == b.volume && a.size[0] + a.size[1] + a.size[2] <
199 b.size[0] + b.size[1] + b.size[2]);
200 });
202 if (is_asu_brick(brick, true)) {
203 for (int i = 0; i < 3; ++i) {
204 if (brick.incl[i] && brick.size[i] != 4) {
205 brick.incl[i] = false;
206 if (!is_asu_brick(brick, false))
207 brick.incl[i] = true;
208 }
209 }
210 return brick;
211 }
213}
214
215
216template<typename T, typename V=std::int8_t> struct MaskedGrid {
217 std::vector<V> mask;
219
220 struct iterator {
222 const std::vector<V>& mask_ref;
223 iterator(typename GridBase<T>::iterator it, const std::vector<V>& mask)
226 do {
228 } while (grid_iterator.index != mask_ref.size() && mask_ref[grid_iterator.index] != 0);
229 return *this;
230 }
232 bool operator==(const iterator &o) const { return grid_iterator == o.grid_iterator; }
233 bool operator!=(const iterator &o) const { return grid_iterator != o.grid_iterator; }
234 };
235 iterator begin() { return {grid->begin(), mask}; }
236 iterator end() { return {grid->end(), mask}; }
237};
238
239template<typename V=std::int8_t>
240std::vector<V> get_asu_mask(const GridMeta& grid) {
241 std::vector<V> mask(grid.point_count(), 2);
242 std::vector<GridOp> ops = grid.get_scaled_ops_except_id();
243 auto end = find_asu_brick(grid.spacegroup).uvw_end(grid);
244 for (int w = 0; w < end[2]; ++w)
245 for (int v = 0; v < end[1]; ++v)
246 for (int u = 0; u < end[0]; ++u) {
247 size_t idx = grid.index_q(u, v, w);
248 if (mask[idx] == 2) {
249 mask[idx] = 0;
250 for (const GridOp& op : ops) {
251 std::array<int, 3> t = op.apply(u, v, w);
252 size_t mate_idx = grid.index_n(t[0], t[1], t[2]);
253 // grid point can be on special position
254 if (mate_idx != idx)
255 mask[mate_idx] = 1;
256 }
257 }
258 }
259 if (std::find(mask.begin(), mask.end(), 2) != mask.end())
260 fail("get_asu_mask(): internal error");
261 return mask;
262}
263
264template<typename T>
266 return {get_asu_mask(grid), &grid};
267}
268
269
270// Calculating bounding box (brick) with the data (non-zero and non-NaN).
271
272namespace impl {
273// find the shortest span (possibly wrapped) that contains all true values
274inline std::pair<int, int> trim_false_values(const std::vector<bool>& vec) {
275 const int n = (int) vec.size();
276 assert(n != 0);
277 std::pair<int, int> span{n, n}; // return value for all-true vector
278 int max_trim = 0;
279 // first calculate the wrapped span (initial + final non-zero values)
280 if (!vec[0] || !vec[n-1]) {
281 // determine trailing-false length and store it in span.first
282 while (span.first != 0 && !vec[span.first-1])
283 --span.first;
284 if (span.first == 0) // all-false vector
285 return span; // i.e. {0,n}
286 // determine leading-false length and store it in span.second
287 span.second = 0;
288 while (span.second != n && !vec[span.second])
289 ++span.second;
290 max_trim = span.second + (n - span.first);
291 }
292 for (int start = 0; ;) {
293 for (;;) {
294 if (start == n)
295 return span;
296 if (!vec[start])
297 break;
298 ++start;
299 }
300 int end = start + 1;
301 while (end != n && !vec[end])
302 ++end;
303 if (end - start > max_trim) {
304 max_trim = end - start;
305 span.first = start;
306 span.second = end;
307 }
308 start = end;
309 }
310 unreachable();
311}
312} // namespace impl
313
314// Get the smallest box with non-zero (and non-NaN) values.
315template<typename T>
317 grid.check_not_empty();
318 std::vector<bool> nonzero[3];
319 nonzero[0].resize(grid.nu, false);
320 nonzero[1].resize(grid.nv, false);
321 nonzero[2].resize(grid.nw, false);
322 size_t idx = 0;
323 for (int w = 0; w != grid.nw; ++w)
324 for (int v = 0; v != grid.nv; ++v)
325 for (int u = 0; u != grid.nu; ++u, ++idx) {
326 T val = grid.data[idx];
327 if (!(impl::is_nan(val) || val == 0)) {
328 nonzero[0][u] = true;
329 nonzero[1][v] = true;
330 nonzero[2][w] = true;
331 }
332 }
334 for (int i = 0; i < 3; ++i) {
335 auto span = impl::trim_false_values(nonzero[i]);
336 double inv_n = 1.0 / nonzero[i].size();
337 box.minimum.at(i) = (span.second - 0.5) * inv_n - int(span.second >= span.first);
338 box.maximum.at(i) = (span.first - 0.5) * inv_n;
339 }
340 return box;
341}
342
343} // namespace gemmi
344#endif
3d grids used by CCP4 maps, cell-method search and hkl data.
MaskedGrid< T > masked_asu(Grid< T > &grid)
Definition asumask.hpp:265
GEMMI_DLL void append_op_fraction(std::string &s, int w)
bool in_vector(const T &x, const std::vector< T > &v)
Definition util.hpp:242
AsuBrick find_asu_brick(const SpaceGroup *sg)
Definition asumask.hpp:65
Box< Fractional > get_nonzero_extent(const GridBase< T > &grid)
Definition asumask.hpp:316
void fail(const std::string &msg)
Definition fail.hpp:59
void unreachable()
Definition fail.hpp:80
std::vector< V > get_asu_mask(const GridMeta &grid)
Definition asumask.hpp:240
std::array< int, 3 > size
Definition asumask.hpp:18
Box< Fractional > get_extent() const
Definition asumask.hpp:46
Fractional get_upper_limit() const
Definition asumask.hpp:38
std::array< bool, 3 > incl
Definition asumask.hpp:19
static constexpr int denom
Definition asumask.hpp:17
std::string str() const
Definition asumask.hpp:25
std::array< int, 3 > uvw_end(const GridMeta &meta) const
Definition asumask.hpp:53
AsuBrick(int a, int b, int c)
Definition asumask.hpp:22
Fractional coordinates.
Definition unitcell.hpp:50
grid coordinates (modulo size) and a pointer to value
Definition grid.hpp:240
iterator end()
Definition grid.hpp:305
iterator begin()
Definition grid.hpp:304
The base of Grid classes that does not depend on stored data type.
Definition grid.hpp:168
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(int u, int v, int w) const
Quick(est) index function, but works only if `0 <= u < nu`, etc.
Definition grid.hpp:210
AxisOrder axis_order
Definition grid.hpp:172
const SpaceGroup * spacegroup
Definition grid.hpp:170
std::vector< GridOp > get_scaled_ops_except_id() const
Definition grid.hpp:184
size_t point_count() const
Definition grid.hpp:174
int order() const
Definition symmetry.hpp:258
bool operator!=(const iterator &o) const
Definition asumask.hpp:233
iterator(typename GridBase< T >::iterator it, const std::vector< V > &mask)
Definition asumask.hpp:223
GridBase< T >::Point operator*()
Definition asumask.hpp:231
const std::vector< V > & mask_ref
Definition asumask.hpp:222
GridBase< T >::iterator grid_iterator
Definition asumask.hpp:221
bool operator==(const iterator &o) const
Definition asumask.hpp:232
iterator begin()
Definition asumask.hpp:235
Grid< T > * grid
Definition asumask.hpp:218
iterator end()
Definition asumask.hpp:236
std::vector< V > mask
Definition asumask.hpp:217
Utilities. Mostly for working with strings and vectors.