def __init__(self,distr=None,xgrid=None): """ Initialize either with given probability distribution 'distr(x)', or give the distribution on grid (given grid or 0,1,2,... by default) """ try: distr(0.0) self.f=distr except: self.x=xgrid if self.x is None: self.x=range(len(distr)) self.distr=distr self.f=SplineFunction(self.x,self.distr,k=1) # put the probability distribution f(x) on grid self.N=1000 self.lim=self.f.limits() self.xgrid=np.linspace(self.lim[0],self.lim[1],self.N) self.fvals=[self.f(z) for z in self.xgrid] # construct the distribution function F(x) cum=[0.0] for i in range(self.N-1): cum.append(cum[-1]+self.fvals[i]+self.fvals[i+1]) cum=np.array(cum)/2*(self.lim[1]-self.lim[0])/self.N self.norm=cum[-1] cum=cum/self.norm self.F=SplineFunction(self.xgrid,cum,k=1)
def __init__(self, distr=None, xgrid=None): """ Initialize either with given probability distribution 'distr(x)', or give the distribution on grid (given grid or 0,1,2,... by default) """ try: distr(0.0) self.f = distr except: self.x = xgrid if self.x is None: self.x = range(len(distr)) self.distr = distr self.f = SplineFunction(self.x, self.distr, k=1) # put the probability distribution f(x) on grid self.N = 1000 self.lim = self.f.limits() self.xgrid = np.linspace(self.lim[0], self.lim[1], self.N) self.fvals = [self.f(z) for z in self.xgrid] # construct the distribution function F(x) cum = [0.0] for i in range(self.N - 1): cum.append(cum[-1] + self.fvals[i] + self.fvals[i + 1]) cum = np.array(cum) / 2 * (self.lim[1] - self.lim[0]) / self.N self.norm = cum[-1] cum = cum / self.norm self.F = SplineFunction(self.xgrid, cum, k=1)
def append_energy_slope(self, weight, p, dEdp, p0, calc, traj, comment=None, label=None, color=None): """ Calculates the V'rep(r) at one point using trajectory over parameters p. Trajectory is calculated using parameters p, giving E(p), where E is the total energy without Vrep(r). The pair distance R=R(p). At p=p0, we set dE/dp|p=p0=dEdp, from which we can set V'rep(R(p)) as dEdp - dE/dp(p0) V'rep(r) = ------------------- N * dR/dp parameters: =========== weight: fitting weight p: parameter list dEdp: slope of energy at p0 p0: the point where energy slope is set calc: Hotbit calculator (remember charge and k-points) traj: filename for ASE trajectory, or PickleTrajectory object comment: fitting comment for par-file (replaced by comment if None) label: plotting label (replaced by comment if None) color: plotting color """ raise NotImplementedError('This method was never tested properly.') from box.interpolation import SplineFunction R, E, N = [], [], [] for atoms in traj: a, c = self._set_calc(atoms, calc) e = a.get_potential_energy() r, n = self._get_repulsion_distances(c) if n > 0 and r < self.r_cut: E.append(atoms.get_potential_energy()) R.append(r) N.append(n) R, E, N = np.array(R), np.array(E), np.array(N) if np.any(N[0] != N): raise NotImplementedError( 'The number of bonds changes during trajectory; check implementation.' ) Ef = SplineFunction(p, E) Rf = SplineFunction(p, R) color = self._get_color(color) comment += ';w=%.1f;N=%i' % (weight, N[0]) return self.append_point(weight, Rf(p0), (dEdp - Ef(p0, der=1)) / (N[0] * Rf(p0, der=1)), comment, label, color)
class ArbitraryDistribution: """ Class for generating random numbers form given distribution. """ def __init__(self,distr=None,xgrid=None): """ Initialize either with given probability distribution 'distr(x)', or give the distribution on grid (given grid or 0,1,2,... by default) """ try: distr(0.0) self.f=distr except: self.x=xgrid if self.x is None: self.x=range(len(distr)) self.distr=distr self.f=SplineFunction(self.x,self.distr,k=1) # put the probability distribution f(x) on grid self.N=1000 self.lim=self.f.limits() self.xgrid=np.linspace(self.lim[0],self.lim[1],self.N) self.fvals=[self.f(z) for z in self.xgrid] # construct the distribution function F(x) cum=[0.0] for i in range(self.N-1): cum.append(cum[-1]+self.fvals[i]+self.fvals[i+1]) cum=np.array(cum)/2*(self.lim[1]-self.lim[0])/self.N self.norm=cum[-1] cum=cum/self.norm self.F=SplineFunction(self.xgrid,cum,k=1) def __call__(self): """ Generate random number from given distribution. """ rnd=np.random.uniform(0.0,1.0) return self.F.solve(rnd) def verify(self,nr=10000): """ Plot and compare given distribution and simulated one. """ c=np.array([self() for rnd in range(nr)]) import pylab as pl pl.hist(c,len(self.xgrid),normed=True) distr=[self.f(x)/self.norm for x in self.xgrid] pl.plot(self.xgrid,distr,c='r') pl.show()
def run(self): """ Solve the self-consistent potential. """ self.timer.start('solve ground state') print('\nStart iteration...', file=self.txt) self.enl={} self.d_enl={} for n,l,nl in self.list_states(): self.enl[nl]=0.0 self.d_enl[nl]=0.0 N=self.grid.get_N() # make confinement and nuclear potentials; intitial guess for veff self.conf=array([self.confinement_potential(r) for r in self.rgrid]) self.nucl=array([self.V_nuclear(r) for r in self.rgrid]) self.get_veff_and_dens() self.calculate_Hartree_potential() #self.Hartree=np.zeros((N,)) for it in range(self.itmax): self.veff=self.mix*self.calculate_veff()+(1-self.mix)*self.veff if self.scalarrel: veff = SplineFunction(self.rgrid, self.veff) self.dveff = array([veff(r, der=1) for r in self.rgrid]) d_enl_max, itmax=self.solve_eigenstates(it) dens0=self.dens.copy() self.dens=self.calculate_density() diff=self.grid.integrate(np.abs(self.dens-dens0),use_dV=True) if diff<self.convergence['density'] and d_enl_max<self.convergence['energies'] and it > 5: break self.calculate_Hartree_potential() if np.mod(it,10)==0: print('iter %3i, dn=%.1e>%.1e, max %i sp-iter' %(it,diff,self.convergence['density'],itmax), file=self.txt) if it==self.itmax-1: if self.timing: self.timer.summary() raise RuntimeError('Density not converged in %i iterations' %(it+1)) self.txt.flush() self.calculate_energies(echo=True) print('converged in %i iterations' %it, file=self.txt) print('%9.4f electrons, should be %9.4f' %(self.grid.integrate(self.dens,use_dV=True),self.nel), file=self.txt) for n,l,nl in self.list_states(): self.Rnl_fct[nl]=Function('spline',self.rgrid,self.Rnlg[nl]) self.unl_fct[nl]=Function('spline',self.rgrid,self.unlg[nl]) self.timer.stop('solve ground state') self.timer.summary() self.txt.flush() self.solved=True if self.write != None: f=open(self.write,'wb') pickle.dump(self.rgrid, f) pickle.dump(self.veff, f) pickle.dump(self.dens, f) f.close()
class ArbitraryDistribution: """ Class for generating random numbers form given distribution. """ def __init__(self, distr=None, xgrid=None): """ Initialize either with given probability distribution 'distr(x)', or give the distribution on grid (given grid or 0,1,2,... by default) """ try: distr(0.0) self.f = distr except: self.x = xgrid if self.x is None: self.x = range(len(distr)) self.distr = distr self.f = SplineFunction(self.x, self.distr, k=1) # put the probability distribution f(x) on grid self.N = 1000 self.lim = self.f.limits() self.xgrid = np.linspace(self.lim[0], self.lim[1], self.N) self.fvals = [self.f(z) for z in self.xgrid] # construct the distribution function F(x) cum = [0.0] for i in range(self.N - 1): cum.append(cum[-1] + self.fvals[i] + self.fvals[i + 1]) cum = np.array(cum) / 2 * (self.lim[1] - self.lim[0]) / self.N self.norm = cum[-1] cum = cum / self.norm self.F = SplineFunction(self.xgrid, cum, k=1) def __call__(self): """ Generate random number from given distribution. """ rnd = np.random.uniform(0.0, 1.0) return self.F.solve(rnd) def verify(self, nr=10000): """ Plot and compare given distribution and simulated one. """ c = np.array([self() for rnd in range(nr)]) import pylab as pl pl.hist(c, len(self.xgrid), normed=True) distr = [self.f(x) / self.norm for x in self.xgrid] pl.plot(self.xgrid, distr, c='r') pl.show()
def derivative(self, f, order=1): """ Get order-th derivative of function f (given with N grid points). """ x = self.get_grid() f_spline = SplineFunction(x, f) dfdx = f_spline(x, der=order) return dfdx
def _integrate_vrep(self, dv_rep, r_cut, N=100): """ Integrate V'_rep(r) from r_cut to zero to get the V_rep(r) """ from box.interpolation import SplineFunction from scipy.integrate import quadrature r_g = np.linspace(r_cut, 0, N) dr = r_g[1] - r_g[0] v_rep = np.zeros(N) for i in range(1,len(r_g)): v_rep[i] = v_rep[i-1] val, err = quadrature(dv_rep, r_g[i-1], r_g[i], tol=1.0e-12, maxiter=50) v_rep[i] += val # SplineFunction wants the x-values in ascending order return SplineFunction(r_g[::-1], v_rep[::-1])
def vrep_to_spline_coefficients(rep): """ Transform simple grid data to spline form used by DFTB+ Input: rep: rep[:,0]=r and rep[:,1]=v(r) Output: spline: List with spline data Each line with: start end c0 c1 c2 c3 where v is interpolated between [start,end] using v(r) = c0 + c1*(r-start)+ c2*(r-start)**2 + c3*(r-start)**3 The last line has coefficients up to c5. """ print(rep.shape) v = SplineFunction(rep[:, 0], rep[:, 1]) n = rep.shape[0] m = 6 spline = [] for i in range(n - 1): r0, r1 = rep[i, 0], rep[i + 1, 0] v0, v1 = rep[i, 1], rep[i + 1, 1] rlist = linspace(r0, r1, m) vlist = v(rlist) if i == n - 2: nc = 1 + 5 else: nc = 1 + 3 pguess = zeros(nc) pguess[0] = 0.5 * (v0 + v1) pguess[1] = (v1 - v0) / (r1 - r0) def chi2(p, x, y): err = y - vrep_poly(x, r0, p) return sum(err**2) p = fmin(chi2, pguess, args=(rlist, vlist), xtol=1E-12, disp=False) spline.append([r0, r1] + list(p)) return spline
def append_energy_curve(self,weight,calc,traj,comment=None,label=None,color=None): """ Calculates the V'rep(r) from a given ase-trajectory. The trajectory can be anything, as long as the ONLY missing energy from DFTB calculation is N*V_rep(R). Hence E_DFT(R) = E_wr(R) + N*V_rep(R) E_DFT'(R) - E_wr'(R) V_rep'(R) = ------------------ , N where R is the nn. distance,N is the number of A-B pairs taken into account, and E_wr(R) = E_bs(R) + E_coul(R) is the DFTB energy without repulsion. At least 3 points in energy curve needed, preferably more. parameters: =========== weight: fitting weight calc: Hotbit calculator (remember charge and k-points) traj: filename for ASE trajectory, or PickleTrajectory object comment: fitting comment for par-file (replaced by comment if None) label: plotting label (replaced by comment if None) color: plotting color """ if comment==None: comment=label if label==None: label=comment #if not ( isinstance(traj, type(PickleTrajectory)) or isinstance(traj, list) ): if not ( isinstance(traj, type(PickleTrajectory)) or isinstance(traj, list) ): print("\nAppending energy curve data from %s..." %traj, file=self.txt) traj = PickleTrajectory(traj) else: print('\nAppending energy curve data...', file=self.txt) Edft, Ewr, N, R = [], [], [], [] if len(traj)<3: raise AssertionError('At least 3 points in energy curve required.') for atoms in traj: a, c = self._set_calc(atoms,calc) e = a.get_potential_energy() r, n = self._get_repulsion_distances(c) if n>0 and r<self.r_cut: Edft.append( atoms.get_potential_energy() ) Ewr.append( e ) R.append(r) N.append(n) Edft = np.array(Edft) Ewr = np.array(Ewr) N = np.array(N) R = np.array(R) if np.any( N-N[0]!=0 ): raise RuntimeError('The number of bonds changes within trajectory.') # sort radii because of spline ind = R.argsort() R = R[ind] Edft = Edft[ind] Ewr = Ewr[ind] from box.interpolation import SplineFunction k = min(len(Edft)-2,3) vrep = SplineFunction(R, (Edft-Ewr)/N, k=k, s=0) color = self._get_color(color) for i, r in enumerate(R): if i==0: com = comment + ';w=%.1f' %weight else: label='_nolegend_' com = None self.append_point(weight/np.sqrt(len(R)),r, vrep(r,der=1), com, label, color) print("Appended %i points around R=%.4f...%.4f" %(len(N),R.min(),R.max()), file=self.txt)
def write_skf(el1, el2, data1, data2, tables, rep, spline, dr=0.1): """ Write .skf files for el1-el2 interactions. Input: el1: element 1 symbol el2: element 2 symbol data1: dictionary for element 1 data data2: dictionary for element 2 data tables: dictionary for Slater-Koster table data rep: repulsive potential on a grid spline: spline in appropriate format already dr: output grid spacing """ if el1 == el2: filename = '%s-%s.skf' % (el1, el2) o = open(filename, 'w') ng = int(ceil(tables['grid'][-1] / dr)) o.write('%.15f, %i, ' % (dr, ng)) e = data1['energies'] u = data1['U'] occ = data1['occupations'] occ = [2, 2] # Ed Ep Es SPE Ud Up Us fd fp fs (orbital energies and Hubbard U) o.write("{} {} {} {} {} {} {} {} {} {} \n".format( 0.0, e[0], e[1], 0., u, u, u, 0.0, occ[0], occ[1])) # mass c2 c3 c4 c5 c6 c7 c8 c9 rcut d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 # where c* are vrep coefficients (unless spline) and d* are not used o.write("{} {} {} {} {} {} {} {} {} {} \n".format( data1['mass'] * 2, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.)) # # interpolate SlaKo tables # tab = tables['%s%s' % (el1, el2)] #[i,j] f = [SplineFunction(tables['grid'], tab[:, i]) for i in range(20)] for i in range(ng): for j in range(20): o.write("{} ".format(f[j]((i + 1) * dr))) o.write("\n") #print>>o # # repulsion # o.write('Spline \n') o.write("{} {} \n".format(len(spline), spline[-1][1])) #nInt, cutoff o.write(" {} {} {} \n".format(0, -1E19, spline[0][2])) for sp in spline: #print(' '.join([str(x) for x in sp])) o.write(' '.join([str(x) for x in sp])) o.write("\n") #print>>o o.close() else: for e1, e2 in [(el1, el2), (el2, el1)]: filename = '%s-%s.skf' % (e1, e2) o = open(filename, 'w') ng = int(ceil(tables['grid'][-1] / dr)) o.write('%.15f, %i, ' % (dr, ng)) o.write(0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.) # # interpolate SlaKo tables # tab = tables['%s%s' % (e1, e2)] #[i,j] f = [SplineFunction(tables['grid'], tab[:, i]) for i in range(20)] for i in range(ng): for j in range(20): o.write(f[j]((i + 1) * dr), '\n') #print>>o # # repulsion # o.write('Spline') o.write(len(spline), spline[-1][1]) #nInt, cutoff o.write(0, -1E19, spline[0][2]) for sp in spline: o.write(' '.join([str(x) for x in sp], '\n')) #print>>o o.close()