Skip to content

Commit

Permalink
Merge pull request #55 from molssi-seamm/dev
Browse files Browse the repository at this point in the history
Bugfix: MOPAC files with references in comments
  • Loading branch information
seamm authored Nov 3, 2024
2 parents ef60e68 + 3b9d6b5 commit 10b4cca
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 13 deletions.
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
=======
History
=======
2024.11.3 -- Bugfix: MOPAC files with references in comments
* Fixed a bug that caused a crash when reading MOPAC files with references in the
comments.
* Updated the MOPAC reader to the new approach for running MOPAC in the cases that
it is needed: Z-matrices and mixed inputs that OpenBabel can't handle.

2024.8.23 -- Enhancements to directory handling
* Changed the handling of paths to make them relative to the directory that the step
is running in. In a loop, for instance, files are relative to the iteration
Expand Down
3 changes: 2 additions & 1 deletion read_structure_step/formats/mop/find_mopac.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import seamm_util
from pathlib import Path
import os

import seamm_util

mopac_error_identifiers = []


Expand Down
16 changes: 5 additions & 11 deletions read_structure_step/formats/mop/obabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@

from openbabel import openbabel
from read_structure_step.formats.registries import register_reader
import seamm
from seamm_util import Q_
from .find_mopac import find_mopac
from .find_mopac import find_mopac # noqa: F401

if "OpenBabel_version" not in globals():
OpenBabel_version = None
Expand Down Expand Up @@ -196,6 +195,7 @@ def load_mop(
references=None,
bibliography=None,
save_data=True,
step=None,
**kwargs,
):
"""Read a MOPAC input file.
Expand Down Expand Up @@ -405,10 +405,6 @@ def load_mop(
except Exception:
logger.info("**** falling back to MOPAC")
# Try using a MOPAC output file instead. Works for e.g. mixed coordinates
mopac_exe = find_mopac()
if mopac_exe is None:
raise FileNotFoundError("The MOPAC executable could not be found")

# Create an input file
text = ["0SCF", "title", "description"]
text.extend(raw_geometry_lines)
Expand All @@ -418,10 +414,7 @@ def load_mop(

logger.debug(f"MOPAC input file:\n\n{files['mopac.dat']}\n")

local = seamm.ExecLocal()
result = local.run(
cmd=[mopac_exe, "mopac.dat"], files=files, return_files=["mopac.out"]
)
result = step.run_mopac(files=files, return_files=["mopac.out"])

if result["mopac.out"]["data"] is None:
raise RuntimeError("MOPAC failed: " + result["mopac.out"]["exception"])
Expand Down Expand Up @@ -642,6 +635,7 @@ def load_mop(
keyword = metadata[keyword]
if value == "":
print(f"Value for {keyword} missing in MOPAC .mop file")
print("\n\t".join(description_lines))
continue
if "reference" in keyword:
description = keyword.split(".")[0]
Expand Down Expand Up @@ -672,7 +666,7 @@ def load_mop(
):
stderr = float(stderr) * kcal2kJ
system_properties.put(new_keyword, stderr)
if (
if "reference" not in keyword and (
"heat capacity" in keyword
or "enthalpy" in keyword
or "entropy" in keyword
Expand Down
5 changes: 5 additions & 0 deletions read_structure_step/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def read(
printer=None,
references=None,
bibliography=None,
step=None,
):
"""
Calls the appropriate functions to parse the requested file.
Expand Down Expand Up @@ -70,6 +71,9 @@ def read(
bibliography : dict
The bibliography as a dictionary.
step : seamm.Node = None
The node in the flowchart, used for running e.g. MOPAC.
Returns
-------
[Configuration]
Expand Down Expand Up @@ -124,6 +128,7 @@ def read(
printer=printer,
references=references,
bibliography=bibliography,
step=step,
)

return configurations
80 changes: 79 additions & 1 deletion read_structure_step/read_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
directory, and is used for all normal output from this step.
"""

import configparser
import importlib
import logging
import os
from pathlib import PurePath, Path
import pprint # noqa: F401
import shutil
import tarfile
import tempfile
import textwrap
Expand All @@ -23,7 +27,7 @@
from .read import read
import seamm
from seamm_util import ureg, Q_ # noqa: F401
from seamm_util import getParser
from seamm_util import Configuration, getParser
import seamm_util.printing as printing
from seamm_util.printing import FormattedText as __
from .utils import guess_extension
Expand Down Expand Up @@ -259,6 +263,7 @@ def run(self):
printer=printer.important,
references=self.references,
bibliography=self._bibliography,
step=self,
)

# Finish the output
Expand Down Expand Up @@ -371,6 +376,7 @@ def read_tarfile(self, tarfile_path, P):
printer=printer.important,
references=self.references,
bibliography=self._bibliography,
step=self,
)

tmp_path.unlink()
Expand All @@ -384,3 +390,75 @@ def read_tarfile(self, tarfile_path, P):
indent=4 * " ",
)
)

def run_mopac(self, files=None, return_files=["mopac.out"]):
"""Run MOPAC to parse the input file."""

import mopac_step

# Access the options
seamm_options = self.global_options

executor = self.flowchart.executor

# Read configuration file for MOPAC if it exists
executor_type = executor.name
full_config = configparser.ConfigParser()
ini_dir = Path(seamm_options["root"]).expanduser()
path = ini_dir / "mopac.ini"
# If the config file doesn't exists, get the default
if not path.exists():
resources = importlib.resources.files("mopac_step") / "data"
ini_text = (resources / "mopac.ini").read_text()
txt_config = Configuration(path)
txt_config.from_string(ini_text)

# Work out the conda info needed
txt_config.set_value("local", "conda", os.environ["CONDA_EXE"])
txt_config.set_value("local", "conda-environment", "seamm-mopac")
txt_config.save()

full_config.read(ini_dir / "mopac.ini")

# Getting desperate! Look for an executable in the path
if executor_type not in full_config:
path = shutil.which("mopac")
if path is None:
raise RuntimeError(
f"No section for '{executor_type}' in MOPAC ini file "
f"({ini_dir / 'mopac.ini'}), nor in the defaults, nor "
"in the path!"
)
else:
txt_config = Configuration(path)
txt_config.add_section(executor_type)
txt_config.set_value(executor_type, "installation", "local")
txt_config.set_value(executor_type, "code", str(path))
txt_config.save()
full_config.read(ini_dir / "mopac.ini")

config = dict(full_config.items(executor_type))

# Use the matching version of the seamm-mopac image by default.
config["version"] = mopac_step.__version__

env = {
"OMP_NUM_THREADS": "1",
}

result = executor.run(
cmd=["{code}", "mopac.dat", ">", "stdout.txt", "2>", "stderr.txt"],
config=config,
directory=self.directory,
files=files,
return_files=return_files,
in_situ=True,
shell=True,
env=env,
)

if not result:
self.logger.error("There was an error running MOPAC")
return None

return result

0 comments on commit 10b4cca

Please sign in to comment.