LIBINT 2.9.0
soad_fock.h
1/*
2 * Copyright (C) 2004-2024 Edward F. Valeev
3 *
4 * This file is part of Libint.
5 *
6 * Libint is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Libint is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Libint. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#ifndef _libint2_src_lib_libint_soad_fock_h_
22#define _libint2_src_lib_libint_soad_fock_h_
23
24#include <libint2/atom.h>
25#include <libint2/basis.h>
26#include <libint2/chemistry/sto3g_atomic_density.h>
27
28#include <Eigen/Dense>
29
30namespace libint2 {
31
34
41Eigen::MatrixXd compute_soad(const std::vector<Atom> &atoms) {
42 // compute number of atomic orbitals
43 size_t nao = 0;
44 for (const auto &atom : atoms) {
45 const auto Z = atom.atomic_number;
46 nao += libint2::sto3g_num_ao(Z);
47 }
48
49 // compute the minimal basis density
50 Eigen::MatrixXd D = Eigen::MatrixXd::Zero(nao, nao);
51 size_t ao_offset = 0; // first AO of this atom
52 for (const auto &atom : atoms) {
53 const auto Z = atom.atomic_number;
54 const auto &occvec = libint2::sto3g_ao_occupation_vector(Z);
55 for (const auto &occ : occvec) {
56 D(ao_offset, ao_offset) = occ;
57 ++ao_offset;
58 }
59 }
60
61 return D * 0.5; // we use densities normalized to # of electrons/2
62}
63
64Eigen::MatrixXd compute_2body_fock_general(const BasisSet &obs,
65 const Eigen::MatrixXd &D,
66 const BasisSet &D_bs,
67 bool D_is_shelldiagonal,
68 double precision) {
69 const auto n = obs.nbf();
70 const auto nshells = obs.size();
71 const auto n_D = D_bs.nbf();
72 assert(D.cols() == D.rows() && D.cols() == n_D);
73
74 Eigen::MatrixXd G = Eigen::MatrixXd ::Zero(n, n);
75
76 // construct the 2-electron repulsion integrals engine
77 libint2::Engine engine(libint2::Operator::coulomb,
78 std::max(obs.max_nprim(), D_bs.max_nprim()),
79 std::max(obs.max_l(), D_bs.max_l()), 0);
80 engine.set_precision(precision);
81 auto shell2bf = obs.shell2bf();
82 auto shell2bf_D = D_bs.shell2bf();
83
84 const auto &buf = engine.results();
85
86 // loop over permutationally-unique set of shells
87 for (auto s1 = 0l, s1234 = 0l; s1 != nshells; ++s1) {
88 auto bf1_first = shell2bf[s1]; // first basis function in this shell
89 auto n1 = obs[s1].size(); // number of basis functions in this shell
90
91 for (auto s2 = 0; s2 <= s1; ++s2) {
92 auto bf2_first = shell2bf[s2];
93 auto n2 = obs[s2].size();
94
95 for (auto s3 = 0; s3 < D_bs.size(); ++s3) {
96 auto bf3_first = shell2bf_D[s3];
97 auto n3 = D_bs[s3].size();
98
99 auto s4_begin = D_is_shelldiagonal ? s3 : 0;
100 auto s4_fence = D_is_shelldiagonal ? s3 + 1 : D_bs.size();
101
102 for (auto s4 = s4_begin; s4 != s4_fence; ++s4, ++s1234) {
103 auto bf4_first = shell2bf_D[s4];
104 auto n4 = D_bs[s4].size();
105
106 // compute the permutational degeneracy (i.e. # of equivalents) of
107 // the given shell set
108 auto s12_deg = (s1 == s2) ? 1.0 : 2.0;
109
110 if (s3 >= s4) {
111 auto s34_deg = (s3 == s4) ? 1.0 : 2.0;
112 auto s1234_deg = s12_deg * s34_deg;
113 // auto s1234_deg = s12_deg;
114 engine.compute2<Operator::coulomb, BraKet::xx_xx, 0>(
115 obs[s1], obs[s2], D_bs[s3], D_bs[s4]);
116 const auto *buf_1234 = buf[0];
117 if (buf_1234 != nullptr) {
118 for (auto f1 = 0, f1234 = 0; f1 != n1; ++f1) {
119 const auto bf1 = f1 + bf1_first;
120 for (auto f2 = 0; f2 != n2; ++f2) {
121 const auto bf2 = f2 + bf2_first;
122 for (auto f3 = 0; f3 != n3; ++f3) {
123 const auto bf3 = f3 + bf3_first;
124 for (auto f4 = 0; f4 != n4; ++f4, ++f1234) {
125 const auto bf4 = f4 + bf4_first;
126
127 const auto value = buf_1234[f1234];
128 const auto value_scal_by_deg = value * s1234_deg;
129 G(bf1, bf2) += 2.0 * D(bf3, bf4) * value_scal_by_deg;
130 }
131 }
132 }
133 }
134 }
135 }
136
137 engine.compute2<Operator::coulomb, BraKet::xx_xx, 0>(
138 obs[s1], D_bs[s3], obs[s2], D_bs[s4]);
139 const auto *buf_1324 = buf[0];
140 if (buf_1324 == nullptr)
141 continue; // if all integrals screened out, skip to next quartet
142
143 for (auto f1 = 0, f1324 = 0; f1 != n1; ++f1) {
144 const auto bf1 = f1 + bf1_first;
145 for (auto f3 = 0; f3 != n3; ++f3) {
146 const auto bf3 = f3 + bf3_first;
147 for (auto f2 = 0; f2 != n2; ++f2) {
148 const auto bf2 = f2 + bf2_first;
149 for (auto f4 = 0; f4 != n4; ++f4, ++f1324) {
150 const auto bf4 = f4 + bf4_first;
151
152 const auto value = buf_1324[f1324];
153 const auto value_scal_by_deg = value * s12_deg;
154 G(bf1, bf2) -= D(bf3, bf4) * value_scal_by_deg;
155 }
156 }
157 }
158 }
159 }
160 }
161 }
162 }
163
164 // symmetrize the result and return
165 return 0.5 * (G + G.transpose());
166}
167
168Eigen::MatrixXd compute_soad_fock(const BasisSet &obs, const BasisSet &minbs,
169 const std::vector<Atom> &atoms) {
170 // use SOAD as guess for the density matrix
171 auto D = compute_soad(atoms);
172 // compute fock with guess density
173 auto F = compute_2body_fock_general(
174 obs, D, minbs, true /* SOAD_D_is_shelldiagonal */,
175 std::numeric_limits<double>::epsilon() // this is cheap, no reason
176 // to be cheaper
177 );
178 return F;
179}
180
181} // namespace libint2
182
183#endif //_libint2_src_lib_libint_soad_fock_h_
Defaults definitions for various parameters assumed by Libint.
Definition algebra.cc:24
Eigen::MatrixXd compute_soad(const std::vector< Atom > &atoms)
computes Superposition-Of-Atomic-Densities guess for the molecular 1-RDM
Definition soad_fock.h:41
const std::vector< Real > & sto3g_ao_occupation_vector(size_t Z)
computes average orbital occupancies in the ground state of a neutral atoms
Definition sto3g_atomic_density.h:86
size_t sto3g_num_ao(size_t Z)
Definition sto3g_atomic_density.h:57