1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: fock.cc
4 // Copyright (C) 2014 Toru Shiozaki
5 //
6 // Author: Toru Shiozaki <shiozaki@northwestern.edu>
7 // Maintainer: Shiozaki group
8 //
9 // This file is part of the BAGEL package.
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 //
24 
25 #include <src/scf/hf/fock.h>
26 
27 using namespace std;
28 using namespace bagel;
29 
30 
31 // Non-DF Fock matrix, standard basis
32 template <>
fock_two_electron_part(shared_ptr<const Matrix> den)33 void Fock<0>::fock_two_electron_part(shared_ptr<const Matrix> den) {
34   const vector<shared_ptr<const Atom>> atoms = geom_->atoms();
35   vector<shared_ptr<const Shell>> basis;
36   vector<int> offset;
37   int cnt = 0;
38   for (auto aiter = atoms.begin(); aiter != atoms.end(); ++aiter, ++cnt) {
39     const vector<shared_ptr<const Shell>> tmp = (*aiter)->shells();
40     basis.insert(basis.end(), tmp.begin(), tmp.end());
41     const vector<int> tmpoff = geom_->offset(cnt);
42     offset.insert(offset.end(), tmpoff.begin(), tmpoff.end());
43   }
44 
45   const int shift = sizeof(int) * 4;
46   const int size = basis.size();
47 
48   // first make max_density_change vector for each batch pair.
49   const double* density_data = density_->data();
50 
51   vector<double> max_density_change(size * size);
52   for (int i = 0; i != size; ++i) {
53     const int ioffset = offset[i];
54     const int isize = basis[i]->nbasis();
55     for (int j = i; j != size; ++j) {
56       const int joffset = offset[j];
57       const int jsize = basis[j]->nbasis();
58 
59       double cmax = 0.0;
60       for (int ii = ioffset; ii != ioffset + isize; ++ii) {
61         const int iin = ii * ndim();
62         for (int jj = joffset; jj != joffset + jsize; ++jj) {
63           cmax = max(cmax, fabs(density_data[iin + jj]));
64         }
65       }
66       const int ij = i * size + j;
67       const int ji = j * size + i;
68       max_density_change[ij] = cmax;
69       max_density_change[ji] = cmax;
70     }
71   }
72 
73   ////////////////////////////////////////////
74   // starting 2-e Fock matrix evaluation!
75   ////////////////////////////////////////////
76   //////////////// ONLY FOR REFERENCES. //////////////////
77 
78   for (int i0 = 0; i0 != size; ++i0) {
79     const shared_ptr<const Shell>  b0 = basis[i0];
80     const int b0offset = offset[i0];
81     const int b0size = b0->nbasis();
82     for (int i1 = i0; i1 != size; ++i1) {
83       const unsigned int i01 = i0 *size + i1;
84 
85       const shared_ptr<const Shell>  b1 = basis[i1];
86       const int b1offset = offset[i1];
87       const int b1size = b1->nbasis();
88 
89       const double density_change_01 = max_density_change[i01] * 4.0;
90 
91       for (int i2 = i0; i2 != size; ++i2) {
92         const shared_ptr<const Shell>  b2 = basis[i2];
93         const int b2offset = offset[i2];
94         const int b2size = b2->nbasis();
95 
96         const double density_change_02 = max_density_change[i0 * size + i2];
97         const double density_change_12 = max_density_change[i1 * size + i2];
98 
99         for (int i3 = i2; i3 != size; ++i3) {
100           const unsigned int i23 = i2 * size + i3;
101           if (i23 < i01) continue;
102 
103           const double density_change_23 = max_density_change[i2 * size + i3] * 4.0;
104           const double density_change_03 = max_density_change[i0 * size + i2];
105           const double density_change_13 = max_density_change[i0 * size + i2];
106 
107           const bool eqli01i23 = (i01 == i23);
108 
109           const shared_ptr<const Shell>  b3 = basis[i3];
110           const int b3offset = offset[i3];
111           const int b3size = b3->nbasis();
112 
113           const double mulfactor = max(max(max(density_change_01, density_change_02),
114                                            max(density_change_12, density_change_23)),
115                                            max(density_change_03, density_change_13));
116           const double integral_bound = mulfactor * schwarz_[i01] * schwarz_[i23];
117           const bool skip_schwarz = integral_bound < schwarz_thresh_;
118           if (skip_schwarz) continue;
119 
120           array<shared_ptr<const Shell>,4> input = {{b3, b2, b1, b0}};
121 #ifdef LIBINT_INTERFACE
122           Libint eribatch(input);
123 #else
124           ERIBatch eribatch(input, mulfactor);
125 #endif
126           eribatch.compute();
127           const double* eridata = eribatch.data();
128           for (int j0 = b0offset; j0 != b0offset + b0size; ++j0) {
129             const int j0n = j0 * ndim();
130 
131             for (int j1 = b1offset; j1 != b1offset + b1size; ++j1) {
132               const unsigned int nj01 = (j0 << shift) + j1;
133               const bool skipj0j1 = (j0 > j1);
134               if (skipj0j1) {
135                 eridata += b2size * b3size;
136                 continue;
137               }
138 
139               const bool eqlj0j1 = (j0 == j1);
140               const double scal01 = (eqlj0j1 ? 0.5 : 1.0);
141               const int j1n = j1 * ndim();
142 
143               for (int j2 = b2offset; j2 != b2offset + b2size; ++j2) {
144                 const int maxj1j2 = max(j1, j2);
145                 const int minj1j2 = min(j1, j2);
146 
147                 const int maxj0j2 = max(j0, j2);
148                 const int minj0j2 = min(j0, j2);
149                 const int j2n = j2 * ndim();
150 
151                 for (int j3 = b3offset; j3 != b3offset + b3size; ++j3, ++eridata) {
152                   const bool skipj2j3 = (j2 > j3);
153                   const unsigned int nj23 = (j2 << shift) + j3;
154                   const bool skipj01j23 = (nj01 > nj23) && eqli01i23;
155 
156                   if (skipj2j3 || skipj01j23) continue;
157 
158                   const int maxj1j3 = max(j1, j3);
159                   const int minj1j3 = min(j1, j3);
160 
161                   double intval = *eridata * scal01 * (j2 == j3 ? 0.5 : 1.0) * (nj01 == nj23 ? 0.25 : 0.5); // 1/2 in the Hamiltonian absorbed here
162                   const double intval4 = 4.0 * intval;
163 
164                   element(j1, j0) += density_data[j2n + j3] * intval4;
165                   element(j3, j2) += density_data[j0n + j1] * intval4;
166                   element(j3, j0) -= density_data[j1n + j2] * intval;
167                   element(maxj1j2, minj1j2) -= density_data[j0n + j3] * intval;
168                   element(maxj0j2, minj0j2) -= density_data[j1n + j3] * intval;
169                   element(maxj1j3, minj1j3) -= density_data[j0n + j2] * intval;
170                 }
171               }
172             }
173           }
174 
175         }
176       }
177     }
178   }
179   for (int i = 0; i != ndim(); ++i) element(i, i) *= 2.0;
180   fill_upper();
181 }
182 
183 
184 template<int DF>
fock_two_electron_part(shared_ptr<const Matrix> den_ex)185 void Fock<DF>::fock_two_electron_part(shared_ptr<const Matrix> den_ex) {
186   static_assert(DF == 1, "Wrong Fock matrix implementation is being compiled.");
187 #ifndef NDEBUG
188   cout << "    .. warning .. use a new Fock builder if possible (coeff_ required)" << endl;
189 #endif
190 
191   shared_ptr<const DFDist> df = geom_->df();
192 
193   // some constants
194   assert(ndim() == df->nbasis0());
195 
196   Timer pdebug(3);
197 
198   shared_ptr<Matrix> coeff = den_ex->copy();
199   *coeff *= -1.0;
200   int nocc = 0;
201   {
202     VectorB vec(ndim());
203     coeff->diagonalize(vec);
204     for (int i = 0; i != ndim(); ++i) {
205       if (vec[i] < -1.0e-8) {
206         ++nocc;
207         const double fac = std::sqrt(-vec(i));
208         for_each(coeff->element_ptr(0,i), coeff->element_ptr(0,i+1), [&fac](double& i) { i *= fac; });
209       } else { break; }
210     }
211   }
212   if (nocc == 0) return;
213   pdebug.tick_print("Compute coeff (redundant)");
214 
215   shared_ptr<DFHalfDist> halfbj = df->compute_half_transform(coeff->slice(0,nocc));
216   pdebug.tick_print("First index transform");
217 
218   shared_ptr<DFHalfDist> half = halfbj->apply_J();
219   pdebug.tick_print("Metric multiply");
220 
221   *this += *half->form_2index(half, -0.5);
222   pdebug.tick_print("Exchange build");
223 
224   *this += *df->compute_Jop(density_);
225   pdebug.tick_print("Coulomb build");
226 }
227 
228 
229 template<int DF>
fock_two_electron_part_with_coeff(const MatView ocoeff,const bool rhf,const double scale_exchange,const double scale_coulomb)230 void Fock<DF>::fock_two_electron_part_with_coeff(const MatView ocoeff, const bool rhf, const double scale_exchange, const double scale_coulomb) {
231   if (DF == 0) throw logic_error("Fock<DF>::fock_two_electron_part_with_coeff() is only for DF cases");
232 
233   Timer pdebug(3);
234 
235   shared_ptr<const DFDist> df = geom_->df();
236 
237   if (scale_exchange != 0.0) {
238     shared_ptr<DFHalfDist> halfbj = df->compute_half_transform(ocoeff);
239     pdebug.tick_print("First index transform");
240 
241     shared_ptr<DFHalfDist> half = halfbj->apply_J();
242     pdebug.tick_print("Metric multiply");
243 
244     *this += *half->form_2index(half, -1.0*scale_exchange);
245     pdebug.tick_print("Exchange build");
246 
247     if (rhf) {
248       Matrix oc(ocoeff);
249       auto coeff = make_shared<const Matrix>(*oc.transpose()*(2.0*scale_coulomb));
250       *this += *df->compute_Jop(half, coeff, true);
251     } else {
252       shared_ptr<Matrix> jop = df->compute_Jop(density_);
253       if (scale_coulomb != 1.0)
254         jop->scale(scale_coulomb);
255       *this += *jop;
256     }
257     // when gradient is requested..
258     if (store_half_)
259       half_ = half;
260   } else {
261     shared_ptr<Matrix> jop = df->compute_Jop(density_);
262     if (scale_coulomb != 1.0)
263       jop->scale(scale_coulomb);
264     *this += *jop;
265   }
266   pdebug.tick_print("Coulomb build");
267 
268 }
269 
270 
271 template class bagel::Fock<0>;
272 template class bagel::Fock<1>;
273 
274 BOOST_CLASS_EXPORT_IMPLEMENT(bagel::Fock<0>)
275 BOOST_CLASS_EXPORT_IMPLEMENT(bagel::Fock<1>)
276 
277