def example_1(): # In this example, we will generate a smooth CNH-HCN isomerization # guessed pathway # Step 1 - Generate the bad initial guess print("Step 1 - Generate the bad initial guess...") H_coords = [(2, 0), (2, 1), (1, 1), (0, 1), (-1, 1), (-1, 0)] CNH_frames = [[ structures.Atom("C", 0, 0, 0), structures.Atom("N", 1, 0, 0), structures.Atom("H", x, y, 0) ] for x, y in H_coords] # Further, randomly rotate the atoms CNH_frames = [ geometry.perturbate(frame, dx=0.0, dr=360) for frame in CNH_frames ] files.write_xyz(CNH_frames, "rotated_pathway.xyz") # Step 2 - Use procrustes to remove rotations print("Step 2 - Use Procrustes to remove rotations...") geometry.procrustes(CNH_frames) files.write_xyz(CNH_frames, "procrustes_pathway.xyz") # Step 3 - Smooth out the band by minimizing the RMS atomic motion between # consecutive frames until it is below 0.1 (with a max of 50 frames). print("Step 3 - Smooth out the band...") CNH_frames = geometry.smooth_xyz(CNH_frames, R_max=0.1, F_max=50, use_procrustes=True) # Save smoothed band files.write_xyz(CNH_frames, "smoothed_pathway.xyz")
def _generate_ion_halide(halide, ion="Pb"): """ A function to generate atomic coordinates of an ion and three halides for a perovskite monomer. **Parameters** halide: *list, str or str* The halides to use. ion: *str, optional* What ion to use. **Returns** ionX3: *Molecule* A molecule object holding the perovskite ion + halide atoms. """ ionX3 = structures.Molecule([structures.Atom(ion, 0, 0, 0)]) if type(halide) is str: halide = [halide, halide, halide] def vdw(y): return constants.PERIODIC_TABLE[units.elem_s2i(y)]['vdw_r'] for x in halide: v = vdw(x) ionX3.atoms.append(structures.Atom(x, v, 0, 0.5 * v)) R = geometry.rotation_matrix([0, 0, 1], 120, units="deg") ionX3.rotate(R) return ionX3
def isolate_atom_types(types, dump_obj, mol_len): count = 0 atoms_list = [] frames = [] for line in dump_obj: words = line.split() if count == mol_len: frames.append(atoms_list) count = 0 atoms_list = [] try: if int(words[0]) in types: atom_type = types[int(words[0])] x = float(words[1]) y = float(words[2]) z = float(words[3]) count = count + 1 atoms_list.append( structures.Atom(atom_type, x, y, z, index=count)) except ValueError: pass return frames
def _test_generate_ion_halide_cation(): obj = generate_ion_halide_cation(["Cl", "Br", "Br"], "Cs", "CsPbBrBrCl_0", WORLD_CONSTS.default_routes[0], WORLD_CONSTS.extra_section, ion="Pb", run_opt=False).atoms saved = [ structures.Atom("Cs", 0.0, 0.0, 2.5299999999999998), structures.Atom("Pb", 0.0, 0.0, 0.0), structures.Atom("Cl", 2.0499999999999998, -3.3306690738754696e-15, 1.0249999999999999), structures.Atom("Br", -1.050000000000002, -1.8186533479473201, 1.05), structures.Atom("Br", -1.0499999999999989, 1.8186533479473219, 1.05), ] TOLERANCE = 1E-6 return geometry.motion_per_frame([obj, saved])[1] < TOLERANCE
def parse_scan(input_file): contents = open(input_file).read() if 'Normal termination of Gaussian 09' not in contents: return None scan_steps = contents.split('on scan point') energy_list = [] atoms_list = [] scan_steps = [ scan_steps[i] for i in range(1, len(scan_steps) - 1) if scan_steps[i][:10].split()[0] != scan_steps[i + 1][:10].split()[0] ] for scan_step in scan_steps: a = scan_step.rindex('SCF Done') energy_line = scan_step[a:scan_step.index('\n', a)] energy = float( re.search('SCF Done: +\S+ += +(\S+)', energy_line).group(1)) last_coordinates = scan_step.rindex('Coordinates (Angstroms)') start = scan_step.index('---\n', last_coordinates) + 4 end = scan_step.index('\n ---', start) atoms = [] for line in scan_step[start:end].splitlines(): columns = line.split() x, y, z = [float(s) for s in columns[3:6]] atoms.append( structures.Atom(element=constants.PERIODIC_TABLE[int( columns[1])]['sym'], x=x, y=y, z=z)) energy_list.append(energy) atoms_list.append(atoms) return energy_list, atoms_list
def read(input_file, atom_units="Ang"): ''' General read in of all possible data from a JDFTx output file. **Parameters** input_file: *str* JDFTx output file to be parsed. atom_units: *str, optional* What units you want coordinates to be converted to. **Returns** data: :class:`results.DFT_out` Generic DFT output object containing all parsed results. ''' raise Exception("NEEDS TO BE DONE!") # Check file exists, and open # Allow absolute paths as filenames if not input_file.startswith('/') and not os.path.isfile(input_file): input_path = 'jdftx/%s/%s.out' % (input_file, input_file) else: input_path = input_file if not os.path.isfile(input_path): raise IOError('Expected JDFTx output file does not exist at %s' % (input_path)) sys.exit() data = open(input_path, 'r').read() data_lines = data.splitlines() # Get coordinates section, frames, gradients = data, [], [] s = "# Ionic positions in cartesian coordinates:" ss = "# Forces in Cartesian coordinates:" while s in section: section = section[section.find(s) + len(s):] atom_block = section[:section.find('\n\n')].split('\n')[1:] frame, gradient = [], [] for i, line in enumerate(atom_block): a = line.split() frame.append(structures.Atom( a[1], units.convert_dist("Bohr", atom_units, float(a[2])), units.convert_dist("Bohr", atom_units, float(a[3])), units.convert_dist("Bohr", atom_units, float(a[4])), index=i)) frames.append(frame) # If we also have forces, read those in if ss in section: section = section[section.find(ss) + len(ss):] force_block = section[:section.find('\n\n')].split('\n')[1:] for i, line in enumerate(force_block): a = line.split() frames[-1][i].fx = units.convert( "Ha/Bohr", "Ha/%s" % atom_units, float(a[2])) frames[-1][i].fy = units.convert( "Ha/Bohr", "Ha/%s" % atom_units, float(a[3])) frames[-1][i].fz = units.convert( "Ha/Bohr", "Ha/%s" % atom_units, float(a[4])) gradient.append( [frames[-1][i].fx, frames[-1][i].fy, frames[-1][i].fz]) gradients.append(gradient) atoms = None if frames: atoms = frames[-1] section, energies = data, [] s = "IonicMinimize: Iter:" while s in section: section = section[section.find(s) + len(s):] energy = float(section.split("\n")[0].strip().split()[2]) grad_k = float(section.split("\n")[0].strip().split()[4]) energies.append(energy) convergence = None if len(energies) > 2: section = data[data.find("ionic-minimize"):] de_criteria = float(section[ section.find("energyDiffThreshold"):].split("\n")[0].strip().split()[1]) k_criteria = float(section[ section.find("knormThreshold"):].split("\n")[0].strip().split()[1]) de1 = abs(energies[-2] - energies[-3]) de2 = abs(energies[-1] - energies[-2]) convergence = [ ["Change in Energy 1", "%.2e" % de1, de_criteria, ["NO", "YES"][de_criteria > de1]], ["Change in Energy 2", "%.2e" % de2, de_criteria, ["NO", "YES"][de_criteria > de2]], ["K Norm", "%.2e" % abs(grad_k), k_criteria, ["NO", "YES"][k_criteria > abs(grad_k)]] ] energy = None if energies: energy = energies[-1] converged = None finished = "Done!" in data if "IonicMinimize: Converged" in data: converged = True elif finished: converged = False time = None if "Duration:" in data: time = data[data.find("Duration:"):].split("\n")[0].split("Duration:")[-1].strip()[:-1].split(":") # Time should be x-x:yy:zz.zz, thus: [x-x, yy, zz.zz] time = float(time[2]) + 60.0 * float(time[1]) + 3600.0 * float(time[0].split("-")[-1]) data = results.DFT_out(input_file, 'jdftx') # data.route = route # data.extra_section = extra_section # data.charge_and_multiplicity = charge_and_multiplicity.strip() data.frames = frames data.atoms = atoms data.gradients = gradients data.energies = energies data.energy = energy # data.charges_MULLIKEN = charges_MULLIKEN # data.charges_LOEWDIN = charges_LOEWDIN # data.charges_CHELPG = charges_CHELPG # data.charges = copy.deepcopy(charges_MULLIKEN) # data.MBO = MBO data.convergence = convergence data.converged = converged data.time = time # data.bandgaps = bandgaps # data.bandgap = bandgap # data.orbitals = orbitals data.finished = finished # data.warnings = warnings return data
from squid import orca from squid import files from squid import geometry from squid.calcs import NEB from squid import structures if __name__ == "__main__": # In this example we will generate the full CNH-HCN isomerization using # only squid. Then we optimize the endpoints in DFT, smooth the frames, # and subsequently run NEB # Step 1 - Generate the bad initial guess print("Step 1 - Generate the bad initial guess...") H_coords = [(2, 0), (2, 0.5), (1, 1), (0, 1), (-1, 0.5), (-1, 0)] CNH_frames = [[ structures.Atom("C", 0, 0, 0), structures.Atom("N", 1, 0, 0), structures.Atom("H", x, y, 0) ] for x, y in H_coords] # Save initial frames files.write_xyz(CNH_frames, "bad_guess.xyz") # Step 2 - Optimize the endpoints print("Step 2 - Optimize endpoints...") frame_start_job = orca.job("frame_start", "! HF-3c Opt", atoms=CNH_frames[0], queue=None) frame_last_job = orca.job("frame_last", "! HF-3c Opt", atoms=CNH_frames[-1],
def parse_atoms(input_file, get_atoms=True, get_energy=True, check_convergence=True, get_time=False, counterpoise=False, parse_all=False): # @input_file [str] : string name of log file # Returns: (? energy, ? atoms, ? time) | None # @energy [float] : If get_energy or parse_all, otherwise return omitted. # @atoms |[atom list] : Iff parse_all, returns atom list list. # |[atom list list] : Iff not parse_all and get_atoms, atom list. # Otherwise omitted. # @time [float] : If get_time returns float (seconds). Otherwise, return # omitted. # Note that None may be returned in the event that Gaussian did not # terminate normally (see 7 lines down). if input_file[-4:] != '.log': input_file = input_file + '.log' if not os.path.exists(input_file): input_file = "gaussian/" + input_file contents = open(input_file).read() time = None if (check_convergence and get_energy and not parse_all and 'Normal termination of Gaussian 09' not in contents): return None if (('Normal termination of Gaussian 09' in contents) and (get_time | parse_all)): m = re.search( 'Job cpu time: +(\S+) +days +(\S+) +hours \ +(\S+) +minutes +(\S+) +seconds', contents) try: time = float(m.group(1)) * 24 * 60 * 60 +\ float(m.group(2)) * 60 * 60 +\ float(m.group(3)) * 60 +\ float(m.group(4)) except: pass if ('Summary of Optimized Potential Surface Scan' in contents and not parse_all): index = contents.rindex('Summary of Optimized Potential Surface Scan') end_section = contents[index:] energy_lines = re.findall('Eigenvalues -- ([^\\n]+)', end_section) energy = [ float(s) for line in energy_lines for s in re.findall('-[\d]+\.[\d]+', line) ] minima = re.split('Stationary point found', contents) atoms = [] for m in minima[1:]: coordinates = m.index('Coordinates (Angstroms)') start = m.index('---\n', coordinates) + 4 end = m.index('\n ---', start) atoms.append([]) for line in m[start:end].splitlines(): columns = line.split() element = columns[1] x, y, z = [float(s) for s in columns[3:6]] atoms[-1].append( structures.Atom(element=constants.PERIODIC_TABLE[int( columns[1])]['sym'], x=x, y=y, z=z, index=len(atoms[-1]) + 1)) if get_energy: return energy, atoms elif get_energy and not parse_all: if ' MP2/' in contents: # MP2 files don't have just SCF energy energy = float( re.findall('EUMP2 = +(\S+)', contents)[-1].replace('D', 'e')) elif ' CCSD/' in contents: energy = float(re.findall('E\(CORR\)= +(\S+)', contents)[-1]) else: if not counterpoise: try: a = contents.rindex('SCF Done') energy_line = contents[a:contents.index('\n', a)] except ValueError: raise Exception('No SCF for ' + input_file) energy = float( re.search('SCF Done: +\S+ += +(\S+)', energy_line).group(1)) else: energy = float( re.findall('Counterpoise: corrected energy = +(\S+)', contents)[-1]) if parse_all: energies = [] atom_frames = [] start = 0 orientation = 'Input orientation:' while True: try: # Match energy input_orientation = contents.find(orientation, start) if input_orientation == -1: orientation = 'Standard orientation' input_orientation = contents.find(orientation, start) if input_orientation >= 0: start = input_orientation next_coordinates = contents.index('Coordinates (Angstroms)', start) start = contents.index('SCF Done', start) energies.append( float( re.search('SCF Done: +\S+ += +(\S+)', contents[start:]).group(1))) except: break start = contents.index('---\n', next_coordinates) + 4 end = contents.index('\n ---', start) lines = contents[start:end].splitlines() start = end atoms = [] for line in lines: columns = line.split() element = columns[1] x, y, z = columns[3:6] atoms.append( structures.Atom(element=element, x=float(x), y=float(y), z=float(z))) atom_frames.append(atoms) return energies, atom_frames, time if get_energy and not get_atoms: if get_time: return energy, time else: return energy try: # Get coordinates last_coordinates = contents.rindex('Input orientation:') last_coordinates = contents.index('Coordinates (Angstroms)', last_coordinates) except ValueError: last_coordinates = contents.rindex('Coordinates (Angstroms)') start = contents.index('---\n', last_coordinates) + 4 end = contents.index('\n ---', start) atoms = [] for line in contents[start:end].splitlines(): columns = line.split() element = columns[1] x, y, z = [float(s) for s in columns[3:6]] atoms.append( structures.Atom(element=constants.PERIODIC_TABLE[int( columns[1])]['sym'], x=x, y=y, z=z, index=len(atoms) + 1)) if 'Forces (Hartrees/Bohr)' in contents: # Get forces last_forces = contents.rindex('Forces (Hartrees/Bohr)') start = contents.index('---\n', last_forces) + 4 end = contents.index('\n ---', start) for i, line in enumerate(contents[start:end].splitlines()): columns = line.split() read_force = [float(s) for s in columns[2:5]] atoms[i].fx, atoms[i].fy, atoms[i].fz = read_force # Return the appropriate values if get_time: if get_atoms: return energy, atoms, time else: return energy, time if get_energy: return energy, atoms else: return atoms