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