def sort_properties_files(): """ Paramters: None Returns: tuple: returns a tuple of lists of lattice constants, bulk moduli, interpolated lattice constants and file paths. """ settings = read_settings_file() filenames = sorted(glob.glob("property_calculations/properties_*")) steps = 1 + 2 * settings['LC_steps'] LC_list = [] BulkM_list = [] N_list = [] LCi_list = [] for i in range(0, round(len(filenames) / steps)): LCa, LCb, LCc, BulkM, N, LCia, LCib, LCic = find_eq_lc( filenames[steps * i:steps * (i + 1)]) LC_list.append([LCa, LCb, LCc]) BulkM_list.append(BulkM) N_list.append(N) LCi_list.append([LCia, LCib, LCic]) """ for fname in filenames[steps*i:steps*i+steps]: if fname != N: os.remove(fname) """ return LC_list, LCi_list, BulkM_list, N_list
def debye_lindemann(a, msd, temp): """Calculates the debye temperature and the Lindemann criterion. Original cell is assumed to be sc, bcc or fcc. Lattice constants in a,b and c may be different. Parameters: a (obj): a is an atoms object of class defined in ase. msd (float): mean square displacment temp (float): temperature Returns list: list of debye temperature and lindemann criterion. """ s = read_settings_file() debye = math.sqrt(9 * units._hbar**2 * temp / (units._k * a.get_masses()[0] * units._amu * msd) * units.m**2) z = s['supercell_size'] n = len(a) / z**3 lc = a.get_cell_lengths_and_angles()[0:3] if n == 1: nnd = min(lc) elif n == 2: nnd = 1 / 2 * math.sqrt(lc[0]**2 + lc[1]**2 + lc[2]**2) elif n == 4: if np.max(lc) != np.min(lc): # all values in lc are not the same lc = np.delete(lc, np.argwhere(lc == max(lc))) nnd = 1 / 2 * math.sqrt(lc[0]**2 + lc[1]**2) else: nnd = 9999 lindemann = math.sqrt(msd) / nnd return debye, lindemann
def lattice_constants(a): """ Calculates the lattice constant of a materialself. Parameters: a (obj): a is an atoms object of class defined in ase. Returns: list: returns the lattice_constants, in the 3 dimensions. """ s = read_settings_file()['supercell_size'] lc = list(a.get_cell_lengths_and_angles()) return [lc[0] / s, lc[1] / s, lc[2] / s]
def test_read_settings(self): # create empty json file with open("empty_test.json", "w+") as f: f.write("{\n \"koko\": 1000 \n}") with open("empty_test.json") as f: data = json.load(f) with self.assertRaises(KeyError): data["temperatur"] # Now test read_settings_file function data = read_settings.read_settings_file("empty_test.json") self.assertTrue(data["time_step"], 5) self.assertTrue(data["max_steps"], 200) self.assertTrue(data["temperature"], 300) self.assertTrue(data["ensemble"], "NVE") self.assertTrue(data["friction"], 0.001) self.assertTrue(data["decimals"], 5) os.remove("empty_test.json")
def calc_properties(a_old, a, id, d, ma): """Calculates prioperties and writes them in a file. Parameters: a_old (obj): a_old is an atoms object from clas defined from ase. it's the old atom that needs to be updated. a (obj): a is an atoms object from clas defined from ase. it's the new updated atom obj for MD molecular dyanimcs. id (str): d (int): ma (boolean): Returns: None """ f = open("property_calculations/properties_" + id + ".txt", "r") epot, ekin, etot, temp = energies_and_temp(a) msd = meansquaredisp(a, a_old) settings = read_settings_file() ln = sum(1 for line in f) time = settings['time_step'] * settings['interval'] * (ln - 6) selfd = self_diff(a, msd, time) lc = lattice_constants(a) vol, pr = volume_pressure(a) f.close() file = open("property_calculations/properties_" + id + ".txt", "a+") file.write( ss(time, d) + ss(epot, d) + ss(ekin, d) + ss(etot, d) + ss(temp, 2) + ss(msd, d)) file.write(ss(selfd, d) + ss(lc[0], 3) + ss(lc[1], 3) + ss(lc[2], 3)) file.write(ss(vol, 3) + ss(pr, d)) if ma: debye, linde = debye_lindemann(a, msd, temp) file.write(ss(debye, 2) + ss(linde, d)) file.write("\n") file.close() return
def specific_heat(temp_store, N, atoms): """Calculates the specific heat for a material. Given by the formula: (E[T²] - E[T]²)/ E[T]² = 3*2^-1*N^-1*(1-3*N*Kb*2^-1*Cv^-1). Where Kb is boltzmansconstant, N is the total number of atoms, T is temperature and Cv the specific heat. E[A(t)] calculates the expectation value of A, which can in this case be seen as a time average for the phase variable A(t). Parameters: temp_store (list): The list over all intantaneous temperatures of a material once MD has been run. N (int): The total number of atoms in the material. Returns: float: specific heat is returned (J/(K*Kg)) """ if len(temp_store) == 0: raise ValueError("temp_store is empty, invalid value.") steps = len(temp_store) z = sum(atoms.get_masses()) * units._amu # total mass: atomic units to kg # Set M = (E[T²] - E[T]²)/ E[T]² ET = sum(temp_store) / steps ET2 = sum(np.array(temp_store)**2) / steps M = (ET2 - ET**2) / ET**2 settings = read_settings_file() N = N / settings['supercell_size']**3 #print("M:", M) #print("N:", N) #print("T:", temp_store) #print(sum(np.array(temp_store)**2)) #print("ET:", ET) #print("ET2:", ET2) #Cv1 = -9*N*units.kB/(4*N*M-6)/z*units._e * settings['supercell_size']**3 # specific heat J/(K*Kg) Cv2 = ((9 * ET**2 * N * units._k) / (ET**2 * (6 + 4 * N) - 4 * N * ET2)) / z * settings['supercell_size']**3 #print("Cv1:", Cv1) #print("Cv2:", Cv2) return Cv2
from ase.visualize import view from ase.build import bulk import md from read_settings import read_settings_file import numpy as np import copy # http://www-ferp.ucsd.edu/LIB/PROPS/PANOS/cu.html # properties of copper atoms_l = [] #atoms_l.append(bulk('Ar', 'fcc', a=5.26, cubic=True)) atoms_l.append(bulk('Cu', 'fcc', a=3.6, cubic=True)) #atoms = bulk('Ar', 'fcc', a=5.26, cubic=True) #atoms = bulk('Cu', 'fcc', a=3.6, cubic=True) settings = read_settings_file('acc_test_setting.json') atoms_list = [] for atoms in atoms_l: if settings['vol_relax']: cell = np.array(atoms.get_cell()) P = settings['LC_steps'] for i in range(-P, 1 + P): atoms_v = copy.deepcopy(atoms) atoms_v.set_cell(cell * (1 + i * settings['LC_mod'])) atoms_list.append(atoms_v) else: atoms_list.append(atoms) print(atoms_list)
def main(): # don't allow Python libraries to start spanning threads # over the available cores supercomputer_init() # set up variables for parallelization comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() # read materials from settings file settings = read_settings_file() mp_properties = read_mp_properties(settings['materials']) # try to create folder 'property_calculations' # if it already exists, continue with the program try: os.mkdir('property_calculations') except: pass # try to create folder 'trajectory_files' # if it already exists, continue with the program try: os.mkdir('trajectory_files') except: pass # create one list of all atom objects in data-file atoms_list = [] for cif in mp_properties['cif']: f = open('tmp'+str(rank)+'.cif', 'w+') f.write(cif) f.close() atoms = ase.io.read('tmp'+str(rank)+'.cif') lengths = atoms.get_cell_lengths_and_angles()[0:3] angles = atoms.get_cell_lengths_and_angles()[3:6] if settings['cubic_only']: if len(set(lengths)) == 1 and len(set(angles)) == 1 and angles[0] == 90: if settings['vol_relax']: cell = np.array(atoms.get_cell()) P = settings['LC_steps'] for i in range(-P,1+P): atoms_v = copy.deepcopy(atoms) atoms_v.set_cell(cell*(1+i*settings['LC_mod'])) atoms_list.append(atoms_v) else: atoms_list.append(atoms) else: if settings['vol_relax']: cell = np.array(atoms.get_cell()) P = settings['LC_steps'] for i in range(-P,1+P): atoms_v = copy.deepcopy(atoms) atoms_v.set_cell(cell*(1+i*settings['LC_mod'])) atoms_list.append(atoms_v) else: atoms_list.append(atoms) print("Created atoms list of length " + str(len(atoms_list))) os.remove("tmp"+str(rank)+".cif") # Run the molecular dynamics in parallell (might want to # improve it) if rank == 0: jobs = np.arange(0, len(atoms_list), dtype=np.int) job_array = np.array_split(jobs, size) #print("we have", size, " processes.") for i in range(0, size): comm.isend(len(job_array[i]), dest=i, tag=i) comm.Isend([job_array[i],MPI.INT], dest=i, tag=i) # how do I send in the correct atoms-object to md_run? l = comm.recv(source=0, tag=rank) data = np.ones(l,dtype=np.int) comm.Recv([data,MPI.INT],source=0, tag=rank) for id in data: #print("ID: ", id) #print(atoms) try: md.run_md(atoms_list[id], str(id).zfill(4), 'settings.json') except Exception as e: print("Run broke!:"+str(e)) print("Happened for ID:" + str(id).zfill(4)) comm.Barrier()
def run_md(atoms, id, settings_fn): """The function does Molecular Dyanamic simulation (MD) on a material, given by argument atoms. Parameters: atoms (obj): an atoms object defined by class in ase. This is the material which MD will run on. id (int): an identifying number for the material. Returns: obj:atoms object defined in ase, is returned. """ # Read settings settings = read_settings_file(settings_fn) initial_unitcell_atoms = copy.deepcopy(atoms) # Scale atoms object, cubic size = settings['supercell_size'] atoms = atoms * size * (1, 1, 1) # atoms = atoms * (size,size,size) #print(atoms.get_chemical_symbols()) N = len(atoms.get_chemical_symbols()) # Use KIM for potentials from OpenKIM use_kim = settings['use_kim'] # Use Asap for a huge performance increase if it is installed use_asap = True # Create a copy of the initial atoms object for future reference old_atoms = copy.deepcopy(atoms) # Describe the interatomic interactions with OpenKIM potential if use_kim: # use KIM potential atoms.calc = KIM( "LJ_ElliottAkerson_2015_Universal__MO_959249795837_003") else: # otherwise, default to asap3 LennardJones atoms.calc = LennardJones([18], [0.010323], [3.40], rCut=6.625, modified=True) # Set the momenta corresponding to temperature from settings file MaxwellBoltzmannDistribution(atoms, settings['temperature'] * units.kB) # Select integrator if settings['ensemble'] == "NVE": from ase.md.verlet import VelocityVerlet dyn = VelocityVerlet(atoms, settings['time_step'] * units.fs) elif settings['ensemble'] == "NVT": from ase.md.langevin import Langevin dyn = Langevin(atoms, settings['time_step'] * units.fs, settings['temperature'] * units.kB, settings['friction']) interval = settings['interval'] # Creates trajectory files in directory trajectory_files traj = Trajectory("trajectory_files/" + id + ".traj", 'w', atoms) dyn.attach(traj.write, interval=interval) # Number of decimals for most calculated properties. decimals = settings['decimals'] # Boolean indicating if the material is monoatomic. monoatomic = len(set(atoms.get_chemical_symbols())) == 1 # Calculation and writing of properties properties.initialize_properties_file(atoms, initial_unitcell_atoms, id, decimals, monoatomic) dyn.attach(properties.calc_properties, 100, old_atoms, atoms, id, decimals, monoatomic) # unnecessary, used for logging md runs # we should write some kind of logger for the MD def logger(a=atoms): # store a reference to atoms in the definition. """Function to print the potential, kinetic and total energy.""" epot = a.get_potential_energy() / len(a) ekin = a.get_kinetic_energy() / len(a) t = ekin / (1.5 * units.kB) print('Energy per atom: Epot = %.3feV Ekin = %.3feV (T=%3.0fK) ' 'Etot = %.3feV' % (epot, ekin, t, epot + ekin)) # Running the dynamics dyn.attach(logger, interval=interval) #logger() #dyn.run(settings['max_steps']) # check for thermal equilibrium counter = 0 equilibrium = False for i in range(round(settings['max_steps'] / settings['search_interval'])): # hyperparameter epot, ekin_pre, etot, t = properties.energies_and_temp(atoms) # kör steg som motsvarar säg 5 fs dyn.run(settings['search_interval']) # hyperparamter epot, ekin_post, etot, t = properties.energies_and_temp(atoms) #print(abs(ekin_pre-ekin_post) / math.sqrt(N)) #print(counter) if (abs(ekin_pre - ekin_post) / math.sqrt(N)) < settings['tolerance']: counter += 1 else: counter = 0 if counter > settings['threshold']: # hyperparameter print("reached equilibrium") equilibrium = True break if equilibrium: dyn.run(settings['max_steps']) properties.finalize_properties_file(atoms, id, decimals, monoatomic) else: properties.delete_properties_file(id) raise RuntimeError("MD did not find equilibrium") return atoms
def extract(): """ Paramters: None Returns: None """ file = open("property_calculations/collected_data.txt", "w+") settings = read_settings_file() d = settings['decimals'] def lj(str, k=d): return " " + str.ljust(k + 10) file.write( lj("Material ID") + lj("Material") + lj("Cohesive energy") + lj("MSD") + lj("Self_diff") + lj("Specific heat")) if settings['vol_relax']: file.write( lj("Lattice const a") + lj("Lattice const b") + lj("Lattice const c")) file.write(lj("Interp LC a") + lj("Interp LC b") + lj("Interp LC c")) file.write(lj("Bulk modulus")) file.write(lj("Debye", 2) + lj("Lindemann")) file.write("\n") file.write( lj(" ") + lj(" ") + lj("eV/atom") + lj("Å^2") + lj("mm^2/s") + lj("J/(K*Kg)")) if settings['vol_relax']: file.write( lj("Å") + lj("Å") + lj("Å") + lj("Å") + lj("Å") + lj("Å") + lj("Pa")) file.write(lj("K", 2) + lj("1")) file.write("\n") file.close() N_list = glob.glob("property_calculations/properties_*") if settings['vol_relax']: LC_list, LCi_list, BulkM_list, N_list = sort_properties_files() for i, filename in enumerate(sorted(N_list)): f = open(filename, "r") lines = f.read().split("\n") f.close() if lines[-4] == 'Time averages:': matID = lines[0].split(":")[1] mat = lines[2].split()[1] Ecoh = lines[-1].split()[0] msd = lines[-1].split()[4] selfd = lines[-1].split()[5] Cv = lines[-1].split()[7] file = open("property_calculations/collected_data.txt", "a+") file.write( lj(matID) + lj(mat) + lj(Ecoh) + lj(msd) + lj(selfd) + lj(Cv)) if settings['vol_relax']: LC = LC_list[i] LCi = LCi_list[i] BulkM = BulkM_list[i] file.write( pr.ss(LC[0], d + 4) + pr.ss(LC[1], d + 4) + pr.ss(LC[2], d + 4)) file.write( pr.ss(LCi[0], d + 4) + pr.ss(LCi[1], d + 4) + pr.ss(LCi[2], d + 4)) file.write(pr.ss(BulkM, d + 4)) if len(lines[-1].split()) > 8: debye = lines[-1].split()[8] linde = lines[-1].split()[9] file.write(pr.ss(debye, d + 4) + pr.ss(linde, d + 4)) file.write("\n") file.close() return
def find_eq_lc(fnames): """ Parameters: fnames (list): A list of strings of file path/names Returns: tuple: returns a tuple of lattice constant, bulk modulus, filename and interpolated lattice constant. """ LCa = 9999 LCb = 9999 LCc = 9999 Etot = 9999 N = "" E_list = [] LCa_list = [] LCb_list = [] LCc_list = [] V_list = [] for name in fnames: f = open(name, "r+") lines = f.read().split("\n") E = float(lines[-1].split()[2]) # The strucutre of header is always the same. l_a = float(lines[11].split()[7]) l_b = float(lines[11].split()[8]) l_c = float(lines[11].split()[9]) E_list.append(E) LCa_list.append(l_a) LCb_list.append(l_b) LCc_list.append(l_c) V_list.append(float(lines[11].split()[10])) if E < Etot: Etot = E LCa = l_a LCb = l_b LCc = l_c N = name f.close() settings = read_settings_file() n = LCa_list[0] * LCb_list[0] * LCc_list[0] * settings[ 'supercell_size']**3 / V_list[0] oLCa = LCa_list[settings['LC_steps']] # Original lattice constant a. s_list = [x / oLCa for x in LCa_list] oV = V_list[settings['LC_steps']] p = np.polyfit(s_list, E_list, 2) LCia = 0 LCib = 0 LCic = 0 if p[0] <= 0: warnings.warn("Dynamically unstable in this range. " + str(fnames)) B = 0 LCi = 0 else: i_scaling = -p[1] / (2 * p[0]) # interpolated lattice scaling factor LCia = i_scaling * LCa LCib = i_scaling * LCb LCic = i_scaling * LCc E_interp = np.polyval(p, i_scaling) V_interp = oV * i_scaling**3 q = np.polyfit(V_list, E_list, 2) B = V_interp * (2 * q[0]) * 160.2 # conversion from ev/Å^3 to GigaPa return LCa, LCb, LCc, B, N, LCia, LCib, LCic
def run_md(atoms, id): # Read settings settings = read_settings_file() # Use KIM for potentials from OpenKIM use_kim = True # Use Asap for a huge performance increase if it is installed use_asap = True # Create a copy of the initial atoms object for future reference old_atoms = copy.deepcopy(atoms) # Describe the interatomic interactions with OpenKIM potential if use_kim: # use KIM potential atoms.calc = KIM( "LJ_ElliottAkerson_2015_Universal__MO_959249795837_003") else: # otherwise, default to asap3 LennardJones atoms.calc = LennardJones([18], [0.010323], [3.40], rCut=6.625, modified=True) # Set the momenta corresponding to temperature from settings file MaxwellBoltzmannDistribution(atoms, settings['temperature'] * units.kB) # Select integrator if settings['ensemble'] == "NVE": from ase.md.verlet import VelocityVerlet dyn = VelocityVerlet(atoms, settings['time_step'] * units.fs) elif settings['ensemble'] == "NVT": from ase.md.langevin import Langevin dyn = Langevin(atoms, settings['time_step'] * units.fs, settings['temperature'] * units.kB, settings['friction']) traj = Trajectory('ar.traj', 'w', atoms) dyn.attach(traj.write, interval=1000) # Identity number given as func. parameter to keep track of properties # Number of decimals for most calculated properties decimals = settings['decimals'] # Calculation and writing of properties properties.initialize_properties_file(atoms, id, decimals) dyn.attach(properties.calc_properties, 100, old_atoms, atoms, id, decimals) # unnecessary, used for logging md runs # we should write some kind of logger for the MD def logger(a=atoms): # store a reference to atoms in the definition. """Function to print the potential, kinetic and total energy.""" epot = a.get_potential_energy() / len(a) ekin = a.get_kinetic_energy() / len(a) t = ekin / (1.5 * units.kB) print('Energy per atom: Epot = %.3feV Ekin = %.3feV (T=%3.0fK) ' 'Etot = %.3feV' % (epot, ekin, t, epot + ekin)) # Running the dynamics dyn.attach(logger, interval=1000) logger() dyn.run(settings['max_steps']) return atoms
def finalize_properties_file(a, id, d, ma): """ Calculates and records the properties of a material. Parameters: a (obj): Atoms object form ase. id (str): a special number identifying the material system. d (int): a number for the formatting of file. Give a correct appending for strings. ma (boolean): ma is a boolean, for True the system is monoatomic. Returns: None """ epot = [] ekin = [] etot = [] temp = [] msd = [] selfd = [] pr = [] debye = [] linde = [] settings = read_settings_file() f = open("property_calculations/properties_" + id + ".txt", "r") f_lines = f.readlines() steps = math.floor(settings['max_steps'] / settings['interval']) for line in f_lines[-steps:]: epot.append(float(line.split()[1])) ekin.append(float(line.split()[2])) etot.append(float(line.split()[3])) temp.append(float(line.split()[4])) msd.append(float(line.split()[5])) selfd.append(line.split()[6]) pr.append(float(line.split()[11])) if ma: debye.append(float(line.split()[12])) linde.append(float(line.split()[13])) f.close() epot_t = sum(epot) / steps ekin_t = sum(ekin) / steps etot_t = sum(etot) / steps temp_t = sum(temp) / steps msd_t = sum(msd) / steps selfd_t = sum(float(i) for i in selfd[1:]) / (steps - 1) pr_t = sum(pr) / steps debye_t = sum(debye) / steps linde_t = sum(linde) / steps Cv = specific_heat(temp, len(a.get_chemical_symbols()), a) file = open("property_calculations/properties_" + id + ".txt", "a+") file.write("\nTime averages:\n") # Help function for formating def lj(str, k=d): return " " + str.ljust(k + 6) file.write( lj(" ") + lj("Epot") + lj("Ekin") + lj("Etot") + lj("Temp", 2) + lj("MSD")) file.write(lj("Self_diff") + lj("Pressure")) file.write(lj("Spec_heat")) if ma: file.write(lj("DebyeT", 2) + lj("Lindemann")) file.write("\n") file.write( lj(" ") + lj("eV/atom") + lj("eV/atom") + lj("eV/atom") + lj("K", 2) + lj("Å^2")) file.write(lj("mm^2/s") + lj("GPa")) file.write(lj("J/(K*Kg)")) if ma: file.write(lj("K", 2) + lj("1")) file.write("\n") file.write( lj(" ") + ss(epot_t, d) + ss(ekin_t, d) + ss(etot_t, d) + ss(temp_t, 2) + ss(msd_t, d)) file.write(ss(selfd_t, d) + ss(pr_t, d)) file.write(ss(Cv, d)) if ma: file.write(ss(debye_t, 2) + ss(linde_t, d)) file.close() return