def do_energy_calculation(self, organism, dictionary, key, composition_space): """ Calculates the energy of an organism using VASP, and stores the relaxed organism in the provided dictionary at the provided key. If the calculation fails, stores None in the dictionary instead. Args: organism: the Organism whose energy we want to calculate dictionary: a dictionary in which to store the relaxed Organism key: the key specifying where to store the relaxed Organism in the dictionary composition_space: the CompositionSpace of the search Precondition: the garun directory and temp subdirectory exist, and we are currently located inside the garun directory TODO: maybe use the custodian package for error handling """ # make the job directory job_dir_path = str(os.getcwd()) + '/temp/' + str(organism.id) os.mkdir(job_dir_path) # copy the INCAR and KPOINTS files to the job directory shutil.copy(self.incar_file, job_dir_path) shutil.copy(self.kpoints_file, job_dir_path) # sort the organism's cell and write to POSCAR file organism.cell.sort() organism.cell.to(fmt='poscar', filename=job_dir_path + '/POSCAR') # get a list of the element symbols in the sorted order symbols = [] for site in organism.cell.sites: if site.specie.symbol not in symbols: symbols.append(site.specie.symbol) # write the POTCAR file by concatenating the appropriate elemental # POTCAR files total_potcar_path = job_dir_path + '/POTCAR' with open(total_potcar_path, 'w') as total_potcar_file: for symbol in symbols: with open(self.potcar_files[symbol], 'r') as potcar_file: for line in potcar_file: total_potcar_file.write(line) # run 'callvasp' script as a subprocess to run VASP print('Starting VASP calculation on organism {} '.format(organism.id)) devnull = open(os.devnull, 'w') try: subprocess.call(['callvasp', job_dir_path], stdout=devnull, stderr=devnull) except: print('Error running VASP on organism {} '.format(organism.id)) dictionary[key] = None return # parse the relaxed structure from the CONTCAR file try: relaxed_cell = Cell.from_file(job_dir_path + '/CONTCAR') except: print('Error reading structure of organism {} from CONTCAR ' 'file '.format(organism.id)) dictionary[key] = None return # check if the VASP calculation converged converged = False with open(job_dir_path + '/OUTCAR') as f: for line in f: if 'reached' in line and 'required' in line and \ 'accuracy' in line: converged = True if not converged: print('VASP relaxation of organism {} did not converge '.format( organism.id)) dictionary[key] = None return # parse the internal energy and pV (if needed) and compute the enthalpy pv = 0 with open(job_dir_path + '/OUTCAR') as f: for line in f: if 'energy(sigma->0)' in line: u = float(line.split()[-1]) elif 'enthalpy' in line: pv = float(line.split()[-1]) enthalpy = u + pv organism.cell = relaxed_cell organism.total_energy = enthalpy organism.epa = enthalpy / organism.cell.num_sites print('Setting energy of organism {} to {} ' 'eV/atom '.format(organism.id, organism.epa)) dictionary[key] = organism
def get_relaxed_cell(self, gout): # Find the structure lines structure_lines = [] cell_param_lines = [] output_lines = gout.split("\n") no_lines = len(output_lines) i = 0 # Compute the input lattice parameters while i < no_lines: line = output_lines[i] if "Full cell parameters" in line: i += 2 line = output_lines[i] a = float(line.split()[8]) alpha = float(line.split()[11]) line = output_lines[i + 1] b = float(line.split()[8]) beta = float(line.split()[11]) line = output_lines[i + 2] c = float(line.split()[8]) gamma = float(line.split()[11]) i += 3 break elif "Cell parameters" in line: i += 2 line = output_lines[i] a = float(line.split()[2]) alpha = float(line.split()[5]) line = output_lines[i + 1] b = float(line.split()[2]) beta = float(line.split()[5]) line = output_lines[i + 2] c = float(line.split()[2]) gamma = float(line.split()[5]) i += 3 break else: i += 1 while i < no_lines: line = output_lines[i] if "Final fractional coordinates of atoms" in line or \ "Final asymmetric unit coordinates" in line: # Ben's add # read the site coordinates in the following lines i += 6 line = output_lines[i] while line[0:2] != '--': structure_lines.append(line) i += 1 line = output_lines[i] # read the cell parameters i += 9 line = output_lines[i] if "Final cell parameters" in line: i += 3 for del_i in range(6): line = output_lines[i + del_i] cell_param_lines.append(line) break else: i += 1 # Process the structure lines if structure_lines: sp = [] coords = [] for line in structure_lines: fields = line.split() if fields[2] == 'c': sp.append(fields[1]) coords.append(list(float(x) for x in fields[3:6])) else: raise IOError("No structure found") if cell_param_lines: a = float(cell_param_lines[0].split()[1]) b = float(cell_param_lines[1].split()[1]) c = float(cell_param_lines[2].split()[1]) alpha = float(cell_param_lines[3].split()[1]) beta = float(cell_param_lines[4].split()[1]) gamma = float(cell_param_lines[5].split()[1]) latt = Lattice.from_parameters(a, b, c, alpha, beta, gamma) return Cell(latt, sp, coords)
def get_relaxed_cell(self, atom_dump_path, data_in_path, element_symbols): """ Parses the relaxed cell from the dump.atom file. Returns the relaxed cell as a Cell object. Args: atom_dump_path: the path (as a string) to the dump.atom file in_data_path: the path (as a string) to the in.data file element_symbols: a tuple containing the set of chemical symbols of all the elements in the compositions space """ # read the dump.atom file as a list of strings with open(atom_dump_path, 'r') as atom_dump: lines = atom_dump.readlines() # get the lattice vectors a_data = lines[5].split() b_data = lines[6].split() c_data = lines[7].split() # parse the tilt factors xy = float(a_data[2]) xz = float(b_data[2]) yz = float(c_data[2]) # parse the bounds xlo_bound = float(a_data[0]) xhi_bound = float(a_data[1]) ylo_bound = float(b_data[0]) yhi_bound = float(b_data[1]) zlo_bound = float(c_data[0]) zhi_bound = float(c_data[1]) # compute xlo, xhi, ylo, yhi, zlo and zhi according to the conversion # given by LAMMPS # http://lammps.sandia.gov/doc/Section_howto.html#howto-12 xlo = xlo_bound - min([0.0, xy, xz, xy + xz]) xhi = xhi_bound - max([0.0, xy, xz, xy + xz]) ylo = ylo_bound - min(0.0, yz) yhi = yhi_bound - max([0.0, yz]) zlo = zlo_bound zhi = zhi_bound # construct a Lattice object from the lo's and hi's and tilts a = [xhi - xlo, 0.0, 0.0] b = [xy, yhi - ylo, 0.0] c = [xz, yz, zhi - zlo] relaxed_lattice = Lattice([a, b, c]) # get the number of atoms num_atoms = int(lines[3]) # get the atom types and their Cartesian coordinates types = [] relaxed_cart_coords = [] for i in range(num_atoms): atom_info = lines[9 + i].split() types.append(int(atom_info[1])) relaxed_cart_coords.append([ float(atom_info[2]) - xlo, float(atom_info[3]) - ylo, float(atom_info[4]) - zlo ]) # read the atom types and corresponding atomic masses from in.data with open(data_in_path, 'r') as data_in: lines = data_in.readlines() types_masses = {} for i in range(len(lines)): if 'Masses' in lines[i]: for j in range(len(element_symbols)): types_masses[int(lines[i + j + 2].split()[0])] = float( lines[i + j + 2].split()[1]) # map the atom types to chemical symbols types_symbols = {} for symbol in element_symbols: for atom_type in types_masses: # round the atomic masses to one decimal point for comparison if format(float(Element(symbol).atomic_mass), '.1f') == format(types_masses[atom_type], '.1f'): types_symbols[atom_type] = symbol # make a list of chemical symbols (one for each site) relaxed_symbols = [] for atom_type in types: relaxed_symbols.append(types_symbols[atom_type]) return Cell(relaxed_lattice, relaxed_symbols, relaxed_cart_coords, coords_are_cartesian=True)