votca 2026-dev
Loading...
Searching...
No Matches
vxc_potential.h
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20#pragma once
21#ifndef VOTCA_XTP_VXC_POTENTIAL_H
22#define VOTCA_XTP_VXC_POTENTIAL_H
23
24#include <xc.h>
25
26#include "grid_containers.h"
27#include "gridbox.h"
28
29#undef LOG
30
31namespace votca {
32namespace xtp {
33
34class AOBasis;
35class QMMolecule;
36
37template <class Grid>
39 public:
40 struct SpinResult {
41 double energy = 0.0;
42 Eigen::MatrixXd vxc_alpha;
43 Eigen::MatrixXd vxc_beta;
44 };
45
46 explicit Vxc_Potential(const Grid& grid) : grid_(grid) {};
48
49 static double getExactExchange(const std::string& functional);
50 void setXCfunctional(const std::string& functional);
51
52 Mat_p_Energy IntegrateVXC(const Eigen::MatrixXd& density_matrix) const;
53 SpinResult IntegrateVXCSpin(const Eigen::MatrixXd& dmat_alpha,
54 const Eigen::MatrixXd& dmat_beta) const;
55
56 // ===========================================================================
57 // STATUS: originally basis-function/Pulay term only (LDA-only); a
58 // second, distinct term (grid-point translation) was added after the
59 // GridWeightGradient C_p fix substantially improved but did not fully
60 // resolve a residual discrepancy against finite differences; a third
61 // addition (GGA sigma-dependent contributions to BOTH the basis and
62 // translation terms, using AOShell::EvalAOspaceHessian) followed once
63 // the LDA-only version was fully validated end to end -- see
64 // vxc_potential.cc for the full derivation of all three. The name
65 // "PulayGradient" is now a real misnomer (it computes basis-function +
66 // grid-point-translation + GGA-sigma contributions together, since
67 // they all loop over the same grid points and share the same
68 // expensive AO evaluation) -- kept for now rather than renaming
69 // mid-branch, but worth reconsidering once the whole XC gradient is
70 // confirmed correct for GGA too.
71 //
72 // The GGA sigma terms are exactly zero for LDA functionals (xc.df_dsigma
73 // is default-initialized to 0 and never written to by xc_lda_exc_vxc --
74 // confirmed directly in EvaluateXC), so they cannot regress the
75 // already-validated LDA behavior; computed unconditionally rather than
76 // branching on functional type. NOT yet confirmed correct for GGA by
77 // an actual test, though the underlying dsigma/dR formula was verified
78 // numerically (to ~1e-12) on a toy multi-atom system before writing
79 // any of this C++ -- see conversation history.
80 // ===========================================================================
81 Eigen::MatrixXd PulayGradient(const Eigen::MatrixXd& density_matrix,
82 const AOBasis& dftbasis) const;
83
84 // ===========================================================================
85 // STATUS: written but NOT yet run/tested. This is the second (and
86 // harder) of the two pieces needed for the XC gradient, alongside
87 // PulayGradient above -- the SSW grid-weight nuclear derivative.
88 //
89 // The underlying formula was derived and independently verified
90 // NUMERICALLY in Python (two separate multi-atom test configurations,
91 // matching finite differences to ~1e-9/1e-10) before writing any of
92 // this C++ -- see conversation history for the full derivation. This
93 // C++ is a direct translation of that verified Python, not a fresh
94 // derivation done blind.
95 //
96 // Combined with PulayGradient, this should give the complete LDA-level
97 // XC gradient (GGA's additional df_dsigma-driven Pulay term is still
98 // out of scope, per the note on PulayGradient). Validate the SUM of
99 // the two against a finite difference of the real total XC energy
100 // (IntegrateVXC's energy output) -- neither piece alone can be checked
101 // against the total energy in isolation.
102 //
103 // SCALING NOTE: this is O(Natoms^3) per grid point (derivative w.r.t.
104 // every atom, each needing a sum over every other atom's pairwise
105 // term) -- consistent with the SSW partition's inherent O(Natoms^2)
106 // energy-level cost, but one power of Natoms worse for the gradient.
107 // Fine for validation-scale systems; would need optimizing before use
108 // on anything large.
109 // ===========================================================================
110 Eigen::MatrixXd GridWeightGradient(const Eigen::MatrixXd& density_matrix,
111 const QMMolecule& atoms) const;
112
113 // ===========================================================================
114 // UKS (spin-polarized) analogs of PulayGradient/GridWeightGradient
115 // above. Now support GGA functionals too, not just LDA -- extended
116 // after the LDA-only version was fully validated, following the same
117 // derive-in-Python-first discipline used throughout this codebase.
118 //
119 // LDA basis-type and translation-type terms generalize the
120 // spin-restricted derivation directly, verified numerically (Python,
121 // toy multi-atom system, ~1e-11) before writing this: for each spin
122 // channel s in {a,b},
123 // d(rho_s,p)/dR_A|_basis = -2 * sum_{mu in A} temp_s,mu * grad(chi_mu)
124 // d(rho_s,p)/dR_A|_translation = grad_s,p if A == owner(p), else 0
125 // (matching this file's own IntegrateVXCSpin convention exactly:
126 // temp_s = dmat_s * ao.values, grad_s = 2*(ao.derivatives^T * temp_s),
127 // no extra factor of 2 needed for temp_s itself since UKS spin
128 // density matrices are NOT pre-doubled, unlike RKS's DMAT_here=2*P).
129 // Contracted with vrho_a/vrho_b (EvaluateXCSpin's own "full potential"
130 // convention, same reasoning already established for the restricted
131 // case's df_drho -- no further correction needed) and summed over
132 // both spin channels.
133 //
134 // GGA sigma_aa/sigma_ab/sigma_bb terms: naively need THREE separate
135 // contributions, but collapse into reusing the SAME per-spin
136 // machinery as the restricted GGA case (Gmat_s, Hessian_rho_s) twice
137 // -- once per spin channel -- by combining the three vsigma weights
138 // into two "effective" gradient vectors (verified numerically,
139 // ~1e-12, before writing the C++):
140 // V_a = 2*vsigma_aa*rho_a_grad + vsigma_ab*rho_b_grad
141 // V_b = 2*vsigma_bb*rho_b_grad + vsigma_ab*rho_a_grad
142 // Basis-type sigma contribution per mu in A:
143 // -[grad(chi_mu)*(Gmat_a.V_a)_mu + temp_a,mu*(Hessian_mu.V_a)
144 // + grad(chi_mu)*(Gmat_b.V_b)_mu + temp_b,mu*(Hessian_mu.V_b)]
145 // Translation-type sigma contribution (A==owner(p) only):
146 // Hessian_rho_a . V_a + Hessian_rho_b . V_b
147 // where Gmat_s = 2*dmat_s*ao.derivatives and Hessian_rho_s is built
148 // exactly like the restricted case's Hessian_rho, just per spin
149 // channel.
150 //
151 // The weight-derivative term is functional-form-agnostic (same
152 // geometric dw/dR logic as the restricted GridWeightGradient,
153 // unchanged) -- only the energy-density prefactor changes, from
154 // rho*f_xc to (rho_a+rho_b)*f_xc (EvaluateXCSpin's own f_xc
155 // convention, matching IntegrateVXCSpin's own energy accumulation
156 // exactly: exc_private += weight*rho*xc.f_xc, rho=rho_a+rho_b), now
157 // evaluated with real sigma_aa/sigma_ab/sigma_bb (computed from the
158 // actual density gradients) instead of the LDA-only version's
159 // hardcoded zeros.
160 Eigen::MatrixXd PulayGradientUKS(const Eigen::MatrixXd& dmat_alpha,
161 const Eigen::MatrixXd& dmat_beta,
162 const AOBasis& dftbasis) const;
163 Eigen::MatrixXd GridWeightGradientUKS(const Eigen::MatrixXd& dmat_alpha,
164 const Eigen::MatrixXd& dmat_beta,
165 const QMMolecule& atoms) const;
166 // ===========================================================================
167
168 private:
169 struct XC_entry {
170 double f_xc = 0;
171 double df_drho = 0;
172 double df_dsigma = 0;
173 };
174
176 double f_xc = 0;
177 double vrho_a = 0;
178 double vrho_b = 0;
179 double vsigma_aa = 0;
180 double vsigma_ab = 0;
181 double vsigma_bb = 0;
182 };
183
184 XC_entry EvaluateXC(double rho, double sigma) const;
185 XC_entry_spin EvaluateXCSpin(double rho_a, double rho_b, double sigma_aa,
186 double sigma_ab, double sigma_bb) const;
187
188 const Grid grid_;
190 bool setXC_ = false;
193 xc_func_type xfunc;
194 xc_func_type cfunc;
195};
196
197} // namespace xtp
198} // namespace votca
199#endif // VOTCA_XTP_VXC_POTENTIAL_H
Container to hold Basisfunctions for all atoms.
Definition aobasis.h:42
Eigen::MatrixXd GridWeightGradient(const Eigen::MatrixXd &density_matrix, const QMMolecule &atoms) const
Eigen::MatrixXd PulayGradient(const Eigen::MatrixXd &density_matrix, const AOBasis &dftbasis) const
XC_entry_spin EvaluateXCSpin(double rho_a, double rho_b, double sigma_aa, double sigma_ab, double sigma_bb) const
Eigen::MatrixXd PulayGradientUKS(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta, const AOBasis &dftbasis) const
XC_entry EvaluateXC(double rho, double sigma) const
Mat_p_Energy IntegrateVXC(const Eigen::MatrixXd &density_matrix) const
void setXCfunctional(const std::string &functional)
Vxc_Potential(const Grid &grid)
static double getExactExchange(const std::string &functional)
Eigen::MatrixXd GridWeightGradientUKS(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta, const QMMolecule &atoms) const
SpinResult IntegrateVXCSpin(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta) const
Provides a means for comparing floating point numbers.
Definition basebead.h:33
double vsigma_ab
double vrho_b
double vrho_a
double vsigma_aa
double vsigma_bb
double f_xc
double f_xc
double df_dsigma
double df_drho