26#include <boost/format.hpp>
44 if (
dftoptions_.get(
"name").as<std::string>() !=
"xtp") {
45 throw std::runtime_error(
"crfit: needs the xtp dftpackage");
47 const std::string method = options.
get(
".method").
as<std::string>();
48 if (method !=
"analytic" && method !=
"finite_differences") {
49 throw std::runtime_error(
50 "crfit: method must be analytic or finite_differences");
52 fit_.analytic = (method ==
"analytic");
53 fit_.tolerance = options.
get(
".tolerance").
as<
double>();
54 fit_.step = options.
get(
".step").
as<
double>();
56 fit_.use_inversion = options.
get(
".inversion").
as<
bool>();
59 if (options.
exists(
".mpsfile")) {
64 const double error =
conv.get(
"error").as<
double>();
65 auto str = [](
double x) {
67 s << std::setprecision(17) << x;
73 const double energy =
conv.get(
"energy").as<
double>();
81 log_.setMultithreading(
true);
82 log_.setCommonPreface(
"\n... ...");
89 throw std::runtime_error(
90 "crfit: closed-shell molecules only (spin 1), got spin " +
91 std::to_string(spin));
98 <<
TimeStamp() <<
" Charge-response fit of " << n_atoms <<
" atoms from "
104 auto eig = [&](
const Eigen::Matrix3d& m) {
105 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> es(m,
106 Eigen::EigenvaluesOnly);
107 return Eigen::Vector3d(es.eigenvalues() * A3);
111 <<
" alpha eigenvalues [A^3]: " << eig(r.
alpha).transpose()
112 <<
"\n charge flow X^T Pi X: " << eig(r.
alpha_ct).transpose()
113 <<
"\n residual: " << eig(r.
alpha_res).transpose()
114 <<
"\n Pi: asymmetry before symmetrizing " << r.
pi_asymmetry
129 s <<
"coupled-perturbed KS, tolerance " <<
fit_.tolerance;
131 s <<
"finite differences, step " <<
fit_.step;
138 const std::string file =
output_ +
".crm";
139 std::ofstream ofs(file);
140 if (!ofs.is_open()) {
141 throw std::runtime_error(
"crfit: cannot write " + file);
143 ofs <<
"! charge-response matrix (Hirshfeld), "
144 <<
dftoptions_.get(
"functional").as<std::string>() <<
"/"
146 <<
" (xtp_tools -e crfit, " <<
MethodLabel() <<
")\n";
147 ofs <<
"! dq = -Pi phi; Pi in e^2/Hartree, atom order as below (Angstrom)\n";
148 ofs << mol.
size() <<
"\n";
149 for (
const QMAtom& atom : mol) {
151 ofs << boost::format(
"%-2s %12.6f %12.6f %12.6f\n") % atom.getElement() %
154 for (
Index j = 0; j < r.
pi.rows(); ++j) {
155 for (
Index k = 0; k < r.
pi.cols(); ++k) {
156 ofs << boost::format(
"%+.10e") % r.
pi(j, k)
157 << (k + 1 < r.
pi.cols() ?
" " :
"\n");
164std::string Json(
const Eigen::MatrixXd& m) {
166 s << std::setprecision(15) <<
"[";
167 for (
Index i = 0; i < m.rows(); ++i) {
168 s << (i ?
", [" :
"[");
169 for (
Index j = 0; j < m.cols(); ++j) {
170 s << (j ?
", " :
"") << m(i, j);
174 return s.str() +
"]";
176std::string JsonVector(
const Eigen::VectorXd& v) {
178 s << std::setprecision(15);
179 for (
Index i = 0; i < v.size(); ++i) {
180 s << (i ?
", " :
"") << v(i);
189 const std::string file =
output_ +
".json";
190 std::ofstream ofs(file);
191 if (!ofs.is_open()) {
192 throw std::runtime_error(
"crfit: cannot write " + file);
195 Eigen::MatrixXd coords(mol.
size(), 3);
196 std::string elements;
198 coords.row(k) = mol[k].
getPos().transpose();
199 elements += (k ?
", \"" :
"\"") + mol[k].getElement() +
"\"";
201 std::string res_atoms;
206 <<
" \"xyz\": \"" <<
xyzfile_ <<
"\",\n"
207 <<
" \"xc\": \"" <<
dftoptions_.get(
"functional").as<std::string>()
209 <<
" \"basis\": \"" <<
dftoptions_.get(
"basisset").as<std::string>()
212 <<
" \"method\": \"xtp_tools -e crfit (" <<
MethodLabel() <<
")\",\n"
213 <<
" \"units\": \"atomic (bohr, e, Hartree); Pi in e^2/Hartree\",\n"
214 <<
" \"elements\": [" << elements <<
"],\n"
215 <<
" \"coords_bohr\": " << Json(coords) <<
",\n"
216 <<
" \"alpha_dft_au\": " << Json(r.
alpha) <<
",\n"
217 <<
" \"alpha_dft_A3\": " << Json(r.
alpha * A3) <<
",\n"
218 <<
" \"Pi\": " << Json(r.
pi) <<
",\n"
219 <<
" \"Pi_eigenvalues\": [" << JsonVector(r.
pi_eigenvalues) <<
"],\n"
220 << std::setprecision(6) <<
" \"Pi_asymmetry\": " << r.
pi_asymmetry
222 <<
" \"alpha_ct_A3\": " << Json(r.
alpha_ct * A3) <<
",\n"
223 <<
" \"alpha_res_A3\": " << Json(r.
alpha_res * A3) <<
",\n"
224 <<
" \"alpha_res_weights\": [" << JsonVector(r.
res_weights) <<
"],\n"
225 <<
" \"alpha_res_atoms_au\": [" << res_atoms <<
"]\n"
235 throw std::runtime_error(
236 "crfit: " +
mpsfile_ +
" has " + std::to_string(tmpl.
size()) +
237 " sites, the molecule " + std::to_string(mol.
size()) +
" atoms");
239 Eigen::MatrixX3d
P(mol.
size(), 3), Q(mol.
size(), 3);
241 if (tmpl[k].getElement() != mol[k].getElement()) {
242 throw std::runtime_error(
"crfit: site " + std::to_string(k) +
" of " +
243 mpsfile_ +
" is " + tmpl[k].getElement() +
244 ", atom " + std::to_string(k) +
" is " +
245 mol[k].getElement());
247 P.row(k) = tmpl[k].
getPos().transpose();
248 Q.row(k) = mol[k].
getPos().transpose();
252 const Eigen::MatrixX3d P0 =
P.rowwise() -
P.colwise().mean();
253 const Eigen::MatrixX3d Q0 = Q.rowwise() - Q.colwise().mean();
254 Eigen::JacobiSVD<Eigen::Matrix3d> svd(
255 Q0.transpose() * P0, Eigen::ComputeFullU | Eigen::ComputeFullV);
256 Eigen::Matrix3d
D = Eigen::Matrix3d::Identity();
257 D(2, 2) = (svd.matrixU() * svd.matrixV().transpose()).determinant() < 0 ? -1.0
259 const Eigen::Matrix3d U =
260 svd.matrixV() *
D * svd.matrixU().transpose();
262 std::sqrt((Q0 * U.transpose() - P0).rowwise().squaredNorm().mean());
265 <<
TimeStamp() <<
" WARNING: template and fit geometry differ by "
273 const std::string file =
output_ +
".mps";
274 tmpl.
WriteMPS(file,
"CRFIT (permanent multipoles of " +
275 std::filesystem::path(
mpsfile_).filename().
string() +
276 ", residual polarizabilities of the fit)");
const Eigen::Vector3d & getPos() const
void WriteJson(const QMMolecule &mol, const ChargeResponseFit::Result &r) const
void WriteMps(const QMMolecule &mol, const ChargeResponseFit::Result &r) const
tools::Property dftoptions_
std::string MethodLabel() const
ChargeResponseFit::FitOptions fit_
void ParseOptions(const tools::Property &options) final
void WriteCrm(const QMMolecule &mol, const ChargeResponseFit::Result &r) const
static Result Fit(const tools::Property &dftoptions, Orbitals &orb, const FitOptions &fit, Logger &log)
void LoadFromFile(std::string filename)
void WriteMPS(std::string filename, std::string header) const
Container for molecular orbitals and derived one-particle data.
const QMMolecule & QMAtoms() const
Return read-only access to the molecular geometry.
void setChargeAndSpin(Index charge, Index spin)
void LoadFromFile(std::string filename)
Timestamp returns the current time as a string Example: cout << TimeStamp().
#define XTP_LOG(level, log)
Charge transport classes.
ClassicalSegment< PolarSite > PolarSegment
Provides a means for comparing floating point numbers.
static Level current_level
std::vector< Eigen::Matrix3d > alpha_res_atoms
Eigen::Matrix3d alpha_res
Eigen::VectorXd res_weights
Eigen::VectorXd pi_eigenvalues