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