Skip to content

Commit 7674f5f

Browse files
committed
Adds CREST adapter to transition state search
Enables the usage of CREST as a TS adapter. The implementation includes: - Adding 'crest' to the list of available TS adapters. - Implementing a function to automatically detect the CREST executable in various locations (standalone builds, conda environments, PATH). - Providing instructions for activating the CREST environment, if necessary. This allows ARC to leverage CREST for enhanced transition state search capabilities.
1 parent 82d12eb commit 7674f5f

1 file changed

Lines changed: 110 additions & 1 deletion

File tree

arc/settings/settings.py

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010
import string
1111
import sys
12+
import shutil
1213

1314
# Users should update the following server dictionary.
1415
# Instructions for RSA key generation can be found here:
@@ -88,7 +89,7 @@
8889
supported_ess = ['cfour', 'gaussian', 'mockter', 'molpro', 'orca', 'qchem', 'terachem', 'onedmin', 'xtb', 'torchani', 'openbabel']
8990

9091
# TS methods to try when appropriate for a reaction (other than user guesses which are always allowed):
91-
ts_adapters = ['heuristics', 'AutoTST', 'GCN', 'xtb_gsm']
92+
ts_adapters = ['heuristics', 'AutoTST', 'GCN', 'xtb_gsm', 'crest']
9293

9394
# List here job types to execute by default
9495
default_job_types = {'conf_opt': True, # defaults to True if not specified
@@ -427,3 +428,111 @@ def add_rmg_db_candidates(prefix: str) -> None:
427428
if path and os.path.isdir(path):
428429
RMG_DB_PATH = path
429430
break
431+
432+
433+
434+
def parse_version(folder_name):
435+
"""
436+
Parses the version from the folder name and returns a tuple for comparison.
437+
Supports versions like: 3.0.2, v212, 2.1, 2
438+
"""
439+
version_regex = re.compile(r"(?:v?(\d+)(?:\.(\d+))?(?:\.(\d+))?)", re.IGNORECASE)
440+
match = version_regex.search(folder_name)
441+
if not match:
442+
return (0, 0, 0)
443+
444+
major = int(match.group(1)) if match.group(1) else 0
445+
minor = int(match.group(2)) if match.group(2) else 0
446+
patch = int(match.group(3)) if match.group(3) else 0
447+
448+
# Example: v212 → (2, 1, 2)
449+
if major >= 100 and match.group(2) is None and match.group(3) is None:
450+
s = str(major).rjust(3, "0")
451+
major = int(s[0])
452+
minor = int(s[1])
453+
patch = int(s[2])
454+
455+
return (major, minor, patch)
456+
457+
458+
def find_highest_version_in_directory(directory, name_contains):
459+
"""
460+
Finds the file with the highest version in a directory containing a specific string.
461+
"""
462+
if not os.path.exists(directory):
463+
return None
464+
465+
highest_version_path = None
466+
highest_version = ()
467+
468+
for folder in os.listdir(directory):
469+
file_path = os.path.join(directory, folder)
470+
if name_contains.lower() in folder.lower() and os.path.isdir(file_path):
471+
crest_path = os.path.join(file_path, "crest")
472+
if os.path.isfile(crest_path) and os.access(crest_path, os.X_OK):
473+
version = parse_version(folder)
474+
if highest_version == () or version > highest_version:
475+
highest_version = version
476+
highest_version_path = crest_path
477+
return highest_version_path
478+
479+
480+
def find_crest_executable():
481+
"""
482+
Returns (crest_path, env_cmd):
483+
484+
- crest_path: full path to 'crest'
485+
- env_cmd: shell snippet to activate its environment (may be "")
486+
"""
487+
# Priority 1: /Local/ce_dana standalone builds
488+
crest_path = find_highest_version_in_directory("/Local/ce_dana", "crest")
489+
if crest_path and os.path.isfile(crest_path) and os.access(crest_path, os.X_OK):
490+
# Standalone binary: no env activation needed
491+
return crest_path, ""
492+
493+
# Priority 2: Conda/Mamba/Micromamba envs
494+
home = os.path.expanduser("~")
495+
potential_env_paths = [
496+
os.path.join(home, "anaconda3", "envs", "crest_env", "bin", "crest"),
497+
os.path.join(home, "miniconda3", "envs", "crest_env", "bin", "crest"),
498+
os.path.join(home, "miniforge3", "envs", "crest_env", "bin", "crest"),
499+
os.path.join(home, ".conda", "envs", "crest_env", "bin", "crest"),
500+
os.path.join(home, "mambaforge", "envs", "crest_env", "bin", "crest"),
501+
os.path.join(home, "micromamba", "envs", "crest_env", "bin", "crest"),
502+
]
503+
504+
# Also check the current env's bin
505+
current_env_bin = os.path.dirname(sys.executable)
506+
potential_env_paths.insert(0, os.path.join(current_env_bin, "crest"))
507+
508+
for crest_path in potential_env_paths:
509+
if os.path.isfile(crest_path) and os.access(crest_path, os.X_OK):
510+
# env_root = .../anaconda3 or .../miniforge3 or .../mambaforge etc.
511+
env_root = crest_path.split("/envs/crest_env/")[0]
512+
if "micromamba" in crest_path:
513+
env_cmd = (
514+
f"source {env_root}/etc/profile.d/micromamba.sh && "
515+
f"micromamba activate crest_env"
516+
)
517+
elif any(
518+
name in env_root
519+
for name in ("anaconda3", "miniconda3", "miniforge3", "mambaforge", ".conda")
520+
):
521+
env_cmd = (
522+
f"source {env_root}/etc/profile.d/conda.sh && "
523+
f"conda activate crest_env"
524+
)
525+
else:
526+
# If for some reason it's just a random prefix with crest in bin
527+
env_cmd = ""
528+
return crest_path, env_cmd
529+
530+
# Priority 3: PATH
531+
crest_in_path = shutil.which("crest")
532+
if crest_in_path:
533+
return crest_in_path, ""
534+
535+
return None, None
536+
537+
538+
CREST_PATH, CREST_ENV_PATH = find_crest_executable()

0 commit comments

Comments
 (0)