Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dipole Scaling Calculation #8

Merged
merged 11 commits into from
Jun 21, 2024
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() ;

Check notice on line 76 in src/capabilities/optimiser/OptimiseDipoleScaling.h

View check run for this annotation

codefactor.io / CodeFactor

src/capabilities/optimiser/OptimiseDipoleScaling.h#L76

Redundant blank line at the end of a code block should be deleted. (whitespace/blank_line)
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;

Check notice on line 94 in src/capabilities/optimiser/OptimiseDipoleScaling.h

View check run for this annotation

codefactor.io / CodeFactor

src/capabilities/optimiser/OptimiseDipoleScaling.h#L94

Redundant blank line at the start of a code block should be deleted. (whitespace/blank_line)
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;

Check notice on line 125 in src/capabilities/optimiser/OptimiseDipoleScaling.h

View check run for this annotation

codefactor.io / CodeFactor

src/capabilities/optimiser/OptimiseDipoleScaling.h#L125

Redundant blank line at the end of a code block should be deleted. (whitespace/blank_line)
}


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 @@ -60,126 +60,126 @@
delete m_unique_structures[i];
}

void SimpleMD::LoadControlJson()
{
m_method = Json2KeyWord<std::string>(m_defaults, "method");
m_thermostat = Json2KeyWord<std::string>(m_defaults, "thermostat");
m_plumed = Json2KeyWord<std::string>(m_defaults, "plumed");

m_spin = Json2KeyWord<int>(m_defaults, "spin");
m_charge = Json2KeyWord<int>(m_defaults, "charge");
m_dT = Json2KeyWord<double>(m_defaults, "dT");
m_maxtime = Json2KeyWord<double>(m_defaults, "MaxTime");
m_T0 = Json2KeyWord<double>(m_defaults, "T");
m_rmrottrans = Json2KeyWord<int>(m_defaults, "rmrottrans");
m_nocenter = Json2KeyWord<bool>(m_defaults, "nocenter");
m_dump = Json2KeyWord<int>(m_defaults, "dump");
m_print = Json2KeyWord<int>(m_defaults, "print");
m_max_top_diff = Json2KeyWord<int>(m_defaults, "MaxTopoDiff");
m_seed = Json2KeyWord<int>(m_defaults, "seed");

m_rmsd = Json2KeyWord<double>(m_defaults, "rmsd");
m_hmass = Json2KeyWord<double>(m_defaults, "hmass");

m_impuls = Json2KeyWord<double>(m_defaults, "impuls");
m_impuls_scaling = Json2KeyWord<double>(m_defaults, "impuls_scaling");
m_writeUnique = Json2KeyWord<bool>(m_defaults, "unique");
m_opt = Json2KeyWord<bool>(m_defaults, "opt");
m_scale_velo = Json2KeyWord<double>(m_defaults, "velo");
m_rescue = Json2KeyWord<bool>(m_defaults, "rescue");
m_coupling = Json2KeyWord<double>(m_defaults, "coupling");
if (m_coupling < m_dT)
m_coupling = m_dT;

m_writerestart = Json2KeyWord<int>(m_defaults, "writerestart");
m_respa = Json2KeyWord<int>(m_defaults, "respa");
m_dipole = Json2KeyWord<bool>(m_defaults, "dipole");

m_writeXYZ = Json2KeyWord<bool>(m_defaults, "writeXYZ");
m_writeinit = Json2KeyWord<bool>(m_defaults, "writeinit");
m_mtd = Json2KeyWord<bool>(m_defaults, "mtd");
m_mtd_dT = Json2KeyWord<int>(m_defaults, "mtd_dT");
if (m_mtd_dT < 0) {
m_eval_mtd = true;
} else {
m_eval_mtd = false;
}
m_initfile = Json2KeyWord<std::string>(m_defaults, "initfile");
m_norestart = Json2KeyWord<bool>(m_defaults, "norestart");
m_dt2 = m_dT * m_dT;
m_rm_COM = Json2KeyWord<double>(m_defaults, "rm_COM");
int rattle = Json2KeyWord<int>(m_defaults, "rattle");
m_rattle_maxiter = Json2KeyWord<int>(m_defaults, "rattle_maxiter");
if (rattle == 1) {
Integrator = [=](double* grad) {
this->Rattle(grad);
};
m_rattle_tolerance = Json2KeyWord<double>(m_defaults, "rattle_tolerance");
// m_coupling = m_dT;
m_rattle = Json2KeyWord<int>(m_defaults, "rattle");
std::cout << "Using rattle to constrain bonds!" << std::endl;
} else {
Integrator = [=](double* grad) {
this->Verlet(grad);
};
}

if (Json2KeyWord<bool>(m_defaults, "cleanenergy")) {
Energy = [=](double* grad) -> double {
return this->CleanEnergy(grad);
};
std::cout << "Energy Calculator will be set up for each step! Single steps are slower, but more reliable. Recommended for the combination of GFN2 and solvation." << std::endl;
} else {
Energy = [=](double* grad) -> double {
return this->FastEnergy(grad);
};
std::cout << "Energy Calculator will NOT be set up for each step! Fast energy calculation! This is the default way and should not be changed unless the energy and gradient calculation are unstable (happens with GFN2 and solvation)." << std::endl;
}

if (Json2KeyWord<std::string>(m_defaults, "wall").compare("spheric") == 0) {
if (Json2KeyWord<std::string>(m_defaults, "wall_type").compare("logfermi") == 0) {
WallPotential = [=](double* grad) -> double {
this->m_wall_potential = this->ApplySphericLogFermiWalls(grad);
return m_wall_potential;
};
} else if (Json2KeyWord<std::string>(m_defaults, "wall_type").compare("harmonic") == 0) {
WallPotential = [=](double* grad) -> double {
this->m_wall_potential = this->ApplySphericHarmonicWalls(grad);
return m_wall_potential;
};
} else {
std::cout << "Did not understand wall potential input. Exit now!" << std::endl;
exit(1);
}
std::cout << "Setting up spherical potential" << std::endl;

InitialiseWalls();
} else if (Json2KeyWord<std::string>(m_defaults, "wall").compare("rect") == 0) {
if (Json2KeyWord<std::string>(m_defaults, "wall_type").compare("logfermi") == 0) {
WallPotential = [=](double* grad) -> double {
this->m_wall_potential = this->ApplyRectLogFermiWalls(grad);
return m_wall_potential;
};
} else if (Json2KeyWord<std::string>(m_defaults, "wall_type").compare("harmonic") == 0) {
WallPotential = [=](double* grad) -> double {
this->m_wall_potential = this->ApplyRectHarmonicWalls(grad);
return m_wall_potential;
};

} else {
std::cout << "Did not understand wall potential input. Exit now!" << std::endl;
exit(1);
}
std::cout << "Setting up rectangular potential" << std::endl;

InitialiseWalls();
} else
WallPotential = [=](double* grad) -> double {
return 0;
};
m_rm_COM_step = m_rm_COM / m_dT;
}

Check notice on line 182 in src/capabilities/simplemd.cpp

View check run for this annotation

codefactor.io / CodeFactor

src/capabilities/simplemd.cpp#L63-L182

Complex Method
bool SimpleMD::Initialise()
{
static std::random_device rd{};
Expand Down Expand Up @@ -1339,7 +1339,7 @@
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 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
Loading