136 int grid_dimension_x = dimensions[0];
137 int grid_dimension_y = dimensions[1];
138 int grid_dimension_z = dimensions[2];
139 int shift_x = shifts[0];
140 int shift_y = shifts[1];
141 int shift_z = shifts[2];
143 double lattice[3][3];
144 std::vector<double> positions;
145 std::vector<int> types;
148 int mesh[3] = {grid_dimension_x, grid_dimension_y, grid_dimension_z};
149 int is_shift[3] = {shift_x, shift_y, shift_z};
150 int is_time_reversal = 1;
152 int total_grid_points = grid_dimension_x * grid_dimension_y * grid_dimension_z;
153 std::vector<int> mapping(total_grid_points);
154 std::vector<int> grid_address_flat(total_grid_points * 3);
155 auto* grid_address =
reinterpret_cast<int(*)[3]
>(grid_address_flat.data());
159 int number_of_irreducible_kpoints =
160 spg_get_ir_reciprocal_mesh(grid_address,
166 reinterpret_cast<double(*)[3]
>(positions.data()),
171 if (number_of_irreducible_kpoints <= 0) {
172 log::warn(
"spglib: reciprocal mesh generation failed, using full grid");
175 grid_dimension_x, grid_dimension_y, grid_dimension_z, shift_x, shift_y, shift_z);
176 return fallback_grid;
180 std::vector<int> irreducible_indices;
181 std::vector<int> weights;
182 for (
int grid_index = 0; grid_index < total_grid_points; grid_index++) {
183 if (mapping[grid_index] == grid_index) {
184 irreducible_indices.push_back(grid_index);
185 weights.push_back(0);
189 for (
int grid_index = 0; grid_index < total_grid_points; grid_index++) {
190 int representative = mapping[grid_index];
191 for (
size_t irreducible_index = 0; irreducible_index < irreducible_indices.size();
192 irreducible_index++) {
193 if (irreducible_indices[irreducible_index] == representative) {
194 weights[irreducible_index]++;
203 for (
int irreducible_index = 0; irreducible_index < number_of_irreducible_kpoints;
204 irreducible_index++) {
205 int grid_index = irreducible_indices[irreducible_index];
208 static_cast<Real>(grid_address[grid_index][0] + is_shift[0] / 2.0) / mesh[0];
210 static_cast<Real>(grid_address[grid_index][1] + is_shift[1] / 2.0) / mesh[1];
212 static_cast<Real>(grid_address[grid_index][2] + is_shift[2] / 2.0) / mesh[2];
215 for (
int coordinate_index = 0; coordinate_index < 3; coordinate_index++) {
217 std::floor(kpoint_data.
coordinates[coordinate_index] + 0.5 +
221 kpoint_data.
weight =
static_cast<Real>(weights[irreducible_index]) / total_grid_points *
226 log::info(
" IBZ reduction: {} -> {} k-points ({} symmetry ops)",
228 number_of_irreducible_kpoints,
229 number_of_operations);
265 bool use_time_reversal =
true,
269 int grid_dimension_1 = dimensions[0];
270 int grid_dimension_2 = dimensions[1];
271 int grid_dimension_3 = dimensions[2];
272 int shift_1 = shifts[0];
273 int shift_2 = shifts[1];
274 int shift_3 = shifts[2];
276 int total_grid_points = grid_dimension_1 * grid_dimension_2 * grid_dimension_3;
284 int number_of_operations =
static_cast<int>(symmetry_operations.size());
301 const std::vector<KPoint>& mesh_kpoints = input_grid.
kpoints();
302 if (
static_cast<int>(mesh_kpoints.size()) != total_grid_points) {
303 throw std::runtime_error(
"reduce_kpoints_internal: the input grid holds " +
304 std::to_string(mesh_kpoints.size()) +
305 " k-points but its dimensions imply " +
306 std::to_string(total_grid_points) +
307 ". Call KPointsGrid::generate() before reducing.");
310 const auto crystal_component = [&mesh_kpoints](
int flat_index,
int dimension) ->
double {
311 const Vector3D& coordinates = mesh_kpoints[flat_index].coordinates;
312 return dimension == 0 ? coordinates.
x
313 : (dimension == 1 ? coordinates.
y : coordinates.
z);
322 std::vector<bool> has_representative(total_grid_points,
false);
323 std::vector<double> weight_accumulator(total_grid_points, 0.0);
325 constexpr double grid_tolerance = 1.0e-5;
327 for (
int kpoint_index = 0; kpoint_index < total_grid_points; kpoint_index++) {
328 if (has_representative[kpoint_index]) {
331 weight_accumulator[kpoint_index] = 1.0;
333 for (
int operation_index = 0; operation_index < number_of_operations;
341 const auto& rotation = symmetry_operations[operation_index].reciprocal_rotation;
344 double rotated_crystal[3];
345 for (
int dimension = 0; dimension < 3; dimension++) {
346 rotated_crystal[dimension] =
347 rotation[dimension][0] * crystal_component(kpoint_index, 0) +
348 rotation[dimension][1] * crystal_component(kpoint_index, 1) +
349 rotation[dimension][2] * crystal_component(kpoint_index, 2);
351 rotated_crystal[dimension] -= std::round(rotated_crystal[dimension]);
355 double fractional_index_1 = rotated_crystal[0] * grid_dimension_1 - 0.5 * shift_1;
356 double fractional_index_2 = rotated_crystal[1] * grid_dimension_2 - 0.5 * shift_2;
357 double fractional_index_3 = rotated_crystal[2] * grid_dimension_3 - 0.5 * shift_3;
360 std::abs(fractional_index_1 - std::round(fractional_index_1)) <=
362 std::abs(fractional_index_2 - std::round(fractional_index_2)) <=
364 std::abs(fractional_index_3 - std::round(fractional_index_3)) <= grid_tolerance;
368 ((
static_cast<int>(std::round(fractional_index_1 + 2 * grid_dimension_1))) %
371 ((
static_cast<int>(std::round(fractional_index_2 + 2 * grid_dimension_2))) %
374 ((
static_cast<int>(std::round(fractional_index_3 + 2 * grid_dimension_3))) %
376 int mapped_flat_index = mapped_index_3 + mapped_index_2 * grid_dimension_3 +
377 mapped_index_1 * grid_dimension_2 * grid_dimension_3;
379 if (mapped_flat_index > kpoint_index &&
380 !has_representative[mapped_flat_index]) {
381 has_representative[mapped_flat_index] =
true;
382 weight_accumulator[kpoint_index] += 1.0;
387 if (use_time_reversal) {
388 double negated_fractional_index_1 =
389 -rotated_crystal[0] * grid_dimension_1 - 0.5 * shift_1;
390 double negated_fractional_index_2 =
391 -rotated_crystal[1] * grid_dimension_2 - 0.5 * shift_2;
392 double negated_fractional_index_3 =
393 -rotated_crystal[2] * grid_dimension_3 - 0.5 * shift_3;
395 bool is_negated_on_grid =
396 std::abs(negated_fractional_index_1 -
397 std::round(negated_fractional_index_1)) <= grid_tolerance &&
398 std::abs(negated_fractional_index_2 -
399 std::round(negated_fractional_index_2)) <= grid_tolerance &&
400 std::abs(negated_fractional_index_3 -
401 std::round(negated_fractional_index_3)) <= grid_tolerance;
403 if (is_negated_on_grid) {
404 int negated_mapped_index_1 =
406 std::round(negated_fractional_index_1 + 2 * grid_dimension_1))) %
408 int negated_mapped_index_2 =
410 std::round(negated_fractional_index_2 + 2 * grid_dimension_2))) %
412 int negated_mapped_index_3 =
414 std::round(negated_fractional_index_3 + 2 * grid_dimension_3))) %
416 int negated_flat_index =
417 negated_mapped_index_3 + negated_mapped_index_2 * grid_dimension_3 +
418 negated_mapped_index_1 * grid_dimension_2 * grid_dimension_3;
420 if (negated_flat_index > kpoint_index &&
421 !has_representative[negated_flat_index]) {
422 has_representative[negated_flat_index] =
true;
423 weight_accumulator[kpoint_index] += 1.0;
431 double total_weight = 0.0;
432 int number_of_irreducible_kpoints = 0;
435 for (
int grid_index = 0; grid_index < total_grid_points; grid_index++) {
436 if (!has_representative[grid_index]) {
437 number_of_irreducible_kpoints++;
438 total_weight += weight_accumulator[grid_index];
449 kpoint_data.
coordinates = mesh_kpoints[grid_index].coordinates;
450 kpoint_data.
weight = weight_accumulator[grid_index];
461 for (
int kpoint_index = 0; kpoint_index < kpoints.
number_of_kpoints(); kpoint_index++) {
463 kpoint_data.
coordinates = kpoints[kpoint_index].coordinates;
464 kpoint_data.
weight = kpoints[kpoint_index].weight / total_weight * 2.0;
468 log::info(
" IBZ reduction (internal): {} -> {} k-points ({} symmetry ops{})",
470 number_of_irreducible_kpoints,
471 number_of_operations,
472 use_time_reversal ?
" + time reversal" :
"");
475 return normalized_kpoints;
487 double lattice[3][3];
488 std::vector<double> positions;
489 std::vector<int> types;
492 SpglibDataset* dataset = spg_get_dataset(lattice,
493 reinterpret_cast<double(*)[3]
>(positions.data()),
498 std::vector<SymmetryOperation> operations;
500 log::warn(
"spglib: failed to get symmetry operations");
503 identity.
rotation = {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}};
506 operations.push_back(identity);
510 operations.resize(dataset->n_operations);
511 for (
int operation_index = 0; operation_index < dataset->n_operations; operation_index++) {
512 auto& operation = operations[operation_index];
514 for (
int row = 0; row < 3; row++) {
515 for (
int column = 0; column < 3; column++) {
516 operation.rotation[row][column] =
517 dataset->rotations[operation_index][row][column];
520 operation.fractional_translation.
x = dataset->translations[operation_index][0];
521 operation.fractional_translation.y = dataset->translations[operation_index][1];
522 operation.fractional_translation.z = dataset->translations[operation_index][2];
537 const auto& rotation_matrix = operation.rotation;
541 rotation_matrix[0][0] * (rotation_matrix[1][1] * rotation_matrix[2][2] -
542 rotation_matrix[1][2] * rotation_matrix[2][1]) -
543 rotation_matrix[0][1] * (rotation_matrix[1][0] * rotation_matrix[2][2] -
544 rotation_matrix[1][2] * rotation_matrix[2][0]) +
545 rotation_matrix[0][2] * (rotation_matrix[1][0] * rotation_matrix[2][1] -
546 rotation_matrix[1][1] * rotation_matrix[2][0]);
551 int cofactor_matrix[3][3];
552 cofactor_matrix[0][0] = rotation_matrix[1][1] * rotation_matrix[2][2] -
553 rotation_matrix[1][2] * rotation_matrix[2][1];
554 cofactor_matrix[0][1] = -(rotation_matrix[1][0] * rotation_matrix[2][2] -
555 rotation_matrix[1][2] * rotation_matrix[2][0]);
556 cofactor_matrix[0][2] = rotation_matrix[1][0] * rotation_matrix[2][1] -
557 rotation_matrix[1][1] * rotation_matrix[2][0];
558 cofactor_matrix[1][0] = -(rotation_matrix[0][1] * rotation_matrix[2][2] -
559 rotation_matrix[0][2] * rotation_matrix[2][1]);
560 cofactor_matrix[1][1] = rotation_matrix[0][0] * rotation_matrix[2][2] -
561 rotation_matrix[0][2] * rotation_matrix[2][0];
562 cofactor_matrix[1][2] = -(rotation_matrix[0][0] * rotation_matrix[2][1] -
563 rotation_matrix[0][1] * rotation_matrix[2][0]);
564 cofactor_matrix[2][0] = rotation_matrix[0][1] * rotation_matrix[1][2] -
565 rotation_matrix[0][2] * rotation_matrix[1][1];
566 cofactor_matrix[2][1] = -(rotation_matrix[0][0] * rotation_matrix[1][2] -
567 rotation_matrix[0][2] * rotation_matrix[1][0]);
568 cofactor_matrix[2][2] = rotation_matrix[0][0] * rotation_matrix[1][1] -
569 rotation_matrix[0][1] * rotation_matrix[1][0];
574 for (
int row = 0; row < 3; row++) {
575 for (
int column = 0; column < 3; column++) {
576 operation.reciprocal_rotation[row][column] =
577 cofactor_matrix[row][column] / determinant;
585 operation.atom_map.resize(number_of_atoms, -1);
586 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
593 rotation_matrix[0][0] * tau_cryst.
x + rotation_matrix[0][1] * tau_cryst.
y +
594 rotation_matrix[0][2] * tau_cryst.
z + operation.fractional_translation.x;
596 rotation_matrix[1][0] * tau_cryst.
x + rotation_matrix[1][1] * tau_cryst.
y +
597 rotation_matrix[1][2] * tau_cryst.
z + operation.fractional_translation.y;
599 rotation_matrix[2][0] * tau_cryst.
x + rotation_matrix[2][1] * tau_cryst.
y +
600 rotation_matrix[2][2] * tau_cryst.
z + operation.fractional_translation.z;
603 for (
int target_atom = 0; target_atom < number_of_atoms; target_atom++) {
607 Vector3D difference = rotated_position - target_cryst;
609 difference.
x -= std::round(difference.
x);
610 difference.
y -= std::round(difference.
y);
611 difference.
z -= std::round(difference.
z);
612 if (std::abs(difference.
x) < symmetry_precision &&
613 std::abs(difference.
y) < symmetry_precision &&
614 std::abs(difference.
z) < symmetry_precision) {
615 operation.atom_map[atom_index] = target_atom;
622 spg_free_dataset(dataset);
635 const std::vector<SymmetryOperation>& symmetry_operations,
636 std::vector<Vector3D>& vector_field) {
652 std::vector<Vector3D> work(number_of_atoms);
653 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
654 for (
int dimension = 0; dimension < 3; dimension++) {
655 work[atom_index][dimension] =
656 vector_field[atom_index][0] * direct_lattice_vectors[dimension][0] +
657 vector_field[atom_index][1] * direct_lattice_vectors[dimension][1] +
658 vector_field[atom_index][2] * direct_lattice_vectors[dimension][2];
686 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
687 vector_field[atom_index] = {0, 0, 0};
690 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
693 const auto& rotation = symmetry_operations[operation_index].rotation;
694 int mapped_atom = symmetry_operations[operation_index].atom_map[atom_index];
696 for (
int dimension = 0; dimension < 3; dimension++) {
697 vector_field[atom_index][dimension] +=
698 rotation[0][dimension] * work[mapped_atom][0] +
699 rotation[1][dimension] * work[mapped_atom][1] +
700 rotation[2][dimension] * work[mapped_atom][2];
707 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
708 for (
int dimension = 0; dimension < 3; dimension++) {
709 work[atom_index][dimension] =
710 vector_field[atom_index][dimension] * inverse_number_of_operations;
721 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
722 for (
int dimension = 0; dimension < 3; dimension++) {
723 vector_field[atom_index][dimension] =
724 work[atom_index][0] * reciprocal_lattice_vectors[0][dimension] +
725 work[atom_index][1] * reciprocal_lattice_vectors[1][dimension] +
726 work[atom_index][2] * reciprocal_lattice_vectors[2][dimension];
741 const std::vector<SymmetryOperation>& symmetry_operations,
760 for (
int row = 0; row < 3; row++) {
761 for (
int column = 0; column < 3; column++) {
763 for (
int contracted_row = 0; contracted_row < 3; contracted_row++) {
764 for (
int contracted_column = 0; contracted_column < 3; contracted_column++) {
765 sum += tensor[contracted_row][contracted_column] *
766 direct_lattice[row][contracted_row] *
767 direct_lattice[column][contracted_column];
770 crystal_tensor[row][column] = sum;
786 for (
int row = 0; row < 3; row++) {
787 for (
int column = 0; column < 3; column++) {
788 symmetrized[row][column] = 0.0;
794 const auto& rotation = symmetry_operations[operation_index].rotation;
795 for (
int row = 0; row < 3; row++) {
796 for (
int column = 0; column < 3; column++) {
797 for (
int contracted_row = 0; contracted_row < 3; contracted_row++) {
798 for (
int contracted_column = 0; contracted_column < 3;
799 contracted_column++) {
800 symmetrized[row][column] +=
801 rotation[contracted_row][row] *
802 rotation[contracted_column][column] *
803 crystal_tensor[contracted_row][contracted_column];
811 for (
int row = 0; row < 3; row++) {
812 for (
int column = 0; column < 3; column++) {
813 crystal_tensor[row][column] =
814 symmetrized[row][column] * inverse_number_of_operations;
827 for (
int row = 0; row < 3; row++) {
828 for (
int column = 0; column < 3; column++) {
830 for (
int contracted_row = 0; contracted_row < 3; contracted_row++) {
831 for (
int contracted_column = 0; contracted_column < 3; contracted_column++) {
832 sum += crystal_tensor[contracted_row][contracted_column] *
833 reciprocal_lattice[contracted_row][row] *
834 reciprocal_lattice[contracted_column][column];
837 tensor[row][column] = sum;