1 /*
2  *                This source code is part of
3  *
4  *                     E  R  K  A  L  E
5  *                             -
6  *                       DFT from Hel
7  *
8  * Written by Susi Lehtola, 2010-2013
9  * Copyright (c) 2010-2013, 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 
19 #include <algorithm>
20 #include <cfloat>
21 #include <cmath>
22 #include <cstdio>
23 #include <string>
24 // For exceptions
25 #include <sstream>
26 #include <stdexcept>
27 
28 #include "basis.h"
29 #include "eriworker.h"
30 #include "elements.h"
31 #include "integrals.h"
32 #include "linalg.h"
33 #include "mathf.h"
34 #include "obara-saika.h"
35 #include "settings.h"
36 #include "solidharmonics.h"
37 #include "stringutil.h"
38 
39 // Debug LIBINT routines against Huzinaga integrals?
40 //#define LIBINTDEBUG
41 
42 // Derivative operator
_der1(const double x[],int l,double zeta)43 inline double _der1(const double x[], int l, double zeta) {
44   double d=-2.0*zeta*x[l+1];
45   if(l>0)
46     d+=l*x[l-1];
47   return d;
48 }
49 
50 // Second derivative operator
_der2(const double x[],int l,double zeta)51 inline double _der2(const double x[], int l, double zeta) {
52   double d=4.0*zeta*zeta*x[l+2] - 2.0*zeta*(2*l+1)*x[l];
53   if(l>1)
54     d+=l*(l-1)*x[l-2];
55   return d;
56 }
57 
58 // Third derivative operator
_der3(const double x[],int l,double zeta)59 inline double _der3(const double x[], int l, double zeta) {
60   double d=-8.0*zeta*zeta*zeta*x[l+3] + 12.0*zeta*zeta*(l+1)*x[l+1];
61   if(l>0)
62     d-=6.0*zeta*l*l*x[l-1];
63   if(l>2)
64     d+=l*(l-1)*(l-2)*x[l-3];
65   return d;
66 }
67 
operator ==(const nucleus_t & lhs,const nucleus_t & rhs)68 bool operator==(const nucleus_t & lhs, const nucleus_t & rhs) {
69   return (lhs.ind == rhs.ind) && (lhs.r == rhs.r) && (lhs.Z == rhs.Z) && \
70     (lhs.bsse == rhs.bsse) && (stricmp(lhs.symbol,rhs.symbol)==0);
71 }
72 
operator ==(const coords_t & lhs,const coords_t & rhs)73 bool operator==(const coords_t & lhs, const coords_t & rhs) {
74   return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z);
75 }
76 
coords_to_vec(const coords_t & c)77 arma::vec coords_to_vec(const coords_t & c) {
78   arma::vec r(3);
79   r(0)=c.x;
80   r(1)=c.y;
81   r(2)=c.z;
82   return r;
83 }
84 
vec_to_coords(const arma::vec & v)85 coords_t vec_to_coords(const arma::vec & v) {
86   if(v.n_elem != 3) {
87     std::ostringstream oss;
88     oss << "Expected a 3-element vector, got " << v.n_elem << "!\n";
89     throw std::logic_error(oss.str());
90   }
91   coords_t r;
92   r.x=v(0);
93   r.y=v(1);
94   r.z=v(2);
95   return r;
96 }
97 
98 // Operators for computing displacements
operator -(const coords_t & lhs,const coords_t & rhs)99 coords_t operator-(const coords_t & lhs, const coords_t & rhs) {
100   coords_t ret;
101   ret.x=lhs.x-rhs.x;
102   ret.y=lhs.y-rhs.y;
103   ret.z=lhs.z-rhs.z;
104   return ret;
105 }
106 
operator +(const coords_t & lhs,const coords_t & rhs)107 coords_t operator+(const coords_t & lhs, const coords_t& rhs) {
108   coords_t ret;
109   ret.x=lhs.x+rhs.x;
110   ret.y=lhs.y+rhs.y;
111   ret.z=lhs.z+rhs.z;
112   return ret;
113 }
114 
operator /(const coords_t & lhs,double fac)115 coords_t operator/(const coords_t & lhs, double fac) {
116   coords_t ret;
117   ret.x=lhs.x/fac;
118   ret.y=lhs.y/fac;
119   ret.z=lhs.z/fac;
120   return ret;
121 }
122 
operator *(const coords_t & lhs,double fac)123 coords_t operator*(const coords_t & lhs, double fac) {
124   coords_t ret;
125   ret.x=lhs.x*fac;
126   ret.y=lhs.y*fac;
127   ret.z=lhs.z*fac;
128   return ret;
129 }
130 
operator <(const contr_t & lhs,const contr_t & rhs)131 bool operator<(const contr_t & lhs, const contr_t & rhs) {
132   // Decreasing order of exponents.
133   return lhs.z>rhs.z;
134 }
135 
operator ==(const contr_t & lhs,const contr_t & rhs)136 bool operator==(const contr_t & lhs, const contr_t & rhs) {
137   //  return (lhs.z==rhs.z) && (lhs.c==rhs.c);
138 
139   // Since this also needs to work for saved and reloaded basis sets, we need to relax the comparison.
140   const double tol=sqrt(DBL_EPSILON);
141 
142   bool same=(fabs(lhs.z-rhs.z)<tol*std::max(1.0,fabs(lhs.z))) && (fabs(lhs.c-rhs.c)<tol*std::max(1.0,fabs(lhs.z)));
143 
144   /*
145     if(!same) {
146     fprintf(stderr,"Contractions differ: %e %e vs %e %e, diff %e %e!\n",lhs.c,lhs.z,rhs.c,rhs.z,rhs.c-lhs.c,rhs.z-lhs.z);
147     }
148   */
149 
150   return same;
151 }
152 
GaussianShell()153 GaussianShell::GaussianShell() {
154   // Dummy constructor
155 }
156 
GaussianShell(int amv,bool lm,const std::vector<contr_t> & C)157 GaussianShell::GaussianShell(int amv, bool lm, const std::vector<contr_t> & C) {
158   // Construct shell of basis functions
159 
160   // Store contraction
161   c=C;
162   // Sort the contraction
163   sort();
164 
165   // Set angular momentum
166   am=amv;
167   // Use spherical harmonics?
168   uselm=lm;
169 
170   // If spherical harmonics are used, fill transformation matrix
171   if(uselm)
172     transmat=Ylm_transmat(am);
173   else {
174     // Do away with uninitialized value warnings in valgrind
175     transmat=arma::mat(1,1);
176     transmat(0,0)=1.0/0.0; // Initialize to NaN
177   }
178 
179   // Compute necessary amount of Cartesians
180   size_t Ncart=(am+1)*(am+2)/2;
181   // Allocate memory
182   cart.reserve(Ncart);
183   cart.resize(Ncart);
184   // Initialize the shells
185 
186   int n=0;
187   for(int i=0; i<=am; i++) {
188     int nx = am - i;
189     for(int j=0; j<=i; j++) {
190       int ny = i-j;
191       int nz = j;
192 
193       cart[n].l=nx;
194       cart[n].m=ny;
195       cart[n].n=nz;
196       cart[n].relnorm=1.0;
197       n++;
198     }
199   }
200 
201   // Default values
202   indstart=0;
203   cenind=0;
204   cen.x=cen.y=cen.z=0.0;
205 }
206 
~GaussianShell()207 GaussianShell::~GaussianShell() {
208 }
209 
set_first_ind(size_t ind)210 void GaussianShell::set_first_ind(size_t ind) {
211   indstart=ind;
212 }
213 
set_center(const coords_t & cenv,size_t cenindv)214 void GaussianShell::set_center(const coords_t & cenv, size_t cenindv) {
215   cen=cenv;
216   cenind=cenindv;
217 }
218 
sort()219 void GaussianShell::sort() {
220   std::stable_sort(c.begin(),c.end());
221 }
222 
convert_contraction()223 void GaussianShell::convert_contraction() {
224   // Convert contraction from contraction of normalized gaussians to
225   // contraction of unnormalized gaussians.
226 
227   // Note - these refer to cartesian functions!
228   double fac=pow(M_2_PI,0.75)*pow(2,am)/sqrt(doublefact(2*am-1));
229 
230   for(size_t i=0;i<c.size();i++)
231     c[i].c*=fac*pow(c[i].z,am/2.0+0.75);
232 }
233 
convert_sap_contraction()234 void GaussianShell::convert_sap_contraction() {
235   // Convert contraction from contraction of normalized density
236   // gaussians to contraction of unnormalized gaussians.
237 
238   if(am != 0) throw std::logic_error("SAP basis should only have S functions!\n");
239   for(size_t i=0;i<c.size();i++)
240     c[i].c*=pow(c[i].z/M_PI,1.5);
241 }
242 
normalize(bool coeffs)243 void GaussianShell::normalize(bool coeffs) {
244   // Normalize contraction of unnormalized primitives wrt first function on shell
245 
246   // Check for dummy shell
247   if(c.size()==1 && c[0].z==0.0) {
248     // Yes, this is a dummy.
249     c[0].c=1.0;
250     return;
251   }
252 
253   if(coeffs) {
254     double fact=0.0;
255 
256     // Calculate overlap of exponents
257     for(size_t i=0;i<c.size();i++)
258       for(size_t j=0;j<c.size();j++)
259 	fact+=c[i].c*c[j].c/pow(c[i].z+c[j].z,am+1.5);
260 
261     // Add constant part
262     fact*=pow(M_PI,1.5)*doublefact(2*am-1)/pow(2.0,am);
263 
264     // The coefficients must be scaled by 1/sqrt(fact)
265     fact=1.0/sqrt(fact);
266     for(size_t i=0;i<c.size();i++)
267       c[i].c*=fact;
268   }
269 
270   // FIXME: Do something more clever here.
271   if(!uselm) {
272     // Compute relative normalization factors
273     for(size_t i=0;i<cart.size();i++)
274       cart[i].relnorm=sqrt(doublefact(2*am-1)/(doublefact(2*cart[i].l-1)*doublefact(2*cart[i].m-1)*doublefact(2*cart[i].n-1)));
275   } else {
276     // Compute self-overlap
277     arma::mat S=overlap(*this);
278     // and scale coefficients.
279     for(size_t i=0;i<cart.size();i++)
280       cart[i].relnorm/=sqrt(S(0,0));
281   }
282 }
283 
coulomb_normalize()284 void GaussianShell::coulomb_normalize() {
285   // Normalize functions using Coulomb norm
286   size_t Ncart=cart.size();
287   size_t Nbf=get_Nbf();
288 
289   // Dummy shell
290   GaussianShell dummy;
291   dummy=dummyshell();
292 
293   // Compute ERI
294   ERIWorker eri(get_am(),get_Ncontr());
295   eri.compute(this,&dummy,this,&dummy);
296   const std::vector<double> * erip=eri.getp();
297 
298   if(!uselm) {
299     // Cartesian functions
300     for(size_t i=0;i<Ncart;i++)
301       cart[i].relnorm*=1.0/sqrt((*erip)[i*Nbf+i]);
302   } else {
303     // Spherical normalization, need to distribute
304     // normalization coefficient among cartesians
305 
306     // Spherical ERI is
307     // ERI = transmat * ERI_cart * trans(transmat)
308 
309     // FIXME - Do something more clever here
310     // Check that all factors are the same
311     int diff=0;
312     for(size_t i=1;i<Nbf;i++)
313       if(fabs((*erip)[i*Nbf+i]-(*erip)[0])>1000*DBL_EPSILON*(*erip)[0]) {
314 	printf("%e != %e, diff %e\n",(*erip)[i*Nbf+i],(*erip)[0],(*erip)[i*Nbf+i]-(*erip)[0]);
315 	diff++;
316       }
317 
318     if(diff) {
319       ERROR_INFO();
320       std::ostringstream oss;
321       oss << "\nSpherical functions have different norms!\n";
322       throw std::runtime_error(oss.str());
323     }
324 
325     // Scale coefficients
326     for(size_t i=0;i<Ncart;i++)
327       cart[i].relnorm*=1.0/sqrt((*erip)[0]);
328   }
329 }
330 
get_contr() const331 std::vector<contr_t> GaussianShell::get_contr() const {
332   return c;
333 }
334 
get_cart() const335 std::vector<shellf_t> GaussianShell::get_cart() const {
336   return cart;
337 }
338 
get_contr_normalized() const339 std::vector<contr_t> GaussianShell::get_contr_normalized() const {
340   // Returned array
341   std::vector<contr_t> cn(c);
342 
343   // Note - these refer to cartesian functions!
344   double fac=pow(M_2_PI,0.75)*pow(2,am)/sqrt(doublefact(2*am-1));
345 
346   // Convert coefficients to those of normalized primitives
347   for(size_t i=0;i<cn.size();i++)
348     cn[i].c/=fac*pow(cn[i].z,am/2.0+0.75);
349 
350   return cn;
351 }
352 
get_Nbf() const353 size_t GaussianShell::get_Nbf() const {
354   if(uselm)
355     return get_Nlm();
356   else
357     return get_Ncart();
358 }
359 
get_Nlm() const360 size_t GaussianShell::get_Nlm() const {
361   return 2*am+1;
362 }
363 
range(double eps) const364 double GaussianShell::range(double eps) const {
365   double oldr;
366   // Start at
367   double r=1.0;
368 
369   double val;
370   // Increase r so that value certainly has dropped below.
371   do {
372     // Increase value of r.
373     oldr=r;
374     r*=2.0;
375 
376     val=0.0;
377     for(size_t i=0;i<c.size();i++)
378       val+=c[i].c*exp(-c[i].z*r*r);
379     val*=pow(r,am);
380   } while(fabs(val)>eps);
381 
382   // OK, now the range lies in the range [oldr,r]. Use binary search to refine
383   double left=oldr, right=r;
384   double middle=(left+right)/2.0;
385 
386   while(right-left>10*DBL_EPSILON*right) {
387     // Compute middle of interval
388     middle=(left+right)/2.0;
389 
390     // Compute value in the middle
391     val=0.0;
392     for(size_t i=0;i<c.size();i++)
393       val+=c[i].c*exp(-c[i].z*middle*middle);
394     val*=pow(middle,am);
395 
396     // Switch values
397     if(fabs(val)<eps) {
398       // Switch right value
399       right=middle;
400     } else
401       // Switch left value
402       left=middle;
403   }
404 
405   return middle;
406 }
407 
lm_in_use() const408 bool GaussianShell::lm_in_use() const {
409   return uselm;
410 }
411 
set_lm(bool lm)412 void GaussianShell::set_lm(bool lm) {
413   uselm=lm;
414 
415   if(uselm)
416     transmat=Ylm_transmat(am);
417   else
418     transmat=arma::mat();
419 }
420 
get_trans() const421 arma::mat GaussianShell::get_trans() const {
422   return transmat;
423 }
424 
get_Ncart() const425 size_t GaussianShell::get_Ncart() const {
426   return cart.size();
427 }
428 
get_Ncontr() const429 size_t GaussianShell::get_Ncontr() const {
430   return c.size();
431 }
432 
get_am() const433 int GaussianShell::get_am() const {
434   return am;
435 }
436 
get_center_ind() const437 size_t GaussianShell::get_center_ind() const {
438   //  return cen->ind;
439   return cenind;
440 }
441 
get_center() const442 coords_t GaussianShell::get_center() const {
443   return cen;
444 }
445 
operator <(const GaussianShell & rhs) const446 bool GaussianShell::operator<(const GaussianShell & rhs) const {
447   // Sort first by nucleus
448   if(cenind < rhs.cenind)
449     return true;
450   else if(cenind == rhs.cenind) {
451     // Then by angular momentum
452     if(am<rhs.am)
453       return true;
454     else if(am==rhs.am) {
455       // Then by decreasing order of exponents
456       if(c.size() && rhs.c.size())
457 	return c[0].z>rhs.c[0].z;
458     }
459   }
460 
461   return false;
462 }
463 
464 
operator ==(const GaussianShell & rhs) const465 bool GaussianShell::operator==(const GaussianShell & rhs) const {
466   // Check first nucleus
467   if(cenind != rhs.cenind) {
468     //    fprintf(stderr,"Center indices differ!\n");
469     return false;
470   }
471 
472   // Then, angular momentum
473   if(am!=rhs.am) {
474     //    fprintf(stderr,"Angular momentum differs!\n");
475     return false;
476   }
477 
478   // Then, by exponents
479   if(c.size() != rhs.c.size()) {
480     //    fprintf(stderr,"Contraction size differs!\n");
481     return false;
482   }
483 
484   for(size_t i=0;i<c.size();i++) {
485     if(!(c[i]==rhs.c[i])) {
486       //      fprintf(stderr,"%i:th contraction differs!\n",(int) i+1);
487       return false;
488     }
489   }
490 
491   return true;
492 }
493 
get_first_ind() const494 size_t GaussianShell::get_first_ind() const {
495   return indstart;
496 }
497 
get_last_ind() const498 size_t GaussianShell::get_last_ind() const {
499   return indstart+get_Nbf()-1;
500 }
501 
print() const502 void GaussianShell::print() const {
503 
504   printf("\t%c shell at nucleus %3i with with basis functions %4i-%-4i\n",shell_types[am],(int) (get_center_ind()+1),(int) get_first_ind()+1,(int) get_last_ind()+1);
505   printf("\t\tCenter of shell is at % 0.4f % 0.4f % 0.4f Å.\n",cen.x/ANGSTROMINBOHR,cen.y/ANGSTROMINBOHR,cen.z/ANGSTROMINBOHR);
506 
507   // Get contraction of normalized primitives
508   std::vector<contr_t> cn(get_contr_normalized());
509 
510   printf("\t\tExponential contraction is\n");
511   printf("\t\t\tzeta\t\tprimitive coeff\ttotal coeff\n");
512   for(size_t i=0;i<c.size();i++)
513     printf("\t\t\t%e\t% e\t% e\n",c[i].z,cn[i].c,c[i].c);
514   if(uselm) {
515     printf("\t\tThe functions on this shell are:\n\t\t\t");
516     for(int m=-am;m<=am;m++)
517       printf(" (%i,%i)",am,m);
518     printf("\n");
519   } else {
520     printf("\t\tThe functions on this shell are:\n\t\t\t");
521     for(size_t i=0;i<cart.size();i++) {
522       printf(" ");
523       if(cart[i].l+cart[i].m+cart[i].n==0)
524 	printf("1");
525       else {
526 	for(int j=0;j<cart[i].l;j++)
527 	  printf("x");
528 	for(int j=0;j<cart[i].m;j++)
529 	  printf("y");
530 	for(int j=0;j<cart[i].n;j++)
531 	  printf("z");
532       }
533     }
534     printf("\n");
535   }
536 
537   /*
538     printf("\t\tThe cartesian functions on this shell are:\n");
539     for(size_t i=0;i<cart.size();i++)
540     printf("\t\t\t%i %i %i\t%0.6f\n",cart[i].l,cart[i].m,cart[i].n,cart[i].relnorm);
541   */
542 }
543 
eval_func(double x,double y,double z) const544 arma::vec GaussianShell::eval_func(double x, double y, double z) const {
545   // Evaluate basis functions at (x,y,z)
546 
547   // Compute coordinates relative to center
548   double xrel=x-cen.x;
549   double yrel=y-cen.y;
550   double zrel=z-cen.z;
551 
552   double rrelsq=xrel*xrel+yrel*yrel+zrel*zrel;
553 
554   // Evaluate exponential factor
555   double expfac=0;
556   for(size_t i=0;i<c.size();i++)
557     expfac+=c[i].c*exp(-c[i].z*rrelsq);
558 
559   // Power arrays, x^l, y^l, z^l
560   double xr[am+1], yr[am+1], zr[am+1];
561 
562   xr[0]=1.0;
563   yr[0]=1.0;
564   zr[0]=1.0;
565 
566   if(am) {
567     xr[1]=xrel;
568     yr[1]=yrel;
569     zr[1]=zrel;
570 
571     for(int i=2;i<=am;i++) {
572       xr[i]=xr[i-1]*xrel;
573       yr[i]=yr[i-1]*yrel;
574       zr[i]=zr[i-1]*zrel;
575     }
576   }
577 
578   // Values of functions
579   arma::vec ret(cart.size());
580 
581   // Loop over functions
582   for(size_t i=0;i<cart.size();i++) {
583     // Value of function at (x,y,z) is
584     ret[i]=cart[i].relnorm*xr[cart[i].l]*yr[cart[i].m]*zr[cart[i].n]*expfac;
585   }
586 
587   if(uselm)
588     // Transform into spherical harmonics
589     return transmat*ret;
590   else
591     return ret;
592 }
593 
eval_grad(double x,double y,double z) const594 arma::mat GaussianShell::eval_grad(double x, double y, double z) const {
595   // Evaluate gradients of functions at (x,y,z)
596 
597   // Compute coordinates relative to center
598   double xrel=x-cen.x;
599   double yrel=y-cen.y;
600   double zrel=z-cen.z;
601 
602   double rrelsq=xrel*xrel+yrel*yrel+zrel*zrel;
603 
604   // Power arrays, x^l, y^l, z^l
605   double xr[am+2], yr[am+2], zr[am+2];
606 
607   xr[0]=1.0;
608   yr[0]=1.0;
609   zr[0]=1.0;
610 
611   xr[1]=xrel;
612   yr[1]=yrel;
613   zr[1]=zrel;
614 
615   for(int i=2;i<=am+1;i++) {
616     xr[i]=xr[i-1]*xrel;
617     yr[i]=yr[i-1]*yrel;
618     zr[i]=zr[i-1]*zrel;
619   }
620 
621   // Gradient array, N_cart x 3
622   arma::mat ret(cart.size(),3);
623   ret.zeros();
624 
625   // Helper variables
626   double expf;
627 
628   // Loop over functions
629   for(size_t icart=0;icart<cart.size();icart++) {
630     // Get types
631     int l=cart[icart].l;
632     int m=cart[icart].m;
633     int n=cart[icart].n;
634 
635     // Loop over exponential contraction
636     for(size_t iexp=0;iexp<c.size();iexp++) {
637       // Contracted exponent
638       expf=c[iexp].c*exp(-c[iexp].z*rrelsq);
639 
640       // x component of gradient:
641       ret(icart,0)+=_der1(xr,l,c[iexp].z)*yr[m]*zr[n]*expf;
642       // y component
643       ret(icart,1)+=xr[l]*_der1(yr,m,c[iexp].z)*zr[n]*expf;
644       // z component
645       ret(icart,2)+=xr[l]*yr[m]*_der1(zr,n,c[iexp].z)*expf;
646     }
647 
648     // Plug in normalization constant
649     ret(icart,0)*=cart[icart].relnorm;
650     ret(icart,1)*=cart[icart].relnorm;
651     ret(icart,2)*=cart[icart].relnorm;
652   }
653 
654   if(uselm)
655     // Need to transform into spherical harmonics
656     return transmat*ret;
657   else
658     return ret;
659 }
660 
eval_lapl(double x,double y,double z) const661 arma::vec GaussianShell::eval_lapl(double x, double y, double z) const {
662   // Evaluate laplacian of basis functions at (x,y,z)
663 
664   // Compute coordinates relative to center
665   double xrel=x-cen.x;
666   double yrel=y-cen.y;
667   double zrel=z-cen.z;
668 
669   double rrelsq=xrel*xrel+yrel*yrel+zrel*zrel;
670 
671   // Power arrays, x^l, y^l, z^l
672   double xr[am+3], yr[am+3], zr[am+3];
673 
674   xr[0]=1.0;
675   yr[0]=1.0;
676   zr[0]=1.0;
677 
678   xr[1]=xrel;
679   yr[1]=yrel;
680   zr[1]=zrel;
681 
682   for(int i=2;i<=am+2;i++) {
683     xr[i]=xr[i-1]*xrel;
684     yr[i]=yr[i-1]*yrel;
685     zr[i]=zr[i-1]*zrel;
686   }
687 
688   // Values of laplacians of functions
689   arma::vec ret(cart.size());
690   ret.zeros();
691 
692   // Helper variables
693   double expf;
694 
695   // Loop over functions
696   for(size_t icart=0;icart<cart.size();icart++) {
697     // Get types
698     int l=cart[icart].l;
699     int m=cart[icart].m;
700     int n=cart[icart].n;
701 
702     // Loop over exponential contraction
703     for(size_t iexp=0;iexp<c.size();iexp++) {
704       // Contracted exponent
705       expf=c[iexp].c*exp(-c[iexp].z*rrelsq);
706 
707       // Derivative wrt x
708       ret(icart)+=_der2(xr,l,c[iexp].z)*yr[m]*zr[n]*expf;
709       // Derivative wrt y
710       ret(icart)+=xr[l]*_der2(yr,m,c[iexp].z)*zr[n]*expf;
711       // Derivative wrt z
712       ret(icart)+=xr[l]*yr[m]*_der2(zr,n,c[iexp].z)*expf;
713     }
714 
715     // Plug in normalization constant
716     ret(icart)*=cart[icart].relnorm;
717   }
718 
719   if(uselm)
720     // Transform into spherical harmonics
721     return transmat*ret;
722   else
723     return ret;
724 }
725 
eval_hess(double x,double y,double z) const726 arma::mat GaussianShell::eval_hess(double x, double y, double z) const {
727   // Evaluate gradients of functions at (x,y,z)
728 
729   // Compute coordinates relative to center
730   double xrel=x-cen.x;
731   double yrel=y-cen.y;
732   double zrel=z-cen.z;
733 
734   double rrelsq=xrel*xrel+yrel*yrel+zrel*zrel;
735 
736   // Power arrays, x^l, y^l, z^l
737   double xr[am+3], yr[am+3], zr[am+3];
738 
739   xr[0]=1.0;
740   yr[0]=1.0;
741   zr[0]=1.0;
742 
743   xr[1]=xrel;
744   yr[1]=yrel;
745   zr[1]=zrel;
746 
747   for(int i=2;i<=am+2;i++) {
748     xr[i]=xr[i-1]*xrel;
749     yr[i]=yr[i-1]*yrel;
750     zr[i]=zr[i-1]*zrel;
751   }
752 
753   // Gradient array, Ncart x 9
754   arma::mat ret(cart.size(),9);
755   ret.zeros();
756 
757   // Helper variables
758   double expf, tmp;
759 
760   // Loop over functions
761   for(size_t icart=0;icart<cart.size();icart++) {
762     // Get types
763     int l=cart[icart].l;
764     int m=cart[icart].m;
765     int n=cart[icart].n;
766 
767     // Loop over exponential contraction
768     for(size_t iexp=0;iexp<c.size();iexp++) {
769       // Contracted exponent
770       expf=c[iexp].c*exp(-c[iexp].z*rrelsq);
771 
772       // d/dx^2
773       ret(icart,0)+=_der2(xr,l,c[iexp].z)*yr[m]*zr[n]*expf;
774       // d/dy^2
775       ret(icart,4)+=xr[l]*_der2(yr,m,c[iexp].z)*zr[n]*expf;
776       // d/dz^2
777       ret(icart,8)+=xr[l]*yr[m]*_der2(zr,n,c[iexp].z)*expf;
778 
779       // dxdy = dydx
780       tmp=_der1(xr,l,c[iexp].z)*_der1(yr,m,c[iexp].z)*zr[n]*expf;
781       ret(icart,1)+=tmp;
782       ret(icart,3)+=tmp;
783 
784       // dxdz = dzdx
785       tmp=_der1(xr,l,c[iexp].z)*yr[m]*_der1(zr,n,c[iexp].z)*expf;
786       ret(icart,2)+=tmp;
787       ret(icart,6)+=tmp;
788 
789       // dydz = dzdy
790       tmp=xr[l]*_der1(yr,m,c[iexp].z)*_der1(zr,n,c[iexp].z)*expf;
791       ret(icart,5)+=tmp;
792       ret(icart,7)+=tmp;
793     }
794 
795     // Plug in normalization constant
796     for(int ii=0;ii<9;ii++)
797       ret(icart,ii)*=cart[icart].relnorm;
798   }
799 
800   if(uselm)
801     return transmat*ret;
802   else
803     return ret;
804 }
805 
eval_laplgrad(double x,double y,double z) const806 arma::mat GaussianShell::eval_laplgrad(double x, double y, double z) const {
807   // Evaluate laplacian of gradients of functions at (x,y,z)
808 
809   // Compute coordinates relative to center
810   double xrel=x-cen.x;
811   double yrel=y-cen.y;
812   double zrel=z-cen.z;
813 
814   double rrelsq=xrel*xrel+yrel*yrel+zrel*zrel;
815 
816   // Power arrays, x^l, y^l, z^l
817   double xr[am+4], yr[am+4], zr[am+4];
818 
819   xr[0]=1.0;
820   yr[0]=1.0;
821   zr[0]=1.0;
822 
823   xr[1]=xrel;
824   yr[1]=yrel;
825   zr[1]=zrel;
826 
827   for(int i=2;i<=am+3;i++) {
828     xr[i]=xr[i-1]*xrel;
829     yr[i]=yr[i-1]*yrel;
830     zr[i]=zr[i-1]*zrel;
831   }
832 
833   // Returned array, N_cart x 3
834   arma::mat ret(cart.size(),3);
835   ret.zeros();
836 
837   // Helper variables
838   double expf;
839 
840   // Loop over functions
841   for(size_t icart=0;icart<cart.size();icart++) {
842     // Get types
843     int l=cart[icart].l;
844     int m=cart[icart].m;
845     int n=cart[icart].n;
846 
847     // Loop over exponential contraction
848     for(size_t iexp=0;iexp<c.size();iexp++) {
849       // Contracted exponent
850       expf=c[iexp].c*exp(-c[iexp].z*rrelsq);
851 
852       // dx^3
853       ret(icart,0)+=_der3(xr,l,c[iexp].z)*yr[m]*zr[n]*expf;
854       // dy^2 dx
855       ret(icart,0)+=_der1(xr,l,c[iexp].z)*_der2(yr,m,c[iexp].z)*zr[n]*expf;
856       // dz^2 dx
857       ret(icart,0)+=_der1(xr,l,c[iexp].z)*yr[m]*_der2(zr,n,c[iexp].z)*expf;
858 
859       // dx^2 dy
860       ret(icart,1)+=_der2(xr,l,c[iexp].z)*_der1(yr,m,c[iexp].z)*zr[n]*expf;
861       // dy^3
862       ret(icart,1)+=xr[l]*_der3(yr,m,c[iexp].z)*zr[n]*expf;
863       // dz^2 dy
864       ret(icart,1)+=xr[l]*_der1(yr,m,c[iexp].z)*_der2(zr,n,c[iexp].z)*expf;
865 
866       // dx^2 dz
867       ret(icart,2)+=_der2(xr,l,c[iexp].z)*yr[m]*_der1(zr,n,c[iexp].z)*expf;
868       // dy^2 dz
869       ret(icart,2)+=xr[l]*_der2(yr,m,c[iexp].z)*_der1(zr,n,c[iexp].z)*expf;
870       // dz^3
871       ret(icart,2)+=xr[l]*yr[m]*_der3(zr,n,c[iexp].z)*expf;
872     }
873 
874     // Plug in normalization constant
875     ret(icart,0)*=cart[icart].relnorm;
876     ret(icart,1)*=cart[icart].relnorm;
877     ret(icart,2)*=cart[icart].relnorm;
878   }
879 
880   if(uselm)
881     // Need to transform into spherical harmonics
882     return transmat*ret;
883   else
884     return ret;
885 }
886 
887 // Calculate overlaps between basis functions
overlap(const GaussianShell & rhs) const888 arma::mat GaussianShell::overlap(const GaussianShell & rhs) const {
889 
890   // Overlap matrix
891   arma::mat S(cart.size(),rhs.cart.size());
892   S.zeros();
893 
894   // Coordinates
895   double xa=cen.x;
896   double ya=cen.y;
897   double za=cen.z;
898 
899   double xb=rhs.cen.x;
900   double yb=rhs.cen.y;
901   double zb=rhs.cen.z;
902 
903   for(size_t ixl=0;ixl<c.size();ixl++)
904     for(size_t ixr=0;ixr<rhs.c.size();ixr++)
905       S+=c[ixl].c*rhs.c[ixr].c*overlap_int_os(xa,ya,za,c[ixl].z,cart,xb,yb,zb,rhs.c[ixr].z,rhs.cart);
906 
907   // Transformation to spherical harmonics. Left side:
908   if(uselm) {
909     S=transmat*S;
910   }
911   // Right side
912   if(rhs.uselm) {
913     S=S*arma::trans(rhs.transmat);
914   }
915 
916   return S;
917 }
918 
919 // Calculate overlaps between basis functions
coulomb_overlap(const GaussianShell & rhs) const920 arma::mat GaussianShell::coulomb_overlap(const GaussianShell & rhs) const {
921 
922   // Number of functions on shells
923   size_t Ni=get_Nbf();
924   size_t Nj=rhs.get_Nbf();
925 
926   // Compute ERI
927   GaussianShell dummy=dummyshell();
928   int maxam=std::max(get_am(),rhs.get_am());
929   int maxcontr=std::max(get_Ncontr(),rhs.get_Ncontr());
930   ERIWorker eri(maxam,maxcontr);
931   const std::vector<double> * erip;
932   eri.compute(this,&dummy,&rhs,&dummy);
933   erip=eri.getp();
934 
935   // Fill overlap matrix
936   arma::mat S(Ni,Nj);
937   for(size_t i=0;i<Ni;i++)
938     for(size_t j=0;j<Nj;j++)
939       S(i,j)=(*erip)[i*Nj+j];
940 
941   return S;
942 }
943 
944 // Calculate kinetic energy matrix element between basis functions
kinetic(const GaussianShell & rhs) const945 arma::mat GaussianShell::kinetic(const GaussianShell & rhs) const {
946 
947   // Kinetic energy matrix
948   arma::mat T(cart.size(),rhs.cart.size());
949   T.zeros();
950 
951   // Coordinates
952   double xa=cen.x;
953   double ya=cen.y;
954   double za=cen.z;
955 
956   double xb=rhs.cen.x;
957   double yb=rhs.cen.y;
958   double zb=rhs.cen.z;
959 
960   for(size_t ixl=0;ixl<c.size();ixl++)
961     for(size_t ixr=0;ixr<rhs.c.size();ixr++)
962       T+=c[ixl].c*rhs.c[ixr].c*kinetic_int_os(xa,ya,za,c[ixl].z,cart,xb,yb,zb,rhs.c[ixr].z,rhs.cart);
963 
964   // Transformation to spherical harmonics. Left side:
965   if(uselm) {
966     T=transmat*T;
967   }
968   // Right side
969   if(rhs.uselm) {
970     T=T*arma::trans(rhs.transmat);
971   }
972 
973   return T;
974 }
975 
976 
977 // Calculate nuclear attraction matrix element between basis functions
nuclear(double cx,double cy,double cz,const GaussianShell & rhs) const978 arma::mat GaussianShell::nuclear(double cx, double cy, double cz, const GaussianShell & rhs) const {
979 
980   // Matrix element of nuclear attraction operator
981   arma::mat Vnuc(cart.size(),rhs.cart.size());
982   Vnuc.zeros();
983 
984   // Coordinates
985   double xa=cen.x;
986   double ya=cen.y;
987   double za=cen.z;
988 
989   double xb=rhs.cen.x;
990   double yb=rhs.cen.y;
991   double zb=rhs.cen.z;
992 
993   for(size_t ixl=0;ixl<c.size();ixl++)
994     for(size_t ixr=0;ixr<rhs.c.size();ixr++)
995       Vnuc+=c[ixl].c*rhs.c[ixr].c*nuclear_int_os(xa,ya,za,c[ixl].z,cart,cx,cy,cz,xb,yb,zb,rhs.c[ixr].z,rhs.cart);
996 
997   // Transformation to spherical harmonics. Left side:
998   if(uselm) {
999     Vnuc=transmat*Vnuc;
1000   }
1001   // Right side
1002   if(rhs.uselm) {
1003     Vnuc=Vnuc*arma::trans(rhs.transmat);
1004   }
1005 
1006   return Vnuc;
1007 }
1008 
nuclear_pulay(double cx,double cy,double cz,const arma::mat & P,const GaussianShell & rhs) const1009 arma::vec GaussianShell::nuclear_pulay(double cx, double cy, double cz, const arma::mat & P, const GaussianShell & rhs) const {
1010   // Coordinates
1011   double xa=cen.x;
1012   double ya=cen.y;
1013   double za=cen.z;
1014 
1015   double xb=rhs.cen.x;
1016   double yb=rhs.cen.y;
1017   double zb=rhs.cen.z;
1018 
1019   // Derivative matrices
1020   std::vector<arma::mat> dermat(6);
1021   for(size_t i=0;i<dermat.size();i++)
1022     dermat[i].zeros(get_Ncart(),rhs.get_Ncart());
1023 
1024   // Increment derivative matrices
1025   for(size_t ixl=0;ixl<c.size();ixl++)
1026     for(size_t ixr=0;ixr<rhs.c.size();ixr++) {
1027       std::vector<arma::mat> hlp=nuclear_int_pulay_os(xa,ya,za,c[ixl].z,cart,cx,cy,cz,xb,yb,zb,rhs.c[ixr].z,rhs.cart);
1028       for(size_t i=0;i<dermat.size();i++)
1029 	dermat[i]+=c[ixl].c*rhs.c[ixr].c*hlp[i];
1030     }
1031 
1032   // Transformation to spherical harmonics. Left side:
1033   if(uselm)
1034     for(size_t i=0;i<dermat.size();i++)
1035       dermat[i]=transmat*dermat[i];
1036   // Right side
1037   if(rhs.uselm)
1038     for(size_t i=0;i<dermat.size();i++)
1039       dermat[i]=dermat[i]*arma::trans(rhs.transmat);
1040 
1041   // Compute the force
1042   arma::vec ret(dermat.size());
1043   ret.zeros();
1044   for(size_t i=0;i<dermat.size();i++) {
1045     ret(i)=arma::trace(arma::trans(P)*dermat[i]);
1046   }
1047 
1048   return ret;
1049 }
1050 
nuclear_der(double cx,double cy,double cz,const arma::mat & P,const GaussianShell & rhs) const1051 arma::vec GaussianShell::nuclear_der(double cx, double cy, double cz, const arma::mat & P, const GaussianShell & rhs) const {
1052   // Coordinates
1053   double xa=cen.x;
1054   double ya=cen.y;
1055   double za=cen.z;
1056 
1057   double xb=rhs.cen.x;
1058   double yb=rhs.cen.y;
1059   double zb=rhs.cen.z;
1060 
1061   // Derivative matrices
1062   std::vector<arma::mat> dermat(3);
1063   for(size_t i=0;i<dermat.size();i++)
1064     dermat[i].zeros(get_Ncart(),rhs.get_Ncart());
1065 
1066   // Increment derivative matrices
1067   for(size_t ixl=0;ixl<c.size();ixl++)
1068     for(size_t ixr=0;ixr<rhs.c.size();ixr++) {
1069       std::vector<arma::mat> hlp=nuclear_int_ders_os(xa,ya,za,c[ixl].z,cart,cx,cy,cz,xb,yb,zb,rhs.c[ixr].z,rhs.cart);
1070       for(size_t i=0;i<dermat.size();i++) {
1071 	dermat[i]+=c[ixl].c*rhs.c[ixr].c*hlp[i];
1072       }
1073     }
1074 
1075   // Transformation to spherical harmonics. Left side:
1076   if(uselm)
1077     for(size_t i=0;i<dermat.size();i++)
1078       dermat[i]=transmat*dermat[i];
1079 
1080   // Right side
1081   if(rhs.uselm)
1082     for(size_t i=0;i<dermat.size();i++)
1083       dermat[i]=dermat[i]*arma::trans(rhs.transmat);
1084 
1085   // Compute the force
1086   arma::vec ret(dermat.size());
1087   ret.zeros();
1088   for(size_t i=0;i<dermat.size();i++) {
1089     ret(i)=arma::trace(arma::trans(P)*dermat[i]);
1090   }
1091 
1092   return ret;
1093 }
1094 
kinetic_pulay(const arma::mat & P,const GaussianShell & rhs) const1095 arma::vec GaussianShell::kinetic_pulay(const arma::mat & P, const GaussianShell & rhs) const {
1096   // Coordinates
1097   double xa=cen.x;
1098   double ya=cen.y;
1099   double za=cen.z;
1100 
1101   double xb=rhs.cen.x;
1102   double yb=rhs.cen.y;
1103   double zb=rhs.cen.z;
1104 
1105   std::vector<arma::mat> dermat(6);
1106   for(size_t i=0;i<dermat.size();i++)
1107     dermat[i].zeros(get_Ncart(),rhs.get_Ncart());
1108 
1109   for(size_t ixl=0;ixl<c.size();ixl++)
1110     for(size_t ixr=0;ixr<rhs.c.size();ixr++) {
1111       std::vector<arma::mat> hlp=kinetic_int_pulay_os(xa,ya,za,c[ixl].z,cart,xb,yb,zb,rhs.c[ixr].z,rhs.cart);
1112       for(size_t i=0;i<dermat.size();i++)
1113 	dermat[i]+=c[ixl].c*rhs.c[ixr].c*hlp[i];
1114     }
1115 
1116   // Transformation to spherical harmonics. Left side:
1117   if(uselm)
1118     for(size_t i=0;i<dermat.size();i++)
1119       dermat[i]=transmat*dermat[i];
1120   // Right side
1121   if(rhs.uselm)
1122     for(size_t i=0;i<dermat.size();i++)
1123       dermat[i]=dermat[i]*arma::trans(rhs.transmat);
1124 
1125   // Compute the force
1126   arma::vec ret(dermat.size());
1127   ret.zeros();
1128   for(size_t i=0;i<dermat.size();i++) {
1129     ret(i)=arma::trace(arma::trans(P)*dermat[i]);
1130   }
1131 
1132   return ret;
1133 }
1134 
overlap_der(const arma::mat & W,const GaussianShell & rhs) const1135 arma::vec GaussianShell::overlap_der(const arma::mat & W, const GaussianShell & rhs) const {
1136   // Coordinates
1137   double xa=cen.x;
1138   double ya=cen.y;
1139   double za=cen.z;
1140 
1141   double xb=rhs.cen.x;
1142   double yb=rhs.cen.y;
1143   double zb=rhs.cen.z;
1144 
1145   std::vector<arma::mat> dermat(6);
1146   for(size_t i=0;i<dermat.size();i++)
1147     dermat[i].zeros(get_Ncart(),rhs.get_Ncart());
1148 
1149   for(size_t ixl=0;ixl<c.size();ixl++)
1150     for(size_t ixr=0;ixr<rhs.c.size();ixr++) {
1151       std::vector<arma::mat> hlp=overlap_int_pulay_os(xa,ya,za,c[ixl].z,cart,xb,yb,zb,rhs.c[ixr].z,rhs.cart);
1152       for(size_t i=0;i<dermat.size();i++)
1153 	dermat[i]+=c[ixl].c*rhs.c[ixr].c*hlp[i];
1154     }
1155 
1156   // Transformation to spherical harmonics. Left side:
1157   if(uselm)
1158     for(size_t i=0;i<dermat.size();i++)
1159       dermat[i]=transmat*dermat[i];
1160   // Right side
1161   if(rhs.uselm)
1162     for(size_t i=0;i<dermat.size();i++)
1163       dermat[i]=dermat[i]*arma::trans(rhs.transmat);
1164 
1165   // Compute the force
1166   arma::vec ret(dermat.size());
1167   ret.zeros();
1168   for(size_t i=0;i<dermat.size();i++) {
1169     ret(i)=-arma::trace(arma::trans(W)*dermat[i]);
1170   }
1171 
1172   return ret;
1173 }
1174 
moment(int momam,double x,double y,double z,const GaussianShell & rhs) const1175 std::vector<arma::mat> GaussianShell::moment(int momam, double x, double y, double z, const GaussianShell & rhs) const {
1176   // Calculate moment integrals around (x,y,z) between shells
1177 
1178   // Amount of moments is
1179   size_t Nmom=(momam+1)*(momam+2)/2;
1180 
1181   // Moments to compute:
1182   std::vector<shellf_t> mom;
1183   mom.reserve(Nmom);
1184   for(int ii=0; ii<=momam; ii++) {
1185     int lc=momam - ii;
1186     for(int jj=0; jj<=ii; jj++) {
1187       int mc=ii - jj;
1188       int nc=jj;
1189 
1190       shellf_t tmp;
1191       tmp.l=lc;
1192       tmp.m=mc;
1193       tmp.n=nc;
1194       tmp.relnorm=1.0;
1195       mom.push_back(tmp);
1196     }
1197   }
1198 
1199   // Temporary array, place moment last so we can use slice()
1200   arma::cube wrk(cart.size(),rhs.cart.size(),Nmom);
1201   wrk.zeros();
1202 
1203   // Coordinates
1204   double xa=cen.x;
1205   double ya=cen.y;
1206   double za=cen.z;
1207   double xb=rhs.cen.x;
1208   double yb=rhs.cen.y;
1209   double zb=rhs.cen.z;
1210 
1211   // Compute moment integrals
1212   for(size_t ixl=0;ixl<c.size();ixl++) {
1213     double ca=c[ixl].c;
1214     double zetaa=c[ixl].z;
1215 
1216     for(size_t ixr=0;ixr<rhs.c.size();ixr++) {
1217       double cb=rhs.c[ixr].c;
1218       double zetab=rhs.c[ixr].z;
1219 
1220       wrk+=ca*cb*three_overlap_int_os(xa,ya,za,xb,yb,zb,x,y,z,zetaa,zetab,0.0,cart,rhs.cart,mom);
1221     }
1222   }
1223 
1224   // Collect the results
1225   std::vector<arma::mat> ret;
1226   ret.reserve(Nmom);
1227   for(size_t m=0;m<Nmom;m++) {
1228     // The matrix for this moment is
1229     arma::mat momval=wrk.slice(m);
1230 
1231     // Convert to spherical basis if necessary
1232     if(uselm) {
1233       momval=transmat*momval;
1234     }
1235     // Right side
1236     if(rhs.uselm) {
1237       momval=momval*arma::trans(rhs.transmat);
1238     }
1239 
1240     // Add it to the stack
1241     ret.push_back(momval);
1242   }
1243 
1244   return ret;
1245 }
1246 
integral() const1247 arma::vec GaussianShell::integral() const {
1248   // Compute integrals over the cartesian functions
1249   arma::vec ints(cart.size());
1250   ints.zeros();
1251 
1252   // Loop over cartesians
1253   for(size_t ic=0;ic<cart.size();ic++) {
1254     int l=cart[ic].l;
1255     int m=cart[ic].m;
1256     int n=cart[ic].n;
1257 
1258     if(l%2 || m%2 || n%2)
1259       // Odd function - zero integral
1260       continue;
1261 
1262     // Loop over exponents
1263     for(size_t ix=0;ix<c.size();ix++) {
1264       double zeta=c[ix].z;
1265 
1266       // Integral over x gives
1267       double intx=2.0*pow(0.5/sqrt(zeta),l+1)*sqrt(M_PI);
1268       // Integral over y
1269       double inty=2.0*pow(0.5/sqrt(zeta),m+1)*sqrt(M_PI);
1270       // Integral over z
1271       double intz=2.0*pow(0.5/sqrt(zeta),n+1)*sqrt(M_PI);
1272 
1273       // Increment total integral
1274       ints(ic)+=c[ix].c*intx*inty*intz;
1275     }
1276 
1277     // Plug in relative norm
1278     ints(ic)*=cart[ic].relnorm;
1279   }
1280 
1281   // Do conversion to spherical basis
1282   if(uselm)
1283     ints=transmat*ints;
1284 
1285   return ints;
1286 }
1287 
1288 
BasisSet()1289 BasisSet::BasisSet() {
1290   // Use spherical harmonics and cartesian functions by default.
1291   uselm=true;
1292   optlm=true;
1293 }
1294 
1295 extern Settings settings;
1296 
BasisSet(size_t Nat)1297 BasisSet::BasisSet(size_t Nat) {
1298   // Use spherical harmonics?
1299   uselm=settings.get_bool("UseLM");
1300   optlm=settings.get_bool("OptLM");
1301 
1302   shells.reserve(Nat);
1303   nuclei.reserve(Nat);
1304 }
1305 
~BasisSet()1306 BasisSet::~BasisSet() {
1307 }
1308 
add_nucleus(const nucleus_t & nuc)1309 void BasisSet::add_nucleus(const nucleus_t & nuc) {
1310   nuclei.push_back(nuc);
1311   // Clear list of functions
1312   nuclei[nuclei.size()-1].shells.clear();
1313   // Set nuclear index
1314   nuclei[nuclei.size()-1].ind=nuclei.size()-1;
1315 }
1316 
add_shell(size_t nucind,const GaussianShell & sh,bool dosort)1317 void BasisSet::add_shell(size_t nucind, const GaussianShell & sh, bool dosort) {
1318   if(nucind>=nuclei.size()) {
1319     ERROR_INFO();
1320     throw std::runtime_error("Cannot add functions to nonexisting nucleus!\n");
1321   }
1322 
1323   // Add shell
1324   shells.push_back(sh);
1325   // Set pointer to nucleus
1326   shells[shells.size()-1].set_center(nuclei[nucind].r,nucind);
1327 
1328   // Sort the basis set, updating the nuclear list and basis function indices as well
1329   if(dosort)
1330     sort();
1331   else {
1332     // Just do the numbering and shell list updates.
1333     check_numbering();
1334     update_nuclear_shell_list();
1335   }
1336 }
1337 
add_shell(size_t nucind,int am,bool lm,const std::vector<contr_t> & C,bool dosort)1338 void BasisSet::add_shell(size_t nucind, int am, bool lm, const std::vector<contr_t> & C, bool dosort) {
1339   // Create new shell.
1340   GaussianShell sh=GaussianShell(am,lm,C);
1341   // Do the rest here
1342   add_shell(nucind,sh,dosort);
1343 }
1344 
add_shells(size_t nucind,ElementBasisSet el,bool dosort)1345 void BasisSet::add_shells(size_t nucind, ElementBasisSet el, bool dosort) {
1346   // Add basis functions at cen
1347 
1348   // Get the shells on the element
1349   std::vector<FunctionShell> bf=el.get_shells();
1350 
1351   // Loop over shells in element basis
1352   for(size_t i=0;i<bf.size();i++) {
1353     // Create shell
1354     GaussianShell sh;
1355     if(!optlm || bf[i].get_am()>=2)
1356       sh=GaussianShell(bf[i].get_am(),uselm,bf[i].get_contr());
1357     else
1358       sh=GaussianShell(bf[i].get_am(),false,bf[i].get_contr());
1359 
1360     // and add it
1361     add_shell(nucind,sh,dosort);
1362   }
1363 }
1364 
check_numbering()1365 void BasisSet::check_numbering() {
1366   // Renumber basis functions
1367   size_t ind=0;
1368   for(size_t i=0;i<shells.size();i++) {
1369     shells[i].set_first_ind(ind);
1370     ind=shells[i].get_last_ind()+1;
1371   }
1372 }
1373 
update_nuclear_shell_list()1374 void BasisSet::update_nuclear_shell_list() {
1375   // First, clear the list on all nuclei.
1376   for(size_t inuc=0;inuc<nuclei.size();inuc++)
1377     nuclei[inuc].shells.clear();
1378 
1379   // Then, update the lists. Loop over shells
1380   for(size_t ish=0;ish<shells.size();ish++) {
1381     // Find out nuclear index
1382     size_t inuc=shells[ish].get_center_ind();
1383     // Add pointer to the nucleus
1384     nuclei[inuc].shells.push_back(&shells[ish]);
1385   }
1386 }
1387 
sort()1388 void BasisSet::sort() {
1389   // Sort the shells first by increasing index of center, then by
1390   // increasing angular momentum and last by decreasing exponent.
1391   std::stable_sort(shells.begin(),shells.end());
1392 
1393   // Check the numbering of the basis functions
1394   check_numbering();
1395 
1396   // and since we probably have changed the order of the basis
1397   // functions, we need to update the list of functions on the nuclei.
1398   update_nuclear_shell_list();
1399 }
1400 
compute_nuclear_distances()1401 void BasisSet::compute_nuclear_distances() {
1402   // Amount of nuclei
1403   size_t N=nuclei.size();
1404 
1405   // Reserve memory
1406   nucleardist=arma::mat(N,N);
1407 
1408   double d;
1409 
1410   // Fill table
1411   for(size_t i=0;i<N;i++)
1412     for(size_t j=0;j<=i;j++) {
1413       d=dist(nuclei[i].r.x,nuclei[i].r.y,nuclei[i].r.z,nuclei[j].r.x,nuclei[j].r.y,nuclei[j].r.z);
1414 
1415       nucleardist(i,j)=d;
1416       nucleardist(j,i)=d;
1417     }
1418 }
1419 
nuclear_distance(size_t i,size_t j) const1420 double BasisSet::nuclear_distance(size_t i, size_t j) const {
1421   return nucleardist(i,j);
1422 }
1423 
nuclear_distances() const1424 arma::mat BasisSet::nuclear_distances() const {
1425   return nucleardist;
1426 }
1427 
operator <(const shellpair_t & lhs,const shellpair_t & rhs)1428 bool operator<(const shellpair_t & lhs, const shellpair_t & rhs) {
1429   // Helper for ordering shellpairs into libint order
1430   return (lhs.li+lhs.lj)<(rhs.li+rhs.lj);
1431 }
1432 
form_unique_shellpairs()1433 void BasisSet::form_unique_shellpairs() {
1434   // Drop list of existing pairs.
1435   shellpairs.clear();
1436 
1437   // Form list of unique shell pairs.
1438   shellpair_t tmp;
1439 
1440   // Now, form list of unique shell pairs
1441   for(size_t i=0;i<shells.size();i++) {
1442     for(size_t j=0;j<=i;j++) {
1443       // Have to set these in every iteration due to swap below
1444       tmp.is=i;
1445       tmp.js=j;
1446 
1447       // Check that libint's angular momentum rules are satisfied
1448       if(shells[j].get_am()>shells[i].get_am())
1449 	std::swap(tmp.is,tmp.js);
1450 
1451       // Set angular momenta
1452       tmp.li=shells[tmp.is].get_am();
1453       tmp.lj=shells[tmp.js].get_am();
1454 
1455       shellpairs.push_back(tmp);
1456     }
1457   }
1458 
1459   // Sort list of unique shell pairs
1460   stable_sort(shellpairs.begin(),shellpairs.end());
1461 
1462   /*
1463   // Print list
1464   printf("\nList of unique shell pairs (%lu pairs):\n",shellpairs.size());
1465   for(size_t ind=0;ind<shellpairs.size();ind++) {
1466   size_t i=shellpairs[ind].is;
1467   size_t j=shellpairs[ind].js;
1468 
1469   int li=shells[i].get_am();
1470   int lj=shells[j].get_am();
1471 
1472   printf("%i\t%i\t%i\t%i\t%i\n",(int) i,(int) j,li,lj,li+lj);
1473   }
1474   */
1475 }
1476 
get_unique_shellpairs() const1477 std::vector<shellpair_t> BasisSet::get_unique_shellpairs() const {
1478   if(shells.size() && !shellpairs.size()) {
1479     throw std::runtime_error("shellpairs not initialized! Maybe you forgot to finalize?\n");
1480   }
1481 
1482   return shellpairs;
1483 }
1484 
get_eripairs(arma::mat & Q,arma::mat & M,double tol,double omega,double alpha,double beta,bool verbose) const1485 std::vector<eripair_t> BasisSet::get_eripairs(arma::mat & Q, arma::mat & M, double tol, double omega, double alpha, double beta, bool verbose) const {
1486   // Get the screening matrices
1487   eri_screening(Q,M,omega,alpha,beta);
1488 
1489   // Fill out list
1490   std::vector<eripair_t> list(shellpairs.size());
1491   for(size_t i=0;i<shellpairs.size();i++) {
1492     list[i].is=shellpairs[i].is;
1493     list[i].i0=shells[shellpairs[i].is].get_first_ind();
1494     list[i].Ni=shells[shellpairs[i].is].get_Nbf();
1495 
1496     list[i].js=shellpairs[i].js;
1497     list[i].j0=shells[shellpairs[i].js].get_first_ind();
1498     list[i].Nj=shells[shellpairs[i].js].get_Nbf();
1499 
1500     list[i].eri=Q(list[i].is,list[i].js);
1501   }
1502   // and sort it
1503   std::stable_sort(list.begin(),list.end());
1504 
1505   // Find out which pairs are negligible.
1506   size_t ulimit=list.size()-1;
1507   // An integral (ij|kl) <= sqrt( (ij|ij) (kl|kl) ). Since the
1508   // integrals are in decreasing order, the integral threshold is
1509   double thr=tol/list[0].eri;
1510   while(list[ulimit].eri < thr)
1511     ulimit--;
1512   if(ulimit<list.size())
1513     list.resize(ulimit+1);
1514 
1515   (void) verbose;
1516   //if(verbose)
1517   // printf("%u shell pairs out of %u are significant.\n",(unsigned int) list.size(),(unsigned int) shellpairs.size());
1518 
1519   /*
1520     FILE *out=fopen("screen.dat","w");
1521     for(size_t i=0;i<list.size();i++)
1522     fprintf(out,"%4i %4i %e\n",(int) list[i].is, (int) list[i].js, list[i].eri);
1523     fclose(out);
1524   */
1525 
1526   return list;
1527 }
1528 
operator <(const eripair_t & lhs,const eripair_t & rhs)1529 bool operator<(const eripair_t & lhs, const eripair_t & rhs) {
1530   // Sort in decreasing order!
1531   return lhs.eri > rhs.eri;
1532 }
1533 
1534 
finalize(bool convert,bool donorm)1535 void BasisSet::finalize(bool convert, bool donorm) {
1536   // Finalize basis set structure for use.
1537 
1538   // Compute nuclear distances.
1539   compute_nuclear_distances();
1540 
1541   // Compute ranges of shells
1542   compute_shell_ranges();
1543 
1544   // Convert contractions
1545   if(convert)
1546     convert_contractions();
1547   // Normalize contractions if requested, and compute cartesian norms
1548   normalize(donorm);
1549 
1550   // Form list of unique shell pairs
1551   form_unique_shellpairs();
1552   // and update the nuclear shell list (in case the basis set was
1553   // loaded from checkpoint)
1554   update_nuclear_shell_list();
1555 }
1556 
get_am(size_t ind) const1557 int BasisSet::get_am(size_t ind) const {
1558   return shells[ind].get_am();
1559 }
1560 
get_max_am() const1561 int BasisSet::get_max_am() const {
1562   if(shells.size()==0) {
1563     ERROR_INFO();
1564     throw std::domain_error("Cannot get maximum angular momentum of an empty basis set!\n");
1565   }
1566 
1567   int maxam=shells[0].get_am();
1568   for(size_t i=1;i<shells.size();i++)
1569     if(shells[i].get_am()>maxam)
1570       maxam=shells[i].get_am();
1571   return maxam;
1572 }
1573 
get_max_Ncontr() const1574 size_t BasisSet::get_max_Ncontr() const {
1575   size_t maxc=shells[0].get_Ncontr();
1576   for(size_t i=1;i<shells.size();i++)
1577     if(shells[i].get_Ncontr()>maxc)
1578       maxc=shells[i].get_Ncontr();
1579   return maxc;
1580 }
1581 
get_Nbf() const1582 size_t BasisSet::get_Nbf() const {
1583   if(shells.size())
1584     return shells[shells.size()-1].get_last_ind()+1;
1585   else
1586     return 0;
1587 }
1588 
compute_shell_ranges(double eps)1589 void BasisSet::compute_shell_ranges(double eps) {
1590   shell_ranges=get_shell_ranges(eps);
1591 }
1592 
get_shell_ranges() const1593 std::vector<double> BasisSet::get_shell_ranges() const {
1594   return shell_ranges;
1595 }
1596 
get_shell_ranges(double eps) const1597 std::vector<double> BasisSet::get_shell_ranges(double eps) const {
1598   std::vector<double> shran(shells.size());
1599 #ifdef _OPENMP
1600 #pragma omp parallel for
1601 #endif
1602   for(size_t i=0;i<shells.size();i++)
1603     shran[i]=shells[i].range(eps);
1604 
1605   return shran;
1606 }
1607 
get_nuclear_distances(size_t inuc) const1608 std::vector<double> BasisSet::get_nuclear_distances(size_t inuc) const {
1609   std::vector<double> d(nucleardist.n_cols);
1610   for(size_t i=0;i<nucleardist.n_cols;i++)
1611     d[i]=nucleardist(inuc,i);
1612   return d;
1613 }
1614 
get_Ncart() const1615 size_t BasisSet::get_Ncart() const {
1616   size_t n=0;
1617   for(size_t i=0;i<shells.size();i++)
1618     n+=shells[i].get_Ncart();
1619   return n;
1620 }
1621 
get_Nlm() const1622 size_t BasisSet::get_Nlm() const {
1623   size_t n=0;
1624   for(size_t i=0;i<shells.size();i++)
1625     n+=shells[i].get_Nlm();
1626   return n;
1627 }
1628 
get_Nbf(size_t ind) const1629 size_t BasisSet::get_Nbf(size_t ind) const {
1630   return shells[ind].get_Nbf();
1631 }
1632 
get_Ncart(size_t ind) const1633 size_t BasisSet::get_Ncart(size_t ind) const {
1634   return shells[ind].get_Ncart();
1635 }
1636 
get_last_ind() const1637 size_t BasisSet::get_last_ind() const {
1638   if(shells.size())
1639     return shells[shells.size()-1].get_last_ind();
1640   else {
1641     std::ostringstream oss;
1642     oss << "\nError in function " << __FUNCTION__ << "(file " << __FILE__ << ", near line " << __LINE__ << "\nCannot get number of last basis function of an empty basis set!\n";
1643     throw std::domain_error(oss.str());
1644   }
1645 }
1646 
get_first_ind(size_t num) const1647 size_t BasisSet::get_first_ind(size_t num) const {
1648   return shells[num].get_first_ind();
1649 }
1650 
get_last_ind(size_t num) const1651 size_t BasisSet::get_last_ind(size_t num) const {
1652   return shells[num].get_last_ind();
1653 }
1654 
get_bf_Rsquared() const1655 arma::vec BasisSet::get_bf_Rsquared() const {
1656   arma::vec Rsq(get_Nbf());
1657   for(size_t i=0;i<shells.size();i++) {
1658     // First function on shell
1659     size_t i0=shells[i].get_first_ind();
1660     // Number of functions
1661     size_t nbf=shells[i].get_Nbf();
1662 
1663     // Get coordinates of shell center
1664     coords_t cen=shells[i].get_center();
1665     // Calculate moment integrals
1666     std::vector<arma::mat> mom2=shells[i].moment(2, cen.x, cen.y, cen.z, shells[i]);
1667     // Compute spatial extents
1668     for(size_t fi=0;fi<nbf;fi++)
1669       Rsq(i0+fi)=mom2[getind(2,0,0)](fi,fi)+mom2[getind(0,2,0)](fi,fi)+mom2[getind(0,0,2)](fi,fi);
1670   }
1671 
1672   return Rsq;
1673 }
1674 
shell_indices() const1675 arma::uvec BasisSet::shell_indices() const {
1676   arma::uvec idx(get_Nbf());
1677   for(size_t i=0;i<shells.size();i++)
1678     idx.subvec(shells[i].get_first_ind(),shells[i].get_last_ind())=i*arma::ones<arma::uvec>(shells[i].get_Nbf());
1679   return idx;
1680 }
1681 
find_shell_ind(size_t find) const1682 size_t BasisSet::find_shell_ind(size_t find) const {
1683   // Find shell the function belongs to
1684   for(size_t i=0;i<shells.size();i++)
1685     if(find>=shells[i].get_first_ind() && find<=shells[i].get_last_ind())
1686       return i;
1687 
1688   std::ostringstream oss;
1689   oss << "Basis function " << find << " not found in basis set!\n";
1690   throw std::runtime_error(oss.str());
1691 }
1692 
get_shell_center_ind(size_t num) const1693 size_t BasisSet::get_shell_center_ind(size_t num) const {
1694   return shells[num].get_center_ind();
1695 }
1696 
get_shells() const1697 std::vector<GaussianShell> BasisSet::get_shells() const {
1698   return shells;
1699 }
1700 
get_shell(size_t ind) const1701 GaussianShell BasisSet::get_shell(size_t ind) const {
1702   return shells[ind];
1703 }
1704 
get_shell_center(size_t num) const1705 coords_t BasisSet::get_shell_center(size_t num) const {
1706   return shells[num].get_center();
1707 }
1708 
get_contr(size_t ind) const1709 std::vector<contr_t> BasisSet::get_contr(size_t ind) const {
1710   return shells[ind].get_contr();
1711 }
1712 
get_contr_normalized(size_t ind) const1713 std::vector<contr_t> BasisSet::get_contr_normalized(size_t ind) const {
1714   return shells[ind].get_contr_normalized();
1715 }
1716 
get_cart(size_t ind) const1717 std::vector<shellf_t> BasisSet::get_cart(size_t ind) const {
1718   return shells[ind].get_cart();
1719 }
1720 
1721 
is_lm_default() const1722 bool BasisSet::is_lm_default() const {
1723   return uselm;
1724 }
1725 
lm_in_use(size_t num) const1726 bool BasisSet::lm_in_use(size_t num) const {
1727   return shells[num].lm_in_use();
1728 }
1729 
set_lm(size_t num,bool lm)1730 void BasisSet::set_lm(size_t num, bool lm) {
1731   // Set use of spherical harmonics
1732   shells[num].set_lm(lm);
1733   // Check numbering of basis functions which may have changed
1734   check_numbering();
1735 }
1736 
get_m_values() const1737 arma::ivec BasisSet::get_m_values() const {
1738   arma::ivec ret(get_Nbf());
1739   for(size_t is=0;is<get_Nshells();is++) {
1740     // Angular momentum is
1741     int am(get_am(is));
1742 
1743     // First function on shell
1744     size_t i0(get_first_ind(is));
1745 
1746     // Functions are -m, -m+1, ..., m-1, m
1747     if(lm_in_use(is)) {
1748       ret.subvec(i0,i0+2*am)=arma::linspace<arma::ivec>(-am,am,2*am+1);
1749     } else {
1750       if(am==0)
1751         ret(i0)=0;
1752       else if(am==1) {
1753         // Functions are in order x, y, z i.e. 1, -1, 0
1754         ret(i0)=1;
1755         ret(i0+1)=-1;
1756         ret(i0+2)=0;
1757       } else
1758         throw std::logic_error("Need to use spherical basis for linear symmetry!\n");
1759     }
1760   }
1761 
1762   return ret;
1763 }
1764 
unique_m_values() const1765 arma::ivec BasisSet::unique_m_values() const {
1766   // Find unique m values
1767   arma::ivec mval(get_m_values());
1768   arma::uvec muni_idx(arma::find_unique(mval));
1769   arma::ivec muni(mval(muni_idx));
1770   return arma::sort(muni);
1771 }
1772 
unique_m_map() const1773 std::map<int, arma::uword> BasisSet::unique_m_map() const {
1774   arma::ivec muni(unique_m_values());
1775   std::map<int, arma::uword> mlook;
1776   for(arma::uword i=0;i<muni.size();i++)
1777     mlook[muni(i)]=i;
1778   return mlook;
1779 }
1780 
count_m_occupied(const arma::mat & C) const1781 arma::imat BasisSet::count_m_occupied(const arma::mat & C) const {
1782   arma::ivec mc(m_classify(C,get_m_values()));
1783 
1784   std::map<int, arma::uword> mlook(unique_m_map());
1785 
1786   arma::imat occ;
1787   occ.zeros(mlook.size(),3);
1788   for(size_t i=0;i<C.n_cols;i++)
1789     occ(mlook[mc(i)],0)++;
1790   occ.col(1)=occ.col(0);
1791   occ.col(2)=unique_m_values();
1792   return occ;
1793 }
1794 
count_m_occupied(const arma::mat & Ca,const arma::mat & Cb) const1795 arma::imat BasisSet::count_m_occupied(const arma::mat & Ca, const arma::mat & Cb) const {
1796   arma::ivec mca(m_classify(Ca,get_m_values()));
1797   arma::ivec mcb(m_classify(Cb,get_m_values()));
1798 
1799   std::map<int, arma::uword> mlook(unique_m_map());
1800 
1801   arma::imat occ;
1802   occ.zeros(mlook.size(),3);
1803   for(size_t i=0;i<Ca.n_cols;i++)
1804     occ(mlook[mca(i)],0)++;
1805   for(size_t i=0;i<Cb.n_cols;i++)
1806     occ(mlook[mcb(i)],1)++;
1807   occ.col(2)=unique_m_values();
1808   return occ;
1809 }
1810 
m_indices(int mwant) const1811 arma::uvec BasisSet::m_indices(int mwant) const {
1812   arma::ivec midx(get_m_values());
1813   return arma::find(midx==mwant);
1814 }
1815 
get_trans(size_t ind) const1816 arma::mat BasisSet::get_trans(size_t ind) const {
1817   return shells[ind].get_trans();
1818 }
1819 
get_Nshells() const1820 size_t BasisSet::get_Nshells() const {
1821   return shells.size();
1822 }
1823 
get_Nnuc() const1824 size_t BasisSet::get_Nnuc() const {
1825   return nuclei.size();
1826 }
1827 
get_nucleus(size_t inuc) const1828 nucleus_t BasisSet::get_nucleus(size_t inuc) const {
1829   return nuclei[inuc];
1830 }
1831 
get_nuclei() const1832 std::vector<nucleus_t> BasisSet::get_nuclei() const {
1833   return nuclei;
1834 }
1835 
get_nuclear_coords() const1836 arma::mat BasisSet::get_nuclear_coords() const {
1837   arma::mat coords(nuclei.size(),3);
1838   for(size_t i=0;i<nuclei.size();i++)
1839     coords.row(i)=arma::trans(coords_to_vec(nuclei[i].r));
1840 
1841   return coords;
1842 }
1843 
set_nuclear_coords(const arma::mat & c)1844 void BasisSet::set_nuclear_coords(const arma::mat & c) {
1845   if(c.n_rows != nuclei.size() || c.n_cols != 3)
1846     throw std::logic_error("Coordinates matrix does not match nuclei!\n");
1847 
1848   for(size_t i=0;i<nuclei.size();i++)
1849     nuclei[i].r=vec_to_coords(arma::trans(c.row(i)));
1850 
1851   // Update shell centers
1852   for(size_t i=0;i<shells.size();i++) {
1853     size_t icen=shells[i].get_center_ind();
1854     shells[i].set_center(nuclei[icen].r,icen);
1855   }
1856   // Update listings
1857   finalize(false,false);
1858 }
1859 
get_nuclear_coords(size_t inuc) const1860 coords_t BasisSet::get_nuclear_coords(size_t inuc) const {
1861   return nuclei[inuc].r;
1862 }
1863 
get_Z(size_t inuc) const1864 int BasisSet::get_Z(size_t inuc) const {
1865   return nuclei[inuc].Z;
1866 }
1867 
get_symbol(size_t inuc) const1868 std::string BasisSet::get_symbol(size_t inuc) const {
1869   return nuclei[inuc].symbol;
1870 }
1871 
get_symbol_hr(size_t inuc) const1872 std::string BasisSet::get_symbol_hr(size_t inuc) const {
1873   if(nuclei[inuc].bsse)
1874     return nuclei[inuc].symbol+"-Bq";
1875   else
1876     return nuclei[inuc].symbol;
1877 }
1878 
get_funcs(size_t inuc) const1879 std::vector<GaussianShell> BasisSet::get_funcs(size_t inuc) const {
1880   std::vector<GaussianShell> ret;
1881   for(size_t i=0;i<nuclei[inuc].shells.size();i++)
1882     ret.push_back(*(nuclei[inuc].shells[i]));
1883 
1884   return ret;
1885 }
1886 
get_shell_inds(size_t inuc) const1887 std::vector<size_t> BasisSet::get_shell_inds(size_t inuc) const {
1888   std::vector<size_t> ret;
1889   for(size_t i=0;i<shells.size();i++)
1890     if(shells[i].get_center_ind()==inuc)
1891       ret.push_back(i);
1892   return ret;
1893 }
1894 
eval_func(double x,double y,double z) const1895 arma::vec BasisSet::eval_func(double x, double y, double z) const {
1896   // Helper
1897   coords_t r;
1898   r.x=x;
1899   r.y=y;
1900   r.z=z;
1901 
1902   // Determine which shells might contribute
1903   std::vector<size_t> compute_shells;
1904   for(size_t inuc=0;inuc<nuclei.size();inuc++) {
1905     // Determine distance to nucleus
1906     double dist=norm(r-nuclei[inuc].r);
1907     // Get indices of shells centered on nucleus
1908     std::vector<size_t> shellinds=get_shell_inds(inuc);
1909 
1910     // Loop over shells on nucleus
1911     for(size_t ish=0;ish<shellinds.size();ish++)
1912       // Shell is relevant if range is larger than distance
1913       if(dist < shell_ranges[shellinds[ish]])
1914 	compute_shells.push_back(shellinds[ish]);
1915   }
1916 
1917   // Returned values
1918   arma::vec ret(get_Nbf());
1919   ret.zeros();
1920 #ifdef _OPENMP
1921 #pragma omp parallel for
1922 #endif
1923   for(size_t i=0;i<compute_shells.size();i++) {
1924     size_t ish=compute_shells[i];
1925 
1926     // Evalute shell. Function values
1927     arma::vec shf=shells[ish].eval_func(x,y,z);
1928     // First function on shell
1929     size_t f0=shells[ish].get_first_ind();
1930 
1931     // and store the functions
1932     for(size_t fi=0;fi<shells[ish].get_Nbf();fi++) {
1933       ret(f0+fi)=shf(fi);
1934     }
1935   }
1936 
1937   return ret;
1938 }
1939 
eval_grad(double x,double y,double z) const1940 arma::mat BasisSet::eval_grad(double x, double y, double z) const {
1941   // Helper
1942   coords_t r;
1943   r.x=x;
1944   r.y=y;
1945   r.z=z;
1946 
1947   // Determine which shells might contribute
1948   std::vector<size_t> compute_shells;
1949   for(size_t inuc=0;inuc<nuclei.size();inuc++) {
1950     // Determine distance to nucleus
1951     double dist=norm(r-nuclei[inuc].r);
1952     // Get indices of shells centered on nucleus
1953     std::vector<size_t> shellinds=get_shell_inds(inuc);
1954 
1955     // Loop over shells on nucleus
1956     for(size_t ish=0;ish<shellinds.size();ish++)
1957       // Shell is relevant if range is larger than distance
1958       if(dist < shell_ranges[shellinds[ish]])
1959 	compute_shells.push_back(shellinds[ish]);
1960   }
1961 
1962   // Returned values
1963   arma::mat ret(get_Nbf(),3);
1964   ret.zeros();
1965 #ifdef _OPENMP
1966 #pragma omp parallel for
1967 #endif
1968   for(size_t i=0;i<compute_shells.size();i++) {
1969     size_t ish=compute_shells[i];
1970 
1971     // Evalute shell. Gradient values
1972     arma::mat gf=shells[ish].eval_grad(x,y,z);
1973     // First function on shell
1974     size_t f0=shells[ish].get_first_ind();
1975 
1976     // and store the functions
1977     for(size_t fi=0;fi<shells[ish].get_Nbf();fi++) {
1978       ret.row(f0+fi)=gf.row(fi);
1979     }
1980   }
1981 
1982   return ret;
1983 }
1984 
eval_hess(double x,double y,double z) const1985 arma::mat BasisSet::eval_hess(double x, double y, double z) const {
1986   // Helper
1987   coords_t r;
1988   r.x=x;
1989   r.y=y;
1990   r.z=z;
1991 
1992   // Determine which shells might contribute
1993   std::vector<size_t> compute_shells;
1994   for(size_t inuc=0;inuc<nuclei.size();inuc++) {
1995     // Determine distance to nucleus
1996     double dist=norm(r-nuclei[inuc].r);
1997     // Get indices of shells centered on nucleus
1998     std::vector<size_t> shellinds=get_shell_inds(inuc);
1999 
2000     // Loop over shells on nucleus
2001     for(size_t ish=0;ish<shellinds.size();ish++)
2002       // Shell is relevant if range is larger than distance
2003       if(dist < shell_ranges[shellinds[ish]])
2004 	compute_shells.push_back(shellinds[ish]);
2005   }
2006 
2007   // Returned values
2008   arma::mat ret(get_Nbf(),9);
2009   ret.zeros();
2010 #ifdef _OPENMP
2011 #pragma omp parallel for
2012 #endif
2013   for(size_t i=0;i<compute_shells.size();i++) {
2014     size_t ish=compute_shells[i];
2015 
2016     // Evalute shell. Gradient values
2017     arma::mat gf=shells[ish].eval_hess(x,y,z);
2018     // First function on shell
2019     size_t f0=shells[ish].get_first_ind();
2020 
2021     // and store the functions
2022     for(size_t fi=0;fi<shells[ish].get_Nbf();fi++) {
2023       ret.row(f0+fi)=gf.row(fi);
2024     }
2025   }
2026 
2027   return ret;
2028 }
2029 
eval_func(size_t ish,double x,double y,double z) const2030 arma::vec BasisSet::eval_func(size_t ish, double x, double y, double z) const {
2031   return shells[ish].eval_func(x,y,z);
2032 }
2033 
eval_grad(size_t ish,double x,double y,double z) const2034 arma::mat BasisSet::eval_grad(size_t ish, double x, double y, double z) const {
2035   return shells[ish].eval_grad(x,y,z);
2036 }
2037 
eval_lapl(size_t ish,double x,double y,double z) const2038 arma::vec BasisSet::eval_lapl(size_t ish, double x, double y, double z) const {
2039   return shells[ish].eval_lapl(x,y,z);
2040 }
2041 
eval_hess(size_t ish,double x,double y,double z) const2042 arma::mat BasisSet::eval_hess(size_t ish, double x, double y, double z) const {
2043   return shells[ish].eval_hess(x,y,z);
2044 }
2045 
eval_laplgrad(size_t ish,double x,double y,double z) const2046 arma::mat BasisSet::eval_laplgrad(size_t ish, double x, double y, double z) const {
2047   return shells[ish].eval_laplgrad(x,y,z);
2048 }
2049 
convert_contractions()2050 void BasisSet::convert_contractions() {
2051   for(size_t i=0;i<shells.size();i++)
2052     shells[i].convert_contraction();
2053 }
2054 
convert_contraction(size_t ind)2055 void BasisSet::convert_contraction(size_t ind) {
2056   shells[ind].convert_contraction();
2057 }
2058 
normalize(bool coeffs)2059 void BasisSet::normalize(bool coeffs) {
2060   for(size_t i=0;i<shells.size();i++)
2061     shells[i].normalize(coeffs);
2062 }
2063 
coulomb_normalize()2064 void BasisSet::coulomb_normalize() {
2065   for(size_t i=0;i<shells.size();i++)
2066     shells[i].coulomb_normalize();
2067 }
2068 
print(bool verbose) const2069 void BasisSet::print(bool verbose) const {
2070   printf("There are %i shells and %i nuclei in the basis set.\n\n",(int) shells.size(),(int) nuclei.size());
2071 
2072   printf("List of nuclei, geometry in Ångström with three decimal places:\n");
2073 
2074   printf("\t\t Z\t    x\t    y\t    z\n");
2075   for(size_t i=0;i<nuclei.size();i++) {
2076     if(nuclei[i].bsse)
2077       printf("%i\t%s\t*%i\t% 7.3f\t% 7.3f\t% 7.3f\n",(int) i+1,nuclei[i].symbol.c_str(),nuclei[i].Z,nuclei[i].r.x/ANGSTROMINBOHR,nuclei[i].r.y/ANGSTROMINBOHR,nuclei[i].r.z/ANGSTROMINBOHR);
2078     else
2079       printf("%i\t%s\t %i\t% 7.3f\t% 7.3f\t% 7.3f\n",(int) i+1,nuclei[i].symbol.c_str(),nuclei[i].Z,nuclei[i].r.x/ANGSTROMINBOHR,nuclei[i].r.y/ANGSTROMINBOHR,nuclei[i].r.z/ANGSTROMINBOHR);
2080   }
2081 
2082   if(nuclei.size()>1 && nuclei.size()<=13) {
2083     // Legend length is 7 + 6*(N-1) chars
2084 
2085     // Print legend
2086     printf("\nInteratomic distance matrix:\n%7s","");
2087     for(size_t i=0;i<nuclei.size()-1;i++)
2088       printf(" %3i%-2s",(int) i+1,nuclei[i].symbol.c_str());
2089     printf("\n");
2090 
2091     // Print atomic entries
2092     for(size_t i=1;i<nuclei.size();i++) {
2093       printf(" %3i%-2s",(int) i+1,nuclei[i].symbol.c_str());
2094       for(size_t j=0;j<i;j++)
2095 	printf(" %5.3f",norm(nuclei[i].r-nuclei[j].r)/ANGSTROMINBOHR);
2096       printf("\n");
2097     }
2098   }
2099 
2100   printf("\nList of basis functions:\n");
2101 
2102   if(verbose) {
2103     for(size_t i=0;i<shells.size();i++) {
2104       printf("Shell %4i",(int) i);
2105       shells[i].print();
2106     }
2107   } else {
2108     for(size_t i=0;i<shells.size();i++) {
2109       // Type of shell - spherical harmonics or cartesians
2110       std::string type;
2111       if(shells[i].lm_in_use())
2112 	type="sph";
2113       else
2114 	type="cart";
2115 
2116 
2117       printf("Shell %4i",(int) i+1);
2118       printf("\t%c %4s shell at nucleus %3i with with basis functions %4i-%-4i\n",shell_types[shells[i].get_am()],type.c_str(),(int) (shells[i].get_center_ind()+1),(int) shells[i].get_first_ind()+1,(int) shells[i].get_last_ind()+1);
2119     }
2120   }
2121 
2122 
2123   printf("\nBasis set contains %i functions, maximum angular momentum is %i.\\
2124 n",(int) get_Nbf(),get_max_am());
2125   if(is_lm_default())
2126     printf("Spherical harmonic Gaussians are used by default, there are %i cartesians.\n",(int) get_Ncart());
2127   else
2128     printf("Cartesian Gaussians are used by default.\n");
2129 }
2130 
cart_to_sph_trans() const2131 arma::mat BasisSet::cart_to_sph_trans() const {
2132   // Form transformation matrix to spherical harmonics
2133 
2134   const size_t Nlm=get_Nlm();
2135   const size_t Ncart=get_Ncart();
2136 
2137   // Returned matrix
2138   arma::mat trans(Nlm,Ncart);
2139   trans.zeros();
2140 
2141   // Bookkeeping indices
2142   size_t n=0, l=0;
2143 
2144   // Helper matrix
2145   arma::mat tmp;
2146 
2147   for(size_t i=0;i<shells.size();i++) {
2148     // Get angular momentum of shell
2149     int am=shells[i].get_am();
2150 
2151     // Number of cartesians and harmonics on shell
2152     int Nc=(am+1)*(am+2)/2;
2153     int Nl=2*am+1;
2154 
2155     // Get transformation matrix
2156     tmp=Ylm_transmat(am);
2157 
2158     // Store transformation matrix
2159     trans.submat(l,n,l+Nl-1,n+Nc-1)=tmp;
2160     n+=Nc;
2161     l+=Nl;
2162   }
2163 
2164   return trans;
2165 }
2166 
sph_to_cart_trans() const2167 arma::mat BasisSet::sph_to_cart_trans() const {
2168   // Form transformation matrix to cartesians
2169 
2170   return inv(cart_to_sph_trans());
2171 }
2172 
2173 
overlap() const2174 arma::mat BasisSet::overlap() const {
2175   // Form overlap matrix
2176 
2177   // Size of basis set
2178   const size_t N=get_Nbf();
2179 
2180   // Initialize matrix
2181   arma::mat S(N,N);
2182   S.zeros();
2183 
2184   // Loop over shells
2185 #ifdef _OPENMP
2186 #pragma omp parallel for schedule(dynamic)
2187 #endif
2188   for(size_t ip=0;ip<shellpairs.size();ip++) {
2189     // Shells in pair
2190     size_t i=shellpairs[ip].is;
2191     size_t j=shellpairs[ip].js;
2192 
2193     // Get overlap between shells
2194     arma::mat tmp=shells[i].overlap(shells[j]);
2195 
2196     // Store overlap
2197     S.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind())=tmp;
2198     S.submat(shells[j].get_first_ind(),shells[i].get_first_ind(),shells[j].get_last_ind(),shells[i].get_last_ind())=arma::trans(tmp);
2199   }
2200 
2201   return S;
2202 }
2203 
coulomb_overlap() const2204 arma::mat BasisSet::coulomb_overlap() const {
2205   // Form overlap matrix
2206 
2207   // Size of basis set
2208   const size_t N=get_Nbf();
2209 
2210   // Initialize matrix
2211   arma::mat S(N,N);
2212   S.zeros();
2213 
2214   // Loop over shells
2215 #ifdef _OPENMP
2216 #pragma omp parallel for schedule(dynamic)
2217 #endif
2218   for(size_t ip=0;ip<shellpairs.size();ip++) {
2219     // Shells in pair
2220     size_t i=shellpairs[ip].is;
2221     size_t j=shellpairs[ip].js;
2222 
2223     arma::mat tmp=shells[i].coulomb_overlap(shells[j]);
2224 
2225     // Store overlap
2226     S.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind())=tmp;
2227     S.submat(shells[j].get_first_ind(),shells[i].get_first_ind(),shells[j].get_last_ind(),shells[i].get_last_ind())=arma::trans(tmp);
2228   }
2229 
2230   return S;
2231 }
2232 
overlap(const BasisSet & rhs) const2233 arma::mat BasisSet::overlap(const BasisSet & rhs) const {
2234   // Form overlap wrt to other basis set
2235 
2236   // Size of this basis set
2237   const size_t Nl=get_Nbf();
2238   // Size of rhs basis
2239   const size_t Nr=rhs.get_Nbf();
2240 
2241   // Initialize matrix
2242   arma::mat S12(Nl,Nr);
2243   S12.zeros();
2244 
2245   // Loop over shells
2246 #ifdef _OPENMP
2247 #pragma omp parallel for schedule(dynamic)
2248 #endif
2249   for(size_t i=0;i<shells.size();i++) {
2250     for(size_t j=0;j<rhs.shells.size();j++) {
2251       S12.submat(shells[i].get_first_ind(),rhs.shells[j].get_first_ind(),
2252 		 shells[i].get_last_ind() ,rhs.shells[j].get_last_ind() )=shells[i].overlap(rhs.shells[j]);;
2253     }
2254   }
2255   return S12;
2256 }
2257 
coulomb_overlap(const BasisSet & rhs) const2258 arma::mat BasisSet::coulomb_overlap(const BasisSet & rhs) const {
2259   // Form overlap wrt to other basis set
2260 
2261   // Size of this basis set
2262   const size_t Nl=get_Nbf();
2263   // Size of rhs basis
2264   const size_t Nr=rhs.get_Nbf();
2265 
2266   // Initialize matrix
2267   arma::mat S12(Nl,Nr);
2268   S12.zeros();
2269 
2270   // Loop over shells
2271 #ifdef _OPENMP
2272 #pragma omp parallel for schedule(dynamic)
2273 #endif
2274   for(size_t i=0;i<shells.size();i++) {
2275     for(size_t j=0;j<rhs.shells.size();j++) {
2276       S12.submat(shells[i].get_first_ind(),rhs.shells[j].get_first_ind(),
2277 		 shells[i].get_last_ind() ,rhs.shells[j].get_last_ind() )=shells[i].coulomb_overlap(rhs.shells[j]);;
2278     }
2279   }
2280   return S12;
2281 }
2282 
2283 
kinetic() const2284 arma::mat BasisSet::kinetic() const {
2285   // Form kinetic energy matrix
2286 
2287   // Size of basis set
2288   size_t N=get_Nbf();
2289 
2290   // Initialize matrix
2291   arma::mat T(N,N);
2292   T.zeros();
2293 
2294   // Loop over shells
2295 #ifdef _OPENMP
2296 #pragma omp parallel for schedule(dynamic)
2297 #endif
2298   for(size_t ip=0;ip<shellpairs.size();ip++) {
2299     // Shells in pair
2300     size_t i=shellpairs[ip].is;
2301     size_t j=shellpairs[ip].js;
2302 
2303     // Get partial kinetic energy matrix
2304     arma::mat tmp=shells[i].kinetic(shells[j]);
2305 
2306     // Store result
2307     T.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind())=tmp;
2308     T.submat(shells[j].get_first_ind(),shells[i].get_first_ind(),shells[j].get_last_ind(),shells[i].get_last_ind())=arma::trans(tmp);
2309   }
2310 
2311   return T;
2312 }
2313 
nuclear() const2314 arma::mat BasisSet::nuclear() const {
2315   // Form nuclear attraction matrix
2316 
2317   // Size of basis set
2318   size_t N=get_Nbf();
2319 
2320   // Initialize matrix
2321   arma::mat Vnuc(N,N);
2322   Vnuc.zeros();
2323 
2324   // Loop over shells
2325 #ifdef _OPENMP
2326 #pragma omp parallel for schedule(dynamic)
2327 #endif
2328   for(size_t ip=0;ip<shellpairs.size();ip++)
2329     for(size_t inuc=0;inuc<nuclei.size();inuc++) {
2330       // If BSSE nucleus, do nothing
2331       if(nuclei[inuc].bsse)
2332 	continue;
2333 
2334       // Nuclear charge
2335       int Z=nuclei[inuc].Z;
2336 
2337       // Coordinates of nucleus
2338       double cx=nuclei[inuc].r.x;
2339       double cy=nuclei[inuc].r.y;
2340       double cz=nuclei[inuc].r.z;
2341 
2342       // Shells in pair
2343       size_t i=shellpairs[ip].is;
2344       size_t j=shellpairs[ip].js;
2345 
2346       // Get subblock
2347       arma::mat tmp=Z*shells[i].nuclear(cx,cy,cz,shells[j]);
2348 
2349       // On the off diagonal we fill out both sides of the matrix
2350       if(i!=j) {
2351 	Vnuc.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind())+=tmp;
2352 	Vnuc.submat(shells[j].get_first_ind(),shells[i].get_first_ind(),shells[j].get_last_ind(),shells[i].get_last_ind())+=arma::trans(tmp);
2353       } else
2354 	// On the diagonal we just get it once
2355 	Vnuc.submat(shells[i].get_first_ind(),shells[i].get_first_ind(),shells[i].get_last_ind(),shells[i].get_last_ind())+=arma::trans(tmp);
2356     }
2357 
2358   return Vnuc;
2359 }
2360 
potential(coords_t r) const2361 arma::mat BasisSet::potential(coords_t r) const {
2362   // Form nuclear attraction matrix
2363 
2364   // Size of basis set
2365   size_t N=get_Nbf();
2366 
2367   // Initialize matrix
2368   arma::mat V(N,N);
2369   V.zeros();
2370 
2371   // Loop over shells
2372 #ifdef _OPENMP
2373 #pragma omp parallel for schedule(dynamic)
2374 #endif
2375   for(size_t ip=0;ip<shellpairs.size();ip++) {
2376     // Shells in pair
2377     size_t i=shellpairs[ip].is;
2378     size_t j=shellpairs[ip].js;
2379 
2380     // Get subblock
2381     arma::mat tmp=shells[i].nuclear(r.x,r.y,r.z,shells[j]);
2382 
2383     // On the off diagonal we fill out both sides of the matrix
2384     if(i!=j) {
2385       V.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind())=tmp;
2386       V.submat(shells[j].get_first_ind(),shells[i].get_first_ind(),shells[j].get_last_ind(),shells[i].get_last_ind())=arma::trans(tmp);
2387     } else
2388       // On the diagonal we just get it once
2389       V.submat(shells[i].get_first_ind(),shells[i].get_first_ind(),shells[i].get_last_ind(),shells[i].get_last_ind())=arma::trans(tmp);
2390   }
2391 
2392   return V;
2393 }
2394 
eri_screening(arma::mat & Q,arma::mat & M,double omega,double alpha,double beta) const2395 void BasisSet::eri_screening(arma::mat & Q, arma::mat & M, double omega, double alpha, double beta) const {
2396   // Get unique pairs
2397   std::vector<shellpair_t> pairs=get_unique_shellpairs();
2398 
2399   Q.zeros(shells.size(),shells.size());
2400   M.zeros(shells.size(),shells.size());
2401 
2402 #ifdef _OPENMP
2403 #pragma omp parallel
2404 #endif
2405   {
2406     ERIWorker *eri;
2407     const std::vector<double> * erip;
2408 
2409     if(omega==0.0 && alpha==1.0 && beta==0.0)
2410       eri=new ERIWorker(get_max_am(),get_max_Ncontr());
2411     else
2412       eri=new ERIWorker_srlr(get_max_am(),get_max_Ncontr(),omega,alpha,beta);
2413 
2414 #ifdef _OPENMP
2415 #pragma omp for schedule(dynamic)
2416 #endif
2417     for(size_t ip=0;ip<pairs.size();ip++) {
2418       size_t i=pairs[ip].is;
2419       size_t j=pairs[ip].js;
2420 
2421       // Compute (ij|ij) integrals
2422       {
2423         eri->compute(&shells[i],&shells[j],&shells[i],&shells[j]);
2424         erip=eri->getp();
2425         // Get maximum value
2426         double m=0.0;
2427         for(size_t k=0;k<erip->size();k++)
2428           m=std::max(m,std::abs((*erip)[k]));
2429         m=sqrt(m);
2430         Q(i,j)=m;
2431         Q(j,i)=m;
2432       }
2433 
2434       // Compute (ii|jj) integrals
2435       {
2436         eri->compute(&shells[i],&shells[i],&shells[j],&shells[j]);
2437         erip=eri->getp();
2438         // Get maximum value
2439         double m=0.0;
2440         for(size_t k=0;k<erip->size();k++)
2441           m=std::max(m,std::abs((*erip)[k]));
2442         m=sqrt(m);
2443         M(i,j)=m;
2444         M(j,i)=m;
2445       }
2446     }
2447 
2448     delete eri;
2449   }
2450 }
2451 
nuclear_pulay(const arma::mat & P) const2452 arma::vec BasisSet::nuclear_pulay(const arma::mat & P) const {
2453   arma::vec f(3*nuclei.size());
2454   f.zeros();
2455 
2456 #ifdef _OPENMP
2457 #pragma omp parallel
2458 #endif
2459   {
2460     // Loop over shells
2461 #ifdef _OPENMP
2462     arma::vec fwrk(3*nuclei.size());
2463     fwrk.zeros();
2464 
2465 #pragma omp for schedule(dynamic)
2466 #endif
2467     for(size_t ip=0;ip<shellpairs.size();ip++)
2468       for(size_t inuc=0;inuc<nuclei.size();inuc++) {
2469 	// If BSSE nucleus, do nothing
2470 	if(nuclei[inuc].bsse)
2471 	  continue;
2472 
2473 	// Shells in pair
2474 	size_t i=shellpairs[ip].is;
2475 	size_t j=shellpairs[ip].js;
2476 
2477 	// Nuclear charge
2478 	int Z=nuclei[inuc].Z;
2479 
2480 	// Coordinates of nucleus
2481 	double cx=nuclei[inuc].r.x;
2482 	double cy=nuclei[inuc].r.y;
2483 	double cz=nuclei[inuc].r.z;
2484 
2485 	// Density matrix for the pair
2486 	arma::mat Pmat=P.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind());
2487 
2488 	// Get the forces
2489 	arma::vec tmp=Z*shells[i].nuclear_pulay(cx,cy,cz,Pmat,shells[j]);
2490 
2491 	// Off-diagonal?
2492 	if(i!=j)
2493 	  tmp*=2.0;
2494 
2495 	// and increment the nuclear force.
2496 #ifdef _OPENMP
2497 	fwrk.subvec(3*shells[i].get_center_ind(),3*shells[i].get_center_ind()+2)+=tmp.subvec(0,2);
2498 	fwrk.subvec(3*shells[j].get_center_ind(),3*shells[j].get_center_ind()+2)+=tmp.subvec(3,5);
2499 #else
2500 	f.subvec(3*shells[i].get_center_ind(),3*shells[i].get_center_ind()+2)+=tmp.subvec(0,2);
2501 	f.subvec(3*shells[j].get_center_ind(),3*shells[j].get_center_ind()+2)+=tmp.subvec(3,5);
2502 #endif
2503       }
2504 
2505 #ifdef _OPENMP
2506 #pragma omp critical
2507     f+=fwrk;
2508 #endif
2509   }
2510 
2511   return f;
2512 }
2513 
nuclear_der(const arma::mat & P) const2514 arma::vec BasisSet::nuclear_der(const arma::mat & P) const {
2515   arma::vec f(3*nuclei.size());
2516   f.zeros();
2517 
2518 #ifdef _OPENMP
2519 #pragma omp parallel
2520 #endif
2521   {
2522     // Loop over shells
2523 #ifdef _OPENMP
2524     arma::vec fwrk(3*nuclei.size());
2525     fwrk.zeros();
2526 
2527 #pragma omp for schedule(dynamic)
2528 #endif
2529     for(size_t ip=0;ip<shellpairs.size();ip++)
2530       for(size_t inuc=0;inuc<nuclei.size();inuc++) {
2531 	// If BSSE nucleus, do nothing
2532 	if(nuclei[inuc].bsse)
2533 	  continue;
2534 
2535 	// Shells in pair
2536 	size_t i=shellpairs[ip].is;
2537 	size_t j=shellpairs[ip].js;
2538 
2539 	// Nuclear charge
2540 	int Z=nuclei[inuc].Z;
2541 
2542 	// Coordinates of nucleus
2543 	double cx=nuclei[inuc].r.x;
2544 	double cy=nuclei[inuc].r.y;
2545 	double cz=nuclei[inuc].r.z;
2546 
2547 	// Density matrix for the pair
2548 	arma::mat Pmat=P.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind());
2549 
2550 	// Get the forces
2551 	arma::vec tmp=Z*shells[i].nuclear_der(cx,cy,cz,Pmat,shells[j]);
2552 
2553 	// Off-diagonal?
2554 	if(i!=j)
2555 	  tmp*=2.0;
2556 
2557 	// and increment the nuclear force.
2558 #ifdef _OPENMP
2559 	fwrk.subvec(3*inuc,3*inuc+2)+=tmp;
2560 #else
2561 	f.subvec(3*inuc,3*inuc+2)+=tmp;
2562 #endif
2563       }
2564 
2565 #ifdef _OPENMP
2566 #pragma omp critical
2567     f+=fwrk;
2568 #endif
2569   }
2570 
2571   return f;
2572 }
2573 
kinetic_pulay(const arma::mat & P) const2574 arma::vec BasisSet::kinetic_pulay(const arma::mat & P) const {
2575   arma::vec f(3*nuclei.size());
2576   f.zeros();
2577 
2578 #ifdef _OPENMP
2579 #pragma omp parallel
2580 #endif
2581   {
2582     // Loop over shells
2583 #ifdef _OPENMP
2584     arma::vec fwrk(3*nuclei.size());
2585     fwrk.zeros();
2586 
2587 #pragma omp for schedule(dynamic)
2588 #endif
2589     for(size_t ip=0;ip<shellpairs.size();ip++) {
2590       // Shells in pair
2591       size_t i=shellpairs[ip].is;
2592       size_t j=shellpairs[ip].js;
2593 
2594       // Density matrix for the pair
2595       arma::mat Pmat=P.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind());
2596 
2597       // Get the forces
2598       arma::vec tmp=shells[i].kinetic_pulay(Pmat,shells[j]);
2599 
2600       // Off-diagonal?
2601       if(i!=j)
2602 	tmp*=2.0;
2603 
2604       // and increment the nuclear force.
2605 #ifdef _OPENMP
2606       fwrk.subvec(3*shells[i].get_center_ind(),3*shells[i].get_center_ind()+2)+=tmp.subvec(0,2);
2607       fwrk.subvec(3*shells[j].get_center_ind(),3*shells[j].get_center_ind()+2)+=tmp.subvec(3,5);
2608 #else
2609       f.subvec(3*shells[i].get_center_ind(),3*shells[i].get_center_ind()+2)+=tmp.subvec(0,2);
2610       f.subvec(3*shells[j].get_center_ind(),3*shells[j].get_center_ind()+2)+=tmp.subvec(3,5);
2611 #endif
2612     }
2613 
2614 #ifdef _OPENMP
2615 #pragma omp critical
2616     f+=fwrk;
2617 #endif
2618   }
2619 
2620   return f;
2621 }
2622 
overlap_der(const arma::mat & P) const2623 arma::vec BasisSet::overlap_der(const arma::mat & P) const {
2624   arma::vec f(3*nuclei.size());
2625   f.zeros();
2626 
2627 #ifdef _OPENMP
2628 #pragma omp parallel
2629 #endif
2630   {
2631     // Loop over shells
2632 #ifdef _OPENMP
2633     arma::vec fwrk(3*nuclei.size());
2634     fwrk.zeros();
2635 
2636 #pragma omp for schedule(dynamic)
2637 #endif
2638     for(size_t ip=0;ip<shellpairs.size();ip++) {
2639 
2640       // Shells in pair
2641       size_t i=shellpairs[ip].is;
2642       size_t j=shellpairs[ip].js;
2643 
2644       // Density matrix for the pair
2645       arma::mat Pmat=P.submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind());
2646 
2647       // Get the forces
2648       arma::vec tmp=shells[i].overlap_der(Pmat,shells[j]);
2649 
2650       // Off-diagonal?
2651       if(i!=j)
2652 	tmp*=2.0;
2653 
2654       // and increment the nuclear force.
2655 #ifdef _OPENMP
2656       fwrk.subvec(3*shells[i].get_center_ind(),3*shells[i].get_center_ind()+2)+=tmp.subvec(0,2);
2657       fwrk.subvec(3*shells[j].get_center_ind(),3*shells[j].get_center_ind()+2)+=tmp.subvec(3,5);
2658 #else
2659       f.subvec(3*shells[i].get_center_ind(),3*shells[i].get_center_ind()+2)+=tmp.subvec(0,2);
2660       f.subvec(3*shells[j].get_center_ind(),3*shells[j].get_center_ind()+2)+=tmp.subvec(3,5);
2661 #endif
2662     }
2663 
2664 #ifdef _OPENMP
2665 #pragma omp critical
2666     f+=fwrk;
2667 #endif
2668   }
2669 
2670   return f;
2671 }
2672 
nuclear_force() const2673 arma::vec BasisSet::nuclear_force() const {
2674   arma::vec f(3*nuclei.size());
2675   f.zeros();
2676 
2677   for(size_t i=0;i<nuclei.size();i++) {
2678     if(nuclei[i].bsse)
2679       continue;
2680 
2681     for(size_t j=0;j<i;j++) {
2682       if(nuclei[j].bsse)
2683 	continue;
2684 
2685       // Calculate distance
2686       coords_t rij=nuclei[i].r-nuclei[j].r;
2687       // and its third power
2688       double rcb=pow(norm(rij),3);
2689 
2690       // Force is
2691       arma::vec F(3);
2692       F(0)=nuclei[i].Z*nuclei[j].Z/rcb*rij.x;
2693       F(1)=nuclei[i].Z*nuclei[j].Z/rcb*rij.y;
2694       F(2)=nuclei[i].Z*nuclei[j].Z/rcb*rij.z;
2695 
2696       f.subvec(3*i,3*i+2)+=F;
2697       f.subvec(3*j,3*j+2)-=F;
2698     }
2699   }
2700 
2701   return f;
2702 }
2703 
moment(int mom,double x,double y,double z) const2704 std::vector<arma::mat> BasisSet::moment(int mom, double x, double y, double z) const {
2705   // Compute moment integrals around (x,y,z);
2706 
2707   // Number of moments to compute is
2708   size_t Nmom=(mom+1)*(mom+2)/2;
2709   // Amount of basis functions is
2710   size_t Nbf=get_Nbf();
2711 
2712   // Returned array, holding the moment integrals
2713   std::vector<arma::mat> ret;
2714   ret.reserve(Nmom);
2715 
2716   // Initialize arrays
2717   for(size_t i=0;i<Nmom;i++) {
2718     ret.push_back(arma::mat(Nbf,Nbf));
2719     ret[i].zeros();
2720   }
2721 
2722   // Loop over shells
2723 #ifdef _OPENMP
2724 #pragma omp parallel for schedule(dynamic)
2725 #endif
2726   for(size_t ip=0;ip<shellpairs.size();ip++) {
2727     // Shells in pair
2728     size_t i=shellpairs[ip].is;
2729     size_t j=shellpairs[ip].js;
2730 
2731 
2732     // Compute moment integral over shells
2733     std::vector<arma::mat> ints=shells[i].moment(mom,x,y,z,shells[j]);
2734 
2735     // Store moments
2736     if(i!=j) {
2737       for(size_t m=0;m<Nmom;m++) {
2738 	ret[m].submat(shells[i].get_first_ind(),shells[j].get_first_ind(),shells[i].get_last_ind(),shells[j].get_last_ind())=ints[m];
2739 	ret[m].submat(shells[j].get_first_ind(),shells[i].get_first_ind(),shells[j].get_last_ind(),shells[i].get_last_ind())=arma::trans(ints[m]);
2740       }
2741     } else {
2742       for(size_t m=0;m<Nmom;m++)
2743 	ret[m].submat(shells[i].get_first_ind(),shells[i].get_first_ind(),shells[i].get_last_ind(),shells[i].get_last_ind())=ints[m];
2744     }
2745   }
2746 
2747   return ret;
2748 }
2749 
integral() const2750 arma::vec BasisSet::integral() const {
2751   arma::vec ints(get_Nbf());
2752 #ifdef _OPENMP
2753 #pragma omp parallel for
2754 #endif
2755   for(size_t is=0;is<shells.size();is++)
2756     ints.subvec(shells[is].get_first_ind(),shells[is].get_last_ind())=shells[is].integral();
2757 
2758   return ints;
2759 }
2760 
three_overlap(const GaussianShell * is,const GaussianShell * js,const GaussianShell * ks)2761 arma::cube three_overlap(const GaussianShell *is, const GaussianShell *js, const GaussianShell*ks) {
2762   // First, compute the cartesian integrals. Centers of shells
2763   coords_t icen=is->get_center();
2764   coords_t jcen=js->get_center();
2765   coords_t kcen=ks->get_center();
2766 
2767   // Cartesian functions
2768   std::vector<shellf_t> icart=is->get_cart();
2769   std::vector<shellf_t> jcart=js->get_cart();
2770   std::vector<shellf_t> kcart=ks->get_cart();
2771 
2772   // Exponential contractions
2773   std::vector<contr_t> icontr=is->get_contr();
2774   std::vector<contr_t> jcontr=js->get_contr();
2775   std::vector<contr_t> kcontr=ks->get_contr();
2776 
2777   // Cartesian integrals
2778   arma::cube cartint(icart.size(),jcart.size(),kcart.size());
2779   cartint.zeros();
2780   for(size_t ix=0;ix<icontr.size();ix++) {
2781     double iz=icontr[ix].z;
2782     double ic=icontr[ix].c;
2783 
2784     for(size_t jx=0;jx<jcontr.size();jx++) {
2785       double jz=jcontr[jx].z;
2786       double jc=jcontr[jx].c;
2787 
2788       for(size_t kx=0;kx<kcontr.size();kx++) {
2789 	double kz=kcontr[kx].z;
2790 	double kc=kcontr[kx].c;
2791 
2792 	cartint+=ic*jc*kc*three_overlap_int_os(icen.x,icen.y,icen.z,jcen.x,jcen.y,jcen.z,kcen.x,kcen.y,kcen.z,iz,jz,kz,icart,jcart,kcart);
2793       }
2794     }
2795   }
2796 
2797   // Convert first two indices to spherical basis
2798   arma::cube twints(is->get_Nbf(),js->get_Nbf(),kcart.size());
2799   twints.zeros();
2800   for(size_t ik=0;ik<kcart.size();ik++) {
2801     // ({i}|{j}|ks) is
2802     arma::mat momval=cartint.slice(ik);
2803 
2804     // Do conversion
2805     if(is->lm_in_use())
2806       momval=is->get_trans()*momval;
2807     if(js->lm_in_use())
2808       momval=momval*arma::trans(js->get_trans());
2809 
2810     twints.slice(ik)=momval;
2811   }
2812 
2813   // Convert last index to spherical basis
2814   if(! ks->lm_in_use())
2815     return twints;
2816 
2817   // Transformation matrix
2818   arma::mat ktrans=ks->get_trans();
2819 
2820   // Final integrals
2821   arma::cube ints(is->get_Nbf(),js->get_Nbf(),ks->get_Nbf());
2822   ints.zeros();
2823 
2824   // Loop over spherical basis functions
2825   for(size_t ik=0;ik<ks->get_Nbf();ik++)
2826     // Loop over cartesians
2827     for(size_t ick=0;ick<kcart.size();ick++)
2828       ints.slice(ik)+=ktrans(ik,ick)*twints.slice(ick);
2829 
2830   return ints;
2831 }
2832 
2833 
Ztot() const2834 int BasisSet::Ztot() const {
2835   int Zt=0;
2836   for(size_t i=0;i<nuclei.size();i++) {
2837     if(nuclei[i].bsse)
2838       continue;
2839     Zt+=nuclei[i].Z;
2840   }
2841   return Zt;
2842 }
2843 
Enuc() const2844 double BasisSet::Enuc() const {
2845   double En=0.0;
2846 
2847   for(size_t i=0;i<nuclei.size();i++) {
2848     if(nuclei[i].bsse)
2849       continue;
2850 
2851     int Zi=nuclei[i].Z;
2852 
2853     for(size_t j=0;j<i;j++) {
2854       if(nuclei[j].bsse)
2855 	continue;
2856       int Zj=nuclei[j].Z;
2857 
2858       En+=Zi*Zj/nucleardist(i,j);
2859     }
2860   }
2861 
2862   return En;
2863 }
2864 
projectMOs(const BasisSet & oldbas,const arma::vec & oldE,const arma::mat & oldMOs,arma::vec & E,arma::mat & MOs,size_t nocc) const2865 void BasisSet::projectMOs(const BasisSet & oldbas, const arma::vec & oldE, const arma::mat & oldMOs, arma::vec & E, arma::mat & MOs, size_t nocc) const {
2866   if(*this == oldbas) {
2867     MOs=oldMOs;
2868     E=oldE;
2869     return;
2870   }
2871 
2872   if(oldMOs.n_cols < nocc) {
2873     oldbas.print();
2874     fflush(stdout);
2875 
2876     std::ostringstream oss;
2877     oss << "Old basis doesn't have enough occupied orbitals: " << oldbas.get_Nbf() << " basis functions but " << nocc << " orbitals wanted!\n";
2878     throw std::runtime_error(oss.str());
2879   }
2880 
2881   BasisSet transbas(oldbas);
2882   transbas.nuclei=nuclei;
2883   // Store new geometries
2884   for(size_t i=0;i<oldbas.shells.size();i++) {
2885     // Index of center in the old basis is
2886     size_t idx=oldbas.shells[i].get_center_ind();
2887     // Set new coordinates
2888     transbas.shells[i].set_center(nuclei[idx].r,idx);
2889   }
2890   transbas.finalize(false,false);
2891 
2892   // Get overlap matrix
2893   arma::mat S11=overlap();
2894   // and overlap with old basis
2895   arma::mat S12=overlap(transbas);
2896 
2897   // Form orthogonalizing matrix
2898   arma::mat Svec;
2899   arma::vec Sval;
2900   eig_sym_ordered(Sval,Svec,S11);
2901 
2902   // Get number of basis functions
2903   const size_t Nbf=get_Nbf();
2904 
2905   // Count number of eigenvalues that are above cutoff
2906   size_t Nind=0;
2907   for(size_t i=0;i<Nbf;i++)
2908     if(Sval(i)>=settings.get_double("LinDepThresh"))
2909       Nind++;
2910   // Number of linearly dependent basis functions
2911   const size_t Ndep=Nbf-Nind;
2912   if(Nind<nocc) {
2913     print();
2914     fflush(stdout);
2915 
2916     std::ostringstream oss;
2917     oss << "Basis set too small for occupied orbitals: " << Nind << " independent functions but " << nocc << " orbitals!\n";
2918     throw std::runtime_error(oss.str());
2919   }
2920 
2921   // Get rid of linearly dependent eigenvalues and eigenvectors
2922   Sval=Sval.subvec(Ndep,Nbf-1);
2923   Svec=Svec.cols(Ndep,Nbf-1);
2924 
2925   // Form canonical orthonormalization matrix
2926   arma::mat Sinvh(Nbf,Nind);
2927   for(size_t i=0;i<Nind;i++)
2928     Sinvh.col(i)=Svec.col(i)/sqrt(Sval(i));
2929 
2930   // and the real S^-1
2931   arma::mat Sinv=Sinvh*arma::trans(Sinvh);
2932 
2933   // New orbitals and orbital energies
2934   MOs.zeros(Sinvh.n_rows,Nind);
2935   E.zeros(Nind);
2936 
2937   if(!nocc) {
2938     MOs=Sinvh;
2939     return;
2940   }
2941 
2942   // Projected orbitals
2943   MOs.cols(0,nocc-1)=Sinv*S12*oldMOs.cols(0,nocc-1);
2944   // and energies. PZ calculations might not have energies stored!
2945   size_t nE=std::min((arma::uword) nocc,oldE.n_elem);
2946   if(nE>0)
2947     E.subvec(0,nE-1)=oldE.subvec(0,nE-1);
2948   // Overlap of projected orbitals is
2949   arma::mat SMO=arma::trans(MOs.cols(0,nocc-1))*S11*MOs.cols(0,nocc-1);
2950 
2951   // Do eigendecomposition
2952   arma::mat SMOvec;
2953   arma::vec SMOval;
2954   bool ok=arma::eig_sym(SMOval,SMOvec,SMO);
2955   if(!ok)
2956     throw std::runtime_error("Failed to diagonalize orbital overlap\n");
2957 
2958   // Orthogonalizing matrix is
2959   arma::mat orthmat=SMOvec*arma::diagmat(1.0/sqrt(SMOval))*trans(SMOvec);
2960 
2961   // Orthonormal projected orbitals are
2962   MOs.cols(0,nocc-1)=MOs.cols(0,nocc-1)*orthmat;
2963 
2964   // Form virtual orbitals
2965   if(nocc<Nind) {
2966     // MO submatrix
2967     arma::mat C=MOs.cols(0,nocc-1);
2968 
2969     // Generate other vectors. Compute overlap of functions
2970     arma::mat X=arma::trans(Sinvh)*S11*C;
2971     // and perform SVD
2972     arma::mat U, V;
2973     arma::vec s;
2974     bool svdok=arma::svd(U,s,V,X);
2975     if(!svdok)
2976       throw std::runtime_error("SVD decomposition failed!\n");
2977 
2978     // Rotate eigenvectors.
2979     arma::mat Cnew(Sinvh*U);
2980 
2981     // Now, the occupied subspace is found in the first nocc
2982     // eigenvectors.
2983     MOs.cols(nocc,Nind-1)=Cnew.cols(nocc,Nind-1);
2984     // Dummy energies
2985     if(nE==nocc)
2986       E.subvec(nocc,Nind-1)=1.1*std::max(E(nocc-1),0.0)*arma::ones(Nind-nocc,1);
2987   }
2988 
2989   // Failsafe
2990   try {
2991     // Check orthogonality of orbitals
2992     check_orth(MOs,S11,false);
2993   } catch(std::runtime_error & err) {
2994     std::ostringstream oss;
2995     oss << "Orbitals generated by MO_project are not orthonormal.\n";
2996     throw std::runtime_error(oss.str());
2997   }
2998 }
2999 
projectOMOs(const BasisSet & oldbas,const arma::cx_mat & oldOMOs,arma::cx_mat & OMOs,size_t nocc) const3000 void BasisSet::projectOMOs(const BasisSet & oldbas, const arma::cx_mat & oldOMOs, arma::cx_mat & OMOs, size_t nocc) const {
3001   if(*this == oldbas) {
3002     OMOs=oldOMOs;
3003     return;
3004   }
3005 
3006   if(oldOMOs.n_cols < nocc) {
3007     oldbas.print();
3008     fflush(stdout);
3009 
3010     std::ostringstream oss;
3011     oss << "Old basis doesn't have enough occupied orbitals: " << oldbas.get_Nbf() << " basis functions but " << nocc << " orbitals wanted!\n";
3012     throw std::runtime_error(oss.str());
3013   }
3014 
3015   BasisSet transbas(oldbas);
3016   transbas.nuclei=nuclei;
3017   // Store new geometries
3018   for(size_t i=0;i<oldbas.shells.size();i++) {
3019     // Index of center in the old basis is
3020     size_t idx=oldbas.shells[i].get_center_ind();
3021     // Set new coordinates
3022     transbas.shells[i].set_center(nuclei[idx].r,idx);
3023   }
3024   transbas.finalize(false,false);
3025 
3026   // Get overlap matrix
3027   arma::mat S11=overlap();
3028   // and overlap with old basis
3029   arma::mat S12=overlap(transbas);
3030 
3031   // Form orthogonalizing matrix
3032   arma::mat Svec;
3033   arma::vec Sval;
3034   eig_sym_ordered(Sval,Svec,S11);
3035 
3036   // Get number of basis functions
3037   const size_t Nbf=get_Nbf();
3038 
3039   // Count number of eigenvalues that are above cutoff
3040   size_t Nind=0;
3041   for(size_t i=0;i<Nbf;i++)
3042     if(Sval(i)>=settings.get_double("LinDepThresh"))
3043       Nind++;
3044   // Number of linearly dependent basis functions
3045   const size_t Ndep=Nbf-Nind;
3046   if(Nind<nocc) {
3047     print();
3048     fflush(stdout);
3049 
3050     std::ostringstream oss;
3051     oss << "Basis set too small for occupied orbitals: " << Nind << " independent functions but " << nocc << " orbitals!\n";
3052     throw std::runtime_error(oss.str());
3053   }
3054 
3055   // Get rid of linearly dependent eigenvalues and eigenvectors
3056   Sval=Sval.subvec(Ndep,Nbf-1);
3057   Svec=Svec.cols(Ndep,Nbf-1);
3058 
3059   // Form canonical orthonormalization matrix
3060   arma::mat Sinvh(Nbf,Nind);
3061   for(size_t i=0;i<Nind;i++)
3062     Sinvh.col(i)=Svec.col(i)/sqrt(Sval(i));
3063 
3064   // and the real S^-1
3065   arma::mat Sinv=Sinvh*arma::trans(Sinvh);
3066 
3067   // New orbitals and orbital energies
3068   OMOs.zeros(Sinvh.n_rows,Nind);
3069 
3070   if(!nocc) {
3071     OMOs=Sinvh*COMPLEX1;
3072     return;
3073   }
3074 
3075   // Projected orbitals
3076   OMOs.cols(0,nocc-1)=Sinv*S12*oldOMOs.cols(0,nocc-1);
3077 
3078   // Overlap of projected orbitals is
3079   arma::cx_mat SMO=arma::trans(OMOs.cols(0,nocc-1))*S11*OMOs.cols(0,nocc-1);
3080 
3081   // Do eigendecomposition
3082   arma::cx_mat SMOvec;
3083   arma::vec SMOval;
3084   bool ok=arma::eig_sym(SMOval,SMOvec,SMO);
3085   if(!ok)
3086     throw std::runtime_error("Failed to diagonalize orbital overlap\n");
3087 
3088   // Orthogonalizing matrix is
3089   arma::cx_mat orthmat=SMOvec*arma::diagmat(1.0/sqrt(SMOval))*trans(SMOvec);
3090 
3091   // Orthonormal projected orbitals are
3092   OMOs.cols(0,nocc-1)=OMOs.cols(0,nocc-1)*orthmat;
3093 
3094   // Form virtual orbitals
3095   if(nocc<Nind) {
3096     // MO submatrix
3097     arma::cx_mat C=OMOs.cols(0,nocc-1);
3098 
3099     // Generate other vectors. Compute overlap of functions
3100     arma::cx_mat X=arma::trans(Sinvh)*S11*C;
3101     // and perform SVD
3102     arma::cx_mat U, V;
3103     arma::vec s;
3104     bool svdok=arma::svd(U,s,V,X);
3105     if(!svdok)
3106       throw std::runtime_error("SVD decomposition failed!\n");
3107 
3108     // Rotate eigenvectors.
3109     arma::cx_mat Cnew(Sinvh*U);
3110 
3111     // Now, the occupied subspace is found in the first nocc
3112     // eigenvectors.
3113     OMOs.cols(nocc,Nind-1)=Cnew.cols(nocc,Nind-1);
3114   }
3115 
3116   // Failsafe
3117   try {
3118     // Check orthogonality of orbitals
3119     check_orth(OMOs,S11,false);
3120   } catch(std::runtime_error & err) {
3121     std::ostringstream oss;
3122     oss << "Orbitals generated by OMO_project are not orthonormal.\n";
3123     throw std::runtime_error(oss.str());
3124   }
3125 }
3126 
exponent_compare(const GaussianShell & lhs,const GaussianShell & rhs)3127 bool exponent_compare(const GaussianShell & lhs, const GaussianShell & rhs) {
3128   return lhs.get_contr()[0].z>rhs.get_contr()[0].z;
3129 }
3130 
density_fitting(double fsam,int lmaxinc) const3131 BasisSet BasisSet::density_fitting(double fsam, int lmaxinc) const {
3132   // Automatically generate density fitting basis.
3133 
3134   // R. Yang, A. P. Rendell and M. J. Frisch, "Automatically generated
3135   // Coulomb fitting basis sets: Design and accuracy for systems
3136   // containing H to Kr", J. Chem. Phys. 127 (2007), 074102
3137 
3138   bool uselm0(settings.get_bool("UseLM"));
3139   settings.set_bool("UseLM",true);
3140   // Density fitting basis set
3141   BasisSet dfit(1);
3142   settings.set_bool("UseLM",uselm0);
3143 
3144   // Loop over nuclei
3145   for(size_t in=0;in<nuclei.size();in++) {
3146     // Add nucleus to fitting set
3147     dfit.add_nucleus(nuclei[in]);
3148     // Dummy nucleus
3149     nucleus_t nuc=nuclei[in];
3150 
3151     // Define lval - (1) in YRF
3152     int lval;
3153     if(nuclei[in].Z<3)
3154       lval=0;
3155     else if(nuclei[in].Z<19)
3156       lval=1;
3157     else if(nuclei[in].Z<55)
3158       lval=2;
3159     else
3160       lval=3;
3161 
3162     // Get shells corresponding to this nucleus
3163     std::vector<GaussianShell> shs=get_funcs(in);
3164 
3165     // Form candidate set - (2), (3) and (6) in YRF
3166     std::vector<GaussianShell> cand;
3167     for(size_t i=0;i<shs.size();i++) {
3168       // Get angular momentum
3169       int am=2*shs[i].get_am();
3170       // Get exponents
3171       std::vector<contr_t> contr=shs[i].get_contr();
3172 
3173       // Dummy contraction
3174       std::vector<contr_t> C(1);
3175       C[0].c=1.0;
3176 
3177       for(size_t j=0;j<contr.size();j++) {
3178 	// Set exponent
3179 	C[0].z=2.0*contr[j].z;
3180 
3181 	// Check that candidate set doesn't already contain the same function
3182 	bool found=0;
3183 	for(size_t k=0;k<cand.size();k++)
3184 	  if((cand[k].get_am()==am) && (cand[k].get_contr()[0]==C[0])) {
3185 	    found=1;
3186 	    break;
3187 	  }
3188 
3189 	// Add function
3190 	if(!found) {
3191 	  cand.push_back(GaussianShell(am,true,C));
3192 	  cand[cand.size()-1].set_center(nuc.r,in);
3193 	}
3194       }
3195     }
3196 
3197     // Sort trial set in order of decreasing exponents (don't care
3198     // about angular momentum) - (4) in YRF
3199     std::stable_sort(cand.begin(),cand.end(),exponent_compare);
3200 
3201     // Define maximum angular momentum for candidate functions and for
3202     // density fitting - (5) in YRF
3203     int lmax_obs=0;
3204     for(size_t i=0;i<shs.size();i++)
3205       if(shs[i].get_am()>lmax_obs)
3206 	lmax_obs=shs[i].get_am();
3207     int lmax_abs=std::max(lmax_obs+lmaxinc,2*lval);
3208 
3209     // (6) was already above.
3210 
3211     while(cand.size()>0) {
3212       // Generate trial set
3213       std::vector<GaussianShell> trial;
3214 
3215       // Function with largest exponent is moved to the trial set and
3216       // its exponent is set as the reference value - (7) in YRF
3217       double ref=(cand[0].get_contr())[0].z;
3218       trial.push_back(cand[0]);
3219       cand.erase(cand.begin());
3220 
3221       if(cand.size()>0) {
3222 	// More functions remaining, move all for which ratio of
3223 	// reference to exponent is smaller than fsam - (8) in YRF
3224 	for(size_t i=cand.size()-1;i<cand.size();i--)
3225 	  if(ref/((cand[i].get_contr())[0].z)<fsam) {
3226 	    trial.push_back(cand[i]);
3227 	    cand.erase(cand.begin()+i);
3228 	  }
3229 
3230 	// Compute geometric average of exponents - (9) in YRF
3231 	double geomav=1.0;
3232 	for(size_t i=0;i<trial.size();i++)
3233 	  geomav*=trial[i].get_contr()[0].z;
3234 	geomav=pow(geomav,1.0/trial.size());
3235 
3236 	//	printf("Geometric average of %i functions is %e.\n",(int) trial.size(),geomav);
3237 
3238 	// Form list of angular momentum values
3239 	// Compute maximum angular moment of current trial set
3240 	int ltrial=0;
3241 	for(size_t i=0;i<trial.size();i++)
3242 	  if(trial[i].get_am()>ltrial)
3243 	    ltrial=trial[i].get_am();
3244 
3245 	// If this is larger than allowed, renormalize
3246 	if(ltrial>lmax_abs)
3247 	  ltrial=lmax_abs;
3248 
3249 	// Form list of angular momentum already used in ABS
3250 	std::vector<int> lvals(max_am+1);
3251 	for(int i=0;i<=max_am;i++)
3252 	  lvals[i]=0;
3253 
3254 	// Maximum angular momentum of trial functions is
3255 	lvals[ltrial]++;
3256 	// Get shells on current center
3257 	std::vector<GaussianShell> cur_shells=dfit.get_funcs(in);
3258 	for(size_t i=0;i<cur_shells.size();i++)
3259 	  lvals[cur_shells[i].get_am()]++;
3260 
3261 	// Check that there are no gaps in lvals
3262 	bool fill=0;
3263 	for(size_t i=lvals.size()-1;i<lvals.size();i--) {
3264 	  // Fill down from here below
3265 	  if(!fill && lvals[i]>0)
3266 	    fill=1;
3267 	  if(fill && !lvals[i])
3268 	    lvals[i]++;
3269 	}
3270 
3271 	// Add density fitting functions
3272 	std::vector<contr_t> C(1);
3273 	C[0].c=1.0;
3274 	C[0].z=geomav;
3275 	for(int l=0;l<=max_am;l++)
3276 	  if(lvals[l]>0) {
3277 	    // Pure spherical functions used
3278 	    dfit.add_shell(in,l,true,C);
3279 	  }
3280       }
3281     } // (10) in YRF
3282   } // (11) in YRF
3283 
3284   // Normalize basis set
3285   dfit.coulomb_normalize();
3286   // Form list of unique shell pairs
3287   dfit.form_unique_shellpairs();
3288 
3289   return dfit;
3290 }
3291 
3292 
exchange_fitting() const3293 BasisSet BasisSet::exchange_fitting() const {
3294   // Exchange fitting basis set
3295 
3296   bool uselm0(settings.get_bool("UseLM"));
3297   settings.set_bool("UseLM",true);
3298   // Density fitting basis set
3299   BasisSet fit(nuclei.size());
3300   settings.set_bool("UseLM",uselm0);
3301 
3302   const int maxam=get_max_am();
3303 
3304   // Loop over nuclei
3305   for(size_t in=0;in<nuclei.size();in++) {
3306     // Get shells corresponding to this nucleus
3307     std::vector<GaussianShell> shs=get_funcs(in);
3308 
3309     // Sort shells in increasing angular momentum
3310     std::sort(shs.begin(),shs.end());
3311 
3312     // Determine amount of functions on current atom and minimum and maximum exponents
3313     std::vector<int> nfunc(2*maxam+1);
3314     std::vector<double> mine(2*maxam+1);
3315     std::vector<double> maxe(2*maxam+1);
3316     int lmax=0;
3317 
3318     // Initialize arrays
3319     for(int l=0;l<=2*maxam;l++) {
3320       nfunc[l]=0;
3321       mine[l]=DBL_MAX;
3322       maxe[l]=0.0;
3323     }
3324 
3325     // Loop over shells of current nucleus
3326     for(size_t ish=0;ish<shs.size();ish++)
3327       // Second loop over shells of current nucleus
3328       for(size_t jsh=0;jsh<shs.size();jsh++) {
3329 
3330 	// Current angular momentum
3331 	int l=shs[ish].get_am()+shs[jsh].get_am();
3332 
3333 	// Update maximum value
3334 	if(l>lmax)
3335 	  lmax=l;
3336 
3337 	// Increase amount of functions
3338 	nfunc[l]++;
3339 
3340 	// Get exponential contractions
3341 	std::vector<contr_t> icontr=shs[ish].get_contr();
3342 	std::vector<contr_t> jcontr=shs[jsh].get_contr();
3343 
3344 	// Minimum exponent
3345 	double mi=icontr[icontr.size()-1].z+jcontr[jcontr.size()-1].z;
3346 	// Maximum exponent
3347 	double ma=icontr[0].z+jcontr[0].z;
3348 
3349 	// Check global minimum and maximum
3350 	if(mi<mine[l])
3351 	  mine[l]=mi;
3352 	if(ma>maxe[l])
3353 	  maxe[l]=ma;
3354       }
3355 
3356     // Add functions to fitting basis set
3357     for(int l=0;l<=lmax;l++) {
3358       std::vector<contr_t> C(1);
3359       C[0].c=1.0;
3360 
3361       // Compute even-tempered formula
3362       double alpha=mine[l];
3363       double beta;
3364       if(nfunc[l]>1)
3365 	beta=pow(maxe[l]/mine[l],1.0/(nfunc[l]-1));
3366       else
3367 	beta=1.0;
3368 
3369       // Add even-tempered functions
3370       for(int n=0;n<nfunc[l];n++) {
3371 	// Compute exponent
3372 	C[0].z=alpha*pow(beta,n);
3373 	fit.add_shell(in,l,true,C);
3374       }
3375     }
3376   }
3377 
3378   // Normalize basis set
3379   fit.coulomb_normalize();
3380   // Form list of unique shell pairs
3381   fit.form_unique_shellpairs();
3382 
3383   return fit;
3384 }
3385 
same_geometry(const BasisSet & rhs) const3386 bool BasisSet::same_geometry(const BasisSet & rhs) const {
3387   if(nuclei.size() != rhs.nuclei.size())
3388     return false;
3389 
3390   for(size_t i=0;i<nuclei.size();i++)
3391     if(!(nuclei[i]==rhs.nuclei[i])) {
3392       //      fprintf(stderr,"Nuclei %i differ!\n",(int) i);
3393       return false;
3394     }
3395 
3396   return true;
3397 }
3398 
same_shells(const BasisSet & rhs) const3399 bool BasisSet::same_shells(const BasisSet & rhs) const {
3400   if(shells.size() != rhs.shells.size())
3401     return false;
3402 
3403   for(size_t i=0;i<shells.size();i++)
3404     if(!(shells[i]==rhs.shells[i])) {
3405       //      fprintf(stderr,"Shells %i differ!\n",(int) i);
3406       return false;
3407     }
3408 
3409   return true;
3410 }
3411 
operator ==(const BasisSet & rhs) const3412 bool BasisSet::operator==(const BasisSet & rhs) const {
3413   return same_geometry(rhs) && same_shells(rhs);
3414 }
3415 
decontract(arma::mat & m) const3416 BasisSet BasisSet::decontract(arma::mat & m) const {
3417   // Decontract basis set. m maps old basis functions to new ones
3418 
3419   // Contraction schemes for the nuclei
3420   std::vector< std::vector<arma::mat> > coeffs(nuclei.size());
3421   std::vector< std::vector<arma::vec> > exps(nuclei.size());
3422   // Is puream used on the shell?
3423   std::vector< std::vector<bool> > puream(nuclei.size());
3424 
3425   // Amount of new basis functions
3426   size_t Nbfnew=0;
3427 
3428   // Collect the schemes. Loop over the nuclei.
3429   for(size_t inuc=0;inuc<nuclei.size();inuc++) {
3430     // Construct an elemental basis set for the nucleus
3431     ElementBasisSet elbas(get_symbol(inuc));
3432 
3433     // Get the shells belonging to this nucleus
3434     std::vector<GaussianShell> shs=get_funcs(inuc);
3435 
3436     // and add the contractions to the elemental basis set
3437     for(size_t ish=0;ish<shs.size();ish++) {
3438       // Angular momentum is
3439       int am=shs[ish].get_am();
3440       // Normalized contraction coefficients
3441       std::vector<contr_t> c=shs[ish].get_contr_normalized();
3442       FunctionShell fsh(am,c);
3443       elbas.add_function(fsh);
3444     }
3445 
3446     // Sanity check - puream must be the same for all shells of the current nucleus with the same am
3447     if(shs.size()>0) {
3448       std::vector<int> pam;
3449       for(int am=0;am<=elbas.get_max_am();am++) {
3450 	// Initialization value
3451 	pam.push_back(-1);
3452 
3453 	for(size_t ish=0;ish<shs.size();ish++) {
3454 	  // Skip if am is not the same
3455 	  if(shs[ish].get_am()!=am)
3456 	    continue;
3457 
3458 	  // Is this the first shell of the type?
3459 	  if(pam[am]==-1)
3460 	    pam[am]=shs[ish].lm_in_use();
3461 	  else if(shs[ish].lm_in_use()!=pam[am]) {
3462 	    ERROR_INFO();
3463 	    throw std::runtime_error("BasisSet::decontract not implemented for mixed pure am on the same center.\n");
3464 	  }
3465 	}
3466 
3467 	// Store the value
3468 	puream[inuc].push_back(pam[am]==1);
3469       }
3470     }
3471 
3472     // Exponents and contraction schemes
3473     for(int am=0;am<=elbas.get_max_am();am++) {
3474       arma::vec z;
3475       arma::mat c;
3476       elbas.get_primitives(z,c,am);
3477       coeffs[inuc].push_back(c);
3478       exps[inuc].push_back(z);
3479 
3480       if(puream[inuc][am])
3481 	Nbfnew+=(2*am+1)*z.size();
3482       else
3483 	Nbfnew+=(am+1)*(am+2)/2*z.size();
3484     }
3485   }
3486 
3487   // Now form the new, decontracted basis set.
3488   BasisSet dec;
3489   // Initialize transformation matrix
3490   m.zeros(Nbfnew,get_Nbf());
3491 
3492   // Add the nuclei
3493   for(size_t i=0;i<nuclei.size();i++)
3494     dec.add_nucleus(nuclei[i]);
3495 
3496   // and the shells.
3497   for(size_t inuc=0;inuc<nuclei.size();inuc++) {
3498     // Get the shells belonging to this nucleus
3499     std::vector<GaussianShell> shs=get_funcs(inuc);
3500 
3501     // Generate the new basis functions. Loop over am
3502     for(int am=0;am<(int) coeffs[inuc].size();am++) {
3503       // First functions with the exponents are
3504       std::vector<size_t> ind0;
3505 
3506       // Add the new shells
3507       for(size_t iz=0;iz<exps[inuc][am].size();iz++) {
3508 	// Index of first function is
3509 	ind0.push_back(dec.get_Nbf());
3510 	// Add the shell
3511 	std::vector<contr_t> hlp(1);
3512 	hlp[0].c=1.0;
3513 	hlp[0].z=exps[inuc][am][iz];
3514 	dec.add_shell(inuc,am,puream[inuc][am],hlp,false);
3515       }
3516 
3517       // and store the coefficients
3518       for(size_t ish=0;ish<shs.size();ish++)
3519 	if(shs[ish].get_am()==am) {
3520 	  // Get the normalized contraction on the shell
3521 	  std::vector<contr_t> ct=shs[ish].get_contr_normalized();
3522 	  // and loop over the exponents
3523 	  for(size_t ic=0;ic<ct.size();ic++) {
3524 
3525 	    // Find out where the exponent is in the new basis set
3526 	    size_t ix;
3527 	    for(ix=0;ix<exps[inuc][am].size();ix++)
3528 	      if(exps[inuc][am][ix]==ct[ic].z)
3529 		// Found exponent
3530 		break;
3531 
3532 	    // Now that we know where the exponent is in the new basis
3533 	    // set, we can just store the coefficients. So, loop over
3534 	    // the functions on the shell
3535 	    for(size_t ibf=0;ibf<shs[ish].get_Nbf();ibf++)
3536 	      m(ind0[ix]+ibf,shs[ish].get_first_ind()+ibf)=ct[ic].c;
3537 	  }
3538 	}
3539     }
3540   }
3541 
3542   // Finalize the basis
3543   dec.finalize();
3544 
3545   return dec;
3546 }
3547 
dummyshell()3548 GaussianShell dummyshell() {
3549   // Set center
3550   coords_t r;
3551   r.x=0.0;
3552   r.y=0.0;
3553   r.z=0.0;
3554 
3555   std::vector<contr_t> C(1);
3556   C[0].c=1.0;
3557   C[0].z=0.0;
3558 
3559   GaussianShell sh(0,false,C);
3560   sh.set_center(r,0);
3561 
3562   return sh;
3563 }
3564 
i_idx(size_t N)3565 std::vector<size_t> i_idx(size_t N) {
3566   std::vector<size_t> ret;
3567   ret.reserve(N);
3568   ret.resize(N);
3569   for(size_t i=0;i<N;i++)
3570     ret[i]=(i*(i+1))/2;
3571   return ret;
3572 }
3573 
construct_basis(BasisSet & basis,const std::vector<nucleus_t> & nuclei,const BasisSetLibrary & baslib)3574 void construct_basis(BasisSet & basis, const std::vector<nucleus_t> & nuclei, const BasisSetLibrary & baslib) {
3575   std::vector<atom_t> atoms(nuclei.size());
3576   for(size_t i=0;i<nuclei.size();i++) {
3577     atoms[i].x=nuclei[i].r.x;
3578     atoms[i].y=nuclei[i].r.y;
3579     atoms[i].z=nuclei[i].r.z;
3580     atoms[i].Q=nuclei[i].Q;
3581     atoms[i].num=nuclei[i].ind;
3582     atoms[i].el=nuclei[i].symbol;
3583   }
3584 
3585   construct_basis(basis,atoms,baslib);
3586 }
3587 
construct_basis(BasisSet & basis,const std::vector<atom_t> & atoms,const BasisSetLibrary & baslib)3588 void construct_basis(BasisSet & basis, const std::vector<atom_t> & atoms, const BasisSetLibrary & baslib) {
3589   // Number of atoms is
3590   size_t Nat=atoms.size();
3591 
3592   // Indices of atoms to decontract basis set for
3593   std::vector<size_t> dec;
3594   bool decall=false;
3595   if(stricmp(settings.get_string("Decontract"),"")!=0) {
3596     // Check for '*'
3597     std::string str=settings.get_string("Decontract");
3598     if(str.size()==1 && str[0]=='*')
3599       decall=true;
3600     else
3601       // Parse and convert to C++ indexing
3602       dec=parse_range(settings.get_string("Decontract"),true);
3603   }
3604 
3605   // Rotation?
3606   bool rotate=settings.get_bool("BasisRotate");
3607   double cutoff=settings.get_double("BasisCutoff");
3608 
3609   // Create basis set
3610   basis=BasisSet(Nat);
3611   // and add atoms to basis set
3612   for(size_t i=0;i<Nat;i++) {
3613     // First we need to add the nucleus itself.
3614     nucleus_t nuc;
3615 
3616     // Get center
3617     nuc.r.x=atoms[i].x;
3618     nuc.r.y=atoms[i].y;
3619     nuc.r.z=atoms[i].z;
3620     // Charge status
3621     nuc.Q=atoms[i].Q;
3622 
3623     // Get symbol in raw form
3624     std::string el=atoms[i].el;
3625 
3626     // Determine if nucleus is BSSE or not
3627     nuc.bsse=false;
3628     if(el.size()>3 && el.substr(el.size()-3,3)=="-Bq") {
3629       // Yes, this is a BSSE nucleus
3630       nuc.bsse=true;
3631       el=el.substr(0,el.size()-3);
3632     }
3633 
3634     // Set symbol
3635     nuc.symbol=el;
3636     // Set charge
3637     nuc.Z=get_Z(el);
3638     // and add the nucleus.
3639     basis.add_nucleus(nuc);
3640 
3641     // Now add the basis functions.
3642     ElementBasisSet elbas;
3643     try {
3644       // Check first if a special set is wanted for given center
3645       elbas=baslib.get_element(el,atoms[i].num+1);
3646     } catch(std::runtime_error & err) {
3647       // Did not find a special basis, use the general one instead.
3648       elbas=baslib.get_element(el,0);
3649     }
3650 
3651     // Decontract set?
3652     bool decon=false;
3653     if(decall)
3654       // All functions decontracted
3655       decon=true;
3656     else
3657       // Check if this center is decontracted
3658       for(size_t j=0;j<dec.size();j++)
3659 	if(i==dec[j])
3660 	  decon=true;
3661 
3662     if(decon)
3663       elbas.decontract();
3664     else if(rotate)
3665       elbas.P_orthogonalize(cutoff);
3666 
3667     basis.add_shells(i,elbas);
3668   }
3669 
3670   // Finalize basis set and convert contractions
3671   basis.finalize(true);
3672 }
3673 
compute_orbitals(const arma::mat & C,const BasisSet & bas,const coords_t & r)3674 arma::vec compute_orbitals(const arma::mat & C, const BasisSet & bas, const coords_t & r) {
3675   // Evaluate basis functions
3676   arma::vec bf=bas.eval_func(r.x,r.y,r.z);
3677 
3678   // Orbitals are
3679   arma::rowvec orbs=arma::trans(bf)*C;
3680 
3681   return arma::trans(orbs);
3682 }
3683 
fermi_lowdin_orbitals(const arma::mat & C,const BasisSet & bas,const arma::mat & r)3684 arma::mat fermi_lowdin_orbitals(const arma::mat & C, const BasisSet & bas, const arma::mat & r) {
3685   if(r.n_cols!=3)
3686     throw std::logic_error("r should have three columns for x, y, z!\n");
3687   if(r.n_rows != C.n_cols)
3688     throw std::logic_error("r should have as many rows as there are orbitals to localize!\n");
3689   if(C.n_rows != bas.get_Nbf())
3690     throw std::logic_error("C does not correspond to basis set!\n");
3691 
3692   // Evaluate basis function matrix: Nbf x nFOD
3693   arma::mat bf(bas.get_Nbf(), r.n_rows);
3694   for(size_t i=0;i<r.n_rows;i++)
3695     bf.col(i)=bas.eval_func(r(i,0),r(i,1),r(i,2));
3696   // Compute the values of the orbitals at the FODs
3697   arma::mat g(bf.t()*C); // nFOD x Nmo
3698   g.print("Orbitals' values at FODs");
3699   // Value of electron density at the FODs
3700   arma::vec n(arma::sqrt(arma::sum(arma::pow(g.t(),2))).t());
3701 
3702   // T matrix (T_{ij}=g_j (r_i) / sqrt(n(r_i)/2))
3703   arma::mat T(r.n_rows, r.n_rows);
3704   for(size_t j=0;j<r.n_rows;j++)
3705     T.col(j)=g.col(j)/n;
3706 
3707   arma::square(n).print("Electron density at FODs");
3708 
3709   // Form unitary transform
3710   arma::mat TTt(T*T.t());
3711   arma::vec tval;
3712   arma::mat tvec;
3713   arma::eig_sym(tval,tvec,TTt);
3714 
3715   arma::mat invsqrtTTt(tvec*arma::diagmat(arma::pow(tval,-0.5))*tvec.t());
3716   arma::mat U(invsqrtTTt*T);
3717 
3718   // Orbitals are rotated as C -> C U^T
3719   U=U.t();
3720 
3721   arma::mat grot(g*U);
3722   grot.print("FLO values at FODs");
3723 
3724   return U;
3725 }
3726 
compute_density(const arma::mat & P,const BasisSet & bas,const coords_t & r)3727 double compute_density(const arma::mat & P, const BasisSet & bas, const coords_t & r) {
3728   // Evaluate basis functions
3729   arma::vec bf=bas.eval_func(r.x,r.y,r.z);
3730 
3731   // Density is
3732   return arma::as_scalar(arma::trans(bf)*P*bf);
3733 }
3734 
compute_density_gradient(const arma::mat & P,const BasisSet & bas,const coords_t & r,double & d,arma::vec & g)3735 void compute_density_gradient(const arma::mat & P, const BasisSet & bas, const coords_t & r, double & d, arma::vec & g) {
3736   // Evaluate basis functions
3737   arma::vec bf=bas.eval_func(r.x,r.y,r.z);
3738   // and gradients
3739   arma::mat grad=bas.eval_grad(r.x,r.y,r.z);
3740 
3741   // Density is
3742   d=arma::as_scalar(arma::trans(bf)*P*bf);
3743   // and the gradient
3744   g=arma::trans(arma::trans(bf)*P*grad);
3745 }
3746 
compute_density_gradient_hessian(const arma::mat & P,const BasisSet & bas,const coords_t & r,double & d,arma::vec & g,arma::mat & h)3747 void compute_density_gradient_hessian(const arma::mat & P, const BasisSet & bas, const coords_t & r, double & d, arma::vec & g, arma::mat & h) {
3748   // Evaluate basis functions
3749   arma::vec bf=bas.eval_func(r.x,r.y,r.z);
3750   // and gradients
3751   arma::mat grad=bas.eval_grad(r.x,r.y,r.z);
3752   // and hessians
3753   arma::mat hess=bas.eval_hess(r.x,r.y,r.z);
3754 
3755   // Density is
3756   d=arma::as_scalar(arma::trans(bf)*P*bf);
3757   // and the gradient
3758   g=arma::trans(arma::trans(bf)*P*grad);
3759 
3760   // First part of hessian is
3761   arma::vec hf=arma::trans(bf)*P*hess;
3762   // and second part
3763   arma::mat hs=arma::trans(grad)*P*grad;
3764 
3765   // Convert to matrix form
3766   h=2.0*(arma::reshape(hf,3,3)+hs);
3767 }
3768 
compute_potential(const arma::mat & P,const BasisSet & bas,const coords_t & r)3769 double compute_potential(const arma::mat & P, const BasisSet & bas, const coords_t & r) {
3770   // Compute nuclear contribution
3771   std::vector<nucleus_t> nucs=bas.get_nuclei();
3772   double nucphi=0.0;
3773   for(size_t i=0;i<nucs.size();i++)
3774     if(!nucs[i].bsse)
3775       nucphi+=nucs[i].Z/norm(r - nucs[i].r);
3776 
3777   // Get potential energy matrix
3778   arma::mat V=bas.potential(r);
3779   // Electronic contribution is (minus sign is already in the definition of the potential matrix)
3780   double elphi=arma::trace(P*V);
3781 
3782   return nucphi+elphi;
3783 }
3784 
compute_elf(const arma::mat & P,const BasisSet & bas,const coords_t & r)3785 double compute_elf(const arma::mat & P, const BasisSet & bas, const coords_t & r) {
3786   // Evaluate basis functions
3787   arma::vec bf=bas.eval_func(r.x,r.y,r.z);
3788   // and gradients
3789   arma::mat grad=bas.eval_grad(r.x,r.y,r.z);
3790 
3791   // Compute kinetic energy term (eqn 9)
3792   double tau=arma::trace(arma::trans(grad)*P*grad);
3793 
3794   // Compute rho and grad rho
3795   double rho=arma::as_scalar(arma::trans(bf)*P*bf);
3796   // and the gradient
3797   arma::vec grho=arma::trans(arma::trans(bf)*P*grad);
3798 
3799   // The D value (eqn 10) is
3800   double D  = tau - 0.25*arma::dot(grho,grho)/rho;
3801   // and the corresponding isotropic D is (eqn 13)
3802   double D0 = 3.0/5.0 * std::pow(6.0*M_PI*M_PI,2.0/3.0) * std::pow(rho,5.0/3.0);
3803 
3804   // from which the chi value is
3805   double chi = D/D0;
3806 
3807   // making the ELF
3808   return 1.0 / (1.0 + chi*chi);
3809 }
3810 
find_identical_shells() const3811 std::vector< std::vector<size_t> > BasisSet::find_identical_shells() const {
3812   // Returned list of identical basis functions
3813   std::vector< std::vector<size_t> > ret;
3814 
3815   // Loop over shells
3816   for(size_t ish=0;ish<shells.size();ish++) {
3817     // Get exponents, contractions and cartesian functions on shell
3818     std::vector<contr_t> shell_contr=shells[ish].get_contr();
3819     std::vector<shellf_t> shell_cart=shells[ish].get_cart();
3820 
3821     // Try to find the shell on the current list of identicals
3822     bool found=false;
3823     for(size_t iident=0;iident<ret.size();iident++) {
3824 
3825       // Check first cartesian part.
3826       std::vector<shellf_t> cmp_cart=shells[ret[iident][0]].get_cart();
3827 
3828       if(shell_cart.size()==cmp_cart.size()) {
3829 	// Default value
3830 	found=true;
3831 
3832 	for(size_t icart=0;icart<shell_cart.size();icart++)
3833 	  if(shell_cart[icart].l!=cmp_cart[icart].l || shell_cart[icart].m!=cmp_cart[icart].m || shell_cart[icart].n!=cmp_cart[icart].n)
3834 	    found=false;
3835 
3836 	// Check that usage of spherical harmonics matches, too
3837 	if(shells[ish].lm_in_use() != shells[ret[iident][0]].lm_in_use())
3838 	  found=false;
3839 
3840 	// If cartesian parts match, check also exponents and contraction coefficients
3841 	if(found) {
3842 	  // Get exponents
3843 	  std::vector<contr_t> cmp_contr=shells[ret[iident][0]].get_contr();
3844 
3845 	  // Check exponents
3846 	  if(shell_contr.size()==cmp_contr.size()) {
3847 	    for(size_t ic=0;ic<shell_contr.size();ic++)
3848 	      if(!(shell_contr[ic]==cmp_contr[ic]))
3849 		found=false;
3850 	  } else
3851 	    found=false;
3852 	}
3853 
3854 	// If everything matches, add the function to the current list.
3855 	if(found) {
3856 	  ret[iident].push_back(ish);
3857 	  // Stop iteration over list of identical functions
3858 	  break;
3859 	}
3860       }
3861     }
3862 
3863     // If the shell was not found on the list of identicals, add it
3864     if(!found) {
3865       std::vector<size_t> hlp;
3866       hlp.push_back(ish);
3867       ret.push_back(hlp);
3868     }
3869   }
3870 
3871   return ret;
3872 }
3873 
orth_diff(const arma::mat & C,const arma::mat & S)3874 double orth_diff(const arma::mat & C, const arma::mat & S) {
3875   // Compute difference from unit overlap
3876   arma::mat d(arma::abs(arma::trans(C)*S*C-arma::eye<arma::mat>(C.n_cols,C.n_cols)));
3877   // Get maximum error
3878   return arma::max(arma::max(d));
3879 }
3880 
orth_diff(const arma::cx_mat & C,const arma::mat & S)3881 double orth_diff(const arma::cx_mat & C, const arma::mat & S) {
3882   // Compute difference from unit overlap
3883   arma::mat d(arma::abs(arma::trans(C)*S*C - arma::eye<arma::mat>(C.n_cols,C.n_cols)));
3884   // Get maximum error
3885   return arma::max(arma::max(d));
3886 }
3887 
check_orth(const arma::mat & C,const arma::mat & S,bool verbose,double thr)3888 void check_orth(const arma::mat & C, const arma::mat & S, bool verbose, double thr) {
3889   if(!C.n_cols)
3890     throw std::logic_error("Error in check_orth: no orbitals!\n");
3891   if(C.n_rows != S.n_rows) {
3892     std::ostringstream oss;
3893     oss << "Error in check_orth: got " << C.n_rows << " x " << C.n_cols << " C and " << S.n_rows << " x " << S.n_cols << " S!\n";
3894     throw std::logic_error(oss.str());
3895   }
3896 
3897   // Compute difference from unit overlap
3898   arma::mat d(arma::abs(arma::trans(C)*S*C-arma::eye<arma::mat>(C.n_cols,C.n_cols)));
3899   // Get maximum error
3900   double maxerr(arma::max(arma::max(d)));
3901 
3902   if(verbose) {
3903     printf("Maximum deviation from orthogonality is %e.\n",maxerr);
3904     fflush(stdout);
3905   }
3906 
3907   if(maxerr>thr) {
3908     // Clean up
3909     for(size_t j=0;j<d.n_cols;j++)
3910       for(size_t i=0;i<d.n_cols;i++)
3911 	if(fabs(d(i,j))<10*DBL_EPSILON)
3912 	  d(i,j)=0.0;
3913 
3914     d.save("MOovl_diff.dat",arma::raw_ascii);
3915 
3916     std::ostringstream oss;
3917     oss << "Generated orbitals are not orthonormal! Maximum deviation from orthonormality is " << maxerr <<".\nCheck the used LAPACK implementation.\n";
3918     throw std::runtime_error(oss.str());
3919   }
3920 }
3921 
check_orth(const arma::cx_mat & C,const arma::mat & S,bool verbose,double thr)3922 void check_orth(const arma::cx_mat & C, const arma::mat & S, bool verbose, double thr) {
3923   if(!C.n_cols)
3924     throw std::logic_error("Error in check_orth: no orbitals!\n");
3925   if(C.n_rows != S.n_rows) {
3926     std::ostringstream oss;
3927     oss << "Error in check_orth: got " << C.n_rows << " x " << C.n_cols << " C and " << S.n_rows << " x " << S.n_cols << " S!\n";
3928     throw std::logic_error(oss.str());
3929   }
3930 
3931   arma::mat d(arma::abs(arma::trans(C)*S*C - arma::eye<arma::cx_mat>(C.n_cols,C.n_cols)));
3932   double maxerr(arma::max(arma::max(d)));
3933 
3934   if(verbose) {
3935     printf("Maximum deviation from orthogonality is %e.\n",maxerr);
3936     fflush(stdout);
3937   }
3938 
3939   if(maxerr>thr) {
3940     // Clean up
3941     for(size_t i=0;i<d.n_cols;i++)
3942       for(size_t j=0;j<d.n_cols;j++)
3943 	if(std::abs(d(i,j))<10*DBL_EPSILON)
3944 	  d(i,j)=0.0;
3945 
3946     d.save("OMOovl_diff.dat",arma::raw_ascii);
3947 
3948     std::ostringstream oss;
3949     oss << "Generated orbitals are not orthonormal! Maximum deviation from orthonormality is " << maxerr <<".\nCheck the used LAPACK implementation.\n";
3950     throw std::runtime_error(oss.str());
3951   }
3952 }
3953 
construct_IAO_wrk(const BasisSet & basis,const arma::Mat<T> & C,std::vector<std::vector<size_t>> & idx,bool verbose,std::string minbaslib)3954 template<typename T> static arma::Mat<T> construct_IAO_wrk(const BasisSet & basis, const arma::Mat<T> & C, std::vector< std::vector<size_t> > & idx, bool verbose, std::string minbaslib) {
3955   // Get minao library
3956   BasisSetLibrary minao;
3957   minao.load_basis(minbaslib);
3958   // Default settings
3959   Settings set;
3960   set.add_scf_settings();
3961   set.set_bool("Verbose",verbose);
3962   // Can't rotate basis or it will break the contractions
3963   set.set_bool("BasisRotate",false);
3964 
3965   // Construct minimal basis set
3966   BasisSet minbas;
3967   construct_basis(minbas,basis.get_nuclei(),minao);
3968 
3969   // Get indices
3970   idx.clear();
3971   idx.resize(minbas.get_Nnuc());
3972   for(size_t inuc=0;inuc<minbas.get_Nnuc();inuc++) {
3973     // Get shells on nucleus
3974     std::vector<GaussianShell> sh=minbas.get_funcs(inuc);
3975     // Store indices
3976     for(size_t si=0;si<sh.size();si++)
3977       for(size_t fi=0;fi<sh[si].get_Nbf();fi++)
3978 	idx[inuc].push_back(sh[si].get_first_ind()+fi);
3979   }
3980 
3981   // Calculate S1, S12, S2, and S21
3982   arma::mat S1(basis.overlap());
3983   arma::mat S2(minbas.overlap());
3984 
3985   arma::mat S12(basis.overlap(minbas));
3986   arma::mat S21(arma::trans(S12));
3987 
3988   // and inverse matrices
3989   arma::mat S1inv_h(BasOrth(S1));
3990   arma::mat S2inv_h(BasOrth(S2));
3991   // Need to be OK for canonical as well
3992   arma::mat S1inv(S1inv_h*arma::trans(S1inv_h));
3993   arma::mat S2inv(S2inv_h*arma::trans(S2inv_h));
3994 
3995   // Compute Ctilde.
3996   arma::Mat<T> Ctilde(S1inv*S12*S2inv*S21*C);
3997 
3998   // and orthonormalize it
3999   Ctilde=orthonormalize(S1,Ctilde);
4000 
4001   // "Density matrices"
4002   arma::Mat<T> P(C*arma::trans(C));
4003   arma::Mat<T> Pt(Ctilde*arma::trans(Ctilde));
4004 
4005   // Identity matrix
4006   arma::mat unit(S1.n_rows,S1.n_cols);
4007   unit.eye();
4008 
4009   // Compute the non-orthonormal IAOs.
4010   arma::Mat<T> A=P*S1*Pt*S12 + (unit-P*S1)*(unit-Pt*S1)*S1inv*S12;
4011 
4012   // and orthonormalize them
4013   return orthonormalize(S1,A);
4014 }
construct_IAO(const BasisSet & basis,const arma::mat & C,std::vector<std::vector<size_t>> & idx,bool verbose,std::string minbaslib)4015 arma::mat construct_IAO(const BasisSet & basis, const arma::mat & C, std::vector< std::vector<size_t> > & idx, bool verbose, std::string minbaslib) {
4016   return construct_IAO_wrk<double>(basis,C,idx,verbose,minbaslib);
4017 }
construct_IAO(const BasisSet & basis,const arma::cx_mat & C,std::vector<std::vector<size_t>> & idx,bool verbose,std::string minbaslib)4018 arma::cx_mat construct_IAO(const BasisSet & basis, const arma::cx_mat & C, std::vector< std::vector<size_t> > & idx, bool verbose, std::string minbaslib) {
4019   return construct_IAO_wrk< std::complex<double> >(basis,C,idx,verbose,minbaslib);
4020 }
4021 
block_m(const arma::mat & F,const arma::ivec & mv)4022 arma::mat block_m(const arma::mat & F, const arma::ivec & mv) {
4023   arma::mat Fnew(F);
4024   Fnew.zeros();
4025   for(arma::sword m=0;m<=mv.max();m++) {
4026     if(m==0) {
4027       // Indices are
4028       arma::uvec idx(arma::find(mv==m));
4029       Fnew(idx,idx)=F(idx,idx);
4030     } else {
4031       // Indices for plus and minus values are
4032       arma::uvec pidx(arma::find(mv==m));
4033       arma::uvec nidx(arma::find(mv==-m));
4034 
4035       // m=m and m=-m are equivalent
4036       Fnew(pidx,pidx)=0.5*(F(pidx,pidx)+F(nidx,nidx));
4037       Fnew(nidx,nidx)=Fnew(pidx,pidx);
4038     }
4039   }
4040 
4041   return Fnew;
4042 }
4043 
m_norm(const arma::mat & C,const arma::ivec & mv)4044 arma::mat m_norm(const arma::mat & C, const arma::ivec & mv) {
4045   arma::mat osym(mv.max()-mv.min()+1,C.n_cols);
4046   for(arma::sword m=mv.min();m<=mv.max();m++) {
4047     arma::uvec idx(arma::find(mv==m));
4048     for(size_t io=0;io<C.n_cols;io++) {
4049       arma::vec cv(C.col(io));
4050       osym(m-mv.min(),io)=arma::norm(cv(idx),"fro");
4051     }
4052   }
4053 
4054   return osym;
4055 }
4056 
m_classify(const arma::mat & C,const arma::ivec & mv)4057 arma::ivec m_classify(const arma::mat & C, const arma::ivec & mv) {
4058   // Orbital class
4059   arma::ivec oclass(C.n_cols);
4060 
4061   // Get symmetries
4062   arma::mat osym(m_norm(C,mv));
4063 
4064   //osym.print("Orbital symmetry");
4065 
4066   // Maximum angular momentum is
4067   if(osym.n_rows%2 != 1) throw std::logic_error("Invalid number of rows!\n");
4068   int maxam((osym.n_rows-1)/2);
4069 
4070   for(size_t io=0;io<C.n_cols;io++) {
4071     arma::vec s(osym.col(io));
4072 
4073     // Get maximum
4074     arma::uword idx;
4075     s.max(idx);
4076 
4077     // This corresponds to the m value
4078     int m=idx;
4079     m-=maxam;
4080 
4081     oclass(io)=m;
4082   }
4083 
4084   return oclass;
4085 }
4086