68 symmetry_initialized_ =
false;
73 log::info(
" Density symmetrization: {} symmetry operations",
74 symmetry_operations_.size());
79 symmetry_initialized_ =
false;
84 const auto& g_vectors = planewave_basis.
g_vectors();
90 [](
int miller_index_1,
int miller_index_2,
int miller_index_3) -> int64_t {
91 constexpr int64_t offset = 1000;
92 return (int64_t)(miller_index_1 + offset) * (2 * offset + 1) * (2 * offset + 1) +
93 (int64_t)(miller_index_2 + offset) * (2 * offset + 1) +
94 (int64_t)(miller_index_3 + offset);
97 std::unordered_map<int64_t, int> miller_index_map;
98 miller_index_map.reserve(number_of_g_vectors);
99 for (
int g_vector_index = 0; g_vector_index < number_of_g_vectors; g_vector_index++) {
100 const auto& miller = g_vectors[g_vector_index].miller;
101 miller_index_map[miller_to_key(miller.x, miller.y, miller.z)] = g_vector_index;
104 std::vector<bool> assigned(number_of_g_vectors,
false);
105 symmetry_shells_.clear();
106 symmetry_shells_.reserve(number_of_g_vectors);
108 for (
int g_vector_index = 0; g_vector_index < number_of_g_vectors; g_vector_index++) {
109 if (assigned[g_vector_index]) {
114 const auto& miller = g_vectors[g_vector_index].miller;
115 std::array<int, 3> miller_indices = {miller.x, miller.y, miller.z};
119 std::array<int, 3> rotated = rotate_miller_indices(operation_index, miller_indices);
120 int64_t key = miller_to_key(rotated[0], rotated[1], rotated[2]);
121 auto it = miller_index_map.find(key);
122 if (it == miller_index_map.end()) {
125 int rotated_index = it->second;
126 if (assigned[rotated_index]) {
129 bool already_in_shell =
false;
130 for (
int existing_index : shell.g_vector_indices) {
131 if (existing_index == rotated_index) {
132 already_in_shell =
true;
136 if (!already_in_shell) {
137 shell.g_vector_indices.push_back(rotated_index);
138 assigned[rotated_index] =
true;
142 if (!shell.g_vector_indices.empty()) {
143 build_rotated_member_indices(g_vectors, shell);
144 symmetry_shells_.push_back(std::move(shell));
148 systematically_absent_g_vector_indices_.clear();
149 for (
const auto& shell : symmetry_shells_) {
150 if (shell.g_vector_indices.size() != 1) {
153 const auto& miller = g_vectors[shell.g_vector_indices[0]].miller;
154 if (is_forbidden_by_a_translation({miller.x, miller.y, miller.z})) {
155 systematically_absent_g_vector_indices_.push_back(shell.g_vector_indices[0]);
159 symmetry_initialized_ =
true;
174 std::vector<std::vector<Complex>>& charge_density_g_components,
175 std::vector<std::vector<Real>>& charge_density_real,
176 int number_of_spin_components)
const {
177 if (!symmetry_initialized_) {
179 for (
int spin_index = 0; spin_index < number_of_spin_components; spin_index++) {
180 planewave_basis.
rho_g_to_r(charge_density_g_components[spin_index],
181 charge_density_real[spin_index]);
191 const auto& fractional_translation =
192 symmetry_operations_[operation_index].fractional_translation;
193 is_non_symmorphic[operation_index] =
194 (std::abs(fractional_translation.x) >
196 std::abs(fractional_translation.y) >
198 std::abs(fractional_translation.z) >
202 for (
const auto& shell : symmetry_shells_) {
203 if (
static_cast<int>(shell.g_vector_indices.size()) <= 1) {
206 symmetrize_shell(planewave_basis,
207 number_of_spin_components,
210 charge_density_g_components);
212 zero_systematically_absent_components(number_of_spin_components,
213 charge_density_g_components);
216 for (
int spin_index = 0; spin_index < number_of_spin_components; spin_index++) {
217 planewave_basis.
rho_g_to_r(charge_density_g_components[spin_index],
218 charge_density_real[spin_index]);
223 std::vector<std::vector<Real>>& charge_density_real,
224 int number_of_spin_components)
const {
225 if (!symmetry_initialized_) {
231 std::vector<std::vector<Complex>> charge_density_g_components(number_of_spin_components);
232 for (
int spin_index = 0; spin_index < number_of_spin_components; spin_index++) {
233 planewave_basis.
rho_r_to_g(charge_density_real[spin_index],
234 charge_density_g_components[spin_index]);
240 const auto& fractional_translation =
241 symmetry_operations_[operation_index].fractional_translation;
242 is_non_symmorphic[operation_index] =
243 (std::abs(fractional_translation.x) >
245 std::abs(fractional_translation.y) >
247 std::abs(fractional_translation.z) >
251 for (
const auto& shell : symmetry_shells_) {
252 if (
static_cast<int>(shell.g_vector_indices.size()) <= 1) {
255 symmetrize_shell(planewave_basis,
256 number_of_spin_components,
259 charge_density_g_components);
261 zero_systematically_absent_components(number_of_spin_components,
262 charge_density_g_components);
264 for (
int spin_index = 0; spin_index < number_of_spin_components; spin_index++) {
265 planewave_basis.
rho_g_to_r(charge_density_g_components[spin_index],
266 charge_density_real[spin_index]);
271 return symmetry_initialized_;
274 return static_cast<int>(symmetry_shells_.size());
277 return static_cast<int>(symmetry_operations_.size());
281 return static_cast<int>(systematically_absent_g_vector_indices_.size());
285 struct SymmetryShell {
286 std::vector<int> g_vector_indices;
300 std::vector<std::vector<int>> rotated_member_indices;
302 std::vector<SymmetryOperation> symmetry_operations_;
303 std::vector<SymmetryShell> symmetry_shells_;
305 std::vector<int> systematically_absent_g_vector_indices_;
306 bool symmetry_initialized_{
false};
320 [[nodiscard]]
bool is_forbidden_by_a_translation(
const std::array<int, 3>& miller)
const {
322 int fixing_operations = 0;
323 for (
int operation_index = 0;
324 operation_index < static_cast<int>(symmetry_operations_.size());
326 if (rotate_miller_indices(operation_index, miller) != miller) {
329 const auto& translation = symmetry_operations_[operation_index].fractional_translation;
332 (miller[0] * translation.x + miller[1] * translation.y + miller[2] * translation.z);
333 phase_sum +=
Complex(std::cos(argument), std::sin(argument));
336 return fixing_operations > 0 && std::abs(phase_sum) < 0.5 * fixing_operations;
339 void zero_systematically_absent_components(
340 int number_of_spin_components,
341 std::vector<std::vector<Complex>>& charge_density_g_components)
const {
342 for (
int spin_index = 0; spin_index < number_of_spin_components; spin_index++) {
343 for (
int g_vector_index : systematically_absent_g_vector_indices_) {
344 charge_density_g_components[spin_index][g_vector_index] =
Complex(0.0, 0.0);
349 std::array<int, 3> rotate_miller_indices(
int operation_index,
350 const std::array<int, 3>& miller)
const {
351 const auto& reciprocal_rotation = symmetry_operations_[operation_index].reciprocal_rotation;
352 return {{reciprocal_rotation[0][0] * miller[0] + reciprocal_rotation[0][1] * miller[1] +
353 reciprocal_rotation[0][2] * miller[2],
354 reciprocal_rotation[1][0] * miller[0] + reciprocal_rotation[1][1] * miller[1] +
355 reciprocal_rotation[1][2] * miller[2],
356 reciprocal_rotation[2][0] * miller[0] + reciprocal_rotation[2][1] * miller[1] +
357 reciprocal_rotation[2][2] * miller[2]}};
373 void build_rotated_member_indices(
const std::vector<GVector>& g_vectors,
374 SymmetryShell& shell)
const {
375 const int shell_size =
static_cast<int>(shell.g_vector_indices.size());
378 std::vector<std::array<int, 3>> shell_miller_indices(shell_size);
379 for (
int member_index = 0; member_index < shell_size; member_index++) {
380 const auto& miller = g_vectors[shell.g_vector_indices[member_index]].miller;
381 shell_miller_indices[member_index] = {miller.x, miller.y, miller.z};
384 shell.rotated_member_indices.assign(shell_size,
386 for (
int member_index = 0; member_index < shell_size; member_index++) {
389 const std::array<int, 3> rotated =
390 rotate_miller_indices(operation_index, shell_miller_indices[member_index]);
391 for (
int candidate_index = 0; candidate_index < shell_size; candidate_index++) {
392 if (rotated == shell_miller_indices[candidate_index]) {
393 shell.rotated_member_indices[member_index][operation_index] =
402 void symmetrize_shell(
const PlanewaveBasis& planewave_basis,
403 int number_of_spin_components,
404 const SymmetryShell& shell,
405 const std::vector<bool>& is_non_symmorphic,
406 std::vector<std::vector<Complex>>& charge_density_g_components)
const {
407 const auto& g_vectors = planewave_basis.g_vectors();
409 int number_of_components = number_of_spin_components;
410 int shell_size =
static_cast<int>(shell.g_vector_indices.size());
412 std::vector<bool> done(shell_size,
false);
414 for (
int shell_member_index = 0; shell_member_index < shell_size; shell_member_index++) {
415 if (done[shell_member_index]) {
419 std::vector<Complex> averaged_density(number_of_components,
Complex(0.0, 0.0));
420 const std::vector<int>& rotated_shell_indices =
421 shell.rotated_member_indices[shell_member_index];
425 if (rotated_shell_indices[operation_index] < 0) {
429 int source_global_index =
430 shell.g_vector_indices[rotated_shell_indices[operation_index]];
432 if (is_non_symmorphic[operation_index]) {
443 const auto& source_miller = g_vectors[source_global_index].miller;
444 const auto& fractional_translation =
445 symmetry_operations_[operation_index].fractional_translation;
448 source_miller.y * fractional_translation.y +
449 source_miller.z * fractional_translation.z);
450 Complex phase_factor(std::cos(argument), std::sin(argument));
451 for (
int spin_index = 0; spin_index < number_of_components; spin_index++) {
452 averaged_density[spin_index] +=
453 charge_density_g_components[spin_index][source_global_index] *
457 for (
int spin_index = 0; spin_index < number_of_components; spin_index++) {
458 averaged_density[spin_index] +=
459 charge_density_g_components[spin_index][source_global_index];
464 for (
int spin_index = 0; spin_index < number_of_components; spin_index++) {
470 if (rotated_shell_indices[operation_index] < 0) {
474 int target_global_index =
475 shell.g_vector_indices[rotated_shell_indices[operation_index]];
477 if (is_non_symmorphic[operation_index]) {
482 const auto& target_miller = g_vectors[target_global_index].miller;
483 const auto& fractional_translation =
484 symmetry_operations_[operation_index].fractional_translation;
487 target_miller.y * fractional_translation.y +
488 target_miller.z * fractional_translation.z);
489 Complex phase_factor(std::cos(argument), -std::sin(argument));
490 for (
int spin_index = 0; spin_index < number_of_components; spin_index++) {
491 charge_density_g_components[spin_index][target_global_index] =
492 averaged_density[spin_index] * phase_factor;
495 for (
int spin_index = 0; spin_index < number_of_components; spin_index++) {
496 charge_density_g_components[spin_index][target_global_index] =
497 averaged_density[spin_index];
500 done[rotated_shell_indices[operation_index]] =
true;