Loading...
Searching...
No Matches
IDPPObjectiveFunction.hpp
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
13#pragma once
14
15#include "Matter.h"
16#include "ObjectiveFunction.h"
17#include "Parameters.h"
18#include <Eigen/Dense>
19#include <cmath>
20#include <vector>
21
22namespace eonc {
23
25 std::shared_ptr<Matter> matter;
26
27public:
28 IDPPObjectiveFunction(std::shared_ptr<Matter> matterPassed,
29 const Parameters &paramsPassed,
30 const MatrixXd &targetDistances)
31 : ObjectiveFunction(paramsPassed),
32 matter{std::move(matterPassed)},
33 d_target(targetDistances) {
34
35 // Initialize working variables to avoid re-allocation
36 int natoms = matter->numberOfAtoms();
37 }
38
39 // IDPP Energy: E = 0.5 * sum( w * (r_ij - d_target_ij)^2 )
40 // w = 1 / r_ij^4
41 double getEnergy() override;
42
43 // IDPP Gradient
44 VectorXd getGradient(bool fdstep = false) override;
45
46 // Standard Interface Plumbing
47 void setPositions(const VectorXd &x) override {
48 // Map 3N vector back to Matter
49 matter->setPositions(AtomMatrix::Map(x.data(), matter->numberOfAtoms(), 3));
50 }
51
52 VectorXd getPositions() override {
53 // Map Matter positions to 3N vector
54 return VectorXd::Map(matter->getPositions().data(),
55 3 * matter->numberOfAtoms());
56 }
57
58 int degreesOfFreedom() override { return 3 * matter->numberOfAtoms(); }
59
60 bool isConverged() override {
61 return getConvergence() < params.neb_options.initialization.force_tolerance;
62 }
63
64 double getConvergence() override {
65 // Return max force component or norm depending on preference
66 // Using norm here for simplicity in path generation
67 return getGradient().norm();
68 }
69
70 // Handles PBC difference correctly using the Matter object
71 VectorXd difference(const VectorXd &a, const VectorXd &b) override {
72 return matter->pbcV(a - b);
73 }
74
75private:
76 MatrixXd d_target; // The interpolated "ideal" distances
77};
78
80public:
81 CollectiveIDPPObjectiveFunction(std::vector<Matter> &pathRef,
82 const Parameters &paramsPassed)
83 : ObjectiveFunction(paramsPassed),
84 path(pathRef) {
85
86 // Initialize distances for endpoints
87 dInit = getDistanceMatrix(path.front());
89 }
90
91 // Return total energy (IDPP + Springs) - Optional for optimization but good
92 // for debugging
93 double getEnergy() override { return 0.0; }
94
95 VectorXd getGradient(bool fdstep = false) override;
96
97 // Plumbing to map the entire path (all images) to one vector
98 void setPositions(const VectorXd &x) override {
99 int atoms = path[0].numberOfAtoms();
100 // Skip endpoints (0 and N+1)
101 for (size_t i = 1; i < path.size() - 1; ++i) {
102 path[i].setPositions(AtomMatrix::Map(
103 x.segment(3 * atoms * (i - 1), 3 * atoms).data(), atoms, 3));
104 }
105 }
106
107 VectorXd getPositions() override {
108 int atoms = path[0].numberOfAtoms();
109 int n_free_images = path.size() - 2;
110 VectorXd pos(3 * atoms * n_free_images);
111
112 for (size_t i = 1; i < path.size() - 1; ++i) {
113 pos.segment(3 * atoms * (i - 1), 3 * atoms) =
114 VectorXd::Map(path[i].getPositions().data(), 3 * atoms);
115 }
116 return pos;
117 }
118
119 int degreesOfFreedom() override {
120 return 3 * path[0].numberOfAtoms() * (path.size() - 2);
121 }
122
123 // Check convergence of the IDPP-NEB
124 bool isConverged() override {
125 return getConvergence() < params.neb_options.initialization.force_tolerance;
126 }
127
128 double getConvergence() override { return lastMaxForce; }
129
130 VectorXd difference(const VectorXd &a, const VectorXd &b) override {
131 // Simple difference for this purpose, assuming pre-aligned or handling PBC
132 // inside
133 return a - b;
134 }
135
136private:
137 std::vector<Matter> &path;
139 double lastMaxForce = 100.0;
140
142 MatrixXd getIDPPForces(const Matter &m, const MatrixXd &dTarget);
143};
144
146public:
147 std::shared_ptr<ObjectiveFunction> idpp_obj;
148 std::shared_ptr<Potential> zbl_pot;
149 std::vector<Matter> &path; // Reference to the actual path vector
151
152 ZBLRepulsiveIDPPObjective(std::shared_ptr<ObjectiveFunction> idpp,
153 std::shared_ptr<Potential> zbl,
154 std::vector<Matter> &p, const Parameters &params,
155 double weight = 1.0)
157 idpp_obj(idpp),
158 zbl_pot(zbl),
159 path(p),
160 zbl_weight(weight) {}
161
162 double getEnergy() override {
163 // IDPP "Energy" (Residual) + ZBL Energy
164 return idpp_obj->getEnergy();
165 }
166
167 VectorXd getGradient(bool fdstep = false) override {
168 // 1. Get IDPP Gradient (forces atoms towards interpolated distances)
169 VectorXd grad = idpp_obj->getGradient(fdstep);
170
171 // 2. Calculate ZBL Forces for every image
172 int n_images = path.size();
173 int atoms_per_image = path[0].numberOfAtoms();
174
175 // ZBL calculation loop
176 for (int i = 1; i < n_images - 1; ++i) { // Skip endpoints
177 AtomMatrix forces = MatrixXd::Zero(atoms_per_image, 3);
178 double energy = 0;
179
180 // Calculate ZBL forces for this image
181 zbl_pot->force(atoms_per_image, path[i].getPositions().data(),
182 path[i].getAtomicNrs().data(), forces.data(), &energy,
183 nullptr, path[i].getCell().data());
184
185 int segment_start = (i - 1) * 3 * atoms_per_image;
186 VectorXd zbl_grad_vec = VectorXd::Map(forces.data(), 3 * atoms_per_image);
187
188 // Add repulsive push (negate force to get gradient)
189 grad.segment(segment_start, 3 * atoms_per_image) -=
190 (zbl_grad_vec * zbl_weight);
191 }
192
193 return grad;
194 }
195
196 // Delegate other methods
197 void setPositions(const VectorXd &x) override { idpp_obj->setPositions(x); }
198 VectorXd getPositions() override { return idpp_obj->getPositions(); }
199 int degreesOfFreedom() override { return idpp_obj->degreesOfFreedom(); }
200 bool isConverged() override { return idpp_obj->isConverged(); }
201 double getConvergence() override { return idpp_obj->getConvergence(); }
202
203 VectorXd difference(const VectorXd &a, const VectorXd &b) override {
204 return idpp_obj->difference(a, b);
205 }
206};
207
208} // namespace eonc
209
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, eOnStorageOrder > MatrixXd
Definition Eigen.h:33
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
CollectiveIDPPObjectiveFunction(std::vector< Matter > &pathRef, const Parameters &paramsPassed)
void setPositions(const VectorXd &x) override
MatrixXd getIDPPForces(const Matter &m, const MatrixXd &dTarget)
CollectiveIDPPObjectiveFunction(std::vector< Matter > &pathRef, const Parameters &paramsPassed)
VectorXd difference(const VectorXd &a, const VectorXd &b) override
VectorXd getGradient(bool fdstep=false) override
std::shared_ptr< Matter > matter
VectorXd difference(const VectorXd &a, const VectorXd &b) override
VectorXd getGradient(bool fdstep=false) override
IDPPObjectiveFunction(std::shared_ptr< Matter > matterPassed, const Parameters &paramsPassed, const MatrixXd &targetDistances)
void setPositions(const VectorXd &x) override
ObjectiveFunction(const Parameters &paramsPassed)
const Parameters & params
std::shared_ptr< Potential > zbl_pot
void setPositions(const VectorXd &x) override
std::shared_ptr< ObjectiveFunction > idpp_obj
VectorXd difference(const VectorXd &a, const VectorXd &b) override
ZBLRepulsiveIDPPObjective(std::shared_ptr< ObjectiveFunction > idpp, std::shared_ptr< Potential > zbl, std::vector< Matter > &p, const Parameters &params, double weight=1.0)
VectorXd getGradient(bool fdstep=false) override
RAII resource manager for the ARTn C library with global synchronization.