Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
# We cannot use this last message as a stop condition in general, because
# often there is vibrational output before it. So we use the 'Total Energy'
# line. However, what comes after that is different for single point calculations
# and in the inner steps of geometry optimizations.
if "SCF CONVERGED AFTER" in line:
if not hasattr(self, "scfenergies"):
self.scfenergies = []
if not hasattr(self, "scfvalues"):
self.scfvalues = []
if not hasattr(self, "scftargets"):
self.scftargets = []
while not "Total Energy :" in line:
line = next(inputfile)
energy = utils.convertor(float(line.split()[3]), "hartree", "eV")
self.scfenergies.append(energy)
self._append_scfvalues_scftargets(inputfile, line)
# Sometimes the SCF does not converge, but does not halt the
# the run (like in bug 3184890). In this this case, we should
# remain consistent and use the energy from the last reported
# SCF cycle. In this case, ORCA print a banner like this:
#
# *****************************************************
# * ERROR *
# * SCF NOT CONVERGED AFTER 8 CYCLES *
# *****************************************************
if "SCF NOT CONVERGED AFTER" in line:
if not hasattr(self, "scfenergies"):
# Nuclear contribution : 0.00000 0.00000 0.00000
# -----------------------------------------
# Total Dipole Moment : 0.00000 -0.00000 -0.00000
# -----------------------------------------
# Magnitude (a.u.) : 0.00000
# Magnitude (Debye) : 0.00000
#
if line.strip() == "DIPOLE MOMENT":
self.skip_lines(inputfile, ['d', 'XYZ', 'electronic', 'nuclear', 'd'])
total = next(inputfile)
assert "Total Dipole Moment" in total
reference = [0.0, 0.0, 0.0]
dipole = numpy.array([float(d) for d in total.split()[-3:]])
dipole = utils.convertor(dipole, "ebohr", "Debye")
if not hasattr(self, 'moments'):
self.set_attribute('moments', [reference, dipole])
else:
try:
assert numpy.all(self.moments[1] == dipole)
except AssertionError:
self.logger.warning('Overwriting previous multipole moments with new values')
self.set_attribute('moments', [reference, dipole])
if "Molecular Dynamics Iteration" in line:
self.skip_lines(inputfile, ['d', 'ORCA MD', 'd', 'New Coordinates'])
line = next(inputfile)
tokens = line.split()
assert tokens[0] == "time"
time = utils.convertor(float(tokens[2]), "time_au", "fs")
# Component Electronic+nuclear Point charges Total
# --------------------------------------------------------------------------
# XX -38.3608511210 0.0000000000 -38.3608511210
# YY -39.0055467347 0.0000000000 -39.0055467347
# ...
#
if line.strip() == "Quadrupole Moment":
self.skip_lines(inputfile, ['d', 'b'])
reference_comment = next(inputfile)
assert "(in au)" in reference_comment
reference = next(inputfile).split()
self.reference = [reference[-7], reference[-4], reference[-1]]
self.reference = numpy.array([float(x) for x in self.reference])
self.reference = utils.convertor(self.reference, 'bohr', 'Angstrom')
self.skip_lines(inputfile, ['b', 'units', 'susc', 'b'])
line = next(inputfile)
assert line.strip() == "Second moments in atomic units"
self.skip_lines(inputfile, ['b', 'header', 'd'])
# Parse into a dictionary and then sort by the component key.
quadrupole = {}
for i in range(6):
line = next(inputfile)
quadrupole[line.split()[0]] = float(line.split()[-1])
lex = sorted(quadrupole.keys())
quadrupole = [quadrupole[key] for key in lex]
self.skip_line(inputfile, 'blank')
self._parse_mosyms_moenergies(inputfile, 1)
elif self.reference[0:2] == 'RO':
line = next(inputfile)
assert line.strip() == 'Virtual:'
self.skip_line(inputfile, 'blank')
self._parse_mosyms_moenergies(inputfile, 0)
# Both Psi3 and Psi4 print the final SCF energy right after
# the orbital energies, but the label is different. Psi4 also
# does DFT, and the label is also different in that case.
if "* SCF total energy" in line:
e = float(line.split()[-1])
if not hasattr(self, 'scfenergies'):
self.scfenergies = []
self.scfenergies.append(utils.convertor(e, 'hartree', 'eV'))
# We can also get some higher moments in Psi3, although here the dipole is not printed
# separately and the order is not lexicographical. However, the numbers seem
# kind of strange -- the quadrupole seems to be traceless, although I'm not sure
# whether the standard transformation has been used. So, until we know what kind
# of moment these are and how to make them raw again, we will only parse the dipole.
#
# --------------------------------------------------------------
# *** Electric multipole moments ***
# --------------------------------------------------------------
#
# CAUTION : The system has non-vanishing dipole moment, therefore
# quadrupole and higher moments depend on the reference point.
#
# -Coordinates of the reference point (a.u.) :
# x y z
self.metadata["methods"].append("DFT")
# The line containing the final SCF energy seems to be always identifiable like this.
if "Total SCF energy" in line or "Total DFT energy" in line:
# NWChem often does a line search during geometry optimization steps, reporting
# the SCF information but not the coordinates (which are not necessarily 'intermediate'
# since the step size can become smaller). We want to skip these SCF cycles,
# unless the coordinates can also be extracted (possibly from the gradients?).
if hasattr(self, 'linesearch') and self.linesearch:
return
if not hasattr(self, "scfenergies"):
self.scfenergies = []
energy = float(line.split()[-1])
energy = utils.convertor(energy, "hartree", "eV")
self.scfenergies.append(energy)
# The final MO orbitals are printed in a simple list, but apparently not for
# DFT calcs, and often this list does not contain all MOs, so make sure to
# parse them from the MO analysis below if possible. This section will be like this:
#
# Symmetry analysis of molecular orbitals - final
# -----------------------------------------------
#
# Numbering of irreducible representations:
#
# 1 ag 2 au 3 bg 4 bu
#
# Orbital symmetries:
#
# 1 bu 2 ag 3 bu 4 ag 5 bu
if 'CHARGE ON SYSTEM =' in line:
charge = int(line.split()[5])
self.set_attribute('charge', charge)
if 'SPIN STATE DEFINED' in line:
# find the multiplicity from the line token (SINGLET, DOUBLET, TRIPLET, etc)
mult = self.spinstate[line.split()[1]]
self.set_attribute('mult', mult)
# Read energy (in kcal/mol, converted to eV)
#
# FINAL HEAT OF FORMATION = -333.88606 KCAL = -1396.97927 KJ
if 'FINAL HEAT OF FORMATION =' in line:
if not hasattr(self, "scfenergies"):
self.scfenergies = []
self.scfenergies.append(utils.convertor(self.float(line.split()[5]), "kcal/mol", "eV"))
# Molecular mass parsing (units will be amu)
#
# MOLECULAR WEIGHT == 130.1890
if line[0:35] == ' MOLECULAR WEIGHT =':
self.molmass = self.float(line.split()[3])
#rotational constants
#Example:
# ROTATIONAL CONSTANTS IN CM(-1)
#
# A = 0.01757641 B = 0.00739763 C = 0.00712013
# could also read in moment of inertia, but this should just differ by a constant: rot cons= h/(8*Pi^2*I)
# note that the last occurence of this in the thermochemistry section has reduced precision,
# so we will want to use the 2nd to last instance
if line[0:40] == ' ROTATIONAL CONSTANTS IN CM(-1)':
def parse_geometry(self, lines):
"""Parse DALTON geometry lines into an atomcoords array."""
coords = []
for lin in lines:
# Without symmetry there are simply four columns, and with symmetry
# an extra label is printed after the atom type.
cols = lin.split()
if cols[1][0] == "_":
xyz = cols[2:]
else:
xyz = cols[1:]
# The assumption is that DALTON always print in atomic units.
xyz = [utils.convertor(float(x), 'bohr', 'Angstrom') for x in xyz]
coords.append(xyz)
return coords
to_bohr = lambda x: utils.convertor(x, 'Angstrom', 'bohr')
nuc_coords = [coord_template % tuple(to_bohr(coord))