def fit(symbol: str) -> Tuple[float, float, float, float]: V = [] E = [] for atoms in read('{}.traj@:'.format(symbol)): V.append(atoms.get_volume() / len(atoms)) E.append(atoms.get_potential_energy() / len(atoms)) eos = EOS(V, E, 'birchmurnaghan') eos.fit() e0, B, Bp, v0 = eos.eos_parameters return e0, v0, B, Bp
def test_eos(): import numpy as np import scipy # skip test early if no scipy from ase.build import bulk from ase.calculators.emt import EMT from ase.eos import EquationOfState as EOS, eos_names scipy # silence pyflakes b = bulk('Al', 'fcc', a=4.0, orthorhombic=True) b.set_calculator(EMT()) cell = b.get_cell() volumes = [] energies = [] for x in np.linspace(0.98, 1.01, 5): b.set_cell(cell * x, scale_atoms=True) volumes.append(b.get_volume()) energies.append(b.get_potential_energy()) results = [] for name in eos_names: if name == 'antonschmidt': # Someone should fix this! continue eos = EOS(volumes, energies, name) v, e, b = eos.fit() print('{0:20} {1:.8f} {2:.8f} {3:.8f} '.format(name, v, e, b)) assert abs(v - 3.18658700e+01) < 4e-4 assert abs(e - -9.76187802e-03) < 5e-7 assert abs(b - 2.46812688e-01) < 2e-4 results.append((v, e, b)) print(np.ptp(results, 0)) print(np.mean(results, 0))
def calc_lattice_parameter_al(atom_set, A, lmbd, D, mu2): global n_sets, cells q = 0.2 n_points = 10 lattice_param_set = np.zeros(n_sets) calc = get_calc((A, lmbd, D, mu2)) for index, atoms in enumerate(atom_set): # # # Find lattice constant with lowest energy # # # cell_0 = atoms.cells[index] # Unit cell object of the Al bulk atoms.set_calculator(calc) energies = [] volumes = [] inval = np.linspace(-q, q, n_points) for eps in inval: atoms.cell = (1 + eps) * cell_0 # Adjust lattice constant of unit cell # Calculate the potential energy for the Al bulk energies.append(atoms.get_potential_energy()) volumes.append(atoms.get_volume()) # Plot energies as a function of unit cell volume (directly related to latt. const.) eos = EquationOfState(volumes, energies) v0, E, B = eos.fit() # Latt. const. acc. to ASE doc., but why is this correct? a_calc = v0**(1 / 3.0) n_min = np.argmin(energies) atoms.cell = (1 + inval[n_min]) * cell_0 lattice_param_set[index] = a_calc return lattice_param_set
def calc_lattice_parameter(al_bulk_ASE, A, lmbd, D, mu2): q = 0.2 n_points = 15 calc = get_calc((A, lmbd, D, mu2)) # # # Find lattice constant with lowest energy # # # cell_0 = al_bulk_ASE.get_cell() # Unit cell object of the Al bulk al_bulk_ASE.set_calculator(calc) energies = [] volumes = [] for eps in np.linspace(-q, q, n_points): al_bulk_ASE.set_cell( (1 + eps) * cell_0) # Adjust lattice constant of unit cell # Calculate the potential energy for the Al bulk energies.append(al_bulk_ASE.get_potential_energy()) volumes.append(al_bulk_ASE.get_volume()) #plt.plot(np.asarray(volumes)**(1 / 3), energies) # plt.show() # Plot energies as a function of unit cell volume (directly related to latt. const.) eos = EquationOfState(volumes, energies) v0, E, B = eos.fit() # Latt. const. acc. to ASE doc., but why is this correct? a_calc = v0**(1 / 3.0) print('a=' + str(a_calc)) al_bulk_ASE.set_cell(cell_0) return a_calc
def eosfit_spec(atoms, calc, rg, method="birchmurnaghan"): from ase.eos import EquationOfState from numpy import linspace rootdir = os.getcwd() if not os.path.exists('eosfit'): os.mkdir('eosfit') os.chdir('eosfit') cell = atoms.get_cell() atoms.set_calculator(calc) traj = Trajectory('eosfit.traj', 'w') for x in rg: print(str(x)) atoms.set_cell(cell * x, scale_atoms=True) atoms.get_potential_energy() traj.write(atoms) configs = Trajectory('eosfit.traj', 'r') volumes = [at.get_volume() for at in configs] energies = [at.get_potential_energy() for at in configs] eos = EquationOfState(volumes, energies, eos=method) v0, e0, B = eos.fit() eos.plot('eosfit.svg') fp = open('eosdata.dat', 'w') fp.write('Volume\t\tEnergy') for i in range(len(configs)): fp.write(str(volumes[i]) + '\t' + str(energies[i]) + '\n') os.chdir(rootdir)
def fit(filename): configs = read(filename + "@:") volumes = [a.get_volume() for a in configs] energies = [a.get_potential_energy() for a in configs] eos = EquationOfState(volumes, energies) v0, e0, B = eos.fit() return (4 * v0)**(1 / 3.0)
def calc_lattice_parameter_al(al_bulk, A, lmbd, D, mu2): q = 0.03 n_points = 14 calc = get_calc((A, lmbd, D, mu2)) # # # Find lattice constant with lowest energy # # # cell_0 = al_bulk.cell # Unit cell object of the Al bulk al_bulk.set_calculator(calc) energies = [] volumes = [] for eps in np.linspace(-q, q, n_points): al_bulk.cell = (1 + eps) * cell_0 # Adjust lattice constant of unit cell # Calculate the potential energy for the Al bulk energies.append(al_bulk.get_potential_energy()) volumes.append(al_bulk.get_volume()) # Plot energies as a function of unit cell volume (directly related to latt. const.) eos = EquationOfState(volumes, energies) v0, E, B = eos.fit() # Latt. const. acc. to ASE doc., but why is this correct? a_calc = (4 * v0)**(1 / 3.0) al_bulk.cell = cell_0 print('Lattice_parameter:', a_calc) return a_calc
def eos(self, atoms, name): args = self.args traj = Trajectory(self.get_filename(name, 'traj'), 'w', atoms) N, eps = args.equation_of_state.split(',') N = int(N) eps = float(eps) / 100 strains = np.linspace(1 - eps, 1 + eps, N) v1 = atoms.get_volume() volumes = strains**3 * v1 energies = [] cell1 = atoms.cell for s in strains: atoms.set_cell(cell1 * s, scale_atoms=True) energies.append(atoms.get_potential_energy()) traj.write(atoms) traj.close() eos = EquationOfState(volumes, energies, args.eos_type) v0, e0, B = eos.fit() atoms.set_cell(cell1 * (v0 / v1)**(1 / 3), scale_atoms=True) data = {'volumes': volumes, 'energies': energies, 'fitted_energy': e0, 'fitted_volume': v0, 'bulk_modulus': B, 'eos_type': args.eos_type} return data
def eos(self, atoms, name): args = self.args traj = Trajectory(self.get_filename(name, 'traj'), 'w', atoms) N, eps = args.equation_of_state.split(',') N = int(N) eps = float(eps) / 100 strains = np.linspace(1 - eps, 1 + eps, N) v1 = atoms.get_volume() volumes = strains**3 * v1 energies = [] cell1 = atoms.cell for s in strains: atoms.set_cell(cell1 * s, scale_atoms=True) energies.append(atoms.get_potential_energy()) traj.write(atoms) traj.close() eos = EquationOfState(volumes, energies, args.eos_type) v0, e0, B = eos.fit() atoms.set_cell(cell1 * (v0 / v1)**(1 / 3), scale_atoms=True) data = { 'volumes': volumes, 'energies': energies, 'fitted_energy': e0, 'fitted_volume': v0, 'bulk_modulus': B, 'eos_type': args.eos_type } return data
def eos(self, atoms, name): args = self.args traj = Trajectory(self.get_filename(name, 'traj'), 'w', atoms) N, eps = args.equation_of_state.split(',') N = int(N) eps = float(eps) / 100 strains = np.linspace(1 - eps, 1 + eps, N) v1 = atoms.get_volume() volumes = strains**3 * v1 energies = [] cell1 = atoms.cell for s in strains: atoms.set_cell(cell1 * s, scale_atoms=True) energies.append(atoms.get_potential_energy()) traj.write(atoms) traj.close() eos = EquationOfState(volumes, energies, args.eos_type) v0, e0, B = eos.fit() atoms.set_cell(cell1 * (v0 / v1)**(1 / 3), scale_atoms=True) from ase.parallel import parprint as p p('volumes:', volumes) p('energies:', energies) p('fitted energy:', e0) p('fitted volume:', v0) p('bulk modulus:', B) p('eos type:', args.eos_type)
def do_autotune_volume(self,start_scan=0.90,end_scan=1.10,num_scan=25,exclude_type=None,only_type=None): # Load system and calculator system = self.system.copy() calc = self.system.calc system.set_calculator(calc) # Scale volumes and collect energy & volume measurements ens = [] vols = [] start_cell = system.get_cell() print("Performing volume scan.") for i,x in enumerate(np.linspace(start_scan,end_scan,num_scan)): #print(i) scale = x**(1./3.) system.set_cell(scale*start_cell,scale_atoms=True) vols.append(system.get_volume()) ens.append(system.get_potential_energy()) self.volume_vs_energy = [vols,ens] # Fit to Equations of States print("Fitting data to equations of state.") eos_types = "sjeos taylor murnaghan birch birchmurnaghan pouriertarantola p3 antonschmidt".split() if exclude_type != None: _ = [eos_types.remove(i) for i in exclude_type] elif only_type != None: eos_types = only_type eos_fits = {} errors = [] for typ in eos_types: try: eos = EquationOfState(vols,ens,eos=typ) v, e, B = eos.fit() eos_fits[typ] = {'volume':v,'energy':e,'buld_modulus':B} except: errors.append(typ) self.eos_fits = eos_fits self.eos_errors = errors if len(errors)>0 else None if len(errors) > 0: print("WARNING: Was not able to fit equation of state for the following: {}".format(errors)) # Rescale initial cell to optimized volume print("Rescaling with optimized volume.") vol_avg = np.average([eos_fits[key]['volume'] for key in eos_fits.keys()]) system.set_cell(start_cell,scale_atoms=True) scale = (vol_avg/system.get_volume())**(1./3.) print("Optimized volume: {}".format(vol_avg)) del self.system self.system = system self.system.set_cell(scale*start_cell,scale_atoms=True) print("Performing second relaxation.") self.system.get_potential_energy()
def relax(input_atoms, ref_db): atoms_string = input_atoms.get_chemical_symbols() # Open connection to the database with reference data db = connect(ref_db) # Load our model structure which is just FCC atoms = FaceCenteredCubic('X', latticeconstant=1.) atoms.set_chemical_symbols(atoms_string) # Compute the average lattice constant of the metals in this individual # and the sum of energies of the constituent metals in the fcc lattice # we will need this for calculating the heat of formation a = 0 ei = 0 for m in set(atoms_string): dct = db.get(metal=m) count = atoms_string.count(m) a += count * dct.latticeconstant ei += count * dct.energy_per_atom a /= len(atoms_string) atoms.set_cell([a, a, a], scale_atoms=True) # Since calculations are extremely fast with EMT we can also do a volume # relaxation atoms.set_calculator(EMT()) eps = 0.05 volumes = (a * np.linspace(1 - eps, 1 + eps, 9))**3 energies = [] for v in volumes: atoms.set_cell([v**(1. / 3)] * 3, scale_atoms=True) energies.append(atoms.get_potential_energy()) eos = EquationOfState(volumes, energies) v1, ef, B = eos.fit() latticeconstant = v1**(1. / 3) # Calculate the heat of formation by subtracting ef with ei hof = (ef - ei) / len(atoms) # Place the calculated parameters in the info dictionary of the # input_atoms object input_atoms.info['key_value_pairs']['hof'] = hof # Raw score must always be set # Use one of the following two; they are equivalent input_atoms.info['key_value_pairs']['raw_score'] = -hof # set_raw_score(input_atoms, -hof) input_atoms.info['key_value_pairs']['latticeconstant'] = latticeconstant # Setting the atoms_string directly for easier analysis atoms_string = ''.join(input_atoms.get_chemical_symbols()) input_atoms.info['key_value_pairs']['atoms_string'] = atoms_string
def run_task(self,fw_spec): jobID,latticeParams = fw_spec['jobID'],fw_spec['latticeParams'] # WHY IS LATTICEPARAMS [[FLOAT]] INSTEAD OF [FLOAT]? job = db2object(jobID) existsPrecalc = job.precalc != 'None' optAtoms = job.fromParams(latticeParams[0]) optCell = optAtoms.get_cell() strains = np.linspace(1-job.strain,1+job.strain,9) calc = job.calc(restart=True) if existsPrecalc else job.calc() vol,eng = [],[] for i, strain in enumerate(strains): atoms = optAtoms.copy() atoms.set_cell(optCell*strain,scale_atoms=True) if existsPrecalc: preCalc = job.calc(precalc=True) job.optimizePos(atoms,preCalc,saveWF = True) if job.dftcode =='gpaw': atoms,calc = job.gpawRestart() job.optimizePos(atoms,calc) energy = atoms.get_potential_energy() volume = atoms.get_volume() vol.append(deepcopy(volume)) eng.append(deepcopy(energy)) parprint('%f %f'%(strain,energy)) with paropen('bulkmod.log','a') as logfile: logfile.write('%s\t%s\n' %(strain,energy)) parprint('%f %f'%(strain,energy)) aHat,quadR2 = quadFit(np.array(deepcopy(vol)),np.array(deepcopy(eng))) try: eos = EquationOfState(vol,eng) v0, e0, b = eos.fit() eos.plot(filename='bulk-eos.png',show=False) b0= b/kJ*1e24 #GPa use this value if EOS doesn't fail except ValueError: # too bad of a fit for ASE to handle b0 = aHat*2*vol[4]*160.2 # units: eV/A^6 * A^3 * 1, where 1 === 160.2 GPa*A^3/eV return FWAction( stored_data= {'b0':b0,'quadR2':quadR2} ,mod_spec=[{'_push': {'b0':b0,'quadR2':quadR2}}])
def Free_energy( x, T, E0, V0, B0, Bp ): #T in K, M is molar mass, E0 in eV, V0 in A^3, B0 in eV/A^3, x is concentration in atomic fraction try: i = 0 M = 0 for el in elements: M = M + mass_dict[el] * x[i] i = i + 1 M = abs(M) r0 = (3 * V0 / 4 / np.pi)**(1.0 / 3.0) T_D0 = C * np.sqrt(r0 * B0 * 160.21766208 / M) g = 1 - (np.tanh(4 * (T - T_D0) / T_D0) / 2.0 + 0.5) / 3.0 #2/3 for high T, 1 for low T gamma = -g + 0.5 * (1 + Bp) x = np.array(x) V = np.linspace(V0 - 0.1 * V0, V0 + 0.1 * V0, NUM_VOL_POINTS) F_temp = [] for Vi in V: E = E0 + 9 * V0 * B0 / 16 * (((V0 / Vi)**(2.0 / 3.0) - 1)**3 * Bp + ((V0 / Vi)**(2.0 / 3.0) - 1)**2 * (6 - 4 * (V0 / Vi)**(2.0 / 3.0))) T_D = T_D0 * (V0 / Vi)**gamma x = T_D / T #t = np.linspace(0.000001,x,10000) #Deb_func = 3.0 / x**3 * np.trapz(t**3 / (np.exp(t) - 1), t) F_vib = 9.0 / 8.0 * kB * T_D - kB * T * Debye( x) + 3 * kB * T * np.log(1 - np.exp(-(T_D / T))) F = E + F_vib F_temp.append(F) eos = EquationOfState(V, F_temp) vol_eq, F_eq, B_eq = eos.fit() return F_eq except KeyboardInterrupt: raise except Exception as e: print(x) raise
def ev_bulk(volumes, energies, plot_name): """Plot volume energy curve and calculates bulk modulus Args: volumes (list of float): The volumes correlated to the energies calculated. energies (list of float): The energies the cell calculated by gfn0 xtb. plot_name (str): The file name the plot is saved to. Returns: v0 (float): The volume of minimum energy. e0 (float): The fitted minimum energy of the V/E curve. B (float): The calculated bulk modulus. """ volumes = np.array(volumes) energies = np.array(energies) * Hartree / eV eos = EquationOfState(volumes, energies, eos="murnaghan") v0, e0, B = eos.fit() print(B / kJ * 1.0e24, "GPa") # Converts into GPa ax = eos.plot() fig = plt.gcf() fig.set_size_inches(8, 5) fig.savefig(plot_name) return v0, e0, B
import pandas as pd from glob import glob from ase.eos import EquationOfState eos_name = 'birch' for f in glob('AutoCrossfilts_VASP/*.csv'): dat = pd.read_csv(f) dat.dropna(inplace=True) #if dat: print(f) try: if max(dat['kpts']) > 100000: Eos = EquationOfState(dat['volume'], dat['energy'], eos=eos_name) E0, E0_err, V0, V0_err, B, B_err, BP, BP_err, y_pred = Eos.fit() pd.DataFrame({ 'E0': [E0], 'V0': [V0], 'B': [B], 'BP': [BP] }).to_csv('Init_files/{}_Rdata_init.csv'.format( f.replace('.csv', '').replace('AutoCrossfilts_VASP/', '')), index=False) except: print('Issue with {}'.format(f))
def fit_a(conv, n, description_for_archive, analysis_type, show, push2archive): """Fit equation of state for bulk systems. The following equation is used:: sjeos (default) A third order inverse polynomial fit 10.1103/PhysRevB.67.026103 2 3 -1/3 E(V) = c + c t + c t + c t , t = V 0 1 2 3 taylor A third order Taylor series expansion about the minimum volume murnaghan PRB 28, 5480 (1983) birch Intermetallic compounds: Principles and Practice, Vol I: Principles. pages 195-210 birchmurnaghan PRB 70, 224107 pouriertarantola PRB 70, 224107 vinet PRB 70, 224107 antonschmidt Intermetallics 11, 23-32 (2003) p3 A third order polynomial fit Use:: eos = EquationOfState(volumes, energies, eos='sjeos') v0, e0, B = eos.fit() eos.plot() """ # e, v, emin, vmin = plot_conv( conv[n], calc, "fit_gb_volume2") alist = [] vlist = [] etotlist = [] magn1 = [] magn2 = [] alphas = [] for id in conv[n]: cl = db[id] st = cl.end alist.append(cl.end.rprimd[0][0]) etotlist.append(cl.energy_sigma0) vlist.append(cl.end.vol) magn1.append(cl.magn1) magn2.append(cl.magn2) alpha, beta, gamma = st.get_angles() alphas.append(alpha) print('alpha, energy: {:4.2f}, {:6.3f}'.format(alpha, cl.energy_sigma0)) fit_and_plot(U1=(alphas, etotlist, 'o-r'), image_name='figs/angle', ylabel='Total energy, eV', xlabel='Angle, deg', xlim=(89, 92.6)) if ase_flag: if 'angle' in analysis_type: eos = EquationOfState(alphas, etotlist, eos='sjeos') else: eos = EquationOfState(vlist, etotlist, eos='sjeos') # import inspect # print (inspect.getfile(EquationOfState)) v0, e0, B = eos.fit() #print "c = ", clist[2] printlog(''' v0 = {0} A^3 a0 = {1} A E0 = {2} eV B = {3} eV/A^3'''.format(v0, v0**(1. / 3), e0, B), imp='Y') savedpath = 'figs/' + cl.name + '.png' makedir(savedpath) cl.B = B * 160.218 # plt.close() # plt.clf() # plt.close('all') if 'fit' in show: mpl.rcParams.update({'font.size': 14}) eos.plot(savedpath, show=True) printlog('fit results are saved in ', savedpath, imp='y') else: printlog('To use fitting install ase: pip install ase') # plt.clf() if push2archive: push_figure_to_archive(local_figure_path=savedpath, caption=description_for_archive) return
from ase.units import kJ, Bohr, Hartree from ase.eos import EquationOfState import numpy as np filename = "TEMP_EOS_data.dat" dat = np.loadtxt(filename) # dont forget to convert to Angstrom and eV volumes = dat[:,0]**3/2.0 * Bohr**3 # only for bcc energies = dat[:,1]*Hartree energies_abinit = dat[:,2]*Hartree energies_pwscf = dat[:,3]*Hartree eos = EquationOfState(volumes, energies) v0, e0, B = eos.fit() print("PWDFT.jl: B = %18.10f GPa" % (B/kJ * 1.0e24)) eos.plot("pwdft-eos-ase.pdf") eos = EquationOfState(volumes, energies_abinit) v0, e0, B = eos.fit() print("ABINIT : B = %18.10f GPa" % (B/kJ * 1.0e24)) eos.plot("abinit-eos-ase.pdf") eos = EquationOfState(volumes, energies_pwscf) v0, e0, B = eos.fit() print("PWSCF : B = %18.10f GPa" % (B/kJ * 1.0e24)) eos.plot("pwscf-eos-ase.pdf")
else: f = open('volumes_energies.txt', 'rU') energies = [] volumes = [] lines = f.readlines() for i in range(len(lines) - 1): volumes.append(float(lines[i + 1].split()[0])) energies.append(float(lines[i + 1].split()[1])) f.close() eos = EquationOfState(volumes, energies, eos='birchmurnaghan') volume, energy, bulkmodulus = eos.fit() latticeconstant = volume**(1. / 3.) f = open('log.txt', 'w+') f.write('lattice constant = {0:18.7f} A\n'.format(latticeconstant)) f.write('bulk energy = {0:18.7f} eV\n'.format(energy)) f.write('bulk modulus = {0:18.7f} eV/A^3\n'.format(bulkmodulus)) f.close() if plot_eos is True: eos.plot(show=True) ################################################################################ # END ################################################################################
def test_rescaled_calculator(): """ Test rescaled RescaledCalculator() by computing lattice constant and bulk modulus using fit to equation of state and comparing it to the desired values """ from ase.calculators.eam import EAM from ase.units import GPa # A simple empirical N-body potential for # transition metals by M. W. Finnis & J.E. Sinclair # https://www.tandfonline.com/doi/abs/10.1080/01418618408244210 # using analytical formulation in order to avoid extra file dependence # All the constants are taken from the paper. # Please refer to the paper for more details def pair_potential(r): """ returns the pair potential as a equation 27 in pair_potential r - numpy array with the values of distance to compute the pair function """ # parameters for W c = 3.25 c0 = 47.1346499 c1 = -33.7665655 c2 = 6.2541999 energy = (c0 + c1 * r + c2 * r**2.0) * (r - c)**2.0 energy[r > c] = 0.0 return energy def cohesive_potential(r): """ returns the cohesive potential as a equation 28 in pair_potential r - numpy array with the values of distance to compute the pair function """ # parameters for W d = 4.400224 rho = (r - d)**2.0 rho[r > d] = 0.0 return rho def embedding_function(rho): """ returns energy as a function of electronic density from eq 3 """ A = 1.896373 energy = -A * np.sqrt(rho) return energy cutoff = 4.400224 W_FS = EAM(elements=['W'], embedded_energy=np.array([embedding_function]), electron_density=np.array([[cohesive_potential]]), phi=np.array([[pair_potential]]), cutoff=cutoff, form='fs') # compute MM and QM equations of state def strain(at, e, calc): at = at.copy() at.set_cell((1.0 + e) * at.cell, scale_atoms=True) at.calc = calc v = at.get_volume() e = at.get_potential_energy() return v, e # desired DFT values a0_qm = 3.18556 C11_qm = 522 # pm 15 GPa C12_qm = 193 # pm 5 GPa B_qm = (C11_qm + 2.0 * C12_qm) / 3.0 bulk_at = bulk("W", cubic=True) mm_calc = W_FS eps = np.linspace(-0.01, 0.01, 13) v_mm, E_mm = zip(*[strain(bulk_at, e, mm_calc) for e in eps]) eos_mm = EquationOfState(v_mm, E_mm) v0_mm, E0_mm, B_mm = eos_mm.fit() B_mm /= GPa a0_mm = v0_mm**(1.0 / 3.0) mm_r = RescaledCalculator(mm_calc, a0_qm, B_qm, a0_mm, B_mm) bulk_at = bulk("W", cubic=True, a=a0_qm) v_mm_r, E_mm_r = zip(*[strain(bulk_at, e, mm_r) for e in eps]) eos_mm_r = EquationOfState(v_mm_r, E_mm_r) v0_mm_r, E0_mm_r, B_mm_r = eos_mm_r.fit() B_mm_r /= GPa a0_mm_r = v0_mm_r**(1.0 / 3) # check match of a0 and B after rescaling is adequate # 0.1% error in lattice constant and bulk modulus assert abs((a0_mm_r - a0_qm) / a0_qm) < 1e-3 assert abs((B_mm_r - B_qm) / B_qm) < 1e-3
# compute MM and QM equations of state def strain(at, e, calc): at = at.copy() at.set_cell((1.0 + e) * at.cell, scale_atoms=True) at.set_calculator(calc) v = at.get_volume() e = at.get_potential_energy() return v, e eps = np.linspace(-0.01, 0.01, 13) v_qm, E_qm = zip(*[strain(bulk_at, e, qm) for e in eps]) v_mm, E_mm = zip(*[strain(bulk_at, e, mm) for e in eps]) eos_qm = EquationOfState(v_qm, E_qm) v0_qm, E0_qm, B_qm = eos_qm.fit() a0_qm = v0_qm**(1.0 / 3.0) eos_mm = EquationOfState(v_mm, E_mm) v0_mm, E0_mm, B_mm = eos_mm.fit() a0_mm = v0_mm**(1.0 / 3.0) mm_r = RescaledCalculator(mm, a0_qm, B_qm, a0_mm, B_mm) v_mm_r, E_mm_r = zip(*[strain(bulk_at, e, mm_r) for e in eps]) eos_mm_r = EquationOfState(v_mm_r, E_mm_r) v0_mm_r, E0_mm_r, B_mm_r = eos_mm_r.fit() a0_mm_r = v0_mm_r**(1.0 / 3) # check match of a0 and B after rescaling is adequete assert abs((a0_mm_r - a0_qm) / a0_qm) < 1e-3 # 0.1% error in lattice constant
def lattice_constant(volumes, energies): eos = EquationOfState(volumes, energies) v, e, B = eos.fit() a = (v * 4)**(1 / 3) return a
cell_0 = al.cell # Unit cell object of the Al bulk for eps in np.linspace(-0.02, 0.02, N_lattice_spacings): al.cell = ( 1 + eps) * cell_0 # Adjust lattice constant of unit cell # Calculate the potential energy for the Al bulk energies.append(al.get_potential_energy()) volumes.append(al.get_volume()) # # confs = read('out.txt@0:' + str(N_lattice_spacings)) # Read the configurations # # Extract volumes and energies: # volumes = [atoms.get_volume() for atoms in confs] # energies = [atoms.get_potential_energy() for atoms in confs] # # if rank == 0: # # print energies, shape(energies) # if rank == 0: print 'Energies: ' + str(energies) print 'Volumes: ' + str(volumes) # Plot energies as a function of unit cell volume (directly related to latt. const.) eos = EquationOfState(volumes, energies) v0, E_bulk, B = eos.fit() eos.plot('Al_eos.png') # Latt. const. acc. to ASE doc., but why is this correct? a_calc = (4 * v0)**(1 / 3.0) print 'a_calc: ' + str(a_calc) else: al.get_potential_energy()
print 'a0 %f\n'% (opt_cell[0][0]) # reoptimize/check volume # volumes = [] energies = [] for x in np.linspace(0.98, 1.02, 5): bulk.set_cell(opt_cell * x, scale_atoms=True) volumes.append(bulk.get_volume() / bulk.get_number_of_atoms()) energies.append(bulk.get_potential_energy() / bulk.get_number_of_atoms()) # fit EOS # eos = EquationOfState(volumes, energies) vpaf, epaf, B1 = eos.fit() print "EOS vpaf:", vpaf, "A^3" print "EOS epaf:", epaf, "eV" print "EOS B1:", B1 / kJ * 1.0e24, "GPa" strain = 0.001 diag = ([1, 0, 0], [0, 1, 0], [0, 0, 1]) # c44 # defm1 = np.array([[0, strain, strain], [strain, 0, strain], [strain, strain, 0]]) cell1 = np.dot(opt_cell, defm1 + diag)
def calc_lattice_parameter(al_bulk_ASE, A, lmbd, D, mu2): calc = get_calc((A, lmbd, D, mu2)) eps = 0.001 energies = [] volumes = [] al_bulk_ASE.set_calculator(calc) # # # Find lattice constant with lowest energy # # # cell_0 = al_bulk_ASE.get_cell() # Unit cell object of the Al bulk # DIRECTION cell_orig = cell_0 E1 = al_bulk_ASE.get_potential_energy() al_bulk_ASE.set_cell((1 + eps) * cell_0) E2 = al_bulk_ASE.get_potential_energy() direction = (E2 - E1) / abs((E2 - E1)) print('Direction', direction) # Go one step in the right al_bulk_ASE.set_cell(cell_orig) energies.append(al_bulk_ASE.get_potential_energy()) volumes.append(al_bulk_ASE.get_volume()) cell_0 = al_bulk_ASE.get_cell() al_bulk_ASE.set_cell( (1 - direction * eps) * cell_0) # Adjust lattice constant of unit cell energies.append(al_bulk_ASE.get_potential_energy()) volumes.append(al_bulk_ASE.get_volume()) # Go two steps back while (energies[-1] - energies[-2]) < 0: cell_0 = al_bulk_ASE.get_cell() # Adjust lattice constant of unit cell al_bulk_ASE.set_cell((1 - direction * eps) * cell_0) # Calculate the potential energy for the Al bulk energies.append(al_bulk_ASE.get_potential_energy()) volumes.append(al_bulk_ASE.get_volume()) cell_0 = al_bulk_ASE.get_cell() al_bulk_ASE.set_cell( (1 - direction * eps) * cell_0) # Adjust lattice constant of unit cell # Calculate the potential energy for the Al bulk energies.append(al_bulk_ASE.get_potential_energy()) volumes.append(al_bulk_ASE.get_volume()) # plt.plot((4 * np.asarray(volumes))**(1 / 3), energies) # plt.show() # Plot energies as a function of unit cell volume (directly related to latt. const.) eos = EquationOfState(volumes, energies) v0, E, B = eos.fit() # Latt. const. acc. to ASE doc., but why is this correct? print('asdasdasdasdasd: ', E) a_calc = (4 * v0)**(1 / 3.0) # a_calc = v0**(1 / 3.0) print('a=' + str(a_calc)) al_bulk_ASE.set_cell(cell_0) return a_calc
import numpy as np from ase.lattice.cubic import FaceCenteredCubic from ase.calculators.emt import EMT from ase.eos import EquationOfState from ase.db import connect db = connect("refs.db") metals = ["Al", "Au", "Cu", "Ag", "Pd", "Pt", "Ni"] for m in metals: atoms = FaceCenteredCubic(m) atoms.set_calculator(EMT()) e0 = atoms.get_potential_energy() a = atoms.cell[0][0] eps = 0.05 volumes = (a * np.linspace(1 - eps, 1 + eps, 9)) ** 3 energies = [] for v in volumes: atoms.set_cell([v ** (1.0 / 3)] * 3, scale_atoms=True) energies.append(atoms.get_potential_energy()) eos = EquationOfState(volumes, energies) v1, e1, B = eos.fit() atoms.set_cell([v1 ** (1.0 / 3)] * 3, scale_atoms=True) ef = atoms.get_potential_energy() db.write(atoms, metal=m, latticeconstant=v1 ** (1.0 / 3), energy_per_atom=ef / len(atoms))
from ase.calculators.emt import EMT from ase.eos import EquationOfState as EOS, eos_names scipy # silence pyflakes b = bulk('Al', 'fcc', a=4.0, orthorhombic=True) b.set_calculator(EMT()) cell = b.get_cell() volumes = [] energies = [] for x in np.linspace(0.98, 1.01, 5): b.set_cell(cell * x, scale_atoms=True) volumes.append(b.get_volume()) energies.append(b.get_potential_energy()) results = [] for name in eos_names: if name == 'antonschmidt': # Someone should fix this! continue eos = EOS(volumes, energies, name) v, e, b = eos.fit() print('{0:20} {1:.8f} {2:.8f} {3:.8f} '.format(name, v, e, b)) assert abs(v - 3.18658700e+01) < 4e-4 assert abs(e - -9.76187802e-03) < 5e-7 assert abs(b - 2.46812688e-01) < 2e-4 results.append((v, e, b)) print(np.ptp(results, 0)) print(np.mean(results, 0))
from ase.calculators.emt import EMT from ase.eos import EquationOfState from ase.db import connect db = connect('refs.db') metals = ['Al', 'Au', 'Cu', 'Ag', 'Pd', 'Pt', 'Ni'] for m in metals: atoms = FaceCenteredCubic(m) atoms.calc = EMT() e0 = atoms.get_potential_energy() a = atoms.cell[0][0] eps = 0.05 volumes = (a * np.linspace(1 - eps, 1 + eps, 9))**3 energies = [] for v in volumes: atoms.set_cell([v**(1. / 3)] * 3, scale_atoms=True) energies.append(atoms.get_potential_energy()) eos = EquationOfState(volumes, energies) v1, e1, B = eos.fit() atoms.set_cell([v1**(1. / 3)] * 3, scale_atoms=True) ef = atoms.get_potential_energy() db.write(atoms, metal=m, latticeconstant=v1**(1. / 3), energy_per_atom=ef / len(atoms))
from ase.io.trajectory import Trajectory from ase.io import read from ase.units import kJ from ase.eos import EquationOfState, vinet numberline = 0 f3 = open('e-v.dat') for line in f3: numberline = numberline + 1 energies = np.zeros(numberline) volumes = np.zeros(numberline) i = 0 with open("e-v.dat") as f3: for line in f3: line = line.rstrip('\n') volumes[i], energies[i] = line.split(" ") i = i + 1 eos = EquationOfState(volumes, energies, eos='vinet') eos.fit() e0, B, Bp, v0 = eos.eos_parameters fp = open("helmholtz-volume.dat") for i, line in enumerate(fp): if i == 1: a, b, Fmin, d, e, V = line.split() fp.close() print(float(Fmin) - vinet(float(V), e0, B, Bp, v0)), #print eos #v0, e0, B= eos.fit()
def main(): latoms = vf.get_list_of_atoms() latoms2 = vf.get_list_of_outcar() natoms = latoms[0].get_positions().shape[0] V = np.array([]) ca = np.array([]) magtot = np.array([]) for i in np.arange(len(latoms)): temp = latoms[i].get_volume()/natoms V = np.append(V, temp) temp = latoms[i].get_cell()[:] temp = np.linalg.norm( temp[2,:] ) / np.linalg.norm( temp[0,:] ) ca = np.append(ca, temp) try: temp = latoms2[i].get_magnetic_moment()/natoms except: temp = 0 magtot = np.append(magtot, temp) magtot = np.abs(magtot) jobn, Etot, Eent, pres = vf.vasp_read_post_data() a_pos = np.array([]) # scale in POSCAR for i in jobn: filename = './y_dir/%s/CONTCAR' %(i) with open(filename,'r') as f: a_pos = np.append( a_pos, float( f.readlines()[1] ) ) Etot = Etot / natoms fitres = myfitting(V, Etot) V1 = np.array([]) a1_pos = np.array([]) p_dft = np.array([]) for i in np.arange(len(jobn)): p_dft = np.append( p_dft, pres[i,0:2].mean()*(0.1) ) # pressure [GPa] if jobn[i][0] == '0' or jobn[i][0] == '1' : V1 = np.append( V1, V[i] / float(jobn[i])) a1_pos = np.append( a1_pos, a_pos[i] / (float(jobn[i]))**(1/3) ) if (V1.std() > 1e-8) or (a1_pos.std() > 1e-8) : print('V1, V1.std(), a1_pos.std():') print( V1, V1.std(), a1_pos.std() ) sys.exit('V1 or a1_pos is wrong. Abort!') else: V1 = V1.mean() a1_pos = a1_pos.mean() write_output(V, Etot, fitres, V1, a1_pos, p_dft) plot_eos(V, Etot, fitres, p_dft, V1, ca, magtot) ASE_eos = EquationOfState(V, Etot, eos='birchmurnaghan') temp = np.array([ ASE_eos.fit()[1] - fitres[0], ASE_eos.fit()[0] - fitres[1], ASE_eos.fit()[2] - fitres[2] ]) print('==> check with ASE fitting, diff: {0} \n'.format(temp) )
from ase.io import read from ase.units import kJ from ase.eos import EquationOfState configs = read('Ag.traj@0:5') # read 5 configurations # Extract volumes and energies: volumes = [ag.get_volume() for ag in configs] energies = [ag.get_potential_energy() for ag in configs] eos = EquationOfState(volumes, energies) v0, e0, B = eos.fit() print(B / kJ * 1.0e24, 'GPa') eos.plot('Ag-eos.png')
def test_forceqmmm(): # parameters N_cell = 2 R_QMs = np.array([3, 7]) # setup bulk and MM region bulk_at = bulk("Cu", cubic=True) sigma = (bulk_at * 2).get_distance(0, 1) * (2.**(-1. / 6)) mm = LennardJones(sigma=sigma, epsilon=0.05) qm = EMT() # compute MM and QM equations of state def strain(at, e, calc): at = at.copy() at.set_cell((1.0 + e) * at.cell, scale_atoms=True) at.calc = calc v = at.get_volume() e = at.get_potential_energy() return v, e eps = np.linspace(-0.01, 0.01, 13) v_qm, E_qm = zip(*[strain(bulk_at, e, qm) for e in eps]) v_mm, E_mm = zip(*[strain(bulk_at, e, mm) for e in eps]) eos_qm = EquationOfState(v_qm, E_qm) v0_qm, E0_qm, B_qm = eos_qm.fit() a0_qm = v0_qm**(1.0 / 3.0) eos_mm = EquationOfState(v_mm, E_mm) v0_mm, E0_mm, B_mm = eos_mm.fit() a0_mm = v0_mm**(1.0 / 3.0) mm_r = RescaledCalculator(mm, a0_qm, B_qm, a0_mm, B_mm) v_mm_r, E_mm_r = zip(*[strain(bulk_at, e, mm_r) for e in eps]) eos_mm_r = EquationOfState(v_mm_r, E_mm_r) v0_mm_r, E0_mm_r, B_mm_r = eos_mm_r.fit() a0_mm_r = v0_mm_r**(1.0 / 3) # check match of a0 and B after rescaling is adequete assert abs( (a0_mm_r - a0_qm) / a0_qm) < 1e-3 # 0.1% error in lattice constant assert abs((B_mm_r - B_qm) / B_qm) < 0.05 # 5% error in bulk modulus # plt.plot(v_mm, E_mm - np.min(E_mm), 'o-', label='MM') # plt.plot(v_qm, E_qm - np.min(E_qm), 'o-', label='QM') # plt.plot(v_mm_r, E_mm_r - np.min(E_mm_r), 'o-', label='MM rescaled') # plt.legend() at0 = bulk_at * N_cell r = at0.get_distances(0, np.arange(1, len(at0)), mic=True) print(len(r)) del at0[0] # introduce a vacancy print("N_cell", N_cell, 'N_MM', len(at0)) ref_at = at0.copy() ref_at.calc = qm opt = FIRE(ref_at) opt.run(fmax=1e-3) u_ref = ref_at.positions - at0.positions us = [] for R_QM in R_QMs: at = at0.copy() mask = r < R_QM print('R_QM', R_QM, 'N_QM', mask.sum(), 'N_total', len(at)) qmmm = ForceQMMM(at, mask, qm, mm, buffer_width=2 * qm.rc) at.calc = qmmm opt = FIRE(at) opt.run(fmax=1e-3) us.append(at.positions - at0.positions) # compute error in energy norm |\nabla u - \nabla u_ref| def strain_error(at0, u_ref, u, cutoff, mask): I, J = neighbor_list('ij', at0, cutoff) I, J = np.array([(i, j) for i, j in zip(I, J) if mask[i]]).T v = u_ref - u dv = np.linalg.norm(v[I, :] - v[J, :], axis=1) return np.linalg.norm(dv) du_global = [ strain_error(at0, u_ref, u, 1.5 * sigma, np.ones(len(r))) for u in us ] du_local = [strain_error(at0, u_ref, u, 1.5 * sigma, r < 3.0) for u in us] print('du_local', du_local) print('du_global', du_global) # check local errors are monotonically decreasing assert np.all(np.diff(du_local) < 0) # check global errors are monotonically converging assert np.all(np.diff(du_global) < 0) # biggest QM/MM should match QM result assert du_local[-1] < 1e-10 assert du_global[-1] < 1e-10
def do_volume_autotune(self, encut=None, start_scan=0.925, end_scan=1.075, num_scans=20, exclude_type=None, only_type=None): if not os.path.isfile(self.poscar_vol): encut = encut if encut != None else self.do_encut_autotune() logging.info("Commencing volume scan...") print("Determining optimal volume...") start_scan, end_scan, num_scans, exclude_type, only_type, vols, ens = self.set_volume_autotune_params( start_scan=start_scan, end_scan=end_scan, num_scans=num_scans, exclude_type=exclude_type, only_type=only_type) # Load POSCAR try: sys = read(self.poscar_init) calc = self.calc_dict['volume'] calc.set(encut=encut) start_cell = sys.get_cell() except: logging.error( "Initial POSCAR file could not be found at {}".format( poscar_init)) print("Error: See error log for details.") return None logging.info("Loaded initial POSCAR file.") # Do volume scan logging.info("Performing volume scan.") for x in np.linspace(start_scan, end_scan, num_scans): sys.set_cell(x * start_cell, scale_atoms=True) sys.set_calculator(calc) ens.append(sys.get_potential_energy()) vols.append(sys.get_volume()) logging.info("Volume scan complete.") # Fit EoS logging.info("Fitting equations of state.") eos_types = "sjeos taylor murnaghan birch birchmurnaghan pouriertarantola p3 antonschmidt".split( ) if exclude_type != None: _ = [eos_types.remove(i) for i in exclude] elif only_type != None: eos_types = only_type eos_fits = {} for typ in eos_types: eos = EquationOfState(vols, ens, eos=typ) try: v, e, B = eos.fit() eos_fits[typ] = { 'volume': v, 'energy': e, 'buld_modulus': B } except: print("Unable to fit type {}.".format(typ)) # Rescale initial cell to optimized volume logging.info("Optimal volume found. Rescaling original cell.") vol_avg = np.average( [eos_fits[key]['volume'] for key in eos_fits.keys()]) sys.set_cell(start_cell, scale_atoms=True) scale = sys.get_volume() / vol_avg sys.set_cell(scale * start_cell, scale_atoms=True) # Perform sc-step logging.info("Performing self-consistent step.") print("Second relaxation") #calc.set(isif=3) # Everything can relax sys.set_calculator(calc) en = sys.get_potential_energy() # Set relaxed structure as active structure print("Setting relaxed structure to active structure.") self.system = sys # Save structure logging.info("Saving self-consistent calculator.") write(self.poscar_vol, sys) print("Done.") logging.info("Volume scan complete.") self.clean_up() else: sys = read(self.poscar_vol) print("Volume optimized system ready.")
def getBulkModulus(jobID, latticeParams): """ Use optimized lattice parameters from fmin(getEnergy). Calculate energies around the minimum. Returns optimized atomic positions, energy of minimum, calculated bulk modulus, and writes wavefunctions to file inp.gpw """ ### INITIALIZE vol, eng = [], [] jobObject, bulkObject, calcObject, dft, preCalcObject, preCalcObject, existsPrecalc, optAtoms, magmoms = initialize( jobID, latticeParams) with open('lattice_opt.log', 'r') as f: nIter = len(f.readlines()) + 9 #count number of ionic steps taken optCell = optAtoms.get_cell() strains = np.linspace(0.99, 1.01, 9) ### GENERATE dE/dV plot for i, strain in enumerate(strains): atoms = deepcopy(optAtoms) atoms.set_cell(optCell * strain, scale_atoms=True) ### PRECALCULATE if existsPrecalc: optimizePos(atoms, dft, preCalcObject, magmoms, restart=False, saveWF=True) ### CALCULATE optimizePos(atoms, dft, calcObject, magmoms, restart=existsPrecalc, saveWF=False) ### COLLECT RESULTS energy = atoms.get_potential_energy() volume = atoms.get_volume() vol.append(deepcopy(volume)) eng.append(deepcopy(energy)) parprint('%f %f' % (strain, energy)) ### COLLECT DETAILED DATA ABOUT MINIMUM ENERGY STATE if i == 4: pos = atoms.get_scaled_positions() vMin = deepcopy(volume) io.write('optimized.traj', atoms) magmom = atoms.get_magnetic_moments( ) if calcObject.magmom > 0 else np.zeros(len(atoms)) if dft == 'gpaw': atoms.calc.write('inp.gpw', mode='all') #for use in getXCContribs aHat, quadR2 = quadFit(np.array(deepcopy(vol)), np.array(deepcopy(eng))) b0 = aHat * 2 * vMin * 160.2 # units: eV/A^6 * A^3 * 1, where 1 === 160.2 GPa*A^3/eV parprint('quadR2 = ' + str(quadR2)) try: eos = EquationOfState(vol, eng) v0, e0, b = eos.fit() eos.plot(filename='bulk-eos.png', show=False) b0 = b / kJ * 1e24 #GPa use this value if EOS doesn't fail except ValueError: e0 = eng[4] return (optCell, nIter, pos, magmom, e0, b0, quadR2) #GPa