def g09_results(NEB, step_to_use, i, state): ''' A method for reading in the output of Gaussian09 single point calculations for NEB calculations. This will both (a) assign forces to the atoms stored in state and (b) return the energy and atoms. **Parameters** NEB: :class:`NEB` An NEB container holding the main NEB simulation step_to_use: *int* Which iteration in the NEB sequence the output to be read in is on. i: *int* The index corresponding to which image on the frame is to be simulated. state: *list,* :class:`squid.structures.atom.Atom` A list of atoms describing the image on the frame associated with index *i*. **Returns** new_energy: *float* The energy of the system in Hartree (Ha). new_atoms: *list,* :class:`squid.structures.atom.Atom` A list of atoms with the forces attached in units of Hartree per Angstrom (Ha/Ang). ''' result = g09.parse_atoms('%s-%d-%d' % (NEB.name, step_to_use, i), check_convergence=False, parse_all=False) if not result: raise Exception('parse_atoms failed') new_energy, new_atoms = result # Check if coordinates are aligned properly between state and new_atoms def check_atom_coords(atoms1, atoms2, precision=1e-6): for a1, a2 in zip(atoms1, atoms2): if (abs(a1.x - a2.x) > precision or abs(a1.y - a2.y) > precision or abs(a1.z - a2.z) > precision): print(i, 'atoms not in same frame:', a1.x, a1.y, a1.z,) print('vs', a2.x, a2.y, a2.z) print(abs(a1.x - a2.x), abs(a1.y - a2.y), abs(a1.z - a2.z)) exit() if i != 0 and i != len(NEB.states) - 1: check_atom_coords(state, new_atoms) for a, b in zip(state, new_atoms): b.fx = units.convert('Ha/Bohr', 'Ha/Ang', b.fx) b.fy = units.convert('Ha/Bohr', 'Ha/Ang', b.fy) b.fz = units.convert('Ha/Bohr', 'Ha/Ang', b.fz) a.fx = b.fx a.fy = b.fy a.fz = b.fz return new_energy, new_atoms
def f(x): return units.convert("Ha/Ang", "eV/Ang", x)
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
def calculate(self, coords): self.calls_to_calculate += 1 # Update coordinates in states. This won't change anything on # the first run through, but will on subsequent ones coord_count = 0 for s in self.states[1:-1]: for a in s: a.x, a.y, a.z = coords[coord_count:coord_count + 3] coord_count += 3 # Start DFT jobs running_jobs = [] if self.initialize == True: # Run a single point calculation to determine energies before main curve-smoothing begins for i, state in enumerate(self.states): running_jobs.append( self.start_job(self, i, state, self.charge, self.procs, self.queue, self.initial_guess, self.extra_section, self.mem, self.priority) ) else: # Once initialization is complete, run main spline_NEB simulation for curve smoothing for i, state in enumerate(self.states): if (i == 0 or i == self.peak or i == len(self.states) - 1) and self.step > 0: # No need to calculate anything for first and last states # after the first step pass else: running_jobs.append( self.start_job(self, i, state, self.charge, self.procs, self.queue, self.initial_guess, self.extra_section, self.mem, self.priority) ) # Wait for jobs to finish for j in running_jobs: j.wait() # Get forces and energies from DFT calculations energies = [] for i, state in enumerate(self.states): # State 0 and state N-1 don't change, so just use result # from self.step == 0 if (i == 0 or i == self.peak or i == len(self.states) - 1): step_to_use = 0 else: step_to_use = self.step new_energy, new_atoms = self.get_results( self, step_to_use, i, state ) energies.append(new_energy) # V = potential energy from DFT. energies = V+springs V = copy.deepcopy(energies) # Get positions in a flat array def get_positions(image): pos = np.array([np.empty([3]) for j in image]) for j, atom in enumerate(image): if j not in self.spring_atoms: continue pos[j] = np.array([atom.x, atom.y, atom.z]) return pos.flatten() if self.initialize == True: # During initialization phase, use single point energies previously calculated to # determine highest energy frame (peak) and generate spring constants between frames # Peak of reaction coordinate is highest energy frame self.peak = energies.index(max(energies)) #Calculate spring constants for smoothing curve d_before = np.linalg.norm(get_positions(self.states[self.peak]) - get_positions(self.states[0])) d_after = np.linalg.norm(get_positions(self.states[self.peak]) - get_positions(self.states[-1])) l_before = - d_before ** 2 / np.log(self.gamma) l_after = - d_after ** 2 / np.log(self.gamma) x1, x2 = [], [] for i in range(self.peak): v = (get_positions(self.states[i]) + get_positions(self.states[i+1])) / 2.0 - \ get_positions(self.states[0]) x1.append(np.linalg.norm(v)) for i in range(self.peak, len(self.states) - 1): v = (get_positions(self.states[i]) + get_positions(self.states[i+1])) / 2.0 - \ get_positions(self.states[0]) x2.append(np.linalg.norm(v)) for x in x1: self.k.append(self.k_max * exp(-((x - d_before) ** 2) / l_before)) for x in x2: self.k.append(self.k_max * exp(-((x - d_after) ** 2) / l_after)) # Add spring forces to atoms for i in range(1, len(self.states) - 1): if i == self.peak: # Set NEB forces at peak to 0 for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue atom.fx, atom.fy, atom.fz = [0, 0, 0] else: a = get_positions(self.states[i - 1]) b = get_positions(self.states[i]) c = get_positions(self.states[i + 1]) real_force = np.array([np.empty([3]) for j in self.states[i]]) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue real_force[j] = np.array([atom.fx, atom.fy, atom.fz]) real_force = real_force.flatten() # Find tangent tplus = c - b tminus = b - a dVmin = min(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) dVmax = max(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) if V[i + 1] > V[i] and V[i] > V[i - 1]: tangent = tplus.copy() elif V[i + 1] < V[i] and V[i] < V[i - 1]: tangent = tminus.copy() elif V[i + 1] > V[i - 1]: tangent = tplus * dVmax + tminus * dVmin else: tangent = tplus * dVmin + tminus * dVmax # Normalize tangent tangent_norm = np.sqrt(np.vdot(tangent, tangent)) if tangent_norm != 0: tangent /= tangent_norm # Set NEB forces forces = (self.k[i] * np.linalg.norm(tplus) - self.k[i-1] * np.linalg.norm(tminus)) * tangent forces = forces.reshape((-1, 3)) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue atom.fx, atom.fy, atom.fz = forces[j] # Remove net translation forces from the gradient if self.fit_rigid: net_translation_force = [] for state in self.states[1:-1]: net_force = np.zeros(3) for a in state: net_force += (a.fx, a.fy, a.fz) net_trans = np.sqrt((net_force**2).sum()) / len(state) net_translation_force.append(net_trans) for a in state: a.fx -= net_force[0] / len(state) a.fy -= net_force[1] / len(state) a.fz -= net_force[2] / len(state) max_translation_force = units.convert( "Ha/Ang", "eV/Ang", max(net_translation_force) ) else: max_translation_force = 0 # Set gradient self.gradient = [] for state in self.states[1:-1]: for a in state: # Gradient of self.error self.gradient += [-a.fx, -a.fy, -a.fz] # Calculate RMS Force and Max force force_mags = [(a.fx**2 + a.fy**2 + a.fz**2)**0.5 for state in self.states[1:-1] for a in state] RMS_force = geometry.rms(force_mags) self.RMS_force = RMS_force MAX_force = max(force_mags) self.MAX_force = MAX_force # Print data V = V[:1] + [units.convert_energy("Ha", "kT_300", e - V[0]) for e in V[1:]] MAX_energy = max(V) if self.prv_RMS is None or self.prv_RMS > RMS_force: rms = print_helper.color_set( float("%.4f" % units.convert_energy( "Ha", "eV", RMS_force) ), 'GREEN') else: rms = print_helper.color_set( float("%.4f" % units.convert_energy( "Ha", "eV", RMS_force) ), 'RED') if self.prv_MAX is None or self.prv_MAX > MAX_force: max_f = print_helper.color_set( float("%.4f" % units.convert_energy( "Ha", "eV", MAX_force) ), 'GREEN') else: max_f = print_helper.color_set( float("%.4f" % units.convert_energy( "Ha", "eV", MAX_force) ), 'RED') if self.prv_MAX_E is None or self.prv_MAX_E > MAX_energy: max_e = print_helper.color_set( float("%.1f" % MAX_energy), 'GREEN') else: max_e = print_helper.color_set( float("%.1f" % MAX_energy), 'RED') if self.step == 0 and self.initialize == False: print("Step\tRMS_F (eV/Ang)\tMAX_F (eV/Ang)\tMAX_E (kT_300)\ \tMAX Translational Force (eV/Ang)\tEnergies (kT_300)\n----") print("%d\t%s\t\t%s\t\t%s\t\t%.4f" % (self.step, rms, max_f, max_e, max_translation_force)), print(' \t\t\t\t', '%7.5g +'\ % V[0], ('%5.1f ' * len(V[1:])) % tuple(V[1:])) sys.stdout.flush() if self.prv_RMS is None: self.prv_RMS = RMS_force self.prv_RMS = min(RMS_force, self.prv_RMS) if self.prv_MAX is None: self.prv_MAX = MAX_force self.prv_MAX = min(MAX_force, self.prv_MAX) if self.prv_MAX_E is None: self.prv_MAX_E = MAX_energy self.prv_MAX_E = min(MAX_energy, self.prv_MAX_E) # Set error self.error = RMS_force # Increment step self.step += 1 # End initialization phase self.initialize = False if self.callback is not None: self.callback(self.states)
def calculate(self, coords): self.calls_to_calculate += 1 # Update coordinates in states. This won't change anything on # the first run through, but will on subsequent ones coord_count = 0 for s in self.states[1:-1]: for a in s: a.x, a.y, a.z = coords[coord_count:coord_count + 3] coord_count += 3 # Start DFT jobs running_jobs = [] for i, state in enumerate(self.states): if (i == 0 or i == len(self.states) - 1) and self.step > 0: # No need to calculate anything for first and last states # after the first step pass else: running_jobs.append( self.start_job(self, i, state, self.charge, self.procs, self.queue, self.initial_guess, self.extra_section, self.mem, self.priority)) # Wait for jobs to finish for j in running_jobs: j.wait() # Get forces and energies from DFT calculations energies = [] for i, state in enumerate(self.states): # State 0 and state N-1 don't change, so just use result # from self.step == 0 if (i == 0 or i == len(self.states) - 1): step_to_use = 0 else: step_to_use = self.step new_energy, new_atoms = self.get_results(self, step_to_use, i, state) energies.append(new_energy) # V = potential energy from DFT. energies = V+springs V = copy.deepcopy(energies) # In climbing image ANEB, after a few iterations we take the highest # energy image and use that. if self.ci_ANEB and self.ci_img is None and self.step > self.ci_N: self.ci_img = V.index(max(V)) if self.ci_img in [0, len(self.states) - 1]: raise Exception("CI found endpoint. Is your band correct?") # Get positions in a flat array def get_positions(image): pos = np.array([np.empty([3]) for j in image]) for j, atom in enumerate(image): if j not in self.spring_atoms: continue pos[j] = np.array([atom.x, atom.y, atom.z]) return pos.flatten() # Add spring forces to atoms for i in range(1, len(self.states) - 1): a = get_positions(self.states[i - 1]) b = get_positions(self.states[i]) c = get_positions(self.states[i + 1]) real_force = np.array([np.empty([3]) for j in self.states[i]]) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue real_force[j] = np.array([atom.fx, atom.fy, atom.fz]) real_force = real_force.flatten() # Find tangent tplus = c - b tminus = b - a dVmin = min(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) dVmax = max(abs(V[i + 1] - V[i]), abs(V[i - 1] - V[i])) if V[i + 1] > V[i] and V[i] > V[i - 1]: tangent = tplus.copy() elif V[i + 1] < V[i] and V[i] < V[i - 1]: tangent = tminus.copy() elif V[i + 1] > V[i - 1]: tangent = tplus * dVmax + tminus * dVmin else: tangent = tplus * dVmin + tminus * dVmax # Normalize tangent tangent_norm = np.sqrt(np.vdot(tangent, tangent)) if tangent_norm != 0: tangent /= tangent_norm F_spring_parallel = self.k[i - 1] * ( np.linalg.norm(tplus) - np.linalg.norm(tminus)) * tangent F_real_perpendicular = real_force -\ (np.vdot(real_force, tangent) * tangent) # Set ANEB forces # Note, in climbing image we have the formula: # F = F_real - 2*F_real*tau*tau # Vs the normal: # F = F_spring_parallel + F_real_perpendicular if self.ci_img is not None and i == self.ci_img: forces = real_force - 2.0 * np.vdot(real_force, tangent) * tangent else: forces = F_spring_parallel + F_real_perpendicular forces = forces.reshape((-1, 3)) for j, atom in enumerate(self.states[i]): if j not in self.spring_atoms: continue atom.fx, atom.fy, atom.fz = forces[j] # Remove net translation forces from the gradient if self.fit_rigid: net_translation_force = [] for state in self.states[1:-1]: net_force = np.zeros(3) for a in state: net_force += (a.fx, a.fy, a.fz) net_trans = np.sqrt((net_force**2).sum()) / len(state) net_translation_force.append(net_trans) for a in state: a.fx -= net_force[0] / len(state) a.fy -= net_force[1] / len(state) a.fz -= net_force[2] / len(state) max_translation_force = units.convert("Ha/Ang", "eV/Ang", max(net_translation_force)) else: max_translation_force = 0 # Set gradient self.gradient = [] for state in self.states[1:-1]: for a in state: # Gradient of self.error self.gradient += [-a.fx, -a.fy, -a.fz] # Calculate RMS Force and Max force force_mags = [(a.fx**2 + a.fy**2 + a.fz**2)**0.5 for state in self.states[1:-1] for a in state] RMS_force = geometry.rms(force_mags) self.RMS_force = RMS_force MAX_force = max(force_mags) self.MAX_force = MAX_force # Print data V = V[:1] + [ units.convert_energy("Ha", "kT_300", e - V[0]) for e in V[1:] ] MAX_energy = max(V) if self.prv_RMS is None or self.prv_RMS > RMS_force: rms = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)), 'GREEN') else: rms = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", RMS_force)), 'RED') if self.prv_MAX is None or self.prv_MAX > MAX_force: max_f = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)), 'GREEN') else: max_f = print_helper.color_set( float("%.4f" % units.convert_energy("Ha", "eV", MAX_force)), 'RED') if self.prv_MAX_E is None or self.prv_MAX_E > MAX_energy: max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'GREEN') else: max_e = print_helper.color_set(float("%.1f" % MAX_energy), 'RED') if self.step == 0: print("Step\tRMS_F (eV/Ang)\tMAX_F (eV/Ang)\tMAX_E (kT_300)\ \tMAX Translational Force (eV/Ang)\tEnergies (kT_300)\n----") print( "%d\t%s\t\t%s\t\t%s\t\t%.4f" % (self.step, rms, max_f, max_e, max_translation_force) + '\t\t\t\t%7.5g +' % V[0], ('%5.1f ' * len(V[1:])) % tuple(V[1:])) self.energy_gaps = [V[1]] + [y - x for y, x in zip(V[2:], V[1:-1])] sys.stdout.flush() if self.prv_RMS is None: self.prv_RMS = RMS_force self.prv_RMS = min(RMS_force, self.prv_RMS) if self.prv_MAX is None: self.prv_MAX = MAX_force self.prv_MAX = min(MAX_force, self.prv_MAX) if self.prv_MAX_E is None: self.prv_MAX_E = MAX_energy self.prv_MAX_E = min(MAX_energy, self.prv_MAX_E) # Set error self.error = RMS_force # Increment step self.step += 1 if self.callback is not None: self.callback(self.states)
def read(input_file): ''' General read in of all possible data from an Orca output file (.out). It should be mentioned that atomic positions are 0 indexed. **Parameters** input_file: *str* Orca .out file to be parsed. **Returns** data: :class:`squid.structures.results.DFT_out` Generic DFT output object containing all parsed results. ''' # Check file exists, and open # Allow absolute paths as filenames if input_file.startswith('/'): input_path = input_file elif os.path.isfile(input_file): input_path = input_file else: input_path = 'orca/%s/%s.out' % (input_file, input_file) if not os.path.isfile(input_path): raise IOError('Expected orca output file does not exist at %s' % (input_path)) data = open(input_path, 'r').read() data_lines = data.splitlines() # Get the route line try: route = [line[5:] for line in data_lines if line.startswith('| 1>')][0].strip() except IndexError: raise IOError('Could not find route line in %s: \ job most likely crashed.' % input_path) # Get the extra section es_block, skip_flag = [], True for d in data_lines: line = d.strip() if line.startswith('| 1>'): skip_flag = False if skip_flag: continue if "*xyz" in line: charge_and_multiplicity = line.split("xyz")[-1] break line = line.split(">")[-1].strip() if line.startswith("!"): continue es_block.append(line) if es_block == []: extra_section = "" else: extra_section = "\n".join(es_block) # Get all the energies energies = re.findall('FINAL SINGLE POINT ENERGY +(\S+)', data) energies = [float(e) for e in energies] if len(energies) > 0: energy = min(energies) else: energy = None # Get all the positions section, frames = data, [] s = 'CARTESIAN COORDINATES (ANGSTROEM)' while s in section: section = section[section.find(s) + len(s):] atom_block = section[:section.find('\n\n')].split('\n')[2:] frame = [] for i, line in enumerate(atom_block): a = line.split() frame.append(Atom( a[0], float(a[1]), float(a[2]), float(a[3]), index=i) ) frames.append(frame) if frames: atoms = frames[-1] else: atoms = None # Get all the gradients if CARTESIAN GRADIENTS is in the file. # Else, if MP2 gradients is in the file, grab the last gradient s_gradient = "CARTESIAN GRADIENT" s_gradient_2 = "The final MP2 gradient" section, gradients = data, [] if s_gradient in section: s = s_gradient elif s_gradient_2 in section: s = s_gradient_2 else: s, gradients = None, None if s is not None: while s in section: gradient = [] if s == s_gradient: grad_block = section[section.find(s_gradient):] grad_block = grad_block.split("\n\n")[1].split("\n") grad_block = [g for g in grad_block if "WARNING" not in g] gradient = [] for line in grad_block: a = line.split() gradient.append([float(b) for b in a[3:]]) elif s == s_gradient_2: grad_block = section[section.find(s_gradient_2):] grad_block = grad_block.split("\n\n")[0].split("\n")[1:] gradient = [] for line in grad_block: a = line.split() gradient.append([float(b) for b in a[1:]]) section = section[section.find(s) + len(s):] gradients.append(gradient) # Get charges hold, charges_MULLIKEN = data, [] s = 'MULLIKEN ATOMIC CHARGES' if hold.rfind(s) != -1: hold = hold[hold.rfind(s):] b = hold[:hold.find('\n\n')].split('\n')[2:-1] for a in b: a = a.split() charges_MULLIKEN.append([a[1].split(':')[0], float(a[-1])]) else: charges_MULLIKEN = None hold, charges_LOEWDIN = data, [] s = 'LOEWDIN ATOMIC CHARGES' if hold.rfind(s) != -1: hold = hold[hold.rfind(s):] b = hold[:hold.find('\n\n')].split('\n')[2:] for a in b: a = a.split() charges_LOEWDIN.append([a[1].split(':')[0], float(a[-1])]) for a, charge in zip(atoms, charges_LOEWDIN): a.charge = charge[1] else: charges_LOEWDIN = None hold, charges_CHELPG = data, [] s = 'CHELPG Charges' if hold.rfind(s) != -1: hold = hold[hold.rfind(s):] s_id = '\n--------------------------------\nTotal charge:' b = hold[:hold.find(s_id)].split('\n')[2:] for a in b: a = a.split() charges_CHELPG.append([a[1].split(':')[0], float(a[-1])]) for a, charge in zip(atoms, charges_CHELPG): a.charge = charge[1] else: charges_CHELPG = None # Get Mayer Bond Orders hold, MBO = data, [] s = 'Mayer bond orders larger than 0.1' if hold.rfind(s) != -1: hold = hold[hold.rfind(s):] b = hold[:hold.find('\n\n')].split('\n')[1:] b = " ".join(b).split("B(") while len(b) > 0 and b[0].strip() == "": b = b[1:] while len(b) > 0 and b[-1].strip() == "": b = b[:-1] for a in b: a = a.split(":") mbo_x = float(a[-1]) bond_x = [int(c.strip().split("-")[0]) for c in a[0].split(",")] MBO.append([[atoms[x] for x in bond_x], mbo_x]) # Get Total Simulation Time hold = data s = 'TOTAL RUN TIME' if hold.rfind(s) != -1: hold = hold[hold.rfind(s):] hold = hold[:hold.find('\n')].split() time = float(hold[3]) * 3600 * 24 + \ float(hold[5]) * 3600 + \ float(hold[7]) * 60 + \ float(hold[9]) + \ float(hold[11]) / 1000.0 else: time = float('NaN') hold, bandgaps = data, [] s = 'ORBITAL ENERGIES' while hold.find(s) != -1: hold = hold[hold.find(s) + len(s):] tmp = hold[:hold.replace('\n\n', '\t\t', 1).find('\n\n')] tmp = tmp.split('\n')[4:] tp = None for i, t in enumerate(tmp): t = t.split() if float(t[1]) == 0: if i == 0: raise Exception("Error in calculating bandgaps. \ Lowest energy orbital is empty.") bandgaps.append(float(t[2]) - float(tp[2])) break tp = t hold = hold[hold.find('\n'):] if len(bandgaps) > 0: bandgap = bandgaps[-1] else: bandgap = None hold, orbitals = data, [] s = 'ORBITAL ENERGIES' hold = '\n'.join(hold[hold.rfind(s):].split('\n')[4:]) hold = hold[:hold.find("\n\n")].split("\n") if hold != ['']: orbitals = [(float(h.split()[1]), float(h.split()[2])) for h in hold] else: orbitals = None hold, convergence = data, [] s = 'Geometry convergence' if hold.rfind(s) != -1: hold = hold[hold.rfind(s) + len(s):] # Cartesian optimization does not compute Max(Bonds). # Instead use a more general '\n\n' if 'Max(Bonds)' cannot be found if hold.rfind('Max(Bonds)') != -1: tmp = hold[:hold.rfind('Max(Bonds)')].split('\n')[3:-2] else: tmp = hold[:hold.find('\n\n')].split('\n')[3:-1] convergence = [] for a in tmp: a = a.split() convergence.append([' '.join(a[:2]), float(a[2]), float(a[3]), a[4]]) else: convergence = None hold, dipole = data, [] s = 'DIPOLE MOMENT' if hold.rfind(s) != -1: hold = hold[hold.rfind(s) + len(s):] if hold.rfind('Total Dipole Moment') != -1: dipole_moment = hold[hold.rfind('Total Dipole Moment'):].split('\n')[0] dipole_moment = tuple([float(x) for x in dipole_moment.strip().split()[-3:]]) else: dipole_moment = None if hold.rfind('Magnitude (Debye)') != -1: dipole_mag = hold[hold.rfind('Magnitude (Debye)'):].split('\n')[0] dipole_mag = float(dipole_mag.strip().split()[-1]) else: dipole_mag = None COM = None if data.rfind("CENTER OF MASS") != -1: COM = data[data.rfind("CENTER OF MASS"):].split("\n")[0].strip().split()[-3:] x = float(COM[0].strip()[1:-1]) y = float(COM[1].strip()) z = float(COM[2].strip()[:-1]) COM = tuple(map( lambda d: units.convert_dist("Bohr", "Ang", d), (x, y, z) )) dipole = [dipole_mag, dipole_moment, COM] else: dipole = None hold, converged = data, False s1, s2 = 'SCF CONVERGED AFTER', 'OPTIMIZATION RUN DONE' if 'opt' in route.lower(): s = s2 else: s = s1 if hold.find(s) != -1: converged = True finished = 'ORCA TERMINATED NORMALLY' in data # Read in Vibrational Frequencies if they exist s1, s2 = 'VIBRATIONAL FREQUENCIES', 'NORMAL MODES' hold, vibfreq = data, None if hold.rfind(s1) != -1 and hold.rfind(s2) != -1: tmp = hold[hold.rfind(s1):hold.rfind(s2)].strip().split("\n") vibfreq = [float(t.split(":")[1].split("cm")[0].strip()) for t in tmp if ":" in t] warnings = [line for line in data_lines if line.startswith('Warning: ')] data = results.DFT_out(input_file, 'orca') if isinstance(route, str): route = route.strip() if isinstance(extra_section, str): extra_section = extra_section.strip() data.route = route data.extra_section = extra_section data.charge, data.multiplicity = map( float, charge_and_multiplicity.strip().split()) data.frames = frames data.atoms = atoms data.gradients = gradients # In default units of Ha/Bohr data.forces = None # Forces are in Ha/Ang, more "reasonable" units for what we expect when # we use forces on the coordinates (in Ang) if data.gradients is not None: data.forces = -1.0 * np.array(gradients) *\ units.convert("Ha/Bohr", "Ha/Ang", 1.0) 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.vibfreq = vibfreq data.convergence = convergence data.converged = converged data.time = time data.bandgaps = bandgaps data.bandgap = bandgap data.orbitals = orbitals data.finished = finished data.warnings = warnings data.dipole = dipole # Dipole are in units of Angstrom and/or Debye return data
def engrad_read(input_file, force='Ha/Bohr', pos='Bohr'): ''' General read in of all possible data from an Orca engrad file (.orca.engrad). **Parameters** input_file: *str* Orca .orca.engrad file to be parsed. force: *str, optional* Units you want force to be returned in. Default is Ha/Bohr. pos: *str, optional* Units you want position to be returned in. Default is Bohr. **Returns** atoms: *list,* :class:`squid.structures.atom.Atom` A list of the final atomic state, with forces appended to each atom. energy: *float* The total energy of this simulation. ''' if not input_file.endswith('.engrad'): input_file = 'orca/%s/%s.orca.engrad' % (input_file, input_file) if not os.path.isfile(input_file): raise IOError("No engrad file %s exists in %s. \ Please run simulation with grad=True." % (input_file, os.getcwd())) data = open(input_file, 'r').read().split('\n') count, grad, atoms = 0, [], [] i = -1 while i < len(data): i += 1 line = data[i] if len(line) < 1: continue if line.strip()[0] == '#': continue if count == 0: num_atoms = int(line.strip()) count += 1 elif count == 1: # Get energy energy = float(line.strip()) count += 1 elif count == 2: # Get gradient for j in range(num_atoms): for k in range(3): grad.append(float(data[i + k].strip())) i += 3 count += 1 elif count == 3: # Get atom coordinates k = 0 for j in range(num_atoms): tmp = data[i].split() atoms.append(Atom( tmp[0], units.convert_dist('Bohr', pos, float(tmp[1])), units.convert_dist('Bohr', pos, float(tmp[2])), units.convert_dist('Bohr', pos, float(tmp[3])) )) atoms[-1].fx = units.convert('Ha/Bohr', force, -grad[k]) atoms[-1].fy = units.convert('Ha/Bohr', force, -grad[k + 1]) atoms[-1].fz = units.convert('Ha/Bohr', force, -grad[k + 2]) i += 1 k += 3 break return atoms, energy