1 /*
2  *                This source code is part of
3  *
4  *                     E  R  K  A  L  E
5  *                             -
6  *                       HF/DFT from Hel
7  *
8  * Written by Susi Lehtola, 2010-2012
9  * Copyright (c) 2010-2012, 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 #include "completeness_profile.h"
18 #include "optimize_completeness.h"
19 #include "../basis.h"
20 #include "../linalg.h"
21 #include "../timer.h"
22 #include "../tempered.h"
23 
24 extern "C" {
25 #include <gsl/gsl_blas.h>
26 #include <gsl/gsl_multimin.h>
27 #include <gsl/gsl_sf_legendre.h>
28 }
29 
30 #ifdef _OPENMP
31 #include <omp.h>
32 #endif
33 
34 // Maximum number of functions allowed in completeness optimization
35 #define NFMAX 70
36 
get_exponents(const gsl_vector * xv,const completeness_scan_t & p)37 arma::vec get_exponents(const gsl_vector *xv, const completeness_scan_t & p) {
38   // Check parameter consistency
39   size_t np=p.nfull;
40   if(p.neven)
41     np++;
42   if(np != xv->size) {
43     std::ostringstream oss;
44     oss << "Expected " << np << " parameters but was given " << xv->size << "!\n";
45     throw std::runtime_error(oss.str());
46   }
47 
48   // One-sided exponents
49   size_t nside=p.neven + p.nfull;
50   arma::vec A(nside);
51   A.zeros();
52 
53   // Plug in the even-tempered exponents
54   if(p.neven) {
55     double lge=gsl_vector_get(xv,0);
56 
57     if(!p.odd)
58       // In the even case, the first exponent is placed halfway
59       A(0)=lge/2.0;
60     else
61       A(0)=lge;
62 
63     // Other exponents
64     for(size_t i=1;i < p.neven;i++)
65       A(i)=A(0)+i*lge;
66   }
67 
68   // x index offset
69   size_t xoff = p.neven ? 1 : 0;
70   size_t eoff = p.neven;
71 
72   // Fully optimized exponents
73   if(p.nfull) {
74     if(p.neven)
75       // First exponent placed at
76       A(eoff)=gsl_vector_get(xv,xoff)+A(p.neven-1);
77     else
78       A(0)=gsl_vector_get(xv,xoff);
79 
80     // Other exponents
81     for(size_t i=1;i < p.nfull;i++) {
82       A(i+eoff)=gsl_vector_get(xv,i+xoff)+A(i+eoff-1);
83     }
84   }
85 
86   /*
87   printf("x:");
88   for(size_t i=0;i<xv->size;i++)
89     printf(" % e",gsl_vector_get(xv,i));
90   printf("\n");
91 
92   A.subvec(0,eoff-1).t().print("Even-tempered");
93   A.subvec(eoff,A.n_elem-1).t().print("Full");
94   A.t().print("A");
95   */
96 
97   // Full set of exponents
98   size_t nexp=2*A.n_elem;
99   if(p.odd)
100     nexp++;
101 
102   arma::vec exps(nexp);
103   exps.zeros();
104 
105   // Smallest exponents
106   for(size_t i=0;i<A.n_elem;i++)
107     exps(i)=std::pow(10.0,-A(A.n_elem-1-i));
108 
109   // Bigger exponents
110   if(p.odd) {
111     exps(A.n_elem)=1.0;
112     exps.subvec(A.n_elem+1,2*A.n_elem)=arma::exp10(A);
113   } else
114     exps.subvec(A.n_elem,2*A.n_elem-1)=arma::exp10(A);
115 
116   //  arma::sort(arma::log10(exps)).t().print("Exponents");
117   //  throw std::runtime_error("Debug");
118 
119   return exps;
120 }
121 
self_overlap(const arma::vec & z,int am)122 arma::mat self_overlap(const arma::vec & z, int am) {
123   // Compute self-overlap
124   size_t N=z.n_elem;
125   arma::mat Suv(N,N);
126 #ifdef _OPENMP
127 #pragma omp parallel for
128 #endif
129   for(size_t i=0;i<N;i++)
130     for(size_t j=0;j<=i;j++) {
131       Suv(i,j)=pow(4.0*z(i)*z(j)/pow(z(i)+z(j),2),am/2.0+0.75);
132       Suv(j,i)=Suv(i,j);
133     }
134 
135   return Suv;
136 }
137 
completeness_profile(const gsl_vector * x,void * params)138 arma::vec completeness_profile(const gsl_vector * x, void * params) {
139   // Get parameters
140   completeness_scan_t *par=(completeness_scan_t *) params;
141 
142   // Get exponents
143   arma::vec z=get_exponents(x,*par);
144 
145   // Get self-overlap
146   arma::mat Suv=self_overlap(z,par->am);
147 
148   // and its half inverse matrix
149   arma::mat Sinvh=BasOrth(Suv,false);
150 
151   // Get overlap of primitives with scanning terms
152   arma::mat amu=overlap(par->scanexp,z,par->am);
153 
154   // Compute intermediary result, Np x N
155   arma::mat J=amu*Sinvh;
156 
157   // The completeness profile
158   size_t N=par->scanexp.size();
159   arma::vec Y(N);
160   for(size_t i=0;i<N;i++)
161     Y[i]=arma::dot(J.row(i),J.row(i));
162 
163   return Y;
164 }
165 
compl_mog(const gsl_vector * x,void * params)166 double compl_mog(const gsl_vector * x, void * params) {
167   // Get parameters
168   completeness_scan_t *p=(completeness_scan_t *) params;
169 
170   // Get completeness profile
171   arma::vec Y=completeness_profile(x,params);
172 
173   // Compute MOG.
174   double phi=0.0;
175 
176   size_t nint=0;
177 
178   switch(p->n) {
179 
180   case(1):
181     for(size_t i=1;i<Y.n_elem-1;i+=2) {
182       // Compute differences from unity
183       double ld=1.0-Y[i-1];
184       double md=1.0-Y[i  ];
185       double rd=1.0-Y[i+1];
186       // Increment profile measure
187       phi+=ld+4.0*md+rd;
188       nint++;
189     }
190     break;
191 
192   case(2):
193     for(size_t i=1;i<Y.n_elem-1;i+=2) {
194       // Compute differences from unity
195       double ld=1.0-Y[i-1];
196       double md=1.0-Y[i  ];
197       double rd=1.0-Y[i+1];
198       // Increment profile measure
199       phi+=ld*ld+4.0*md*md+rd*rd;
200       nint++;
201     }
202     break;
203 
204   default:
205     ERROR_INFO();
206     throw std::runtime_error("Value of n not supported!\n");
207   }
208   // Plug in normalization factors
209   phi/=6.0*nint;
210 
211   return phi;
212 }
213 
compl_mog_df(const gsl_vector * x,void * params,gsl_vector * g)214 void compl_mog_df(const gsl_vector * x, void * params, gsl_vector * g) {
215   // Step size to use. Since we are working in logarithmic space, this
216   // should be always OK.
217   double h=1e-6;
218 
219   // It'd be nice to parallellize here, but it seems it causes
220   // problems with large amounts of exponents... and I have no idea
221   // why.
222 
223   // Helper for parameters
224   gsl_vector *tmp=gsl_vector_alloc(x->size);
225 
226   // Compute gradient using central difference
227   for(size_t ip=0; ip < x->size; ip++) {
228     // Initialize helper vector
229     gsl_vector_memcpy(tmp,x);
230 
231     // Original parameter value
232     double x0=gsl_vector_get(x,ip);
233 
234     // RH value
235     gsl_vector_set(tmp,ip,x0+h);
236     double rh=compl_mog(tmp,params);
237 
238     // LH value
239     gsl_vector_set(tmp,ip,x0-h);
240     double lh=compl_mog(tmp,params);
241 
242     // Gradient value is
243     gsl_vector_set(g,ip, (rh-lh)/(2.0*h));
244   }
245 
246   // Free memory
247   gsl_vector_free(tmp);
248 }
249 
compl_mog_fdf(const gsl_vector * x,void * params,double * f,gsl_vector * g)250 void compl_mog_fdf(const gsl_vector * x, void * params, double * f, gsl_vector * g) {
251   *f=compl_mog(x,params);
252   compl_mog_df(x,params,g);
253 }
254 
optimize_completeness(int am,double min,double max,int Nf,int n,bool verbose,double * mog,int nfull)255 arma::vec optimize_completeness(int am, double min, double max, int Nf, int n, bool verbose, double *mog, int nfull) {
256   return optimize_completeness_cg(am,min,max,Nf,n,verbose,mog,nfull);
257   //  return optimize_completeness_simplex(am,min,max,Nf,n,verbose,mog,nfull);
258 }
259 
eventempered_exps(double len,int Nf)260 arma::vec eventempered_exps(double len, int Nf) {
261   // Spacing to use is
262   double h=len/(Nf+1);
263   // Starting point is at
264   double start=-len/2.0+h;
265 
266   return eventempered_set(std::pow(10.0,start),std::pow(10.0,h),Nf);
267 }
268 
get_start(arma::vec exps,const completeness_scan_t & p,gsl_vector * x)269 void get_start(arma::vec exps, const completeness_scan_t & p, gsl_vector * x) {
270   if((p.neven && x->size != p.nfull + 1) || (!p.neven && x->size != p.nfull)) {
271     throw std::runtime_error("Parameter sizes do not match!\n");
272   }
273 
274   // Sort into increasing order
275   exps=arma::sort(exps);
276 
277   // Convert to logarithms
278   exps=arma::log10(exps);
279 
280   //  exps.t().print("Starting point");
281 
282   // Get the free parameter part
283   int Np=exps.n_elem/2;
284   exps=exps.subvec(exps.n_elem-Np,exps.n_elem-1);
285 
286   //  exps.t().print("Free parameter part");
287 
288   if(p.neven) {
289     // Even-tempered parameter
290     gsl_vector_set(x,0,exps(1)-exps(0));
291     // Free exponents
292     for(size_t fi=0;fi<p.nfull;fi++)
293       gsl_vector_set(x,1+fi,exps(p.neven+fi)-exps(p.neven+fi-1));
294   } else {
295     // Free exponents
296     gsl_vector_set(x,0,exps(0));
297     for(size_t fi=1;fi<p.nfull;fi++)
298       gsl_vector_set(x,fi,exps(fi)-exps(fi-1));
299   }
300 
301   //  arma::log10(get_exponents(x,p)).t().print("Final exponents");
302 }
303 
optimize_completeness_simplex(int am,double min,double max,int Nf,int n,bool verbose,double * mog,int nfull)304 arma::vec optimize_completeness_simplex(int am, double min, double max, int Nf, int n, bool verbose, double *mog, int nfull) {
305   // Optimized exponents
306   arma::vec exps;
307 
308   // Length of interval is
309   double len=max-min;
310 
311   // Parameters for the optimization.
312   completeness_scan_t pars;
313   // Angular momentum
314   pars.am=am;
315   // Moment to optimize
316   pars.n=n;
317   // Scanning exponents
318   pars.scanexp=get_scanning_exponents(-len/2.0,len/2.0,50*Nf+1);
319 
320   // Odd amount of exponents?
321   pars.odd=Nf%2;
322   // Amount of fully optimized exponents
323   pars.nfull=std::min(Nf/2,nfull);
324   // Amount of even-tempered exponents
325   pars.neven=Nf/2-pars.nfull;
326 
327   if(Nf<1) {
328     throw std::runtime_error("Cannot completeness-optimize less than one primitive.\n");
329 
330   } else if(Nf==1) {
331     // Only single exponent at 10^0 = 1.0.
332     gsl_vector x;
333     x.size=0;
334 
335     // Get exponent
336     exps=get_exponents(&x,pars);
337     // and the mog
338     if(mog!=NULL)
339       *mog=compl_mog(&x,(void *) &pars);
340 
341   } else {
342     // Time minimization
343     Timer tmin;
344 
345     // Optimized profile will be always symmetric around the midpoint,
346     // so we can use this to reduce the amount of degrees of freedom in
347     // the optimization. For even amount of exponents, the mid exponent
348     // is pinned to the midway of the interval.
349     int Ndof=pars.nfull;
350     if(pars.neven)
351       Ndof++;
352 
353     // Maximum number of iterations
354     size_t maxiter = 10000;
355 
356     // GSL stuff
357     const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex2;
358     gsl_multimin_fminimizer *s = NULL;
359     gsl_multimin_function minfunc;
360 
361     size_t iter = 0;
362     int status;
363     double size;
364 
365     // Starting point exponents
366     arma::vec sp;
367     /*    if(nfull>2) {
368       // Run optimization with one free exponent
369       double m;
370       sp=optimize_completeness_simplex(am,-len/2.0,len/2.0,Nf,n,false,&m,1);
371       } else */
372       // Start from even-tempered formula
373       sp=eventempered_exps(len,Nf);
374 
375     // Parameters
376     gsl_vector *x = gsl_vector_alloc (Ndof);
377     get_start(sp,pars,x);
378 
379     /* Set initial step sizes to 0.1 */
380     gsl_vector *ss = gsl_vector_alloc (Ndof);
381     gsl_vector_set_all (ss, 0.1);
382 
383     /* Initialize method and iterate */
384     minfunc.n = Ndof;
385     minfunc.f = compl_mog;
386     minfunc.params = (void *) &pars;
387 
388     s = gsl_multimin_fminimizer_alloc (T, Ndof);
389     gsl_multimin_fminimizer_set (s, &minfunc, x, ss);
390 
391     // Progress timer
392     Timer t;
393 
394     // Legend
395     if(verbose) {
396       printf("Optimizing tau_%i for a=[%.3f ... %.3f] of %c shell with %i exponents.\n",n,min,max,shell_types[am],Nf);
397       if(pars.odd)
398 	printf("One exponent is fixed at the center of the interval.\n");
399       if(pars.neven)
400 	printf("%i exponents at the both sides of the center are represented by an even-tempered formula.\n",(int)(pars.neven));
401       if(pars.nfull)
402 	printf("%i exponents at both edges are fully optimized.\n",(int)(pars.nfull));
403       printf("Using the simplex method.\n");
404       printf("\n");
405 
406       printf("%4s  %12s  %12s","iter","tau","size");
407       char num[80];
408       for(int i=0;i<Ndof;i++) {
409 	sprintf(num,"lg par%i",i+1);
410 	printf(" %9s",num);
411       }
412       printf("\n");
413     }
414 
415     do
416       {
417 	iter++;
418 	status = gsl_multimin_fminimizer_iterate(s);
419 
420 	if (status)
421 	  break;
422 
423 	size = gsl_multimin_fminimizer_size (s);
424 	status = gsl_multimin_test_size (size, 1e-6);
425 
426 	if (status == GSL_SUCCESS && verbose)
427 	  printf ("converged to minimum at\n");
428 
429 	if(verbose) {
430 	  t.set();
431 	  printf("%4u  %e  %e",(unsigned int) iter, pow(s->fval,1.0/n), size);
432 	  for(int i=0;i<Ndof;i++)
433 	    printf(" % 9.5f",gsl_vector_get(s->x,i));
434 	  printf("\n");
435 
436 	  // print_gradient(s->x,(void *) &pars);
437 	}
438       }
439     while (status == GSL_CONTINUE && iter < maxiter);
440 
441     // Save mog
442     if(mog!=NULL)
443       *mog=pow(s->fval,1.0/n);
444 
445     // The optimized exponents in descending order
446     exps=arma::sort(get_exponents(s->x,pars),"descend");
447 
448     gsl_vector_free(x);
449     gsl_vector_free(ss);
450     gsl_multimin_fminimizer_free (s);
451 
452     if(verbose)
453       printf("\nMinimization completed in %s.\n",tmin.elapsed().c_str());
454   }
455 
456   // Move starting point from 0 to start
457   exps*=std::pow(10.0,min+len/2.0);
458 
459   return exps;
460 }
461 
optimize_completeness_cg(int am,double min,double max,int Nf,int n,bool verbose,double * mog,int nfull)462 arma::vec optimize_completeness_cg(int am, double min, double max, int Nf, int n, bool verbose, double *mog, int nfull) {
463   // Optimized exponents
464   arma::vec exps;
465 
466   // Length of interval is
467   double len=max-min;
468 
469   // Parameters for the optimization.
470   completeness_scan_t pars;
471   // Angular momentum
472   pars.am=am;
473   // Moment to optimize
474   pars.n=n;
475   // Scanning exponents
476   pars.scanexp=get_scanning_exponents(-len/2.0,len/2.0,50*Nf+1);
477 
478   // Odd amount of exponents?
479   pars.odd=Nf%2;
480   // Amount of fully optimized exponents
481   pars.nfull=std::min(Nf/2,nfull);
482   // Amount of even-tempered exponents
483   pars.neven=Nf/2-pars.nfull;
484 
485   if(Nf<1) {
486     throw std::runtime_error("Cannot completeness-optimize less than one primitive.\n");
487 
488   } else if(Nf==1) {
489     // Only single exponent at 10^0 = 1.0.
490     gsl_vector x;
491     x.size=0;
492 
493     // Get exponent
494     exps=get_exponents(&x,pars);
495     // and the mog
496     if(mog!=NULL)
497       *mog=compl_mog(&x,(void *) &pars);
498 
499   } else {
500     // Time minimization
501     Timer tmin;
502 
503     // Optimized profile will be always symmetric around the midpoint,
504     // so we can use this to reduce the amount of degrees of freedom in
505     // the optimization. For even amount of exponents, the mid exponent
506     // is pinned to the midway of the interval.
507     int Ndof=pars.nfull;
508     if(pars.neven)
509       Ndof++;
510 
511     // Maximum number of iterations
512     size_t maxiter = 10000;
513 
514     // Use Fletcher-Reeves gradient, which works here better than Polak-Ribiere or BFGS.
515     const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_conjugate_fr;
516     // const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_steepest_descent;
517     // const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_vector_bfgs2;
518     gsl_multimin_fdfminimizer *s = NULL;
519     gsl_multimin_function_fdf minfunc;
520 
521     size_t iter = 0;
522     int status;
523 
524     // Starting point
525     arma::vec sp;
526     /*    if(nfull>2) {
527       // Run optimization with one free exponent
528       double m;
529       sp=optimize_completeness_cg(am,-len/2.0,len/2.0,Nf,n,false,&m,1);
530     } else
531     */
532       // Start from even-tempered formula
533       sp=eventempered_exps(len,Nf);
534 
535     // Set starting point
536     gsl_vector *x = gsl_vector_alloc (Ndof);
537     get_start(sp,pars,x);
538 
539     /* Initialize method and iterate */
540     minfunc.n = Ndof;
541     minfunc.f = compl_mog;
542     minfunc.df = compl_mog_df;
543     minfunc.fdf = compl_mog_fdf;
544     minfunc.params = (void *) &pars;
545 
546     s = gsl_multimin_fdfminimizer_alloc (T, Ndof);
547 
548     // Initial step size is 1e-3, and the line minimization parameter is 1e-4
549     gsl_multimin_fdfminimizer_set (s, &minfunc, x, 1e-4, 1e-4);
550 
551     // Progress timer
552     Timer t;
553 
554     // Legend
555     if(verbose) {
556       printf("Optimizing tau_%i for a=[%.3f ... %.3f] of %c shell with %i exponents.\n",n,min,max,shell_types[am],Nf);
557       if(pars.odd)
558 	printf("One exponent is fixed at the center of the interval.\n");
559       if(pars.neven)
560 	printf("%i exponents at the both sides of the center are represented by an even-tempered formula.\n",(int)(pars.neven));
561       if(pars.nfull)
562 	printf("%i exponents at both edges are fully optimized.\n",(int)(pars.nfull));
563       printf("Using Fletcher-Reeves conjugate gradients.\n");
564       printf("\n");
565 
566       printf("%4s  %12s  %12s","iter","tau","grad norm");
567       char num[80];
568       for(int i=0;i<Ndof;i++) {
569 	sprintf(num,"lg par%i",i+1);
570 	printf(" %9s",num);
571       }
572       printf("\n");
573     }
574 
575     do
576       {
577 	iter++;
578 	status = gsl_multimin_fdfminimizer_iterate(s);
579 
580 	if (status)
581 	  break;
582 
583 	status = gsl_multimin_test_gradient (s->gradient, 1e-8);
584 
585 	if (status == GSL_SUCCESS && verbose)
586 	  printf ("converged to minimum at\n");
587 
588 	if(verbose) {
589 	  t.set();
590 	  printf("%4u  %e  %e",(unsigned int) iter, pow(s->f,1.0/n), gsl_blas_dnrm2(s->gradient));
591 	  for(int i=0;i<Ndof;i++)
592 	    printf(" % 9.5f",gsl_vector_get(s->x,i));
593 	  printf("\n");
594 
595 	  // print_gradient(s->x,(void *) &pars);
596 	}
597       }
598     while (status == GSL_CONTINUE && iter < maxiter);
599 
600 
601     //if (status != GSL_SUCCESS)
602     //    printf ("encountered error \"%s\"\n",gsl_strerror(status));
603 
604 
605     // Save mog
606     if(mog!=NULL)
607       *mog=pow(s->f,1.0/n);
608 
609     // The optimized exponents in descending order
610     exps=arma::sort(get_exponents(s->x,pars),"descend");
611 
612     gsl_vector_free(x);
613     gsl_multimin_fdfminimizer_free (s);
614 
615     if(verbose)
616       printf("\nMinimization completed in %s.\n",tmin.elapsed().c_str());
617   }
618 
619   // Move starting point from 0 to start
620   exps*=std::pow(10.0,min+len/2.0);
621 
622   return exps;
623 }
624 
maxwidth(int am,double tol,int nexp,int nval,int nfull)625 double maxwidth(int am, double tol, int nexp, int nval, int nfull) {
626   // Dummy value
627   double width=-1.0;
628   maxwidth_exps(am,tol,nexp,width,nval,nfull);
629   return width;
630 }
631 
maxwidth_exps(int am,double tol,int nexp,double & width,int nval,int nfull)632 arma::vec maxwidth_exps(int am, double tol, int nexp, double & width, int nval, int nfull) {
633   // Error check
634   if(nexp<=0) {
635     arma::vec exps;
636     return exps;
637   }
638 
639   if(tol<MINTAU) {
640     printf("Renormalized CO tolerance to %e.\n",MINTAU);
641     tol=MINTAU;
642   }
643 
644   // Left value - vanishing plateau width, i.e. tau too small
645   double left=0.0;
646   double lval=0.0;
647 
648   // Right value - pretty big plateau, i.e. tau too big
649   double right=0.5*nexp;
650   double rval;
651 
652   // Check that right value is OK
653   arma::vec rexps=optimize_completeness(am,0.0,right,nexp,nval,false,&rval,nfull);
654   while(rval<tol) {
655     // Must have tau too big here
656     left=right;
657     right*=2.0;
658     rexps=optimize_completeness(am,0.0,right,nexp,nval,false,&rval,nfull);
659   }
660 
661   // Check for unitialized left value
662   if(left==0.0) {
663     left=right;
664     lval=rval;
665     arma::vec lexps;
666     while(lval>tol) {
667       // Must have tau too small here
668       left/=2.0;
669       lexps=optimize_completeness(am,0.0,left,nexp,nval,false,&lval,nfull);
670     }
671   }
672 
673   // Refine until in linear regime
674   size_t ii;
675   const size_t iimax=100;
676   for(ii=0;ii<iimax;ii++) {
677     // Compute midpoint value
678     double middle=(right+left)/2.0;
679     double mval;
680     arma::vec mexps=optimize_completeness(am,0.0,middle,nexp,nval,false,&mval,nfull);
681 
682     // Check for linear regime: predicted value is
683     double mpred=lval + (middle-left)/(right-left)*(rval-lval);
684     //    printf("mpred accuracy: %e\n",fabs((mpred-mval)/mval));
685 
686     // and we are converged if the correction term is O(eps)
687     bool converged = (fabs((mpred-mval)) <= sqrt(DBL_EPSILON)*fabs(mval));
688 
689     // Update limits
690     if(mval<tol) {
691       left=middle;
692       lval=mval;
693     } else if(mval>tol) {
694       right=middle;
695       rval=mval;
696     }
697 
698     // Stop if converged
699     if(converged)
700       break;
701   }
702 
703   if(ii==iimax)
704     throw std::runtime_error("Error finding limits in maxwidth_exps.\n");
705 
706   // Interpolate to the exact value
707   width=left + (tol-lval)/(rval-lval)*(right-left);
708 
709   // Exponents and realized value of tau are
710   double tau;
711   arma::vec exps=optimize_completeness(am,0.0,width,nexp,nval,false,&tau,nfull);
712 
713   //  printf("Interpolation from w = %e .. %e with tau = %e .. %e yielded w = %e, tau = %e.\n",left,right,lval,rval,*width,tau);
714 
715   return exps;
716 }
717 
move_exps(const arma::vec & exps,double x)718 arma::vec move_exps(const arma::vec & exps, double x) {
719   return exps*std::pow(10.0,x);
720 }
721 
722 /// Perform completeness-optimization of exponents
get_exponents(int am,double start,double end,double tol,int nval,bool verbose,int nfull)723 arma::vec get_exponents(int am, double start, double end, double tol, int nval, bool verbose, int nfull) {
724   // Exponents
725   arma::vec exps;
726   bool succ=false;
727 
728   // Work array
729   std::vector<arma::vec> expwrk;
730   std::vector<double> mog;
731 
732   // Sanity check
733   if(tol<MINTAU) {
734     if(verbose)
735       printf("Renormalized CO tolerance to %e.\n",MINTAU);
736     tol=MINTAU;
737   }
738 
739   // Allocate work memory
740 #ifdef _OPENMP
741   int nth=omp_get_max_threads();
742 #else
743   int nth=1;
744 #endif
745 
746   expwrk.resize(nth);
747   mog.resize(nth);
748 
749   // Do completeness optimization
750   int nf=1;
751   if(verbose)
752     printf("\tNf  tau_%i\n",nval);
753 
754   while(nf<=NFMAX) {
755 
756 #ifdef _OPENMP
757 #pragma omp parallel
758 #endif
759     {
760 
761 #ifdef _OPENMP
762       int ith=omp_get_thread_num();
763 #else
764       int ith=0;
765 #endif
766 
767 #ifdef _OPENMP
768 #pragma omp for ordered
769 #endif
770       for(int mf=nf;mf<nf+nth;mf++) {
771 	// Get exponents.
772 	mog[ith]=-1.0;
773 	expwrk[ith]=optimize_completeness(am,start,end,mf,nval,false,&(mog[ith]),nfull);
774 #ifdef _OPENMP
775 #pragma omp ordered
776 #endif
777 	if(verbose) {
778 	  if(mog[ith]<(1+sqrt(DBL_EPSILON))*tol)
779 	    printf("\t%2i *%e\n",mf,mog[ith]);
780 	  else
781 	    printf("\t%2i  %e\n",mf,mog[ith]);
782 	}
783       }
784     }
785 
786     // Did we achieve the wanted mog?
787     for(int i=0;i<nth;i++) {
788       if(mog[i]<(1+sqrt(DBL_EPSILON))*tol) {
789 	// Tolerance achieved. Save exponents.
790 	exps=expwrk[i];
791 	succ=true;
792 	break;
793       }
794     }
795 
796     // Need another break clause here.
797     if(succ)
798       break;
799 
800     // Increase nf
801     nf+=nth;
802   }
803 
804   if(!succ) {
805     fprintf(stderr,"Could not get exponents for %c shell with tol=%e.\n",shell_types[am],tol);
806     throw std::runtime_error("Unable to achieve wanted tolerance.\n");
807   } else if(verbose)
808     printf("Wanted tolerance achieved with %i exponents.\n",(int) exps.size());
809 
810   return exps;
811 }
812