def displace(self): """ Displaces the center of the distribution towards the optimized limit. """ # Conditionally, before performing the optimization checks if # the forces are statistically significant by checking if the error # is smaller than the forces. When sobol sequences are used, since # the true error is not known, we use the "Monte Carlo standrard error" # as a proxy. if self.precheck: # Calculates the force, the error and the # batch weights. f, f_err, batch_w = self.weighted_force() fnm = np.dot(self.dm.V.T, f) # Assumes error = standard error. fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2)) fnm[self.z] = 0.0 if np.all(np.abs(fnm) < fnm_err): info( " @SCP: All the average force components are statistically insignificant.", verbosity.medium, ) info(" @SCP: Skipping the optimization step.", verbosity.medium) return # Uses the displacement correction and a used defined scaling # factor to move in the direction of the force. elif self.dm.displace_mode == "sd": # Calculates the force, the error and the # batch weights. f, f_err, batch_w = self.weighted_force() # Calculates the Hessian.. k = self.weighted_hessian() # Displaces. self.dm.beads.q += np.dot(self.dm.D, f) * self.dm.tau # Calculates the new dynamical matrix. self.dm.dynmatrix = np.dot(self.dm.isqM, np.dot((k + k.T) / 2.0, self.dm.isqM)) # Applies acoustic sum rule to project out zero modes. # Applies a high pass filter to zero out low frequency modes. # Calculates the displacement correction matrix. self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix, self.dm.beads) self.apply_hpf() self.get_KnD() info(" @SCP: <f> = %10.8f +/- %10.8f: " % (f, f_err), verbosity.medium) info( " @SCP: OPTMODE = sd : Using the displacement correlation as a preconditioner.", verbosity.medium, ) # Uses the inverse Hessian to determine how much # to move in the direction of the force. if self.dm.displace_mode == "ik": # Calculates the force, the error and the # batch weights. f, f_err, batch_w = self.weighted_force() self.dm.beads.q += np.dot(self.dm.iK, f) # Calculates the new dynamical matrix. k = self.weighted_hessian() self.dm.dynmatrix = np.dot(self.dm.isqM, np.dot((k + k.T) / 2.0, self.dm.isqM)) # Applies acoustic sum rule to project out zero modes. # Applies a high pass filter to zero out low frequency modes. # Calculates the displacement correction matrix. self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix, self.dm.beads) self.apply_hpf() self.get_KnD() info(" @SCP: <f> = %10.8f +/- %10.8f: " % (f, f_err), verbosity.medium) info( " @SCP: OPTMODE = iK : Using the inverse Hessian as a preconditioner.", verbosity.medium, ) # Same as iK but only moves along normal modes which have statistically # significant force components. elif self.dm.displace_mode == "nmik": # Calculates the force, the error and the # batch weights. f, f_err, batch_w = self.weighted_force() # Calculates the new dynamical matrix. k = self.weighted_hessian() self.dm.dynmatrix = np.dot(self.dm.isqM, np.dot((k + k.T) / 2.0, self.dm.isqM)) # Applies acoustic sum rule to project out zero modes. # Applies a high pass filter to zero out low frequency modes. # Calculates the displacement correction matrix. self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix, self.dm.beads) self.apply_hpf() self.get_KnD() # Calculates the force along normal moces. # Zeros the forces along the known "zero modes". # Estimates the error. fnm = np.dot(self.dm.V.T, f) fnm[self.z] = 0.0 fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2)) # Zeros the displacement along "insignificant" normal modes. iKfnm = self.dm.iw2 * fnm iKfnm[np.abs(fnm) < fnm_err] = 0.0 dqnm = iKfnm dqnm[self.z] = 0.0 self.dm.beads.q += np.dot(self.dm.V, dqnm) # Calculates the new forces. f, f_err, batch_w = self.weighted_force() info( " @SCP: <f> = %10.8f +/- %10.8f: : number of batch weights > %10.8f = %8d / %8d" % ( np.linalg.norm(f), np.linalg.norm(f_err), self.wthreshold, sum(batch_w > self.wthreshold), len(batch_w), ), verbosity.medium, ) info( " @SCP: OPTMODE = nmiK : Using the inverse Hessian along statistically significant normal modes as a preconditioner.", verbosity.medium, ) elif self.dm.displace_mode == "rnmik": scale_forces = 1.0 * self.dm.tau # Outer Optimization Loop while True: # Inner Optimization Loop w = 1.0 while True: # Calculates the force, the error and the # batch weights. f, f_err, batch_w = self.weighted_force() # Saves the change in the weight associated with # last batch. w_old = w w = batch_w[-1] if np.absolute(w - w_old) < 1e-4: scale_forces *= 1.1 # info(" @SCP: Increasing displacement by 10 %.") elif np.absolute(w - w_old) > 5e-2: scale_forces /= 1.1 # Computes the force along the normal modes. fnm = np.dot(self.dm.V.T, f) fnm[self.z] = 0.0 fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2)) # Breaks if all the forces are statistically insignificant. if np.all(np.abs(fnm) < fnm_err): info( " @SCP: All forces are statistically insignificant.", verbosity.medium, ) info( " @SCP: Exiting the inner optimization loop.", verbosity.medium, ) break # or if the batch weights go to shit. elif np.max(batch_w) < self.wthreshold: info(" @SCP: Batch weights are small.", verbosity.medium) info( " @SCP: Exiting the inner optimization loop.", verbosity.medium, ) break # Calculates the displacement along the normal modes. # Moves only if forces are statistically insignificant. # Does not move along zero modes. dqnm = self.dm.iw2 * fnm * scale_forces / self.dm.dof dqnm[np.abs(fnm) < fnm_err] = 0.0 dqnm[self.z] = 0.0 self.dm.beads.q += np.dot(self.dm.V, dqnm) info( " @SCP: <f> = %10.8f +/- %10.8f : number of batch weights > %10.8f = %8d / %8d : largest batch weight = %10.8e" % ( np.linalg.norm(f), np.linalg.norm(f_err), self.wthreshold, sum(batch_w > self.wthreshold), len(batch_w), batch_w[-1], ), verbosity.medium, ) # Calculates the hessian. # Applies acoustic sum rule to project out zero modes. # Applies a high pass filter to zero out low frequency modes. # Calculates the displacement correction matrix. k = self.weighted_hessian() self.dm.dynmatrix = np.dot( self.dm.isqM, np.dot((k + k.T) / 2.0, self.dm.isqM)) self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix, self.dm.beads) self.apply_hpf() self.get_KnD() # Stores the old forces. fnm_old = fnm # Recalculates the new forces in normal mode coordinates.. f, f_err, batch_w = self.weighted_force() f_err = f_err fnm = np.dot(self.dm.V.T, f) fnm[self.z] = 0.0 fnm_err = np.sqrt(np.dot(self.dm.V.T**2, f_err**2)) # Breaks if all the forces are statistically insignificant. if np.all(np.abs(fnm) < fnm_err): info( " @SCP: All forces are statistically insignificant.", verbosity.medium, ) info(" @SCP: Skipping the optimization step.", verbosity.medium) break # or if the batch weights have gone to shit. elif np.max(batch_w) < self.wthreshold: info(" @SCP: Batch weights are small..", verbosity.medium) info(" @SCP: Skipping the optimization step.", verbosity.medium) break if np.all(np.abs(fnm_old - fnm) < fnm_err): info(" @SCP: All forces are converged.", verbosity.medium) break
def reset(self): """ Resets the variables for a new round of phonon evaluation. """ self.dm.w = np.zeros(self.dm.dof) self.dm.iw = np.zeros(self.dm.dof) self.dm.iw2 = np.zeros(self.dm.dof) self.dm.avgPot = 0.0 self.dm.avgForce = np.zeros((1, self.dm.dof)) self.dm.avgHessian = np.zeros((self.dm.dof, self.dm.dof)) self.dm.dynmatrix = apply_asr(self.dm.asr, self.dm.dynmatrix, self.dm.beads) self.apply_hpf() self.get_KnD() self.iD[self.dm.isc] = self.dm.iD.copy() self.q[self.dm.isc] = self.dm.beads.q.copy() self.dm.oldK = self.dm.K self.dm.imc = 1 info("\n @SCP: Beginning SCP step no. %8d" % (self.dm.isc, ), verbosity.medium) # prints the force constant matrix. outfile = self.dm.output_maker.get_output(self.dm.prefix + ".K." + str(self.dm.isc)) np.savetxt(outfile, self.dm.K) outfile.close_stream() info(" @SCP: Saving the force constant matrix.", verbosity.medium) # prints the inverse displacement correlation matrix. outfile = self.dm.output_maker.get_output(self.dm.prefix + ".iD." + str(self.dm.isc)) np.savetxt(outfile, self.dm.iD) outfile.close_stream() info( " @SCP: Saving the inverse displacement correlation matrix.", verbosity.medium, ) # prints the mean position. outfile = self.dm.output_maker.get_output(self.dm.prefix + ".q." + str(self.dm.isc)) np.savetxt(outfile, self.dm.beads.q) outfile.close_stream() info(" @SCP: Saving the mean position.", verbosity.medium) # prints the frequencies. outfile = self.dm.output_maker.get_output(self.dm.prefix + ".w." + str(self.dm.isc)) np.savetxt(outfile, self.dm.w) outfile.close_stream() info(" @SCP: Saving the frequencies.", verbosity.medium) # prints the potential energy at the mean position. outfile = self.dm.output_maker.get_output(self.dm.prefix + ".V0." + str(self.dm.isc)) np.savetxt(outfile, self.dm.forces.pots) outfile.close_stream() info(" @SCP: Saving the minimum potential.", verbosity.medium) if os.path.exists(self.dm.output_maker.prefix + "." + self.dm.prefix + ".x." + str(self.dm.isc)): info( " @SCP: Loading %8d configurations from file." % (self.dm.max_steps, ), verbosity.medium, ) self.x[self.dm.isc] = np.loadtxt(self.dm.output_maker.prefix + "." + self.dm.prefix + ".x." + str(self.dm.isc)) else: info( " @SCP: Generating %8d new configurations to be sampled." % (self.dm.max_steps, ), verbosity.medium, ) # Creates a list of configurations that are to be sampled. while self.dm.imc <= self.dm.max_steps: irng = (self.dm.isc) * self.dm.max_steps // 2 + (self.dm.imc + 1) // 2 x = self.dm.fginv(self.dm.random_sequence[irng]) # picks the elements of the vector in a random order. # this introduces a degree of randomness in the sobol-like PRNGs x = x[self.dm.random_shuffle] # Transforms the "normal" random number and stores it. x = np.dot(self.dm.isqM, np.dot(self.dm.sqtD, x)) self.x[self.dm.isc, self.dm.imc - 1] = (self.dm.beads.q + x.T)[-1] self.dm.imc += 1 # Performs an inversion to the displacement and samples another configuration. x = -x self.x[self.dm.isc, self.dm.imc - 1] = (self.dm.beads.q + x.T)[-1] self.dm.imc += 1 # Resets the number of MC steps to 1. self.dm.imc = 1 info( " @SCP: Performing %8d new force evaluations." % (self.dm.max_steps, ), verbosity.medium, )