51 std::vector<Vector3D> atom_positions_crystal)
52 : cartesian_from_crystal_(real_lattice_bohr.transpose())
53 , crystal_from_cartesian_(real_lattice_bohr.inverse().transpose())
54 , atoms_crystal_(std::move(atom_positions_crystal)) {
63 summarize_spreads(report);
73 if (spreads.empty()) {
76 std::vector<Real> sorted = spreads;
77 std::sort(sorted.begin(), sorted.end());
78 const std::size_t middle = sorted.size() / 2;
80 sorted.size() % 2 == 1 ? sorted[middle] : 0.5 * (sorted[middle - 1] + sorted[middle]);
84 for (
const Real spread : spreads) {
87 mean /=
static_cast<Real>(spreads.size());
89 for (
const Real spread : spreads) {
90 variance += (spread - mean) * (spread - mean);
96 for (std::size_t index = 0; index < spreads.size(); index++) {
101 if (spreads[index] > large) {
107 void locate_centers(WannierQualityReport& report,
108 const std::vector<Vector3D>& centers_bohr)
const {
109 report.shortest_bond_bohr = shortest_bond_bohr_;
110 for (
const Vector3D& center : centers_bohr) {
111 const Vector3D center_crystal = crystal_from_cartesian_ * center;
112 report.nearest_atom_distance_bohr.push_back(
113 nearest_distance(center_crystal, atoms_crystal_));
114 report.nearest_bond_midpoint_distance_bohr.push_back(
115 bond_midpoints_crystal_.empty()
117 : nearest_distance(center_crystal, bond_midpoints_crystal_));
122 [[nodiscard]]
Real nearest_distance(
const Vector3D& point_crystal,
123 const std::vector<Vector3D>& sites_crystal)
const {
125 for (
const Vector3D& site : sites_crystal) {
126 const Real distance = minimum_image_distance(point_crystal - site);
127 if (nearest < 0.0 || distance < nearest) {
135 [[nodiscard]]
Real minimum_image_distance(Vector3D offset_crystal)
const {
136 for (
int axis = 0; axis < 3; axis++) {
137 offset_crystal[axis] -= std::round(offset_crystal[axis]);
139 Real shortest = -1.0;
140 for_each_image(1, [&](
const IntegerVector3D& image) {
141 const Real distance =
142 (cartesian_from_crystal_ * (offset_crystal + translation(image))).norm();
143 if (shortest < 0.0 || distance < shortest) {
157 Vector3D midpoint_crystal;
160 std::vector<Pair> pairs;
161 Real shortest = -1.0;
162 for (std::size_t first = 0; first < atoms_crystal_.size(); first++) {
163 for (std::size_t second = first; second < atoms_crystal_.size(); second++) {
164 for_each_image(BOND_IMAGE_RANGE, [&](
const IntegerVector3D& image) {
165 if (first == second && image == IntegerVector3D{0, 0, 0}) {
168 const Vector3D separation =
169 atoms_crystal_[second] + translation(image) - atoms_crystal_[first];
170 const Real length = (cartesian_from_crystal_ * separation).norm();
171 pairs.push_back({atoms_crystal_[first] + separation * 0.5, length});
172 if (shortest < 0.0 || length < shortest) {
178 shortest_bond_bohr_ = shortest > 0.0 ? shortest : 0.0;
179 for (
const Pair& pair : pairs) {
181 bond_midpoints_crystal_.push_back(pair.midpoint_crystal);
187 template <
typename Visitor>
static void for_each_image(
int range, Visitor visit) {
188 for (
int first = -range; first <= range; first++) {
189 for (
int second = -range; second <= range; second++) {
190 for (
int third = -range; third <= range; third++) {
191 visit(IntegerVector3D{first, second, third});
197 [[nodiscard]]
static Vector3D translation(
const IntegerVector3D& image) {
199 static_cast<Real>(image.x),
static_cast<Real>(image.y),
static_cast<Real>(image.z)};
202 static constexpr int BOND_IMAGE_RANGE = 2;
204 Matrix3x3 cartesian_from_crystal_;
205 Matrix3x3 crystal_from_cartesian_;
206 std::vector<Vector3D> atoms_crystal_;
207 std::vector<Vector3D> bond_midpoints_crystal_;
208 Real shortest_bond_bohr_ = 0.0;