Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
"""Test scan logfiles in cclib"""
import os
import unittest
import numpy
import cclib
from skip import skipForParser
__filedir__ = os.path.realpath(os.path.dirname(__file__))
OPT_DONE = cclib.parser.data.ccData.OPT_DONE
OPT_NEW = cclib.parser.data.ccData.OPT_NEW
class GenericScanTestBase(unittest.TestCase):
"""Base relaxed potential energy surface scan unittest."""
def assertOptNew(self, optstatus_value):
return optstatus_value & OPT_NEW == OPT_NEW
def assertOptDone(self, optstatus_value):
return optstatus_value & OPT_DONE == OPT_DONE
class GenericScanTest_optdone_bool(GenericScanTestBase):
"""Generic relaxed potential energy surface scan unittest."""
datatype = cclib.parser.data.ccData_optdone_bool
# Convert any string into the parser object we will be using.
if isinstance(parser, str):
parser = all_parsers[parser]
datadir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "data"))
programdir = os.path.join(get_program_dir(parser.__name__), subdir)
inputs = [os.path.join(datadir, programdir, fn) for fn in files]
# We should be able to pass a list of length one here, but for some reason
# this does not work with some parsers and we get errors.
if len(inputs) == 1:
inputs = inputs[0]
stream = stream or sys.stdout
logfile = parser(inputs, logstream=stream, loglevel=loglevel,
datatype=datatype or cclib.parser.data.ccData)
data = logfile.parse()
return data, logfile
def construct(self,json_data):
for Key, Value in ccData._attributes.items():
jsonKey = Value.jsonKey
attributePath = Value.attributePath.split(":")
if attributePath[0] == 'N/A':
continue
levels = len(attributePath)
if attributePath[0] in json_data:
l1_data_object = json_data[attributePath[0]]
if levels == 1:
if jsonKey in l1_data_object:
self.datatype[Key] = l1_data_object[jsonKey]
elif levels >= 2:
if attributePath[1] in l1_data_object:
l2_data_object = l1_data_object[attributePath[1]]
if criteria[istart:istart+9].strip():
geotargets.append(float(criteria[istart:istart+9]))
else:
geotargets.append(numpy.inf)
self.skip_line(inputfile, 'dashes')
values = next(inputfile)
step = int(values.split()[0])
geovalues = []
for istart in starts:
if values[istart:istart+9].strip():
geovalues.append(float(values[istart:istart+9]))
if step == 1:
self.optstatus[-1] += data.ccData.OPT_NEW
# This assertion may be too restrictive, but we haven't seen the geotargets change.
# If such an example comes up, update the value since we're interested in the last ones.
if not hasattr(self, 'geotargets'):
self.geotargets = geotargets
else:
assert self.geotargets == geotargets
if not hasattr(self, 'geovalues'):
self.geovalues = []
self.geovalues.append(geovalues)
# This message signals a converged optimization, in which case we want
# to append the index for this step to optdone, which should be equal
# to the number of geovalues gathered so far.
if "Optimization is complete!" in line:
# Need to decide on a number format.
cjson_dict['chemical json'] = 0
if self.jobfilename is not None:
cjson_dict['name'] = self.pathname(self.jobfilename)
# These are properties that can be collected using Open Babel.
if _has_openbabel:
cjson_dict['smiles'] = self.pbmol.write('smiles')
cjson_dict['inchi'] = self.pbmol.write('inchi')
cjson_dict['inchikey'] = self.pbmol.write('inchikey')
cjson_dict['formula'] = self.pbmol.formula
# TODO Incorporate unit cell information.
# Iterate through the attribute list present in ccData. Depending on the
# availability of the attribute add it at the right 'level'.
for attribute_name, v in ccData._attributes.items():
if not hasattr(self.ccdata, attribute_name):
continue
attribute_path = v.attribute_path.split(":")
# Depth of the attribute in the CJSON.
levels = len(attribute_path)
# The attributes which haven't been included in the CJSON format.
if attribute_path[0] == 'N/A':
continue
if attribute_path[0] not in cjson_dict:
cjson_dict[attribute_path[0]] = dict()
l1_data_object = cjson_dict[attribute_path[0]]
'Molpro' : ['fonames', 'fooverlaps', 'fragnames', 'frags'],
'NWChem' : ['fonames', 'fooverlaps', 'fragnames', 'frags'],
'ORCA' : ['fonames', 'fooverlaps', 'fragnames', 'frags'],
'Psi' : ['fonames', 'fooverlaps', 'fragnames', 'frags'],
'QChem' : ['fonames', 'fooverlaps', 'fragnames', 'frags'],
}
not_possible = {
'Psi' : ['aooverlaps', 'vibirs'],
'QChem' : ['aooverlaps', 'etrotats'],
}
# For each attribute, get a list of Boolean values for each parser that flags
# if it has been parsed by at least one unit test. Substitute an OK sign or
# T/D appropriately, with the exception of attributes that have been explicitely
# designated as N/A.
attributes = sorted(cclib.parser.data.ccData._attrlist)
for attr in attributes:
parsed = [any([attr in t['data'].__dict__ for t in alltests[p]]) for p in parser_names]
for ip, p in enumerate(parsed):
if p:
parsed[ip] = "√"
else:
if attr in not_applicable.get(parser_names[ip], []):
parsed[ip] = "N/A"
elif attr in not_possible.get(parser_names[ip], []):
parsed[ip] = "N/P"
else:
parsed[ip] = "T/D"
lines.append(colfmt*ncols % tuple(["`%s`_" % attr] + parsed))
lines.append(dashes)
lines.append("")
if hasattr(self, 'optstatus'):
unconverged_indexes = [x for x, y in enumerate(self.optstatus) if y & self.OPT_UNCONVERGED > 0]
return self.atomcoords[unconverged_indexes]
else:
return self.atomcoords
@property
def nelectrons(self):
return Electrons(self).count()
@property
def closed_shell(self):
return orbitals.Orbitals(self).closed_shell()
class ccData_optdone_bool(ccData):
"""This is the version of ccData where optdone is a Boolean."""
def __init__(self, *args, **kwargs):
super(ccData_optdone_bool, self).__init__(*args, **kwargs)
self._attributes["optdone"] = Attribute(bool, 'done', 'optimization')
def setattributes(self, *args, **kwargs):
invalid = super(ccData_optdone_bool, self).setattributes(*args, **kwargs)
# Reduce optdone to a Boolean, because it will be parsed as a list. If this list has any element,
# it means that there was an optimized structure and optdone should be True.
if hasattr(self, 'optdone'):
self.optdone = len(self.optdone) > 0
# Measures of convergence in internal coordinates in au.
# Criteria marked as inactive (o), active & met (*), and active & unmet ( ).
# ---------------------------------------------------------------------------------------------
# Step Total Energy Delta E MAX Force RMS Force MAX Disp RMS Disp
# ---------------------------------------------------------------------------------------------
# Convergence Criteria 1.00e-06 * 3.00e-04 * o 1.20e-03 * o
# ---------------------------------------------------------------------------------------------
# 2 -379.77675264 -7.79e-03 1.88e-02 4.37e-03 o 2.29e-02 6.76e-03 o ~
# ---------------------------------------------------------------------------------------------
#
if (self.section == "Convergence Check") and line.strip() == "==> Convergence Check <==" \
and not hasattr(self, 'finite_difference'):
if not hasattr(self, "optstatus"):
self.optstatus = []
self.optstatus.append(data.ccData.OPT_UNKNOWN)
self.skip_lines(inputfile, ['b', 'units', 'comment', 'dash+tilde', 'header', 'dash+tilde'])
# These are the position in the line at which numbers should start.
starts = [27, 41, 55, 69, 83]
criteria = next(inputfile)
geotargets = []
for istart in starts:
if criteria[istart:istart+9].strip():
geotargets.append(float(criteria[istart:istart+9]))
else:
geotargets.append(numpy.inf)
self.skip_line(inputfile, 'dashes')
atomnos = [self.pt.number[atomsym] for atomsym in atomsyms]
atomcoords = [line[1:4] for line in lines]
# Everything beyond the fourth column is ignored.
all_atomcoords.append(atomcoords)
except StopIteration:
break
attributes = {
'natom': natom,
'atomnos': atomnos,
'atomcoords': all_atomcoords,
'metadata': {"comments": comments},
}
self.data = ccData(attributes)
# The attributes which haven't been included in the CJSON format.
if attribute_path[0] == 'N/A':
continue
if attribute_path[0] not in cjson_dict:
cjson_dict[attribute_path[0]] = dict()
l1_data_object = cjson_dict[attribute_path[0]]
# 'moments' and 'atomcoords' key will contain processed data
# obtained from the output file. TODO rewrite this
if attribute_name in ('moments', 'atomcoords'):
if attribute_name == 'moments':
dipole_moment = self._calculate_total_dipole_moment()
if dipole_moment is not None:
cjson_dict['properties'][ccData._attributes['moments'].json_key] = dipole_moment
else:
cjson_dict['atoms']['coords'] = dict()
cjson_dict['atoms']['coords']['3d'] = self.ccdata.atomcoords[-1].flatten().tolist()
continue
if levels == 1:
self.set_JSON_attribute(l1_data_object, attribute_name)
elif levels >= 2:
if attribute_path[1] not in l1_data_object:
l1_data_object[attribute_path[1]] = dict()
l2_data_object = l1_data_object[attribute_path[1]]
if levels == 2:
self.set_JSON_attribute(l2_data_object, attribute_name)
elif levels == 3:
if attribute_path[2] not in l2_data_object: