97 Real distance_tolerance = 1e-6) {
98 lattice_vectors_.clear();
99 if (grid_dimension_x <= 0 || grid_dimension_y <= 0 || grid_dimension_z <= 0) {
100 throw std::invalid_argument(
101 "WignerSeitzSupercell::compute: every grid dimension must be positive");
103 const std::array<int, 3> grid_dimensions = {
104 grid_dimension_x, grid_dimension_y, grid_dimension_z};
105 const int number_of_kpoints = grid_dimension_x * grid_dimension_y * grid_dimension_z;
108 for (
int axis = 0; axis < 3; axis++) {
112 throw std::invalid_argument(
113 "WignerSeitzSupercell::compute: the lattice vectors are linearly dependent");
121 Real longest_diagonal = 0.0;
122 for (
Real sign_y : {-1.0, 1.0}) {
123 for (
Real sign_z : {-1.0, 1.0}) {
125 superlattice[0] + superlattice[1] * sign_y + superlattice[2] * sign_z;
126 longest_diagonal = std::max(longest_diagonal, diagonal.
norm());
129 const Real ball_radius = 0.5 * longest_diagonal;
133 std::array<Real, 3> dual_norm = {0.0, 0.0, 0.0};
134 for (
int axis = 0; axis < 3; axis++) {
135 Vector3D dual(inverse_superlattice[0][axis],
136 inverse_superlattice[1][axis],
137 inverse_superlattice[2][axis]);
138 dual_norm[axis] = dual.
norm();
141 Real shortest_superlattice_length = superlattice[0].norm();
142 for (
int axis = 1; axis < 3; axis++) {
143 shortest_superlattice_length =
144 std::min(shortest_superlattice_length, superlattice[axis].norm());
146 const Real absolute_tolerance = distance_tolerance * shortest_superlattice_length;
153 std::array<int, 3> candidate_bound = {0, 0, 0};
154 for (
int axis = 0; axis < 3; axis++) {
155 candidate_bound[axis] =
157 static_cast<int>(std::floor(
158 grid_dimensions[axis] * ball_radius * dual_norm[axis] + 1e-9)));
162 Real farthest_candidate = 0.0;
163 for (
Real sign_y : {-1.0, 1.0}) {
164 for (
Real sign_z : {-1.0, 1.0}) {
168 farthest_candidate = std::max(farthest_candidate, corner.
norm());
178 std::max(farthest_candidate + ball_radius, longest_diagonal) + absolute_tolerance;
179 std::array<int, 3> image_bound = {0, 0, 0};
180 for (
int axis = 0; axis < 3; axis++) {
182 std::max(1,
static_cast<int>(std::ceil(reach * dual_norm[axis] - 1e-9)));
185 std::vector<Vector3D> superlattice_translations;
186 for (
int image_x = -image_bound[0]; image_x <= image_bound[0]; image_x++) {
187 for (
int image_y = -image_bound[1]; image_y <= image_bound[1]; image_y++) {
188 for (
int image_z = -image_bound[2]; image_z <= image_bound[2]; image_z++) {
189 superlattice_translations.push_back(
190 superlattice[0] *
static_cast<Real>(image_x) +
191 superlattice[1] *
static_cast<Real>(image_y) +
192 superlattice[2] *
static_cast<Real>(image_z));
197 for (
int index_x = -candidate_bound[0]; index_x <= candidate_bound[0]; index_x++) {
198 for (
int index_y = -candidate_bound[1]; index_y <= candidate_bound[1]; index_y++) {
199 for (
int index_z = -candidate_bound[2]; index_z <= candidate_bound[2]; index_z++) {
204 Real own_distance_squared = candidate.
norm2();
205 Real minimum_distance_squared = own_distance_squared;
206 for (
const Vector3D& translation : superlattice_translations) {
207 Vector3D shifted = candidate - translation;
208 minimum_distance_squared =
209 std::min(minimum_distance_squared, shifted.
norm2());
212 Real own_distance = std::sqrt(own_distance_squared);
213 Real minimum_distance = std::sqrt(minimum_distance_squared);
214 if (own_distance > minimum_distance + absolute_tolerance) {
219 for (
const Vector3D& translation : superlattice_translations) {
220 Vector3D shifted = candidate - translation;
221 if (std::abs(shifted.
norm() - minimum_distance) <= absolute_tolerance) {
225 lattice_vectors_.push_back(
231 if (lattice_vectors_.empty()) {
232 throw std::runtime_error(
233 "WignerSeitzSupercell::compute: no lattice vectors retained -- the search range "
234 "cannot cover the Wigner-Seitz cell of this lattice");
238 if (std::abs(weight -
static_cast<Real>(number_of_kpoints)) >
239 1e-8 *
static_cast<Real>(number_of_kpoints)) {
240 throw std::runtime_error(
241 "WignerSeitzSupercell::compute: the retained set sums to a weight of " +
242 std::to_string(weight) +
" but the " + std::to_string(grid_dimension_x) +
"x" +
243 std::to_string(grid_dimension_y) +
"x" + std::to_string(grid_dimension_z) +
244 " mesh has " + std::to_string(number_of_kpoints) +
245 " k-points. The two must agree for the k -> R -> k transform to be an "
246 "identity; this is a defect in the Wigner-Seitz construction, not in the input.");