33 bool tsInterpolate =
false;
34 std::shared_ptr<Matter> transitionState =
nullptr;
36 std::filesystem::exists(
"ts.con")) {
37 std::string transitionStateFilename =
39 transitionState = std::make_shared<Matter>(
pot,
params);
42 transitionState->con2matter(transitionStateFilename))) {
44 throw std::runtime_error(
"failed to load " + transitionStateFilename);
48 auto initial = std::make_shared<Matter>(
pot,
params);
49 auto final_state = std::make_shared<Matter>(
pot,
params);
54 const bool usePathEndpoints =
56 !
params.neb_options.initialization.input_path.empty();
58 if (usePathEndpoints) {
60 params.neb_options.initialization.input_path);
61 if (file_paths.size() < 2) {
62 throw std::runtime_error(
63 "NEB initial_path_in must list at least two frames "
64 "(reactant and product endpoints)");
66 if (!
eonc::io::io_ok(initial->con2matter(file_paths.front().string()))) {
68 file_paths.front().string());
69 throw std::runtime_error(
"failed to load NEB path reactant frame");
71 if (!
eonc::io::io_ok(final_state->con2matter(file_paths.back().string()))) {
73 file_paths.back().string());
74 throw std::runtime_error(
"failed to load NEB path product frame");
77 "NEB endpoints from initial path list (skipped {} / {})",
78 reactantFilename, productFilename);
82 throw std::runtime_error(
"failed to load reactant.con");
86 throw std::runtime_error(
"failed to load product.con");
98 bool shouldMinimizeEndpoints =
false;
99 if (!
params.neb_options.endpoints.minimize) {
100 QUILL_LOG_DEBUG(
m_log,
101 "minimize_endpoints == false: not minimizing endpoints.");
102 shouldMinimizeEndpoints =
false;
104 if (
params.neb_options.initialization.input_path.empty()) {
105 QUILL_LOG_DEBUG(
m_log,
"minimize_endpoints == true and nebIpath "
106 "empty: minimizing endpoints.");
107 shouldMinimizeEndpoints =
true;
111 if (
params.neb_options.endpoints.use_path_file) {
114 "minimize_endpoints == true and nebIpath provided, but "
115 "minimize_endpoints_for_ipath == true: minimizing endpoints.");
116 shouldMinimizeEndpoints =
true;
120 "minimize_endpoints == true but nebIpath provided and "
121 "minimize_endpoints_for_ipath == false: not minimizing endpoints.");
122 shouldMinimizeEndpoints =
false;
127 if (shouldMinimizeEndpoints) {
128 QUILL_LOG_DEBUG(
m_log,
"Minimizing reactant");
131 initial->relax(
false,
params.debug_options.write_movies,
132 params.main_options.checkpoint,
"react_neb",
"react_neb");
136 QUILL_LOG_DEBUG(
m_log,
"Minimized reactant in ");
137 QUILL_LOG_DEBUG(
m_log,
"Minimizing product");
138 final_state->relax(
false,
params.debug_options.write_movies,
139 params.main_options.checkpoint,
"prod_neb",
"prod_neb");
143 std::make_unique<NudgedElasticBand>(initial, final_state,
params,
pot);
146 AtomMatrix reactantToTS = transitionState->pbc(
147 transitionState->getPositions() - initial->getPositions());
148 AtomMatrix TSToProduct = transitionState->pbc(
149 final_state->getPositions() - transitionState->getPositions());
150 for (
int image = 1; image <=
neb->numImages; image++) {
151 int mid =
neb->numImages / 2 + 1;
153 double frac =
static_cast<double>(image) /
static_cast<double>(mid);
154 neb->path[image]->setPositions(initial->getPositions() +
155 frac * reactantToTS);
156 }
else if (image > mid) {
157 double frac =
static_cast<double>(image - mid) /
158 static_cast<double>(
neb->numImages - mid + 1);
159 neb->path[image]->setPositions(transitionState->getPositions() +
161 }
else if (image == mid) {
162 neb->path[image]->setPositions(transitionState->getPositions());
168 status =
neb->compute();
172 neb->printImageData();
184 std::string resultsFilename(
"results.dat");
188 std::ofstream out(resultsFilename, std::ios::binary);
190 QUILL_LOG_ERROR(
m_log,
"Failed to open {} for writing", resultsFilename);
194 out << std::format(
"{} termination_reason\n",
static_cast<int>(status));
195 out << std::format(
"{} termination_reason_text\n",
196 magic_enum::enum_name(status));
198 "{} potential_type\n",
199 magic_enum::enum_name<PotType>(
params.potential_options.potential));
200 out << std::format(
"{} total_force_calls\n",
202 out << std::format(
"{} force_calls_neb\n",
fCallsNEB);
203 out << std::format(
"{:f} energy_reference\n",
204 neb->path[0]->getPotentialEnergy());
205 out << std::format(
"{} number_of_images\n",
neb->numImages);
207 for (
long i = 0; i <=
neb->numImages + 1; i++) {
208 out << std::format(
"{:f} image{}_energy\n",
209 neb->path[i]->getPotentialEnergy() -
210 neb->path[0]->getPotentialEnergy(),
212 out << std::format(
"{:f} image{}_force\n",
213 neb->path[i]->getForces().norm(), i);
214 double proj_norm = (i >= 1 && i <=
neb->numImages)
215 ?
neb->projectedForce[i]->norm()
217 out << std::format(
"{:f} image{}_projected_force\n", proj_norm, i);
220 long safeNumExtrema = std::min(
221 neb->numExtrema,
static_cast<long>(
neb->extremumPosition.size()));
222 out << std::format(
"{} number_of_extrema\n", safeNumExtrema);
223 for (
long i = 0; i < safeNumExtrema; i++) {
224 out << std::format(
"{:f} extremum{}_position\n",
neb->extremumPosition[i],
226 out << std::format(
"{:f} extremum{}_energy\n",
neb->extremumEnergy[i], i);
231 std::string nebFilename(
"neb.con");
234 neb->path,
neb->tangent,
neb->eigenmode_solvers,
neb->numImages,
235 params.debug_options.estimate_neb_eigenvalues, nebFilename))) {
236 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", nebFilename);
240 std::string spFilename(
"sp.con");
242 neb->path[
neb->maxEnergyImage]->matter2con(spFilename))) {
243 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", spFilename);
248 if (
params.neb_options.mmf_peaks.enabled &&
neb->numExtrema > 0) {
250 for (
long i = 0; i <
neb->numExtrema; i++) {
254 double relativeEnergy =
255 neb->extremumEnergy[i] -
neb->path[0]->getPotentialEnergy();
257 if (
neb->extremumCurvature[i] < 0 &&
258 relativeEnergy >
params.neb_options.mmf_peaks.tolerance) {
259 double posFraction =
neb->extremumPosition[i];
260 int leftIdx =
static_cast<int>(std::floor(posFraction));
261 double f = posFraction - leftIdx;
263 if (leftIdx < 0 || leftIdx >=
neb->numImages + 1)
268 *
neb->path[leftIdx], *
neb->path[leftIdx + 1], f);
269 std::string peakPosFile = std::format(
"peak{:02d}_pos.con", peakCount);
271 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", peakPosFile);
277 f * (*
neb->tangent[leftIdx + 1]);
278 peakMode.normalize();
280 std::string peakModeFile =
281 std::format(
"peak{:02d}_mode.dat", peakCount);
283 std::ofstream modeOut(peakModeFile);
285 for (
long row = 0; row < peakMode.rows(); ++row) {
286 modeOut << std::format(
"{:.17g} {:.17g} {:.17g}\n",
287 peakMode(row, 0), peakMode(row, 1),
296 "Generated MMF peak {:02d} at position {:.3f} (Energy: {:.3f} eV)",
297 peakCount, posFraction, relativeEnergy);
304 neb->printImageData(
true, std::numeric_limits<size_t>::max());
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)
Write a NEB band as a multi-frame .con via readcon ConFrameBuilder::clone().