38 bool tsInterpolate =
false;
39 std::shared_ptr<Matter> transitionState =
nullptr;
41 std::filesystem::exists(
"ts.con")) {
42 std::string transitionStateFilename =
44 transitionState = std::make_shared<Matter>(
pot,
params);
47 transitionState->con2matter(transitionStateFilename))) {
49 throw std::runtime_error(
"failed to load " + transitionStateFilename);
53 auto initial = std::make_shared<Matter>(
pot,
params);
54 auto final_state = std::make_shared<Matter>(
pot,
params);
59 const bool usePathEndpoints =
61 !
params.neb_options().initialization.input_path.empty();
63 if (usePathEndpoints) {
65 params.neb_options().initialization.input_path);
66 if (file_paths.size() < 2) {
67 throw std::runtime_error(
68 "NEB initial_path_in must list at least two frames "
69 "(reactant and product endpoints)");
71 if (!
eonc::io::io_ok(initial->con2matter(file_paths.front().string()))) {
73 file_paths.front().string());
74 throw std::runtime_error(
"failed to load NEB path reactant frame");
76 if (!
eonc::io::io_ok(final_state->con2matter(file_paths.back().string()))) {
78 file_paths.back().string());
79 throw std::runtime_error(
"failed to load NEB path product frame");
82 "NEB endpoints from initial path list (skipped {} / {})",
83 reactantFilename, productFilename);
87 throw std::runtime_error(
"failed to load reactant.con");
91 throw std::runtime_error(
"failed to load product.con");
96 "reactant and product");
99 "reactant and ts.con");
111 bool shouldMinimizeEndpoints =
false;
112 if (!
params.neb_options().endpoints.minimize) {
113 QUILL_LOG_DEBUG(
m_log,
114 "minimize_endpoints == false: not minimizing endpoints.");
115 shouldMinimizeEndpoints =
false;
117 if (
params.neb_options().initialization.input_path.empty()) {
118 QUILL_LOG_DEBUG(
m_log,
"minimize_endpoints == true and nebIpath "
119 "empty: minimizing endpoints.");
120 shouldMinimizeEndpoints =
true;
124 if (
params.neb_options().endpoints.use_path_file) {
127 "minimize_endpoints == true and nebIpath provided, but "
128 "minimize_endpoints_for_ipath == true: minimizing endpoints.");
129 shouldMinimizeEndpoints =
true;
133 "minimize_endpoints == true but nebIpath provided and "
134 "minimize_endpoints_for_ipath == false: not minimizing endpoints.");
135 shouldMinimizeEndpoints =
false;
140 if (shouldMinimizeEndpoints) {
141 QUILL_LOG_DEBUG(
m_log,
"Minimizing reactant");
142 initial->relax(
false,
params.debug_options().write_movies,
143 params.main_options().checkpoint,
"react_neb",
"react_neb");
144 QUILL_LOG_DEBUG(
m_log,
"Minimized reactant");
145 QUILL_LOG_DEBUG(
m_log,
"Minimizing product");
146 final_state->relax(
false,
params.debug_options().write_movies,
147 params.main_options().checkpoint,
"prod_neb",
152 std::make_unique<NudgedElasticBand>(initial, final_state,
params,
pot);
155 AtomMatrix reactantToTS = transitionState->pbc(
156 transitionState->getPositions() - initial->getPositions());
157 AtomMatrix TSToProduct = transitionState->pbc(
158 final_state->getPositions() - transitionState->getPositions());
159 for (
int image = 1; image <=
neb->numImages; image++) {
160 int mid =
neb->numImages / 2 + 1;
162 double frac =
static_cast<double>(image) /
static_cast<double>(mid);
163 neb->path[image]->setPositions(initial->getPositions() +
164 frac * reactantToTS);
165 }
else if (image > mid) {
166 double frac =
static_cast<double>(image - mid) /
167 static_cast<double>(
neb->numImages - mid + 1);
168 neb->path[image]->setPositions(transitionState->getPositions() +
170 }
else if (image == mid) {
171 neb->path[image]->setPositions(transitionState->getPositions());
177 status =
neb->compute();
181 neb->printImageData();
193 std::string resultsFilename(
"results.dat");
201 params.potential_options().potential,
203 env.job_type =
"neb";
204 env.extras.emplace_back(
"force_calls_neb",
static_cast<double>(
fCallsNEB));
205 env.extras.emplace_back(
"energy_reference",
neb->reactantEnergy);
206 env.extras.emplace_back(
"number_of_images",
207 static_cast<double>(
neb->numImages));
208 env.writeResultsDat(resultsFilename);
209 std::ofstream out(resultsFilename, std::ios::binary | std::ios::app);
211 QUILL_LOG_ERROR(
m_log,
"Failed to reopen {} for image keys",
216 for (
long i = 0; i <=
neb->numImages + 1; i++) {
218 "{:f} image{}_energy\n",
219 neb->path[i]->getPotentialEnergy() -
neb->reactantEnergy, i);
220 out << std::format(
"{:f} image{}_force\n",
221 neb->path[i]->getForces().norm(), i);
222 double proj_norm = (i >= 1 && i <=
neb->numImages)
223 ?
neb->projectedForce[i]->norm()
225 out << std::format(
"{:f} image{}_projected_force\n", proj_norm, i);
228 long safeNumExtrema = std::min(
229 neb->numExtrema,
static_cast<long>(
neb->extremumPosition.size()));
230 out << std::format(
"{} number_of_extrema\n", safeNumExtrema);
231 for (
long i = 0; i < safeNumExtrema; i++) {
232 out << std::format(
"{:f} extremum{}_position\n",
neb->extremumPosition[i],
234 out << std::format(
"{:f} extremum{}_energy\n",
neb->extremumEnergy[i], i);
239 std::string nebFilename(
"neb.con");
242 neb->path,
neb->tangent,
neb->eigenmode_solvers,
neb->numImages,
243 params.debug_options().estimate_neb_eigenvalues, nebFilename,
244 std::nullopt,
neb->reactantEnergy))) {
245 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", nebFilename);
249 std::string spFilename(
"sp.con");
251 neb->path[
neb->maxEnergyImage]->matter2con(spFilename))) {
252 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", spFilename);
257 if (
params.neb_options().mmf_peaks.enabled &&
neb->numExtrema > 0) {
259 for (
long i = 0; i <
neb->numExtrema; i++) {
263 double relativeEnergy =
neb->extremumEnergy[i] -
neb->reactantEnergy;
265 if (
neb->extremumCurvature[i] < 0 &&
266 relativeEnergy >
params.neb_options().mmf_peaks.tolerance) {
267 double posFraction =
neb->extremumPosition[i];
268 int leftIdx =
static_cast<int>(std::floor(posFraction));
269 double f = posFraction - leftIdx;
271 if (leftIdx < 0 || leftIdx >=
neb->numImages + 1)
276 *
neb->path[leftIdx], *
neb->path[leftIdx + 1], f);
277 std::string peakPosFile = std::format(
"peak{:02d}_pos.con", peakCount);
279 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", peakPosFile);
285 neb->path,
neb->tangent,
neb->numImages, posFraction);
287 std::string peakModeFile =
288 std::format(
"peak{:02d}_mode.dat", peakCount);
290 std::ofstream modeOut(peakModeFile);
292 for (
long row = 0; row < peakMode.rows(); ++row) {
293 modeOut << std::format(
"{:.17g} {:.17g} {:.17g}\n",
294 peakMode(row, 0), peakMode(row, 1),
303 "Generated MMF peak {:02d} at position {:.3f} (Energy: {:.3f} eV)",
304 peakCount, posFraction, relativeEnergy);
311 neb->printImageData(
true, std::numeric_limits<size_t>::max());
AtomMatrix interpolatedPeakMode(const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, long numImages, double posFraction)
Unit tangent at a fractional image index.
eonc::io::IoStatus writePathCon(const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, const std::vector< std::shared_ptr< EigenmodeStrategy > > &eigenmode_solvers, long numImages, bool estimateEigenvalues, std::string filename, std::optional< size_t > bandIndex, double referenceEnergy)
Write a NEB band as a multi-frame .con via readcon ConFrameBuilder::clone().