Skip to content

Commit

Permalink
Merge pull request #8 from gg27fyla/master
Browse files Browse the repository at this point in the history
Dipole Scaling Calculation
  • Loading branch information
conradhuebler authored Jun 21, 2024
2 parents 93916d8 + e10cec3 commit 703d340
Show file tree
Hide file tree
Showing 9 changed files with 386 additions and 171 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ option (USE_XTB
"Compile XTB and use as library" OFF)

option (USE_TBLITE
"Compile TBLITE and use as library" OFF)
"Compile TBLITE and use as library" ON)

option (USE_D3
"Compile cpp-D4 and use as library" OFF)
"Compile cpp-D4 and use as library" ON)

option (USE_D4
"Compile cpp-D4 and use as library" OFF)
Expand Down
4 changes: 2 additions & 2 deletions src/capabilities/curcumaopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void CurcumaOpt::ProcessMoleculesSerial(const std::vector<Molecule>& molecules)
double energy = interface.CalculateEnergy(true, true);
#ifdef USE_TBLITE
if (method.compare("gfn2") == 0) {
std::vector<double> dipole = interface.Dipole();
auto dipole = interface.Dipole();
std::cout << std::endl
<< std::endl
<< "Dipole momement (GFN2)" << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) << std::endl;
Expand Down Expand Up @@ -233,7 +233,7 @@ double CurcumaOpt::SinglePoint(const Molecule* initial, const json& controller,
double store = 0;
#ifdef USE_TBLITE
if (method.compare("gfn2") == 0) {
std::vector<double> dipole = interface.Dipole();
auto dipole = interface.Dipole();
std::cout << std::endl
<< std::endl
<< "Dipole momement (GNF2)" << dipole[0] << " " << dipole[1] << " " << dipole[2] << " : " << sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]) * 2.5418 << std::endl;
Expand Down
201 changes: 113 additions & 88 deletions src/capabilities/optimiser/OptimiseDipoleScaling.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* <LevenbergMarquardt Optimsation for Dipole Calculation from Partial Charges. >
* <LevenbergMarquardt Optimisation for Dipole Calculation from Partial Charges. >
* Copyright (C) 2023 Conrad Hübler <[email protected]>
*
* 2024 Gerd Gehrisch
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
Expand All @@ -25,113 +25,138 @@

#include <iostream>

#include "src/capabilities/optimiser/LevMarDocking.h"
#include "src/core/elements.h"
#include "src/core/global.h"
#include "src/core/molecule.h"
#include "src/core/pseudoff.h"

#include "src/tools/geometry.h"

#include "json.hpp"
#include <LBFGS.h>
#include <LBFGSB.h>

using json = nlohmann::json;
template <typename _Scalar, int NX = Eigen::Dynamic, int NY = Eigen::Dynamic>

// Implement argmin x: F(x) = sum_i^N (y_i - f_i(x))**2
// f_i(x) = sum_j (x*q_j*r_j), N... number of confomere
// r_j=[ [xyz]_1, [xyz]_2, ..., [xyz]_m] m... number of atoms/parameter
struct TFunctor {
typedef _Scalar Scalar;
enum {
InputsAtCompileTime = NX,
ValuesAtCompileTime = NY
};
typedef Eigen::Matrix<Scalar, InputsAtCompileTime, 1> InputType;
typedef Eigen::Matrix<Scalar, ValuesAtCompileTime, 1> ValueType;
typedef Eigen::Matrix<Scalar, ValuesAtCompileTime, InputsAtCompileTime> JacobianType;

int m_inputs, m_values;

inline TFunctor(int inputs, int values)
: m_inputs(inputs)
, m_values(values)
{
}

using Eigen::VectorXd;
using namespace LBFGSpp;
int inputs() const { return m_inputs; }
int values() const { return m_values; }
};

class LBFGSDipoleInterface {
public:
LBFGSDipoleInterface(int n_)
struct OptDipoleFunctor : TFunctor<double> {
inline OptDipoleFunctor(int inputs, int values)
: TFunctor(inputs, values)
, no_parameter(inputs)
, no_points(values)
{
}
double operator()(const VectorXd& x, VectorXd& grad)
inline ~OptDipoleFunctor() = default;
inline int operator()(const Vector& scaling, Eigen::VectorXd& fvec) const
{
double fx = 0.0;
double dx = 5e-1;
std::vector<double> scaling(x.size());
for (int i = 0; i < scaling.size(); ++i) {
scaling[i] = x(i);
}
for (int i = 0; i < m_conformers.size(); ++i ){
auto conf = m_conformers.at(i);
fvec(i) = (conf.getDipole() - conf.CalculateDipoleMoment(scaling)).norm() ;

auto dipole_vector = m_molecule->CalculateDipoleMoment(scaling);
double dipole = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]) * 2.5418;
fx = std::abs(m_dipole - dipole);
// std::cout << dipole << " " << m_dipole << " "<< fx<< std::endl;
for (int i = 0; i < scaling.size(); ++i) {
// if(std::abs(fx) < std::abs(m_smaller))
// std::cout << scaling[i] << " ";
scaling[i] += dx;
dipole_vector = m_molecule->CalculateDipoleMoment(scaling);
double dipolep = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]) * 2.5418;
scaling[i] -= 2 * dx;
dipole_vector = m_molecule->CalculateDipoleMoment(scaling);
double dipolem = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]) * 2.5418;

