1 /*
2 * This source code is part of
3 *
4 * E R K A L E
5 * -
6 * HF/DFT from Hel
7 *
8 * Written by Susi Lehtola, 2010-2011
9 * Copyright (c) 2010-2011, Susi Lehtola
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 */
16
17
18 #include <algorithm>
19 #include <cfloat>
20 #include <cstdio>
21 #include "bfprod.h"
22 #include "../mathf.h"
23 #include "../basis.h"
24
operator <(const prod_gaussian_1d_contr_t & lhs,const prod_gaussian_1d_contr_t & rhs)25 bool operator<(const prod_gaussian_1d_contr_t &lhs, const prod_gaussian_1d_contr_t &rhs) {
26 return lhs.m < rhs.m;
27 }
28
operator ==(const prod_gaussian_1d_contr_t & lhs,const prod_gaussian_1d_contr_t & rhs)29 bool operator==(const prod_gaussian_1d_contr_t &lhs, const prod_gaussian_1d_contr_t &rhs) {
30 return lhs.m == rhs.m;
31 }
32
operator <(const prod_gaussian_1d_t & lhs,const prod_gaussian_1d_t & rhs)33 bool operator<(const prod_gaussian_1d_t & lhs, const prod_gaussian_1d_t & rhs) {
34 // Sort first by center
35 if(lhs.xp<rhs.xp)
36 return 1;
37 else if(lhs.xp==rhs.xp) {
38 // Then, sort by exponent
39 if(lhs.zeta<rhs.zeta)
40 return 1;
41 else if(lhs.zeta==rhs.zeta) {
42 // Last, sort by polynomials
43 if(lhs.c[0].m<rhs.c[0].m)
44 return 1;
45 }
46 }
47
48 return 0;
49 }
50
operator ==(const prod_gaussian_1d_t & lhs,const prod_gaussian_1d_t & rhs)51 bool operator==(const prod_gaussian_1d_t & lhs, const prod_gaussian_1d_t & rhs) {
52 return (lhs.xp==rhs.xp) && (lhs.zeta==rhs.zeta);
53 }
54
~prod_gaussian_1d()55 prod_gaussian_1d::~prod_gaussian_1d() {
56 }
57
prod_gaussian_1d(double xa,double xb,int la,int lb,double zetaa,double zetab)58 prod_gaussian_1d::prod_gaussian_1d(double xa, double xb, int la, int lb, double zetaa, double zetab) {
59 // Helper to push to stack
60 prod_gaussian_1d_t hlp;
61
62 // Compute reduced exponents zeta and eta
63 hlp.zeta=zetaa+zetab;
64 double eta=zetaa*zetab/hlp.zeta;
65
66 // Center of product Gaussian is at
67 hlp.xp=(zetaa*xa+zetab*xb)/hlp.zeta;
68
69 // Add term to list
70 p.push_back(hlp);
71
72 // Global scale factor is
73 double scale=exp(-eta*(xa-xb)*(xa-xb));
74
75 // Combinatorial factors
76 double afac[la+1];
77 for(int ia=0;ia<=la;ia++)
78 afac[ia]=choose(la,ia)*pow(hlp.xp-xa,la-ia);
79
80 double bfac[lb+1];
81 for(int ib=0;ib<=lb;ib++)
82 bfac[ib]=choose(lb,ib)*pow(hlp.xp-xb,lb-ib);
83
84 // Generate products
85 for(int ia=0;ia<=la;ia++)
86 for(int ib=0;ib<=lb;ib++) {
87 prod_gaussian_1d_contr_t tmp;
88 tmp.m=ia+ib;
89 tmp.c=scale*afac[ia]*bfac[ib];
90
91 add_contr(0,tmp);
92 }
93 }
94
operator +(const prod_gaussian_1d & rhs) const95 prod_gaussian_1d prod_gaussian_1d::operator+(const prod_gaussian_1d & rhs) const {
96 // Returned value
97 prod_gaussian_1d ret(*this);
98
99 // Add rhs terms
100 for(size_t i=0;i<rhs.p.size();i++)
101 ret.add_term(rhs.p[i]);
102
103 return ret;
104 }
105
operator +=(const prod_gaussian_1d & rhs)106 prod_gaussian_1d & prod_gaussian_1d::operator+=(const prod_gaussian_1d & rhs) {
107 // Add rhs terms
108 for(size_t i=0;i<rhs.p.size();i++)
109 add_term(rhs.p[i]);
110
111 return *this;
112 }
113
add_term(const prod_gaussian_1d_t & t)114 void prod_gaussian_1d::add_term(const prod_gaussian_1d_t & t) {
115 if(p.size()==0) {
116 p.push_back(t);
117 } else {
118 // Get upper bound
119 std::vector<prod_gaussian_1d_t>::iterator high;
120 high=std::upper_bound(p.begin(),p.end(),t);
121
122 // Corresponding index is
123 size_t ind=high-p.begin();
124
125 if(ind>0 && p[ind-1]==t)
126 // Need to find place in p[ind-1] to add terms.
127 // Loop over terms in t
128 for(size_t it=0;it<t.c.size();it++)
129 add_contr(ind-1,t.c[it]);
130 else {
131 // Term does not exist, add it
132 p.insert(high,t);
133 }
134 }
135 }
136
add_contr(size_t ind,const prod_gaussian_1d_contr_t & t)137 void prod_gaussian_1d::add_contr(size_t ind, const prod_gaussian_1d_contr_t & t) {
138 if(p[ind].c.size()==0) {
139 p[ind].c.push_back(t);
140 } else {
141
142 // Get upper bound
143 std::vector<prod_gaussian_1d_contr_t>::iterator hi;
144 hi=std::upper_bound(p[ind].c.begin(),p[ind].c.end(),t);
145
146 size_t indt=hi-p[ind].c.begin();
147 if(indt>0 && p[ind].c[indt-1] == t)
148 // Found it!
149 p[ind].c[indt-1].c += t.c;
150 else
151 // Need to add the term.
152 p[ind].c.insert(hi,t);
153 }
154 }
155
print() const156 void prod_gaussian_1d::print() const {
157 for(size_t i=0;i<p.size();i++) {
158 printf("Product gaussian at %e with exponent %e, contains %i terms:\n",p[i].xp,p[i].zeta, (int) p[i].c.size());
159
160 for(size_t j=0;j<p[i].c.size();j++)
161 printf(" %+e x^%i",p[i].c[j].c,p[i].c[j].m);
162 printf("\n");
163 }
164 }
165
get() const166 std::vector<prod_gaussian_1d_t> prod_gaussian_1d::get() const {
167 return p;
168 }
169
operator <(const prod_gaussian_3d_contr_t & lhs,const prod_gaussian_3d_contr_t & rhs)170 bool operator<(const prod_gaussian_3d_contr_t &lhs, const prod_gaussian_3d_contr_t &rhs) {
171 // Sort first by total am
172 if(lhs.l+lhs.m+lhs.n < rhs.l+rhs.m+rhs.n)
173 return 1;
174 else if(lhs.l+lhs.m+lhs.n == rhs.l+rhs.m+rhs.n) {
175
176 // Then, by l
177 if(lhs.l < rhs.l)
178 return 1;
179 else if(lhs.l == rhs.l) {
180 // Then, by m
181 if(lhs.m < rhs.m)
182 return 1;
183 else if(lhs.m == rhs.m) {
184 // Then, by n
185 return lhs.n<rhs.n;
186 }
187 }
188 }
189
190 return 0;
191 }
192
operator ==(const prod_gaussian_3d_contr_t & lhs,const prod_gaussian_3d_contr_t & rhs)193 bool operator==(const prod_gaussian_3d_contr_t &lhs, const prod_gaussian_3d_contr_t &rhs) {
194 return (lhs.l == rhs.l) && (lhs.m == rhs.m) && (lhs.n == rhs.n);
195 }
196
operator <(const prod_gaussian_3d_t & lhs,const prod_gaussian_3d_t & rhs)197 bool operator<(const prod_gaussian_3d_t & lhs, const prod_gaussian_3d_t & rhs) {
198 // Sort first by exponent
199 if(lhs.zeta<rhs.zeta)
200 return 1;
201 else if(lhs.zeta==rhs.zeta) {
202
203 // then by center
204 if(lhs.xp<rhs.xp)
205 return 1;
206 else if(lhs.xp == rhs.xp) {
207
208 if(lhs.yp<rhs.yp)
209 return 1;
210 else if(lhs.yp == rhs.yp) {
211
212 if(lhs.zp<rhs.zp)
213 return 1;
214 else if(lhs.zp==rhs.zp) {
215
216 // Last, sort by polynomials
217 if(lhs.c[lhs.c.size()-1].l+lhs.c[lhs.c.size()-1].m+lhs.c[lhs.c.size()-1].n<rhs.c[rhs.c.size()-1].l+rhs.c[rhs.c.size()-1].m+rhs.c[rhs.c.size()-1].n)
218 return 1;
219 }
220 }
221 }
222 }
223
224 return 0;
225 }
226
operator ==(const prod_gaussian_3d_t & lhs,const prod_gaussian_3d_t & rhs)227 bool operator==(const prod_gaussian_3d_t & lhs, const prod_gaussian_3d_t & rhs) {
228 return (lhs.xp==rhs.xp) && (lhs.yp==rhs.yp) && (lhs.zp==rhs.zp) && (lhs.zeta==rhs.zeta);
229 }
230
prod_gaussian_3d()231 prod_gaussian_3d::prod_gaussian_3d() {
232 }
233
~prod_gaussian_3d()234 prod_gaussian_3d::~prod_gaussian_3d() {
235 }
236
prod_gaussian_3d(double xa,double xb,double ya,double yb,double za,double zb,int la,int lb,int ma,int mb,int na,int nb,double zetaa,double zetab)237 prod_gaussian_3d::prod_gaussian_3d(double xa, double xb, double ya, double yb, double za, double zb, int la, int lb, int ma, int mb, int na, int nb, double zetaa, double zetab) {
238 // Form 1d transforms
239 prod_gaussian_1d xp(xa,xb,la,lb,zetaa,zetab);
240 prod_gaussian_1d yp(ya,yb,ma,mb,zetaa,zetab);
241 prod_gaussian_1d zp(za,zb,na,nb,zetaa,zetab);
242
243 // and get them
244 std::vector<prod_gaussian_1d_t> x=xp.get();
245 std::vector<prod_gaussian_1d_t> y=yp.get();
246 std::vector<prod_gaussian_1d_t> z=zp.get();
247
248 // Initialize list:
249 prod_gaussian_3d_t hlp;
250 // Get center. We only have a single set of coordinates and a single
251 // exponent, so the size of x, y and z is always 1.
252 hlp.xp=x[0].xp;
253 hlp.yp=y[0].xp;
254 hlp.zp=z[0].xp;
255 // and reduced exponent
256 hlp.zeta=zetaa+zetab;
257 // Add to stack
258 p.push_back(hlp);
259
260 // Now, add the terms in the product.
261 for(size_t i=0;i<x[0].c.size();i++)
262 for(size_t j=0;j<y[0].c.size();j++)
263 for(size_t k=0;k<z[0].c.size();k++) {
264 prod_gaussian_3d_contr_t tmp;
265 tmp.c=x[0].c[i].c*y[0].c[j].c*z[0].c[k].c;
266 // Angular moment
267 tmp.l=x[0].c[i].m;
268 tmp.m=y[0].c[j].m;
269 tmp.n=z[0].c[k].m;
270
271 add_contr(0,tmp);
272 }
273 }
274
275
operator +(const prod_gaussian_3d & rhs) const276 prod_gaussian_3d prod_gaussian_3d::operator+(const prod_gaussian_3d & rhs) const {
277 // Returned value
278 prod_gaussian_3d ret(*this);
279 ret+=rhs;
280 return ret;
281 }
282
operator +=(const prod_gaussian_3d & rhs)283 prod_gaussian_3d & prod_gaussian_3d::operator+=(const prod_gaussian_3d & rhs) {
284 // Add rhs terms
285 for(size_t i=0;i<rhs.p.size();i++)
286 add_term(rhs.p[i]);
287
288 return *this;
289 }
290
operator *(double fac) const291 prod_gaussian_3d prod_gaussian_3d::operator*(double fac) const {
292 prod_gaussian_3d ret=*this;
293
294 for(size_t i=0;i<ret.p.size();i++)
295 for(size_t j=0;j<ret.p[i].c.size();j++)
296 ret.p[i].c[j].c*=fac;
297
298 return ret;
299 }
300
add_term(const prod_gaussian_3d_t & t)301 void prod_gaussian_3d::add_term(const prod_gaussian_3d_t & t) {
302 if(p.size()==0) {
303 p.push_back(t);
304 } else {
305 // Get upper bound
306 std::vector<prod_gaussian_3d_t>::iterator high;
307 high=std::upper_bound(p.begin(),p.end(),t);
308
309 // Corresponding index is
310 size_t ind=high-p.begin();
311
312 if(ind>0 && p[ind-1]==t)
313 // Need to find place in p[ind-1] to add terms.
314 // Loop over terms in t
315 for(size_t it=0;it<t.c.size();it++)
316 add_contr(ind-1,t.c[it]);
317 else {
318 // Term does not exist, add it
319 p.insert(high,t);
320 }
321 }
322 }
323
add_contr(size_t ind,const prod_gaussian_3d_contr_t & t)324 void prod_gaussian_3d::add_contr(size_t ind, const prod_gaussian_3d_contr_t & t) {
325 if(p[ind].c.size()==0) {
326 p[ind].c.push_back(t);
327 } else {
328
329 // Get upper bound
330 std::vector<prod_gaussian_3d_contr_t>::iterator hi;
331 hi=std::upper_bound(p[ind].c.begin(),p[ind].c.end(),t);
332
333 size_t indt=hi-p[ind].c.begin();
334 if(indt>0 && p[ind].c[indt-1] == t)
335 // Found it!
336 p[ind].c[indt-1].c += t.c;
337 else
338 // Need to add the term.
339 p[ind].c.insert(hi,t);
340 }
341 }
342
clean()343 void prod_gaussian_3d::clean() {
344 for(size_t i=0;i<p.size();i++)
345 for(size_t j=p[i].c.size()-1;j<p[i].c.size();j--)
346 if(p[i].c[j].c==0.0) {
347 // printf("Erasing p[%i].c[%i], for which c=%e.\n",i,j,p[i].c[j].c);
348 p[i].c.erase(p[i].c.begin()+j);
349 }
350 }
351
integral() const352 double prod_gaussian_3d::integral() const {
353 double res=0.0;
354 for(size_t i=0;i<p.size();i++) { // Loop over centers
355 // Exponent is
356 double zeta=p[i].zeta;
357
358 // Loop over contraction
359 for(size_t j=0;j<p[i].c.size();j++) {
360 // Exponents are
361 int l=p[i].c[j].l;
362 int m=p[i].c[j].m;
363 int n=p[i].c[j].n;
364
365 // Check that these are even
366 if((l%2==1) || (m%2==1) || (n%2==1))
367 continue;
368
369 // Get the halves
370 int lh=l/2;
371 int mh=m/2;
372 int nh=n/2;
373
374 // Contraction coefficient is
375 double c=p[i].c[j].c;
376
377 // The contribution is
378 res+=c*pow(M_PI,3.0/2.0)*doublefact(l-1)*doublefact(m-1)*doublefact(n-1)*pow(2.0,-(lh+mh+nh))/(pow(zeta,lh+mh+nh)*pow(sqrt(zeta),3));
379 }
380 }
381
382 return res;
383 }
384
get() const385 std::vector<prod_gaussian_3d_t> prod_gaussian_3d::get() const {
386 return p;
387 }
388
print() const389 void prod_gaussian_3d::print() const {
390 for(size_t i=0;i<p.size();i++) {
391 printf("Product gaussian at (% e,% e,% e) with exponent %e, contains %i terms:\n",p[i].xp,p[i].yp,p[i].zp,p[i].zeta,(int) p[i].c.size());
392 for(size_t j=0;j<p[i].c.size();j++)
393 printf("\t%+e x^%i y^%i z^%i\n",p[i].c[j].c,p[i].c[j].l,p[i].c[j].m,p[i].c[j].n);
394 }
395 }
396
397
compute_product(const BasisSet & bas,size_t is,size_t js)398 std::vector<prod_gaussian_3d> compute_product(const BasisSet & bas, size_t is, size_t js) {
399 // Contractions on shells
400 std::vector<contr_t> icontr=bas.get_contr(is);
401 std::vector<contr_t> jcontr=bas.get_contr(js);
402
403 // Cartesian functions on shells
404 std::vector<shellf_t> icart=bas.get_cart(is);
405 std::vector<shellf_t> jcart=bas.get_cart(js);
406
407 // Centers of shells
408 coords_t icen=bas.get_shell_center(is);
409 coords_t jcen=bas.get_shell_center(js);
410
411 // Returned array
412 std::vector<prod_gaussian_3d> ret;
413 ret.reserve(icart.size()*jcart.size());
414
415 // Form products
416 for(size_t ii=0;ii<icart.size();ii++)
417 for(size_t jj=0;jj<jcart.size();jj++) {
418
419 // Result;
420 prod_gaussian_3d tmp;
421
422 // Loop over exponents
423 for(size_t ix=0;ix<icontr.size();ix++)
424 for(size_t jx=0;jx<jcontr.size();jx++) {
425 // Compute product
426 prod_gaussian_3d term(icen.x,jcen.x,icen.y,jcen.y,icen.z,jcen.z,icart[ii].l,jcart[jj].l,icart[ii].m,jcart[jj].m,icart[ii].n,jcart[jj].n,icontr[ix].z,jcontr[jx].z);
427 // Add to partial result
428 tmp+=term*(icontr[ix].c*jcontr[jx].c);
429
430 /*
431 printf("Product gaussian of (%i,%i,%i) centered at (% e,% e,% e) and (%i,%i,%i) centered at (% e,% e,% e) is\n",icart[ii].l,icart[ii].m,icart[ii].n,icen.x,icen.y,icen.z,jcart[jj].l,jcart[jj].m,jcart[jj].n,jcen.x,jcen.y,jcen.z);
432 term.print();
433 */
434 }
435
436 // Plug in normalization factors
437 tmp=tmp*(icart[ii].relnorm*jcart[jj].relnorm);
438
439 // Add to stack
440 ret.push_back(tmp);
441 }
442
443 // Transform into spherical basis if necessary
444 if(bas.lm_in_use(is) || bas.lm_in_use(js))
445 return spherical_transform(bas,is,js,ret);
446 else {
447 // Clean out terms with zero contribution
448 for(size_t i=0;i<ret.size();i++)
449 ret[i].clean();
450 // Return result
451 return ret;
452 }
453 }
454
spherical_transform(const BasisSet & bas,size_t is,size_t js,std::vector<prod_gaussian_3d> & res)455 std::vector<prod_gaussian_3d> spherical_transform(const BasisSet & bas, size_t is, size_t js, std::vector<prod_gaussian_3d> & res) {
456 bool lm_i=bas.lm_in_use(is);
457 bool lm_j=bas.lm_in_use(js);
458
459 const size_t Ni_cart=bas.get_Ncart(is);
460 const size_t Nj_cart=bas.get_Ncart(js);
461
462 const size_t Ni_tgt=bas.get_Nbf(is);
463 const size_t Nj_tgt=bas.get_Nbf(js);
464
465 // First, transform over j. Helper array
466 std::vector<prod_gaussian_3d> tmp(Ni_cart*Nj_tgt);
467
468 if(lm_j) {
469 // Get transformation matrix
470 arma::mat trans_j=bas.get_trans(js);
471
472 // Loop over functions
473 for(size_t iic=0;iic<Ni_cart;iic++)
474 for(size_t jjs=0;jjs<Nj_tgt;jjs++)
475 for(size_t jjc=0;jjc<Nj_cart;jjc++)
476 tmp[iic*Nj_tgt+jjs]+=res[iic*Nj_cart+jjc]*trans_j(jjs,jjc);
477 } else
478 // No transformation necessary.
479 tmp=res;
480
481 if(lm_i) {
482 // Get transformation matrix
483 arma::mat trans_i=bas.get_trans(is);
484
485 // Resize output vector
486 res.resize(Ni_tgt*Nj_tgt);
487
488 // Loop over functions
489 for(size_t jjs=0;jjs<Nj_tgt;jjs++)
490 for(size_t iis=0;iis<Ni_tgt;iis++) {
491 // Clean output
492 res[iis*Nj_tgt+jjs]=prod_gaussian_3d();
493 // Compute transform
494 for(size_t iic=0;iic<Ni_cart;iic++)
495 res[iis*Nj_tgt+jjs]+=tmp[iic*Nj_tgt+jjs]*trans_i(iis,iic);
496 }
497
498 // Clean out terms with zero contribution
499 for(size_t i=0;i<res.size();i++)
500 res[i].clean();
501 return res;
502 } else {
503 // No transformation necessary.
504 for(size_t i=0;i<tmp.size();i++)
505 tmp[i].clean();
506 return tmp;
507 }
508 }
509
510
compute_products(const BasisSet & bas)511 std::vector<prod_gaussian_3d> compute_products(const BasisSet & bas) {
512 // Amount of basis functions is
513 size_t Nbf=bas.get_Nbf();
514
515 // .. so the size of the returned array is
516 std::vector<prod_gaussian_3d> ret(Nbf*(Nbf+1)/2);
517
518 // Get shells in the basis set.
519 std::vector<GaussianShell> shells=bas.get_shells();
520
521 // Amount of functions on shells
522 std::vector<size_t> nbf(shells.size());
523 for(size_t is=0;is<shells.size();is++)
524 nbf[is]=shells[is].get_Nbf();
525
526 // First functions on shells
527 std::vector<size_t> ind0(shells.size());
528 for(size_t is=0;is<shells.size();is++)
529 ind0[is]=shells[is].get_first_ind();
530
531
532 // Form products
533 for(size_t is=0;is<shells.size();is++) {
534 // Do off-diagonal first
535 for(size_t js=0;js<is;js++) {
536 // Compute products
537 std::vector<prod_gaussian_3d> prod=compute_product(bas,is,js);
538
539 // Store output
540 for(size_t ii=0;ii<nbf[is];ii++)
541 for(size_t jj=0;jj<nbf[js];jj++) {
542 size_t i=ind0[is]+ii;
543 size_t j=ind0[js]+jj;
544
545 // Since is>js we know i>j
546 ret[(i*(i+1))/2 + j]=prod[ii*nbf[js]+jj];
547 }
548 }
549
550 // Then, do diagonal
551 std::vector<prod_gaussian_3d> prod=compute_product(bas,is,is);
552 for(size_t ii=0;ii<nbf[is];ii++)
553 for(size_t jj=0;jj<=ii;jj++) {
554 size_t i=ind0[is]+ii;
555 size_t j=ind0[is]+jj;
556
557 // Here we know as well that i>=j
558 ret[(i*(i+1))/2 + j]=prod[ii*nbf[is]+jj];
559 }
560 }
561
562 return ret;
563 }
564