Gemmi C++ API
Loading...
Searching...
No Matches
symmetry.hpp
Go to the documentation of this file.
1// Copyright 2017-2019 Global Phasing Ltd.
2//
3// Crystallographic Symmetry. Space Groups. Coordinate Triplets.
4//
5// If this is all that you need from Gemmi you can just copy this file,
6// src/symmetry.cpp fail.hpp and LICENSE.txt to your project.
7
8#ifndef GEMMI_SYMMETRY_HPP_
9#define GEMMI_SYMMETRY_HPP_
10
11#include <cstdlib> // for strtol, abs
12#include <array>
13#include <algorithm> // for sort, remove
14#include <functional> // for hash
15#include <stdexcept> // for invalid_argument
16#include <string>
17#include <tuple> // for tie
18#include <vector>
19
20#include "fail.hpp" // for fail, unreachable
21
22namespace gemmi {
23
24// OP
25
26// Op is a symmetry operation, or a change-of-basic transformation,
27// or a different operation of similar kind.
28// Both "rotation" matrix and translation vector are fractional, with DEN
29// used as the denominator.
30struct GEMMI_DLL Op {
31 static constexpr int DEN = 24; // 24 to handle 1/8 in change-of-basis
32 typedef std::array<std::array<int, 3>, 3> Rot;
33 typedef std::array<int, 3> Tran;
34
37 char notation = ' ';
38
39 bool is_hkl() const { return notation == 'h'; }
40
41 Op as_hkl() const {
42 return is_hkl() ? *this : Op{rot, {0,0,0}, 'h'};
43 }
44 Op as_xyz() const {
45 return is_hkl() ? Op{rot, {0,0,0}, 'x'} : *this;
46 }
47
48 std::string triplet(char style=' ') const;
49
50 Op inverse() const;
51
53 Op::Tran t = tran;
54 for (int i = 0; i != 3; ++i) {
55 if (t[i] >= DEN) // elements need to be in [0,DEN)
56 t[i] %= DEN;
57 else if (t[i] < 0)
58 t[i] = ((t[i] + 1) % DEN) + DEN - 1;
59 }
60 return t;
61 }
62
63 // If the translation points outside of the unit cell, wrap it.
64 Op& wrap() {
65 tran = wrapped_tran();
66 return *this;
67 }
68
69 Op& translate(const Tran& a) {
70 for (int i = 0; i != 3; ++i)
71 tran[i] += a[i];
72 return *this;
73 }
74
75 Op translated(const Tran& a) const { return Op(*this).translate(a); }
76
77 Op add_centering(const Tran& a) const { return translated(a).wrap(); }
78
79 Rot negated_rot() const {
80 return {{{-rot[0][0], -rot[0][1], -rot[0][2]},
81 {-rot[1][0], -rot[1][1], -rot[1][2]},
82 {-rot[2][0], -rot[2][1], -rot[2][2]}}};
83 }
84
85 static Rot transpose(const Rot& rot) {
86 return {{{rot[0][0], rot[1][0], rot[2][0]},
87 {rot[0][1], rot[1][1], rot[2][1]},
88 {rot[0][2], rot[1][2], rot[2][2]}}};
89 }
90 Rot transposed_rot() const { return transpose(rot); }
91
92 // DEN^3 for rotation, -DEN^3 for rotoinversion
93 int det_rot() const {
94 return rot[0][0] * (rot[1][1] * rot[2][2] - rot[1][2] * rot[2][1])
95 - rot[0][1] * (rot[1][0] * rot[2][2] - rot[1][2] * rot[2][0])
96 + rot[0][2] * (rot[1][0] * rot[2][1] - rot[1][1] * rot[2][0]);
97 }
98
99 // Rotation-part type based on Table 1 in RWGK, Acta Cryst. A55, 383 (1999)
100 int rot_type() const {
101 int det = det_rot();
102 int tr_den = rot[0][0] + rot[1][1] + rot[2][2];
103 int tr = tr_den / DEN;
104 const int table[] = {0, 0, 2, 3, 4, 6, 1};
105 if (std::abs(det) == DEN * DEN * DEN && tr * DEN == tr_den && std::abs(tr) <= 3)
106 return det > 0 ? table[3 + tr] : -table[3 - tr];
107 return 0;
108 }
109
110 Op combine(const Op& b) const {
111 if (is_hkl() != b.is_hkl())
112 fail("can't combine real- and reciprocal-space Op");
113 Op r;
114 for (int i = 0; i != 3; ++i) {
115 r.tran[i] = tran[i] * Op::DEN;
116 for (int j = 0; j != 3; ++j) {
117 r.rot[i][j] = (rot[i][0] * b.rot[0][j] +
118 rot[i][1] * b.rot[1][j] +
119 rot[i][2] * b.rot[2][j]) / Op::DEN;
120 r.tran[i] += rot[i][j] * b.tran[j];
121 }
122 r.tran[i] /= Op::DEN;
123 }
124 r.notation = notation;
125 return r;
126 }
127
128 std::array<double, 3> apply_to_xyz(const std::array<double, 3>& xyz) const {
129 if (is_hkl())
130 fail("can't apply reciprocal-space Op to xyz");
131 std::array<double, 3> out;
132 for (int i = 0; i != 3; ++i)
133 out[i] = (rot[i][0] * xyz[0] + rot[i][1] * xyz[1] + rot[i][2] * xyz[2] +
134 tran[i]) / Op::DEN;
135 return out;
136 }
137
138 // Miller is defined in the same way in namespace gemmi in unitcell.hpp
139 using Miller = std::array<int, 3>;
140
142 Miller r;
143 for (int i = 0; i != 3; ++i)
144 r[i] = (rot[0][i] * hkl[0] + rot[1][i] * hkl[1] + rot[2][i] * hkl[2]);
145 return r;
146 }
147 static Miller divide_hkl_by_DEN(const Miller& hkl) {
148 return {{ hkl[0] / DEN, hkl[1] / DEN, hkl[2] / DEN }};
149 }
150 Miller apply_to_hkl(const Miller& hkl) const {
151 return divide_hkl_by_DEN(apply_to_hkl_without_division(hkl));
152 }
153
154 double phase_shift(const Miller& hkl) const {
155 constexpr double mult = -2 * 3.1415926535897932384626433832795 / Op::DEN;
156 return mult * (hkl[0] * tran[0] + hkl[1] * tran[1] + hkl[2] * tran[2]);
157 }
158
159 std::array<std::array<int, 4>, 4> int_seitz() const {
160 std::array<std::array<int, 4>, 4> t;
161 for (int i = 0; i < 3; ++i)
162 t[i] = { rot[i][0], rot[i][1], rot[i][2], tran[i] };
163 t[3] = { 0, 0, 0, 1 };
164 return t;
165 }
166
167 std::array<std::array<double, 4>, 4> float_seitz() const {
168 std::array<std::array<double, 4>, 4> t;
169 double m = 1.0 / Op::DEN;
170 for (int i = 0; i < 3; ++i)
171 t[i] = { m * rot[i][0], m * rot[i][1], m * rot[i][2], m * tran[i] };
172 t[3] = { 0., 0., 0., 1. };
173 return t;
174 }
175
176 static constexpr Op identity() {
177 return {{{{DEN,0,0}, {0,DEN,0}, {0,0,DEN}}}, {0,0,0}, ' '};
178 }
179 static constexpr Op::Rot inversion_rot() {
180 return {{{-DEN,0,0}, {0,-DEN,0}, {0,0,-DEN}}};
181 }
182 bool operator<(const Op& rhs) const {
183 return std::tie(rot, tran) < std::tie(rhs.rot, rhs.tran);
184 }
185};
186
187inline bool operator==(const Op& a, const Op& b) {
188 return a.rot == b.rot && a.tran == b.tran;
189}
190inline bool operator!=(const Op& a, const Op& b) { return !(a == b); }
191
192inline Op operator*(const Op& a, const Op& b) { return a.combine(b).wrap(); }
193inline Op& operator*=(Op& a, const Op& b) { a = a * b; return a; }
194
195inline Op Op::inverse() const {
196 int detr = det_rot();
197 if (detr == 0)
198 fail("cannot invert matrix: " + Op{rot, {0,0,0}, notation}.triplet());
199 int d2 = Op::DEN * Op::DEN;
200 Op inv;
201 inv.rot[0][0] = d2 * (rot[1][1] * rot[2][2] - rot[2][1] * rot[1][2]) / detr;
202 inv.rot[0][1] = d2 * (rot[0][2] * rot[2][1] - rot[0][1] * rot[2][2]) / detr;
203 inv.rot[0][2] = d2 * (rot[0][1] * rot[1][2] - rot[0][2] * rot[1][1]) / detr;
204 inv.rot[1][0] = d2 * (rot[1][2] * rot[2][0] - rot[1][0] * rot[2][2]) / detr;
205 inv.rot[1][1] = d2 * (rot[0][0] * rot[2][2] - rot[0][2] * rot[2][0]) / detr;
206 inv.rot[1][2] = d2 * (rot[1][0] * rot[0][2] - rot[0][0] * rot[1][2]) / detr;
207 inv.rot[2][0] = d2 * (rot[1][0] * rot[2][1] - rot[2][0] * rot[1][1]) / detr;
208 inv.rot[2][1] = d2 * (rot[2][0] * rot[0][1] - rot[0][0] * rot[2][1]) / detr;
209 inv.rot[2][2] = d2 * (rot[0][0] * rot[1][1] - rot[1][0] * rot[0][1]) / detr;
210 for (int i = 0; i != 3; ++i)
211 inv.tran[i] = (-tran[0] * inv.rot[i][0]
212 -tran[1] * inv.rot[i][1]
213 -tran[2] * inv.rot[i][2]) / Op::DEN;
214 inv.notation = notation;
215 return inv;
216}
217
218// inverse of Op::float_seitz()
219GEMMI_DLL Op seitz_to_op(const std::array<std::array<double,4>, 4>& t);
220
221// helper function for use in AsuBrick::str()
222GEMMI_DLL void append_op_fraction(std::string& s, int w);
223
224// TRIPLET -> OP
225GEMMI_DLL std::array<int, 4> parse_triplet_part(const std::string& s, char& notation,
226 double* decimal_fract=nullptr);
227GEMMI_DLL Op parse_triplet(const std::string& s, char notation=' ');
228
229// GROUPS OF OPERATIONS
230
231// corresponds to Table A1.4.2.2 in ITfC vol.B (edition 2010)
232inline std::vector<Op::Tran> centring_vectors(char centring_type) {
233 constexpr int h = Op::DEN / 2;
234 constexpr int t = Op::DEN / 3;
235 constexpr int d = 2 * t;
236 // note: find_centering() depends on the order of operations in vector
237 switch (centring_type & ~0x20) {
238 case 'P': return {{0, 0, 0}};
239 case 'A': return {{0, 0, 0}, {0, h, h}};
240 case 'B': return {{0, 0, 0}, {h, 0, h}};
241 case 'C': return {{0, 0, 0}, {h, h, 0}};
242 case 'I': return {{0, 0, 0}, {h, h, h}};
243 case 'R': return {{0, 0, 0}, {d, t, t}, {t, d, d}};
244 // hall_symbols.html has no H, ITfC 2010 has no S and T
245 case 'H': return {{0, 0, 0}, {d, t, 0}, {t, d, 0}};
246 case 'S': return {{0, 0, 0}, {t, t, d}, {d, t, d}};
247 case 'T': return {{0, 0, 0}, {t, d, t}, {d, t, d}};
248 case 'F': return {{0, 0, 0}, {0, h, h}, {h, 0, h}, {h, h, 0}};
249 default: fail("not a centring type: ", centring_type);
250 }
251}
252
253
254struct GroupOps {
255 std::vector<Op> sym_ops;
256 std::vector<Op::Tran> cen_ops;
257
258 int order() const { return static_cast<int>(sym_ops.size()*cen_ops.size()); }
259
261 void add_missing_elements_part2(const std::vector<Op>& gen,
262 size_t max_size, bool ignore_bad_gen);
263
265 size_t init_size = sym_ops.size();
266 sym_ops.reserve(2 * init_size);
267 for (const Op& op : sym_ops) {
268 Op::Rot neg = op.negated_rot();
269 if (find_by_rotation(neg)) {
270 sym_ops.resize(init_size);
271 return false;
272 }
273 sym_ops.push_back({neg, op.tran, op.notation});
274 }
275 return true;
276 }
277
278 char find_centering() const {
279 if (cen_ops.size() == 1 && cen_ops[0] == Op::Tran{0, 0, 0})
280 return 'P';
281 std::vector<Op::Tran> trans = cen_ops;
282 std::sort(trans.begin(), trans.end());
283 for (char c : {'A', 'B', 'C', 'I', 'F', 'R', 'H', 'S', 'T'}) {
284 std::vector<Op::Tran> c_vectors = centring_vectors(c);
285 if (c == 'R' || c == 'H') // these two are returned not sorted
286 std::swap(c_vectors[1], c_vectors[2]);
287 if (trans == c_vectors)
288 return c;
289 }
290 return 0;
291 }
292
294 for (Op& op : sym_ops)
295 if (op.rot == r)
296 return &op;
297 return nullptr;
298 }
299
300 const Op* find_by_rotation(const Op::Rot& r) const {
301 return const_cast<GroupOps*>(this)->find_by_rotation(r);
302 }
303
304 bool is_centrosymmetric() const {
305 return find_by_rotation(Op::inversion_rot()) != nullptr;
306 }
307
308 bool is_reflection_centric(const Op::Miller& hkl) const {
309 Op::Miller mhkl = {{-Op::DEN * hkl[0], -Op::DEN * hkl[1], -Op::DEN * hkl[2]}};
310 for (const Op& op : sym_ops)
311 if (op.apply_to_hkl_without_division(hkl) == mhkl)
312 return true;
313 return false;
314 }
315
317 Op::Miller denh = {{Op::DEN * hkl[0], Op::DEN * hkl[1], Op::DEN * hkl[2]}};
318 int epsilon = 0;
319 for (const Op& op : sym_ops)
320 if (op.apply_to_hkl_without_division(hkl) == denh)
321 ++epsilon;
322 return epsilon;
323 }
324 int epsilon_factor(const Op::Miller& hkl) const {
325 return epsilon_factor_without_centering(hkl) * (int) cen_ops.size();
326 }
327
328 static bool has_phase_shift(const Op::Tran& c, const Op::Miller& hkl) {
329 return (hkl[0] * c[0] + hkl[1] * c[1] + hkl[2] * c[2]) % Op::DEN != 0;
330 }
331
332 bool is_systematically_absent(const Op::Miller& hkl) const {
333 for (auto i = cen_ops.begin() + 1; i != cen_ops.end(); ++i)
334 if (has_phase_shift(*i, hkl))
335 return true;
336 Op::Miller denh = {{Op::DEN * hkl[0], Op::DEN * hkl[1], Op::DEN * hkl[2]}};
337 for (auto op = sym_ops.begin() + 1; op != sym_ops.end(); ++op)
338 if (op->apply_to_hkl_without_division(hkl) == denh) {
339 for (const Op::Tran& c : cen_ops)
340 if (has_phase_shift({{op->tran[0] + c[0],
341 op->tran[1] + c[1],
342 op->tran[2] + c[2]}}, hkl))
343 return true;
344 }
345 return false;
346 }
347
348 void change_basis_impl(const Op& cob, const Op& inv) {
349 if (sym_ops.empty() || cen_ops.empty())
350 return;
351
352 // Apply change-of-basis to sym_ops.
353 // Ignore the first item in sym_ops -- it's identity.
354 for (auto op = sym_ops.begin() + 1; op != sym_ops.end(); ++op)
355 *op = cob.combine(*op).combine(inv).wrap();
356
357 // The number of centering vectors may be different.
358 // As an ad-hoc method (not proved to be robust) add lattice points
359 // from a super-cell.
360 int idet = inv.det_rot() / (Op::DEN * Op::DEN * Op::DEN);
361 if (idet > 1) {
362 std::vector<Op::Tran> new_cen_ops;
363 new_cen_ops.reserve(cen_ops.size() * idet * idet * idet);
364 for (int i = 0; i < idet; ++i)
365 for (int j = 0; j < idet; ++j)
366 for (int k = 0; k < idet; ++k)
367 for (Op::Tran& cen : cen_ops)
368 new_cen_ops.push_back({i * Op::DEN + cen[0],
369 j * Op::DEN + cen[1],
370 k * Op::DEN + cen[2]});
371 cen_ops.swap(new_cen_ops);
372 }
373
374 // Apply change-of-basis to centering vectors
375 Op cvec = Op::identity();
376 for (auto tr = cen_ops.begin() + 1; tr != cen_ops.end(); ++tr) {
377 cvec.tran = *tr;
378 *tr = cob.combine(cvec).combine(inv).wrap().tran;
379 }
380
381 // Remove redundant centering vectors.
382 for (int i = static_cast<int>(cen_ops.size()) - 1; i > 0; --i)
383 for (int j = i - 1; j >= 0; --j)
384 if (cen_ops[i] == cen_ops[j]) {
385 cen_ops.erase(cen_ops.begin() + i);
386 break;
387 }
388 }
389
390 void change_basis_forward(const Op& cob) { change_basis_impl(cob, cob.inverse()); }
391 void change_basis_backward(const Op& inv) { change_basis_impl(inv.inverse(), inv); }
392
393 std::vector<Op> all_ops_sorted() const {
394 std::vector<Op> ops;
395 ops.reserve(sym_ops.size() * cen_ops.size());
396 for (const Op& so : sym_ops)
397 for (const Op::Tran& co : cen_ops)
398 ops.push_back(so.add_centering(co));
399 std::sort(ops.begin(), ops.end());
400 return ops;
401 }
402
403 Op get_op(int n) const {
404 int n_cen = n / (int) sym_ops.size();
405 int n_sym = n % (int) sym_ops.size();
406 return sym_ops.at(n_sym).add_centering(cen_ops.at(n_cen));
407 }
408
409 bool is_same_as(const GroupOps& other) const {
410 if (cen_ops.size() != other.cen_ops.size() ||
411 sym_ops.size() != other.sym_ops.size())
412 return false;
413 return all_ops_sorted() == other.all_ops_sorted();
414 }
415
416 bool has_same_centring(const GroupOps& other) const {
417 if (cen_ops.size() != other.cen_ops.size())
418 return false;
419 if (std::is_sorted(cen_ops.begin(), cen_ops.end()) &&
420 std::is_sorted(other.cen_ops.begin(), other.cen_ops.end()))
421 return cen_ops == other.cen_ops;
422 std::vector<Op::Tran> v1 = cen_ops;
423 std::vector<Op::Tran> v2 = other.cen_ops;
424 std::sort(v1.begin(), v1.end());
425 std::sort(v2.begin(), v2.end());
426 return v1 == v2;
427 }
428
429 bool has_same_rotations(const GroupOps& other) const {
430 if (sym_ops.size() != other.sym_ops.size())
431 return false;
432 auto sorted_rotations = [](const GroupOps& g) {
433 std::vector<Op::Rot> r(g.sym_ops.size());
434 for (size_t i = 0; i != r.size(); ++i)
435 r[i] = g.sym_ops[i].rot;
436 std::sort(r.begin(), r.end());
437 return r;
438 };
439 return sorted_rotations(*this) == sorted_rotations(other);
440 }
441
442 // minimal multiplicity for real-space grid in each direction
443 // examples: 1,2,1 for P21, 1,1,6 for P61
444 std::array<int, 3> find_grid_factors() const {
445 const int T = Op::DEN;
446 int r[3] = {T, T, T};
447 for (Op op : *this)
448 for (int i = 0; i != 3; ++i)
449 if (op.tran[i] != 0 && op.tran[i] < r[i])
450 r[i] = op.tran[i];
451 return {T / r[0], T / r[1], T / r[2]};
452 }
453
454 bool are_directions_symmetry_related(int u, int v) const {
455 for (const Op& op : sym_ops)
456 if (op.rot[u][v] != 0)
457 return true;
458 return false;
459 }
460
461 // remove translation part of sym_ops
463 GroupOps r(*this);
464 for (Op& op : r.sym_ops)
465 op.tran[0] = op.tran[1] = op.tran[2] = 0;
466 return r;
467 }
468
469 struct Iter {
471 int n_sym, n_cen;
472 void operator++() {
473 if (++n_sym == (int) gops.sym_ops.size()) {
474 ++n_cen;
475 n_sym = 0;
476 }
477 }
478 Op operator*() const {
479 return gops.sym_ops.at(n_sym).translated(gops.cen_ops.at(n_cen)).wrap();
480 }
481 bool operator==(const Iter& other) const {
482 return n_sym == other.n_sym && n_cen == other.n_cen;
483 }
484 bool operator!=(const Iter& other) const { return !(*this == other); }
485 };
486
487 Iter begin() const { return {*this, 0, 0}; }
488 Iter end() const { return {*this, 0, (int) cen_ops.size()}; }
489};
490
491inline void GroupOps::add_missing_elements() {
492 // We always keep identity as sym_ops[0].
493 if (sym_ops.empty() || sym_ops[0] != Op::identity())
494 fail("oops");
495 if (sym_ops.size() == 1)
496 return;
497 constexpr size_t max_size = 1024;
498 // Below we assume that all centring vectors are already known (in cen_ops)
499 // so when checking for a new element we compare only the 3x3 matrix.
500 // Dimino's algorithm. https://physics.stackexchange.com/a/351400/95713
501 std::vector<Op> gen(sym_ops.begin() + 1, sym_ops.end());
502 sym_ops.resize(2);
503 const Op::Rot idrot = Op::identity().rot;
504 for (Op g = sym_ops[1] * sym_ops[1]; g.rot != idrot; g *= sym_ops[1]) {
505 sym_ops.push_back(g);
506 if (sym_ops.size() > max_size)
507 fail("Too many elements in the group - bad generators");
508 }
509 // the rest is in separate function b/c it's reused in twin.hpp
510 add_missing_elements_part2(gen, max_size, false);
511}
512
513inline void GroupOps::add_missing_elements_part2(const std::vector<Op>& gen,
514 size_t max_size, bool ignore_bad_gen) {
515 for (size_t i = 1; i < gen.size(); ++i) {
516 std::vector<Op> coset_repr(1, Op::identity());
517 size_t init_size = sym_ops.size();
518 for (;;) {
519 size_t len = coset_repr.size();
520 for (size_t j = 0; j != len; ++j) {
521 for (size_t n = 0; n != i + 1; ++n) {
522 Op sg = gen[n] * coset_repr[j];
523 if (find_by_rotation(sg.rot) == nullptr) {
524 sym_ops.push_back(sg);
525 for (size_t k = 1; k != init_size; ++k)
526 sym_ops.push_back(sg * sym_ops[k]);
527 coset_repr.push_back(sg);
528 }
529 }
530 }
531 if (len == coset_repr.size())
532 break;
533 if (sym_ops.size() > max_size) {
534 if (!ignore_bad_gen)
535 fail("Too many elements in the group - bad generators");
536 // ignore this generator and continue with the next one
537 sym_ops.resize(init_size);
538 break;
539 }
540 }
541 }
542}
543
544// Create GroupOps from Ops by separating centering vectors
545inline GroupOps split_centering_vectors(const std::vector<Op>& ops) {
546 const Op identity = Op::identity();
547 GroupOps go;
548 go.sym_ops.push_back(identity);
549 for (const Op& op : ops)
550 if (Op* old_op = go.find_by_rotation(op.rot)) {
551 Op::Tran tran = op.wrapped_tran();
552 if (op.rot == identity.rot) // pure shift
553 go.cen_ops.push_back(tran);
554 if (tran == identity.tran) // or rather |tran| < |old_op->tran| ?
555 old_op->tran = op.tran;
556 } else {
557 go.sym_ops.push_back(op);
558 }
559 return go;
560}
561
563
564inline GroupOps symops_from_hall(const char* hall) {
566 ops.add_missing_elements();
567 return ops;
568}
569
570// CRYSTAL SYSTEMS, POINT GROUPS AND LAUE CLASSES
571
572enum class CrystalSystem : unsigned char {
574};
575
577 static const char* names[7] = {
578 "triclinic", "monoclinic", "orthorhombic", "tetragonal",
579 "trigonal", "hexagonal", "cubic"
580 };
581 return names[static_cast<int>(system)];
582}
583
584enum class PointGroup : unsigned char {
585 C1=0, Ci, C2, Cs, C2h, D2, C2v, D2h, C4, S4, C4h, D4, C4v, D2d, D4h, C3,
586 C3i, D3, C3v, D3d, C6, C3h, C6h, D6, C6v, D3h, D6h, T, Th, O, Td, Oh
587};
588
589inline const char* point_group_hm(PointGroup pg) {
590 static const char hm_pointgroup_names[32][6] = {
591 "1", "-1", "2", "m", "2/m", "222", "mm2", "mmm",
592 "4", "-4", "4/m", "422", "4mm", "-42m", "4/mmm", "3",
593 "-3", "32", "3m", "-3m", "6", "-6", "6/m", "622",
594 "6mm", "-62m", "6/mmm", "23", "m-3", "432", "-43m", "m-3m",
595 };
596 return hm_pointgroup_names[static_cast<int>(pg)];
597}
598
599// http://reference.iucr.org/dictionary/Laue_class
600enum class Laue : unsigned char {
601 L1=0, L2m, Lmmm, L4m, L4mmm, L3, L3m, L6m, L6mmm, Lm3, Lm3m
602};
603
605 static const Laue laue[32] = {
606 Laue::L1, Laue::L1,
607 Laue::L2m, Laue::L2m, Laue::L2m,
608 Laue::Lmmm, Laue::Lmmm, Laue::Lmmm,
609 Laue::L4m, Laue::L4m, Laue::L4m,
610 Laue::L4mmm, Laue::L4mmm, Laue::L4mmm, Laue::L4mmm,
611 Laue::L3, Laue::L3,
612 Laue::L3m, Laue::L3m, Laue::L3m,
613 Laue::L6m, Laue::L6m, Laue::L6m,
614 Laue::L6mmm, Laue::L6mmm, Laue::L6mmm, Laue::L6mmm,
615 Laue::Lm3, Laue::Lm3,
616 Laue::Lm3m, Laue::Lm3m, Laue::Lm3m,
617 };
618 return laue[static_cast<int>(pg)];
619}
620
621// return centrosymmetric pointgroup from the Laue class
623 static const PointGroup pg[11] = {
624 PointGroup::Ci, PointGroup::C2h, PointGroup::D2h, PointGroup::C4h,
625 PointGroup::D4h, PointGroup::C3i, PointGroup::D3d, PointGroup::C6h,
626 PointGroup::D6h, PointGroup::Th, PointGroup::Oh
627 };
628 return pg[static_cast<int>(laue)];
629}
630
631inline const char* laue_class_str(Laue laue) {
633}
634
636 static const CrystalSystem crystal_systems[11] = {
637 CrystalSystem::Triclinic,
638 CrystalSystem::Monoclinic,
639 CrystalSystem::Orthorhombic,
640 CrystalSystem::Tetragonal, CrystalSystem::Tetragonal,
641 CrystalSystem::Trigonal, CrystalSystem::Trigonal,
642 CrystalSystem::Hexagonal, CrystalSystem::Hexagonal,
643 CrystalSystem::Cubic, CrystalSystem::Cubic
644 };
645 return crystal_systems[static_cast<int>(laue)];
646}
647
651
653 // 0x20=Sohncke, 0x40=enantiomorphic, 0x80=symmorphic
654 enum : unsigned char { S=0x20, E=(0x20|0x40), Y=0x80, Z=(0x20|0x80) };
655 static const unsigned char indices[230] = {
656 0|Z, 1|Y, 2|Z, 2|S, 2|Z, 3|Y, 3, 3|Y, 3, 4|Y, // 1-10
657 4, 4|Y, 4, 4, 4, 5|Z, 5|S, 5|S, 5|S, 5|S, // 11-20
658 5|Z, 5|Z, 5|Z, 5|S, 6|Y, 6, 6, 6, 6, 6, // 21-30
659 6, 6, 6, 6, 6|Y, 6, 6, 6|Y, 6, 6, // 31-40
660 6, 6|Y, 6, 6|Y, 6, 6, 7|Y, 7, 7, 7, // 41-50
661 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 51-60
662 7, 7, 7, 7, 7|Y, 7, 7, 7, 7|Y, 7, // 61-70
663 7|Y, 7, 7, 7, 8|Z, 8|E, 8|S, 8|E, 8|Z, 8|S, // 71-80
664 9|Y, 9|Y, 10|Y, 10, 10, 10, 10|Y, 10, 11|Z, 11|S, // 81-90
665 11|E, 11|E, 11|S, 11|S, 11|E, 11|E, 11|Z, 11|S, 12|Y, 12, // 91-100
666 12, 12, 12, 12, 12, 12, 12|Y, 12, 12, 12, // 101-110
667 13|Y, 13, 13, 13, 13|Y, 13, 13, 13, 13|Y, 13, // 111-120
668 13|Y, 13, 14|Y, 14, 14, 14, 14, 14, 14, 14, // 121-130
669 14, 14, 14, 14, 14, 14, 14, 14, 14|Y, 14, // 131-140
670 14, 14, 15|Z, 15|E, 15|E, 15|Z, 16|Y, 16|Y, 17|Z, 17|Z, // 141-150
671 17|E, 17|E, 17|E, 17|E, 17|Z, 18|Y, 18|Y, 18, 18, 18|Y, // 151-160
672 18, 19|Y, 19, 19|Y, 19, 19|Y, 19, 20|Z, 20|E, 20|E, // 161-170
673 20|E, 20|E, 20|S, 21|Y, 22|Y, 22, 23|Z, 23|E, 23|E, 23|E, // 171-180
674 23|E, 23|S, 24|Y, 24, 24, 24, 25|Y, 25, 25|Y, 25, // 181-190
675 26|Y, 26, 26, 26, 27|Z, 27|Z, 27|Z, 27|S, 27|S, 28|Y, // 191-200
676 28, 28|Y, 28, 28|Y, 28, 28, 29|Z, 29|S, 29|Z, 29|S, // 201-210
677 29|Z, 29|E, 29|E, 29|S, 30|Y, 30|Y, 30|Y, 30, 30, 30, // 211-220
678 31|Y, 31, 31, 31, 31|Y, 31, 31, 31, 31|Y, 31 // 221-230
679 };
680 return indices[space_group_number-1];
681}
682
685 return static_cast<PointGroup>(n & 0x1f);
686}
687
688// true for 65 Sohncke (non-enantiogenic) space groups
691}
692
693// true for 22 space groups (11 enantiomorphic pairs)
697
698// true for 73 space groups
702
708 constexpr int D = Op::DEN;
709 switch (space_group_number) {
710 case 43: return {D/8, D/8, 0};
711 case 80: return {D/4, 0, 0};
712 case 98: return {D/4, 0, D/8};
713 case 109: return {D/4, 0, 0};
714 case 110: return {D/4, 0, 0};
715 case 122: return {D/4, 0, D/8};
716 case 210: return {D/8, D/8, D/8};
717 default: return {0, 0, 0};
718 }
719}
720
721GEMMI_DLL const char* get_basisop(int basisop_idx);
722
723
724// Returns a change-of-basis operator for centred -> primitive transformation.
725// The same operator as inverse of z2p_op in sgtbx.
726inline Op::Rot centred_to_primitive(char centring_type) {
727 constexpr int D = Op::DEN;
728 constexpr int H = Op::DEN / 2;
729 constexpr int T = Op::DEN / 3;
730 switch (centring_type) {
731 case 'P': return {{{D,0,0}, {0,D,0}, {0,0,D}}};
732 case 'A': return {{{-D,0,0}, {0,-H,H}, {0,H,H}}};
733 case 'B': return {{{-H,0,H}, {0,-D,0}, {H,0,H}}};
734 case 'C': return {{{H,H,0}, {H,-H,0}, {0,0,-D}}};
735 case 'I': return {{{-H,H,H}, {H,-H,H}, {H,H,-H}}};
736 case 'R': return {{{2*T,-T,-T}, {T,T,-2*T}, {T,T,T}}};
737 case 'H': return {{{2*T,-T,0}, {T,T,0}, {0,0,D}}}; // not used normally
738 case 'F': return {{{0,H,H}, {H,0,H}, {H,H,0}}};
739 default: fail("not a centring type: ", centring_type);
740 }
741}
742
743
744// LIST OF CRYSTALLOGRAPHIC SPACE GROUPS
745
746struct SpaceGroup { // typically 44 bytes
748 int ccp4;
749 char hm[11]; // Hermann-Mauguin (international) notation
750 char ext;
751 char qualifier[5];
752 char hall[15];
754
755 std::string xhm() const {
756 std::string ret = hm;
757 if (ext) {
758 ret += ':';
759 ret += ext;
760 }
761 return ret;
762 }
763
764 char centring_type() const { return ext == 'R' ? 'P' : hm[0]; }
765
766 // (old) CCP4 spacegroup names start with H for hexagonal setting
767 char ccp4_lattice_type() const { return ext == 'H' ? 'H' : hm[0]; }
768
769 // P 1 2 1 -> P2, but P 1 1 2 -> P112. R 3:H -> H3.
770 std::string short_name() const {
771 std::string s(hm);
772 size_t len = s.size();
773 if (len > 6 && s[2] == '1' && s[len - 2] == ' ' && s[len - 1] == '1')
774 s = s[0] + s.substr(4, len - 4 - 2);
775 if (ext == 'H')
776 s[0] = 'H';
777 s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
778 return s;
779 }
780
781 // As explained in Phenix newsletter CCN_2011_01.pdf#page=12
782 // the PDB uses own, non-standard symbols for rhombohedral space groups.
783 std::string pdb_name() const {
784 std::string s;
785 s += ccp4_lattice_type();
786 s += hm+1;
787 return s;
788 }
789
790 bool is_sohncke() const { return gemmi::is_sohncke(number); }
791 bool is_enantiomorphic() const { return gemmi::is_enantiomorphic(number); }
792 bool is_symmorphic() const { return gemmi::is_symmorphic(number); }
793 PointGroup point_group() const { return gemmi::point_group(number); }
794 const char* point_group_hm() const {
796 }
798 const char* laue_str() const { return laue_class_str(laue_class()); }
802 const char* crystal_system_str() const {
804 }
805 bool is_centrosymmetric() const {
806 return laue_to_pointgroup(laue_class()) == point_group();
807 }
808
811 if (crystal_system() == CrystalSystem::Monoclinic)
812 return qualifier[qualifier[0] == '-' ? 1 : 0];
813 return '\0';
814 }
815
816 const char* basisop_str() const { return get_basisop(basisop_idx); }
817 Op basisop() const { return parse_triplet(basisop_str()); }
818 bool is_reference_setting() const { return basisop_idx == 0; }
819
821 return {gemmi::centred_to_primitive(centring_type()), {0,0,0}, 'x'};
822 }
823
826 if (is_centrosymmetric())
827 return Op::identity();
829 Op op{Op::inversion_rot(), {2*t[0], 2*t[1], 2*t[2]}, 'x'};
830 if (!is_reference_setting()) {
831 Op b = basisop();
832 op = b.combine(op).combine(b.inverse());
833 }
834 return op;
835 }
836
837 GroupOps operations() const { return symops_from_hall(hall); }
838};
839
841 char hm[11];
842 char ext;
843 int pos;
844};
845
847 static const SpaceGroup main[564];
848 static const SpaceGroupAltName alt_names[28];
849 static const unsigned char ccp4_hkl_asu[230];
850};
851
852inline const SpaceGroup* find_spacegroup_by_number(int ccp4) noexcept {
853 if (ccp4 == 0)
854 return &spacegroup_tables::main[0];
855 for (const SpaceGroup& sg : spacegroup_tables::main)
856 if (sg.ccp4 == ccp4)
857 return &sg;
858 return nullptr;
859}
860
861inline const SpaceGroup& get_spacegroup_by_number(int ccp4) {
863 if (sg == nullptr)
864 throw std::invalid_argument("Invalid space-group number: "
865 + std::to_string(ccp4));
866 return *sg;
867}
868
870 for (const SpaceGroup& sg : spacegroup_tables::main)
871 if (sg.number == number && sg.is_reference_setting())
872 return sg;
873 throw std::invalid_argument("Invalid space-group number: "
874 + std::to_string(number));
875}
876
883 double alpha=0., double gamma=0.,
884 const char* prefer=nullptr);
885
886inline const SpaceGroup& get_spacegroup_by_name(const std::string& name) {
888 if (sg == nullptr)
889 throw std::invalid_argument("Unknown space-group name: " + name);
890 return *sg;
891}
892
894 return spacegroup_tables::main[0];
895}
896
897inline const SpaceGroup* find_spacegroup_by_ops(const GroupOps& gops) {
898 char c = gops.find_centering();
899 for (const SpaceGroup& sg : spacegroup_tables::main)
900 if ((c == sg.hall[0] || c == sg.hall[1]) &&
901 gops.is_same_as(sg.operations()))
902 return &sg;
903 return nullptr;
904}
905
906// Reciprocal space asu (asymmetric unit).
907// The same 12 choices of ASU as in CCP4 symlib and cctbx.
909 int idx;
910 Op::Rot rot{}; // value-initialized only to avoid -Wmaybe-uninitialized
911 bool is_ref;
912
913 ReciprocalAsu(const SpaceGroup* sg, bool tnt=false) {
914 if (sg == nullptr)
915 fail("Missing space group");
916 idx = spacegroup_tables::ccp4_hkl_asu[sg->number - 1];
917 if (tnt) {
918 idx += 10;
919 is_ref = true; // TNT ASU is given wrt current (not standard) settings
920 } else {
921 is_ref = sg->is_reference_setting();
922 if (!is_ref)
923 rot = sg->basisop().rot;
924 }
925 }
926
927 bool is_in(const Op::Miller& hkl) const {
928 if (is_ref)
929 return is_in_reference_setting(hkl[0], hkl[1], hkl[2]);
931 for (int i = 0; i != 3; ++i)
932 r[i] = rot[0][i] * hkl[0] + rot[1][i] * hkl[1] + rot[2][i] * hkl[2];
933 return is_in_reference_setting(r[0], r[1], r[2]);
934 }
935
936 bool is_in_reference_setting(int h, int k, int l) const {
937 switch (idx) {
938 // 0-9: CCP4 hkl asu, 10-19: TNT hkl asu
939 case 0: return l>0 || (l==0 && (h>0 || (h==0 && k>=0)));
940 case 1: return k>=0 && (l>0 || (l==0 && h>=0));
941 case 12: // orthorhombic-D
942 case 2: return h>=0 && k>=0 && l>=0;
943 case 3: return l>=0 && ((h>=0 && k>0) || (h==0 && k==0));
944 case 14: // tetragonal-D, hexagonal-D
945 case 4: return h>=k && k>=0 && l>=0;
946 case 5: return (h>=0 && k>0) || (h==0 && k==0 && l>=0);
947 case 16: // trigonal-D P312
948 case 6: return h>=k && k>=0 && (k>0 || l>=0);
949 case 17: // trigonal-D P321
950 case 7: return h>=k && k>=0 && (h>k || l>=0);
951 case 8: return h>=0 && ((l>=h && k>h) || (l==h && k==h));
952 case 9: return k>=l && l>=h && h>=0;
953 case 10: return k>0 || (k==0 && (h>0 || (h==0 && l>=0))); // triclinic
954 case 11: return k>=0 && (h>0 || (h==0 && l>=0)); // monoclinic-B
955 case 13: return l>=0 && ((k>=0 && h>0) || (h==0 && k==0)); // tetragonal-C, hexagonal-C
956 case 15: return (k>=0 && h>0) || (h==0 && k==0 && l>=0); // trigonal-C
957 case 18: return k>=0 && l>=0 && ((h>k && h>l) || (h==k && h>=l)); // cubic-T
958 case 19: return h>=k && k>=l && l>=0; // cubic-O
959 }
960 unreachable();
961 }
962
963 const char* condition_str() const {
964 switch (idx) {
965 case 0: return "l>0 or (l=0 and (h>0 or (h=0 and k>=0)))";
966 case 1: return "k>=0 and (l>0 or (l=0 and h>=0))";
967 case 12:
968 case 2: return "h>=0 and k>=0 and l>=0";
969 case 3: return "l>=0 and ((h>=0 and k>0) or (h=0 and k=0))";
970 case 14:
971 case 4: return "h>=k and k>=0 and l>=0";
972 case 5: return "(h>=0 and k>0) or (h=0 and k=0 and l>=0)";
973 case 16:
974 case 6: return "h>=k and k>=0 and (k>0 or l>=0)";
975 case 17:
976 case 7: return "h>=k and k>=0 and (h>k or l>=0)";
977 case 8: return "h>=0 and ((l>=h and k>h) or (l=h and k=h))";
978 case 9: return "k>=l and l>=h and h>=0";
979 case 10: return "k>0 or (k==0 and (h>0 or (h=0 and l>=0)))";
980 case 11: return "k>=0 and (h>0 or (h=0 and l>=0))";
981 case 13: return "l>=0 and ((k>=0 and h>0) or (h=0 and k==0))";
982 case 15: return "(k>=0 and h>0) or (h=0 and k==0 and l>=0)";
983 case 18: return "k>=0 and l>=0 and ((h>k and h>l) or (h=k and h>=l))";
984 case 19: return "h>=k and k>=l and l>=0";
985 }
986 unreachable();
987 }
988
991 std::pair<Op::Miller,int> to_asu(const Op::Miller& hkl, const std::vector<Op>& sym_ops) const {
992 int isym = 0;
993 for (const Op& op : sym_ops) {
994 ++isym;
995 Op::Miller new_hkl = op.apply_to_hkl_without_division(hkl);
996 if (is_in(new_hkl))
997 return {Op::divide_hkl_by_DEN(new_hkl), isym};
998 ++isym;
1000 if (is_in(negated_new_hkl))
1001 return {Op::divide_hkl_by_DEN(negated_new_hkl), isym};
1002 }
1003 fail("Oops, maybe inconsistent GroupOps?");
1004 }
1005
1006 std::pair<Op::Miller,int> to_asu(const Op::Miller& hkl, const GroupOps& gops) const {
1007 return to_asu(hkl, gops.sym_ops);
1008 }
1009
1011 std::pair<Op::Miller,bool> to_asu_sign(const Op::Miller& hkl, const GroupOps& gops) const {
1012 std::pair<Op::Miller,bool> neg = {{0,0,0}, true};
1013 for (const Op& op : gops.sym_ops) {
1014 Op::Miller new_hkl = op.apply_to_hkl_without_division(hkl);
1015 if (is_in(new_hkl))
1016 return {Op::divide_hkl_by_DEN(new_hkl), true};
1018 if (is_in(negated_new_hkl))
1019 // don't return it yet, because for centric reflection we prefer (+)
1020 neg = {Op::divide_hkl_by_DEN(negated_new_hkl), false};
1021 }
1022 if (neg.second)
1023 fail("Oops, maybe inconsistent GroupOps?");
1024 return neg;
1025 }
1026};
1027
1028} // namespace gemmi
1029
1030namespace std {
1031template<> struct hash<gemmi::Op> {
1032 size_t operator()(const gemmi::Op& op) const {
1033 size_t h = 0;
1034 for (int i = 0; i != 3; ++i)
1035 for (int j = 0; j != 3; ++j)
1036 h = (h << 2) ^ (op.rot[i][j] + 1);
1037 for (int i = 0; i != 3; ++i)
1038 h = (h << 5) ^ op.tran[i];
1039 return h;
1040 }
1041};
1042} // namespace std
1043
1044#endif
fail(), unreachable() and __declspec/__attribute__ macros
#define GEMMI_DLL
Definition fail.hpp:53
const SpaceGroup & get_spacegroup_p1()
Definition symmetry.hpp:893
GEMMI_DLL GroupOps generators_from_hall(const char *hall)
CrystalSystem crystal_system(Laue laue)
Definition symmetry.hpp:635
const SpaceGroup & get_spacegroup_reference_setting(int number)
Definition symmetry.hpp:869
GEMMI_DLL std::array< int, 4 > parse_triplet_part(const std::string &s, char &notation, double *decimal_fract=nullptr)
const SpaceGroup & get_spacegroup_by_name(const std::string &name)
Definition symmetry.hpp:886
unsigned char point_group_index_and_category(int space_group_number)
Definition symmetry.hpp:652
GEMMI_DLL Op parse_triplet(const std::string &s, char notation=' ')
const char * point_group_hm(PointGroup pg)
Definition symmetry.hpp:589
GEMMI_DLL void append_op_fraction(std::string &s, int w)
Op::Rot centred_to_primitive(char centring_type)
Definition symmetry.hpp:726
bool operator==(const Op &a, const Op &b)
Definition symmetry.hpp:187
GroupOps symops_from_hall(const char *hall)
Definition symmetry.hpp:564
PointGroup point_group(int space_group_number)
Definition symmetry.hpp:683
const SpaceGroup * find_spacegroup_by_number(int ccp4) noexcept
Definition symmetry.hpp:852
GEMMI_DLL Op seitz_to_op(const std::array< std::array< double, 4 >, 4 > &t)
GEMMI_DLL const SpaceGroup * find_spacegroup_by_name(std::string name, double alpha=0., double gamma=0., const char *prefer=nullptr)
If angles alpha and gamma are provided, they are used to distinguish hexagonal and rhombohedral setti...
GroupOps split_centering_vectors(const std::vector< Op > &ops)
Definition symmetry.hpp:545
Vec3 operator*(double d, const Vec3 &v)
Definition math.hpp:116
Laue pointgroup_to_laue(PointGroup pg)
Definition symmetry.hpp:604
GEMMI_DLL const char * get_basisop(int basisop_idx)
Op & operator*=(Op &a, const Op &b)
Definition symmetry.hpp:193
const SpaceGroup & get_spacegroup_by_number(int ccp4)
Definition symmetry.hpp:861
bool is_enantiomorphic(int space_group_number)
Definition symmetry.hpp:694
Op::Tran nonzero_inversion_center(int space_group_number)
Inversion center of the Euclidean normalizer that is not at the origin of reference settings.
Definition symmetry.hpp:707
std::vector< Op::Tran > centring_vectors(char centring_type)
Definition symmetry.hpp:232
const char * laue_class_str(Laue laue)
Definition symmetry.hpp:631
bool is_sohncke(int space_group_number)
Definition symmetry.hpp:689
void fail(const std::string &msg)
Definition fail.hpp:59
PointGroup laue_to_pointgroup(Laue laue)
Definition symmetry.hpp:622
const SpaceGroup * find_spacegroup_by_ops(const GroupOps &gops)
Definition symmetry.hpp:897
void unreachable()
Definition fail.hpp:80
bool is_symmorphic(int space_group_number)
Definition symmetry.hpp:699
bool operator!=(const Op &a, const Op &b)
Definition symmetry.hpp:190
CrystalSystem
Definition symmetry.hpp:572
const char * crystal_system_str(CrystalSystem system)
Definition symmetry.hpp:576
Definition seqid.hpp:151
const GroupOps & gops
Definition symmetry.hpp:470
bool operator==(const Iter &other) const
Definition symmetry.hpp:481
bool operator!=(const Iter &other) const
Definition symmetry.hpp:484
Op * find_by_rotation(const Op::Rot &r)
Definition symmetry.hpp:293
const Op * find_by_rotation(const Op::Rot &r) const
Definition symmetry.hpp:300
bool has_same_centring(const GroupOps &other) const
Definition symmetry.hpp:416
void change_basis_backward(const Op &inv)
Definition symmetry.hpp:391
std::vector< Op > sym_ops
Definition symmetry.hpp:255
bool is_systematically_absent(const Op::Miller &hkl) const
Definition symmetry.hpp:332
char find_centering() const
Definition symmetry.hpp:278
void add_missing_elements()
Definition symmetry.hpp:491
int order() const
Definition symmetry.hpp:258
bool is_same_as(const GroupOps &other) const
Definition symmetry.hpp:409
void change_basis_forward(const Op &cob)
Definition symmetry.hpp:390
std::array< int, 3 > find_grid_factors() const
Definition symmetry.hpp:444
std::vector< Op::Tran > cen_ops
Definition symmetry.hpp:256
int epsilon_factor(const Op::Miller &hkl) const
Definition symmetry.hpp:324
void change_basis_impl(const Op &cob, const Op &inv)
Definition symmetry.hpp:348
bool is_centrosymmetric() const
Definition symmetry.hpp:304
void add_missing_elements_part2(const std::vector< Op > &gen, size_t max_size, bool ignore_bad_gen)
Definition symmetry.hpp:513
bool are_directions_symmetry_related(int u, int v) const
Definition symmetry.hpp:454
int epsilon_factor_without_centering(const Op::Miller &hkl) const
Definition symmetry.hpp:316
std::vector< Op > all_ops_sorted() const
Definition symmetry.hpp:393
Iter begin() const
Definition symmetry.hpp:487
bool add_inversion()
Definition symmetry.hpp:264
bool has_same_rotations(const GroupOps &other) const
Definition symmetry.hpp:429
GroupOps derive_symmorphic() const
Definition symmetry.hpp:462
static bool has_phase_shift(const Op::Tran &c, const Op::Miller &hkl)
Definition symmetry.hpp:328
bool is_reflection_centric(const Op::Miller &hkl) const
Definition symmetry.hpp:308
Op get_op(int n) const
Definition symmetry.hpp:403
Iter end() const
Definition symmetry.hpp:488
Op & wrap()
Definition symmetry.hpp:64
Op & translate(const Tran &a)
Definition symmetry.hpp:69
char notation
Definition symmetry.hpp:37
std::array< std::array< double, 4 >, 4 > float_seitz() const
Definition symmetry.hpp:167
Miller apply_to_hkl(const Miller &hkl) const
Definition symmetry.hpp:150
std::array< std::array< int, 4 >, 4 > int_seitz() const
Definition symmetry.hpp:159
std::array< std::array< int, 3 >, 3 > Rot
Definition symmetry.hpp:32
int rot_type() const
Definition symmetry.hpp:100
Rot negated_rot() const
Definition symmetry.hpp:79
static Miller divide_hkl_by_DEN(const Miller &hkl)
Definition symmetry.hpp:147
std::string triplet(char style=' ') const
std::array< double, 3 > apply_to_xyz(const std::array< double, 3 > &xyz) const
Definition symmetry.hpp:128
std::array< int, 3 > Miller
Definition symmetry.hpp:139
Tran tran
Definition symmetry.hpp:36
Op as_xyz() const
Definition symmetry.hpp:44
Op::Tran wrapped_tran() const
Definition symmetry.hpp:52
static constexpr int DEN
Definition symmetry.hpp:31
std::array< int, 3 > Tran
Definition symmetry.hpp:33
bool is_hkl() const
Definition symmetry.hpp:39
static Rot transpose(const Rot &rot)
Definition symmetry.hpp:85
Miller apply_to_hkl_without_division(const Miller &hkl) const
Definition symmetry.hpp:141
int det_rot() const
Definition symmetry.hpp:93
Rot transposed_rot() const
Definition symmetry.hpp:90
double phase_shift(const Miller &hkl) const
Definition symmetry.hpp:154
Op as_hkl() const
Definition symmetry.hpp:41
Op inverse() const
Definition symmetry.hpp:195
Op translated(const Tran &a) const
Definition symmetry.hpp:75
Op combine(const Op &b) const
Definition symmetry.hpp:110
static constexpr Op::Rot inversion_rot()
Definition symmetry.hpp:179
static constexpr Op identity()
Definition symmetry.hpp:176
Op add_centering(const Tran &a) const
Definition symmetry.hpp:77
bool operator<(const Op &rhs) const
Definition symmetry.hpp:182
bool is_in_reference_setting(int h, int k, int l) const
Definition symmetry.hpp:936
std::pair< Op::Miller, int > to_asu(const Op::Miller &hkl, const std::vector< Op > &sym_ops) const
Returns hkl in asu and MTZ ISYM - 2*n-1 for reflections in the positive asu (I+ of a Friedel pair),...
Definition symmetry.hpp:991
std::pair< Op::Miller, bool > to_asu_sign(const Op::Miller &hkl, const GroupOps &gops) const
Similar to to_asu(), but the second returned value is sign: true for + or centric.
bool is_in(const Op::Miller &hkl) const
Definition symmetry.hpp:927
std::pair< Op::Miller, int > to_asu(const Op::Miller &hkl, const GroupOps &gops) const
const char * condition_str() const
Definition symmetry.hpp:963
ReciprocalAsu(const SpaceGroup *sg, bool tnt=false)
Definition symmetry.hpp:913
Op basisop() const
Definition symmetry.hpp:817
const char * laue_str() const
Definition symmetry.hpp:798
const char * basisop_str() const
Definition symmetry.hpp:816
bool is_symmorphic() const
Definition symmetry.hpp:792
GroupOps operations() const
Definition symmetry.hpp:837
std::string xhm() const
Definition symmetry.hpp:755
char monoclinic_unique_axis() const
returns 'a', 'b' or 'c' for monoclinic SG, '\0' otherwise
Definition symmetry.hpp:810
Laue laue_class() const
Definition symmetry.hpp:797
char ccp4_lattice_type() const
Definition symmetry.hpp:767
std::string short_name() const
Definition symmetry.hpp:770
const char * point_group_hm() const
Definition symmetry.hpp:794
Op change_of_hand_op() const
Returns change-of-hand operator. Compatible with similar sgtbx function.
Definition symmetry.hpp:825
const char * crystal_system_str() const
Definition symmetry.hpp:802
bool is_sohncke() const
Definition symmetry.hpp:790
char centring_type() const
Definition symmetry.hpp:764
std::string pdb_name() const
Definition symmetry.hpp:783
bool is_centrosymmetric() const
Definition symmetry.hpp:805
Op centred_to_primitive() const
Definition symmetry.hpp:820
bool is_enantiomorphic() const
Definition symmetry.hpp:791
bool is_reference_setting() const
Definition symmetry.hpp:818
CrystalSystem crystal_system() const
Definition symmetry.hpp:799
PointGroup point_group() const
Definition symmetry.hpp:793
size_t operator()(const gemmi::Op &op) const