votca 2026-dev
Loading...
Searching...
No Matches
vxc_potential.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2026 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// Third party includes
21#include <algorithm>
22#include <boost/format.hpp>
23#include <stdexcept>
24
25// VOTCA includes
27
28// Local VOTCA includes
30#include "votca/xtp/vxc_grid.h"
32
33namespace votca {
34namespace xtp {
35
36template <class Grid>
38 if (setXC_) {
39 xc_func_end(&xfunc);
40 if (use_separate_) {
41 xc_func_end(&cfunc);
42 }
43 }
44}
45
46template <class Grid>
47double Vxc_Potential<Grid>::getExactExchange(const std::string& functional) {
48 double exactexchange = 0.0;
50
51 std::vector<std::string> functional_names =
52 tools::Tokenizer(functional, " ").ToVector();
53
54 if (functional_names.size() > 2) {
55 throw std::runtime_error("Too many functional names");
56 } else if (functional_names.empty()) {
57 throw std::runtime_error("Specify at least one functional");
58 }
59
60 for (const std::string& functional_name : functional_names) {
61 int func_id = map.getID(functional_name);
62 if (func_id < 0) {
63 exactexchange = 0.0;
64 break;
65 }
66
67 xc_func_type func;
68 if (xc_func_init(&func, func_id, XC_UNPOLARIZED) != 0) {
69 throw std::runtime_error(
70 (boost::format("Functional %s not found\n") % functional_name).str());
71 }
72
73 if (exactexchange > 0 && func.cam_alpha > 0) {
74 xc_func_end(&func);
75 throw std::runtime_error(
76 "You have specified two functionals with exact exchange");
77 }
78
79 exactexchange += func.cam_alpha;
80 xc_func_end(&func);
81 }
82
83 return exactexchange;
84}
85
86template <class Grid>
87void Vxc_Potential<Grid>::setXCfunctional(const std::string& functional) {
89 std::vector<std::string> strs =
90 tools::Tokenizer(functional, " ,\n\t").ToVector();
91
92 xfunc_id = 0;
93 use_separate_ = false;
94 cfunc_id = 0;
95
96 if (strs.size() == 1) {
97 xfunc_id = map.getID(strs[0]);
98 } else if (strs.size() == 2) {
99 xfunc_id = map.getID(strs[0]);
100 cfunc_id = map.getID(strs[1]);
101 use_separate_ = true;
102 } else {
103 throw std::runtime_error(
104 "LIBXC. Please specify one combined or an exchange and a correlation "
105 "functionals");
106 }
107
108 // Keep the stored handles UNPOLARIZED for the closed-shell/restricted code
109 // path. The UKS path creates temporary polarized handles inside
110 // EvaluateXCSpin().
111 if (xc_func_init(&xfunc, xfunc_id, XC_UNPOLARIZED) != 0) {
112 throw std::runtime_error(
113 (boost::format("Functional %s not found\n") % strs[0]).str());
114 }
115
116 if (xfunc.info->kind != 2 && !use_separate_) {
117 throw std::runtime_error(
118 "Your functional misses either correlation or exchange, please specify "
119 "another functional, separated by whitespace");
120 }
121
122 if (use_separate_) {
123 if (xc_func_init(&cfunc, cfunc_id, XC_UNPOLARIZED) != 0) {
124 xc_func_end(&xfunc);
125 throw std::runtime_error(
126 (boost::format("Functional %s not found\n") % strs[1]).str());
127 }
128
129 if ((xfunc.info->kind + cfunc.info->kind) != 1) {
130 xc_func_end(&xfunc);
131 xc_func_end(&cfunc);
132 throw std::runtime_error(
133 "Your functionals are not one exchange and one correlation");
134 }
135 }
136
137 setXC_ = true;
138 return;
139}
140
141template <class Grid>
143 double rho, double sigma) const {
144 typename Vxc_Potential<Grid>::XC_entry result;
145
146 switch (xfunc.info->family) {
147 case XC_FAMILY_LDA:
148 xc_lda_exc_vxc(&xfunc, 1, &rho, &result.f_xc, &result.df_drho);
149 break;
150 case XC_FAMILY_GGA:
151 case XC_FAMILY_HYB_GGA:
152 xc_gga_exc_vxc(&xfunc, 1, &rho, &sigma, &result.f_xc, &result.df_drho,
153 &result.df_dsigma);
154 break;
155 default:
156 throw std::runtime_error("Unsupported XC family for unpolarized DFT.");
157 }
158
159 if (use_separate_) {
160 typename Vxc_Potential<Grid>::XC_entry temp;
161
162 switch (cfunc.info->family) {
163 case XC_FAMILY_LDA:
164 xc_lda_exc_vxc(&cfunc, 1, &rho, &temp.f_xc, &temp.df_drho);
165 break;
166 case XC_FAMILY_GGA:
167 case XC_FAMILY_HYB_GGA:
168 xc_gga_exc_vxc(&cfunc, 1, &rho, &sigma, &temp.f_xc, &temp.df_drho,
169 &temp.df_dsigma);
170 break;
171 default:
172 throw std::runtime_error(
173 "Unsupported correlation family for unpolarized DFT.");
174 }
175
176 result.f_xc += temp.f_xc;
177 result.df_drho += temp.df_drho;
178 result.df_dsigma += temp.df_dsigma;
179 }
180
181 return result;
182}
183
184template <class Grid>
186 double rho_a, double rho_b, double sigma_aa, double sigma_ab,
187 double sigma_bb) const {
189
190 // UKS/open-shell path: use temporary POLARIZED handles so LibXC receives the
191 // correct rho[2], sigma[3], vrho[2], vsigma[3] layout.
192 xc_func_type xfunc_pol;
193 if (xc_func_init(&xfunc_pol, xfunc_id, XC_POLARIZED) != 0) {
194 throw std::runtime_error(
195 "Failed to initialize polarized exchange XC "
196 "functional in EvaluateXCSpin.");
197 }
198
199 xc_func_type cfunc_pol;
200 bool cfunc_pol_init = false;
201 if (use_separate_) {
202 if (xc_func_init(&cfunc_pol, cfunc_id, XC_POLARIZED) != 0) {
203 xc_func_end(&xfunc_pol);
204 throw std::runtime_error(
205 "Failed to initialize polarized correlation XC "
206 "functional in EvaluateXCSpin.");
207 }
208 cfunc_pol_init = true;
209 }
210
211 double rho[2] = {rho_a, rho_b};
212
213 switch (xfunc_pol.info->family) {
214 case XC_FAMILY_LDA: {
215 double vrho[2] = {0.0, 0.0};
216 xc_lda_exc_vxc(&xfunc_pol, 1, rho, &result.f_xc, vrho);
217 result.vrho_a = vrho[0];
218 result.vrho_b = vrho[1];
219 break;
220 }
221 case XC_FAMILY_GGA:
222 case XC_FAMILY_HYB_GGA: {
223 double sigma[3] = {sigma_aa, sigma_ab, sigma_bb};
224 double vrho[2] = {0.0, 0.0};
225 double vsigma[3] = {0.0, 0.0, 0.0};
226 xc_gga_exc_vxc(&xfunc_pol, 1, rho, sigma, &result.f_xc, vrho, vsigma);
227 result.vrho_a = vrho[0];
228 result.vrho_b = vrho[1];
229 result.vsigma_aa = vsigma[0];
230 result.vsigma_ab = vsigma[1];
231 result.vsigma_bb = vsigma[2];
232 break;
233 }
234 default:
235 xc_func_end(&xfunc_pol);
236 if (cfunc_pol_init) {
237 xc_func_end(&cfunc_pol);
238 }
239 throw std::runtime_error("Unsupported XC family for polarized DFT.");
240 }
241
242 if (use_separate_) {
244
245 switch (cfunc_pol.info->family) {
246 case XC_FAMILY_LDA: {
247 double vrho[2] = {0.0, 0.0};
248 xc_lda_exc_vxc(&cfunc_pol, 1, rho, &temp.f_xc, vrho);
249 temp.vrho_a = vrho[0];
250 temp.vrho_b = vrho[1];
251 break;
252 }
253 case XC_FAMILY_GGA:
254 case XC_FAMILY_HYB_GGA: {
255 double sigma[3] = {sigma_aa, sigma_ab, sigma_bb};
256 double vrho[2] = {0.0, 0.0};
257 double vsigma[3] = {0.0, 0.0, 0.0};
258 xc_gga_exc_vxc(&cfunc_pol, 1, rho, sigma, &temp.f_xc, vrho, vsigma);
259 temp.vrho_a = vrho[0];
260 temp.vrho_b = vrho[1];
261 temp.vsigma_aa = vsigma[0];
262 temp.vsigma_ab = vsigma[1];
263 temp.vsigma_bb = vsigma[2];
264 break;
265 }
266 default:
267 xc_func_end(&xfunc_pol);
268 xc_func_end(&cfunc_pol);
269 throw std::runtime_error(
270 "Unsupported correlation family for polarized DFT.");
271 }
272
273 result.f_xc += temp.f_xc;
274 result.vrho_a += temp.vrho_a;
275 result.vrho_b += temp.vrho_b;
276 result.vsigma_aa += temp.vsigma_aa;
277 result.vsigma_ab += temp.vsigma_ab;
278 result.vsigma_bb += temp.vsigma_bb;
279 }
280
281 xc_func_end(&xfunc_pol);
282 if (cfunc_pol_init) {
283 xc_func_end(&cfunc_pol);
284 }
285
286 return result;
287}
288
289template <class Grid>
291 const Eigen::MatrixXd& density_matrix) const {
292 assert(density_matrix.isApprox(density_matrix.transpose()) &&
293 "Density matrix has to be symmetric!");
294
295 Mat_p_Energy vxc = Mat_p_Energy(density_matrix.rows(), density_matrix.cols());
296
297#pragma omp parallel for schedule(guided) reduction(+ : vxc)
298 for (Index i = 0; i < grid_.getBoxesSize(); ++i) {
299 const GridBox& box = grid_[i];
300 if (!box.Matrixsize()) {
301 continue;
302 }
303
304 double EXC_box = 0.0;
305
306 // two because we have to use the density matrix and its transpose
307 const Eigen::MatrixXd DMAT_here = 2 * box.ReadFromBigMatrix(density_matrix);
308
309 double cutoff =
310 1.e-40 / double(density_matrix.rows()) / double(density_matrix.rows());
311 if (DMAT_here.cwiseAbs2().maxCoeff() < cutoff) {
312 continue;
313 }
314
315 Eigen::MatrixXd Vxc_here =
316 Eigen::MatrixXd::Zero(DMAT_here.rows(), DMAT_here.cols());
317
318 const std::vector<Eigen::Vector3d>& points = box.getGridPoints();
319 const std::vector<double>& weights = box.getGridWeights();
320
321 for (Index p = 0; p < box.size(); ++p) {
322 AOShell::AOValues ao = box.CalcAOValues(points[p]);
323
324 Eigen::VectorXd temp = ao.values.transpose() * DMAT_here;
325 double rho = 0.5 * temp.dot(ao.values);
326 const double weight = weights[p];
327
328 if (rho * weight < 1.e-20) {
329 continue;
330 }
331
332 const Eigen::Vector3d rho_grad = temp.transpose() * ao.derivatives;
333
335 EvaluateXC(rho, rho_grad.squaredNorm());
336
337 EXC_box += weight * rho * xc.f_xc;
338
339 auto grad = ao.derivatives * rho_grad;
340 temp.noalias() =
341 weight * (0.5 * xc.df_drho * ao.values + 2.0 * xc.df_dsigma * grad);
342 Vxc_here.noalias() += temp * ao.values.transpose();
343 }
344
345 box.AddtoBigMatrix(vxc.matrix(), Vxc_here);
346 vxc.energy() += EXC_box;
347 }
348
349 return Mat_p_Energy(vxc.energy(), vxc.matrix() + vxc.matrix().transpose());
350}
351
352template <class Grid>
354 const Eigen::MatrixXd& dmat_alpha, const Eigen::MatrixXd& dmat_beta) const {
355 assert(dmat_alpha.isApprox(dmat_alpha.transpose()) &&
356 "Alpha density matrix has to be symmetric!");
357 assert(dmat_beta.isApprox(dmat_beta.transpose()) &&
358 "Beta density matrix has to be symmetric!");
359
360 typename Vxc_Potential<Grid>::SpinResult result;
361 result.vxc_alpha =
362 Eigen::MatrixXd::Zero(dmat_alpha.rows(), dmat_alpha.cols());
363 result.vxc_beta = Eigen::MatrixXd::Zero(dmat_beta.rows(), dmat_beta.cols());
364
365#pragma omp parallel
366 {
367 Eigen::MatrixXd vxc_alpha_private =
368 Eigen::MatrixXd::Zero(dmat_alpha.rows(), dmat_alpha.cols());
369 Eigen::MatrixXd vxc_beta_private =
370 Eigen::MatrixXd::Zero(dmat_beta.rows(), dmat_beta.cols());
371 double exc_private = 0.0;
372
373#pragma omp for schedule(guided)
374 for (Index i = 0; i < grid_.getBoxesSize(); ++i) {
375 const GridBox& box = grid_[i];
376 if (!box.Matrixsize()) {
377 continue;
378 }
379
380 const Eigen::MatrixXd DMa = box.ReadFromBigMatrix(dmat_alpha);
381 const Eigen::MatrixXd DMb = box.ReadFromBigMatrix(dmat_beta);
382
383 double cutoff =
384 1.e-40 / double(dmat_alpha.rows()) / double(dmat_alpha.rows());
385 if (std::max(DMa.cwiseAbs2().maxCoeff(), DMb.cwiseAbs2().maxCoeff()) <
386 cutoff) {
387 continue;
388 }
389
390 Eigen::MatrixXd Vxc_a_here =
391 Eigen::MatrixXd::Zero(DMa.rows(), DMa.cols());
392 Eigen::MatrixXd Vxc_b_here =
393 Eigen::MatrixXd::Zero(DMb.rows(), DMb.cols());
394
395 const std::vector<Eigen::Vector3d>& points = box.getGridPoints();
396 const std::vector<double>& weights = box.getGridWeights();
397
398 for (Index p = 0; p < box.size(); ++p) {
399 AOShell::AOValues ao = box.CalcAOValues(points[p]);
400
401 Eigen::VectorXd temp_a = DMa * ao.values;
402 Eigen::VectorXd temp_b = DMb * ao.values;
403
404 const double rho_a = ao.values.dot(temp_a);
405 const double rho_b = ao.values.dot(temp_b);
406 const double rho = rho_a + rho_b;
407 const double weight = weights[p];
408
409 if (rho * weight < 1.e-20) {
410 continue;
411 }
412
413 // For symmetric density matrices, this gives the full gradient
414 // consistent with the restricted implementation, which used 2*P.
415 const Eigen::Vector3d grad_a =
416 2.0 * (ao.derivatives.transpose() * temp_a);
417 const Eigen::Vector3d grad_b =
418 2.0 * (ao.derivatives.transpose() * temp_b);
419
420 const double sigma_aa = grad_a.dot(grad_a);
421 const double sigma_ab = grad_a.dot(grad_b);
422 const double sigma_bb = grad_b.dot(grad_b);
423
425 EvaluateXCSpin(rho_a, rho_b, sigma_aa, sigma_ab, sigma_bb);
426
427 exc_private += weight * rho * xc.f_xc;
428
429 if (xfunc.info->family == XC_FAMILY_LDA) {
430 // 0.5 factor because we symmetrize by adding transpose at the end
431 Eigen::VectorXd wa = weight * (0.5 * xc.vrho_a) * ao.values;
432 Eigen::VectorXd wb = weight * (0.5 * xc.vrho_b) * ao.values;
433
434 Vxc_a_here.noalias() += wa * ao.values.transpose();
435 Vxc_b_here.noalias() += wb * ao.values.transpose();
436 } else {
437 Eigen::VectorXd g_a = ao.derivatives * grad_a;
438 Eigen::VectorXd g_b = ao.derivatives * grad_b;
439
440 // Same 0.5 prefactor on vrho term as in restricted path.
441 Eigen::VectorXd wa =
442 weight * (0.5 * xc.vrho_a * ao.values + 2.0 * xc.vsigma_aa * g_a +
443 xc.vsigma_ab * g_b);
444
445 Eigen::VectorXd wb =
446 weight * (0.5 * xc.vrho_b * ao.values + xc.vsigma_ab * g_a +
447 2.0 * xc.vsigma_bb * g_b);
448
449 Vxc_a_here.noalias() += wa * ao.values.transpose();
450 Vxc_b_here.noalias() += wb * ao.values.transpose();
451 }
452 }
453
454 box.AddtoBigMatrix(vxc_alpha_private, Vxc_a_here);
455 box.AddtoBigMatrix(vxc_beta_private, Vxc_b_here);
456 }
457
458#pragma omp critical
459 {
460 result.vxc_alpha += vxc_alpha_private + vxc_alpha_private.transpose();
461 result.vxc_beta += vxc_beta_private + vxc_beta_private.transpose();
462 result.energy += exc_private;
463 }
464 }
465
466 return result;
467}
468
469template class Vxc_Potential<Vxc_Grid>;
470
471} // namespace xtp
472} // namespace votca
break string into words
Definition tokenizer.h:72
std::vector< T > ToVector()
store all words in a vector of type T, does type conversion.
Definition tokenizer.h:109
const std::vector< Eigen::Vector3d > & getGridPoints() const
Definition gridbox.h:41
Eigen::MatrixXd ReadFromBigMatrix(const Eigen::MatrixXd &bigmatrix) const
Definition gridbox.cc:68
void AddtoBigMatrix(Eigen::MatrixXd &bigmatrix, const Eigen::MatrixXd &smallmatrix) const
Definition gridbox.cc:55
const std::vector< double > & getGridWeights() const
Definition gridbox.h:43
AOShell::AOValues CalcAOValues(const Eigen::Vector3d &point) const
Definition gridbox.cc:44
Index size() const
Definition gridbox.h:51
Index Matrixsize() const
Definition gridbox.h:55
Eigen::MatrixXd & matrix()
Definition eigen.h:80
double & energy()
Definition eigen.h:81
conversion of functional string into integer
XC_entry_spin EvaluateXCSpin(double rho_a, double rho_b, double sigma_aa, double sigma_ab, double sigma_bb) 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)
static double getExactExchange(const std::string &functional)
SpinResult IntegrateVXCSpin(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta) const
Charge transport classes.
Definition ERIs.h:28
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
Eigen::MatrixX3d derivatives
Definition aoshell.h:131
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