Loading...
Searching...
No Matches
NEBZoom.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#include "eon/NEBZoom.h"
14
15#include <algorithm>
16#include <cmath>
17#include <span>
18#include <utility>
19
20namespace eonc::neb::zoom {
21namespace {
22
23std::pair<std::size_t, std::size_t>
24manualWindow(std::size_t n, std::size_t climbingImage, int offset) {
25 const auto half = static_cast<std::size_t>(std::max(offset, 1));
26 const std::size_t lo = climbingImage > half ? climbingImage - half : 0;
27 const std::size_t hi = std::min(n - 1, climbingImage + half);
28 return {lo, hi};
29}
30
31std::vector<Matter> linearResample(const std::vector<Matter> &sub,
32 std::size_t count) {
33 const std::size_t n = sub.size();
34 std::vector<double> arc(n, 0.0);
35 for (std::size_t i = 1; i < n; ++i) {
36 AtomMatrix diff =
37 sub[i].pbc(sub[i].getPositions() - sub[i - 1].getPositions());
38 arc[i] = arc[i - 1] + diff.norm();
39 }
40 const double total = arc.back();
41 std::vector<Matter> placed;
42 placed.reserve(count);
43 if (count == 0) {
44 return placed;
45 }
46 placed.push_back(sub.front());
47 for (std::size_t i = 1; i + 1 < count; ++i) {
48 const double target = (total > 1e-12) ? total * static_cast<double>(i) /
49 static_cast<double>(count - 1)
50 : 0.0;
51 std::size_t lo = 0;
52 for (std::size_t j = 1; j < n; ++j) {
53 if (arc[j] >= target) {
54 lo = j - 1;
55 break;
56 }
57 }
58 const std::size_t hi = std::min(lo + 1, n - 1);
59 const double span = arc[hi] - arc[lo];
60 const double f = (span > 1e-12) ? (target - arc[lo]) / span : 0.0;
61 Matter image(sub.front());
62 image.setPositions((1.0 - f) * sub[lo].getPositions() +
63 f * sub[hi].getPositions());
64 placed.push_back(std::move(image));
65 }
66 if (count > 1) {
67 placed.push_back(sub.back());
68 }
69 return placed;
70}
71
72} // namespace
73
74Window selectWindow(const std::vector<double> &energy,
75 std::size_t climbingImage,
77 Window window;
78 const std::size_t n = energy.size();
79 if (n < 3 || climbingImage == 0 || climbingImage + 1 >= n) {
80 return window;
81 }
82
83 std::size_t lo = 0;
84 std::size_t hi = 0;
86 std::tie(lo, hi) = manualWindow(n, climbingImage, cfg.offset);
87 } else {
88 const double eRef = std::min(energy.front(), energy.back());
89 const double eMax = *std::max_element(energy.begin(), energy.end());
90 const double barrier = eMax - eRef;
91 const bool usable = barrier > 0.0 && cfg.alpha > 0.0 && cfg.alpha < 1.0;
92 if (!usable) {
93 std::tie(lo, hi) = manualWindow(n, climbingImage, cfg.offset);
94 } else {
95 const double threshold = eRef + cfg.alpha * barrier;
96 lo = climbingImage;
97 hi = climbingImage;
98 while (lo > 0 && energy[lo - 1] > threshold) {
99 --lo;
100 }
101 while (hi + 1 < n && energy[hi + 1] > threshold) {
102 ++hi;
103 }
104 if (hi <= lo) {
105 std::tie(lo, hi) = manualWindow(n, climbingImage, cfg.offset);
106 }
107 }
108 }
109 if (hi <= lo || hi >= n) {
110 return window;
111 }
112 window.lo = lo;
113 window.hi = hi;
114 window.valid = true;
115 return window;
116}
117
118bool redistributePath(std::vector<std::shared_ptr<Matter>> &path, Window window,
120 if (!window.valid || path.size() < 3 || window.hi <= window.lo ||
121 window.hi >= path.size()) {
122 return false;
123 }
124 for (const auto &image : path) {
125 if (!image) {
126 return false;
127 }
128 }
129
130 std::vector<Matter> sub;
131 sub.reserve(window.hi - window.lo + 1);
132 for (std::size_t i = window.lo; i <= window.hi; ++i) {
133 sub.push_back(*path[i]);
134 }
135
136 std::vector<Matter> placed;
138 placed = linearResample(sub, path.size());
139 } else if (sub.size() == path.size()) {
140 std::vector<std::shared_ptr<Matter>> tmp;
141 tmp.reserve(sub.size());
142 for (const auto &image : sub) {
143 tmp.push_back(std::make_shared<Matter>(image));
144 }
146 std::span<std::shared_ptr<Matter>>{tmp.data(), tmp.size()});
147 placed.reserve(tmp.size());
148 for (const auto &image : tmp) {
149 placed.push_back(*image);
150 }
151 } else {
152 placed = eonc::helpers::neb_paths::resamplePath(sub, path.size() - 2);
153 }
154 if (placed.size() != path.size()) {
155 return false;
156 }
157 for (std::size_t i = 0; i < path.size(); ++i) {
158 path[i]->setPositions(placed[i].getPositions());
159 }
160 return true;
161}
162
163} // namespace eonc::neb::zoom
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
void setPositions(const AtomMatrix &pos)
Definition Matter.cpp:350
std::vector< Matter > resamplePath(const std::vector< Matter > &densePath, size_t targetCount)
void resamplePathInPlace(std::span< std::shared_ptr< Matter > > path)
In-place path reparameterization for NEB shared_ptr paths.
Window selectWindow(const std::vector< double > &energy, std::size_t climbingImage, const neb_options_t::zoom_options_t &cfg)
Auto: contiguous images around the climbing image whose energy is above E_ref + alpha * barrier.
Definition NEBZoom.cpp:74
bool redistributePath(std::vector< std::shared_ptr< Matter > > &path, Window window, neb_options_t::zoom_options_t::Interpolation how)
Place every band image on the window by equal arc length.
Definition NEBZoom.cpp:118
Inclusive image indices on the current band.
Definition NEBZoom.h:25
Zoom-NEB packs every image onto a window around the climbing image once that image is stable and the ...