grad[i] = (std::abs(dipolep - dipole) - std::abs(dipolem - dipole)) / (2.0 * dx);
scaling[i] += dx;
}
// if(std::abs(fx) < std::abs(m_smaller))
// std::cout << std::endl;
m_smaller = std::abs(fx);
m_parameter = x;
return fx;
}

Vector Parameter() const { return m_parameter; }
void setMolecule(const Molecule* molecule)
{
m_molecule = molecule;
return 0;
}
int no_parameter;
int no_points;
std::vector<Molecule> m_conformers;

double m_dipole;
double m_smaller = 1;
int inputs() const { return no_parameter; }
int values() const { return no_points; }
};

private:
int m_atoms = 0;
Vector m_parameter;
const Molecule* m_molecule;
struct OptDipoleFunctorNumericalDiff : Eigen::NumericalDiff<OptDipoleFunctor> {
};

inline std::pair<double, std::vector<double>> OptimiseScaling(const Molecule* molecule, double dipole, double initial, double threshold, int maxiter)
inline Vector OptimiseDipoleScaling(const std::vector<Molecule>& conformers, Vector scaling)
{
Vector parameter(molecule->AtomCount());
for (int i = 0; i < molecule->AtomCount(); ++i)
parameter(i) = initial;

Vector old_param = parameter;
// std::cout << parameter.transpose() << std::endl;

LBFGSParam<double> param;
param.epsilon = 1e-5;
LBFGSSolver<double> solver(param);
LBFGSDipoleInterface fun(parameter.size());
fun.m_dipole = dipole * 2.5418;
fun.setMolecule(molecule);
int iteration = 0;
// std::cout << parameter.transpose() << std::endl;
double fx;
int converged = solver.InitializeSingleSteps(fun, parameter, fx);
bool loop = true;
for (iteration = 1; iteration <= maxiter && fx > threshold && loop; ++iteration) {
try {
solver.SingleStep(fun, parameter, fx);

} catch (const std::logic_error& error_result) {
loop = false;
} catch (const std::runtime_error& error_result) {
loop = false;

OptDipoleFunctor functor(6,conformers.size());
functor.m_conformers = conformers;
Eigen::NumericalDiff<OptDipoleFunctor> numDiff(functor);
Eigen::LevenbergMarquardt<Eigen::NumericalDiff<OptDipoleFunctor>> lm(numDiff);


/*
lm.parameters.factor = config["LevMar_Factor"].toInt(); //step bound for the diagonal shift, is this related to damping parameter, lambda?
lm.parameters.maxfev = config["LevMar_MaxFEv"].toDouble(); //max number of function evaluations
lm.parameters.xtol = config["LevMar_Xtol"].toDouble(); //tolerance for the norm of the solution vector
lm.parameters.ftol = config["LevMar_Ftol"].toDouble(); //tolerance for the norm of the vector function
lm.parameters.gtol = config["LevMar_Gtol"].toDouble(); // tolerance for the norm of the gradient of the error vector
lm.parameters.epsfcn = config["LevMar_epsfcn"].toDouble(); //error precision
*/

Eigen::LevenbergMarquardtSpace::Status status = lm.minimizeInit(scaling);

int MaxIter = 3000;
Vector old_param = scaling;

for (int iter = 0; iter < MaxIter; ++iter) {
status = lm.minimizeOneStep(scaling);

if ((old_param - scaling).norm() < 1e-5)
break;

old_param = scaling;
}

return scaling;

}


inline Matrix DipoleScalingCalculation(const std::vector<Molecule>& conformers){
std::vector<Position> y;
std::vector<Geometry> F;
auto para_size = conformers[0].AtomCount();
auto conformer_size = conformers.size();
Matrix FTF(para_size, para_size);
Vector FTy(para_size);
for (const auto & conformer : conformers) {
y.push_back(conformer.getDipole());//TODO Einheit überprüfen
F.push_back(conformer.ChargeDistribution());
}

for (int i = 0; i < para_size; ++i){
for (int j = 0; j < para_size; ++j){
for (auto & k : F)
FTF(i, j) += k(i, 0) * k(j, 0) + k(i, 1) * k(j, 1) + k(i, 2) * k(j, 2);
}
std::cout << "Iteration: " << iteration << " Difference: " << fx << std::endl;
}
std::vector<double> scaling(parameter.size());
for (int i = 0; i < scaling.size(); ++i) {
scaling[i] = parameter(i);
std::cout << scaling[i] << " ";

for (int j = 0; j < para_size; ++j) {
for (int i = 0; i < conformer_size; ++i){
FTy(j) += y[i](0) * F[i](j, 0) + y[i](1) * F[i](j, 1) + y[i](2) * F[i](j, 2);
}
}
auto dipole_vector = molecule->CalculateDipoleMoment(scaling);
dipole = sqrt(dipole_vector[0] * dipole_vector[0] + dipole_vector[1] * dipole_vector[1] + dipole_vector[2] * dipole_vector[2]);
std::cout << std::endl;
return std::pair<double, std::vector<double>>(dipole, scaling);
}


Matrix Theta(para_size,1);

Theta = FTF.inverse() * FTy;


//inv(F.t@F)@(F.t@y);
return Theta;
}
4 changes: 2 additions & 2 deletions src/capabilities/simplemd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1339,7 +1339,7 @@ double SimpleMD::CleanEnergy(double* grad)
double Energy = interface.CalculateEnergy(true);
interface.getGradient(grad);
if (m_dipole) {
std::vector<double> dipole = interface.Dipole();
auto dipole = interface.Dipole();
m_curr_dipole = sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]);
m_collected_dipole.push_back(sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]));
}
Expand All @@ -1353,7 +1353,7 @@ double SimpleMD::FastEnergy(double* grad)
double Energy = m_interface->CalculateEnergy(true);
m_interface->getGradient(grad);
if (m_dipole) {
std::vector<double> dipole = m_interface->Dipole();
auto dipole = m_interface->Dipole();
m_curr_dipole = sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]);
m_collected_dipole.push_back(sqrt(dipole[0] * dipole[0] + dipole[1] * dipole[1] + dipole[2] * dipole[2]));
}
Expand Down
19 changes: 15 additions & 4 deletions src/core/energycalculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
namespace fs = std::filesystem;
#endif

