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