|
| 1 | +#include "DmkSpinTransform.h" |
| 2 | + |
| 3 | +#include "module_base/parallel_2d.h" |
| 4 | +#include "module_base/tool_quit.h" |
| 5 | + |
| 6 | +#include <array> |
| 7 | +#include <cstddef> |
| 8 | +#include <utility> |
| 9 | + |
| 10 | +namespace |
| 11 | +{ |
| 12 | + |
| 13 | +using Complex = std::complex<double>; |
| 14 | +using SpinorOffsets = std::array<std::size_t, 4>; |
| 15 | + |
| 16 | +void require_transform_shape(const bool condition, const char* message) |
| 17 | +{ |
| 18 | + if (!condition) |
| 19 | + { |
| 20 | + ModuleBase::WARNING_QUIT("DmkSpinTransform", message); |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +std::size_t flat_offset(const int local_row, |
| 25 | + const int local_col, |
| 26 | + const Parallel_2D& pv, |
| 27 | + const bool column_major) |
| 28 | +{ |
| 29 | + const std::size_t row = static_cast<std::size_t>(local_row); |
| 30 | + const std::size_t col = static_cast<std::size_t>(local_col); |
| 31 | + const std::size_t row_size = static_cast<std::size_t>(pv.get_row_size()); |
| 32 | + const std::size_t col_size = static_cast<std::size_t>(pv.get_col_size()); |
| 33 | + return column_major ? col * row_size + row : row * col_size + col; |
| 34 | +} |
| 35 | + |
| 36 | +std::vector<SpinorOffsets> build_spinor_offsets(const Parallel_2D& pv, const bool column_major) |
| 37 | +{ |
| 38 | + const int global_rows = pv.get_global_row_size(); |
| 39 | + const int global_cols = pv.get_global_col_size(); |
| 40 | + require_transform_shape(global_rows >= 0 && global_cols >= 0 && global_rows % 2 == 0 |
| 41 | + && global_cols % 2 == 0, |
| 42 | + "nspin=4 DMK requires even global matrix dimensions"); |
| 43 | + require_transform_shape(pv.get_row_size() >= 0 && pv.get_col_size() >= 0 && pv.get_local_size() >= 0, |
| 44 | + "nspin=4 DMK has an invalid local matrix shape"); |
| 45 | + |
| 46 | + std::vector<std::pair<int, int>> local_row_pairs; |
| 47 | + for (int mu = 0; mu < global_rows / 2; ++mu) |
| 48 | + { |
| 49 | + const int up = pv.global2local_row(2 * mu); |
| 50 | + const int down = pv.global2local_row(2 * mu + 1); |
| 51 | + const bool up_is_local = up >= 0; |
| 52 | + const bool down_is_local = down >= 0; |
| 53 | + require_transform_shape(up_is_local == down_is_local, |
| 54 | + "nspin=4 row spin pair crosses MPI ranks"); |
| 55 | + if (up_is_local) |
| 56 | + { |
| 57 | + local_row_pairs.push_back(std::make_pair(up, down)); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + std::vector<std::pair<int, int>> local_col_pairs; |
| 62 | + for (int nu = 0; nu < global_cols / 2; ++nu) |
| 63 | + { |
| 64 | + const int up = pv.global2local_col(2 * nu); |
| 65 | + const int down = pv.global2local_col(2 * nu + 1); |
| 66 | + const bool up_is_local = up >= 0; |
| 67 | + const bool down_is_local = down >= 0; |
| 68 | + require_transform_shape(up_is_local == down_is_local, |
| 69 | + "nspin=4 column spin pair crosses MPI ranks"); |
| 70 | + if (up_is_local) |
| 71 | + { |
| 72 | + local_col_pairs.push_back(std::make_pair(up, down)); |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + const std::size_t local_size = static_cast<std::size_t>(pv.get_local_size()); |
| 77 | + std::vector<SpinorOffsets> offsets; |
| 78 | + offsets.reserve(local_size / 4); |
| 79 | + |
| 80 | + // Keep the packed spatial-matrix order consistent with the KS solver. |
| 81 | + if (column_major) |
| 82 | + { |
| 83 | + for (const std::pair<int, int>& col : local_col_pairs) |
| 84 | + { |
| 85 | + for (const std::pair<int, int>& row : local_row_pairs) |
| 86 | + { |
| 87 | + offsets.push_back({flat_offset(row.first, col.first, pv, true), |
| 88 | + flat_offset(row.first, col.second, pv, true), |
| 89 | + flat_offset(row.second, col.first, pv, true), |
| 90 | + flat_offset(row.second, col.second, pv, true)}); |
| 91 | + } |
| 92 | + } |
| 93 | + } |
| 94 | + else |
| 95 | + { |
| 96 | + for (const std::pair<int, int>& row : local_row_pairs) |
| 97 | + { |
| 98 | + for (const std::pair<int, int>& col : local_col_pairs) |
| 99 | + { |
| 100 | + offsets.push_back({flat_offset(row.first, col.first, pv, false), |
| 101 | + flat_offset(row.first, col.second, pv, false), |
| 102 | + flat_offset(row.second, col.first, pv, false), |
| 103 | + flat_offset(row.second, col.second, pv, false)}); |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + require_transform_shape(offsets.size() * std::size_t{4} == local_size, |
| 109 | + "nspin=4 spin quartets do not cover the local matrix"); |
| 110 | + |
| 111 | + std::vector<bool> covered(local_size, false); |
| 112 | + for (const SpinorOffsets& quartet : offsets) |
| 113 | + { |
| 114 | + for (const std::size_t offset : quartet) |
| 115 | + { |
| 116 | + require_transform_shape(offset < local_size && !covered[offset], |
| 117 | + "nspin=4 spin quartet offsets are invalid or duplicated"); |
| 118 | + covered[offset] = true; |
| 119 | + } |
| 120 | + } |
| 121 | + return offsets; |
| 122 | +} |
| 123 | + |
| 124 | +template <typename Tdata> |
| 125 | +std::size_t validate_nspin2_physical(const std::vector<std::vector<Tdata>>& physical) |
| 126 | +{ |
| 127 | + require_transform_shape(!physical.empty() && physical.size() % 2 == 0, |
| 128 | + "nspin=2 physical DMK requires complete up/down stream pairs"); |
| 129 | + const std::size_t local_size = physical.front().size(); |
| 130 | + for (const std::vector<Tdata>& stream : physical) |
| 131 | + { |
| 132 | + require_transform_shape(stream.size() == local_size, |
| 133 | + "nspin=2 physical DMK streams must have equal lengths"); |
| 134 | + } |
| 135 | + return local_size; |
| 136 | +} |
| 137 | + |
| 138 | +template <typename Tdata> |
| 139 | +std::size_t validate_nspin2_pauli(const std::vector<const std::vector<Tdata>*>& pauli) |
| 140 | +{ |
| 141 | + require_transform_shape(!pauli.empty(), "nspin=2 Pauli DMK requires at least one stream"); |
| 142 | + for (const std::vector<Tdata>* stream : pauli) |
| 143 | + { |
| 144 | + require_transform_shape(stream != nullptr, "nspin=2 Pauli DMK contains a null stream"); |
| 145 | + } |
| 146 | + const std::size_t packed_size = pauli.front()->size(); |
| 147 | + require_transform_shape(packed_size % 2 == 0, |
| 148 | + "nspin=2 Pauli DMK stream length must be divisible by two"); |
| 149 | + for (const std::vector<Tdata>* stream : pauli) |
| 150 | + { |
| 151 | + require_transform_shape(stream->size() == packed_size, |
| 152 | + "nspin=2 Pauli DMK streams must have equal lengths"); |
| 153 | + } |
| 154 | + return packed_size / 2; |
| 155 | +} |
| 156 | + |
| 157 | +void validate_nspin4_physical(const std::vector<std::vector<Complex>>& physical, |
| 158 | + const std::size_t local_size) |
| 159 | +{ |
| 160 | + require_transform_shape(!physical.empty(), "nspin=4 physical DMK requires at least one stream"); |
| 161 | + for (const std::vector<Complex>& stream : physical) |
| 162 | + { |
| 163 | + require_transform_shape(stream.size() == local_size, |
| 164 | + "nspin=4 physical DMK stream length does not match Parallel_2D"); |
| 165 | + } |
| 166 | +} |
| 167 | + |
| 168 | +void validate_nspin4_pauli(const std::vector<const std::vector<Complex>*>& pauli, |
| 169 | + const std::size_t local_size) |
| 170 | +{ |
| 171 | + require_transform_shape(!pauli.empty(), "nspin=4 Pauli DMK requires at least one stream"); |
| 172 | + for (const std::vector<Complex>* stream : pauli) |
| 173 | + { |
| 174 | + require_transform_shape(stream != nullptr, "nspin=4 Pauli DMK contains a null stream"); |
| 175 | + require_transform_shape(stream->size() == local_size, |
| 176 | + "nspin=4 Pauli DMK stream length does not match Parallel_2D"); |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +} // namespace |
| 181 | + |
| 182 | +namespace DmkSpinTransform |
| 183 | +{ |
| 184 | + |
| 185 | +template <typename Tdata> |
| 186 | +std::vector<std::vector<Tdata>> to_pauli_nspin2(const std::vector<std::vector<Tdata>>& physical) |
| 187 | +{ |
| 188 | + const std::size_t component_size = validate_nspin2_physical(physical); |
| 189 | + const std::size_t nk = physical.size() / 2; |
| 190 | + std::vector<std::vector<Tdata>> pauli(nk, std::vector<Tdata>(2 * component_size)); |
| 191 | + |
| 192 | + for (std::size_t ik = 0; ik < nk; ++ik) |
| 193 | + { |
| 194 | + const std::vector<Tdata>& up = physical[ik]; |
| 195 | + const std::vector<Tdata>& down = physical[ik + nk]; |
| 196 | + for (std::size_t i = 0; i < component_size; ++i) |
| 197 | + { |
| 198 | + pauli[ik][i] = up[i] + down[i]; |
| 199 | + pauli[ik][component_size + i] = up[i] - down[i]; |
| 200 | + } |
| 201 | + } |
| 202 | + return pauli; |
| 203 | +} |
| 204 | + |
| 205 | +template <typename Tdata> |
| 206 | +std::vector<std::vector<Tdata>> to_physical_nspin2( |
| 207 | + const std::vector<const std::vector<Tdata>*>& pauli) |
| 208 | +{ |
| 209 | + const std::size_t component_size = validate_nspin2_pauli(pauli); |
| 210 | + const std::size_t nk = pauli.size(); |
| 211 | + std::vector<std::vector<Tdata>> physical(2 * nk, std::vector<Tdata>(component_size)); |
| 212 | + |
| 213 | + for (std::size_t ik = 0; ik < nk; ++ik) |
| 214 | + { |
| 215 | + const std::vector<Tdata>& packed = *pauli[ik]; |
| 216 | + for (std::size_t i = 0; i < component_size; ++i) |
| 217 | + { |
| 218 | + physical[ik][i] = Tdata(0.5) * (packed[i] + packed[component_size + i]); |
| 219 | + physical[ik + nk][i] = Tdata(0.5) * (packed[i] - packed[component_size + i]); |
| 220 | + } |
| 221 | + } |
| 222 | + return physical; |
| 223 | +} |
| 224 | + |
| 225 | +std::vector<std::vector<Complex>> to_pauli_nspin4(const std::vector<std::vector<Complex>>& physical, |
| 226 | + const Parallel_2D& pv, |
| 227 | + const bool column_major) |
| 228 | +{ |
| 229 | + const std::vector<SpinorOffsets> offsets = build_spinor_offsets(pv, column_major); |
| 230 | + const std::size_t local_size = static_cast<std::size_t>(pv.get_local_size()); |
| 231 | + validate_nspin4_physical(physical, local_size); |
| 232 | + |
| 233 | + const std::size_t component_size = offsets.size(); |
| 234 | + const Complex imaginary(0.0, 1.0); |
| 235 | + std::vector<std::vector<Complex>> pauli(physical.size(), std::vector<Complex>(local_size)); |
| 236 | + for (std::size_t ik = 0; ik < physical.size(); ++ik) |
| 237 | + { |
| 238 | + for (std::size_t i = 0; i < component_size; ++i) |
| 239 | + { |
| 240 | + const SpinorOffsets& quartet = offsets[i]; |
| 241 | + const Complex& uu = physical[ik][quartet[0]]; |
| 242 | + const Complex& ud = physical[ik][quartet[1]]; |
| 243 | + const Complex& du = physical[ik][quartet[2]]; |
| 244 | + const Complex& dd = physical[ik][quartet[3]]; |
| 245 | + pauli[ik][i] = uu + dd; |
| 246 | + pauli[ik][component_size + i] = ud + du; |
| 247 | + pauli[ik][2 * component_size + i] = imaginary * (ud - du); |
| 248 | + pauli[ik][3 * component_size + i] = uu - dd; |
| 249 | + } |
| 250 | + } |
| 251 | + return pauli; |
| 252 | +} |
| 253 | + |
| 254 | +std::vector<std::vector<Complex>> to_physical_nspin4( |
| 255 | + const std::vector<const std::vector<Complex>*>& pauli, |
| 256 | + const Parallel_2D& pv, |
| 257 | + const bool column_major) |
| 258 | +{ |
| 259 | + const std::vector<SpinorOffsets> offsets = build_spinor_offsets(pv, column_major); |
| 260 | + const std::size_t local_size = static_cast<std::size_t>(pv.get_local_size()); |
| 261 | + validate_nspin4_pauli(pauli, local_size); |
| 262 | + |
| 263 | + const std::size_t component_size = offsets.size(); |
| 264 | + const Complex imaginary(0.0, 1.0); |
| 265 | + std::vector<std::vector<Complex>> physical(pauli.size(), std::vector<Complex>(local_size)); |
| 266 | + for (std::size_t ik = 0; ik < pauli.size(); ++ik) |
| 267 | + { |
| 268 | + const std::vector<Complex>& packed = *pauli[ik]; |
| 269 | + for (std::size_t i = 0; i < component_size; ++i) |
| 270 | + { |
| 271 | + const Complex& d0 = packed[i]; |
| 272 | + const Complex& dx = packed[component_size + i]; |
| 273 | + const Complex& dy = packed[2 * component_size + i]; |
| 274 | + const Complex& dz = packed[3 * component_size + i]; |
| 275 | + const SpinorOffsets& quartet = offsets[i]; |
| 276 | + physical[ik][quartet[0]] = 0.5 * (d0 + dz); |
| 277 | + physical[ik][quartet[1]] = 0.5 * (dx - imaginary * dy); |
| 278 | + physical[ik][quartet[2]] = 0.5 * (dx + imaginary * dy); |
| 279 | + physical[ik][quartet[3]] = 0.5 * (d0 - dz); |
| 280 | + } |
| 281 | + } |
| 282 | + return physical; |
| 283 | +} |
| 284 | + |
| 285 | +template std::vector<std::vector<double>> to_pauli_nspin2( |
| 286 | + const std::vector<std::vector<double>>& physical); |
| 287 | +template std::vector<std::vector<Complex>> to_pauli_nspin2( |
| 288 | + const std::vector<std::vector<Complex>>& physical); |
| 289 | +template std::vector<std::vector<double>> to_physical_nspin2( |
| 290 | + const std::vector<const std::vector<double>*>& pauli); |
| 291 | +template std::vector<std::vector<Complex>> to_physical_nspin2( |
| 292 | + const std::vector<const std::vector<Complex>*>& pauli); |
| 293 | + |
| 294 | +} // namespace DmkSpinTransform |
0 commit comments