JDFTx  1.0.0
Pulay.h
Go to the documentation of this file.
1 /*-------------------------------------------------------------------
2 Copyright 2015 Ravishankar Sundararaman
3 
4 This file is part of JDFTx.
5 
6 JDFTx is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 JDFTx is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with JDFTx. If not, see <http://www.gnu.org/licenses/>.
18 -------------------------------------------------------------------*/
19 
20 #ifndef JDFTX_CORE_PULAY_H
21 #define JDFTX_CORE_PULAY_H
22 
23 #include <core/PulayParams.h>
24 #include <core/Util.h>
25 #include <electronic/matrix.h>
26 #include <cfloat>
27 
33 template<typename Variable> class Pulay
36 {
37 public:
38  Pulay(const PulayParams& pp);
39 
45  double minimize(double Eprev=+DBL_MAX, std::vector<string> extraNames=std::vector<string>(), std::vector<double> extraThresh=std::vector<double>());
46 
47  void loadState(const char* filename);
48  void saveState(const char* filename) const;
49 
51  virtual double sync(double x) const { return x; }
52 
53 protected:
54  //----- Interface specification -----
55 
64  virtual double cycle(double dEprev, std::vector<double>& extraValues)=0;
65 
66  virtual void report(int iter) {}
67  virtual void axpy(double alpha, const Variable& X, Variable& Y) const=0;
68  virtual double dot(const Variable& X, const Variable& Y) const=0;
69  virtual size_t variableSize() const=0;
70  virtual void readVariable(Variable&, FILE*) const=0;
71  virtual void writeVariable(const Variable&, FILE*) const=0;
72  virtual Variable getVariable() const=0;
73  virtual void setVariable(const Variable&)=0;
74  virtual Variable precondition(const Variable&) const=0;
75  virtual Variable applyMetric(const Variable&) const=0;
76 
77 private:
78  const PulayParams& pp;
79  std::vector<Variable> pastVariables;
80  std::vector<Variable> pastResiduals;
81  matrix overlap;
82 };
83 
84 
85 //---------------------- Implementation ----------------------------
87 
88 #include <core/Minimize_linmin.h>
89 #include <memory>
90 
91 //Norm convergence check (eigenvalue-difference or residual)
92 //Make sure value is within tolerance for nCheck consecutive cycles
93 class NormCheck
94 { unsigned nCheck; double threshold;
95  std::deque<bool> history;
96 public:
97  NormCheck(unsigned nCheck, double threshold) : nCheck(nCheck), threshold(fabs(threshold)) {}
98  bool checkConvergence(double norm)
99  { history.push_back(fabs(norm)<threshold);
100  if(history.size()==nCheck+1) history.pop_front(); //discard old unneeded elements
101  if(history.size()==nCheck)
102  { for(bool converged: history)
103  if(!converged)
104  return false;
105  return true;
106  }
107  else return false;
108  }
109 };
110 
111 template<typename Variable> Pulay<Variable>::Pulay(const PulayParams& pp)
112 : pp(pp), overlap(pp.history, pp.history)
113 {
114 }
115 
116 template<typename Variable> double Pulay<Variable>::minimize(double Eprev, std::vector<string> extraNames, std::vector<double> extraThresh)
117 {
118  double E = sync(Eprev); Eprev = 0.;
119  double dE = E-Eprev;
120  assert(extraNames.size()==extraThresh.size());
121 
122  //Initialize convergence checkers:
123  EdiffCheck ediffCheck(2, pp.energyDiffThreshold); ediffCheck.checkConvergence(E); //store the initial energy in the check's history
124  NormCheck resCheck(2, pp.residualThreshold);
125  std::vector<std::shared_ptr<NormCheck> > extraCheck(extraNames.size());
126  for(size_t iExtra=0; iExtra<extraNames.size(); iExtra++)
127  extraCheck[iExtra] = std::make_shared<NormCheck>(2, extraThresh[iExtra]);
128 
129  for(int iter=0; iter<pp.nIterations; iter++)
130  {
131  //If history is full, remove oldest member
132  assert(pastResiduals.size() == pastVariables.size());
133  if((int)pastResiduals.size() >= pp.history)
134  { size_t ndim = pastResiduals.size();
135  if(ndim>1) overlap.set(0,ndim-1, 0,ndim-1, overlap(1,ndim, 1,ndim));
136  pastVariables.erase(pastVariables.begin());
137  pastResiduals.erase(pastResiduals.begin());
138  }
139 
140  //Cache the old energy and variables
141  Eprev = E;
142  pastVariables.push_back(getVariable());
143 
144  //Perform cycle:
145  std::vector<double> extraValues(extraThresh.size());
146  E = sync(cycle(dE, extraValues));
147  dE = E - Eprev;
148  for(auto& v: extraValues) v = sync(v);
149 
150  //Calculate and cache residual:
151  double residualNorm = 0.;
152  { Variable residual = getVariable(); axpy(-1., pastVariables.back(), residual);
153  pastResiduals.push_back(residual);
154  residualNorm = sync(sqrt(dot(residual,residual)));
155  }
156 
157  //Print energy and convergence parameters:
158  fprintf(pp.fpLog, "%sCycle: %2i %s: ", pp.linePrefix, iter, pp.energyLabel);
159  fprintf(pp.fpLog, pp.energyFormat, E);
160  fprintf(pp.fpLog, " d%s: %+.3e", pp.energyLabel, dE);
161  fprintf(pp.fpLog, " |Residual|: %.3e", residualNorm);
162  for(size_t iExtra=0; iExtra<extraNames.size(); iExtra++)
163  fprintf(pp.fpLog, " |%s|: %.3e", extraNames[iExtra].c_str(), extraValues[iExtra]);
164  fprintf(pp.fpLog, "\n"); fflush(pp.fpLog);
165 
166  //Optional reporting:
167  report(iter);
168 
169  //Check for convergence and update variable:
170  bool converged = false;
171  if(!converged && ediffCheck.checkConvergence(E))
172  { fprintf(pp.fpLog, "%sConverged (|Delta E|<%le for 2 iters).\n\n", pp.linePrefix, pp.energyDiffThreshold);
173  converged = true;
174  }
175  if(!converged && resCheck.checkConvergence(residualNorm))
176  { fprintf(pp.fpLog, "%sConverged (|Residual|<%le for 2 iters).\n\n", pp.linePrefix, pp.residualThreshold);
177  converged = true;
178  }
179  if(!converged && extraNames.size())
180  for(size_t iExtra=0; iExtra<extraNames.size(); iExtra++)
181  if(extraCheck[iExtra]->checkConvergence(extraValues[iExtra]))
182  { fprintf(pp.fpLog, "%sConverged (|%s|<%le for 2 iters).\n\n", pp.linePrefix, extraNames[iExtra].c_str(), extraThresh[iExtra]);
183  converged = true;
184  break;
185  }
186  fflush(pp.fpLog);
187  if(converged || killFlag) break; //converged or manually interrupted
188 
189  //---- DIIS/Pulay mixing -----
190 
191  //Update the overlap matrix
192  size_t ndim = pastResiduals.size();
193  Variable MlastResidual = applyMetric(pastResiduals.back());
194  for(size_t j=0; j<ndim; j++)
195  { double thisOverlap = dot(pastResiduals[j], MlastResidual);
196  overlap.set(j, ndim-1, thisOverlap);
197  overlap.set(ndim-1, j, thisOverlap);
198  }
199 
200  //Invert the residual overlap matrix to get the minimum of residual
201  matrix cOverlap(ndim+1, ndim+1); //Add row and column to enforce normalization constraint
202  cOverlap.set(0, ndim, 0, ndim, overlap(0, ndim, 0, ndim));
203  for(size_t j=0; j<ndim; j++)
204  { cOverlap.set(j, ndim, 1);
205  cOverlap.set(ndim, j, 1);
206  }
207  cOverlap.set(ndim, ndim, 0);
208  matrix cOverlap_inv = inv(cOverlap);
209 
210  //Update variable:
211  complex* coefs = cOverlap_inv.data();
212  Variable v;
213  for(size_t j=0; j<ndim; j++)
214  { double alpha = coefs[cOverlap_inv.index(j, ndim)].real();
215  axpy(alpha, pastVariables[j], v);
216  axpy(alpha, precondition(pastResiduals[j]), v);
217  }
218  setVariable(v);
219  }
220  return E;
221 }
222 
223 template<typename Variable> void Pulay<Variable>::loadState(const char* filename)
224 {
225  size_t nBytesCycle = 2 * variableSize(); //number of bytes per history entry
226  size_t nBytesFile = fileSize(filename);
227  size_t ndim = nBytesFile / nBytesCycle;
228  size_t dimOffset = 0;
229  if(int(ndim) > pp.history)
230  { dimOffset = ndim - pp.history;
231  ndim = pp.history;
232  }
233  if(nBytesFile % nBytesCycle != 0)
234  die("Pulay history file '%s' does not contain an integral multiple of the mixed variables and residuals.\n", filename);
235  fprintf(pp.fpLog, "%sReading %lu past variables and residuals from '%s' ... ", pp.linePrefix, ndim, filename); logFlush();
236  pastVariables.resize(ndim);
237  pastResiduals.resize(ndim);
238  FILE* fp = fopen(filename, "r");
239  if(dimOffset) fseek(fp, dimOffset*nBytesCycle, SEEK_SET);
240  for(size_t idim=0; idim<ndim; idim++)
241  { readVariable(pastVariables[idim], fp);
242  readVariable(pastResiduals[idim], fp);
243  }
244  fclose(fp);
245  fprintf(pp.fpLog, "done.\n"); fflush(pp.fpLog);
246  //Compute overlaps of loaded history:
247  for(size_t i=0; i<ndim; i++)
248  { Variable Mresidual_i = applyMetric(pastResiduals[i]);
249  for(size_t j=0; j<=i; j++)
250  { double thisOverlap = dot(pastResiduals[j], Mresidual_i);
251  overlap.set(i,j, thisOverlap);
252  overlap.set(j,i, thisOverlap);
253  }
254  }
255 }
256 
257 template<typename Variable> void Pulay<Variable>::saveState(const char* filename) const
258 {
259  if(mpiUtil->isHead())
260  { FILE* fp = fopen(filename, "w");
261  for(size_t idim=0; idim<pastVariables.size(); idim++)
262  { writeVariable(pastVariables[idim], fp);
263  writeVariable(pastResiduals[idim], fp);
264  }
265  fclose(fp);
266  }
267 }
268 
270 
271 #endif //JDFTX_CORE_PULAY_H
Pulay mixing to optimize self-consistent field optimization .
Definition: Pulay.h:35
Definition: Minimize_linmin.h:27
ScalarField sqrt(const ScalarField &)
Elementwise square root (preserve input)
virtual Variable applyMetric(const Variable &) const =0
Apply metric to variable/residual.
double energyDiffThreshold
convergence threshold for energy difference between successive iterations
Definition: PulayParams.h:34
General complex matrix.
Definition: matrix.h:57
#define logFlush()
fflush() for log files
Definition: Util.h:115
virtual double dot(const Variable &X, const Variable &Y) const =0
Euclidean dot product. Metric applied separately for efficiency.
int nIterations
maximum iterations (single point calculation if 0)
Definition: PulayParams.h:33
bool isHead() const
whether this is the root process (makes code more readable)
Definition: MPIUtil.h:39
const char * linePrefix
prefix for each output line of Pulay (default "Pulay: ")
Definition: PulayParams.h:29
int index(int i, int j) const
Index into data()
Definition: matrix.h:65
void set(int i, int j, complex m)
set element to m
off_t fileSize(const char *filename)
Get the size of a file.
virtual Variable precondition(const Variable &) const =0
Apply preconditioner to variable/residual.
virtual void setVariable(const Variable &)=0
Set the state of system to specified variable.
virtual void readVariable(Variable &, FILE *) const =0
Read variable from stream.
Parameters to control Pulay mixing.
Definition: PulayParams.h:26
virtual void report(int iter)
Override to perform optional reporting.
Definition: Pulay.h:66
const char * energyFormat
printf format for the minimized quantity (default "%22.15le")
Definition: PulayParams.h:31
bool killFlag
Flag set by signal handlers - all compute loops should quit cleanly when this is set.
FILE * fpLog
Stream to log iterations to.
Definition: PulayParams.h:28
double residualThreshold
convergence threshold on the residual
Definition: PulayParams.h:35
void loadState(const char *filename)
Load the state from a single binary file.
#define die(...)
Quit with an error message (formatted using printf()). Must be called from all processes.
Definition: Util.h:118
Miscellaneous utilities.
ScalarField inv(const ScalarField &)
Elementwise reciprocal (preserve input)
void saveState(const char *filename) const
Save the state to a single binary file.
virtual Variable getVariable() const =0
Write variable to stream.
virtual double cycle(double dEprev, std::vector< double > &extraValues)=0
Single cycle of the self-consistency loop. In each subsequent cycle, Pulay will try to zero the diffe...
int history
Number of past residuals and vectors that are cached and used for mixing.
Definition: PulayParams.h:37
complex * data()
Return a pointer to the actual data Return a CPU pointer to the actual data, will move data from GPU ...
virtual double sync(double x) const
Override to synchronize scalars over MPI processes (if the same minimization is happening in sync ove...
Definition: Pulay.h:51
Complex number (need to define our own because we need operators for gpu code as well) ...
Definition: scalar.h:49
double minimize(double Eprev=+DBL_MAX, std::vector< string > extraNames=std::vector< string >(), std::vector< double > extraThresh=std::vector< double >())
Minimize energy using a self-consistent iteration.
const char * energyLabel
Label for the minimized quantity (default "E")
Definition: PulayParams.h:30
virtual void axpy(double alpha, const Variable &X, Variable &Y) const =0
Scaled accumulate on variable.
virtual size_t variableSize() const =0
Number of bytes per variable.
#define assert(expr)
A custom assertion with stack trace (NOTE: enabled in release modes as well)
Definition: Util.h:104