2024-08-29 20:48:16 +02:00
|
|
|
|
2024-08-29 23:18:44 +02:00
|
|
|
import time
|
|
|
|
import threading
|
2024-08-29 20:48:16 +02:00
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
from pathlib import Path
|
|
|
|
import subprocess
|
|
|
|
import platform
|
|
|
|
from datetime import datetime
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
from molecule import get_molecules_from_db
|
|
|
|
from molecule import generate_xyz
|
2024-08-29 23:18:44 +02:00
|
|
|
from utils import print_col, stdout_col
|
2024-08-29 20:48:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
current_date = datetime.now()
|
|
|
|
|
|
|
|
quack_root = os.getenv('QUACK_ROOT')
|
|
|
|
|
|
|
|
# User Name
|
2024-11-15 18:55:39 +01:00
|
|
|
# Fallback to 'default_user' if USER is not set
|
|
|
|
user_name = os.getenv('USER', 'default_user')
|
2024-08-29 20:48:16 +02:00
|
|
|
|
|
|
|
# Operating System
|
|
|
|
os_name = platform.system()
|
|
|
|
os_release = platform.release()
|
|
|
|
os_version = platform.version()
|
|
|
|
|
|
|
|
# CPU Information
|
|
|
|
machine = platform.machine()
|
|
|
|
processor = platform.processor()
|
|
|
|
|
|
|
|
# System Architecture
|
|
|
|
architecture = platform.architecture()[0]
|
|
|
|
|
|
|
|
# Python Version
|
|
|
|
python_version_full = platform.python_version_tuple()
|
|
|
|
PYTHON_VERSION = "{}.{}".format(python_version_full[0], python_version_full[1])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(f"The current date and time is {current_date.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
|
|
print(f"User Name: {user_name}")
|
|
|
|
print(f"Operating System: {os_name} {os_release} ({os_version})")
|
|
|
|
print(f"CPU: {processor} ({machine})")
|
|
|
|
print(f"System Architecture: {architecture}")
|
|
|
|
print(f"QUACK_ROOT: {quack_root}")
|
|
|
|
print(f"Python version: {python_version_full}\n\n")
|
|
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Benchmark Data Sets")
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
'-s', '--set_type',
|
|
|
|
choices=['light', 'medium', 'heavy'],
|
|
|
|
default='light',
|
|
|
|
help="Specify the type of data set: light (default), medium, or heavy."
|
|
|
|
)
|
2024-11-15 17:22:07 +01:00
|
|
|
|
|
|
|
thresh_default = 1e-7
|
2024-08-30 00:10:19 +02:00
|
|
|
parser.add_argument(
|
|
|
|
'-t', '--thresh',
|
|
|
|
type=float,
|
2024-11-15 17:22:07 +01:00
|
|
|
default=thresh_default,
|
|
|
|
help='Threshold for acceptable difference, default = {}'.format(thresh_default)
|
2024-08-30 00:10:19 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-29 20:48:16 +02:00
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
2024-08-30 00:10:19 +02:00
|
|
|
THRESH = args.thresh
|
|
|
|
|
2024-08-29 20:48:16 +02:00
|
|
|
if args.set_type == 'light':
|
|
|
|
bench = 'FeatherBench'
|
|
|
|
bench_title = "\n\nSelected Light Benchmark: {}\n\n".format(bench)
|
|
|
|
elif args.set_type == 'medium':
|
|
|
|
bench = 'BalanceBench'
|
|
|
|
bench_title = "\n\nSelected Medium Benchmark: {}\n\n".format(bench)
|
|
|
|
elif args.set_type == 'heavy':
|
|
|
|
bench = 'TitanBench'
|
|
|
|
bench_title = "\n\nSelected Heavy Benchmark: {}\n\n".format(bench)
|
|
|
|
else:
|
|
|
|
bench_title = "\n\nSelected Light Benchmark: {}\n\n".format(bench)
|
|
|
|
|
|
|
|
print(bench_title.center(150, '-'))
|
|
|
|
print("\n\n")
|
|
|
|
|
|
|
|
# ---
|
|
|
|
|
|
|
|
class Quack_Job:
|
|
|
|
|
2024-11-15 17:22:07 +01:00
|
|
|
def __init__(self, mol, multip, basis, geom, methd, workdir):
|
2024-08-29 20:48:16 +02:00
|
|
|
self.mol = mol
|
|
|
|
self.multip = multip
|
|
|
|
self.basis = basis
|
|
|
|
self.geom = geom
|
|
|
|
self.methd = methd
|
2024-11-15 17:22:07 +01:00
|
|
|
self.workdir = workdir
|
2024-08-29 20:48:16 +02:00
|
|
|
|
|
|
|
def prep_inp(self):
|
|
|
|
|
|
|
|
# geometry
|
2024-11-15 17:22:07 +01:00
|
|
|
if not os.path.exists("{}/mol".format(self.workdir)):
|
|
|
|
os.makedirs("{}/mol".format(self.workdir))
|
|
|
|
generate_xyz(self.geom, filename="{}/mol/{}.xyz".format(self.workdir, self.mol))
|
2024-08-29 20:48:16 +02:00
|
|
|
|
|
|
|
# input files
|
|
|
|
for inp in ["methods", "options"]:
|
|
|
|
inp_file = "{}.{}".format(inp, self.methd.upper())
|
|
|
|
if os.path.exists("inp/{}".format(inp_file)):
|
2024-08-30 20:38:00 +02:00
|
|
|
shutil.copy("{}/tests/inp/{}".format(quack_root, inp_file),
|
2024-11-15 17:22:07 +01:00
|
|
|
"{}/input/{}".format(self.workdir, inp))
|
2024-08-29 20:48:16 +02:00
|
|
|
else:
|
|
|
|
print_col("File 'inp/{}' does not exist.".format(inp_file), "red")
|
|
|
|
sys.exit(1)
|
|
|
|
|
2024-11-15 19:52:24 +01:00
|
|
|
def run_ci(self):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
os.chdir('..')
|
|
|
|
|
|
|
|
command = [
|
|
|
|
'python{}'.format(PYTHON_VERSION), 'PyDuck.py',
|
|
|
|
'--working_dir', '{}'.format(self.workdir),
|
|
|
|
'-x', '{}'.format(self.mol),
|
|
|
|
'-b', '{}'.format(self.basis),
|
|
|
|
'-m', '{}'.format(self.multip)
|
|
|
|
]
|
|
|
|
|
|
|
|
file_out = "{}/{}/{}_{}_{}.out".format(self.workdir, self.methd, self.mol, self.multip, self.basis)
|
|
|
|
with open(file_out, 'w') as fobj:
|
|
|
|
result = subprocess.run(command, stdout=fobj, stderr=subprocess.PIPE, text=True)
|
|
|
|
if result.stderr:
|
|
|
|
print("Error output:", result.stderr)
|
|
|
|
|
|
|
|
os.chdir('tests')
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
print_col(f"An error occurred: {str(e)}", "red")
|
|
|
|
|
2024-11-15 17:22:07 +01:00
|
|
|
def run(self):
|
2024-08-29 23:18:44 +02:00
|
|
|
|
|
|
|
def display_spinner():
|
|
|
|
spinner = ['|', '/', '-', '\\']
|
|
|
|
idx = 0
|
|
|
|
while not done_event.is_set():
|
2024-08-30 00:10:19 +02:00
|
|
|
stdout_col(f'\r Testing {self.methd} ({self.basis}) {spinner[idx]}', "cyan")
|
2024-08-29 23:18:44 +02:00
|
|
|
sys.stdout.flush()
|
|
|
|
idx = (idx + 1) % len(spinner)
|
2024-08-30 00:10:19 +02:00
|
|
|
time.sleep(0.05)
|
2024-08-31 13:30:34 +02:00
|
|
|
stdout_col(f'\r Testing {self.methd} ({self.basis}) \n\n', "cyan")
|
2024-08-29 23:18:44 +02:00
|
|
|
|
|
|
|
done_event = threading.Event()
|
|
|
|
spinner_thread = threading.Thread(target=display_spinner)
|
|
|
|
spinner_thread.start()
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
os.chdir('..')
|
|
|
|
#print_col(f" Starting QuAck..", "magenta")
|
|
|
|
#print_col(f" $ cd ..", "magenta")
|
|
|
|
|
|
|
|
command = [
|
|
|
|
'python{}'.format(PYTHON_VERSION), 'PyDuck.py',
|
2024-11-15 17:22:07 +01:00
|
|
|
'--working_dir', '{}'.format(self.workdir),
|
2024-08-29 23:18:44 +02:00
|
|
|
'-x', '{}'.format(self.mol),
|
|
|
|
'-b', '{}'.format(self.basis),
|
|
|
|
'-m', '{}'.format(self.multip)
|
|
|
|
]
|
|
|
|
#print_col(f" $ {' '.join(command)}", "magenta")
|
|
|
|
|
2024-11-15 17:22:07 +01:00
|
|
|
file_out = "{}/{}/{}_{}_{}.out".format(self.workdir, self.methd, self.mol, self.multip, self.basis)
|
2024-08-29 23:18:44 +02:00
|
|
|
with open(file_out, 'w') as fobj:
|
|
|
|
result = subprocess.run(command, stdout=fobj, stderr=subprocess.PIPE, text=True)
|
|
|
|
if result.stderr:
|
|
|
|
print("Error output:", result.stderr)
|
|
|
|
|
|
|
|
os.chdir('tests')
|
|
|
|
#print_col(f" $ cd tests", "magenta")
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
print_col(f"An error occurred: {str(e)}", "red")
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
|
|
|
done_event.set()
|
|
|
|
spinner_thread.join()
|
2024-08-30 00:10:19 +02:00
|
|
|
|
2024-11-15 17:22:07 +01:00
|
|
|
def check_data(self, data_ref, test_failed_):
|
|
|
|
filepath = '{}/test/Rtest.dat'.format(self.workdir)
|
2024-08-30 00:10:19 +02:00
|
|
|
data_new = {}
|
|
|
|
try:
|
|
|
|
# read data_new
|
|
|
|
with open(filepath, 'r') as f:
|
|
|
|
lines = f.readlines()
|
|
|
|
for i in range(0, len(lines) - 1, 2):
|
|
|
|
key = lines[i].strip()
|
|
|
|
value = lines[i + 1].strip()
|
|
|
|
data_new[key] = float(value) # Convert value to float
|
|
|
|
|
|
|
|
# Compare with data_ref
|
|
|
|
for key in data_ref:
|
|
|
|
if key not in data_new:
|
|
|
|
print_col(f" 😐 {key} missing ⚠️ ", "yellow")
|
2024-11-15 17:22:07 +01:00
|
|
|
test_failed_ = True
|
2024-08-30 00:10:19 +02:00
|
|
|
else:
|
|
|
|
diff = abs(data_new[key] - data_ref[key]) / (1e-15 + abs(data_ref[key]))
|
|
|
|
if(diff <= THRESH):
|
2024-08-31 13:30:34 +02:00
|
|
|
print_col(f" 🙂 {key}", "green")
|
2024-08-30 00:10:19 +02:00
|
|
|
else:
|
2024-08-30 20:15:39 +02:00
|
|
|
print_col(f" ☹️ {key}: ❌ {data_ref[key]} ≠ {data_new[key]}", "red")
|
2024-11-15 17:22:07 +01:00
|
|
|
test_failed_ = True
|
2024-08-30 00:10:19 +02:00
|
|
|
except FileNotFoundError:
|
|
|
|
print_col(f"Error: The file '{filepath}' does not exist.", "red")
|
2024-08-30 20:38:00 +02:00
|
|
|
sys.exit(1)
|
2024-08-30 00:10:19 +02:00
|
|
|
except Exception as e:
|
|
|
|
print_col(f"An error occurred: {str(e)}", "red")
|
2024-08-30 20:38:00 +02:00
|
|
|
sys.exit(1)
|
2024-08-29 23:18:44 +02:00
|
|
|
|
2024-08-30 00:10:19 +02:00
|
|
|
|
2024-08-29 20:48:16 +02:00
|
|
|
# ---
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
|
|
work_path = Path('{}/tests/work'.format(quack_root))
|
|
|
|
if not work_path.exists():
|
|
|
|
work_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
print(f"Directory '{work_path}' created.\n")
|
|
|
|
|
2024-11-15 19:13:05 +01:00
|
|
|
# for I/O
|
2024-11-15 17:22:07 +01:00
|
|
|
if not os.path.exists("{}/test".format(work_path)):
|
|
|
|
os.makedirs("{}/test".format(work_path))
|
2024-11-15 19:13:05 +01:00
|
|
|
if not os.path.exists("{}/input".format(work_path)):
|
|
|
|
os.makedirs("{}/input".format(work_path))
|
2024-11-15 17:22:07 +01:00
|
|
|
|
2024-11-15 19:52:24 +01:00
|
|
|
is_ci = os.getenv('CI')
|
2024-11-15 17:22:07 +01:00
|
|
|
test_failed = False
|
2024-08-29 20:48:16 +02:00
|
|
|
for mol in molecules:
|
|
|
|
|
|
|
|
mol_name = mol.name
|
|
|
|
mol_mult = mol.multiplicity
|
|
|
|
mol_geom = mol.geometry
|
|
|
|
mol_data = mol.properties
|
|
|
|
|
|
|
|
print_col(" Molecule: {} (2S+1 = {})".format(mol_name, mol_mult), "blue")
|
|
|
|
|
|
|
|
for mol_prop_name, mol_prop_data in mol_data.items():
|
|
|
|
|
|
|
|
methd = mol_prop_name[len('properties_'):]
|
|
|
|
|
|
|
|
if(len(mol_prop_data) == 0):
|
|
|
|
continue
|
|
|
|
|
|
|
|
for basis_name, basis_data in mol_prop_data.items():
|
|
|
|
|
|
|
|
if(len(basis_data) == 0):
|
|
|
|
continue
|
|
|
|
|
|
|
|
work_methd = Path('{}/{}'.format(work_path, methd))
|
|
|
|
if not work_methd.exists():
|
|
|
|
work_methd.mkdir(parents=True, exist_ok=True)
|
2024-11-15 17:22:07 +01:00
|
|
|
|
|
|
|
New_Quack_Job = Quack_Job(mol_name, mol_mult, basis_name, mol_geom, methd, work_path)
|
2024-08-29 20:48:16 +02:00
|
|
|
New_Quack_Job.prep_inp()
|
2024-11-15 19:52:24 +01:00
|
|
|
if is_ci:
|
|
|
|
New_Quack_Job.run_ci()
|
|
|
|
else:
|
|
|
|
New_Quack_Job.run()
|
2024-11-15 17:22:07 +01:00
|
|
|
|
|
|
|
test_failed_ = False
|
|
|
|
New_Quack_Job.check_data(basis_data, test_failed_)
|
|
|
|
if (test_failed_):
|
|
|
|
test_failed = True
|
2024-08-29 20:48:16 +02:00
|
|
|
|
|
|
|
print()
|
|
|
|
print()
|
|
|
|
print()
|
|
|
|
|
2024-11-15 17:22:07 +01:00
|
|
|
if test_failed:
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
2024-08-29 20:48:16 +02:00
|
|
|
|
|
|
|
db_name = '{}.db'.format(bench)
|
|
|
|
|
|
|
|
molecules = get_molecules_from_db(db_name)
|
|
|
|
|
|
|
|
main()
|
|
|
|
|