1 /*
2  *                This source code is part of
3  *
4  *                     E  R  K  A  L  E
5  *                             -
6  *                       HF/DFT from Hel
7  *
8  * Written by Susi Lehtola, 2010-2011
9  * Copyright (c) 2010-2011, Susi Lehtola
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  */
16 
17 #include "basislibrary.h"
18 #include "basis.h"
19 #include "checkpoint.h"
20 #include "dftfuncs.h"
21 #include "dftgrid.h"
22 #include "elements.h"
23 #include "emd/emd.h"
24 #include "find_molecules.h"
25 #include "lebedev.h"
26 #include "linalg.h"
27 #include "mathf.h"
28 #include "xyzutils.h"
29 #include "properties.h"
30 #include "scf.h"
31 #include "settings.h"
32 #include "stringutil.h"
33 #include "timer.h"
34 #include "lbfgs.h"
35 
36 // Needed for libint init
37 #include "eriworker.h"
38 
39 #include <armadillo>
40 #include <cstdio>
41 #include <cstdlib>
42 #include <cfloat>
43 
44 #ifdef _OPENMP
45 #include <omp.h>
46 #endif
47 
48 #ifdef SVNRELEASE
49 #include "version.h"
50 #endif
51 
52 // Settings
53 Settings settings;
54 
55 // Optimization helpers
56 typedef struct {
57   // Atoms in the system
58   std::vector<atom_t> atoms;
59   // Basis set library
60   BasisSetLibrary baslib;
61   // Indices of dofs
62   std::vector<size_t> dofidx;
63 
64   // Numeric gradient?
65   bool numgrad;
66   // Step size for numeric gradient
67   double step;
68   // Order of stencil
69   int npoints;
70 } opthelper_t;
71 
72 enum minimizer {
73   // Fletcher-Reeves conjugate gradients
74   gCGFR,
75   // Polak-Ribiere conjugate gradient
76   gCGPR,
77   // Broyden-Fletcher-Goldfarb-Shanno
78   gBFGS,
79   // Steepest descent
80   gSD
81 };
82 
83 // Convergence criteria
84 enum convergence {
85   LOOSE,
86   NORMAL,
87   TIGHT,
88   VERYTIGHT
89 };
90 
get_displacement(const std::vector<atom_t> & g,const std::vector<atom_t> & o,double & dmax,double & drms)91 void get_displacement(const std::vector<atom_t> & g, const std::vector<atom_t> & o, double & dmax, double & drms) {
92   if(g.size()!=o.size()) {
93     ERROR_INFO();
94     throw std::runtime_error("Cannot compare different geometries.\n");
95   }
96 
97   dmax=0.0;
98   drms=0.0;
99 
100   // Compute displacements
101   for(size_t i=0;i<g.size();i++) {
102     double dsq=pow(g[i].x-o[i].x,2) + pow(g[i].y-o[i].y,2) + pow(g[i].z-o[i].z,2);
103 
104     drms+=dsq;
105     if(dsq>dmax)
106       dmax=dsq;
107   }
108 
109   // Take square roots here
110   drms=sqrt(drms/g.size());
111   dmax=sqrt(dmax);
112 }
113 
calculate_projection(const std::vector<atom_t> & g,const std::vector<atom_t> & o,const arma::vec & f,const std::vector<size_t> & dofidx)114 double calculate_projection(const std::vector<atom_t> & g, const std::vector<atom_t> & o, const arma::vec & f, const std::vector<size_t> & dofidx) {
115   double dE=0.0;
116   for(size_t i=0;i<g.size();i++) {
117     size_t j=dofidx[i];
118     dE+=f(3*i)*(g[j].x-o[j].x) + f(3*i+1)*(g[j].y-o[j].y) + f(3*i+2)*(g[j].z-o[j].z);
119   }
120   return dE;
121 }
122 
get_forces(const arma::vec & g,double & fmax,double & frms)123 void get_forces(const arma::vec & g, double & fmax, double & frms) {
124   fmax=0.0;
125   frms=0.0;
126 
127   for(size_t i=0;i<g.n_elem/3;i++) {
128     double x=g(3*i);
129     double y=g(3*i+1);
130     double z=g(3*i+2);
131 
132     double fsq=x*x + y*y + z*z;
133 
134     frms+=fsq;
135     if(fsq>fmax)
136       fmax=fsq;
137   }
138 
139   frms=sqrt(3*frms/g.n_elem);
140   fmax=sqrt(fmax);
141 }
142 
get_atoms(const arma::vec & x,const opthelper_t & opts)143 std::vector<atom_t> get_atoms(const arma::vec & x, const opthelper_t & opts) {
144   // Update atomic positions
145   std::vector<atom_t> atoms(opts.atoms);
146 
147   for(size_t i=0;i<opts.dofidx.size();i++) {
148     size_t j=opts.dofidx[i];
149     atoms[j].x=x(3*i);
150     atoms[j].y=x(3*i+1);
151     atoms[j].z=x(3*i+2);
152   }
153 
154   return atoms;
155 }
156 
157 enum calcd {
158   // Full calculation
159   FULLCALC,
160   // Just the forces
161   FORCECALC,
162   // Just the energy
163   ECALC,
164   // Nothing
165   NOCALC
166 };
167 
168 
run_calc(const BasisSet & basis,bool force)169 void run_calc(const BasisSet & basis, bool force) {
170   bool pz=false;
171   try {
172     pz=settings.get_bool("PZ");
173   } catch(std::runtime_error &) {
174   }
175 
176   if(pz)
177     throw std::logic_error("Analytic forces not implemented for PZ-SIC!\n");
178 
179   Timer t;
180 
181   // Nothing to load - run full calculation.
182   calculate(basis,force);
183   if(force) {
184     Checkpoint solchk(settings.get_string("SaveChk"),false);
185     arma::vec f;
186     solchk.read("Force",f);
187     interpret_force(f).t().print("Analytic force");
188 
189     double fmax, frms;
190     get_forces(f, fmax, frms);
191     printf("Max force = %.3e, rms force = %.3e, evaluated in %s\n",fmax,frms,t.elapsed().c_str());
192   }
193 }
194 
run_calc_num(const BasisSet & basis,bool force,int npoints,double h)195 void run_calc_num(const BasisSet & basis, bool force, int npoints, double h) {
196   std::string savename=settings.get_string("SaveChk");
197   std::string tempname=".tempchk";
198 
199   // Run calculation
200   Timer t;
201   calculate(basis,false);
202   if(force)
203     printf("Energy evaluated in %s\n",t.elapsed().c_str());
204 
205   // All we needed was the energy.
206   if(!force)
207     return;
208   // Turn off verbose setting
209   settings.set_bool("Verbose",false);
210 
211   // We have converged the energy, next compute force by finite
212   // differences.
213   arma::mat fm;
214   fm.zeros(3,basis.get_Nnuc());
215 
216   // Nuclear coordinates. Take the transpose so that (x,y,z) are
217   // stored consecutively in memory
218   arma::mat nuccoord(basis.get_nuclear_coords().t());
219 
220   // Get the stencil
221   if(npoints<2)
222     throw std::runtime_error("Invalid stencil, must be >=2.\n");
223   // Points to evaluate at
224   arma::vec dx=arma::linspace<arma::vec>(-(npoints-1)/2.0,(npoints-1)/2.0,npoints);
225   // Weights at the points
226   arma::vec w;
227   {
228     arma::mat c(dx.n_elem,2);
229     stencil(0.0,dx,c);
230     w=c.col(1);
231 
232     // Eliminate any small weights
233     for(size_t i=0;i<w.n_elem;i++)
234       if(std::abs(w(i))<DBL_EPSILON*npoints) {
235 	dx.subvec(i,dx.n_elem-2)=dx.subvec(i+1,dx.n_elem-1);
236 	dx.resize(dx.n_elem-1);
237 
238 	w.subvec(i,w.n_elem-2)=w.subvec(i+1,w.n_elem-1);
239 	w.resize(w.n_elem-1);
240       }
241 
242     static bool printout=true;
243     if(printout) {
244       // Only print out stencil once
245       printf("\n%13s %13s\n","xsten","wsten");
246       for(size_t i=0;i<w.n_elem;i++)
247 	printf("% e % e\n",dx(i),w(i));
248       printout=false;
249     }
250 
251     // Put in spacing
252     dx*=h;
253     w/=h;
254   }
255 
256   t.set();
257 
258   // Loop over degrees of freedom
259   size_t Ndof=3*basis.get_Nnuc()-3;
260   printf("Calculating %i displacements with %i point stencil\n",(int) Ndof,(int) dx.n_elem);
261   fflush(stdout);
262   for(size_t idof=0;idof<Ndof;idof++) {
263     // No x or y force
264     if(settings.get_bool("LinearSymmetry"))
265       if(idof%3!=2)
266 	continue;
267 
268     Timer tdof;
269     // Energies
270     arma::vec E(dx.n_elem);
271 
272     for(size_t isten=0;isten<dx.n_elem;isten++) {
273       // Coordinates of nuclei.
274       arma::mat dcoord(nuccoord);
275       dcoord[idof]+=dx(isten);
276       // Take the back-transpose
277       dcoord=dcoord.t();
278 
279       // Basis set
280       BasisSet dbas(basis);
281       dbas.set_nuclear_coords(dcoord);
282 
283       // Energy
284       energy_t en;
285 
286       Settings settings0(settings);
287       settings.set_string("LoadChk",savename);
288       settings.set_string("SaveChk",tempname);
289       {
290 	// Run calculation
291 	calculate(dbas,false);
292 	Checkpoint solchk(settings.get_string("SaveChk"),false);
293 	// Current energy is
294 	solchk.read(en);
295 	E(isten)=en.E;
296       }
297       remove(tempname.c_str());
298       // Restore original settings
299       settings=settings0;
300     }
301 
302     // Calculate force: - grad E
303     fm(idof)=-arma::dot(w,E);
304 
305     printf("%i/%i done in %s\n",(int) idof+1,(int) Ndof,tdof.elapsed().c_str());
306     fflush(stdout);
307   }
308 
309   // Force on last nucleus is just the negative of the sum of all the
310   // forces on the other nuclei
311   fm.col(fm.n_cols-1)=-arma::sum(fm.cols(0,fm.n_cols-2),1);
312   //fm.print("Numerical forces");
313 
314   // Open the checkpoint in write mode, don't truncate it
315   Checkpoint chkpt(savename,true,false);
316   arma::vec f(arma::vectorise(fm));
317   chkpt.write("Force",f);
318   chkpt.close();
319 
320   interpret_force(f).t().print("Numerical force");
321   double fmax, frms;
322   get_forces(f, fmax, frms);
323   printf("Max force = %.3e, rms force = %.3e, evaluated in %s\n",fmax,frms,t.elapsed().c_str());
324 }
325 
calculate(const arma::vec & x,const opthelper_t & p,double & E,arma::vec & g,bool force)326 void calculate(const arma::vec & x, const opthelper_t & p, double & E, arma::vec & g, bool force) {
327   Timer t;
328 
329   // Get the atomic positions
330   std::vector<atom_t> atoms=get_atoms(x,p);
331   //  print_xyz(atoms);
332 
333   // Construct basis set
334   BasisSet basis;
335   construct_basis(basis,atoms,p.baslib);
336 
337   // Perform the electronic structure calculation
338   if(p.numgrad)
339     run_calc_num(basis,force,p.npoints,p.step);
340   else
341     run_calc(basis,force);
342 
343   // Solution checkpoint
344   Checkpoint solchk(settings.get_string("SaveChk"),false);
345 
346   // Energy
347   energy_t en;
348   solchk.read(en);
349   E=en.E;
350 
351   // Force
352   arma::vec f;
353   if(force) {
354     solchk.read("Force",f);
355 
356     // Set components
357     g.zeros(3*p.dofidx.size());
358     for(size_t i=0;i<p.dofidx.size();i++) {
359       size_t j=p.dofidx[i];
360       g(3*i)=-f(3*j);
361       g(3*i+1)=-f(3*j+1);
362       g(3*i+2)=-f(3*j+2);
363     }
364   }
365 }
366 
getchk(size_t n)367 std::string getchk(size_t n) {
368   std::ostringstream oss;
369   oss << "geomcalc_" << getpid() << "_" << n << ".chk";
370   return oss.str();
371 }
372 
373 // Helper for line search
374 typedef struct {
375   // Step length
376   double s;
377   // Energy
378   double E;
379   // Calculation
380   size_t icalc;
381 } linesearch_t;
382 
operator <(const linesearch_t & lh,const linesearch_t & rh)383 bool operator<(const linesearch_t & lh, const linesearch_t & rh) {
384   return lh.s < rh.s;
385 }
386 
print_header()387 void print_header() {
388 #ifdef _OPENMP
389   printf("ERKALE - Geometry optimization from Hel, OpenMP version, running on %i cores.\n",omp_get_max_threads());
390 #else
391   printf("ERKALE - Geometry optimization from Hel, serial version.\n");
392 #endif
393   print_copyright();
394   print_license();
395 #ifdef SVNRELEASE
396   printf("At svn revision %s.\n\n",SVNREVISION);
397 #endif
398   print_hostname();
399 }
400 
find_minimum(const std::vector<linesearch_t> & steps,double & Emin,double & smin,size_t & imin)401 void find_minimum(const std::vector<linesearch_t> & steps, double & Emin, double & smin, size_t & imin) {
402   Emin=steps[0].E;
403   smin=steps[0].s;
404   imin=0;
405   for(size_t i=1;i<steps.size();i++)
406     if(steps[i].E < Emin) {
407       Emin=steps[i].E;
408       smin=steps[i].s;
409       imin=i;
410     }
411 }
412 
main_guarded(int argc,char ** argv)413 int main_guarded(int argc, char **argv) {
414   print_header();
415 
416   if(argc!=2) {
417     printf("Usage: $ %s runfile\n",argv[0]);
418     return 0;
419   }
420 
421   // Initialize libint
422   init_libint_base();
423   // Initialize libderiv
424   init_libderiv_base();
425 
426   Timer tprog;
427   tprog.print_time();
428 
429   // Parse settings
430   settings.add_scf_settings();
431   settings.add_string("SaveChk","File to use as checkpoint","erkale.chk");
432   settings.add_string("LoadChk","File to load old results from","");
433   settings.add_bool("ForcePol","Force polarized calculation",false);
434   settings.add_string("Optimizer","Optimizer to use: CGFR, CGPR, BFGS, SD","BFGS");
435   settings.add_int("CGReset","Reset CG direction to SD every N steps", 5);
436   settings.add_int("MaxSteps","Maximum amount of geometry steps",256);
437   settings.add_string("Criterion","Convergence criterion to use: LOOSE, NORMAL, TIGHT, VERYTIGHT","NORMAL");
438   settings.add_string("OptMovie","xyz movie to store progress in","optimize.xyz");
439   settings.add_string("Result","File to save optimized geometry in","optimized.xyz");
440   settings.set_string("Logfile","erkale_geom.log");
441   settings.add_bool("NumGrad","Use finite-difference gradient?",false);
442   settings.add_int("Stencil","Order of finite-difference stencil for numgrad",2);
443   settings.add_double("Stepsize","Finite-difference stencil step size",1e-6);
444   settings.add_double("LineStepFac","Line search step length factor",sqrt(10.0));
445   settings.add_double("InitialStep","Initial step size in bohr",0.2);
446   settings.add_double("ParaThr","Threshold for parabolic fit interval in bohr (dimer only)",1e-2);
447   settings.add_double("BoundThr","Threshold for bond distance of bound molecules (dimer only)",10.0);
448   settings.add_double("FuncThr","Threshold for function value change in search (dimer only)",1e-6);
449   settings.parse(std::string(argv[1]),true);
450   settings.print();
451 
452   // Don't try saving or loading Cholesky integrals
453   settings.set_int("CholeskyMode",0);
454 
455   int maxiter=settings.get_int("MaxSteps");
456   std::string optmovie=settings.get_string("OptMovie");
457   std::string result=settings.get_string("Result");
458   bool numgrad=settings.get_bool("NumGrad");
459   int stencil=settings.get_int("Stencil");
460   double step=settings.get_double("Stepsize");
461   double steplen=settings.get_double("InitialStep");
462   double Rcutoff=settings.get_double("BoundThr");
463   double fac=settings.get_double("LineStepFac");
464   double parathr=settings.get_double("ParaThr");
465   double functhr=settings.get_double("FuncThr");
466   int cgreset=settings.get_int("CGReset");
467 
468   // Interpret optimizer
469   enum minimizer alg;
470   std::string method=settings.get_string("Optimizer");
471   if(stricmp(method,"CGFR")==0)
472     alg=gCGFR;
473   else if(stricmp(method,"CGPR")==0)
474     alg=gCGPR;
475   else if(stricmp(method,"BFGS")==0)
476     alg=gBFGS;
477   else if(stricmp(method,"SD")==0)
478     alg=gSD;
479   else {
480     ERROR_INFO();
481     throw std::runtime_error("Unknown optimization method.\n");
482   }
483 
484   // Interpret optimizer
485   enum convergence crit;
486   method=settings.get_string("Criterion");
487   if(stricmp(method,"LOOSE")==0)
488     crit=LOOSE;
489   else if(stricmp(method,"NORMAL")==0)
490     crit=NORMAL;
491   else if(stricmp(method,"TIGHT")==0)
492     crit=TIGHT;
493   else if(stricmp(method,"VERYTIGHT")==0)
494     crit=VERYTIGHT;
495   else {
496     ERROR_INFO();
497     throw std::runtime_error("Unknown optimization method.\n");
498   }
499 
500   // Redirect output?
501   std::string logfile=settings.get_string("Logfile");
502   if(stricmp(logfile,"stdout")!=0) {
503     // Redirect stdout to file
504     FILE *outstream=freopen(logfile.c_str(),"w",stdout);
505     if(outstream==NULL) {
506       ERROR_INFO();
507       throw std::runtime_error("Unable to redirect output!\n");
508     }
509 
510     fprintf(stderr,"\n");
511     // Print out the header in the logfile as well
512     print_header();
513   }
514 
515   // Read in atoms.
516   std::string atomfile=settings.get_string("System");
517   const std::vector<atom_t> origgeom=load_xyz(atomfile,!settings.get_bool("InputBohr"));
518   std::vector<atom_t> atoms(origgeom);
519 
520   // Are any atoms fixed?
521   std::vector<size_t> dofidx;
522   for(size_t i=0;i<atoms.size();i++) {
523     bool fixed=false;
524 
525     if(atoms[i].el.size()>3)
526       if(stricmp(atoms[i].el.substr(atoms[i].el.size()-3),"-Fx")==0) {
527 	fixed=true;
528 	atoms[i].el=atoms[i].el.substr(0,atoms[i].el.size()-3);
529       }
530 
531     // Add to degrees of freedom
532     if(!fixed)
533       dofidx.push_back(i);
534   }
535 
536   // Read in basis set
537   BasisSetLibrary baslib;
538   std::string basfile=settings.get_string("Basis");
539   baslib.load_basis(basfile);
540   printf("\n");
541 
542   // Save to output
543   save_xyz(atoms,"Initial configuration",optmovie,false);
544 
545   // Minimizer options
546   opthelper_t pars;
547   pars.atoms=atoms;
548   pars.baslib=baslib;
549   pars.dofidx=dofidx;
550   pars.numgrad=numgrad;
551   pars.npoints=stencil+1;
552   pars.step=step;
553 
554   /* Starting point */
555   arma::vec x(3*dofidx.size());
556   for(size_t i=0;i<dofidx.size();i++) {
557     x(3*i)=atoms[dofidx[i]].x;
558     x(3*i+1)=atoms[dofidx[i]].y;
559     x(3*i+2)=atoms[dofidx[i]].z;
560   }
561 
562   // Steps taken in optimization, index of reference calc
563   size_t ncalc=0, iref=0;
564   // Stored checkpoints
565   std::vector<size_t> chkstore;
566 
567   // Energy and force, as well as old energy and force
568   double E=0.0, Eold;
569   arma::vec f, fold;
570   // Current and old search direction
571   arma::vec sd, sdold;
572 
573   // Old geometry
574   std::vector<atom_t> oldgeom(atoms);
575 
576   // Helper
577   LBFGS bfgs;
578 
579   // Save calculation to
580   Settings settings0(settings);
581   std::string savechk0(settings.get_string("SaveChk"));
582   settings.set_string("SaveChk",getchk(ncalc));
583 
584   if(atoms.size()==2) {
585     // Specialized code for diatomic molecules
586     double R0=round(dist(atoms[0],atoms[1])/steplen)*steplen;
587 
588     // Nuclear coordinates
589     x.zeros(6);
590     x(2)=-R0/2;
591     x(5)=R0/2;
592 
593     // Calculate energy at the starting point
594     calculate(x,pars,E,f,false);
595     chkstore.push_back(ncalc);
596     ncalc++;
597     // Turn off verbose setting for any later calcs
598     settings.set_bool("Verbose",false);
599     try {
600       // Also, don't localize, since it would screw up the converged guess
601       settings.set_string("PZloc","false");
602       // And don't run stability analysis, since we are only doing small displacements
603       settings.set_int("PZstab",0);
604     } catch(std::runtime_error &) {
605     }
606 
607     // Bond distance search structure
608     std::vector<linesearch_t> steps;
609     linesearch_t p;
610     p.s=0;
611     p.icalc=ncalc-1;
612     p.E=E;
613     steps.push_back(p);
614 
615     printf("\nDimer calculation\n");
616     printf("%8s %14s %12s\n","R (bohr)","Energy","Delta R");
617     printf("%8.5f % 14.6f\n",R0,E);
618 
619     fprintf(stderr,"%8s %14s %12s\n","R (bohr)","Energy","Delta R");
620     fprintf(stderr,"%8.5f % 14.6f\n",R0,E);
621 
622     // Search direction is bond length
623     sd.zeros(6);
624     sd(2)=-0.5;
625     sd(5)=0.5;
626 
627     // Minimum energy and index
628     double Emin;
629     double smin;
630     size_t imin;
631 
632     // Go closer
633     size_t ileft=0;
634     do {
635       p.s=-1.0*((++ileft)*steplen);
636       p.icalc=ncalc++;
637 
638       // Load reference from earlier calculation
639       settings.set_string("LoadChk",getchk(p.icalc-1));
640       // Save calculation to
641       settings.set_string("SaveChk",getchk(p.icalc));
642 
643       calculate(x+p.s*sd,pars,p.E,f,false);
644       chkstore.push_back(p.icalc);
645       steps.insert(steps.begin(),p);
646 
647       printf("%8.5f % 14.6f\n",R0+p.s,p.E);
648       fflush(stdout);
649 
650       fprintf(stderr,"%8.5f % 14.6f\n",R0+p.s,p.E);
651       fflush(stderr);
652 
653       // Find the minimum energy
654       find_minimum(steps,Emin,smin,imin);
655     } while(imin < 2);
656 
657     bool bound=true;
658 
659     size_t iright=0;
660     do {
661       p.s=(++iright)*steplen;
662       p.icalc=ncalc++;
663 
664       double Rlen=R0+p.s;
665       // System is not bound
666       if(Rlen>Rcutoff) {
667         bound=false;
668         break;
669       }
670 
671       // Load reference from earlier calculation
672       if(iright==1)
673         settings.set_string("LoadChk",getchk(0));
674       else
675         settings.set_string("LoadChk",getchk(p.icalc-1));
676       // Save calculation to
677       settings.set_string("SaveChk",getchk(p.icalc));
678       calculate(x+p.s*sd,pars,p.E,f,false);
679       chkstore.push_back(p.icalc);
680       steps.push_back(p);
681 
682       printf("%8.5f % 14.6f\n",R0+p.s,p.E);
683       fflush(stdout);
684 
685       fprintf(stderr,"%8.5f % 14.6f\n",R0+p.s,p.E);
686       fflush(stderr);
687 
688       // Find the minimum energy
689       find_minimum(steps,Emin,smin,imin);
690     } while(imin >= steps.size()-1);
691 
692     // Threshold
693     double deltaRthr;
694     switch(crit) {
695     case(LOOSE):
696       deltaRthr=1.0e-2;
697       break;
698     case(NORMAL):
699       deltaRthr=1.0e-3;
700       break;
701     case(TIGHT):
702       deltaRthr=1.0e-4;
703       break;
704     case(VERYTIGHT):
705       deltaRthr=1.0e-5;
706       break;
707 
708     default:
709       throw std::logic_error("Case not handled.\n");
710     }
711 
712     // Minimum is bracketed, proceed with search
713     double deltaR;
714     bool convd=false;
715     int nrefined=0;
716     if(bound) {
717       while(true) {
718         // Sort the steps in length
719         std::sort(steps.begin(),steps.end());
720 
721         // Find minimum position
722         find_minimum(steps,Emin,smin,imin);
723 
724         // Do parabolic fit
725         double a=steps[imin-1].s;
726         double b=steps[imin].s;
727         double c=steps[imin+1].s;
728         double fa=steps[imin-1].E;
729         double fb=steps[imin].E;
730         double fc=steps[imin+1].E;
731 
732         // Is there any variation in the function value?
733         double fmin=std::min(std::min(fa,fb),fc);
734         double fmax=std::max(std::max(fa,fb),fc);
735         if(fmax-fmin<=functhr) {
736           printf("Function value converged within threshold\n");
737           convd=true;
738           break;
739         }
740 
741         // New position is at
742         double xs = b - 0.5*((b-a)*(b-a)*(fb-fc) - (b-c)*(b-c)*(fb-fa))/((b-a)*(fb-fc) - (b-c)*(fb-fa));
743 
744         // Length of search intervals are
745         double len1(b-a);
746         double len2(c-b);
747         deltaR=std::max(len1,len2);
748 
749         // Is this within the search interval?
750         bool parafit = (deltaR <= parathr) && xs>=a && xs<=c;
751         if(!parafit) {
752           // No, use golden ratio search
753           const double golden =(sqrt(5.0)-1.0)/2.0;
754           // Split larger interval
755           if(len1 > len2) {
756             xs = a + golden*(b-a);
757           } else {
758             xs = b + golden*(c-b);
759           }
760         }
761 
762         // Determine point closest to the minimum
763         size_t rmin=0;
764         double dRmin=std::abs(steps[0].s-xs);
765         for(size_t i=1;i<steps.size();i++) {
766           double dR=std::abs(steps[i].s-xs);
767           if(dR<dRmin) {
768             rmin=i;
769             dRmin=dR;
770           }
771         }
772         settings.set_string("LoadChk",getchk(rmin));
773         p.s=xs;
774         p.icalc=ncalc++;
775         settings.set_string("SaveChk",getchk(p.icalc));
776         chkstore.push_back(p.icalc);
777         calculate(x+p.s*sd,pars,p.E,f,false);
778         steps.push_back(p);
779 
780         printf("%8.5f % 14.6f %e\n",R0+p.s,p.E,deltaR);
781         fflush(stdout);
782 
783         fprintf(stderr,"%8.5f % 14.6f %e\n",R0+p.s,p.E,deltaR);
784         fflush(stderr);
785 
786         if(deltaR < deltaRthr) {
787           convd=true;
788           break;
789         }
790 
791         // Increment iteration count
792         nrefined++;
793         if(nrefined>=maxiter) {
794           break;
795         }
796       }
797     }
798     if(convd)
799       printf("Converged\n");
800     else {
801       printf("Failed to attain converge\n");
802       return 1;
803     }
804 
805     // Update geometry
806     find_minimum(steps,Emin,smin,imin);
807     x+=sd*smin;
808 
809   } else {
810 
811     // Calculate energy at the starting point
812     calculate(x,pars,E,f,false);
813     chkstore.push_back(ncalc);
814     ncalc++;
815     // Turn off verbose setting for any later calcs
816     settings.set_bool("Verbose",false);
817     try {
818       // Also, don't localize, since it would screw up the converged guess
819       settings.set_string("PZloc","false");
820       // And don't run stability analysis, since we are only doing small displacements
821       settings.set_int("PZstab",0);
822     } catch(std::runtime_error &) {
823     }
824 
825     printf("\n\nStarting geometry optimization\n");
826     printf("%4s %18s %9s %9s\n","iter","E","fmax","frms");
827 
828     fprintf(stderr,"\n%3s %18s %10s %10s %10s %10s %10s %10s %s\n", "it", "E", "dE", "dEfrac", "dmax ", "drms ", "fmax ", "frms ", "t");
829     fflush(stderr);
830 
831     for(int iiter=0;iiter<maxiter;iiter++) {
832       Timer titer;
833 
834       // Store old values of gradient and search direction
835       fold=f;
836       sdold=sd;
837 
838       // Load reference from earlier calculation
839       settings.set_string("LoadChk",getchk(iref));
840       // Save calculation to
841       settings.set_string("SaveChk",getchk(ncalc));
842 
843       // Calculate energy and force at current position
844       calculate(x,pars,E,f,true);
845       chkstore.push_back(ncalc);
846       // Change reference
847       iref=ncalc;
848       // Increment step value
849       ncalc++;
850 
851       // Store old value of energy
852       Eold=E;
853 
854       // Save geometry step
855       {
856         char comment[80];
857         sprintf(comment,"Step %i",(int) iiter);
858         save_xyz(get_atoms(x,pars),comment,optmovie,true);
859       }
860 
861       // Search direction
862       sd=-f;
863       std::string steptype="SD";
864 
865       if(iiter>0) {
866         if((alg==gCGPR || alg==gCGFR) && (iiter%cgreset!=0)) {
867           // Polak-Ribière
868           double gamma;
869           if(alg==gCGPR) {
870             gamma=arma::dot(f,f-fold)/arma::dot(fold,fold);
871             steptype="CGPR";
872           } else {
873             gamma=arma::dot(f,f)/arma::dot(fold,fold);
874             steptype="CGFR";
875           }
876 
877           arma::vec sdnew=sd+gamma*sdold;
878           if(arma::dot(f,fold)>=0.2*arma::dot(fold,fold)) {
879             steptype="Powell restart - SD";
880           } else
881             sd=sdnew;
882 
883         } else if(alg==gBFGS) {
884           // Update BFGS
885           bfgs.update(x,f);
886           // Get search direction
887           sd=-bfgs.solve();
888           steptype="BFGS";
889         }
890 
891         // Check we are still going downhill / BFGS direction isn't nan
892         if(!(arma::dot(sd,f)<=0)) {
893           steptype+=": Bad search direction. SD";
894           sd=-f;
895         }
896       }
897 
898       // Get forces
899       double fmax, frms;
900       get_forces(f, fmax, frms);
901 
902       // Macroiteration status
903       printf("\n%s step\n",steptype.c_str());
904       printf("%4i % 18.10f %.3e %.3e\n",iiter,E,fmax,frms);
905       fflush(stdout);
906 
907       // Legend
908       printf("\t%2s %12s %13s\n","i","step","dE");
909 
910       // Do a line search on the search direction by bracketing the
911       // minimum
912       std::vector<linesearch_t> steps;
913 
914       // Initial entry is
915       {
916         // Step length and energy
917         linesearch_t p;
918         p.s=0.0;
919         p.E=E;
920         p.icalc=iref;
921         steps.push_back(p);
922       }
923 
924       // First, try the step length as is
925       {
926         Timer ts;
927 
928         // Step length and energy
929         linesearch_t p;
930         p.s=steplen;
931         p.icalc=ncalc;
932 
933         double Et;
934         arma::vec ft;
935         settings.set_string("LoadChk",getchk(iref));
936         settings.set_string("SaveChk",getchk(ncalc));
937         calculate(x+p.s*sd,pars,Et,ft,false);
938         iref=ncalc;
939         chkstore.push_back(ncalc);
940         ncalc++;
941 
942         p.E=Et;
943         steps.push_back(p);
944 
945         printf("\t%2i %e % e %s\n",(int) steps.size()-1,p.s,Et-E,ts.elapsed().c_str());
946         fflush(stdout);
947       }
948 
949       // Minimum energy and index
950       double Emin;
951       size_t imin;
952 
953       while(true) {
954         // Sort the steps in length
955         std::sort(steps.begin(),steps.end());
956 
957         // Find the minimum energy
958         double smin;
959         find_minimum(steps,Emin,smin,imin);
960 
961         // Where is the minimum?
962         if(imin==0 || imin==1 || imin==steps.size()-1) {
963           Timer ts;
964 
965           linesearch_t p;
966           if(imin==0 || (steps.size()>2 && imin==1)) {
967             // Need smaller step
968             p.s=steps[1].s/fac;
969 
970             if(p.s*arma::max(arma::abs(sd)) < DBL_EPSILON) {
971               printf("Step length too small, stopping line search.\n");
972               break;
973             }
974           } else {
975             // Need bigger step
976             p.s=steps[imin].s*fac;
977           }
978           p.icalc=ncalc;
979 
980           double Et;
981           arma::vec ft;
982           settings.set_string("LoadChk",getchk(steps[imin].icalc));
983           settings.set_string("SaveChk",getchk(ncalc));
984           calculate(x+p.s*sd,pars,Et,ft,false);
985           chkstore.push_back(ncalc);
986           ncalc++;
987 
988           p.E=Et;
989           steps.push_back(p);
990           printf("\t%2i %e % e %s\n",(int) steps.size()-1,p.s,Et-E,ts.elapsed().c_str());
991           fflush(stdout);
992 
993         } else {
994           // Optimum is somewhere in the middle
995           break;
996         }
997       }
998 
999       // Find the minimum energy
1000       double smin;
1001       find_minimum(steps,Emin,smin,imin);
1002       if(imin == 0) {
1003         printf("Energy not decreasing.\n");
1004         break;
1005       }
1006 
1007       {
1008         // Interpolate: A b = y
1009         arma::mat A(3,3);
1010         arma::vec y(3);
1011         for(size_t i=0;i<3;i++) {
1012           A(i,0)=1.0;
1013           A(i,1)=steps[imin+i-1].s;
1014           A(i,2)=std::pow(A(i,1),2);
1015 
1016           y(i)=steps[imin+i-1].E;
1017         }
1018 
1019         arma::mat b;
1020         if(arma::solve(b,A,y) && b(2)>sqrt(DBL_EPSILON)) {
1021           // Success in solution and parabola gives minimum.
1022 
1023           // The minimum of the parabola is at
1024           double x0=-b(1)/(2*b(2));
1025 
1026           // Is this an interpolation?
1027           if(A(0,1) < x0 && x0 < A(2,1)) {
1028             Timer ts;
1029 
1030             // Figure out which reference is the closest
1031             iref=steps[imin-1].icalc;
1032             double dminv=std::abs(x0-A(0,1));
1033             for(size_t i=1;i<A.n_rows;i++) {
1034               double d=std::abs(x0-A(i,1));
1035               if(d<dminv) {
1036                 dminv=d;
1037                 iref=steps[imin+i-1].icalc;
1038               }
1039             }
1040 
1041             // Do the calculation with the interpolated step
1042             linesearch_t p;
1043             p.s=x0;
1044             p.icalc=ncalc;
1045 
1046             double Et;
1047             arma::vec ft;
1048             settings.set_string("LoadChk",getchk(iref));
1049             settings.set_string("SaveChk",getchk(ncalc));
1050             calculate(x+p.s*sd,pars,Et,ft,false);
1051             chkstore.push_back(ncalc);
1052             ncalc++;
1053 
1054             p.E=Et;
1055             steps.push_back(p);
1056             printf("\t%2i %e % e %s\n",(int) steps.size()-1,p.s,Et-E,ts.elapsed().c_str());
1057             fflush(stdout);
1058 
1059             // Resort the steps in length
1060             std::sort(steps.begin(),steps.end());
1061 
1062             // Find the minimum energy
1063             find_minimum(steps,Emin,smin,imin);
1064           }
1065         }
1066       }
1067 
1068       // Switch to the minimum geometry
1069       x+=steps[imin].s*sd;
1070       iref=steps[imin].icalc;
1071 
1072       // Store optimal step length
1073       steplen=steps[imin].s;
1074 
1075       printf("Best step size %e changed energy by % e\n",steplen,steps[imin].E-E);
1076 
1077       // Copy checkpoint file
1078       {
1079         std::ostringstream oss;
1080         oss << "\\cp " << getchk(iref) << " " << savechk0;
1081         if(system(oss.str().c_str()))
1082           throw std::runtime_error("Error copying checkpoint.\n");
1083       }
1084 
1085       // Erase all unnecessary calcs
1086       for(size_t i=0;i<chkstore.size();i++)
1087         if(chkstore[i]!=iref)
1088           remove(getchk(chkstore[i]).c_str());
1089 
1090       // New geometry
1091       std::vector<atom_t> geom=get_atoms(x,pars);
1092 
1093       // Calculate displacements
1094       double dmax, drms;
1095       get_displacement(geom, oldgeom, dmax, drms);
1096       // Calculate projected change of energy
1097       double dEproj=calculate_projection(geom,oldgeom,f,pars.dofidx);
1098       // Actual change of energy is
1099       double dE=steps[imin].E - Eold;
1100 
1101       // Store new geometry
1102       oldgeom=geom;
1103 
1104       // Check convergence
1105       bool fmaxconv=false, frmsconv=false;
1106       bool dmaxconv=false, drmsconv=false;
1107 
1108       switch(crit) {
1109 
1110       case(LOOSE):
1111         if(fmax < 2.5e-3)
1112           fmaxconv=true;
1113         if(frms < 1.7e-3)
1114           frmsconv=true;
1115         if(dmax < 1.0e-2)
1116           dmaxconv=true;
1117         if(drms < 6.7e-3)
1118           drmsconv=true;
1119         break;
1120 
1121       case(NORMAL):
1122         if(fmax < 4.5e-4)
1123           fmaxconv=true;
1124         if(frms < 3.0e-4)
1125           frmsconv=true;
1126         if(dmax < 1.8e-3)
1127           dmaxconv=true;
1128         if(drms < 1.2e-3)
1129           drmsconv=true;
1130         break;
1131 
1132       case(TIGHT):
1133         if(fmax < 1.5e-5)
1134           fmaxconv=true;
1135         if(frms < 1.0e-5)
1136           frmsconv=true;
1137         if(dmax < 6.0e-5)
1138           dmaxconv=true;
1139         if(drms < 4.0e-5)
1140           drmsconv=true;
1141         break;
1142 
1143       case(VERYTIGHT):
1144         if(fmax < 2.0e-6)
1145           fmaxconv=true;
1146         if(frms < 1.0e-6)
1147           frmsconv=true;
1148         if(dmax < 6.0e-6)
1149           dmaxconv=true;
1150         if(drms < 4.0e-6)
1151           drmsconv=true;
1152         break;
1153 
1154       default:
1155         ERROR_INFO();
1156         throw std::runtime_error("Not implemented!\n");
1157       }
1158 
1159       double dEfrac;
1160       if(dEproj!=0.0)
1161         dEfrac=dE/dEproj;
1162       else
1163         dEfrac=0.0;
1164 
1165       const static char cconv[]=" *";
1166 
1167       fprintf(stderr,"%3i % 18.10f % .3e % .3e %.3e%c %.3e%c %.3e%c %.3e%c %s\n", iiter, E, dE, dEfrac, dmax, cconv[dmaxconv], drms, cconv[drmsconv], fmax, cconv[fmaxconv], frms, cconv[frmsconv], titer.elapsed().c_str());
1168       fflush(stderr);
1169 
1170       bool convd=dmaxconv && drmsconv && fmaxconv && frmsconv;
1171 
1172       if(convd) {
1173         fprintf(stderr,"Converged.\n");
1174         break;
1175       }
1176     }
1177   }
1178 
1179   // Run calculation to get final checkpoint
1180   settings=settings0;
1181   settings.set_string("LoadChk",savechk0);
1182   settings.set_string("SaveChk",savechk0);
1183   calculate(x,pars,E,f,false);
1184   save_xyz(get_atoms(x,pars),"Optimized configuration",result,false);
1185 
1186   // Remove the rest
1187   for(size_t i=0;i<chkstore.size();i++)
1188     remove(getchk(chkstore[i]).c_str());
1189 
1190   printf("Running program took %s.\n",tprog.elapsed().c_str());
1191 
1192   return 0;
1193 }
1194 
main(int argc,char ** argv)1195 int main(int argc, char **argv) {
1196   try {
1197     return main_guarded(argc, argv);
1198   } catch (const std::exception &e) {
1199     std::cerr << "error: " << e.what() << std::endl;
1200     return 1;
1201   }
1202 }
1203