120 const std::vector<SymmetryOperation>& symmetry_operations,
121 std::vector<ChannelAngularLayout> layout_by_species) {
122 layout_by_species_ = std::move(layout_by_species);
123 symmetry_operations_ = symmetry_operations;
124 rotations_by_operation_.clear();
125 rotation_slot_by_species_.clear();
126 angular_momenta_.clear();
128 if (symmetry_operations_.size() <= 1)
131 for (
const auto& layout : layout_by_species_)
132 for (
int momentum : layout.angular_momentum)
133 if (std::find(angular_momenta_.begin(), angular_momenta_.end(), momentum) ==
134 angular_momenta_.end())
135 angular_momenta_.push_back(momentum);
136 if (angular_momenta_.empty())
139 rotation_slot_by_species_.assign(layout_by_species_.size(), {});
140 for (
size_t species = 0; species < layout_by_species_.size(); species++)
141 for (
int momentum : layout_by_species_[species].angular_momentum)
142 rotation_slot_by_species_[species].push_back(
static_cast<int>(
143 std::find(angular_momenta_.begin(), angular_momenta_.end(), momentum) -
144 angular_momenta_.begin()));
151 rotations_by_operation_.resize(symmetry_operations_.size());
152 for (
size_t operation = 0; operation < symmetry_operations_.size(); operation++) {
155 rotations_by_operation_[operation].resize(angular_momenta_.size());
156 for (
size_t slot = 0; slot < angular_momenta_.size(); slot++)
157 rotations_by_operation_[operation][slot] =
188 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
190 if (species_index < 0 || species_index >=
static_cast<int>(layout_by_species_.size()))
192 const auto& layout = layout_by_species_[species_index];
193 const auto& rotation_slots = rotation_slot_by_species_[species_index];
194 int channels = layout.number_of_channels;
198 std::vector<Real> averaged(
static_cast<size_t>(channels) * channels, 0.0);
199 int contributing_operations = 0;
201 for (
size_t operation = 0; operation < symmetry_operations_.size(); operation++) {
202 const auto& atom_map = symmetry_operations_[operation].atom_map;
203 if (atom_index >=
static_cast<int>(atom_map.size()))
205 int image_atom = atom_map[atom_index];
206 if (image_atom < 0 || image_atom >= number_of_atoms)
211 const Real* source = original[image_atom];
212 const auto& rotations = rotations_by_operation_[operation];
214 for (
int first = 0; first < channels; first++) {
215 const auto& rotation_first =
216 rotations[
static_cast<size_t>(rotation_slots[first])];
217 int size_first = 2 * layout.angular_momentum[first] + 1;
218 int base_first = layout.manifold_base[first];
219 int position_first = layout.manifold_position[first];
221 for (
int second = 0; second < channels; second++) {
222 const auto& rotation_second =
223 rotations[
static_cast<size_t>(rotation_slots[second])];
224 int size_second = 2 * layout.angular_momentum[second] + 1;
225 int base_second = layout.manifold_base[second];
226 int position_second = layout.manifold_position[second];
233 Real accumulated = 0.0;
234 for (
int p = 0; p < size_first; p++) {
235 Real left = rotation_first[
static_cast<size_t>(p) * size_first +
239 const Real* row = source +
240 static_cast<size_t>(base_first + p) * channels +
242 for (
int q = 0; q < size_second; q++) {
243 Real right = rotation_second[
static_cast<size_t>(q) * size_second +
247 accumulated += left * right * row[q];
250 averaged[
static_cast<size_t>(first) * channels + second] += accumulated;
253 contributing_operations++;
256 if (contributing_operations == 0)
258 Real normalization = 1.0 /
static_cast<Real>(contributing_operations);
259 Real* destination = occupations[atom_index];
260 for (
int index = 0; index < channels * channels; index++)
261 destination[index] = averaged[
static_cast<size_t>(index)] * normalization;