Source code for tcutility.results.crest
import os
from tcutility import molecule, pathfunc
from tcutility.results import Result
j = os.path.join
[docs]
def get_calc_files(calc_dir: str) -> Result:
"""Function that returns files relevant to AMS calculations stored in ``calc_dir``.
Args:
calc_dir: path pointing to the desired calculation
Returns:
Result object containing filenames and paths
"""
# collect all files in the current directory and subdirectories
files = []
for root, _, files_ in os.walk(calc_dir):
files.extend([j(root, file) for file in files_])
files = sorted(files, key=pathfunc.path_depth, reverse=True)
# parse the filenames
ret = Result()
ret.extra = []
ret.root = os.path.abspath(calc_dir)
for file in files:
if file.endswith(".out") and not file.endswith("g98.out"):
ret.out = os.path.abspath(file)
continue
if file.endswith("xtbopt.xyz"):
ret.opt_out = os.path.abspath(file)
continue
if file.endswith("xtbopt.log"):
ret.opt_history = os.path.abspath(file)
continue
if file.endswith("xtbscan.log"):
ret.scan_out = os.path.abspath(file)
continue
if file.endswith("vibspectrum"):
ret.vibspectrum = os.path.abspath(file)
continue
if file.endswith("hessian"):
ret.hessian = os.path.abspath(file)
continue
if file.endswith('crest_conformers.xyz'):
ret.conformers = os.path.abspath(file)
continue
if file.endswith('crest_rotamers.xyz'):
ret.rotamers = os.path.abspath(file)
continue
if file.endswith('crest_best.xyz'):
ret.best = os.path.abspath(file)
continue
ret.extra.append(os.path.abspath(file))
return ret
[docs]
def get_version(info: Result) -> Result:
"""Function to get the CREST version used in the calculation.
Args:
info: Result object containing CREST calculation settings.
Returns:
:Result object containing results about the CREST version:
- **full (str)** – the full version string as written by CREST.
- **major (str)** – major CREST version.
- **minor (str)** – minor CREST version.
"""
ret = Result()
with open(info.files.out) as out:
for line in out.readlines():
line = line.strip()
if "Version" not in line:
continue
version = line.split()[1][:-1]
ret.full = version
ret.major = version.split(".")[0]
ret.minor = version.split(".")[1]
ret.micro = ''
return ret
[docs]
def get_calculation_status(info: Result) -> Result:
"""Function that returns the status of the ORCA calculation described in reader. In case of non-succes it will also give possible reasons for the errors/warnings.
Args:
info: Result object containing ORCA calculation information.
Returns:
:Result object containing information about the calculation status:
- **fatal (bool)** – True if calculation cannot be analysed correctly, False otherwise
- **reasons (list[str])** – list of reasons to explain the status, they can be errors, warnings, etc.
- **name (str)** – calculation status written as a string, one of ("SUCCESS", "RUNNING", "UNKNOWN", "SUCCESS(W)", "FAILED")
- **code (str)** – calculation status written as a single character, one of ("S", "R", "U", "W" "F")
"""
ret = Result()
ret.fatal = True
ret.name = None
ret.code = None
ret.reasons = []
if "out" not in info.files:
ret.reasons.append("Calculation status unknown")
ret.name = "UNKNOWN"
ret.code = "U"
return ret
with open(info.files.out) as out:
lines = out.readlines()
if any(["[WARNING] Runtime exception occurred" in line for line in lines]):
ret.fatal = True
ret.name = "FAILED"
ret.code = "F"
line_index = [i for i, line in enumerate(lines) if "[WARNING] Runtime exception occurred" in line][0]
for line in lines[line_index + 1 :]:
if "##########" in line:
break
ret.reasons.append(line.strip())
return ret
if any(["CREST terminated normally." in line for line in lines]):
ret.fatal = False
ret.name = "SUCCESS"
ret.code = "S"
return ret
ret.name = "FAILED"
ret.code = "F"
return ret
[docs]
def get_molecules(info: Result) -> Result:
"""Function that returns information about the molecules for this calculation.
Args:
info: Result object containing ORCA calculation information.
Returns:
:Result object containing properties from the ORCA calculation:
- **input (plams.Molecule)** - the input molecule for this calculation.
- **output (plams.Molecule)** - the output molecule for this calculation, for example the optimized structure after a geometry optimization.
- **number_of_atoms (int)** - the number of atoms in the molecular system.
"""
ret = Result()
for file in info.files.extra:
if file.endswith(info.input.coord_file):
coord_file = file
break
ret.input = molecule.load(coord_file)
ret.number_of_atoms = len(ret.input)
ret.output = ret.input.copy()
if "best" in info.files:
ret.output = molecule.load(info.files.best)
if "conformers" in info.files:
with open(info.files.conformers) as ensemble:
lines = ensemble.readlines()
number_of_mols = len(lines)//(ret.number_of_atoms + 2)
mol_lines = [lines[i*(ret.number_of_atoms + 2):(i+1)*(ret.number_of_atoms + 2)] for i in range(number_of_mols)]
ret.conformers = [molecule.from_string("".join(mol_lines_)) for mol_lines_ in mol_lines]
if "rotamers" in info.files:
with open(info.files.rotamers) as ensemble:
lines = ensemble.readlines()
number_of_mols = len(lines)//(ret.number_of_atoms + 2)
mol_lines = [lines[i*(ret.number_of_atoms + 2):(i+1)*(ret.number_of_atoms + 2)] for i in range(number_of_mols)]
ret.rotamers = [molecule.from_string("".join(mol_lines_)) for mol_lines_ in mol_lines]
return ret
[docs]
def get_info(calc_dir: str) -> Result:
"""Function to read useful info about the calculation in ``calc_dir``. Returned information will depend on the type of file that is provided.
Args:
calc_dir: path pointing to the desired calculation.
Returns:
:Result object containing results about the calculation and AMS:
- **version (Result)** – information about the AMS version used, see :func:`get_version`.
- **files (Result)** - paths to files that are important for this calculation.
- **input (Result)** - information about the input of this calculation, see :func:`get_input`.
- **level (Result)** - information about the level of theory used for this calculation, see :func:`get_level_of_theory`.
- **engine (str)** – the engine that was used to perform the calculation, for example 'adf', 'dftb', ...
- **status (Result)** – information about calculation status, see :func:`get_calculation_status`.
- **molecule (Result)** – information about the input and output molecules and the molecular system in general, see :func:`get_molecules`.
"""
ret = Result()
ret.engine = "crest"
ret.files = get_calc_files(calc_dir)
# store the input of the calculation
ret.input = get_input(ret)
# store information about the version of AMS
ret.version = get_version(ret)
# # store the calculation status
ret.status = get_calculation_status(ret)
# # read molecules
ret.molecule = get_molecules(ret)
return ret
if __name__ == "__main__":
from tcutility import log
ret = get_info("/Users/yumanhordijk/PhD/Projects/RadicalAdditionWorkflow/calculations/preparation/radicals/NMe2")
log.log(ret.files)
print(ret.status)
# ret = get_info("/Users/yumanhordijk/PhD/ganesh_project/calculations/AlCl3_xtb/scan_1")
# print(ret)