28 auto true_params = std::make_shared<Parameters>(
params);
29 true_params->main_options.job =
params.sub_job;
30 auto initial = std::make_shared<Matter>(
pot, *true_params);
33 throw std::runtime_error(
"failed to load " + reactantFilename);
35 auto final_state = std::make_shared<Matter>(
pot, *true_params);
38 throw std::runtime_error(
"failed to load " + productFilename);
44std::shared_ptr<NudgedElasticBand>
46 std::shared_ptr<Matter> final_state) {
47 if (!initial || !final_state) {
48 throw std::runtime_error(
"GPSurrogateJob::runFromMatter: null Matter");
51 auto true_params = std::make_shared<Parameters>(
params);
52 true_params->main_options.job =
params.sub_job;
55 auto pyparams = std::make_shared<Parameters>(
params);
58 initial->setPotential(
pot);
59 final_state->setPotential(
pot);
61 *initial, *final_state,
params.neb_options.image_count);
65 magic_enum::enum_name<PotType>(
pot->getType()));
69 auto surpot = eonc::helpers::create::makeSurrogatePotential(
71 surpot->train_optimize(features, targets);
72 auto neb = std::make_unique<NudgedElasticBand>(initial, final_state,
74 auto status_neb{
neb->compute()};
75 bool job_not_finished{
true};
77 double unc_conv{pyparams->gp_uncertainty};
78 while (job_not_finished) {
84 EONC_LOG_TRACE(
"Must handle update to the GP, update number {}", n_gp);
85 auto [maxUnc, maxIndex] =
87 auto [feature, target] =
91 surpot->train_optimize(features, targets);
92 pyparams->nebClimbingImageMethod =
false;
93 pyparams->optimizer_options.converged_force =
94 params.optimizer_options.converged_force * 0.8;
95 for (
auto &&obj :
neb->path) {
96 obj->setPotential(surpot);
98 if (!(pyparams->gp_linear_path_always)) {
100 neb = std::make_unique<NudgedElasticBand>(
neb->path, *pyparams, surpot);
103 neb = std::make_unique<NudgedElasticBand>(initial, final_state, *pyparams,
106 status_neb =
neb->compute();
108 std::string nebFilename(std::format(
"neb_final_gpr_{:03d}.con", n_gp));
111 neb->path,
neb->tangent,
neb->eigenmode_solvers,
neb->numImages,
112 params.debug_options.estimate_neb_eigenvalues, nebFilename,
113 static_cast<size_t>(n_gp)))) {
114 throw std::runtime_error(
"Failed to write file: " + nebFilename);
123 neb->printImageData();
126 std::shared_ptr<NudgedElasticBand> out(
neb.release());
135 std::unique_ptr<NudgedElasticBand>
neb) {
136 std::string resultsFilename =
"results.dat";
139 std::ofstream fileResults(resultsFilename);
142 throw std::runtime_error(
"Failed to open file: " + resultsFilename);
145 fileResults << static_cast<int>(status) <<
" termination_reason\n";
146 fileResults << magic_enum::enum_name(status) <<
" termination_reason_text\n";
147 fileResults << magic_enum::enum_name<PotType>(
148 params.potential_options.potential)
149 <<
" potential_type\n";
150 fileResults << std::format(
"{:.6f} energy_reference\n",
151 neb->path[0]->getPotentialEnergy());
152 fileResults <<
neb->numImages <<
" number_of_images\n";
154 for (
long i = 0; i <=
neb->numImages + 1; i++) {
155 fileResults << std::format(
"{:.6f} image{}_energy\n",
156 neb->path[i]->getPotentialEnergy() -
157 neb->path[0]->getPotentialEnergy(),
159 fileResults << std::format(
"{:.6f} image{}_force\n",
160 neb->path[i]->getForces().norm(), i);
161 fileResults << std::format(
"{:.6f} image{}_projected_force\n",
162 neb->projectedForce[i]->norm(), i);
165 fileResults <<
neb->numExtrema <<
" number_of_extrema\n";
166 for (
long i = 0; i <
neb->numExtrema; i++) {
167 fileResults << std::format(
"{:.6f} extremum{}_position\n",
168 neb->extremumPosition[i], i);
169 fileResults << std::format(
"{:.6f} extremum{}_energy\n",
170 neb->extremumEnergy[i], i);
175 std::string nebFilename =
"neb.con";
179 neb->path,
neb->tangent,
neb->eigenmode_solvers,
neb->numImages,
180 params.debug_options.estimate_neb_eigenvalues, nebFilename))) {
181 throw std::runtime_error(
"Failed to write file: " + nebFilename);
185 neb->printImageData(
true);
190 MatrixXd features(matobjs.size(), matobjs.front().numberOfFreeAtoms() * 3);
192 matobjs.front().numberOfFreeAtoms() * 3);
193 for (
long idx{0}; idx < features.rows(); idx++) {
194 features.row(idx) = matobjs[idx].getPositionsFreeV();
196 std::ostringstream oss;
203 MatrixXd features(matobjs.size(), matobjs.front()->numberOfFreeAtoms() * 3);
205 matobjs.front()->numberOfFreeAtoms() * 3);
206 for (
long idx{0}; idx < features.rows(); idx++) {
207 features.row(idx) = matobjs[idx]->getPositionsFreeV();
209 std::ostringstream oss;
215 std::shared_ptr<Potential> true_pot) {
218 const auto nrows = matobjs.size();
219 const auto ncols = (matobjs.front().numberOfFreeAtoms() * 3) + 1;
221 for (
long idx{0}; idx < targets.rows(); idx++) {
222 matobjs[idx].setPotential(true_pot);
223 targets.row(idx)[0] = matobjs[idx].getPotentialEnergy();
224 targets.block(idx, 1, 1, ncols - 1) =
225 matobjs[idx].getForcesFree().array() * -1;
227 std::ostringstream oss;
233 std::shared_ptr<Potential> true_pot) {
234 const auto nrows = matobjs.size();
235 const auto ncols = (matobjs.front()->numberOfFreeAtoms() * 3) + 1;
237 for (
long idx{0}; idx < targets.rows(); idx++) {
238 matobjs[idx]->setPotential(true_pot);
239 targets.row(idx)[0] = matobjs[idx]->getPotentialEnergy();
240 targets.block(idx, 1, 1, ncols - 1) =
241 matobjs[idx]->getForcesFree().array() * -1;
243 std::ostringstream oss;
248std::vector<Matter>
getMidSlice(
const std::vector<Matter> &matobjs) {
250 std::vector<Matter> res;
252 res.push_back(matobjs.front());
257 res.push_back(matobjs.back());
258 res.push_back(matobjs[((matobjs.size() - 2) * 2.0 / 3.0) + 1]);
263 Eigen::VectorXd target(ncols);
271std::pair<double, Eigen::VectorXd::Index>
273 Eigen::VectorXd pathUncertainty{Eigen::VectorXd::Zero(matobjs.size() - 2)};
274 for (
auto idx{0}; idx < pathUncertainty.size(); idx++) {
275 pathUncertainty[idx] = matobjs[idx + 1]->getEnergyVariance();
277 Eigen::VectorXd::Index maxIndex;
278 double maxUnc{pathUncertainty.maxCoeff()};
279 pathUncertainty.maxCoeff(&maxIndex);
283 return std::make_pair(maxUnc, maxIndex);
285std::pair<Eigen::VectorXd, Eigen::VectorXd>
287 std::shared_ptr<Potential> true_pot) {
289 Matter candidate{*matobjs[maxIndex + 1]};
290 return std::make_pair<Eigen::VectorXd, Eigen::VectorXd>(
294 std::shared_ptr<Potential> true_pot) {
295 Eigen::VectorXd predEnergies{Eigen::VectorXd::Zero(matobjs.size())};
296 Eigen::VectorXd trueEnergies{Eigen::VectorXd::Zero(matobjs.size())};
297 Eigen::VectorXd accuracy{Eigen::VectorXd::Zero(matobjs.size())};
298 for (
auto idx{0}; idx < predEnergies.size(); idx++) {
299 predEnergies[idx] = matobjs[idx]->getPotentialEnergy();
300 matobjs[idx]->setPotential(true_pot);
301 trueEnergies[idx] = matobjs[idx]->getPotentialEnergy();
303 accuracy[idx] = std::sqrt(predEnergies[idx] * predEnergies[idx] -
304 trueEnergies[idx] * trueEnergies[idx]);
306 Eigen::VectorXd difference = predEnergies - trueEnergies;
307 auto mae = difference.array()
310 std::ostringstream oss;
312 << predEnergies <<
"\ntrue\n"
313 << trueEnergies <<
"\ndifference\n"
314 << difference <<
"\n MAE: " << mae;
322 assert(m1.cols() == m2.cols());
323 MatrixXd res(m1.rows() + m2.rows(), m2.cols());
328 assert(data.cols() == newrow.size());
329 data.conservativeResize(data.rows() + 1, data.cols());
330 data.row(data.rows() - 1) = newrow;
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, eOnStorageOrder > MatrixXd
#define EONC_LOG_TRACE(...)
Convenience macro for one-shot logging without storing a logger.
#define EONC_LOG_CRITICAL(...)
std::vector< std::string > run() override
Virtual run; used solely for dynamic dispatch.
std::shared_ptr< NudgedElasticBand > runFromMatter(std::shared_ptr< Matter > initial, std::shared_ptr< Matter > final_state)
Matter-first NEB surrogate path (endpoints as Matter).
void saveData(NudgedElasticBand::NEBStatus status, std::unique_ptr< NudgedElasticBand > neb)
std::vector< std::string > returnFiles
std::shared_ptr< Potential > pot
double getPotentialEnergy() const
VectorXd getForcesFreeV() const
VectorXd getPositionsFreeV() const
long int numberOfFreeAtoms() const
void setPotential(std::shared_ptr< Potential > pot)
MatrixXd vertCat(const MatrixXd &m1, const MatrixXd &m2)
void addVectorRow(MatrixXd &data, const Eigen::VectorXd &newrow)
std::vector< Matter > linearPath(const Matter &initImg, const Matter &finalImg, const size_t nimgs)
MatrixXd get_targets(std::vector< Matter > &matobjs, std::shared_ptr< Potential > true_pot)
MatrixXd get_features(const std::vector< Matter > &matobjs)
bool accuratePES(std::vector< std::shared_ptr< Matter > > &matobjs, std::shared_ptr< Potential > true_pot)
std::pair< double, Eigen::VectorXd::Index > getMaxUncertainty(const std::vector< std::shared_ptr< Matter > > &matobjs)
std::vector< Matter > getMidSlice(const std::vector< Matter > &matobjs)
std::pair< Eigen::VectorXd, Eigen::VectorXd > getNewDataPoint(const std::vector< std::shared_ptr< Matter > > &matobjs, std::shared_ptr< Potential > true_pot)
Eigen::VectorXd make_target(Matter &m1, std::shared_ptr< Potential > true_pot)
std::string getRelevantFile(std::string filename)
std::unique_ptr< Job > makeJob(std::unique_ptr< Parameters > params)
constexpr bool io_ok(IoStatus s) noexcept
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().