Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for Turbomole #101

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

jonathan-schoeps
Copy link
Contributor

A new interface for Turbomole has been implemented to facilitate its use in post-processing step, including both single-point energy calculations and geometry optimizations.

Signed-off-by: Jonathan Schöps <[email protected]>
Merge latest changes to dev branche
@jonathan-schoeps jonathan-schoeps added the algorithm Related to the back-end algorithm for generating the mindless molecules. label Jan 8, 2025
@jonathan-schoeps jonathan-schoeps self-assigned this Jan 8, 2025
@jonathan-schoeps jonathan-schoeps linked an issue Jan 8, 2025 that may be closed by this pull request
Copy link
Member

@marcelmbn marcelmbn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some things to do, but it's already on a very good track!

CHANGELOG.md Show resolved Hide resolved
# > Functional/Method: Options: <str>
functional = "PBE"
# > Basis set: Options: <str>
basis = "def2-SVP"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested if this combination works if you directly use this mindlessgen.toml? If yes, just resolve this question.

@@ -1048,6 +1133,9 @@ def load_from_dict(self, config_dict: dict) -> None:
},
"orca": {
"orca_option": "opt"
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a simplified and exemplary structure only anyway. You can make this whole dictionary example also a bit shorter to save some lines of code (or comment, in this case).

Comment on lines +233 to +250
def get_turbomole_path(binary_name: str | Path | None = None) -> Path:
"""
Get the path to the turbomole binary based on different possible names
that are searched for in the PATH.
"""
default_turbomole_names: list[str | Path] = ["ridft", "jobex"]
# put binary name at the beginning of the lixt to prioritize it
if binary_name is not None:
binary_names = [binary_name] + default_turbomole_names
else:
binary_names = default_turbomole_names
# Get turbomole path from 'which ridft' command
for binpath in binary_names:
which_ridft = shutil.which(binpath)
if which_ridft:
ridft_path = Path(which_ridft).resolve()
return ridft_path
raise ImportError("'turbomole' binary could not be found.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole part does not yet resolve the problem of TURBOMOLE having two instead of only one binary name.
My suggestion (to be in-line with how it's handled for ORCA and xTB):
Document everything in a way that the ridft binary can be handled and only this full path (including the binary itself) is then handled from now on.
For geometry optimizations or other methods, the jobex path is then assumed to be the same as the ridft one, just with a different suffix (xx/yy/ridft -> xx/yy/jobex).
Currently the return variable can contain either ridft, jobex or whatever the user puts in there.

src/mindlessgen/qm/tm.py Show resolved Hide resolved
molecule.write_xyz_to_file(temp_path / molfile)

# convert molfile to coord file (tm format)
command = f"x2t {temp_path / molfile} > {temp_path / 'coord'}"
Copy link
Member

@marcelmbn marcelmbn Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do this rather in the code and not relying on another part in the code.
Using Copilot or ChatGPT, this should be quite easy (just add a new function write_coord_to_file similar to write_xyz_to_file in molecule.py in Molecule.
The conversion constant is already included in refinement.py.

Comment on lines +51 to +61

try:
# run the command in a shell
sp.run(command, shell=True, check=True)
except sp.CalledProcessError as e:
print(f"The xyz file could not be converted to a coord file: {e}")

if verbosity > 2:
with open(temp_path / "coord", encoding="utf8") as f:
tm_coordinates = f.read()
print(tm_coordinates)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we'd get rid of this code as well (printing out coordinates is just for debugging).

f.write(tm_input)

# Setup the turbomole optimization command including the max number of optimization cycles
arguments = [f"PARNODES=1 jobex -ri -c {max_cycles} > jobex.out"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Arguments should be a list of strings, e.g. ["jobex", "-ri"] and so on.
  2. We don't include the pipe command in the arguments, as this belongs to the handling of the subprocess in _run.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

            orca_out = sp.run(
                [str(self.path)] + arguments,
                cwd=temp_path,
                capture_output=True,
                check=True,
            )

This does the piping of the output for you. (See comment below at: #101 (comment))

check=True,
shell=True,
)
if "PARNODES=1 ridft > ridft.out" in arguments[0]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

and the return code
"""
try:
sp.run(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at the orca.py, how it's handled there:

            orca_out = sp.run(
                [str(self.path)] + arguments,
                cwd=temp_path,
                capture_output=True,
                check=True,
            )
            # get the output of the ORCA calculation (of both stdout and stderr)
            orca_log_out = orca_out.stdout.decode("utf8", errors="replace")
            orca_log_err = orca_out.stderr.decode("utf8", errors="replace")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
algorithm Related to the back-end algorithm for generating the mindless molecules.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TM class for TURBOMOLE interface
2 participants