#include <filesystem>
#include <functional>

#include "forcefieldgenerator.h"
Expand All @@ -52,7 +53,7 @@ EnergyCalculator::EnergyCalculator(const std::string& method, const json& contro
return std::vector<double>{};
};
m_dipole = []() {
return std::vector<double>{};
return Position{};
};
m_bonds = []() {
return std::vector<std::vector<double>>{ {} };
Expand All @@ -74,7 +75,12 @@ EnergyCalculator::EnergyCalculator(const std::string& method, const json& contro
return this->m_tblite->Charges();
};
m_dipole = [this]() {
return this->m_tblite->Dipole();
Position dipole;
dipole(0) = this->m_tblite->Dipole()[0];
dipole(1) = this->m_tblite->Dipole()[1];
dipole(2) = this->m_tblite->Dipole()[2];

return dipole;
};
m_bonds = [this]() {
return this->m_tblite->BondOrders();
Expand All @@ -94,7 +100,12 @@ EnergyCalculator::EnergyCalculator(const std::string& method, const json& contro
return this->m_xtb->Charges();
};
m_dipole = [this]() {
return this->m_xtb->Dipole();
Position dipole;
dipole(0) = this->m_xtb->Dipole()[0];
dipole(1) = this->m_xtb->Dipole()[1];
dipole(2) = this->m_xtb->Dipole()[2];

return dipole;
};
m_bonds = [this]() {
return this->m_xtb->BondOrders();
Expand Down Expand Up @@ -439,7 +450,7 @@ std::vector<double> EnergyCalculator::Charges() const
return m_charges();
}

std::vector<double> EnergyCalculator::Dipole() const
Position EnergyCalculator::Dipole() const
{
return m_dipole();
}
Expand Down
5 changes: 3 additions & 2 deletions src/core/energycalculator.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class EnergyCalculator {
}

std::vector<double> Charges() const;
std::vector<double> Dipole() const;
Position Dipole() const;

std::vector<std::vector<double>> BondOrders() const;

Expand Down Expand Up @@ -168,7 +168,8 @@ class EnergyCalculator {
StringList m_d3_methods = { "d3" };
StringList m_d4_methods = { "d4" };
std::function<void(bool, bool)> m_ecengine;
std::function<std::vector<double>()> m_charges, m_dipole;
std::function<std::vector<double>()> m_charges;
std::function<Position()> m_dipole;
std::function<std::vector<std::vector<double>>()> m_bonds;
json m_parameter;
std::string m_method, m_param_file;
Expand Down
Loading

0 comments on commit 703d340

Please sign in to comment.