Source code for qubit.utilities.gaussian
import os
"""This submodule aims to provide utilities for the gaussian software package.
It will allow the user to quickly write custom interfaces to analyse the output files.
"""
[docs]class Extractor:
"""This class supports data extraction from gaussian output files.
It provides functionality to extract all the implemented data at once or custom extraction
can be set up by using its public methods.
"""
def __init__(self, filepath, labels=None):
self.filepath = filepath
self.labels = labels
self.normal_executions = 0
# Initialize
self.check_normal_execution()
self.check_frequencies()
self.label_positions = self._get_label_positions()
[docs] def check_normal_execution(self):
"""Checks for normal execution
Checks for normal execution of the gaussian output file.
Use this first when writing custom extraction methods to check the validity of the calculations.
Returns:
(bool): Returns True when a calculation has normal execution.
"""
with open(self.filepath, "r") as f:
for line in f:
if 'Normal termination of Gaussian' in line:
self.normal_executions += 1
if self.labels != None:
if self.normal_executions == len(self.labels)+1:
return True
else:
raise Exception('There are {} Normal terminations, please check this file manually: {}'.format(
self.normal_executions, self.filepath))
else:
if self.normal_executions == 0:
raise Exception(
'There are no normal terminations, please check this file manually: {}'.format(self.filepath))
elif self.normal_executions == 1:
return True
else:
raise Exception(
'There are multiple normal terminations, please set the labels when constructing the flagg.')
[docs] def check_frequencies(self):
"""Check for negative (imaginary) frequencies.
Returns:
(bool): Returns True if no negative frequencies are found.
Raises:
Exception: Raises when negative frequencies are found.
"""
with open(self.filepath, 'r') as f:
imag = False
vals = []
for line in f:
if 'Frequencies -- ' in line:
vals.append(line)
split = vals[-1].split()
if float(split[2]) < 0:
imag = True
if float(split[3]) < 0:
imag = True
if float(split[4]) < 0:
imag = True
if imag:
raise Exception(
'There are imaginary frequencies, please check this file manually: {}'.format(self.filepath))
else:
return True
def _get_label_positions(self):
results = []
with open(self.filepath, 'r') as f:
for i, line in enumerate(f):
for l in self.labels:
if l in line:
results.append([i, l])
for i, n in enumerate(results):
if n[0] == results[i-1][0]:
results.remove(results[i-1])
def clean_list():
for i, n in enumerate(results):
if n[1] == results[i-1][1]:
results.remove(results[i-1])
clean_list()
clean_list()
return results
def extract_error(self):
with open(self.filepath, 'r') as f:
temp = None
for line in f:
if 'Error termination' in line:
return temp
else:
temp = line
def _extract_geometry(self, file):
file.readline()
file.readline()
file.readline()
file.readline()
atoms = []
xyz = []
is_molecule = True
while is_molecule:
# read and process the line
line = file.readline()
split = line.split()
# check if is still the molecule
if len(split) == 1:
is_molecule = False
else:
# process the line
atoms.append(split[1])
coords = []
coords.append(split[3])
coords.append(split[4])
coords.append(split[5])
xyz.append(coords)
return atoms, xyz
[docs] def extract_optimized_geometry(self):
"""Extracts the optimized geometry
Extracts the optimized geometry from the gaussian output file.
Returns:
(tuple): tuple containing:
atoms (list) : Atom numbers
coördinates (list): Cartesian coordinates in a 2D list
"""
results = []
with open(self.filepath, 'r') as f:
for line in f:
if 'Standard orientation' in line:
atoms, xyz = self._extract_geometry(f)
results.append([atoms, xyz])
if self.labels[1] in line:
break
return results[-2]
def extract_SCF(self):
vals = []
results = []
with open(self.filepath, 'r') as f:
for i, line in enumerate(f):
if 'SCF Done' in line:
split = line.split()
vals.append([i, split[4]])
for p in self._get_label_positions():
temp = None
for v in vals:
if v[0] < p[0]:
temp = v
temp = [p[1], temp[1]]
results.append(temp)
return results
def extract_HOMO_energy(self):
with open(self.filepath, 'r') as f:
inFreq = False
vals = []
for line in f:
if 'Link1' in line:
inFreq = True
if self.labels[1] in line:
inFreq = False
if inFreq:
if 'Alpha occ. eigenvalues' in line:
vals.append(line)
split = vals[-1].split()
return split[-1]
def extract_LUMO_energy(self):
with open(self.filepath, 'r') as f:
inFreq = False
vals = []
for line in f:
if 'Link1' in line:
inFreq = True
if self.labels[1] in line:
inFreq = False
if inFreq:
if 'Alpha virt. eigenvalues' in line:
vals.append(line)
split = vals[0].split()
return split[4]
def extract_zero_point_correction(self):
with open(self.filepath, 'r') as f:
for line in f:
if 'Zero-point correction' in line:
split = line.split()
return split[2]
def extract_thermal_correction_to_energy(self):
with open(self.filepath, 'r') as f:
for line in f:
if 'Thermal correction to Energy' in line:
split = line.split()
return split[4]
def extract_thermal_correction_to_enthalpy(self):
with open(self.filepath, 'r') as f:
for line in f:
if 'Thermal correction to Enthalpy' in line:
split = line.split()
return split[4]
def extract_thermal_correction_to_gibbs_free_energy(self):
with open(self.filepath, 'r') as f:
for line in f:
if 'Thermal correction to Gibbs Free Energy' in line:
split = line.split()
return split[6]
def _extract_npa(self, file):
file.readline()
file.readline()
file.readline()
file.readline()
file.readline()
natural_charges = []
is_molecule = True
while is_molecule:
line = file.readline()
split = line.split()
if len(split) == 1:
is_molecule = False
else:
natural_charges.append(split[2])
return natural_charges
def extract_npas(self):
results = []
with open(self.filepath, 'r') as f:
vals = []
for line in f:
if 'Summary of Natural Population Analysis:' in line:
vals.append(self._extract_npa(f))
results.append(vals[0])
results.append(vals[1])
results.append(vals[4])
return results