|
9 | 9 | import os |
10 | 10 | import string |
11 | 11 | import sys |
| 12 | +import shutil |
12 | 13 |
|
13 | 14 | # Users should update the following server dictionary. |
14 | 15 | # Instructions for RSA key generation can be found here: |
|
88 | 89 | supported_ess = ['cfour', 'gaussian', 'mockter', 'molpro', 'orca', 'qchem', 'terachem', 'onedmin', 'xtb', 'torchani', 'openbabel'] |
89 | 90 |
|
90 | 91 | # 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'] |
92 | 93 |
|
93 | 94 | # List here job types to execute by default |
94 | 95 | default_job_types = {'conf_opt': True, # defaults to True if not specified |
@@ -427,3 +428,111 @@ def add_rmg_db_candidates(prefix: str) -> None: |
427 | 428 | if path and os.path.isdir(path): |
428 | 429 | RMG_DB_PATH = path |
429 | 430 | 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