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), matter{std::move(matterPassed)},
32 d_target(targetDistances) {}
33
34 // IDPP Energy: E = 0.5 * sum( w * (r_ij - d_target_ij)^2 )
35 // w = 1 / r_ij^4
36 double getEnergy() override;
37
38 // IDPP Gradient
39 VectorXd getGradient(bool fdstep = false) override;
40
41 // Free-atom DOF only. Full-3N writes let nearby movers drag frozen atoms
42 // (TheochemUI/eOn#410). Same contract as MatterObjectiveFunction.
43 void setPositions(const VectorXd &x) override {
44 matter->setPositionsFreeV(x);
45 }
46
47 VectorXd getPositions() override { return matter->getPositionsFreeV(); }
48
49 int degreesOfFreedom() override {
50 return 3 * static_cast<int>(matter->numberOfFreeAtoms());
51 }
52
53 bool isConverged() override {
54 return getConvergence() <
55 params.neb_options().initialization.force_tolerance;
56 }
57
58 double getConvergence() override {
59 // Return max force component or norm depending on preference
60 // Using norm here for simplicity in path generation
61 return getGradient().norm();
62 }
63
64 // Handles PBC difference correctly using the Matter object
65 VectorXd difference(const VectorXd &a, const VectorXd &b) override {
66 return matter->pbcV(a - b);
67 }
68
69private:
70 MatrixXd d_target; // The interpolated "ideal" distances
71};
72
74public:
75 CollectiveIDPPObjectiveFunction(std::vector<Matter> &pathRef,
76 const Parameters &paramsPassed)
77 : ObjectiveFunction(paramsPassed), path(pathRef) {
78
79 // Initialize distances for endpoints
80 dInit = getDistanceMatrix(path.front());
82 }
83
84 // Return total energy (IDPP + Springs) - Optional for optimization but good
85 // for debugging
86 double getEnergy() override { return 0.0; }
87
88 VectorXd getGradient(bool fdstep = false) override;
89
90 void setPositions(const VectorXd &x) override {
91 const int nfree = static_cast<int>(path[0].numberOfFreeAtoms());
92 const int seg = 3 * nfree;
93 for (size_t i = 1; i < path.size() - 1; ++i) {
94 path[i].setPositionsFreeV(x.segment(seg * static_cast<int>(i - 1), seg));
95 }
96 }
97
98 VectorXd getPositions() override {
99 const int nfree = static_cast<int>(path[0].numberOfFreeAtoms());
100 const int seg = 3 * nfree;
101 const int n_free_images = static_cast<int>(path.size()) - 2;
102 VectorXd pos(seg * n_free_images);
103 for (size_t i = 1; i < path.size() - 1; ++i) {
104 pos.segment(seg * static_cast<int>(i - 1), seg) =
105 path[i].getPositionsFreeV();
106 }
107 return pos;
108 }
109
110 int degreesOfFreedom() override {
111 return 3 * static_cast<int>(path[0].numberOfFreeAtoms()) *
112 (static_cast<int>(path.size()) - 2);
113 }
114
115 // Check convergence of the IDPP-NEB
116 bool isConverged() override {
117 return getConvergence() <
118 params.neb_options().initialization.force_tolerance;
119 }
120
121 double getConvergence() override { return lastMaxForce; }
122
123 VectorXd difference(const VectorXd &a, const VectorXd &b) override {
124 // Simple difference for this purpose, assuming pre-aligned or handling PBC
125 // inside
126 return a - b;
127 }
128
129private:
130 std::vector<Matter> &path;
132 double lastMaxForce = 100.0;
133
135 MatrixXd getIDPPForces(const Matter &m, const MatrixXd &dTarget);
136};
137
139public:
140 std::shared_ptr<ObjectiveFunction> idpp_obj;
141 std::shared_ptr<Potential> zbl_pot;
142 std::vector<Matter> &path; // Reference to the actual path vector
144
145 ZBLRepulsiveIDPPObjective(std::shared_ptr<ObjectiveFunction> idpp,
146 std::shared_ptr<Potential> zbl,
147 std::vector<Matter> &p, const Parameters &params,
148 double weight = 1.0)
149 : ObjectiveFunction(params), idpp_obj(idpp), zbl_pot(zbl), path(p),
150 zbl_weight(weight) {}
151
152 double getEnergy() override {
153 // IDPP "Energy" (Residual) + ZBL Energy
154 return idpp_obj->getEnergy();
155 }
156
157 VectorXd getGradient(bool fdstep = false) override {
158 // 1. Get IDPP Gradient (forces atoms towards interpolated distances)
159 VectorXd grad = idpp_obj->getGradient(fdstep);
160
161 // 2. Calculate ZBL Forces for every image
162 int n_images = path.size();
163 int atoms_per_image = path[0].numberOfAtoms();
164
165 const int nfree = static_cast<int>(path[0].numberOfFreeAtoms());
166 const int seg = 3 * nfree;
167 for (int i = 1; i < n_images - 1; ++i) {
168 AtomMatrix forces = MatrixXd::Zero(atoms_per_image, 3);
169 double energy = 0;
170
171 zbl_pot->force(atoms_per_image, path[i].getPositions().data(),
172 path[i].getAtomicNrs().data(), forces.data(), &energy,
173 nullptr, path[i].getCell().data());
174
175 VectorXd zbl_free(seg);
176 long k = 0;
177 for (int a = 0; a < atoms_per_image; ++a) {
178 if (!path[i].getFixed(a)) {
179 zbl_free[k++] = forces(a, 0);
180 zbl_free[k++] = forces(a, 1);
181 zbl_free[k++] = forces(a, 2);
182 }
183 }
184 grad.segment((i - 1) * seg, seg) -= (zbl_free * zbl_weight);
185 }
186
187 return grad;
188 }
189
190 // Delegate other methods
191 void setPositions(const VectorXd &x) override { idpp_obj->setPositions(x); }
192 VectorXd getPositions() override { return idpp_obj->getPositions(); }
193 int degreesOfFreedom() override { return idpp_obj->degreesOfFreedom(); }
194 bool isConverged() override { return idpp_obj->isConverged(); }
195 double getConvergence() override { return idpp_obj->getConvergence(); }
196
197 VectorXd difference(const VectorXd &a, const VectorXd &b) override {
198 return idpp_obj->difference(a, b);
199 }
200};
201
202} // namespace eonc
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
void setPositions(const VectorXd &x) override
CollectiveIDPPObjectiveFunction(std::vector< Matter > &pathRef, const Parameters &paramsPassed)
VectorXd difference(const VectorXd &a, const VectorXd &b) override
VectorXd getGradient(bool fdstep=false) override
MatrixXd getIDPPForces(const Matter &m, const MatrixXd &dTarget)
std::shared_ptr< Matter > matter
VectorXd difference(const VectorXd &a, const VectorXd &b) override
IDPPObjectiveFunction(std::shared_ptr< Matter > matterPassed, const Parameters &paramsPassed, const MatrixXd &targetDistances)
void setPositions(const VectorXd &x) override
VectorXd getGradient(bool fdstep=false) 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.