33static std::pair<std::vector<int>, std::vector<r3::vector<int>>>
34find_sym_atom(
int num_atoms__, sddk::mdarray<double, 2>
const& positions__, r3::matrix<int>
const& R__,
35 r3::vector<double>
const& t__,
double tolerance__,
bool inverse__ =
false)
37 PROFILE(
"sirius::find_sym_atom");
39 std::vector<int> sym_atom(num_atoms__);
40 std::vector<r3::vector<int>> sym_atom_T(num_atoms__);
42 for (
int ia = 0; ia < num_atoms__; ia++) {
44 r3::vector<double> r_ia(positions__(0, ia), positions__(1, ia), positions__(2, ia));
47 auto rp_ia = (inverse__) ? dot(
inverse(R__), r_ia - t__) : dot(R__, r_ia) + t__;
51 for (
int ja = 0; ja < num_atoms__; ja++) {
52 r3::vector<double> r_ja(positions__(0, ja), positions__(1, ja), positions__(2, ja));
54 auto v = rp_ia - r_ja;
58 for (
int x : {0, 1, 2}) {
59 T[x] = std::round(v[x]);
60 diff += std::abs(T[x] - v[x]);
63 if (diff < tolerance__) {
72 s <<
"equivalent atom was not found" << std::endl
73 <<
" symmetry operaton R:" << R__ <<
", t: " << t__ << std::endl
74 <<
" atom: " << ia <<
", position: " << r_ia <<
", transformed position: " << rp_ia << std::endl
75 <<
" tolerance: " << tolerance__;
79 return std::make_pair(sym_atom, sym_atom_T);
82static space_group_symmetry_descriptor
83get_spg_sym_op(
int isym_spg__, SpglibDataset* spg_dataset__, r3::matrix<double>
const& lattice_vectors__,
int num_atoms__,
84 sddk::mdarray<double, 2>
const& positions__,
double tolerance__)
86 space_group_symmetry_descriptor sym_op;
88 auto inverse_lattice_vectors =
inverse(lattice_vectors__);
91 sym_op.R = r3::matrix<int>(spg_dataset__->rotations[isym_spg__]);
93 int p = sym_op.R.det();
94 if (!(p == 1 || p == -1)) {
95 RTE_THROW(
"wrong rotation matrix");
98 sym_op.invR =
inverse(sym_op.R);
102 sym_op.t = r3::vector<double>(spg_dataset__->translations[isym_spg__][0],
103 spg_dataset__->translations[isym_spg__][1],
104 spg_dataset__->translations[isym_spg__][2]);
108 sym_op.Rc = dot(dot(lattice_vectors__, r3::matrix<double>(sym_op.R)), inverse_lattice_vectors);
110 sym_op.Rcp = sym_op.Rc * p;
113 sym_op.euler_angles =
euler_angles(sym_op.Rcp, tolerance__);
114 }
catch(std::exception
const& e) {
116 s << e.what() << std::endl;
117 s <<
"number of symmetry operations found by spglib: " << spg_dataset__->n_operations << std::endl
118 <<
"symmetry operation: " << isym_spg__ << std::endl
119 <<
"rotation matrix in lattice coordinates: " << sym_op.R << std::endl
120 <<
"rotation matrix in Cartesian coordinates: " << sym_op.Rc << std::endl
121 <<
"lattice vectors: " << lattice_vectors__ << std::endl
122 <<
"metric tensor error: " <<
metric_tensor_error(lattice_vectors__, sym_op.R) << std::endl
124 <<
"possible solution: decrease the spglib_tolerance";
129 auto result = find_sym_atom(num_atoms__, positions__, sym_op.R, sym_op.t, tolerance__);
130 sym_op.sym_atom = result.first;
131 result = find_sym_atom(num_atoms__, positions__, sym_op.R, sym_op.t, tolerance__,
true);
132 sym_op.inv_sym_atom = result.first;
133 sym_op.inv_sym_atom_T = result.second;
134 for (
int ia = 0; ia < num_atoms__; ia++) {
135 int ja = sym_op.sym_atom[ia];
136 if (sym_op.inv_sym_atom[ja] != ia) {
138 s <<
"atom symmetry tables are not consistent" << std::endl
139 <<
"ia: " << ia <<
" sym_atom[ia]: " << ja <<
" inv_sym_atom[sym_atom[ia]]: "
140 << sym_op.inv_sym_atom[ja];
144 }
catch(std::exception
const& e) {
146 s << e.what() << std::endl;
147 s <<
"R: " << sym_op.R << std::endl
148 <<
"t: " << sym_op.t << std::endl
149 <<
"tolerance: " << tolerance__;
156static space_group_symmetry_descriptor
157get_identity_spg_sym_op(
int num_atoms__)
159 space_group_symmetry_descriptor sym_op;
161 sym_op.
R = r3::matrix<int>({{1, 0, 0}, {0, 1, 0}, {0, 0, 1}});
163 sym_op.invR =
inverse(sym_op.R);
167 sym_op.t = r3::vector<double>(0, 0, 0);
171 sym_op.Rcp = sym_op.Rc = r3::matrix<double>({{1, 0, 0}, {0, 1, 0}, {0, 0, 1}});
175 sym_op.sym_atom = std::vector<int>(num_atoms__);
176 std::iota(sym_op.sym_atom.begin(), sym_op.sym_atom.end(), 0);
178 sym_op.inv_sym_atom = std::vector<int>(num_atoms__);
179 std::iota(sym_op.inv_sym_atom.begin(), sym_op.inv_sym_atom.end(), 0);
180 sym_op.inv_sym_atom_T = std::vector<r3::vector<int>>(num_atoms__, r3::vector<int>(0, 0, 0));
185Crystal_symmetry::Crystal_symmetry(r3::matrix<double>
const& lattice_vectors__,
int num_atoms__,
186 int num_atom_types__, std::vector<int>
const& types__, sddk::mdarray<double, 2>
const& positions__,
187 sddk::mdarray<double, 2>
const& spins__,
bool spin_orbit__,
double tolerance__,
bool use_sym__)
188 : lattice_vectors_(lattice_vectors__)
189 , num_atoms_(num_atoms__)
190 , num_atom_types_(num_atom_types__)
192 , tolerance_(tolerance__)
194 PROFILE(
"sirius::Crystal_symmetry");
197 if (lattice_vectors__.det() < 0 && use_sym__) {
199 s <<
"spglib requires positive determinant for a matrix of lattice vectors";
204 inverse_lattice_vectors_ =
inverse(lattice_vectors_);
206 double lattice[3][3];
207 for (
int i: {0, 1, 2}) {
208 for (
int j: {0, 1, 2}) {
209 lattice[i][j] = lattice_vectors_(i, j);
213 positions_ = sddk::mdarray<double, 2>(3, num_atoms_);
216 magnetization_ = sddk::mdarray<double, 2>(3, num_atoms_);
219 PROFILE_START(
"sirius::Crystal_symmetry|spg");
222 spg_dataset_ = spg_get_dataset(lattice, (
double(*)[3])&positions_(0, 0), &types_[0], num_atoms_, tolerance_);
223 if (spg_dataset_ == NULL) {
224 RTE_THROW(
"spg_get_dataset() returned NULL");
227 if (spg_dataset_->spacegroup_number == 0) {
228 RTE_THROW(
"spg_get_dataset() returned 0 for the space group");
231 if (spg_dataset_->n_atoms != num_atoms__) {
233 s <<
"spg_get_dataset() returned wrong number of atoms (" << spg_dataset_->n_atoms <<
")" << std::endl
234 <<
"expected number of atoms is " << num_atoms__;
238 PROFILE_STOP(
"sirius::Crystal_symmetry|spg");
241 auto lat_sym = find_lat_sym(lattice_vectors_, tolerance_);
243 for (
int isym = 0; isym < spg_dataset_->n_operations; isym++) {
244 r3::matrix<int> R(spg_dataset_->rotations[isym]);
245 for (
auto& e: lat_sym) {
247 auto sym_op = get_spg_sym_op(isym, spg_dataset_, lattice_vectors__, num_atoms__, positions__, tolerance__);
249 space_group_symmetry_.push_back(sym_op);
254 auto sym_op = get_identity_spg_sym_op(num_atoms__);
256 space_group_symmetry_.push_back(sym_op);
259 PROFILE_START(
"sirius::Crystal_symmetry|mag");
261 for (
int isym = 0; isym < num_spg_sym(); isym++) {
263 int jsym1 = num_spg_sym() - 1;
265 jsym0 = jsym1 = isym;
268 for (
int jsym = jsym0; jsym <= jsym1; jsym++) {
270 auto Rspin = space_group_symmetry(jsym).Rcp;
274 for (
int ia = 0; ia < num_atoms_; ia++) {
275 int ja = space_group_symmetry(isym).sym_atom[ia];
279 auto vd = dot(Rspin, r3::vector<double>(spins__(0, ia), spins__(1, ia), spins__(2, ia))) -
280 r3::vector<double>(spins__(0, ja), spins__(1, ja), spins__(2, ja));
282 if (vd.length() < 1e-10) {
287 if (n == num_atoms_) {
288 magnetic_group_symmetry_descriptor mag_op;
289 mag_op.spg_op = space_group_symmetry(isym);
290 mag_op.spin_rotation = Rspin;
291 mag_op.spin_rotation_inv =
inverse(Rspin);
294 magnetic_group_symmetry_.push_back(std::move(mag_op));
299 PROFILE_STOP(
"sirius::Crystal_symmetry|mag");
306 for (
auto const& e: magnetic_group_symmetry_) {
313Crystal_symmetry::sym_op_R_error()
const
316 for (
auto const& e: magnetic_group_symmetry_) {
317 auto R = e.spg_op.Rcp;
319 for (
int i: {0, 1, 2}) {
320 for (
int j: {0, 1, 2}) {
321 diff = std::max(diff, std::abs(R1(i, j) - R(i, j)));
329Crystal_symmetry::print_info(std::ostream& out__,
int verbosity__)
const
331 if (this->spg_dataset_ && (this->spg_dataset_->n_operations != this->num_spg_sym())) {
332 out__ <<
"space group found by spglib is different" << std::endl
333 <<
" num. sym. spglib : " << this->spg_dataset_->n_operations << std::endl
334 <<
" num. sym. actual : " << this->num_spg_sym() << std::endl
335 <<
" tolerance : " << this->tolerance_ << std::endl;
337 out__ <<
"space group number : " << this->spacegroup_number() << std::endl
338 <<
"international symbol : " << this->international_symbol() << std::endl
339 <<
"Hall symbol : " << this->hall_symbol() << std::endl
340 <<
"space group transformation matrix : " << std::endl;
341 auto tm = this->transformation_matrix();
342 for (
int i = 0; i < 3; i++) {
343 for (
int j = 0; j < 3; j++) {
344 out__ <<
ffmt(8, 4) << tm(i, j);
348 out__ <<
"space group origin shift : " << std::endl;
349 auto t = this->origin_shift();
350 for (
auto x: {0, 1, 2}) {
351 out__ <<
ffmt(8, 4) << t[x];
355 out__ <<
"number of space group operations : " << this->num_spg_sym() << std::endl
356 <<
"number of magnetic group operations : " << this->size() << std::endl
358 <<
"rotation matrix error: " << std::scientific << this->sym_op_R_error() << std::endl;
360 if (verbosity__ >= 2) {
362 <<
"symmetry operations " << std::endl
364 for (
int isym = 0; isym < this->size(); isym++) {
365 auto R = this->operator[](isym).spg_op.R;
366 auto Rc = this->operator[](isym).spg_op.Rc;
367 auto t = this->operator[](isym).spg_op.t;
368 auto S = this->operator[](isym).spin_rotation;
370 out__ <<
"isym : " << isym << std::endl
372 for (
int i: {0, 1, 2}) {
376 for (
int j: {0, 1, 2,}) {
377 out__ << std::setw(3) << R(i, j);
382 for (
int i: {0, 1, 2}) {
386 for (
int j: {0, 1, 2}) {
387 out__ <<
ffmt(8, 4) << Rc(i, j);
392 for (
int j: {0, 1, 2}) {
393 out__ <<
ffmt(8, 4) << t[j];
397 for (
int i: {0, 1, 2}) {
401 for (
int j: {0, 1, 2}) {
402 out__ <<
ffmt(8, 4) << S(i, j);
406 out__ <<
"proper: " << std::setw(2) << this->operator[](isym).spg_op.proper << std::endl
Floating-point formatting (precision and width).
Contains definition and partial implementation of sirius::Crystal_symmetry class.
Crystal lattice functions.
void copy(T *target__, T const *source__, size_t n__)
Copy memory inside a device.
auto transpose(matrix< T > src)
Return transpose of the matrix.
auto inverse(matrix< int > src)
Return inverse of the integer matrix.
Namespace of the SIRIUS library.
auto euler_angles(r3::matrix< double > const &rot__, double tolerance__)
Compute Euler angles corresponding to the proper rotation matrix.
auto rotation_matrix_su2(std::array< double, 3 > u__, double theta__)
Generate SU(2) rotation matrix from the axes and angle.
double metric_tensor_error(r3::matrix< double > const &lat_vec__, r3::matrix< int > const &R__)
Compute error of the symmetry-transformed metric tensor.
Generate rotation matrices and related entities.
r3::matrix< int > R
Rotational part of symmetry operation (fractional coordinates).