Merge pull request #2 from HideyoshiNakazone/diceplayer-refactoring
Diceplayer Refactoring
This commit is contained in:
30
.github/workflows/python-tests.yml
vendored
Normal file
30
.github/workflows/python-tests.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||||
|
|
||||||
|
name: Python application
|
||||||
|
|
||||||
|
on:
|
||||||
|
push
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
run-unitest:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.8
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: "3.8"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip poetry;
|
||||||
|
[ -f pyproject.toml ] && poetry install;
|
||||||
|
- name: Test with unittest
|
||||||
|
run: |
|
||||||
|
poetry run python -m unittest -v
|
||||||
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
.vscode/settings.json
|
||||||
|
|
||||||
|
*.ljc
|
||||||
|
*.log
|
||||||
|
*.log.backup
|
||||||
|
|
||||||
|
simfiles/*
|
||||||
|
|
||||||
|
.vscode/*
|
||||||
|
.idea/*
|
||||||
|
*.pkl
|
||||||
|
|
||||||
|
*.xyz
|
||||||
|
|
||||||
|
dist/
|
||||||
17
Pipfile
17
Pipfile
@@ -1,17 +0,0 @@
|
|||||||
[[source]]
|
|
||||||
url = "https://pypi.org/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
name = "pypi"
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
numpy = "*"
|
|
||||||
pickle5 = "*"
|
|
||||||
argparse = "*"
|
|
||||||
pyinstaller = "*"
|
|
||||||
setproctitle = "*"
|
|
||||||
pyyaml = "*"
|
|
||||||
|
|
||||||
[dev-packages]
|
|
||||||
|
|
||||||
[requires]
|
|
||||||
python_version = "3.10"
|
|
||||||
24
control.example.yml
Normal file
24
control.example.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
diceplayer:
|
||||||
|
opt: no
|
||||||
|
mem: 24
|
||||||
|
maxcyc: 5
|
||||||
|
ncores: 5
|
||||||
|
nprocs: 4
|
||||||
|
qmprog: 'g16'
|
||||||
|
lps: no
|
||||||
|
ghosts: no
|
||||||
|
altsteps: 2000
|
||||||
|
|
||||||
|
dice:
|
||||||
|
nmol: [1, 100]
|
||||||
|
dens: 1.5
|
||||||
|
nstep: [2000, 3000]
|
||||||
|
isave: 1000
|
||||||
|
outname: 'phb'
|
||||||
|
progname: '~/.local/bin/dice'
|
||||||
|
ljname: 'phb.ljc'
|
||||||
|
randominit: 'always'
|
||||||
|
|
||||||
|
gaussian:
|
||||||
|
qmprog: 'g16'
|
||||||
|
level: 'MP2/aug-cc-pVDZ'
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
diceplayer:
|
|
||||||
maxcyc: 3
|
|
||||||
opt: no
|
|
||||||
nprocs: 4
|
|
||||||
qmprog: 'g16'
|
|
||||||
lps: no
|
|
||||||
ghosts: no
|
|
||||||
altsteps: 20000
|
|
||||||
|
|
||||||
dice:
|
|
||||||
ncores: 3
|
|
||||||
nmol: [1, 50]
|
|
||||||
dens: 0.75
|
|
||||||
nstep: [2000, 3000]
|
|
||||||
isave: 1000
|
|
||||||
ljname: 'phb.ljc'
|
|
||||||
outname: 'phb'
|
|
||||||
randominit: 'always'
|
|
||||||
|
|
||||||
gaussian:
|
|
||||||
qmprog: 'g16'
|
|
||||||
level: 'MP2/aug-cc-pVDZ'
|
|
||||||
keywords: 'freq'
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
from diceplayer.DPpack.Utils.PTable import *
|
|
||||||
from diceplayer.DPpack.Utils.Misc import *
|
|
||||||
|
|
||||||
from diceplayer.DPpack.Environment.Molecule import ANG2BOHR, BOHR2ANG, Molecule
|
|
||||||
from diceplayer.DPpack.Environment.Atom import Atom
|
|
||||||
|
|
||||||
from typing import IO, Final, Tuple, List, TextIO
|
|
||||||
|
|
||||||
from numpy import linalg
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from copy import deepcopy
|
|
||||||
import sys, math
|
|
||||||
import sys
|
|
||||||
import math
|
|
||||||
|
|
||||||
BOHR2ANG: Final[float] = 0.52917721092
|
|
||||||
ANG2BOHR: Final[float] = 1 / BOHR2ANG
|
|
||||||
|
|
||||||
|
|
||||||
class System:
|
|
||||||
"""
|
|
||||||
System class declaration. This class is used throughout the DicePlayer program to represent the system containing the molecules.
|
|
||||||
|
|
||||||
Atributes:
|
|
||||||
molecule (List[Molecule]): List of molecules of the system
|
|
||||||
nmols (List[int]): List of number of molecules in the system
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
"""
|
|
||||||
Initializes a empty system object that will be populated afterwards
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.molecule: List[Molecule] = []
|
|
||||||
self.nmols: List[int] = []
|
|
||||||
|
|
||||||
def add_type(self, nmols: int, m: Molecule) -> None:
|
|
||||||
"""
|
|
||||||
Adds a new molecule type to the system
|
|
||||||
|
|
||||||
Args:
|
|
||||||
nmols (int): Number of molecules of the new type in the system
|
|
||||||
m (Molecule): The instance of the new type of molecule
|
|
||||||
"""
|
|
||||||
self.molecule.append(m)
|
|
||||||
self.nmols.append(nmols)
|
|
||||||
|
|
||||||
def center_of_mass_distance(self, a: Molecule, b: Molecule) -> float:
|
|
||||||
"""
|
|
||||||
Calculates the distance between the center of mass of two molecules
|
|
||||||
|
|
||||||
Args:
|
|
||||||
a (Molecule): First Molecule Instance
|
|
||||||
b (Molecule): Second Molecule Instance
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: module of the distance between the two center of masses
|
|
||||||
"""
|
|
||||||
|
|
||||||
com1 = self.molecule[a].center_of_mass()
|
|
||||||
com2 = self.molecule[b].center_of_mass()
|
|
||||||
dx = com1[0] - com2[0]
|
|
||||||
dy = com1[1] - com2[1]
|
|
||||||
dz = com1[2] - com2[2]
|
|
||||||
distance = math.sqrt(dx**2 + dy**2 + dz**2)
|
|
||||||
|
|
||||||
return distance
|
|
||||||
|
|
||||||
def rmsd_fit(self, p_index: int, r_index: int) -> Tuple[float, Molecule]:
|
|
||||||
|
|
||||||
projecting_mol = self.molecule[p_index]
|
|
||||||
reference_mol = self.molecule[r_index]
|
|
||||||
|
|
||||||
if len(projecting_mol.atom) != len(reference_mol.atom):
|
|
||||||
sys.exit(
|
|
||||||
"Error in RMSD fit procedure: molecules have different number of atoms"
|
|
||||||
)
|
|
||||||
dim = len(projecting_mol.atom)
|
|
||||||
|
|
||||||
new_projecting_mol = deepcopy(projecting_mol)
|
|
||||||
new_reference_mol = deepcopy(reference_mol)
|
|
||||||
|
|
||||||
new_projecting_mol.center_of_mass_to_origin()
|
|
||||||
new_reference_mol.center_of_mass_to_origin()
|
|
||||||
|
|
||||||
x = []
|
|
||||||
y = []
|
|
||||||
|
|
||||||
for atom in new_projecting_mol.atom:
|
|
||||||
x.extend([atom.rx, atom.ry, atom.rz])
|
|
||||||
|
|
||||||
for atom in new_reference_mol.atom:
|
|
||||||
y.extend([atom.rx, atom.ry, atom.rz])
|
|
||||||
|
|
||||||
x = np.array(x).reshape(dim, 3)
|
|
||||||
y = np.array(y).reshape(dim, 3)
|
|
||||||
|
|
||||||
r = np.matmul(y.T, x)
|
|
||||||
rr = np.matmul(r.T, r)
|
|
||||||
|
|
||||||
try:
|
|
||||||
evals, evecs = linalg.eigh(rr)
|
|
||||||
except:
|
|
||||||
sys.exit("Error: diagonalization of RR matrix did not converge")
|
|
||||||
|
|
||||||
a1 = evecs[:, 2].T
|
|
||||||
a2 = evecs[:, 1].T
|
|
||||||
a3 = np.cross(a1, a2)
|
|
||||||
|
|
||||||
A = np.array([a1[0], a1[1], a1[2], a2[0], a2[1], a2[2], a3[0], a3[1], a3[2]])
|
|
||||||
A = A.reshape(3, 3)
|
|
||||||
|
|
||||||
b1 = np.matmul(r, a1.T).T # or np.dot(r, a1)
|
|
||||||
b1 /= linalg.norm(b1)
|
|
||||||
b2 = np.matmul(r, a2.T).T # or np.dot(r, a2)
|
|
||||||
b2 /= linalg.norm(b2)
|
|
||||||
b3 = np.cross(b1, b2)
|
|
||||||
|
|
||||||
B = np.array([b1[0], b1[1], b1[2], b2[0], b2[1], b2[2], b3[0], b3[1], b3[2]])
|
|
||||||
B = B.reshape(3, 3).T
|
|
||||||
|
|
||||||
rot_matrix = np.matmul(B, A)
|
|
||||||
x = np.matmul(rot_matrix, x.T).T
|
|
||||||
|
|
||||||
rmsd = 0
|
|
||||||
for i in range(dim):
|
|
||||||
rmsd += (
|
|
||||||
(x[i, 0] - y[i, 0]) ** 2
|
|
||||||
+ (x[i, 1] - y[i, 1]) ** 2
|
|
||||||
+ (x[i, 2] - y[i, 2]) ** 2
|
|
||||||
)
|
|
||||||
rmsd = math.sqrt(rmsd / dim)
|
|
||||||
|
|
||||||
for i in range(dim):
|
|
||||||
new_projecting_mol.atom[i].rx = x[i, 0]
|
|
||||||
new_projecting_mol.atom[i].ry = x[i, 1]
|
|
||||||
new_projecting_mol.atom[i].rz = x[i, 2]
|
|
||||||
|
|
||||||
reference_mol.center_of_mass()
|
|
||||||
|
|
||||||
projected_mol = new_projecting_mol.translate(reference_mol.com)
|
|
||||||
|
|
||||||
return rmsd, projected_mol
|
|
||||||
|
|
||||||
def update_molecule(self, position: np.ndarray, fh: TextIO) -> None:
|
|
||||||
"""Updates the position of the molecule in the Output file
|
|
||||||
|
|
||||||
Args:
|
|
||||||
position (np.ndarray): numpy position vector
|
|
||||||
fh (TextIO): Output file
|
|
||||||
"""
|
|
||||||
|
|
||||||
position_in_ang = (position * BOHR2ANG).tolist()
|
|
||||||
self.add_type(self.nmols[0], deepcopy(self.molecule[0]))
|
|
||||||
|
|
||||||
for atom in self.molecule[-1].atom:
|
|
||||||
|
|
||||||
atom.rx = position_in_ang.pop(0)
|
|
||||||
atom.ry = position_in_ang.pop(0)
|
|
||||||
atom.rz = position_in_ang.pop(0)
|
|
||||||
|
|
||||||
rmsd, self.molecule[0] = self.rmsd_fit(-1, 0)
|
|
||||||
self.molecule.pop(-1)
|
|
||||||
|
|
||||||
fh.write("\nProjected new conformation of reference molecule with RMSD fit\n")
|
|
||||||
fh.write("RMSD = {:>8.5f} Angstrom\n".format(rmsd))
|
|
||||||
|
|
||||||
def nearest_image(
|
|
||||||
self,
|
|
||||||
index_r: int,
|
|
||||||
index_m: int,
|
|
||||||
lx: float,
|
|
||||||
ly: float,
|
|
||||||
lz: float,
|
|
||||||
criterium=None,
|
|
||||||
) -> Tuple[float, Molecule]:
|
|
||||||
|
|
||||||
if criterium in None:
|
|
||||||
criterium = "com"
|
|
||||||
|
|
||||||
if criterium != "com" and criterium != "min":
|
|
||||||
sys.exit("Error in value passed to function nearest_image")
|
|
||||||
|
|
||||||
min_dist = 1e20
|
|
||||||
|
|
||||||
for i in range(-1, 2):
|
|
||||||
for j in range(-1, 2):
|
|
||||||
for k in range(-1, 2):
|
|
||||||
|
|
||||||
tr_vector = [i * lx, j * ly, k * lz]
|
|
||||||
self.add_molecule(self.molecule[index_m].translate(tr_vector))
|
|
||||||
|
|
||||||
if criterium == "com":
|
|
||||||
dist = self.center_of_mass_distance(index_r, -1)
|
|
||||||
else:
|
|
||||||
dist = self.minimum_distance(index_r, -1)
|
|
||||||
|
|
||||||
if dist < min_dist:
|
|
||||||
min_dist = dist
|
|
||||||
nearestmol = deepcopy(self.molecule[-1])
|
|
||||||
|
|
||||||
self.molecule.pop(-1)
|
|
||||||
|
|
||||||
return min_dist, nearestmol
|
|
||||||
|
|
||||||
def print_geom(self, cycle: int, fh: TextIO) -> None:
|
|
||||||
"""
|
|
||||||
Print the geometry of the molecule in the Output file
|
|
||||||
|
|
||||||
Args:
|
|
||||||
cycle (int): Number of the cycle
|
|
||||||
fh (TextIO): Output file
|
|
||||||
"""
|
|
||||||
|
|
||||||
fh.write("Cycle # {}\n".format(cycle))
|
|
||||||
fh.write("Number of site: {}\n".format(len(self.molecule[0].atom)))
|
|
||||||
for atom in self.molecule[0].atom:
|
|
||||||
symbol = atomsymb[atom.na]
|
|
||||||
fh.write(
|
|
||||||
"{:<2s} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(
|
|
||||||
symbol, atom.rx, atom.ry, atom.rz
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def printChargesAndDipole(self, cycle: int, fh: TextIO) -> None:
|
|
||||||
"""
|
|
||||||
Print the charges and dipole of the molecule in the Output file
|
|
||||||
|
|
||||||
Args:
|
|
||||||
cycle (int): Number of the cycle
|
|
||||||
fh (TextIO): Output file
|
|
||||||
"""
|
|
||||||
|
|
||||||
fh.write("Cycle # {}\n".format(cycle))
|
|
||||||
fh.write("Number of site: {}\n".format(len(self.molecule[0].atom)))
|
|
||||||
|
|
||||||
chargesAndDipole = self.molecule[0].charges_and_dipole()
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
"{:>10.6f} {:>10.6f} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(
|
|
||||||
chargesAndDipole[0], chargesAndDipole[1], chargesAndDipole[2], chargesAndDipole[3], chargesAndDipole[4]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
753
diceplayer/DPpack/External/Dice.py
vendored
753
diceplayer/DPpack/External/Dice.py
vendored
@@ -1,753 +0,0 @@
|
|||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from multiprocessing import Process, connection
|
|
||||||
from typing import Final, List, TextIO
|
|
||||||
|
|
||||||
import setproctitle
|
|
||||||
from diceplayer.DPpack.Utils.Misc import *
|
|
||||||
from diceplayer.DPpack.Utils.PTable import *
|
|
||||||
from diceplayer.DPpack.Utils.StepDTO import StepDTO
|
|
||||||
from diceplayer.DPpack.Utils.Validations import NotNull
|
|
||||||
from numpy import random
|
|
||||||
|
|
||||||
DICE_END_FLAG: Final[str] = "End of simulation"
|
|
||||||
DICE_FLAG_LINE: Final[int] = -2
|
|
||||||
UMAANG3_TO_GCM3: Final[float] = 1.6605
|
|
||||||
|
|
||||||
MAX_SEED: Final[int] = 4294967295
|
|
||||||
|
|
||||||
|
|
||||||
class Dice:
|
|
||||||
|
|
||||||
title = "Diceplayer run"
|
|
||||||
progname = "dice"
|
|
||||||
|
|
||||||
nprocs: int = None
|
|
||||||
randominit = "first"
|
|
||||||
combrule = "*"
|
|
||||||
|
|
||||||
temp = 300.0
|
|
||||||
press = 1.0
|
|
||||||
isave = 1000
|
|
||||||
dens = None
|
|
||||||
ljname = None
|
|
||||||
outname = None
|
|
||||||
nmol: List[int] = None
|
|
||||||
nstep: List[int] = None
|
|
||||||
upbuf = 360
|
|
||||||
|
|
||||||
def __init__(self, infile: TextIO, outfile: TextIO) -> None:
|
|
||||||
|
|
||||||
self.infile = infile
|
|
||||||
self.outfile = outfile
|
|
||||||
|
|
||||||
@NotNull(requiredArgs=["ncores", "nmol", "dens", "nstep", "ljname", "outname"])
|
|
||||||
def updateKeywords(self, **data):
|
|
||||||
self.__dict__.update(**data)
|
|
||||||
|
|
||||||
def __new_density(self, cycle: int, proc: int) -> float:
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
step_dir = "step{:02d}".format(cycle - 1)
|
|
||||||
proc_dir = "p{:02d}".format(proc)
|
|
||||||
path = sim_dir + os.sep + step_dir + os.sep + proc_dir
|
|
||||||
file = path + os.sep + "last.xyz"
|
|
||||||
|
|
||||||
if not os.path.isfile(file):
|
|
||||||
sys.exit(
|
|
||||||
"Error: cannot find the xyz file {} in main directory".format(file)
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
with open(file) as fh:
|
|
||||||
xyzfile = fh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
box = xyzfile[1].split()
|
|
||||||
volume = float(box[-3]) * float(box[-2]) * float(box[-1])
|
|
||||||
|
|
||||||
total_mass = 0
|
|
||||||
for i in range(len(self.step.molecule)):
|
|
||||||
|
|
||||||
total_mass += self.step.molecule[i].total_mass * self.step.nmol[i]
|
|
||||||
|
|
||||||
density = (total_mass / volume) * UMAANG3_TO_GCM3
|
|
||||||
|
|
||||||
return density
|
|
||||||
|
|
||||||
def __print_last_config(self, cycle: int, proc: int) -> None:
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
step_dir = "step{:02d}".format(cycle)
|
|
||||||
proc_dir = "p{:02d}".format(proc)
|
|
||||||
path = sim_dir + os.sep + step_dir + os.sep + proc_dir
|
|
||||||
file = path + os.sep + "phb.xyz"
|
|
||||||
if not os.path.isfile(file):
|
|
||||||
sys.exit("Error: cannot find the xyz file {}".format(file))
|
|
||||||
try:
|
|
||||||
with open(file) as fh:
|
|
||||||
xyzfile = fh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
nsites = len(self.step.molecule[0].atom) * self.step.nmol[0]
|
|
||||||
for i in range(1, len(self.step.nmol)):
|
|
||||||
nsites += self.step.nmol[i] * len(self.step.molecule[i].atom)
|
|
||||||
|
|
||||||
nsites += 2
|
|
||||||
|
|
||||||
nsites *= -1
|
|
||||||
xyzfile = xyzfile[nsites:]
|
|
||||||
|
|
||||||
file = path + os.sep + "last.xyz"
|
|
||||||
fh = open(file, "w")
|
|
||||||
for line in xyzfile:
|
|
||||||
fh.write(line)
|
|
||||||
|
|
||||||
def __make_dice_inputs(self, cycle: int, proc: int) -> None:
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
step_dir = "step{:02d}".format(cycle)
|
|
||||||
proc_dir = "p{:02d}".format(proc)
|
|
||||||
path = sim_dir + os.sep + step_dir + os.sep + proc_dir
|
|
||||||
|
|
||||||
num = time.time()
|
|
||||||
num = (num - int(num)) * 1e6
|
|
||||||
|
|
||||||
num = int((num - int(num)) * 1e6)
|
|
||||||
random.seed((os.getpid() * num) % (MAX_SEED + 1))
|
|
||||||
|
|
||||||
if self.randominit == "first" and cycle > self.step.initcyc:
|
|
||||||
last_step_dir = "step{:02d}".format(cycle - 1)
|
|
||||||
last_path = sim_dir + os.sep + last_step_dir + os.sep + proc_dir
|
|
||||||
xyzfile = last_path + os.sep + "last.xyz"
|
|
||||||
self.__make_init_file(path, xyzfile)
|
|
||||||
|
|
||||||
if len(self.nstep) == 2:
|
|
||||||
|
|
||||||
self.__make_nvt_ter(cycle, path)
|
|
||||||
self.__make_nvt_eq(path)
|
|
||||||
|
|
||||||
elif len(self.nstep) == 3:
|
|
||||||
|
|
||||||
if self.randominit == "first" and cycle > self.step.initcyc:
|
|
||||||
self.dens = self.__new_density(cycle, proc)
|
|
||||||
else:
|
|
||||||
self.__make_nvt_ter(cycle, path)
|
|
||||||
|
|
||||||
self.__make_npt_ter(cycle, path)
|
|
||||||
self.__make_npt_eq(path)
|
|
||||||
|
|
||||||
else:
|
|
||||||
sys.exit("Error: bad number of entries for 'nstep'")
|
|
||||||
|
|
||||||
self.__make_potential(path)
|
|
||||||
|
|
||||||
def __make_nvt_ter(self, cycle: int, path: str) -> None:
|
|
||||||
|
|
||||||
file = path + os.sep + "NVT.ter"
|
|
||||||
try:
|
|
||||||
fh = open(file, "w")
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
fh.write("title = {} - NVT Thermalization\n".format(self.title))
|
|
||||||
fh.write("ncores = {}\n".format(self.ncores))
|
|
||||||
fh.write("ljname = {}\n".format(self.ljname))
|
|
||||||
fh.write("outname = {}\n".format(self.outname))
|
|
||||||
|
|
||||||
string = " ".join(str(x) for x in self.nmol)
|
|
||||||
fh.write("nmol = {}\n".format(string))
|
|
||||||
|
|
||||||
fh.write("dens = {}\n".format(self.dens))
|
|
||||||
fh.write("temp = {}\n".format(self.temp))
|
|
||||||
|
|
||||||
if self.randominit == "first" and cycle > self.step.initcyc:
|
|
||||||
fh.write("init = yesreadxyz\n")
|
|
||||||
fh.write("nstep = {}\n".format(self.step.altsteps))
|
|
||||||
else:
|
|
||||||
fh.write("init = yes\n")
|
|
||||||
fh.write("nstep = {}\n".format(self.nstep[0]))
|
|
||||||
|
|
||||||
fh.write("vstep = 0\n")
|
|
||||||
fh.write("mstop = 1\n")
|
|
||||||
fh.write("accum = no\n")
|
|
||||||
fh.write("iprint = 1\n")
|
|
||||||
fh.write("isave = 0\n")
|
|
||||||
fh.write("irdf = 0\n")
|
|
||||||
|
|
||||||
seed = int(1e6 * random.random())
|
|
||||||
fh.write("seed = {}\n".format(seed))
|
|
||||||
fh.write("upbuf = {}".format(self.upbuf))
|
|
||||||
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
def __make_nvt_eq(self, path: str) -> None:
|
|
||||||
|
|
||||||
file = path + os.sep + "NVT.eq"
|
|
||||||
try:
|
|
||||||
fh = open(file, "w")
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
fh.write("title = {} - NVT Production\n".format(self.title))
|
|
||||||
fh.write("ncores = {}\n".format(self.ncores))
|
|
||||||
fh.write("ljname = {}\n".format(self.ljname))
|
|
||||||
fh.write("outname = {}\n".format(self.outname))
|
|
||||||
|
|
||||||
string = " ".join(str(x) for x in self.nmol)
|
|
||||||
fh.write("nmol = {}\n".format(string))
|
|
||||||
|
|
||||||
fh.write("dens = {}\n".format(self.dens))
|
|
||||||
fh.write("temp = {}\n".format(self.temp))
|
|
||||||
fh.write("init = no\n")
|
|
||||||
fh.write("nstep = {}\n".format(self.nstep[1]))
|
|
||||||
fh.write("vstep = 0\n")
|
|
||||||
fh.write("mstop = 1\n")
|
|
||||||
fh.write("accum = no\n")
|
|
||||||
fh.write("iprint = 1\n")
|
|
||||||
fh.write("isave = {}\n".format(self.isave))
|
|
||||||
fh.write("irdf = {}\n".format(10 * self.step.nprocs))
|
|
||||||
|
|
||||||
seed = int(1e6 * random.random())
|
|
||||||
fh.write("seed = {}\n".format(seed))
|
|
||||||
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
def __make_npt_ter(self, cycle: int, path: str) -> None:
|
|
||||||
|
|
||||||
file = path + os.sep + "NPT.ter"
|
|
||||||
try:
|
|
||||||
fh = open(file, "w")
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
fh.write("title = {} - NPT Thermalization\n".format(self.title))
|
|
||||||
fh.write("ncores = {}\n".format(self.ncores))
|
|
||||||
fh.write("ljname = {}\n".format(self.ljname))
|
|
||||||
fh.write("outname = {}\n".format(self.outname))
|
|
||||||
|
|
||||||
string = " ".join(str(x) for x in self.nmol)
|
|
||||||
fh.write("nmol = {}\n".format(string))
|
|
||||||
|
|
||||||
fh.write("press = {}\n".format(self.press))
|
|
||||||
fh.write("temp = {}\n".format(self.temp))
|
|
||||||
|
|
||||||
if self.randominit == "first" and cycle > self.step.initcyc:
|
|
||||||
fh.write("init = yesreadxyz\n")
|
|
||||||
fh.write("dens = {:<8.4f}\n".format(self.dens))
|
|
||||||
fh.write("vstep = {}\n".format(int(self.step.altsteps / 5)))
|
|
||||||
else:
|
|
||||||
fh.write("init = no\n")
|
|
||||||
fh.write("vstep = {}\n".format(int(self.nstep[1] / 5)))
|
|
||||||
|
|
||||||
fh.write("nstep = 5\n")
|
|
||||||
fh.write("mstop = 1\n")
|
|
||||||
fh.write("accum = no\n")
|
|
||||||
fh.write("iprint = 1\n")
|
|
||||||
fh.write("isave = 0\n")
|
|
||||||
fh.write("irdf = 0\n")
|
|
||||||
|
|
||||||
seed = int(1e6 * random.random())
|
|
||||||
fh.write("seed = {}\n".format(seed))
|
|
||||||
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
def __make_npt_eq(self, path: str) -> None:
|
|
||||||
|
|
||||||
file = path + os.sep + "NPT.eq"
|
|
||||||
try:
|
|
||||||
fh = open(file, "w")
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
fh.write("title = {} - NPT Production\n".format(self.title))
|
|
||||||
fh.write("ncores = {}\n".format(self.ncores))
|
|
||||||
fh.write("ljname = {}\n".format(self.ljname))
|
|
||||||
fh.write("outname = {}\n".format(self.outname))
|
|
||||||
|
|
||||||
string = " ".join(str(x) for x in self.nmol)
|
|
||||||
fh.write("nmol = {}\n".format(string))
|
|
||||||
|
|
||||||
fh.write("press = {}\n".format(self.press))
|
|
||||||
fh.write("temp = {}\n".format(self.temp))
|
|
||||||
|
|
||||||
fh.write("nstep = 5\n")
|
|
||||||
|
|
||||||
fh.write("vstep = {}\n".format(int(self.nstep[2] / 5)))
|
|
||||||
fh.write("init = no\n")
|
|
||||||
fh.write("mstop = 1\n")
|
|
||||||
fh.write("accum = no\n")
|
|
||||||
fh.write("iprint = 1\n")
|
|
||||||
fh.write("isave = {}\n".format(self.isave))
|
|
||||||
fh.write("irdf = {}\n".format(10 * self.step.nprocs))
|
|
||||||
|
|
||||||
seed = int(1e6 * random.random())
|
|
||||||
fh.write("seed = {}\n".format(seed))
|
|
||||||
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
def __make_init_file(self, path: str, file: TextIO) -> None:
|
|
||||||
|
|
||||||
if not os.path.isfile(file):
|
|
||||||
sys.exit(
|
|
||||||
"Error: cannot find the xyz file {} in main directory".format(file)
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
with open(file) as fh:
|
|
||||||
xyzfile = fh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
nsites_mm = 0
|
|
||||||
for i in range(1, len(self.step.nmol)):
|
|
||||||
nsites_mm += self.step.nmol[i] * len(self.step.molecule[i].atom)
|
|
||||||
|
|
||||||
nsites_mm *= -1
|
|
||||||
|
|
||||||
xyzfile = xyzfile[nsites_mm:]
|
|
||||||
|
|
||||||
file = path + os.sep + self.outname + ".xy"
|
|
||||||
|
|
||||||
try:
|
|
||||||
fh = open(file, "w", 1)
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
for atom in self.step.molecule[0].atom:
|
|
||||||
fh.write(
|
|
||||||
"{:>10.6f} {:>10.6f} {:>10.6f}\n".format(atom.rx, atom.ry, atom.rz)
|
|
||||||
)
|
|
||||||
|
|
||||||
for line in xyzfile:
|
|
||||||
atom = line.split()
|
|
||||||
rx = float(atom[1])
|
|
||||||
ry = float(atom[2])
|
|
||||||
rz = float(atom[3])
|
|
||||||
fh.write("{:>10.6f} {:>10.6f} {:>10.6f}\n".format(rx, ry, rz))
|
|
||||||
|
|
||||||
fh.write("$end")
|
|
||||||
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
def __make_potential(self, path: str) -> None:
|
|
||||||
|
|
||||||
fstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f}\n"
|
|
||||||
|
|
||||||
file = path + os.sep + self.ljname
|
|
||||||
try:
|
|
||||||
fh = open(file, "w")
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
fh.write("{}\n".format(self.combrule))
|
|
||||||
fh.write("{}\n".format(len(self.step.nmol)))
|
|
||||||
|
|
||||||
nsites_qm = (
|
|
||||||
len(self.step.molecule[0].atom)
|
|
||||||
+ len(self.step.molecule[0].ghost_atoms)
|
|
||||||
+ len(self.step.molecule[0].lp_atoms)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write("{} {}\n".format(nsites_qm, self.step.molecule[0].molname))
|
|
||||||
for atom in self.step.molecule[0].atom:
|
|
||||||
fh.write(
|
|
||||||
fstr.format(
|
|
||||||
atom.lbl,
|
|
||||||
atom.na,
|
|
||||||
atom.rx,
|
|
||||||
atom.ry,
|
|
||||||
atom.rz,
|
|
||||||
atom.chg,
|
|
||||||
atom.eps,
|
|
||||||
atom.sig,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
ghost_label = self.step.molecule[0].atom[-1].lbl + 1
|
|
||||||
for i in self.step.molecule[0].ghost_atoms:
|
|
||||||
fh.write(
|
|
||||||
fstr.format(
|
|
||||||
ghost_label,
|
|
||||||
ghost_number,
|
|
||||||
self.step.molecule[0].atom[i].rx,
|
|
||||||
self.step.molecule[0].atom[i].ry,
|
|
||||||
self.step.molecule[0].atom[i].rz,
|
|
||||||
self.step.molecule[0].atom[i].chg,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
ghost_label += 1
|
|
||||||
for lp in self.step.molecule[0].lp_atoms:
|
|
||||||
fh.write(
|
|
||||||
fstr.format(
|
|
||||||
ghost_label,
|
|
||||||
ghost_number,
|
|
||||||
lp["rx"],
|
|
||||||
lp["ry"],
|
|
||||||
lp["rz"],
|
|
||||||
lp["chg"],
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
for mol in self.step.molecule[1:]:
|
|
||||||
fh.write("{} {}\n".format(len(mol.atom), mol.molname))
|
|
||||||
for atom in mol.atom:
|
|
||||||
fh.write(
|
|
||||||
fstr.format(
|
|
||||||
atom.lbl,
|
|
||||||
atom.na,
|
|
||||||
atom.rx,
|
|
||||||
atom.ry,
|
|
||||||
atom.rz,
|
|
||||||
atom.chg,
|
|
||||||
atom.eps,
|
|
||||||
atom.sig,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __make_proc_dir(self, cycle: int, proc: int) -> None:
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
step_dir = "step{:02d}".format(cycle)
|
|
||||||
proc_dir = "p{:02d}".format(proc)
|
|
||||||
path = sim_dir + os.sep + step_dir + os.sep + proc_dir
|
|
||||||
try:
|
|
||||||
os.makedirs(path)
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot make directory {}".format(path))
|
|
||||||
|
|
||||||
def __run_dice(self, cycle: int, proc: int, fh: str) -> None:
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
step_dir = "step{:02d}".format(cycle)
|
|
||||||
proc_dir = "p{:02d}".format(proc)
|
|
||||||
|
|
||||||
try:
|
|
||||||
fh.write(
|
|
||||||
"Simulation process {} initiated with pid {}\n".format(
|
|
||||||
sim_dir + os.sep + step_dir + os.sep + proc_dir, os.getpid()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
print("I/O error({0}): {1}".format(err))
|
|
||||||
|
|
||||||
path = sim_dir + os.sep + step_dir + os.sep + proc_dir
|
|
||||||
working_dir = os.getcwd()
|
|
||||||
os.chdir(path)
|
|
||||||
|
|
||||||
if len(self.nstep) == 2:
|
|
||||||
|
|
||||||
if self.randominit == "first" and cycle > self.step.initcyc:
|
|
||||||
string_tmp = "previous"
|
|
||||||
else:
|
|
||||||
string_tmp = "random"
|
|
||||||
|
|
||||||
string = "(from " + string_tmp + " configuration)"
|
|
||||||
fh.write(
|
|
||||||
"p{:02d}> NVT thermalization finished {} on {}\n".format(
|
|
||||||
proc, string, date_time()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
infh = open("NVT.ter")
|
|
||||||
outfh = open("NVT.ter.out", "w")
|
|
||||||
|
|
||||||
if shutil.which("bash") != None:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
[
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"exec -a dice-step{}-p{} {} < {} > {}".format(
|
|
||||||
cycle, proc, self.progname, infh.name, outfh.name
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
self.progname, stin=infh.name, stout=outfh.name
|
|
||||||
)
|
|
||||||
|
|
||||||
infh.close()
|
|
||||||
outfh.close()
|
|
||||||
|
|
||||||
if os.getppid() == 1:
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
if exit_status != 0:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
outfh = open("NVT.ter.out")
|
|
||||||
flag = outfh.readlines()[DICE_FLAG_LINE].strip()
|
|
||||||
outfh.close()
|
|
||||||
if flag != DICE_END_FLAG:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
"p{:02d}> NVT production initiated on {}\n".format(proc, date_time())
|
|
||||||
)
|
|
||||||
|
|
||||||
infh = open("NVT.eq")
|
|
||||||
outfh = open("NVT.eq.out", "w")
|
|
||||||
|
|
||||||
if shutil.which("bash") != None:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
[
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"exec -a dice-step{}-p{} {} < {} > {}".format(
|
|
||||||
cycle, proc, self.progname, infh.name, outfh.name
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
self.progname, stin=infh.name, stout=outfh.name
|
|
||||||
)
|
|
||||||
|
|
||||||
infh.close()
|
|
||||||
outfh.close()
|
|
||||||
|
|
||||||
if os.getppid() == 1:
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
if exit_status != 0:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
outfh = open("NVT.eq.out")
|
|
||||||
flag = outfh.readlines()[DICE_FLAG_LINE].strip()
|
|
||||||
outfh.close()
|
|
||||||
if flag != DICE_END_FLAG:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
"p{:02d}> ----- NVT production finished on {}\n".format(
|
|
||||||
proc, date_time()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif len(self.nstep) == 3:
|
|
||||||
if (
|
|
||||||
self.randominit == "always"
|
|
||||||
or (self.randominit == "first" and cycle == 1)
|
|
||||||
or self.continued
|
|
||||||
):
|
|
||||||
string = "(from random configuration)"
|
|
||||||
fh.write(
|
|
||||||
"p{:02d}> NVT thermalization initiated {} on {}\n".format(
|
|
||||||
proc, string, date_time()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
infh = open("NVT.ter")
|
|
||||||
outfh = open("NVT.ter.out", "w")
|
|
||||||
|
|
||||||
if shutil.which("bash") != None:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
[
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"exec -a dice-step{}-p{} {} < {} > {}".format(
|
|
||||||
cycle, proc, self.progname, infh.name, outfh.name
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
self.progname, stin=infh.name, stout=outfh.name
|
|
||||||
)
|
|
||||||
|
|
||||||
infh.close()
|
|
||||||
outfh.close()
|
|
||||||
|
|
||||||
if os.getppid() == 1:
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
if exit_status != 0:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
outfh = open("NVT.ter.out")
|
|
||||||
flag = outfh.readlines()[DICE_FLAG_LINE].strip()
|
|
||||||
outfh.close()
|
|
||||||
if flag != DICE_END_FLAG:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not self.randominit == "always" or (
|
|
||||||
(self.randominit == "first" and cycle > self.step.initcyc)
|
|
||||||
):
|
|
||||||
string = " (from previous configuration) "
|
|
||||||
else:
|
|
||||||
string = " "
|
|
||||||
fh.write(
|
|
||||||
"p{:02d}> NPT thermalization finished {} on {}\n".format(
|
|
||||||
proc, string, date_time()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
infh = open("NPT.ter")
|
|
||||||
outfh = open("NPT.ter.out", "w")
|
|
||||||
|
|
||||||
if shutil.which("bash") != None:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
[
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"exec -a dice-step{}-p{} {} < {} > {}".format(
|
|
||||||
cycle, proc, self.progname, infh.name, outfh.name
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
self.progname, stin=infh.name, stout=outfh.name
|
|
||||||
)
|
|
||||||
|
|
||||||
infh.close()
|
|
||||||
outfh.close()
|
|
||||||
|
|
||||||
if os.getppid() == 1:
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
if exit_status != 0:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
outfh = open("NPT.ter.out")
|
|
||||||
flag = outfh.readlines()[DICE_FLAG_LINE].strip()
|
|
||||||
outfh.close()
|
|
||||||
if flag != DICE_END_FLAG:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
"p{:02d}> NPT production initiated on {}\n".format(proc, date_time())
|
|
||||||
)
|
|
||||||
|
|
||||||
infh = open("NPT.eq")
|
|
||||||
outfh = open("NPT.eq.out", "w")
|
|
||||||
|
|
||||||
if shutil.which("bash") != None:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
[
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"exec -a dice-step{}-p{} {} < {} > {}".format(
|
|
||||||
cycle, proc, self.progname, infh.name, outfh.name
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
self.progname, stin=infh.name, stout=outfh.name
|
|
||||||
)
|
|
||||||
|
|
||||||
infh.close()
|
|
||||||
outfh.close()
|
|
||||||
|
|
||||||
if os.getppid() == 1:
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
if exit_status != 0:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
outfh = open("NPT.eq.out")
|
|
||||||
flag = outfh.readlines()[DICE_FLAG_LINE].strip()
|
|
||||||
outfh.close()
|
|
||||||
if flag != DICE_END_FLAG:
|
|
||||||
sys.exit(
|
|
||||||
"Dice process step{:02d}-p{:02d} did not exit properly".format(
|
|
||||||
cycle, proc
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
"p{:02d}> ----- NPT production finished on {}\n".format(
|
|
||||||
proc, date_time()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
os.chdir(working_dir)
|
|
||||||
|
|
||||||
def __simulation_process(self, cycle: int, proc: int):
|
|
||||||
setproctitle.setproctitle("diceplayer-step{:0d}-p{:0d}".format(cycle, proc))
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.__make_proc_dir(cycle, proc)
|
|
||||||
self.__make_dice_inputs(cycle, proc)
|
|
||||||
self.__run_dice(cycle, proc, self.outfile)
|
|
||||||
except Exception as err:
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
def configure(self, step: StepDTO):
|
|
||||||
self.step = step
|
|
||||||
|
|
||||||
def start(self, cycle: int) -> None:
|
|
||||||
|
|
||||||
procs = []
|
|
||||||
sentinels = []
|
|
||||||
|
|
||||||
for proc in range(1, self.step.nprocs + 1):
|
|
||||||
|
|
||||||
p = Process(target=self.__simulation_process, args=(cycle, proc))
|
|
||||||
p.start()
|
|
||||||
|
|
||||||
procs.append(p)
|
|
||||||
sentinels.append(p.sentinel)
|
|
||||||
|
|
||||||
while procs:
|
|
||||||
finished = connection.wait(sentinels)
|
|
||||||
for proc_sentinel in finished:
|
|
||||||
i = sentinels.index(proc_sentinel)
|
|
||||||
status = procs[i].exitcode
|
|
||||||
procs.pop(i)
|
|
||||||
sentinels.pop(i)
|
|
||||||
if status != 0:
|
|
||||||
for p in procs:
|
|
||||||
p.terminate()
|
|
||||||
sys.exit(status)
|
|
||||||
|
|
||||||
for proc in range(1, self.step.nprocs + 1):
|
|
||||||
self.__print_last_config(cycle, proc)
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
del self.step
|
|
||||||
595
diceplayer/DPpack/External/Gaussian.py
vendored
595
diceplayer/DPpack/External/Gaussian.py
vendored
@@ -1,595 +0,0 @@
|
|||||||
from ast import keyword
|
|
||||||
from asyncore import read
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import textwrap
|
|
||||||
from typing import Dict, List, TextIO
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from diceplayer.DPpack.Environment.Atom import Atom
|
|
||||||
from diceplayer.DPpack.Environment.Molecule import Molecule
|
|
||||||
from diceplayer.DPpack.Utils.Misc import *
|
|
||||||
from diceplayer.DPpack.Utils.PTable import *
|
|
||||||
from diceplayer.DPpack.Utils.StepDTO import StepDTO
|
|
||||||
from diceplayer.DPpack.Utils.Validations import NotNull
|
|
||||||
|
|
||||||
|
|
||||||
class Gaussian:
|
|
||||||
|
|
||||||
mem = None
|
|
||||||
chgmult = [0, 1]
|
|
||||||
gmiddle = None # In each case, if a filename is given, its content will be
|
|
||||||
gbottom = None # inserted in the gaussian input
|
|
||||||
pop = "chelpg"
|
|
||||||
|
|
||||||
keywords = ""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@NotNull(requiredArgs=["qmprog","level"])
|
|
||||||
def updateKeywords(self, **data):
|
|
||||||
self.__dict__.update(**data)
|
|
||||||
self.checkKeywords()
|
|
||||||
|
|
||||||
def checkKeywords(self):
|
|
||||||
|
|
||||||
if self.pop not in ["chelpg", "mk", "nbo"]:
|
|
||||||
self.pop = "chelpg"
|
|
||||||
|
|
||||||
def run_formchk(self, cycle: int, fh: TextIO):
|
|
||||||
|
|
||||||
simdir = "simfiles"
|
|
||||||
stepdir = "step{:02d}".format(cycle)
|
|
||||||
path = simdir + os.sep + stepdir + os.sep + "qm"
|
|
||||||
|
|
||||||
work_dir = os.getcwd()
|
|
||||||
os.chdir(path)
|
|
||||||
|
|
||||||
fh.write("Formatting the checkpoint file... \n")
|
|
||||||
|
|
||||||
exit_status = subprocess.call(["formchk", "asec.chk"], stdout=fh)
|
|
||||||
|
|
||||||
fh.write("Done\n")
|
|
||||||
|
|
||||||
os.chdir(work_dir)
|
|
||||||
|
|
||||||
def readChargesFromFchk(self, file: str, fh: TextIO) -> List[float]:
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(file) as fchk:
|
|
||||||
fchkfile = fchk.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
if self.pop in ["chelpg", "mk"]:
|
|
||||||
CHARGE_FLAG = "ESP Charges"
|
|
||||||
else:
|
|
||||||
CHARGE_FLAG = "ESP Charges"
|
|
||||||
|
|
||||||
start = fchkfile.pop(0).strip()
|
|
||||||
while start.find(CHARGE_FLAG) != 0: # expression in begining of line
|
|
||||||
start = fchkfile.pop(0).strip()
|
|
||||||
|
|
||||||
charges: List[float] = []
|
|
||||||
while len(charges) < len(self.step.molecule[0].atom):
|
|
||||||
charges.extend([float(x) for x in fchkfile.pop(0).split()])
|
|
||||||
|
|
||||||
return charges
|
|
||||||
|
|
||||||
def read_forces_fchk(self, file: str, fh: TextIO) -> np.ndarray:
|
|
||||||
|
|
||||||
forces = []
|
|
||||||
try:
|
|
||||||
with open(file) as tmpfh:
|
|
||||||
fchkfile = tmpfh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
start = fchkfile.pop(0).strip()
|
|
||||||
while start.find("Cartesian Gradient") != 0: # expression in begining of line
|
|
||||||
start = fchkfile.pop(0).strip()
|
|
||||||
|
|
||||||
degrees = 3 * len(self.step.molecule[0].atom)
|
|
||||||
count = 0
|
|
||||||
while len(forces) < degrees:
|
|
||||||
values = fchkfile.pop(0).split()
|
|
||||||
forces.extend([float(x) for x in values])
|
|
||||||
count += len(values)
|
|
||||||
if count >= degrees:
|
|
||||||
forces = forces[:degrees]
|
|
||||||
break
|
|
||||||
|
|
||||||
gradient = np.array(forces)
|
|
||||||
|
|
||||||
fh.write("\nGradient read from file {}:\n".format(file))
|
|
||||||
fh.write(
|
|
||||||
"-----------------------------------------------------------------------\n"
|
|
||||||
"Center Atomic Forces (Hartree/Bohr)\n"
|
|
||||||
"Number Number X Y Z\n"
|
|
||||||
"-----------------------------------------------------------------------\n"
|
|
||||||
)
|
|
||||||
for i in range(len(self.step.molecule[0].atom)):
|
|
||||||
fh.write(
|
|
||||||
" {:>5d} {:>3d} {:>14.9f} {:>14.9f} {:>14.9f}\n".format(
|
|
||||||
i + 1,
|
|
||||||
self.step.molecule[0].atom[i].na,
|
|
||||||
forces.pop(0),
|
|
||||||
forces.pop(0),
|
|
||||||
forces.pop(0),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
"-----------------------------------------------------------------------\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
force_max = np.amax(np.absolute(gradient))
|
|
||||||
force_rms = np.sqrt(np.mean(np.square(gradient)))
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
" Max Force = {:>14.9f} RMS Force = {:>14.9f}\n\n".format(
|
|
||||||
force_max, force_rms
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return gradient
|
|
||||||
|
|
||||||
def read_hessian_fchk(self, file: str) -> np.ndarray:
|
|
||||||
|
|
||||||
force_const = []
|
|
||||||
try:
|
|
||||||
with open(file) as tmpfh:
|
|
||||||
fchkfile = tmpfh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
start = fchkfile.pop(0).strip()
|
|
||||||
while start.find("Cartesian Force Constants") != 0:
|
|
||||||
start = fchkfile.pop(0).strip()
|
|
||||||
|
|
||||||
degrees = 3 * len(self.step.molecule[0].atom)
|
|
||||||
last = round(degrees * (degrees + 1) / 2)
|
|
||||||
count = 0
|
|
||||||
|
|
||||||
while len(force_const) < last:
|
|
||||||
|
|
||||||
value = fchkfile.pop(0).split()
|
|
||||||
force_const.extend([float(x) for x in value])
|
|
||||||
|
|
||||||
# while len(force_const) < last:
|
|
||||||
|
|
||||||
# values = fchkfile.pop(0).split()
|
|
||||||
# force_const.extend([ float(x) for x in values ])
|
|
||||||
# count += len(values)
|
|
||||||
# if count >= last:
|
|
||||||
# force_const = force_const[:last]
|
|
||||||
# break
|
|
||||||
|
|
||||||
hessian = np.zeros((degrees, degrees))
|
|
||||||
for i in range(degrees):
|
|
||||||
for j in range(i + 1):
|
|
||||||
hessian[i, j] = force_const.pop(0)
|
|
||||||
hessian[j, i] = hessian[i, j]
|
|
||||||
|
|
||||||
return hessian
|
|
||||||
|
|
||||||
def read_hessian_log(self, file: str) -> np.ndarray:
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(file) as tmpfh:
|
|
||||||
logfile = tmpfh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
start = logfile.pop(0).strip()
|
|
||||||
while start.find("The second derivative matrix:") != 0:
|
|
||||||
start = logfile.pop(0).strip()
|
|
||||||
|
|
||||||
degrees = 3 * len(self.step.molecule[0].atom)
|
|
||||||
hessian = np.zeros((degrees, degrees))
|
|
||||||
|
|
||||||
k = 0
|
|
||||||
while k < degrees:
|
|
||||||
logfile.pop(0)
|
|
||||||
for i in range(k, degrees):
|
|
||||||
values = logfile.pop(0).split()[1:]
|
|
||||||
for j in range(k, min(i + 1, k + 5)):
|
|
||||||
hessian[i, j] = float(values.pop(0))
|
|
||||||
hessian[j, i] = hessian[i, j]
|
|
||||||
k += 5
|
|
||||||
|
|
||||||
return hessian
|
|
||||||
|
|
||||||
def print_grad_hessian(
|
|
||||||
self, cycle: int, cur_gradient: np.ndarray, hessian: np.ndarray
|
|
||||||
) -> None:
|
|
||||||
|
|
||||||
try:
|
|
||||||
fh = open("grad_hessian.dat", "w")
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file grad_hessian.dat")
|
|
||||||
|
|
||||||
fh.write("Optimization cycle: {}\n".format(cycle))
|
|
||||||
fh.write("Cartesian Gradient\n")
|
|
||||||
degrees = 3 * len(self.step.molecule[0].atom)
|
|
||||||
for i in range(degrees):
|
|
||||||
fh.write(" {:>11.8g}".format(cur_gradient[i]))
|
|
||||||
if (i + 1) % 5 == 0 or i == degrees - 1:
|
|
||||||
fh.write("\n")
|
|
||||||
|
|
||||||
fh.write("Cartesian Force Constants\n")
|
|
||||||
n = int(np.sqrt(2 * degrees))
|
|
||||||
last = degrees * (degrees + 1) / 2
|
|
||||||
count = 0
|
|
||||||
for i in range(n):
|
|
||||||
for j in range(i + 1):
|
|
||||||
count += 1
|
|
||||||
fh.write(" {:>11.8g}".format(hessian[i, j]))
|
|
||||||
if count % 5 == 0 or count == last:
|
|
||||||
fh.write("\n")
|
|
||||||
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
# Change the name to make_gaussian_input
|
|
||||||
def make_gaussian_input(self, cycle: int, asec_charges: List[Dict]) -> None:
|
|
||||||
|
|
||||||
simdir = "simfiles"
|
|
||||||
stepdir = "step{:02d}".format(cycle)
|
|
||||||
path = simdir + os.sep + stepdir + os.sep + "qm"
|
|
||||||
|
|
||||||
file = path + os.sep + "asec.gjf"
|
|
||||||
|
|
||||||
try:
|
|
||||||
fh = open(file, "w")
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
fh.write("%Chk=asec.chk\n")
|
|
||||||
if self.mem != None:
|
|
||||||
fh.write("%Mem={}MB\n".format(self.mem))
|
|
||||||
fh.write("%Nprocs={}\n".format(self.step.nprocs * self.step.ncores))
|
|
||||||
|
|
||||||
kword_line = "#P " + str(self.level)
|
|
||||||
|
|
||||||
if self.keywords != "":
|
|
||||||
kword_line += " " + self.keywords
|
|
||||||
|
|
||||||
if self.step.opt == "yes":
|
|
||||||
kword_line += " Force"
|
|
||||||
|
|
||||||
# kword_line += " Charge"
|
|
||||||
kword_line += " NoSymm"
|
|
||||||
kword_line += " Pop={} Density=Current".format(self.pop)
|
|
||||||
|
|
||||||
if cycle > 1:
|
|
||||||
kword_line += " Guess=Read"
|
|
||||||
|
|
||||||
fh.write(textwrap.fill(kword_line, 90))
|
|
||||||
fh.write("\n")
|
|
||||||
|
|
||||||
fh.write("\nForce calculation - Cycle number {}\n".format(cycle))
|
|
||||||
fh.write("\n")
|
|
||||||
fh.write("{},{}\n".format(self.chgmult[0], self.chgmult[1]))
|
|
||||||
|
|
||||||
for atom in self.step.molecule[0].atom:
|
|
||||||
symbol = atomsymb[atom.na]
|
|
||||||
fh.write(
|
|
||||||
"{:<2s} {:>10.5f} {:>10.5f} {:>10.5f}\n".format(
|
|
||||||
symbol, atom.rx, atom.ry, atom.rz
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write("\n")
|
|
||||||
|
|
||||||
for charge in asec_charges:
|
|
||||||
fh.write(
|
|
||||||
"{:>10.5f} {:>10.5f} {:>10.5f} {:>11.8f}\n".format(
|
|
||||||
charge['rx'], charge['ry'], charge['rz'], charge['chg']
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fh.write("\n")
|
|
||||||
|
|
||||||
fh.close()
|
|
||||||
|
|
||||||
def read_charges(self, file: str, fh: TextIO) -> None:
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(file) as tmpfh:
|
|
||||||
glogfile = tmpfh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
start = glogfile.pop(0).strip()
|
|
||||||
while start != "Fitting point charges to electrostatic potential":
|
|
||||||
start = glogfile.pop(0).strip()
|
|
||||||
|
|
||||||
glogfile = glogfile[3:] # Consume 3 more lines
|
|
||||||
|
|
||||||
fh.write("\nAtomic charges:\n")
|
|
||||||
fh.write("------------------------------------\n")
|
|
||||||
for atom in self.step.molecule[0].atom:
|
|
||||||
line = glogfile.pop(0).split()
|
|
||||||
atom_str = line[1]
|
|
||||||
charge = float(line[2])
|
|
||||||
atom.chg = charge
|
|
||||||
fh.write(" {:<2s} {:>10.6f}\n".format(atom_str, charge))
|
|
||||||
|
|
||||||
# if self.pop == "chelpg":
|
|
||||||
# for ghost in ghost_atoms:
|
|
||||||
# line = glogfile.pop(0).split()
|
|
||||||
# atom_str = line[1]
|
|
||||||
# charge = float(line[2])
|
|
||||||
# ghost['chg'] = charge
|
|
||||||
# fh.write(" {:<2s} {:>10.6f}\n".format(atom_str, charge))
|
|
||||||
|
|
||||||
# for lp in lp_atoms:
|
|
||||||
# line = glogfile.pop(0).split()
|
|
||||||
# atom_str = line[1]
|
|
||||||
# charge = float(line[2])
|
|
||||||
# lp['chg'] = charge
|
|
||||||
# fh.write(" {:<2s} {:>10.6f}\n".format(atom_str, charge))
|
|
||||||
|
|
||||||
fh.write("------------------------------------\n")
|
|
||||||
|
|
||||||
def executeOptimizationRoutine(self, cycle: int, outfile: TextIO, readhessian: str):
|
|
||||||
|
|
||||||
try:
|
|
||||||
gradient
|
|
||||||
old_gradient = gradient
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
gradient = self.read_forces_fchk(file, outfile)
|
|
||||||
|
|
||||||
# If 1st step, read the hessian
|
|
||||||
if cycle == 1:
|
|
||||||
|
|
||||||
if readhessian == "yes":
|
|
||||||
|
|
||||||
file = "grad_hessian.dat"
|
|
||||||
outfile.write(
|
|
||||||
"\nReading the hessian matrix from file {}\n".format(file)
|
|
||||||
)
|
|
||||||
hessian = self.read_hessian_log(file)
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
file = (
|
|
||||||
"simfiles"
|
|
||||||
+ os.sep
|
|
||||||
+ "step01"
|
|
||||||
+ os.sep
|
|
||||||
+ "qm"
|
|
||||||
+ os.sep
|
|
||||||
+ "asec.fchk"
|
|
||||||
)
|
|
||||||
outfile.write(
|
|
||||||
"\nReading the hessian matrix from file {}\n".format(file)
|
|
||||||
)
|
|
||||||
hessian = self.read_hessian_fchk(file)
|
|
||||||
|
|
||||||
# From 2nd step on, update the hessian
|
|
||||||
else:
|
|
||||||
outfile.write("\nUpdating the hessian matrix using the BFGS method... ")
|
|
||||||
hessian = self.step.molecule[0].update_hessian(
|
|
||||||
step, gradient, old_gradient, hessian
|
|
||||||
)
|
|
||||||
outfile.write("Done\n")
|
|
||||||
|
|
||||||
# Save gradient and hessian
|
|
||||||
self.print_grad_hessian(cycle, gradient, hessian)
|
|
||||||
|
|
||||||
# Calculate the step and update the position
|
|
||||||
step = self.calculate_step(cycle, gradient, hessian)
|
|
||||||
|
|
||||||
position += step
|
|
||||||
|
|
||||||
## If needed, calculate the charges
|
|
||||||
if cycle < self.step.switchcyc:
|
|
||||||
|
|
||||||
# internal.gaussian.make_charge_input(cycle, asec_charges)
|
|
||||||
self.run_gaussian(cycle, "charge", outfile)
|
|
||||||
|
|
||||||
file = (
|
|
||||||
"simfiles"
|
|
||||||
+ os.sep
|
|
||||||
+ "step{:02d}".format(cycle)
|
|
||||||
+ os.sep
|
|
||||||
+ "qm"
|
|
||||||
+ os.sep
|
|
||||||
+ "asec2.log"
|
|
||||||
)
|
|
||||||
self.read_charges(file, outfile)
|
|
||||||
else:
|
|
||||||
file = (
|
|
||||||
"simfiles"
|
|
||||||
+ os.sep
|
|
||||||
+ "step{:02d}".format(cycle)
|
|
||||||
+ os.sep
|
|
||||||
+ "qm"
|
|
||||||
+ os.sep
|
|
||||||
+ "asec.log"
|
|
||||||
)
|
|
||||||
self.read_charges(file, outfile)
|
|
||||||
|
|
||||||
self.outfile.write("\nNew values for molecule type 1:\n\n")
|
|
||||||
self.step.molecule[0].print_mol_info(outfile)
|
|
||||||
|
|
||||||
def run_gaussian(self, cycle: int, type: str, fh: TextIO) -> None:
|
|
||||||
|
|
||||||
simdir = "simfiles"
|
|
||||||
stepdir = "step{:02d}".format(cycle)
|
|
||||||
path = simdir + os.sep + stepdir + os.sep + "qm"
|
|
||||||
work_dir = os.getcwd()
|
|
||||||
os.chdir(path)
|
|
||||||
|
|
||||||
# if type == "force":
|
|
||||||
# infile = "asec.gjf"
|
|
||||||
# elif type == "charge":
|
|
||||||
# infile = "asec2.gjf"
|
|
||||||
|
|
||||||
infile = "asec.gjf"
|
|
||||||
|
|
||||||
fh.write(
|
|
||||||
"\nCalculation of {}s initiated with Gaussian on {}\n".format(
|
|
||||||
type, date_time()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if shutil.which("bash") != None:
|
|
||||||
exit_status = subprocess.call(
|
|
||||||
[
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"exec -a {}-step{} {} {}".format(
|
|
||||||
self.qmprog, cycle, self.qmprog, infile
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exit_status = subprocess.call([self.qmprog, infile])
|
|
||||||
|
|
||||||
if exit_status != 0:
|
|
||||||
sys.exit("Gaussian process did not exit properly")
|
|
||||||
|
|
||||||
fh.write("Calculation of {}s finished on {}\n".format(type, date_time()))
|
|
||||||
|
|
||||||
os.chdir(work_dir)
|
|
||||||
|
|
||||||
# def calculate_step(
|
|
||||||
# self, cycle: int, gradient: np.ndarray, hessian: np.ndarray
|
|
||||||
# ) -> np.ndarray:
|
|
||||||
|
|
||||||
# invhessian = np.linalg.inv(hessian)
|
|
||||||
# pre_step = -1 * np.matmul(invhessian, gradient.T).T
|
|
||||||
# maxstep = np.amax(np.absolute(pre_step))
|
|
||||||
# factor = min(1, self.player.maxstep / maxstep)
|
|
||||||
# step = factor * pre_step
|
|
||||||
|
|
||||||
# self.outfile.write("\nCalculated step-{}:\n".format(cycle))
|
|
||||||
# pre_step_list = pre_step.tolist()
|
|
||||||
|
|
||||||
# self.outfile.write(
|
|
||||||
# "-----------------------------------------------------------------------\n"
|
|
||||||
# "Center Atomic Step (Bohr)\n"
|
|
||||||
# "Number Number X Y Z\n"
|
|
||||||
# "-----------------------------------------------------------------------\n"
|
|
||||||
# )
|
|
||||||
# for i in range(len(self.system.molecule[0].atom)):
|
|
||||||
# self.outfile.write(
|
|
||||||
# " {:>5d} {:>3d} {:>14.9f} {:>14.9f} {:>14.9f}\n".format(
|
|
||||||
# i + 1,
|
|
||||||
# self.system.molecule[0].atom[i].na,
|
|
||||||
# pre_step_list.pop(0),
|
|
||||||
# pre_step_list.pop(0),
|
|
||||||
# pre_step_list.pop(0),
|
|
||||||
# )
|
|
||||||
# )
|
|
||||||
|
|
||||||
# self.outfile.write(
|
|
||||||
# "-----------------------------------------------------------------------\n"
|
|
||||||
# )
|
|
||||||
|
|
||||||
# self.outfile.write("Maximum step is {:>11.6}\n".format(maxstep))
|
|
||||||
# self.outfile.write("Scaling factor = {:>6.4f}\n".format(factor))
|
|
||||||
# self.outfile.write("\nFinal step (Bohr):\n")
|
|
||||||
# step_list = step.tolist()
|
|
||||||
|
|
||||||
# self.outfile.write(
|
|
||||||
# "-----------------------------------------------------------------------\n"
|
|
||||||
# "Center Atomic Step (Bohr)\n"
|
|
||||||
# "Number Number X Y Z\n"
|
|
||||||
# "-----------------------------------------------------------------------\n"
|
|
||||||
# )
|
|
||||||
# for i in range(len(self.system.molecule[0].atom)):
|
|
||||||
# self.outfile.write(
|
|
||||||
# " {:>5d} {:>3d} {:>14.9f} {:>14.9f} {:>14.9f}\n".format(
|
|
||||||
# i + 1,
|
|
||||||
# self.system.molecule[0].atom[i].na,
|
|
||||||
# step_list.pop(0),
|
|
||||||
# step_list.pop(0),
|
|
||||||
# step_list.pop(0),
|
|
||||||
# )
|
|
||||||
# )
|
|
||||||
|
|
||||||
# self.outfile.write(
|
|
||||||
# "-----------------------------------------------------------------------\n"
|
|
||||||
# )
|
|
||||||
|
|
||||||
# step_max = np.amax(np.absolute(step))
|
|
||||||
# step_rms = np.sqrt(np.mean(np.square(step)))
|
|
||||||
|
|
||||||
# self.outfile.write(
|
|
||||||
# " Max Step = {:>14.9f} RMS Step = {:>14.9f}\n\n".format(
|
|
||||||
# step_max, step_rms
|
|
||||||
# )
|
|
||||||
# )
|
|
||||||
|
|
||||||
# return step
|
|
||||||
|
|
||||||
def configure(self, step: StepDTO):
|
|
||||||
|
|
||||||
self.step = step
|
|
||||||
|
|
||||||
def start(self, cycle: int, outfile: TextIO, asec_charges: List[Dict], readhessian: str) -> StepDTO:
|
|
||||||
|
|
||||||
make_qm_dir(cycle)
|
|
||||||
|
|
||||||
if cycle > 1:
|
|
||||||
|
|
||||||
src = (
|
|
||||||
"simfiles"
|
|
||||||
+ os.sep
|
|
||||||
+ "step{:02d}".format(cycle - 1)
|
|
||||||
+ os.sep
|
|
||||||
+ "qm"
|
|
||||||
+ os.sep
|
|
||||||
+ "asec.chk"
|
|
||||||
)
|
|
||||||
dst = (
|
|
||||||
"simfiles"
|
|
||||||
+ os.sep
|
|
||||||
+ "step{:02d}".format(cycle)
|
|
||||||
+ os.sep
|
|
||||||
+ "qm"
|
|
||||||
+ os.sep
|
|
||||||
+ "asec.chk"
|
|
||||||
)
|
|
||||||
shutil.copyfile(src, dst)
|
|
||||||
|
|
||||||
self.make_gaussian_input(cycle, asec_charges)
|
|
||||||
self.run_gaussian(cycle, "force", outfile)
|
|
||||||
self.run_formchk(cycle, outfile)
|
|
||||||
|
|
||||||
## Read the gradient
|
|
||||||
file = (
|
|
||||||
"simfiles"
|
|
||||||
+ os.sep
|
|
||||||
+ "step{:02d}".format(cycle)
|
|
||||||
+ os.sep
|
|
||||||
+ "qm"
|
|
||||||
+ os.sep
|
|
||||||
+ "asec.fchk"
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.step.opt:
|
|
||||||
|
|
||||||
pass
|
|
||||||
# position = self.executeOptimizationRoutine(cycle, outfile, readhessian)
|
|
||||||
# self.step.position = position
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
charges = self.readChargesFromFchk(file, outfile)
|
|
||||||
self.step.charges = charges
|
|
||||||
|
|
||||||
return self.step
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
|
|
||||||
del self.step
|
|
||||||
@@ -1,801 +0,0 @@
|
|||||||
import os
|
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
import textwrap
|
|
||||||
from typing import Dict, List, TextIO
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from diceplayer.DPpack.Environment.Atom import Atom
|
|
||||||
from diceplayer.DPpack.Environment.Molecule import Molecule
|
|
||||||
from diceplayer.DPpack.Environment.System import System
|
|
||||||
from diceplayer.DPpack.External.Dice import Dice
|
|
||||||
from diceplayer.DPpack.External.Gaussian import Gaussian
|
|
||||||
from diceplayer.DPpack.Utils.Misc import *
|
|
||||||
from diceplayer.DPpack.Utils.PTable import *
|
|
||||||
from diceplayer.DPpack.Utils.StepDTO import StepDTO
|
|
||||||
from diceplayer.DPpack.Utils.Validations import NotNull
|
|
||||||
|
|
||||||
env = ["OMP_STACKSIZE"]
|
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
|
||||||
|
|
||||||
maxcyc = None
|
|
||||||
opt = None
|
|
||||||
nprocs = None
|
|
||||||
qmprog = None
|
|
||||||
lps = None
|
|
||||||
ghosts = None
|
|
||||||
altsteps = None
|
|
||||||
combrule = None
|
|
||||||
|
|
||||||
switchcyc = 3
|
|
||||||
maxstep = 0.3
|
|
||||||
freq = "no"
|
|
||||||
readhessian = "no"
|
|
||||||
vdwforces = "no"
|
|
||||||
tol_factor = 1.2
|
|
||||||
|
|
||||||
TOL_RMS_FORCE = 3e-4
|
|
||||||
TOL_MAX_FORCE = 4.5e-4
|
|
||||||
TOL_RMS_STEP = 1.2e-3
|
|
||||||
TOL_MAX_SET = 1.8e-3
|
|
||||||
TRUST_RADIUS = None
|
|
||||||
|
|
||||||
continued: bool = False
|
|
||||||
|
|
||||||
def __init__(self, infile: TextIO, outfile: TextIO) -> None:
|
|
||||||
|
|
||||||
self.infile = infile
|
|
||||||
self.outfile = outfile
|
|
||||||
|
|
||||||
self.system = System()
|
|
||||||
|
|
||||||
self.dice = Dice(infile, outfile)
|
|
||||||
self.dice_keywords = [
|
|
||||||
a
|
|
||||||
for a in dir(self.dice)
|
|
||||||
if not a.startswith("__") and not callable(getattr(self.dice, a))
|
|
||||||
]
|
|
||||||
|
|
||||||
self.gaussian = Gaussian()
|
|
||||||
self.gaussian_keywords = [
|
|
||||||
a
|
|
||||||
for a in dir(self.gaussian)
|
|
||||||
if not a.startswith("__") and not callable(getattr(self.gaussian, a))
|
|
||||||
]
|
|
||||||
|
|
||||||
@NotNull(
|
|
||||||
requiredArgs=["maxcyc", "opt", "nprocs", "qmprog", "altsteps"]
|
|
||||||
)
|
|
||||||
def updateKeywords(self, **data):
|
|
||||||
self.__dict__.update(**data)
|
|
||||||
|
|
||||||
def read_keywords(self) -> None:
|
|
||||||
|
|
||||||
with self.infile as f:
|
|
||||||
data = yaml.load(f, Loader=yaml.SafeLoader)
|
|
||||||
|
|
||||||
self.updateKeywords(**data.get("diceplayer"))
|
|
||||||
self.dice.updateKeywords(**data.get("dice"))
|
|
||||||
self.gaussian.updateKeywords(**data.get("gaussian"))
|
|
||||||
|
|
||||||
def check_keywords(self) -> None:
|
|
||||||
|
|
||||||
min_steps = 20000
|
|
||||||
|
|
||||||
if self.dice.ljname == None:
|
|
||||||
sys.exit(
|
|
||||||
"Error: 'ljname' keyword not specified in file {}".format(self.infile)
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.dice.outname == None:
|
|
||||||
sys.exit(
|
|
||||||
"Error: 'outname' keyword not specified in file {}".format(self.infile)
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.dice.dens == None:
|
|
||||||
sys.exit(
|
|
||||||
"Error: 'dens' keyword not specified in file {}".format(self.infile)
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.dice.nmol == 0:
|
|
||||||
sys.exit(
|
|
||||||
"Error: 'nmol' keyword not defined appropriately in file {}".format(
|
|
||||||
self.infile
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.dice.nstep == 0:
|
|
||||||
sys.exit(
|
|
||||||
"Error: 'nstep' keyword not defined appropriately in file {}".format(
|
|
||||||
self.infile
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check only if QM program is Gaussian:
|
|
||||||
if self.qmprog in ("g03", "g09", "g16"):
|
|
||||||
|
|
||||||
if self.gaussian.level == None:
|
|
||||||
sys.exit(
|
|
||||||
"Error: 'level' keyword not specified in file {}".format(
|
|
||||||
self.infile
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.gaussian.gmiddle != None:
|
|
||||||
if not os.path.isfile(self.gaussian.gmiddle):
|
|
||||||
sys.exit("Error: file {} not found".format(self.gaussian.gmiddle))
|
|
||||||
|
|
||||||
if self.gaussian.gbottom != None:
|
|
||||||
if not os.path.isfile(self.gaussian.gbottom):
|
|
||||||
sys.exit("Error: file {} not found".format(self.gaussian.gbottom))
|
|
||||||
|
|
||||||
if self.gaussian.pop != "chelpg" and (
|
|
||||||
self.ghosts == "yes" or self.lps == "yes"
|
|
||||||
):
|
|
||||||
sys.exit(
|
|
||||||
"Error: ghost atoms or lone pairs only available with 'pop = chelpg')"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check only if QM program is Molcas:
|
|
||||||
# if self.qmprog == "molcas":
|
|
||||||
|
|
||||||
# if self.molcas.mbottom == None:
|
|
||||||
# sys.exit("Error: 'mbottom' keyword not specified in file {}".format(self.infile))
|
|
||||||
# else:
|
|
||||||
# if not os.path.isfile(self.molcas.mbottom):
|
|
||||||
# sys.exit("Error: file {} not found".format(self.molcas.mbottom))
|
|
||||||
|
|
||||||
# if self.molcas.basis == None:
|
|
||||||
# sys.exit("Error: 'basis' keyword not specified in file {}".format(self.infile))
|
|
||||||
|
|
||||||
if self.altsteps != 0:
|
|
||||||
|
|
||||||
# Verifica se tem mais de 1 molecula QM
|
|
||||||
# (No futuro usar o RMSD fit para poder substituir todas as moleculas QM
|
|
||||||
# no arquivo outname.xy - Need to change the __make_init_file!!)
|
|
||||||
if self.dice.nmol[0] > 1:
|
|
||||||
sys.exit(
|
|
||||||
"Error: altsteps > 0 only possible with 1 QM molecule (nmol = 1 n2 n3 n4)"
|
|
||||||
)
|
|
||||||
|
|
||||||
# if not zero, altsteps cannot be less than min_steps
|
|
||||||
self.altsteps = max(min_steps, self.altsteps)
|
|
||||||
# altsteps value is always the nearest multiple of 1000
|
|
||||||
self.altsteps = round(self.altsteps / 1000) * 1000
|
|
||||||
|
|
||||||
for i in range(len(self.dice.nstep)):
|
|
||||||
# nstep can never be less than min_steps
|
|
||||||
self.dice.nstep[i] = max(min_steps, self.dice.nstep[i])
|
|
||||||
# nstep values are always the nearest multiple of 1000
|
|
||||||
self.dice.nstep[i] = round(self.dice.nstep[i] / 1000) * 1000
|
|
||||||
|
|
||||||
# isave must be between 100 and 2000
|
|
||||||
self.dice.isave = max(100, self.dice.isave)
|
|
||||||
self.dice.isave = min(2000, self.dice.isave)
|
|
||||||
# isave value is always the nearest multiple of 100
|
|
||||||
self.dice.isave = round(self.dice.isave / 100) * 100
|
|
||||||
|
|
||||||
def print_keywords(self) -> None:
|
|
||||||
|
|
||||||
self.outfile.write(
|
|
||||||
"##########################################################################################\n"
|
|
||||||
"############# Welcome to DICEPLAYER version 1.0 #############\n"
|
|
||||||
"##########################################################################################\n"
|
|
||||||
"\n"
|
|
||||||
)
|
|
||||||
self.outfile.write("Your python version is {}\n".format(sys.version))
|
|
||||||
self.outfile.write("\n")
|
|
||||||
self.outfile.write("Program started on {}\n".format(weekday_date_time()))
|
|
||||||
self.outfile.write("\n")
|
|
||||||
self.outfile.write("Environment variables:\n")
|
|
||||||
for var in env:
|
|
||||||
self.outfile.write(
|
|
||||||
"{} = {}\n".format(
|
|
||||||
var, (os.environ[var] if var in os.environ else "Not set")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.outfile.write(
|
|
||||||
"\n==========================================================================================\n"
|
|
||||||
" CONTROL variables being used in this run:\n"
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
"\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.outfile.write("\n")
|
|
||||||
|
|
||||||
self.outfile.write(
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
" DICE variables being used in this run:\n"
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
"\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
for key in sorted(self.dice_keywords):
|
|
||||||
if getattr(self.dice, key) != None:
|
|
||||||
if isinstance(getattr(self.dice, key), list):
|
|
||||||
string = " ".join(str(x) for x in getattr(self.dice, key))
|
|
||||||
self.outfile.write("{} = {}\n".format(key, string))
|
|
||||||
else:
|
|
||||||
self.outfile.write("{} = {}\n".format(key, getattr(self.dice, key)))
|
|
||||||
|
|
||||||
self.outfile.write("\n")
|
|
||||||
|
|
||||||
if self.qmprog in ("g03", "g09", "g16"):
|
|
||||||
|
|
||||||
self.outfile.write(
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
" GAUSSIAN variables being used in this run:\n"
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
"\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
for key in sorted(self.gaussian_keywords):
|
|
||||||
if getattr(self.gaussian, key) != None:
|
|
||||||
if isinstance(getattr(self.gaussian, key), list):
|
|
||||||
string = " ".join(str(x) for x in getattr(self.gaussian, key))
|
|
||||||
self.outfile.write("{} = {}\n".format(key, string))
|
|
||||||
else:
|
|
||||||
self.outfile.write(
|
|
||||||
"{} = {}\n".format(key, getattr(self.gaussian, key))
|
|
||||||
)
|
|
||||||
|
|
||||||
self.outfile.write("\n")
|
|
||||||
|
|
||||||
# elif self.qmprog == "molcas":
|
|
||||||
|
|
||||||
# self.outfile.write("------------------------------------------------------------------------------------------\n"
|
|
||||||
# " MOLCAS variables being used in this run:\n"
|
|
||||||
# "------------------------------------------------------------------------------------------\n"
|
|
||||||
# "\n")
|
|
||||||
|
|
||||||
# for key in sorted(molcas):
|
|
||||||
# if molcas[key] != None:
|
|
||||||
# if isinstance(molcas[key], list):
|
|
||||||
# string = " ".join(str(x) for x in molcas[key])
|
|
||||||
# self.outfile.write("{} = {}\n".format(key, string))
|
|
||||||
# else:
|
|
||||||
# self.outfile.write("{} = {}\n".format(key, molcas[key]))
|
|
||||||
|
|
||||||
# self.outfile.write("\n")
|
|
||||||
|
|
||||||
def read_potential(self) -> None: # Deve ser atualizado para o uso de
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(self.dice.ljname) as file:
|
|
||||||
ljfile = file.readlines()
|
|
||||||
except EnvironmentError as err:
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
combrule = ljfile.pop(0).split()[0]
|
|
||||||
if combrule not in ("*", "+"):
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a '*' or a '+' sign in 1st line of file {}".format(
|
|
||||||
self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.dice.combrule = combrule
|
|
||||||
|
|
||||||
ntypes = ljfile.pop(0).split()[0]
|
|
||||||
if not ntypes.isdigit():
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected an integer in the 2nd line of file {}".format(
|
|
||||||
self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
ntypes = int(ntypes)
|
|
||||||
|
|
||||||
if ntypes != len(self.dice.nmol):
|
|
||||||
sys.exit(
|
|
||||||
"Error: number of molecule types in file {} must match that of 'nmol' keyword in file {}".format(
|
|
||||||
self.dice.ljname, self.infile
|
|
||||||
)
|
|
||||||
)
|
|
||||||
line = 2
|
|
||||||
for i in range(ntypes):
|
|
||||||
|
|
||||||
line += 1
|
|
||||||
nsites, molname = ljfile.pop(0).split()[:2]
|
|
||||||
|
|
||||||
if not nsites.isdigit():
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected an integer in line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if molname is None:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a molecule name in line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
nsites = int(nsites)
|
|
||||||
|
|
||||||
self.system.add_type(nsites, Molecule(molname))
|
|
||||||
|
|
||||||
for j in range(nsites):
|
|
||||||
|
|
||||||
line += 1
|
|
||||||
new_atom = ljfile.pop(0).split()
|
|
||||||
|
|
||||||
if len(new_atom) < 8:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected at least 8 fields in line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not new_atom[0].isdigit():
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected an integer in field 1, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
lbl = int(new_atom[0])
|
|
||||||
|
|
||||||
if not new_atom[1].isdigit():
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected an integer in field 2, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
atnumber = int(new_atom[1])
|
|
||||||
if (
|
|
||||||
atnumber == ghost_number and i == 0
|
|
||||||
): # Ghost atom not allowed in the QM molecule
|
|
||||||
sys.exit(
|
|
||||||
"Error: found a ghost atom in line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
na = atnumber
|
|
||||||
|
|
||||||
try:
|
|
||||||
rx = float(new_atom[2])
|
|
||||||
except:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a float in field 3, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
ry = float(new_atom[3])
|
|
||||||
except:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a float in field 4, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
rz = float(new_atom[4])
|
|
||||||
except:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a float in field 5, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
chg = float(new_atom[5])
|
|
||||||
except:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a float in field 6, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
eps = float(new_atom[6])
|
|
||||||
except:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a float in field 7, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
sig = float(new_atom[7])
|
|
||||||
except:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a float in field 8, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
mass = atommass[na]
|
|
||||||
|
|
||||||
if len(new_atom) > 8:
|
|
||||||
masskey, mass = new_atom[8].partition("=")[::2]
|
|
||||||
if masskey.lower() == "mass" and len(mass) != 0:
|
|
||||||
try:
|
|
||||||
new_mass = float(mass)
|
|
||||||
if new_mass > 0:
|
|
||||||
mass = new_mass
|
|
||||||
except:
|
|
||||||
sys.exit(
|
|
||||||
"Error: expected a positive float after 'mass=' in field 9, line {} of file {}".format(
|
|
||||||
line, self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.system.molecule[i].add_atom(
|
|
||||||
Atom(lbl, na, rx, ry, rz, chg, eps, sig)
|
|
||||||
)
|
|
||||||
|
|
||||||
to_delete = ["lbl", "na", "rx", "ry", "rz", "chg", "eps", "sig", "mass"]
|
|
||||||
for _var in to_delete:
|
|
||||||
if _var in locals() or _var in globals():
|
|
||||||
exec(f"del {_var}")
|
|
||||||
|
|
||||||
def print_potential(self) -> None:
|
|
||||||
|
|
||||||
formatstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f} {:>9.4f}\n"
|
|
||||||
self.outfile.write(
|
|
||||||
"\n"
|
|
||||||
"==========================================================================================\n"
|
|
||||||
)
|
|
||||||
self.outfile.write(
|
|
||||||
" Potential parameters from file {}:\n".format(
|
|
||||||
self.dice.ljname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.outfile.write(
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
"\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.outfile.write("Combination rule: {}\n".format(self.dice.combrule))
|
|
||||||
self.outfile.write(
|
|
||||||
"Types of molecules: {}\n\n".format(len(self.system.molecule))
|
|
||||||
)
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
for mol in self.system.molecule:
|
|
||||||
i += 1
|
|
||||||
self.outfile.write(
|
|
||||||
"{} atoms in molecule type {}:\n".format(len(mol.atom), i)
|
|
||||||
)
|
|
||||||
self.outfile.write(
|
|
||||||
"---------------------------------------------------------------------------------\n"
|
|
||||||
"Lbl AN X Y Z Charge Epsilon Sigma Mass\n"
|
|
||||||
)
|
|
||||||
self.outfile.write(
|
|
||||||
"---------------------------------------------------------------------------------\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
for atom in mol.atom:
|
|
||||||
|
|
||||||
self.outfile.write(
|
|
||||||
formatstr.format(
|
|
||||||
atom.lbl,
|
|
||||||
atom.na,
|
|
||||||
atom.rx,
|
|
||||||
atom.ry,
|
|
||||||
atom.rz,
|
|
||||||
atom.chg,
|
|
||||||
atom.eps,
|
|
||||||
atom.sig,
|
|
||||||
atom.mass,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.outfile.write("\n")
|
|
||||||
|
|
||||||
if self.ghosts == "yes" or self.lps == "yes":
|
|
||||||
self.outfile.write(
|
|
||||||
"\n"
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
" Aditional potential parameters:\n"
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
# if player['ghosts'] == "yes":
|
|
||||||
|
|
||||||
# self.outfile.write("\n")
|
|
||||||
# self.outfile.write("{} ghost atoms appended to molecule type 1 at:\n".format(len(ghost_types)))
|
|
||||||
# self.outfile.write("---------------------------------------------------------------------------------\n")
|
|
||||||
|
|
||||||
# atoms_string = ""
|
|
||||||
# for ghost in ghost_types:
|
|
||||||
# for atom in ghost['numbers']:
|
|
||||||
# atom_sym = atomsymb[ molecules[0][atom - 1]['na'] ].strip()
|
|
||||||
# atoms_string += "{}{} ".format(atom_sym,atom)
|
|
||||||
|
|
||||||
# if ghost['type'] == "g":
|
|
||||||
# self.outfile.write(textwrap.fill("* Geometric center of atoms {}".format(atoms_string), 80))
|
|
||||||
# elif ghost['type'] == "m":
|
|
||||||
# self.outfile.write(textwrap.fill("* Center of mass of atoms {}".format(atoms_string), 80))
|
|
||||||
# elif ghost['type'] == "z":
|
|
||||||
# self.outfile.write(textwrap.fill("* Center of atomic number of atoms {}".format(atoms_string), 80))
|
|
||||||
|
|
||||||
# self.outfile.write("\n")
|
|
||||||
|
|
||||||
# if player['lps'] == 'yes':
|
|
||||||
|
|
||||||
# self.outfile.write("\n")
|
|
||||||
# self.outfile.write("{} lone pairs appended to molecule type 1:\n".format(len(lp_types)))
|
|
||||||
# self.outfile.write("---------------------------------------------------------------------------------\n")
|
|
||||||
|
|
||||||
# for lp in lp_types:
|
|
||||||
# # LP type 1 or 2
|
|
||||||
# if lp['type'] in (1, 2):
|
|
||||||
# atom1_num = lp['numbers'][0]
|
|
||||||
# atom1_sym = atomsymb[ molecules[0][atom1_num - 1]['na'] ].strip()
|
|
||||||
# atom2_num = lp['numbers'][1]
|
|
||||||
# atom2_sym = atomsymb[ molecules[0][atom2_num - 1]['na'] ].strip()
|
|
||||||
# atom3_num = lp['numbers'][2]
|
|
||||||
# atom3_sym = atomsymb[ molecules[0][atom3_num - 1]['na'] ].strip()
|
|
||||||
|
|
||||||
# self.outfile.write(textwrap.fill(
|
|
||||||
# "* Type {} on atom {}{} with {}{} {}{}. Alpha = {:<5.1f} Deg and D = {:<4.2f} Angs".format(
|
|
||||||
# lp['type'], atom1_sym, atom1_num, atom2_sym, atom2_num, atom3_sym, atom3_num, lp['alpha'],
|
|
||||||
# lp['dist']), 86))
|
|
||||||
# self.outfile.write("\n")
|
|
||||||
|
|
||||||
# # Other LP types
|
|
||||||
|
|
||||||
self.outfile.write(
|
|
||||||
"\n"
|
|
||||||
"==========================================================================================\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
def check_executables(self) -> None:
|
|
||||||
|
|
||||||
self.outfile.write("\n")
|
|
||||||
self.outfile.write(90 * "=")
|
|
||||||
self.outfile.write("\n\n")
|
|
||||||
|
|
||||||
dice_path = shutil.which(self.dice.progname)
|
|
||||||
if dice_path != None:
|
|
||||||
self.outfile.write(
|
|
||||||
"Program {} found at {}\n".format(self.dice.progname, dice_path)
|
|
||||||
)
|
|
||||||
self.dice.path = dice_path
|
|
||||||
else:
|
|
||||||
sys.exit("Error: cannot find dice executable")
|
|
||||||
|
|
||||||
qmprog_path = shutil.which(self.gaussian.qmprog)
|
|
||||||
if qmprog_path != None:
|
|
||||||
self.outfile.write(
|
|
||||||
"Program {} found at {}\n".format(self.gaussian.qmprog, qmprog_path)
|
|
||||||
)
|
|
||||||
self.gaussian.path = qmprog_path
|
|
||||||
else:
|
|
||||||
sys.exit("Error: cannot find {} executable".format(self.gaussian.qmprog))
|
|
||||||
|
|
||||||
if self.gaussian.qmprog in ("g03", "g09", "g16"):
|
|
||||||
formchk_path = shutil.which("formchk")
|
|
||||||
if formchk_path != None:
|
|
||||||
self.outfile.write("Program formchk found at {}\n".format(formchk_path))
|
|
||||||
else:
|
|
||||||
sys.exit("Error: cannot find formchk executable")
|
|
||||||
|
|
||||||
def dice_start(self, cycle: int):
|
|
||||||
|
|
||||||
self.dice.configure(
|
|
||||||
StepDTO(
|
|
||||||
initcyc=self.initcyc,
|
|
||||||
nprocs=self.nprocs,
|
|
||||||
altsteps=self.altsteps,
|
|
||||||
nmol=self.system.nmols,
|
|
||||||
molecule=self.system.molecule,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.dice.start(cycle)
|
|
||||||
|
|
||||||
self.dice.reset()
|
|
||||||
|
|
||||||
def gaussian_start(self, cycle: int, geomsfh: TextIO):
|
|
||||||
|
|
||||||
self.gaussian.configure(
|
|
||||||
StepDTO(
|
|
||||||
initcyc=self.initcyc,
|
|
||||||
nprocs=self.nprocs,
|
|
||||||
ncores=self.dice.ncores,
|
|
||||||
altsteps=self.altsteps,
|
|
||||||
switchcyc=self.switchcyc,
|
|
||||||
opt=self.opt,
|
|
||||||
nmol=self.system.nmols,
|
|
||||||
molecule=self.system.molecule
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make ASEC
|
|
||||||
self.outfile.write("\nBuilding the ASEC and vdW meanfields... ")
|
|
||||||
asec_charges = self.populate_asec_vdw(cycle)
|
|
||||||
|
|
||||||
step = self.gaussian.start(cycle, self.outfile, asec_charges, self.readhessian)
|
|
||||||
|
|
||||||
if self.opt:
|
|
||||||
|
|
||||||
position = step.position
|
|
||||||
|
|
||||||
## Update the geometry of the reference molecule
|
|
||||||
self.system.update_molecule(position, self.outfile)
|
|
||||||
|
|
||||||
## Print new geometry in geoms.xyz
|
|
||||||
self.system.print_geom(cycle, geomsfh)
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
charges = step.charges
|
|
||||||
|
|
||||||
self.system.molecule[0].updateCharges(charges)
|
|
||||||
|
|
||||||
self.system.printChargesAndDipole(cycle, self.outfile)
|
|
||||||
|
|
||||||
self.gaussian.reset()
|
|
||||||
|
|
||||||
def populate_asec_vdw(self, cycle) -> List[Dict]:
|
|
||||||
|
|
||||||
# Both asec_charges and vdw_meanfield will utilize the Molecule() class and Atoms() with some None elements
|
|
||||||
|
|
||||||
asec_charges = []
|
|
||||||
|
|
||||||
if self.dice.nstep[-1] % self.dice.isave == 0:
|
|
||||||
nconfigs = round(self.dice.nstep[-1] / self.dice.isave)
|
|
||||||
else:
|
|
||||||
nconfigs = int(self.dice.nstep[-1] / self.dice.isave)
|
|
||||||
|
|
||||||
norm_factor = nconfigs * self.nprocs
|
|
||||||
|
|
||||||
nsitesref = len(self.system.molecule[0].atom)
|
|
||||||
|
|
||||||
nsites_total = self.dice.nmol[0] * nsitesref
|
|
||||||
for i in range(1, len(self.dice.nmol)):
|
|
||||||
nsites_total += self.dice.nmol[i] * len(self.system.molecule[i].atom)
|
|
||||||
|
|
||||||
thickness = []
|
|
||||||
picked_mols = []
|
|
||||||
|
|
||||||
for proc in range(1, self.nprocs + 1): # Run over folders
|
|
||||||
|
|
||||||
path = (
|
|
||||||
"simfiles"
|
|
||||||
+ os.sep
|
|
||||||
+ "step{:02d}".format(cycle)
|
|
||||||
+ os.sep
|
|
||||||
+ "p{:02d}".format(proc)
|
|
||||||
)
|
|
||||||
file = path + os.sep + self.dice.outname + ".xyz"
|
|
||||||
if not os.path.isfile(file):
|
|
||||||
sys.exit("Error: cannot find file {}".format(file))
|
|
||||||
try:
|
|
||||||
with open(file) as xyzfh:
|
|
||||||
xyzfile = xyzfh.readlines()
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot open file {}".format(file))
|
|
||||||
|
|
||||||
for config in range(nconfigs): # Run over configs in a folder
|
|
||||||
|
|
||||||
if int(xyzfile.pop(0).split()[0]) != nsites_total:
|
|
||||||
sys.exit("Error: wrong number of sites in file {}".format(file))
|
|
||||||
|
|
||||||
box = xyzfile.pop(0).split()[-3:]
|
|
||||||
box = [float(box[0]), float(box[1]), float(box[2])]
|
|
||||||
sizes = self.system.molecule[0].sizes_of_molecule()
|
|
||||||
thickness.append(
|
|
||||||
min(
|
|
||||||
[
|
|
||||||
(box[0] - sizes[0]) / 2,
|
|
||||||
(box[1] - sizes[1]) / 2,
|
|
||||||
(box[2] - sizes[2]) / 2,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
xyzfile = xyzfile[nsitesref:]
|
|
||||||
mol_count = 0
|
|
||||||
for type in range(len(self.dice.nmol)):
|
|
||||||
|
|
||||||
if type == 0:
|
|
||||||
nmols = self.dice.nmol[0] - 1
|
|
||||||
else:
|
|
||||||
nmols = self.dice.nmol[type]
|
|
||||||
|
|
||||||
for mol in range(nmols):
|
|
||||||
|
|
||||||
new_molecule = Molecule("ASEC TMP MOLECULE")
|
|
||||||
for site in range(len(self.system.molecule[type].atom)):
|
|
||||||
|
|
||||||
line = xyzfile.pop(0).split()
|
|
||||||
|
|
||||||
if (
|
|
||||||
line[0].title()
|
|
||||||
!= atomsymb[self.system.molecule[type].atom[site].na].strip()
|
|
||||||
):
|
|
||||||
sys.exit("Error reading file {}".format(file))
|
|
||||||
|
|
||||||
new_molecule.add_atom(
|
|
||||||
Atom(
|
|
||||||
self.system.molecule[type].atom[site].lbl,
|
|
||||||
self.system.molecule[type].atom[site].na,
|
|
||||||
float(line[1]),
|
|
||||||
float(line[2]),
|
|
||||||
float(line[3]),
|
|
||||||
self.system.molecule[type].atom[site].chg,
|
|
||||||
self.system.molecule[type].atom[site].eps,
|
|
||||||
self.system.molecule[type].atom[site].sig,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
dist = self.system.molecule[0].minimum_distance(new_molecule)
|
|
||||||
if dist < thickness[-1]:
|
|
||||||
mol_count += 1
|
|
||||||
for atom in new_molecule.atom:
|
|
||||||
asec_charges.append({"lbl": atomsymb[atom.na], "rx": atom.rx, "ry": atom.ry, "rz": atom.rz, "chg": atom.chg})
|
|
||||||
|
|
||||||
# if self.vdwforces == "yes":
|
|
||||||
# vdw_meanfield[-1]["rx"] = atom["rx"]
|
|
||||||
# vdw_meanfield[-1]["ry"] = atom["ry"]
|
|
||||||
# vdw_meanfield[-1]["rz"] = atom["rz"]
|
|
||||||
# vdw_meanfield[-1]["eps"] = atom["eps"]
|
|
||||||
# vdw_meanfield[-1]["sig"] = atom["sig"]
|
|
||||||
|
|
||||||
# #### Read lines with ghosts or lps in molecules of type 0 (reference)
|
|
||||||
# #### and, if dist < thickness, appends to asec
|
|
||||||
# if type == 0:
|
|
||||||
# for ghost in ghost_atoms:
|
|
||||||
# line = xyzfile.pop(0).split()
|
|
||||||
# if line[0] != dice_ghost_label:
|
|
||||||
# sys.exit("Error reading file {}".format(file))
|
|
||||||
# if dist < thickness[-1]:
|
|
||||||
# asec_charges.append({})
|
|
||||||
# asec_charges[-1]['rx'] = float(line[1])
|
|
||||||
# asec_charges[-1]['ry'] = float(line[2])
|
|
||||||
# asec_charges[-1]['rz'] = float(line[3])
|
|
||||||
# asec_charges[-1]['chg'] = ghost['chg'] / norm_factor
|
|
||||||
|
|
||||||
# for lp in lp_atoms:
|
|
||||||
# line = xyzfile.pop(0).split()
|
|
||||||
# if line[0] != dice_ghost_label:
|
|
||||||
# sys.exit("Error reading file {}".format(file))
|
|
||||||
# if dist < thickness[-1]:
|
|
||||||
# asec_charges.append({})
|
|
||||||
# asec_charges[-1]['rx'] = float(line[1])
|
|
||||||
# asec_charges[-1]['ry'] = float(line[2])
|
|
||||||
# asec_charges[-1]['rz'] = float(line[3])
|
|
||||||
# asec_charges[-1]['chg'] = lp['chg'] / norm_factor
|
|
||||||
|
|
||||||
picked_mols.append(mol_count)
|
|
||||||
|
|
||||||
self.outfile.write("Done\n")
|
|
||||||
|
|
||||||
string = "In average, {:^7.2f} molecules ".format(
|
|
||||||
sum(picked_mols) / norm_factor
|
|
||||||
)
|
|
||||||
string += "were selected from each of the {} configurations ".format(
|
|
||||||
len(picked_mols)
|
|
||||||
)
|
|
||||||
string += (
|
|
||||||
"of the production simulations to form the ASEC, comprising a shell with "
|
|
||||||
)
|
|
||||||
string += "minimum thickness of {:>6.2f} Angstrom\n".format(
|
|
||||||
sum(thickness) / norm_factor
|
|
||||||
)
|
|
||||||
|
|
||||||
self.outfile.write(textwrap.fill(string, 86))
|
|
||||||
self.outfile.write("\n")
|
|
||||||
|
|
||||||
otherfh = open("ASEC.xyz", "w", 1)
|
|
||||||
for charge in asec_charges:
|
|
||||||
otherfh.write(
|
|
||||||
"{} {:>10.5f} {:>10.5f} {:>10.5f}\n".format(
|
|
||||||
charge['lbl'], charge['rx'], charge['ry'], charge['rz']
|
|
||||||
)
|
|
||||||
)
|
|
||||||
otherfh.close()
|
|
||||||
|
|
||||||
for charge in asec_charges:
|
|
||||||
charge['chg'] /= norm_factor
|
|
||||||
|
|
||||||
return asec_charges
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import os, sys, time
|
|
||||||
from posixpath import sep
|
|
||||||
import shutil, gzip
|
|
||||||
|
|
||||||
####################################### functions ######################################
|
|
||||||
|
|
||||||
def weekday_date_time():
|
|
||||||
|
|
||||||
return time.strftime("%A, %d %b %Y at %H:%M:%S")
|
|
||||||
|
|
||||||
|
|
||||||
def date_time():
|
|
||||||
|
|
||||||
return time.strftime("%d %b %Y at %H:%M:%S")
|
|
||||||
|
|
||||||
|
|
||||||
def compress_files_1mb(path):
|
|
||||||
|
|
||||||
working_dir = os.getcwd()
|
|
||||||
os.chdir(path)
|
|
||||||
|
|
||||||
files = filter(os.path.isfile, os.listdir(os.curdir))
|
|
||||||
for file in files:
|
|
||||||
if os.path.getsize(file) > 1024 * 1024: ## If bigger than 1MB
|
|
||||||
filegz = file + ".gz"
|
|
||||||
try:
|
|
||||||
with open(file, 'rb') as f_in:
|
|
||||||
with gzip.open(filegz, 'wb') as f_out:
|
|
||||||
shutil.copyfileobj(f_in, f_out)
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot compress file {}".format(file))
|
|
||||||
|
|
||||||
os.chdir(working_dir)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def make_simulation_dir():
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
if os.path.exists(sim_dir):
|
|
||||||
sys.exit("Error: a file or a directory {} already exists, move or delete de simfiles directory to continue.".format(sim_dir))
|
|
||||||
try:
|
|
||||||
os.makedirs(sim_dir)
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot make directory {}".format(sim_dir))
|
|
||||||
|
|
||||||
def make_step_dir(cycle):
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
step_dir = "step{:02d}".format(cycle)
|
|
||||||
path = sim_dir + os.sep + step_dir
|
|
||||||
if os.path.exists(path):
|
|
||||||
sys.exit("Error: a file or directory {} already exists".format(step_dir))
|
|
||||||
try:
|
|
||||||
os.makedirs(path)
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot make directory {}".format(step_dir))
|
|
||||||
|
|
||||||
def make_qm_dir(cycle):
|
|
||||||
|
|
||||||
sim_dir = "simfiles"
|
|
||||||
step_dir = "step{:02d}".format(cycle)
|
|
||||||
path = sim_dir + os.sep + step_dir + os.sep + "qm"
|
|
||||||
try:
|
|
||||||
os.makedirs(path)
|
|
||||||
except:
|
|
||||||
sys.exit("Error: cannot make directory {}".format(path))
|
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
# import sys, math
|
|
||||||
# from copy import deepcopy
|
|
||||||
|
|
||||||
# import numpy as np
|
|
||||||
# from numpy import linalg
|
|
||||||
|
|
||||||
# from diceplayer.DPpack.SetGlobals import *
|
|
||||||
|
|
||||||
|
|
||||||
# epsilon = 1e-8
|
|
||||||
|
|
||||||
# ####################################### functions ######################################
|
|
||||||
|
|
||||||
|
|
||||||
# def best_previous_point():
|
|
||||||
|
|
||||||
# min_energy = 0
|
|
||||||
# idx = 0
|
|
||||||
# for energy in internal["energy"][:-1]:
|
|
||||||
# if energy < min_energy or abs(energy - min_energy) < 1e-10:
|
|
||||||
# min_energy = energy
|
|
||||||
# min_idx = idx
|
|
||||||
# idx += 1
|
|
||||||
|
|
||||||
# return min_idx
|
|
||||||
|
|
||||||
|
|
||||||
# def best_point():
|
|
||||||
|
|
||||||
# min_energy = 0
|
|
||||||
# idx = 0
|
|
||||||
# for energy in internal["energy"]:
|
|
||||||
# if energy < min_energy or abs(energy - min_energy) < 1e-10:
|
|
||||||
# min_energy = energy
|
|
||||||
# min_idx = idx
|
|
||||||
# idx += 1
|
|
||||||
|
|
||||||
# return min_idx
|
|
||||||
|
|
||||||
|
|
||||||
# def line_search(fh):
|
|
||||||
|
|
||||||
# X1 = internal["position"][-1] # numpy array
|
|
||||||
# e1 = internal["energy"][-1]
|
|
||||||
# G1 = internal["gradient"][-1] # numpy array
|
|
||||||
|
|
||||||
# idx = best_previous_point()
|
|
||||||
# X0 = internal["position"][idx] # numpy array
|
|
||||||
# e0 = internal["energy"][idx]
|
|
||||||
# G0 = internal["gradient"][idx] # numpy array
|
|
||||||
|
|
||||||
# # First try a quartic fit
|
|
||||||
# fh.write("Attempting a quartic fit.\n")
|
|
||||||
# success, y0 = quartic_fit(X0, X1, e0, e1, G0, G1, fh)
|
|
||||||
# if success and y0 > 0:
|
|
||||||
# if y0 < 1:
|
|
||||||
# new_point = X0 + y0 * (X1 - X0)
|
|
||||||
# new_gradient = interpolate_gradient(G0, G1, y0)
|
|
||||||
# new_gradient = perpendicular_projection(new_gradient, X1 - X0)
|
|
||||||
# fh.write("Line search succeded.\n")
|
|
||||||
# return True, new_point, new_gradient
|
|
||||||
# else:
|
|
||||||
# idx = best_point()
|
|
||||||
# if idx == len(internal["energy"]) - 1:
|
|
||||||
# new_point = X0 + y0 * (X1 - X0)
|
|
||||||
# new_gradient = interpolate_gradient(G0, G1, y0)
|
|
||||||
# new_gradient = perpendicular_projection(new_gradient, X1 - X0)
|
|
||||||
# fh.write("Line search succeded.\n")
|
|
||||||
# return True, new_point, new_gradient
|
|
||||||
# else:
|
|
||||||
# fh.write("Quartic step is not acceptable. ")
|
|
||||||
# elif success:
|
|
||||||
# fh.write("Quartic step is not acceptable. ")
|
|
||||||
|
|
||||||
# # If no condition is met, then y0 is unacceptable. Try the cubic fit next
|
|
||||||
# fh.write("Attempting a cubic fit.\n")
|
|
||||||
# success, y0 = cubic_fit(X0, X1, e0, e1, G0, G1, fh)
|
|
||||||
# if success and y0 > 0:
|
|
||||||
# if y0 < 1:
|
|
||||||
# new_point = X0 + y0 * (X1 - X0)
|
|
||||||
# new_gradient = interpolate_gradient(G0, G1, y0)
|
|
||||||
# new_gradient = perpendicular_projection(new_gradient, X1 - X0)
|
|
||||||
# fh.write("Line search succeded.\n")
|
|
||||||
# return True, new_point, new_gradient
|
|
||||||
# else:
|
|
||||||
# previous_step = X1 - internal["position"][-2]
|
|
||||||
# previous_step_size = linalg.norm(previous_step)
|
|
||||||
# new_point = X0 + y0 * (X1 - X0)
|
|
||||||
# step = new_point - X1
|
|
||||||
# step_size = linalg.norm(step)
|
|
||||||
# if step_size < previous_step_size:
|
|
||||||
# new_gradient = interpolate_gradient(G0, G1, y0)
|
|
||||||
# new_gradient = perpendicular_projection(new_gradient, X1 - X0)
|
|
||||||
# fh.write("Line search succeded.\n")
|
|
||||||
# return True, new_point, new_gradient
|
|
||||||
# else:
|
|
||||||
# fh.write("Cubic step is not acceptable. ")
|
|
||||||
# elif success:
|
|
||||||
# fh.write("Cubic step is not acceptable. ")
|
|
||||||
|
|
||||||
# # If no condition is met again, then all fits fail.
|
|
||||||
# fh.write("All fits fail. ")
|
|
||||||
|
|
||||||
# # Then, if the latest point is not the best, use y0 = 0.5 (step to the midpoint)
|
|
||||||
# idx = best_point()
|
|
||||||
# if idx < len(internal["energy"]) - 1:
|
|
||||||
# y0 = 0.5
|
|
||||||
# new_point = X0 + y0 * (X1 - X0)
|
|
||||||
# new_gradient = interpolate_gradient(G0, G1, y0)
|
|
||||||
# new_gradient = perpendicular_projection(new_gradient, X1 - X0)
|
|
||||||
# fh.write("Moving to the midpoint.\n")
|
|
||||||
# return True, new_point, new_gradient
|
|
||||||
|
|
||||||
# # If the latest point is the best point, no linear search is done
|
|
||||||
# fh.write("No linear search will be used in this step.\n")
|
|
||||||
|
|
||||||
# return False, None, None
|
|
||||||
|
|
||||||
|
|
||||||
# ## For cubic and quartic fits, G0 and G1 are the gradient vectors
|
|
||||||
|
|
||||||
|
|
||||||
# def cubic_fit(X0, X1, e0, e1, G0, G1, fh):
|
|
||||||
|
|
||||||
# line = X1 - X0
|
|
||||||
# line /= linalg.norm(line)
|
|
||||||
|
|
||||||
# g0 = np.dot(G0, line)
|
|
||||||
# g1 = np.dot(G1, line)
|
|
||||||
|
|
||||||
# De = e1 - e0
|
|
||||||
|
|
||||||
# fh.write(
|
|
||||||
# "De = {:<18.15e} g0 = {:<12.8f} g1 = {:<12.8f}\n".format(De, g0, g1)
|
|
||||||
# )
|
|
||||||
|
|
||||||
# alpha = g1 + g0 - 2 * De
|
|
||||||
# if abs(alpha) < epsilon:
|
|
||||||
# fh.write("Cubic fit failed: alpha too small\n")
|
|
||||||
# return False, None
|
|
||||||
|
|
||||||
# beta = 3 * De - 2 * g0 - g1
|
|
||||||
# discriminant = 4 * (beta**2 - 3 * alpha * g0)
|
|
||||||
# if discriminant < 0:
|
|
||||||
# fh.write("Cubic fit failed: no minimum found (negative Delta)\n")
|
|
||||||
# return False, None
|
|
||||||
# if abs(discriminant) < epsilon:
|
|
||||||
# fh.write("Cubic fit failed: no minimum found (null Delta)\n")
|
|
||||||
# return False, None
|
|
||||||
|
|
||||||
# y0 = (-beta + math.sqrt(discriminant / 4)) / (3 * alpha)
|
|
||||||
# fh.write("Minimum found with y0 = {:<8.4f}\n".format(y0))
|
|
||||||
|
|
||||||
# return True, y0
|
|
||||||
|
|
||||||
|
|
||||||
# def quartic_fit(X0, X1, e0, e1, G0, G1, fh):
|
|
||||||
|
|
||||||
# line = X1 - X0
|
|
||||||
# line /= linalg.norm(line)
|
|
||||||
|
|
||||||
# g0 = np.dot(G0, line)
|
|
||||||
# g1 = np.dot(G1, line)
|
|
||||||
|
|
||||||
# De = e1 - e0
|
|
||||||
# Dg = g1 - g0
|
|
||||||
|
|
||||||
# fh.write(
|
|
||||||
# "De = {:<18.15e} g0 = {:<12.8f} g1 = {:<12.8f}\n".format(De, g0, g1)
|
|
||||||
# )
|
|
||||||
|
|
||||||
# if Dg < 0 or De - g0 < 0:
|
|
||||||
# fh.write("Quartic fit failed: negative alpha\n")
|
|
||||||
# return False, None
|
|
||||||
# if abs(Dg) < epsilon or abs(De - g0) < epsilon:
|
|
||||||
# fh.write("Quartic fit failed: alpha too small\n")
|
|
||||||
# return False, None
|
|
||||||
|
|
||||||
# discriminant = 16 * (Dg**2 - 3 * (g1 + g0 - 2 * De) ** 2)
|
|
||||||
# if discriminant < 0:
|
|
||||||
# fh.write("Quartic fit failed: no minimum found (negative Delta)\n")
|
|
||||||
# return False, None
|
|
||||||
|
|
||||||
# alpha1 = (Dg + math.sqrt(discriminant / 16)) / 2
|
|
||||||
# alpha2 = (Dg - math.sqrt(discriminant / 16)) / 2
|
|
||||||
|
|
||||||
# fh.write("alpha1 = {:<7.4e} alpha2 = {:<7.4e}\n".format(alpha1, alpha2))
|
|
||||||
|
|
||||||
# alpha = alpha1
|
|
||||||
# beta = g1 + g0 - 2 * De - 2 * alpha
|
|
||||||
# gamma = De - g0 - alpha - beta
|
|
||||||
|
|
||||||
# y0 = (-1 / (2 * alpha)) * (
|
|
||||||
# (beta**3 - 4 * alpha * beta * gamma + 8 * g0 * alpha**2) / 4
|
|
||||||
# ) ** (1 / 3)
|
|
||||||
# fh.write("Minimum found with y0 = {:<8.4f}\n".format(y0))
|
|
||||||
|
|
||||||
# return True, y0
|
|
||||||
|
|
||||||
|
|
||||||
# def rfo_step(gradient, hessian, type):
|
|
||||||
|
|
||||||
# dim = len(gradient)
|
|
||||||
|
|
||||||
# aug_hessian = []
|
|
||||||
# for i in range(dim):
|
|
||||||
# aug_hessian.extend(hessian[i, :].tolist())
|
|
||||||
# aug_hessian.append(gradient[i])
|
|
||||||
|
|
||||||
# aug_hessian.extend(gradient.tolist())
|
|
||||||
# aug_hessian.append(0)
|
|
||||||
|
|
||||||
# aug_hessian = np.array(aug_hessian).reshape(dim + 1, dim + 1)
|
|
||||||
|
|
||||||
# evals, evecs = linalg.eigh(aug_hessian)
|
|
||||||
|
|
||||||
# if type == "min":
|
|
||||||
# step = np.array(evecs[:-1, 0])
|
|
||||||
# elif type == "ts":
|
|
||||||
# step = np.array(evecs[:-1, 1])
|
|
||||||
|
|
||||||
# return step
|
|
||||||
|
|
||||||
|
|
||||||
# def update_trust_radius():
|
|
||||||
|
|
||||||
# if internal["trust_radius"] == None:
|
|
||||||
# internal["trust_radius"] = player["maxstep"]
|
|
||||||
# elif len(internal["energy"]) > 1:
|
|
||||||
# X1 = internal["position"][-1]
|
|
||||||
# X0 = internal["position"][-2]
|
|
||||||
# Dx = X1 - X0
|
|
||||||
# displace = linalg.norm(Dx)
|
|
||||||
# e1 = internal["energy"][-1]
|
|
||||||
# e0 = internal["energy"][-2]
|
|
||||||
# De = e1 - e0
|
|
||||||
# g0 = internal["gradient"][-2]
|
|
||||||
# h0 = internal["hessian"][-2]
|
|
||||||
|
|
||||||
# rho = De / (np.dot(g0, Dx) + 0.5 * np.dot(Dx, np.matmul(h0, Dx.T).T))
|
|
||||||
|
|
||||||
# if rho > 0.75 and displace > 0.8 * internal["trust_radius"]:
|
|
||||||
# internal["trust_radius"] = 2 * internal["trust_radius"]
|
|
||||||
# elif rho < 0.25:
|
|
||||||
# internal["trust_radius"] = 0.25 * displace
|
|
||||||
|
|
||||||
# return
|
|
||||||
|
|
||||||
|
|
||||||
# def interpolate_gradient(G0, G1, y0):
|
|
||||||
|
|
||||||
# DG = G1 - G0
|
|
||||||
# gradient = G0 + y0 * DG
|
|
||||||
|
|
||||||
# return gradient
|
|
||||||
|
|
||||||
|
|
||||||
# def perpendicular_projection(vector, line):
|
|
||||||
|
|
||||||
# direction = line / linalg.norm(line)
|
|
||||||
# projection = np.dot(vector, direction) * direction
|
|
||||||
|
|
||||||
# return vector - projection
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from diceplayer.DPpack.Environment.Molecule import Molecule
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class StepDTO:
|
|
||||||
|
|
||||||
cycle: int = None
|
|
||||||
initcyc: int = None
|
|
||||||
nprocs: int = None
|
|
||||||
ncores: int = None
|
|
||||||
altsteps: int = None
|
|
||||||
switchcyc: int = None
|
|
||||||
opt: str = None
|
|
||||||
nmol: List[int] = None
|
|
||||||
molecule: List[Molecule] = None
|
|
||||||
|
|
||||||
charges: List[float] = None
|
|
||||||
position: List[float] = None
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
def NotNull(requiredArgs=[]):
|
|
||||||
def _NotNull(function):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
for arg in requiredArgs:
|
|
||||||
try:
|
|
||||||
assert (
|
|
||||||
kwargs.get(arg) is not None
|
|
||||||
), "Invalid Config File. Keyword {} is required".format(arg)
|
|
||||||
except AssertionError as err:
|
|
||||||
print(err)
|
|
||||||
return function(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
return _NotNull
|
|
||||||
4
diceplayer/__init__.py
Normal file
4
diceplayer/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from diceplayer.shared.utils.logger import Logger
|
||||||
|
|
||||||
|
|
||||||
|
logger = Logger(__name__)
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
|
from diceplayer.shared.interface.dice_interface import DiceInterface
|
||||||
|
from diceplayer.player import Player
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import logging
|
||||||
import pickle
|
import pickle
|
||||||
import shutil
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import setproctitle
|
|
||||||
|
|
||||||
from diceplayer.DPpack.Player import Player
|
|
||||||
from diceplayer.DPpack.Utils.Misc import *
|
|
||||||
|
|
||||||
__VERSION = "v0.0.1"
|
__VERSION = "v0.0.1"
|
||||||
os.nice(+19)
|
|
||||||
setproctitle.setproctitle("diceplayer-{}".format(__VERSION))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
#### Read and store the arguments passed to the program ####
|
def main():
|
||||||
#### and set the usage and help messages ####
|
"""
|
||||||
|
Read and store the arguments passed to the program
|
||||||
|
and set the usage and help messages
|
||||||
|
"""
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog="Diceplayer")
|
parser = argparse.ArgumentParser(prog="Diceplayer")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -38,173 +38,37 @@ if __name__ == "__main__":
|
|||||||
metavar="OUTFILE",
|
metavar="OUTFILE",
|
||||||
help="output file of diceplayer [default = run.log]"
|
help="output file of diceplayer [default = run.log]"
|
||||||
)
|
)
|
||||||
## Study the option of a parameter for continuing the last process via data from control.in and run.log files
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
#### Open OUTFILE for writing and print keywords and initial info
|
# Open OUTFILE for writing and print keywords and initial info
|
||||||
|
logger.set_logger(args.outfile, logging.INFO)
|
||||||
try:
|
|
||||||
|
|
||||||
if args.opt_continue and os.path.exists(args.outfile):
|
|
||||||
|
|
||||||
save = pickle.load(open("latest-step.pkl", "rb"))
|
|
||||||
|
|
||||||
if os.path.isfile(args.outfile + ".backup"):
|
|
||||||
os.remove(args.outfile + ".backup")
|
|
||||||
|
|
||||||
os.rename(args.outfile, args.outfile + ".backup")
|
|
||||||
outfile = open(args.outfile, "w", 1)
|
|
||||||
|
|
||||||
elif os.path.exists(args.outfile):
|
|
||||||
os.rename(args.outfile, args.outfile + ".backup")
|
|
||||||
outfile = open(args.outfile, "w", 1)
|
|
||||||
else:
|
|
||||||
outfile = open(args.outfile, "w", 1)
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
if os.path.exists(args.infile):
|
|
||||||
infile = open(args.infile, "r")
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
#### Read and check the keywords in INFILE
|
|
||||||
|
|
||||||
player = Player(infile, outfile)
|
|
||||||
|
|
||||||
player.read_keywords()
|
|
||||||
|
|
||||||
player.check_keywords()
|
|
||||||
player.print_keywords()
|
|
||||||
|
|
||||||
if args.opt_continue:
|
if args.opt_continue:
|
||||||
player.initcyc = save[0] + 1
|
player = Player.from_save()
|
||||||
player.system = save[1]
|
|
||||||
else:
|
else:
|
||||||
player.initcyc = 1
|
player = Player.from_file(args.infile)
|
||||||
player.read_potential()
|
|
||||||
|
|
||||||
#### Check whether the executables are in the path
|
player.read_potentials()
|
||||||
#### and print potential to Log File
|
|
||||||
|
|
||||||
player.check_executables()
|
player.create_simulation_dir()
|
||||||
|
player.create_geoms_file()
|
||||||
|
|
||||||
player.print_potential()
|
player.print_keywords()
|
||||||
|
|
||||||
#### Bring the molecules to standard orientation and prints info about them
|
player.print_potentials()
|
||||||
|
|
||||||
for i in range(len(player.system.molecule)):
|
player.prepare_system()
|
||||||
|
|
||||||
player.outfile.write(
|
player.start()
|
||||||
"\nMolecule type {} - {}:\n\n".format(
|
|
||||||
i + 1, player.system.molecule[i].molname
|
|
||||||
)
|
|
||||||
)
|
|
||||||
player.system.molecule[i].print_mol_info(player.outfile)
|
|
||||||
player.outfile.write(
|
|
||||||
" Translating and rotating molecule to standard orientation..."
|
|
||||||
)
|
|
||||||
player.system.molecule[i].standard_orientation()
|
|
||||||
player.outfile.write(" Done\n\n New values:\n")
|
|
||||||
player.system.molecule[i].print_mol_info(player.outfile)
|
|
||||||
|
|
||||||
player.outfile.write(90 * "=")
|
logger.info("\n+" + 88 * "-" + "+\n")
|
||||||
player.outfile.write("\n")
|
|
||||||
|
|
||||||
if not args.opt_continue:
|
player.print_results()
|
||||||
make_simulation_dir()
|
|
||||||
else:
|
|
||||||
simdir = "simfiles"
|
|
||||||
stepdir = "step{:02d}".format(player.initcyc)
|
|
||||||
if os.path.exists(simdir + os.sep + stepdir):
|
|
||||||
shutil.rmtree(simdir + os.sep + stepdir)
|
|
||||||
|
|
||||||
#### Open the geoms.xyz file and prints the initial geometry if starting from zero
|
logger.info("\n+" + 88 * "-" + "+\n")
|
||||||
|
|
||||||
if player.initcyc == 1:
|
logger.info("Diceplayer finished successfully \n")
|
||||||
try:
|
|
||||||
path = "geoms.xyz"
|
|
||||||
geomsfh = open(path, "w", 1)
|
|
||||||
except EnvironmentError as err:
|
|
||||||
sys.exit(err)
|
|
||||||
player.system.print_geom(0, geomsfh)
|
|
||||||
geomsfh.write(40 * "-" + "\n")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
path = "geoms.xyz"
|
|
||||||
geomsfh = open(path, "a", 1)
|
|
||||||
except EnvironmentError as err:
|
|
||||||
sys.exit(err)
|
|
||||||
|
|
||||||
player.outfile.write("\nStarting the iterative process.\n")
|
|
||||||
|
|
||||||
## Initial position (in Bohr)
|
if __name__ == "__main__":
|
||||||
position = player.system.molecule[0].read_position()
|
main()
|
||||||
|
|
||||||
## If restarting, read the last gradient and hessian
|
|
||||||
# if player.initcyc > 1:
|
|
||||||
# if player.qmprog in ("g03", "g09", "g16"):
|
|
||||||
# Gaussian.read_forces("grad_hessian.dat")
|
|
||||||
# Gaussian.read_hessian_fchk("grad_hessian.dat")
|
|
||||||
|
|
||||||
# if player['qmprog'] == "molcas":
|
|
||||||
# Molcas.read_forces("grad_hessian.dat")
|
|
||||||
# Molcas.read_hessian("grad_hessian.dat")
|
|
||||||
|
|
||||||
###
|
|
||||||
### Start the iterative process
|
|
||||||
###
|
|
||||||
|
|
||||||
player.outfile.write("\n" + 90 * "-" + "\n")
|
|
||||||
|
|
||||||
for cycle in range(player.initcyc, player.initcyc + player.maxcyc):
|
|
||||||
|
|
||||||
player.outfile.write("{} Step # {}\n".format(40 * " ", cycle))
|
|
||||||
player.outfile.write(90 * "-" + "\n\n")
|
|
||||||
|
|
||||||
make_step_dir(cycle)
|
|
||||||
|
|
||||||
####
|
|
||||||
#### Start block of parallel simulations
|
|
||||||
####
|
|
||||||
|
|
||||||
player.dice_start(cycle)
|
|
||||||
|
|
||||||
###
|
|
||||||
### End of parallel simulations block
|
|
||||||
###
|
|
||||||
|
|
||||||
## After ASEC is built, compress files bigger than 1MB
|
|
||||||
for proc in range(1, player.nprocs + 1):
|
|
||||||
path = "simfiles"+os.sep+"step{:02d}".format(cycle) + os.sep + "p{:02d}".format(proc)
|
|
||||||
compress_files_1mb(path)
|
|
||||||
|
|
||||||
###
|
|
||||||
### Start QM calculation
|
|
||||||
###
|
|
||||||
|
|
||||||
player.gaussian_start(cycle, geomsfh)
|
|
||||||
|
|
||||||
player.system.print_geom(cycle, geomsfh)
|
|
||||||
geomsfh.write(40 * "-" + "\n")
|
|
||||||
|
|
||||||
player.outfile.write("\n+" + 88 * "-" + "+\n")
|
|
||||||
|
|
||||||
pickle.dump([cycle, player.system], open("latest-step.pkl", "wb"))
|
|
||||||
####
|
|
||||||
#### End of the iterative process
|
|
||||||
####
|
|
||||||
|
|
||||||
## imprimir ultimas mensagens, criar um arquivo de potencial para ser usado em eventual
|
|
||||||
## continuacao, fechar arquivos (geoms.xyz, run.log, ...)
|
|
||||||
|
|
||||||
player.outfile.write("\nDiceplayer finished normally!\n")
|
|
||||||
player.outfile.close()
|
|
||||||
####
|
|
||||||
#### End of the program
|
|
||||||
####
|
|
||||||
|
|||||||
480
diceplayer/player.py
Normal file
480
diceplayer/player.py
Normal file
@@ -0,0 +1,480 @@
|
|||||||
|
from diceplayer.shared.interface.gaussian_interface import GaussianInterface
|
||||||
|
from diceplayer.shared.interface.dice_interface import DiceInterface
|
||||||
|
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||||
|
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||||
|
from diceplayer.shared.config.player_config import PlayerConfig
|
||||||
|
from diceplayer.shared.config.dice_config import DiceConfig
|
||||||
|
from diceplayer.shared.utils.misc import weekday_date_time
|
||||||
|
from diceplayer.shared.environment.molecule import Molecule
|
||||||
|
from diceplayer.shared.environment.system import System
|
||||||
|
from diceplayer.shared.environment.atom import Atom
|
||||||
|
from diceplayer.shared.utils.ptable import atomsymb
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
from dataclasses import fields
|
||||||
|
from typing import Type, Tuple
|
||||||
|
from pathlib import Path
|
||||||
|
import pickle
|
||||||
|
import yaml
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
ENV = ["OMP_STACKSIZE"]
|
||||||
|
|
||||||
|
|
||||||
|
class Player:
|
||||||
|
def __init__(self, infile: str = None, optimization: bool = False):
|
||||||
|
if infile is None and optimization is False:
|
||||||
|
raise ValueError("Must specify either infile or optimization")
|
||||||
|
|
||||||
|
elif infile is not None:
|
||||||
|
self.config = self.set_config(
|
||||||
|
self.read_keywords(infile)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.system = System()
|
||||||
|
|
||||||
|
self.initial_cycle = 1
|
||||||
|
|
||||||
|
elif optimization is True:
|
||||||
|
save = self.load_run_from_pickle()
|
||||||
|
|
||||||
|
self.config = save[0]
|
||||||
|
|
||||||
|
self.system = save[1]
|
||||||
|
|
||||||
|
self.initial_cycle = save[2] + 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError("Must specify either infile or config")
|
||||||
|
|
||||||
|
self.dice_interface = DiceInterface()
|
||||||
|
self.gaussian_interface = GaussianInterface()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
logger.info(
|
||||||
|
"==========================================================================================\n"
|
||||||
|
"Starting the iterative process.\n"
|
||||||
|
"==========================================================================================\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
for cycle in range(self.initial_cycle, self.initial_cycle + self.config.maxcyc):
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"------------------------------------------------------------------------------------------\n"
|
||||||
|
f" Step # {cycle}\n"
|
||||||
|
f"------------------------------------------------------------------------------------------\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.dice_start(cycle)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.gaussian_start(cycle)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.save_run_in_pickle(cycle)
|
||||||
|
|
||||||
|
def prepare_system(self):
|
||||||
|
for i, mol in enumerate(self.system.molecule):
|
||||||
|
logger.info(
|
||||||
|
f"Molecule {i + 1} - {mol.molname}"
|
||||||
|
)
|
||||||
|
|
||||||
|
mol.print_mol_info()
|
||||||
|
logger.info(
|
||||||
|
"\n Translating and rotating molecule to standard orientation..."
|
||||||
|
)
|
||||||
|
|
||||||
|
mol.standard_orientation()
|
||||||
|
logger.info("\n Done")
|
||||||
|
logger.info("\nNew values:\n")
|
||||||
|
mol.print_mol_info()
|
||||||
|
|
||||||
|
logger.info("\n")
|
||||||
|
|
||||||
|
def create_simulation_dir(self):
|
||||||
|
simulation_dir_path = Path(self.config.simulation_dir)
|
||||||
|
if simulation_dir_path.exists():
|
||||||
|
raise FileExistsError(
|
||||||
|
f"Error: a file or a directory {self.config.simulation_dir} already exists,"
|
||||||
|
f" move or delete the simfiles directory to continue."
|
||||||
|
)
|
||||||
|
simulation_dir_path.mkdir()
|
||||||
|
|
||||||
|
def create_geoms_file(self):
|
||||||
|
geoms_file_path = Path(self.config.geoms_file)
|
||||||
|
if geoms_file_path.exists():
|
||||||
|
raise FileExistsError(
|
||||||
|
f"Error: a file or a directory {self.config.geoms_file} already exists,"
|
||||||
|
f" move or delete the simfiles directory to continue."
|
||||||
|
)
|
||||||
|
geoms_file_path.touch()
|
||||||
|
|
||||||
|
def print_keywords(self) -> None:
|
||||||
|
|
||||||
|
def log_keywords(config: Dataclass, dto: Type[Dataclass]):
|
||||||
|
for key in sorted(list(map(lambda f: f.name, fields(dto)))):
|
||||||
|
if getattr(config, key) is not None:
|
||||||
|
if isinstance(getattr(config, key), list):
|
||||||
|
string = " ".join(str(x) for x in getattr(config, key))
|
||||||
|
logger.info(f"{key} = [ {string} ]")
|
||||||
|
else:
|
||||||
|
logger.info(f"{key} = {getattr(config, key)}")
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"##########################################################################################\n"
|
||||||
|
"############# Welcome to DICEPLAYER version 1.0 #############\n"
|
||||||
|
"##########################################################################################\n"
|
||||||
|
)
|
||||||
|
logger.info("Your python version is {}\n".format(sys.version))
|
||||||
|
logger.info("Program started on {}\n".format(weekday_date_time()))
|
||||||
|
logger.info("Environment variables:")
|
||||||
|
for var in ENV:
|
||||||
|
logger.info(
|
||||||
|
"{} = {}\n".format(
|
||||||
|
var, (os.environ[var] if var in os.environ else "Not set")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"------------------------------------------------------------------------------------------\n"
|
||||||
|
" DICE variables being used in this run:\n"
|
||||||
|
"------------------------------------------------------------------------------------------\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
log_keywords(self.config.dice, DiceConfig)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"------------------------------------------------------------------------------------------\n"
|
||||||
|
" GAUSSIAN variables being used in this run:\n"
|
||||||
|
"------------------------------------------------------------------------------------------\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
log_keywords(self.config.gaussian, GaussianDTO)
|
||||||
|
|
||||||
|
logger.info("\n")
|
||||||
|
|
||||||
|
def read_potentials(self):
|
||||||
|
ljname_path = Path(self.config.dice.ljname)
|
||||||
|
if ljname_path.exists():
|
||||||
|
with open(self.config.dice.ljname) as file:
|
||||||
|
ljc_data = file.readlines()
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Potential file {self.config.dice.ljname} not found."
|
||||||
|
)
|
||||||
|
|
||||||
|
combrule = ljc_data.pop(0).split()[0]
|
||||||
|
if combrule not in ("*", "+"):
|
||||||
|
sys.exit(
|
||||||
|
"Error: expected a '*' or a '+' sign in 1st line of file {}".format(
|
||||||
|
self.config.dice.ljname
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.config.dice.combrule = combrule
|
||||||
|
|
||||||
|
ntypes = ljc_data.pop(0).split()[0]
|
||||||
|
if not ntypes.isdigit():
|
||||||
|
sys.exit(
|
||||||
|
"Error: expected an integer in the 2nd line of file {}".format(
|
||||||
|
self.config.dice.ljname
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ntypes = int(ntypes)
|
||||||
|
|
||||||
|
if ntypes != len(self.config.dice.nmol):
|
||||||
|
sys.exit(
|
||||||
|
f"Error: number of molecule types in file {self.config.dice.ljname} "
|
||||||
|
f"must match that of 'nmol' keyword in config file"
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(ntypes):
|
||||||
|
|
||||||
|
try:
|
||||||
|
nsites, molname = ljc_data.pop(0).split()[:2]
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(
|
||||||
|
f"Error: expected nsites and molname for the molecule type {i + 1}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not nsites.isdigit():
|
||||||
|
raise ValueError(
|
||||||
|
f"Error: expected nsites to be an integer for molecule type {i + 1}"
|
||||||
|
)
|
||||||
|
|
||||||
|
nsites = int(nsites)
|
||||||
|
self.system.add_type(Molecule(molname))
|
||||||
|
|
||||||
|
atom_fields = ["lbl", "na", "rx", "ry", "rz", "chg", "eps", "sig"]
|
||||||
|
for j in range(nsites):
|
||||||
|
new_atom = dict(zip(
|
||||||
|
atom_fields,
|
||||||
|
ljc_data.pop(0).split()
|
||||||
|
))
|
||||||
|
self.system.molecule[i].add_atom(
|
||||||
|
Atom(**self.validate_atom_dict(i, j, new_atom))
|
||||||
|
)
|
||||||
|
|
||||||
|
def print_potentials(self) -> None:
|
||||||
|
formatstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f} {:>9.4f}"
|
||||||
|
logger.info(
|
||||||
|
"==========================================================================================\n"
|
||||||
|
f" Potential parameters from file {self.config.dice.ljname}:\n"
|
||||||
|
"------------------------------------------------------------------------------------------"
|
||||||
|
"\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Combination rule: {self.config.dice.combrule}")
|
||||||
|
logger.info(
|
||||||
|
f"Types of molecules: {len(self.system.molecule)}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for mol in self.system.molecule:
|
||||||
|
i += 1
|
||||||
|
logger.info(
|
||||||
|
"{} atoms in molecule type {}:".format(len(mol.atom), i)
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"---------------------------------------------------------------------------------"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Lbl AN X Y Z Charge Epsilon Sigma Mass"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"---------------------------------------------------------------------------------"
|
||||||
|
)
|
||||||
|
|
||||||
|
for atom in mol.atom:
|
||||||
|
logger.info(
|
||||||
|
formatstr.format(
|
||||||
|
atom.lbl,
|
||||||
|
atom.na,
|
||||||
|
atom.rx,
|
||||||
|
atom.ry,
|
||||||
|
atom.rz,
|
||||||
|
atom.chg,
|
||||||
|
atom.eps,
|
||||||
|
atom.sig,
|
||||||
|
atom.mass,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("\n")
|
||||||
|
|
||||||
|
def dice_start(self, cycle: int):
|
||||||
|
self.dice_interface.configure(
|
||||||
|
self.config,
|
||||||
|
self.system,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.dice_interface.start(cycle)
|
||||||
|
|
||||||
|
self.dice_interface.reset()
|
||||||
|
|
||||||
|
def gaussian_start(self, cycle: int):
|
||||||
|
self.gaussian_interface.configure(
|
||||||
|
self.config,
|
||||||
|
self.system,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.gaussian_interface.start(cycle)
|
||||||
|
|
||||||
|
self.gaussian_interface.reset()
|
||||||
|
|
||||||
|
if self.config.opt:
|
||||||
|
if 'position' not in result:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Optimization failed. No position found in result.'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.system.update_molecule(result['position'])
|
||||||
|
|
||||||
|
else:
|
||||||
|
if 'charges' not in result:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Charges optimization failed. No charges found in result.'
|
||||||
|
)
|
||||||
|
|
||||||
|
diff = self.system.molecule[0] \
|
||||||
|
.update_charges(result['charges'])
|
||||||
|
|
||||||
|
self.system.print_charges_and_dipole(cycle)
|
||||||
|
self.print_geoms(cycle)
|
||||||
|
|
||||||
|
if diff < self.config.gaussian.chg_tol:
|
||||||
|
logger.info(
|
||||||
|
f'Charges converged after {cycle} cycles.'
|
||||||
|
)
|
||||||
|
raise StopIteration()
|
||||||
|
|
||||||
|
def print_geoms(self, cycle: int):
|
||||||
|
with open(self.config.geoms_file, 'a') as file:
|
||||||
|
file.write(f'Cycle # {cycle}\n')
|
||||||
|
|
||||||
|
for atom in self.system.molecule[0].atom:
|
||||||
|
symbol = atomsymb[atom.na]
|
||||||
|
file.write(
|
||||||
|
f'{symbol:<2s} {atom.rx:>10.6f} {atom.ry:>10.6f} {atom.rz:>10.6f}\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
file.write('\n')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate_atom_dict(molecule_type, molecule_site, atom_dict: dict) -> dict:
|
||||||
|
molecule_type += 1
|
||||||
|
molecule_site += 1
|
||||||
|
|
||||||
|
if len(atom_dict) < 8:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid number of fields for site {molecule_site} for molecule type {molecule_type}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['lbl'] = int(atom_dict['lbl'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid lbl fields for site {molecule_site} for molecule type {molecule_type}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['na'] = int(atom_dict['na'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid na fields for site {molecule_site} for molecule type {molecule_type}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['rx'] = float(atom_dict['rx'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid rx fields for site {molecule_site} for molecule type {molecule_type}. '
|
||||||
|
f'Value must be a float.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['ry'] = float(atom_dict['ry'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid ry fields for site {molecule_site} for molecule type {molecule_type}. '
|
||||||
|
f'Value must be a float.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['rz'] = float(atom_dict['rz'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid rz fields for site {molecule_site} for molecule type {molecule_type}. '
|
||||||
|
f'Value must be a float.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['chg'] = float(atom_dict['chg'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid chg fields for site {molecule_site} for molecule type {molecule_type}. '
|
||||||
|
f'Value must be a float.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['eps'] = float(atom_dict['eps'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid eps fields for site {molecule_site} for molecule type {molecule_type}. '
|
||||||
|
f'Value must be a float.'
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
atom_dict['sig'] = float(atom_dict['sig'])
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(
|
||||||
|
f'Invalid sig fields for site {molecule_site} for molecule type {molecule_type}. '
|
||||||
|
f'Value must be a float.'
|
||||||
|
)
|
||||||
|
|
||||||
|
return atom_dict
|
||||||
|
|
||||||
|
def print_results(self):
|
||||||
|
formatstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f} {:>9.4f}"
|
||||||
|
|
||||||
|
mol = self.system.molecule[0]
|
||||||
|
logger.info(
|
||||||
|
"{} atoms in molecule type {}:".format(len(mol.atom), 1)
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"---------------------------------------------------------------------------------"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Lbl AN X Y Z Charge Epsilon Sigma Mass"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"---------------------------------------------------------------------------------"
|
||||||
|
)
|
||||||
|
|
||||||
|
for atom in mol.atom:
|
||||||
|
logger.info(
|
||||||
|
formatstr.format(
|
||||||
|
atom.lbl,
|
||||||
|
atom.na,
|
||||||
|
atom.rx,
|
||||||
|
atom.ry,
|
||||||
|
atom.rz,
|
||||||
|
atom.chg,
|
||||||
|
atom.eps,
|
||||||
|
atom.sig,
|
||||||
|
atom.mass,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("\n")
|
||||||
|
|
||||||
|
def save_run_in_pickle(self, cycle):
|
||||||
|
try:
|
||||||
|
with open('latest-step.pkl', 'wb') as pickle_file:
|
||||||
|
pickle.dump(
|
||||||
|
(self.config, self.system, cycle),
|
||||||
|
pickle_file
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'Could not save pickle file latest-step.pkl.'
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_run_from_pickle() -> Tuple[PlayerConfig, System, int]:
|
||||||
|
pickle_path = Path("latest-step.pkl")
|
||||||
|
try:
|
||||||
|
with open(pickle_path, 'rb') as pickle_file:
|
||||||
|
save = pickle.load(pickle_file)
|
||||||
|
return save[0], save[1], save[2] + 1
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'Could not load pickle file {pickle_path}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_config(data: dict) -> PlayerConfig:
|
||||||
|
return PlayerConfig.from_dict(data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read_keywords(infile) -> dict:
|
||||||
|
with open(infile, 'r') as yml_file:
|
||||||
|
config = yaml.load(yml_file, Loader=yaml.SafeLoader)
|
||||||
|
|
||||||
|
if "diceplayer" in config:
|
||||||
|
return config.get("diceplayer")
|
||||||
|
|
||||||
|
raise RuntimeError(
|
||||||
|
f'Could not find diceplayer section in {infile}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_file(cls, infile: str) -> 'Player':
|
||||||
|
return cls(infile=infile)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_save(cls):
|
||||||
|
return cls(optimization=True)
|
||||||
56
diceplayer/shared/config/dice_config.py
Normal file
56
diceplayer/shared/config/dice_config.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from dacite import from_dict
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DiceConfig(Dataclass):
|
||||||
|
"""
|
||||||
|
Data Transfer Object for the Dice configuration.
|
||||||
|
"""
|
||||||
|
ljname: str
|
||||||
|
outname: str
|
||||||
|
dens: float
|
||||||
|
nmol: List[int]
|
||||||
|
nstep: List[int]
|
||||||
|
|
||||||
|
upbuf = 360
|
||||||
|
combrule = "*"
|
||||||
|
isave: int = 1000
|
||||||
|
press: float = 1.0
|
||||||
|
temp: float = 300.0
|
||||||
|
progname: str = "dice"
|
||||||
|
randominit: str = 'first'
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
|
||||||
|
if not isinstance(self.ljname, str):
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'ljname' keyword not specified in config file"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not isinstance(self.outname, str):
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'outname' keyword not specified in config file"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not isinstance(self.dens, float):
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'dens' keyword not specified in config file"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not isinstance(self.nmol, list):
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'nmol' keyword not defined appropriately in config file"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not isinstance(self.nstep, list) or len(self.nstep) not in (2, 3):
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'nstep' keyword not defined appropriately in config file"
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, param: dict):
|
||||||
|
return from_dict(DiceConfig, param)
|
||||||
32
diceplayer/shared/config/gaussian_config.py
Normal file
32
diceplayer/shared/config/gaussian_config.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from dacite import from_dict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GaussianDTO(Dataclass):
|
||||||
|
"""
|
||||||
|
Data Transfer Object for the Gaussian configuration.
|
||||||
|
"""
|
||||||
|
level: str
|
||||||
|
qmprog: str
|
||||||
|
|
||||||
|
chgmult = [0, 1]
|
||||||
|
pop: str = 'chelpg'
|
||||||
|
chg_tol: float = 0.01
|
||||||
|
keywords: str = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.qmprog not in ("g03", "g09", "g16"):
|
||||||
|
raise ValueError(
|
||||||
|
"Error: invalid qmprog value."
|
||||||
|
)
|
||||||
|
if self.level is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'level' keyword not specified in config file."
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, param: dict):
|
||||||
|
return from_dict(GaussianDTO, param)
|
||||||
48
diceplayer/shared/config/player_config.py
Normal file
48
diceplayer/shared/config/player_config.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||||
|
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||||
|
from diceplayer.shared.config.dice_config import DiceConfig
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from dacite import from_dict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlayerConfig(Dataclass):
|
||||||
|
"""
|
||||||
|
Data Transfer Object for the player configuration.
|
||||||
|
"""
|
||||||
|
opt: bool
|
||||||
|
maxcyc: int
|
||||||
|
nprocs: int
|
||||||
|
ncores: int
|
||||||
|
|
||||||
|
dice: DiceConfig
|
||||||
|
gaussian: GaussianDTO
|
||||||
|
|
||||||
|
mem: int = None
|
||||||
|
switchcyc: int = 3
|
||||||
|
qmprog: str = 'g16'
|
||||||
|
altsteps: int = 20000
|
||||||
|
geoms_file = 'geoms.xyz'
|
||||||
|
simulation_dir = 'simfiles'
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
MIN_STEP = 20000
|
||||||
|
# altsteps value is always the nearest multiple of 1000
|
||||||
|
self.altsteps = round(max(MIN_STEP, self.altsteps) / 1000) * 1000
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, param: dict):
|
||||||
|
if param['dice'] is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'dice' keyword not specified in config file."
|
||||||
|
)
|
||||||
|
param['dice'] = DiceConfig.from_dict(param['dice'])
|
||||||
|
|
||||||
|
if param['gaussian'] is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Error: 'gaussian' keyword not specified in config file."
|
||||||
|
)
|
||||||
|
param['gaussian'] = GaussianDTO.from_dict(param['gaussian'])
|
||||||
|
|
||||||
|
return from_dict(PlayerConfig, param)
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
from diceplayer.DPpack.Utils.PTable import *
|
from diceplayer.shared.utils.ptable import atommass
|
||||||
from diceplayer.DPpack.Utils.Misc import *
|
|
||||||
|
|
||||||
|
|
||||||
class Atom:
|
class Atom:
|
||||||
@@ -16,16 +15,17 @@ class Atom:
|
|||||||
eps (float): quantum number epsilon of the represented atom.
|
eps (float): quantum number epsilon of the represented atom.
|
||||||
sig (float): quantum number sigma of the represented atom.
|
sig (float): quantum number sigma of the represented atom.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
lbl: int,
|
lbl: int,
|
||||||
na: int,
|
na: int,
|
||||||
rx: float,
|
rx: float,
|
||||||
ry: float,
|
ry: float,
|
||||||
rz: float,
|
rz: float,
|
||||||
chg: float,
|
chg: float,
|
||||||
eps: float,
|
eps: float,
|
||||||
sig: float,
|
sig: float,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
The constructor function __init__ is used to create new instances of the Atom class.
|
The constructor function __init__ is used to create new instances of the Atom class.
|
||||||
@@ -1,25 +1,20 @@
|
|||||||
from diceplayer.DPpack.Utils.PTable import *
|
from __future__ import annotations
|
||||||
from diceplayer.DPpack.Utils.Misc import *
|
|
||||||
|
|
||||||
from diceplayer.DPpack.Environment.Atom import Atom
|
from diceplayer.shared.utils.ptable import ghost_number
|
||||||
|
from diceplayer.shared.environment.atom import Atom
|
||||||
|
from diceplayer.shared.utils.misc import BOHR2ANG
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
from typing import IO, Any, Final, Tuple, List, TextIO
|
from nptyping import NDArray, Shape, Float
|
||||||
from nptyping import Float, NDArray, Shape
|
from numpy.linalg import linalg
|
||||||
|
|
||||||
from numpy import linalg
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from typing import List, Any, Tuple, Union
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import sys, math
|
import logging
|
||||||
import sys
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
|
||||||
""" Constants of unit conversion """
|
|
||||||
BOHR2ANG: Final[float] = 0.52917721092
|
|
||||||
ANG2BOHR: Final[float] = 1 / BOHR2ANG
|
|
||||||
|
|
||||||
|
|
||||||
class Molecule:
|
class Molecule:
|
||||||
"""
|
"""
|
||||||
Molecule class declaration. This class is used throughout the DicePlayer program to represent molecules.
|
Molecule class declaration. This class is used throughout the DicePlayer program to represent molecules.
|
||||||
@@ -52,9 +47,9 @@ class Molecule:
|
|||||||
|
|
||||||
self.ghost_atoms: List[Atom] = []
|
self.ghost_atoms: List[Atom] = []
|
||||||
self.lp_atoms: List[Atom] = []
|
self.lp_atoms: List[Atom] = []
|
||||||
|
|
||||||
self.total_mass: int = 0
|
self.total_mass: int = 0
|
||||||
self.com: NDArray[Any, Any] = None
|
self.com: Union[None, NDArray[Any, Any]] = None
|
||||||
|
|
||||||
def add_atom(self, a: Atom) -> None:
|
def add_atom(self, a: Atom) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -67,13 +62,9 @@ class Molecule:
|
|||||||
self.atom.append(a)
|
self.atom.append(a)
|
||||||
self.total_mass += a.mass
|
self.total_mass += a.mass
|
||||||
|
|
||||||
if a.na == ghost_number:
|
|
||||||
|
|
||||||
self.ghost_atoms.append(self.atom.index(a))
|
|
||||||
|
|
||||||
self.center_of_mass()
|
self.center_of_mass()
|
||||||
|
|
||||||
def center_of_mass(self) -> None:
|
def center_of_mass(self) -> NDArray[Any, Any]:
|
||||||
"""
|
"""
|
||||||
Calculates the center of mass of the molecule
|
Calculates the center of mass of the molecule
|
||||||
"""
|
"""
|
||||||
@@ -81,24 +72,23 @@ class Molecule:
|
|||||||
self.com = np.zeros(3)
|
self.com = np.zeros(3)
|
||||||
|
|
||||||
for atom in self.atom:
|
for atom in self.atom:
|
||||||
|
|
||||||
self.com += atom.mass * np.array([atom.rx, atom.ry, atom.rz])
|
self.com += atom.mass * np.array([atom.rx, atom.ry, atom.rz])
|
||||||
|
|
||||||
self.com = self.com / self.total_mass
|
self.com = self.com / self.total_mass
|
||||||
|
|
||||||
|
return self.com
|
||||||
|
|
||||||
def center_of_mass_to_origin(self) -> None:
|
def center_of_mass_to_origin(self) -> None:
|
||||||
"""
|
"""
|
||||||
Updated positions based on the center of mass of the molecule
|
Updated positions based on the center of mass of the molecule
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.center_of_mass()
|
|
||||||
|
|
||||||
for atom in self.atom:
|
for atom in self.atom:
|
||||||
|
|
||||||
atom.rx -= self.com[0]
|
atom.rx -= self.com[0]
|
||||||
atom.ry -= self.com[1]
|
atom.ry -= self.com[1]
|
||||||
atom.rz -= self.com[2]
|
atom.rz -= self.com[2]
|
||||||
|
|
||||||
|
self.center_of_mass()
|
||||||
|
|
||||||
def charges_and_dipole(self) -> List[float]:
|
def charges_and_dipole(self) -> List[float]:
|
||||||
"""
|
"""
|
||||||
Calculates the charges and dipole of the molecule atoms
|
Calculates the charges and dipole of the molecule atoms
|
||||||
@@ -121,7 +111,7 @@ class Molecule:
|
|||||||
|
|
||||||
return [charge, dipole[0], dipole[1], dipole[2], total_dipole]
|
return [charge, dipole[0], dipole[1], dipole[2], total_dipole]
|
||||||
|
|
||||||
def distances_between_atoms(self) -> NDArray[Shape["Any,Any"],Float]:
|
def distances_between_atoms(self) -> NDArray[Shape["Any,Any"], Float]:
|
||||||
"""
|
"""
|
||||||
Calculates distances between the atoms of the molecule
|
Calculates distances between the atoms of the molecule
|
||||||
|
|
||||||
@@ -131,16 +121,15 @@ class Molecule:
|
|||||||
|
|
||||||
distances = []
|
distances = []
|
||||||
dim = len(self.atom)
|
dim = len(self.atom)
|
||||||
for atom1 in self.atom:
|
for index1, atom1 in enumerate(self.atom):
|
||||||
if atom1.na != ghost_number:
|
for index2, atom2 in enumerate(self.atom):
|
||||||
for atom2 in self.atom:
|
if index1 != index2:
|
||||||
if atom2.na != ghost_number:
|
dx = atom1.rx - atom2.rx
|
||||||
dx = atom1.rx - atom2.rx
|
dy = atom1.ry - atom2.ry
|
||||||
dy = atom1.ry - atom2.ry
|
dz = atom1.rz - atom2.rz
|
||||||
dz = atom1.rz - atom2.rz
|
distances.append(math.sqrt(dx ** 2 + dy ** 2 + dz ** 2))
|
||||||
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
|
|
||||||
|
|
||||||
return np.array(distances).reshape(dim, dim)
|
return np.array(distances).reshape(dim, dim-1)
|
||||||
|
|
||||||
def inertia_tensor(self) -> NDArray[Shape["3, 3"], Float]:
|
def inertia_tensor(self) -> NDArray[Shape["3, 3"], Float]:
|
||||||
"""
|
"""
|
||||||
@@ -154,14 +143,13 @@ class Molecule:
|
|||||||
Ixx = Ixy = Ixz = Iyy = Iyz = Izz = 0.0
|
Ixx = Ixy = Ixz = Iyy = Iyz = Izz = 0.0
|
||||||
|
|
||||||
for atom in self.atom:
|
for atom in self.atom:
|
||||||
|
|
||||||
dx = atom.rx - self.com[0]
|
dx = atom.rx - self.com[0]
|
||||||
dy = atom.ry - self.com[1]
|
dy = atom.ry - self.com[1]
|
||||||
dz = atom.rz - self.com[2]
|
dz = atom.rz - self.com[2]
|
||||||
|
|
||||||
Ixx += atom.mass * (dy**2 + dz**2)
|
Ixx += atom.mass * (dy ** 2 + dz ** 2)
|
||||||
Iyy += atom.mass * (dz**2 + dx**2)
|
Iyy += atom.mass * (dz ** 2 + dx ** 2)
|
||||||
Izz += atom.mass * (dx**2 + dy**2)
|
Izz += atom.mass * (dx ** 2 + dy ** 2)
|
||||||
|
|
||||||
Ixy += atom.mass * dx * dy * -1
|
Ixy += atom.mass * dx * dy * -1
|
||||||
Ixz += atom.mass * dx * dz * -1
|
Ixz += atom.mass * dx * dz * -1
|
||||||
@@ -169,40 +157,6 @@ class Molecule:
|
|||||||
|
|
||||||
return np.array([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]])
|
return np.array([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]])
|
||||||
|
|
||||||
def axes(self) -> NDArray[Shape["3, 3"], Float]:
|
|
||||||
"""
|
|
||||||
Calculates the axes of the molecule
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NDArray[Shape["3, 3"], Float]: Returns the axes of molecule
|
|
||||||
"""
|
|
||||||
|
|
||||||
eixos = np.zeros(3)
|
|
||||||
if len(self.atom) == 2:
|
|
||||||
|
|
||||||
position1 = np.array([self.atom[0].rx, self.atom[0].ry, self.atom[0].rz])
|
|
||||||
position2 = np.array([self.atom[1].rx, self.atom[1].ry, self.atom[1].rz])
|
|
||||||
eixos = position2 - position1
|
|
||||||
eixos /= linalg.norm(eixos)
|
|
||||||
|
|
||||||
elif len(self.atom) > 2:
|
|
||||||
|
|
||||||
position1 = np.array([self.atom[0].rx, self.atom[0].ry, self.atom[0].rz])
|
|
||||||
position2 = np.array([self.atom[1].rx, self.atom[1].ry, self.atom[1].rz])
|
|
||||||
position3 = np.array([self.atom[2].rx, self.atom[2].ry, self.atom[2].rz])
|
|
||||||
v1 = position2 - position1
|
|
||||||
v2 = position3 - position1
|
|
||||||
v3 = np.cross(v1, v2)
|
|
||||||
v2 = np.cross(v1, v3)
|
|
||||||
v1 /= linalg.norm(v1)
|
|
||||||
v2 /= linalg.norm(v2)
|
|
||||||
v3 /= linalg.norm(v3)
|
|
||||||
eixos = np.array(
|
|
||||||
[[v1[0], v1[1], v1[2]], [v2[0], v2[1], v2[2]], [v3[0], v3[1], v3[2]]]
|
|
||||||
)
|
|
||||||
|
|
||||||
return eixos
|
|
||||||
|
|
||||||
def principal_axes(self) -> Tuple[np.ndarray, np.ndarray]:
|
def principal_axes(self) -> Tuple[np.ndarray, np.ndarray]:
|
||||||
"""
|
"""
|
||||||
Calculates the principal axes of the molecule
|
Calculates the principal axes of the molecule
|
||||||
@@ -214,8 +168,8 @@ class Molecule:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
evals, evecs = linalg.eigh(self.inertia_tensor())
|
evals, evecs = linalg.eigh(self.inertia_tensor())
|
||||||
except:
|
except ValueError:
|
||||||
sys.exit("Error: diagonalization of inertia tensor did not converge")
|
raise RuntimeError("Error: diagonalization of inertia tensor did not converge")
|
||||||
|
|
||||||
return evals, evecs
|
return evals, evecs
|
||||||
|
|
||||||
@@ -234,38 +188,45 @@ class Molecule:
|
|||||||
|
|
||||||
return position
|
return position
|
||||||
|
|
||||||
def updateCharges(self, charges: List[float]) -> None:
|
def update_charges(self, charges: NDArray) -> int:
|
||||||
|
"""
|
||||||
|
Updates the charges of the atoms of the molecule and
|
||||||
|
returns the max difference between the new and old charges
|
||||||
|
"""
|
||||||
|
diff = 0
|
||||||
for i, atom in enumerate(self.atom):
|
for i, atom in enumerate(self.atom):
|
||||||
|
diff = max(diff, abs(atom.chg - charges[i]))
|
||||||
atom.chg = charges[i]
|
atom.chg = charges[i]
|
||||||
|
|
||||||
def update_hessian(
|
return diff
|
||||||
self,
|
|
||||||
step: np.ndarray,
|
|
||||||
cur_gradient: np.ndarray,
|
|
||||||
old_gradient: np.ndarray,
|
|
||||||
hessian: np.ndarray,
|
|
||||||
) -> np.ndarray:
|
|
||||||
"""
|
|
||||||
Updates the Hessian of the molecule based on the current hessian, the current gradient and the previous gradient
|
|
||||||
|
|
||||||
Args:
|
# @staticmethod
|
||||||
step (np.ndarray): step value of the iteration
|
# def update_hessian(
|
||||||
cur_gradient (np.ndarray): current gradient
|
# step: np.ndarray,
|
||||||
old_gradient (np.ndarray): previous gradient
|
# cur_gradient: np.ndarray,
|
||||||
hessian (np.ndarray): current hessian
|
# old_gradient: np.ndarray,
|
||||||
|
# hessian: np.ndarray,
|
||||||
Returns:
|
# ) -> np.ndarray:
|
||||||
np.ndarray: updated hessian of the molecule
|
# """
|
||||||
"""
|
# Updates the Hessian of the molecule based on the current hessian, the current gradient and the previous gradient
|
||||||
|
#
|
||||||
dif_gradient = cur_gradient - old_gradient
|
# Args:
|
||||||
|
# step (np.ndarray): step value of the iteration
|
||||||
mat1 = 1 / np.dot(dif_gradient, step) * np.matmul(dif_gradient.T, dif_gradient)
|
# cur_gradient (np.ndarray): current gradient
|
||||||
mat2 = 1 / np.dot(step, np.matmul(hessian, step.T).T)
|
# old_gradient (np.ndarray): previous gradient
|
||||||
mat2 *= np.matmul(np.matmul(hessian, step.T), np.matmul(step, hessian))
|
# hessian (np.ndarray): current hessian
|
||||||
|
#
|
||||||
return hessian + mat1 - mat2
|
# Returns:
|
||||||
|
# np.ndarray: updated hessian of the molecule
|
||||||
|
# """
|
||||||
|
#
|
||||||
|
# dif_gradient = cur_gradient - old_gradient
|
||||||
|
#
|
||||||
|
# mat1 = 1 / np.dot(dif_gradient, step) * np.matmul(dif_gradient.T, dif_gradient)
|
||||||
|
# mat2 = 1 / np.dot(step, np.matmul(hessian, step.T).T)
|
||||||
|
# mat2 *= np.matmul(np.matmul(hessian, step.T), np.matmul(step, hessian))
|
||||||
|
#
|
||||||
|
# return hessian + mat1 - mat2
|
||||||
|
|
||||||
def sizes_of_molecule(self) -> List[float]:
|
def sizes_of_molecule(self) -> List[float]:
|
||||||
"""
|
"""
|
||||||
@@ -280,10 +241,9 @@ class Molecule:
|
|||||||
z_list = []
|
z_list = []
|
||||||
|
|
||||||
for atom in self.atom:
|
for atom in self.atom:
|
||||||
if atom.na != ghost_number:
|
x_list.append(atom.rx)
|
||||||
x_list.append(atom.rx)
|
y_list.append(atom.ry)
|
||||||
y_list.append(atom.ry)
|
z_list.append(atom.rz)
|
||||||
z_list.append(atom.rz)
|
|
||||||
|
|
||||||
x_max = max(x_list)
|
x_max = max(x_list)
|
||||||
x_min = min(x_list)
|
x_min = min(x_list)
|
||||||
@@ -305,21 +265,18 @@ class Molecule:
|
|||||||
evals, evecs = self.principal_axes()
|
evals, evecs = self.principal_axes()
|
||||||
|
|
||||||
if round(linalg.det(evecs)) == -1:
|
if round(linalg.det(evecs)) == -1:
|
||||||
|
|
||||||
evecs[0, 2] *= -1
|
evecs[0, 2] *= -1
|
||||||
evecs[1, 2] *= -1
|
evecs[1, 2] *= -1
|
||||||
evecs[2, 2] *= -1
|
evecs[2, 2] *= -1
|
||||||
|
|
||||||
if round(linalg.det(evecs)) != 1:
|
if round(linalg.det(evecs)) != 1:
|
||||||
|
raise RuntimeError(
|
||||||
sys.exit(
|
|
||||||
"Error: could not make a rotation matrix while adopting the standard orientation"
|
"Error: could not make a rotation matrix while adopting the standard orientation"
|
||||||
)
|
)
|
||||||
|
|
||||||
rot_matrix = evecs.T
|
rot_matrix = evecs.T
|
||||||
|
|
||||||
for atom in self.atom:
|
for atom in self.atom:
|
||||||
|
|
||||||
position = np.array([atom.rx, atom.ry, atom.rz])
|
position = np.array([atom.rx, atom.ry, atom.rz])
|
||||||
new_position = np.matmul(rot_matrix, position.T).T
|
new_position = np.matmul(rot_matrix, position.T).T
|
||||||
|
|
||||||
@@ -332,7 +289,7 @@ class Molecule:
|
|||||||
Creates a new Molecule object where its' atoms has been translated by a vector
|
Creates a new Molecule object where its' atoms has been translated by a vector
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vector (np.ndarray): translation vector
|
vector (np.ndarray): translation vector
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Molecule: new Molecule object translated by a vector
|
Molecule: new Molecule object translated by a vector
|
||||||
@@ -341,68 +298,64 @@ class Molecule:
|
|||||||
new_molecule = deepcopy(self)
|
new_molecule = deepcopy(self)
|
||||||
|
|
||||||
for atom in new_molecule.atom:
|
for atom in new_molecule.atom:
|
||||||
|
|
||||||
atom.rx += vector[0]
|
atom.rx += vector[0]
|
||||||
atom.ry += vector[1]
|
atom.ry += vector[1]
|
||||||
atom.rz += vector[2]
|
atom.rz += vector[2]
|
||||||
|
|
||||||
return new_molecule
|
return new_molecule
|
||||||
|
|
||||||
def print_mol_info(self, fh: TextIO) -> None:
|
def print_mol_info(self) -> None:
|
||||||
"""
|
"""
|
||||||
Prints the Molecule information into a Output File
|
Prints the Molecule information into a Output File
|
||||||
|
|
||||||
Args:
|
|
||||||
fh (TextIO): Output File
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fh.write(
|
logger.info(
|
||||||
" Center of mass = ( {:>10.4f} , {:>10.4f} , {:>10.4f} )\n".format(
|
" Center of mass = ( {:>10.4f} , {:>10.4f} , {:>10.4f} )".format(
|
||||||
self.com[0], self.com[1], self.com[2]
|
self.com[0], self.com[1], self.com[2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
inertia = self.inertia_tensor()
|
inertia = self.inertia_tensor()
|
||||||
evals, evecs = self.principal_axes()
|
evals, evecs = self.principal_axes()
|
||||||
|
|
||||||
fh.write(
|
logger.info(
|
||||||
" Moments of inertia = {:>9E} {:>9E} {:>9E}\n".format(
|
" Moments of inertia = {:>9E} {:>9E} {:>9E}".format(
|
||||||
evals[0], evals[1], evals[2]
|
evals[0], evals[1], evals[2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
fh.write(
|
logger.info(
|
||||||
" Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
|
" Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format(
|
||||||
evecs[0, 0], evecs[1, 0], evecs[2, 0]
|
evecs[0, 0], evecs[1, 0], evecs[2, 0]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fh.write(
|
logger.info(
|
||||||
" Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
|
" Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format(
|
||||||
evecs[0, 1], evecs[1, 1], evecs[2, 1]
|
evecs[0, 1], evecs[1, 1], evecs[2, 1]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fh.write(
|
logger.info(
|
||||||
" Minor principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format(
|
" Minor principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format(
|
||||||
evecs[0, 2], evecs[1, 2], evecs[2, 2]
|
evecs[0, 2], evecs[1, 2], evecs[2, 2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
sizes = self.sizes_of_molecule()
|
sizes = self.sizes_of_molecule()
|
||||||
fh.write(
|
logger.info(
|
||||||
" Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )\n".format(
|
" Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )".format(
|
||||||
sizes[0], sizes[1], sizes[2]
|
sizes[0], sizes[1], sizes[2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fh.write(" Total mass = {:>8.2f} au\n".format(self.total_mass))
|
logger.info(" Total mass = {:>8.2f} au".format(self.total_mass))
|
||||||
|
|
||||||
chg_dip = self.charges_and_dipole()
|
chg_dip = self.charges_and_dipole()
|
||||||
fh.write(" Total charge = {:>8.4f} e\n".format(chg_dip[0]))
|
logger.info(" Total charge = {:>8.4f} e".format(chg_dip[0]))
|
||||||
fh.write(
|
logger.info(
|
||||||
" Dipole moment = ( {:>9.4f} , {:>9.4f} , {:>9.4f} ) Total = {:>9.4f} Debye\n\n".format(
|
" Dipole moment = ( {:>9.4f} , {:>9.4f} , {:>9.4f} ) Total = {:>9.4f} Debye".format(
|
||||||
chg_dip[1], chg_dip[2], chg_dip[3], chg_dip[4]
|
chg_dip[1], chg_dip[2], chg_dip[3], chg_dip[4]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def minimum_distance(self, molec: "Molecule") -> float:
|
def minimum_distance(self, molec: 'Molecule') -> float:
|
||||||
"""
|
"""
|
||||||
Return the minimum distance between two molecules
|
Return the minimum distance between two molecules
|
||||||
|
|
||||||
@@ -421,6 +374,6 @@ class Molecule:
|
|||||||
dx = atom1.rx - atom2.rx
|
dx = atom1.rx - atom2.rx
|
||||||
dy = atom1.ry - atom2.ry
|
dy = atom1.ry - atom2.ry
|
||||||
dz = atom1.rz - atom2.rz
|
dz = atom1.rz - atom2.rz
|
||||||
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
|
distances.append(math.sqrt(dx ** 2 + dy ** 2 + dz ** 2))
|
||||||
|
|
||||||
return min(distances)
|
return min(distances)
|
||||||
233
diceplayer/shared/environment/system.py
Normal file
233
diceplayer/shared/environment/system.py
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
from diceplayer.shared.environment.molecule import Molecule
|
||||||
|
from diceplayer.shared.utils.ptable import atomsymb
|
||||||
|
from diceplayer.shared.utils.misc import BOHR2ANG
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
from typing import List, Tuple, TextIO
|
||||||
|
from copy import deepcopy
|
||||||
|
from numpy import linalg
|
||||||
|
import numpy as np
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
class System:
|
||||||
|
"""
|
||||||
|
System class declaration. This class is used throughout the DicePlayer program to represent the system containing the molecules.
|
||||||
|
|
||||||
|
Atributes:
|
||||||
|
molecule (List[Molecule]): List of molecules of the system
|
||||||
|
nmols (List[int]): List of number of molecules in the system
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""
|
||||||
|
Initializes an empty system object that will be populated afterwards
|
||||||
|
"""
|
||||||
|
self.nmols: List[int] = []
|
||||||
|
self.molecule: List[Molecule] = []
|
||||||
|
|
||||||
|
def add_type(self, m: Molecule) -> None:
|
||||||
|
"""
|
||||||
|
Adds a new molecule type to the system
|
||||||
|
|
||||||
|
Args:
|
||||||
|
m (Molecule): The instance of the new type of molecule
|
||||||
|
"""
|
||||||
|
if isinstance(m, Molecule) is False:
|
||||||
|
raise TypeError("Error: molecule is not a Molecule instance")
|
||||||
|
self.molecule.append(m)
|
||||||
|
|
||||||
|
def update_molecule(self, position: np.ndarray) -> None:
|
||||||
|
"""Updates the position of the molecule in the Output file
|
||||||
|
|
||||||
|
Args:
|
||||||
|
position (np.ndarray): numpy position vector
|
||||||
|
"""
|
||||||
|
|
||||||
|
position_in_ang = (position * BOHR2ANG).tolist()
|
||||||
|
self.add_type(deepcopy(self.molecule[0]))
|
||||||
|
|
||||||
|
for atom in self.molecule[-1].atom:
|
||||||
|
atom.rx = position_in_ang.pop(0)
|
||||||
|
atom.ry = position_in_ang.pop(0)
|
||||||
|
atom.rz = position_in_ang.pop(0)
|
||||||
|
|
||||||
|
rmsd, self.molecule[0] = self.rmsd_fit(-1, 0)
|
||||||
|
self.molecule.pop(-1)
|
||||||
|
|
||||||
|
logger.info("Projected new conformation of reference molecule with RMSD fit")
|
||||||
|
logger.info(f"RMSD = {rmsd:>8.5f} Angstrom")
|
||||||
|
|
||||||
|
def rmsd_fit(self, p_index: int, r_index: int) -> Tuple[float, Molecule]:
|
||||||
|
|
||||||
|
projecting_mol = self.molecule[p_index]
|
||||||
|
reference_mol = self.molecule[r_index]
|
||||||
|
|
||||||
|
if len(projecting_mol.atom) != len(reference_mol.atom):
|
||||||
|
raise RuntimeError(
|
||||||
|
"Error in RMSD fit procedure: molecules have different number of atoms"
|
||||||
|
)
|
||||||
|
dim = len(projecting_mol.atom)
|
||||||
|
|
||||||
|
new_projecting_mol = deepcopy(projecting_mol)
|
||||||
|
new_reference_mol = deepcopy(reference_mol)
|
||||||
|
|
||||||
|
new_projecting_mol.center_of_mass_to_origin()
|
||||||
|
new_reference_mol.center_of_mass_to_origin()
|
||||||
|
|
||||||
|
x = []
|
||||||
|
y = []
|
||||||
|
|
||||||
|
for atom in new_projecting_mol.atom:
|
||||||
|
x.extend([atom.rx, atom.ry, atom.rz])
|
||||||
|
|
||||||
|
for atom in new_reference_mol.atom:
|
||||||
|
y.extend([atom.rx, atom.ry, atom.rz])
|
||||||
|
|
||||||
|
x = np.array(x).reshape(dim, 3)
|
||||||
|
y = np.array(y).reshape(dim, 3)
|
||||||
|
|
||||||
|
r = np.matmul(y.T, x)
|
||||||
|
rr = np.matmul(r.T, r)
|
||||||
|
|
||||||
|
try:
|
||||||
|
evals, evecs = linalg.eigh(rr)
|
||||||
|
except:
|
||||||
|
raise RuntimeError("Error: diagonalization of RR matrix did not converge")
|
||||||
|
|
||||||
|
a1 = evecs[:, 2].T
|
||||||
|
a2 = evecs[:, 1].T
|
||||||
|
a3 = np.cross(a1, a2)
|
||||||
|
|
||||||
|
A = np.array([a1[0], a1[1], a1[2], a2[0], a2[1], a2[2], a3[0], a3[1], a3[2]])
|
||||||
|
A = A.reshape(3, 3)
|
||||||
|
|
||||||
|
b1 = np.matmul(r, a1.T).T # or np.dot(r, a1)
|
||||||
|
b1 /= linalg.norm(b1)
|
||||||
|
b2 = np.matmul(r, a2.T).T # or np.dot(r, a2)
|
||||||
|
b2 /= linalg.norm(b2)
|
||||||
|
b3 = np.cross(b1, b2)
|
||||||
|
|
||||||
|
B = np.array([b1[0], b1[1], b1[2], b2[0], b2[1], b2[2], b3[0], b3[1], b3[2]])
|
||||||
|
B = B.reshape(3, 3).T
|
||||||
|
|
||||||
|
rot_matrix = np.matmul(B, A)
|
||||||
|
x = np.matmul(rot_matrix, x.T).T
|
||||||
|
|
||||||
|
rmsd = 0
|
||||||
|
for i in range(dim):
|
||||||
|
rmsd += (
|
||||||
|
(x[i, 0] - y[i, 0]) ** 2
|
||||||
|
+ (x[i, 1] - y[i, 1]) ** 2
|
||||||
|
+ (x[i, 2] - y[i, 2]) ** 2
|
||||||
|
)
|
||||||
|
rmsd = math.sqrt(rmsd / dim)
|
||||||
|
|
||||||
|
for i in range(dim):
|
||||||
|
new_projecting_mol.atom[i].rx = x[i, 0]
|
||||||
|
new_projecting_mol.atom[i].ry = x[i, 1]
|
||||||
|
new_projecting_mol.atom[i].rz = x[i, 2]
|
||||||
|
|
||||||
|
reference_mol.center_of_mass()
|
||||||
|
|
||||||
|
projected_mol = new_projecting_mol.translate(reference_mol.com)
|
||||||
|
|
||||||
|
return rmsd, projected_mol
|
||||||
|
|
||||||
|
# def center_of_mass_distance(self, a: int, b: int) -> float:
|
||||||
|
# """
|
||||||
|
# Calculates the distance between the center of mass of two molecules
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# a (Molecule): First Molecule Instance
|
||||||
|
# b (Molecule): Second Molecule Instance
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# float: module of the distance between the two center of masses
|
||||||
|
# """
|
||||||
|
#
|
||||||
|
# com1 = self.molecule[a].center_of_mass()
|
||||||
|
# com2 = self.molecule[b].center_of_mass()
|
||||||
|
# dx = com1[0] - com2[0]
|
||||||
|
# dy = com1[1] - com2[1]
|
||||||
|
# dz = com1[2] - com2[2]
|
||||||
|
# distance = math.sqrt(dx**2 + dy**2 + dz**2)
|
||||||
|
#
|
||||||
|
# return distance
|
||||||
|
|
||||||
|
# def nearest_image(
|
||||||
|
# self,
|
||||||
|
# index_r: int,
|
||||||
|
# index_m: int,
|
||||||
|
# lx: float,
|
||||||
|
# ly: float,
|
||||||
|
# lz: float,
|
||||||
|
# criterium=None,
|
||||||
|
# ) -> Tuple[float, Molecule]:
|
||||||
|
#
|
||||||
|
# if criterium in None:
|
||||||
|
# criterium = "com"
|
||||||
|
#
|
||||||
|
# if criterium != "com" and criterium != "min":
|
||||||
|
# raise RuntimeError("Error in value passed to function nearest_image")
|
||||||
|
#
|
||||||
|
# min_dist = 1e20
|
||||||
|
#
|
||||||
|
# for i in range(-1, 2):
|
||||||
|
# for j in range(-1, 2):
|
||||||
|
# for k in range(-1, 2):
|
||||||
|
#
|
||||||
|
# tr_vector = [i * lx, j * ly, k * lz]
|
||||||
|
# self.add_molecule(self.molecule[index_m].translate(tr_vector))
|
||||||
|
#
|
||||||
|
# if criterium == "com":
|
||||||
|
# dist = self.center_of_mass_distance(index_r, -1)
|
||||||
|
# else:
|
||||||
|
# dist = self.minimum_distance(index_r, -1)
|
||||||
|
#
|
||||||
|
# if dist < min_dist:
|
||||||
|
# min_dist = dist
|
||||||
|
# nearestmol = deepcopy(self.molecule[-1])
|
||||||
|
#
|
||||||
|
# self.molecule.pop(-1)
|
||||||
|
#
|
||||||
|
# return min_dist, nearestmol
|
||||||
|
|
||||||
|
# def print_geom(self, cycle: int, fh: TextIO) -> None:
|
||||||
|
# """
|
||||||
|
# Print the geometry of the molecule in the Output file
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# cycle (int): Number of the cycle
|
||||||
|
# fh (TextIO): Output file
|
||||||
|
# """
|
||||||
|
#
|
||||||
|
# fh.write("Cycle # {}\n".format(cycle))
|
||||||
|
# fh.write("Number of site: {}\n".format(len(self.molecule[0].atom)))
|
||||||
|
# for atom in self.molecule[0].atom:
|
||||||
|
# symbol = atomsymb[atom.na]
|
||||||
|
# fh.write(
|
||||||
|
# "{:<2s} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(
|
||||||
|
# symbol, atom.rx, atom.ry, atom.rz
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
def print_charges_and_dipole(self, cycle: int) -> None:
|
||||||
|
"""
|
||||||
|
Print the charges and dipole of the molecule in the Output file
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cycle (int): Number of the cycle
|
||||||
|
fh (TextIO): Output file
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.info("Cycle # {}\n".format(cycle))
|
||||||
|
logger.info("Number of site: {}\n".format(len(self.molecule[0].atom)))
|
||||||
|
|
||||||
|
chargesAndDipole = self.molecule[0].charges_and_dipole()
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"{:>10.6f} {:>10.6f} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(
|
||||||
|
chargesAndDipole[0], chargesAndDipole[1], chargesAndDipole[2], chargesAndDipole[3], chargesAndDipole[4]
|
||||||
|
)
|
||||||
|
)
|
||||||
1
diceplayer/shared/interface/__init__.py
Normal file
1
diceplayer/shared/interface/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .__interface import Interface
|
||||||
29
diceplayer/shared/interface/__interface.py
Normal file
29
diceplayer/shared/interface/__interface.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from diceplayer.shared.config.player_config import PlayerConfig
|
||||||
|
from diceplayer.shared.environment.system import System
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class Interface(ABC):
|
||||||
|
__slots__ = [
|
||||||
|
'step',
|
||||||
|
'system'
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.system: System | None = None
|
||||||
|
self.step: PlayerConfig | None = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def configure(self, step: PlayerConfig, system: System):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def start(self, cycle: int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def reset(self):
|
||||||
|
pass
|
||||||
395
diceplayer/shared/interface/dice_interface.py
Normal file
395
diceplayer/shared/interface/dice_interface.py
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from diceplayer.shared.config.player_config import PlayerConfig
|
||||||
|
from diceplayer.shared.environment.system import System
|
||||||
|
from diceplayer.shared.interface import Interface
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
from multiprocessing import Process, connection
|
||||||
|
from setproctitle import setproctitle
|
||||||
|
from typing import Final, TextIO
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
DICE_END_FLAG: Final[str] = "End of simulation"
|
||||||
|
DICE_FLAG_LINE: Final[int] = -2
|
||||||
|
UMAANG3_TO_GCM3: Final[float] = 1.6605
|
||||||
|
|
||||||
|
MAX_SEED: Final[int] = 4294967295
|
||||||
|
|
||||||
|
|
||||||
|
class DiceInterface(Interface):
|
||||||
|
title = "Diceplayer run"
|
||||||
|
|
||||||
|
def configure(self, step: PlayerConfig, system: System):
|
||||||
|
self.step = step
|
||||||
|
self.system = system
|
||||||
|
|
||||||
|
def start(self, cycle: int):
|
||||||
|
procs = []
|
||||||
|
sentinels = []
|
||||||
|
|
||||||
|
for proc in range(1, self.step.nprocs + 1):
|
||||||
|
p = Process(target=self._simulation_process, args=(cycle, proc))
|
||||||
|
p.start()
|
||||||
|
|
||||||
|
procs.append(p)
|
||||||
|
sentinels.append(p.sentinel)
|
||||||
|
|
||||||
|
while procs:
|
||||||
|
finished = connection.wait(sentinels)
|
||||||
|
for proc_sentinel in finished:
|
||||||
|
i = sentinels.index(proc_sentinel)
|
||||||
|
status = procs[i].exitcode
|
||||||
|
procs.pop(i)
|
||||||
|
sentinels.pop(i)
|
||||||
|
if status != 0:
|
||||||
|
for p in procs:
|
||||||
|
p.terminate()
|
||||||
|
sys.exit(status)
|
||||||
|
|
||||||
|
logger.info("\n")
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
del self.step
|
||||||
|
del self.system
|
||||||
|
|
||||||
|
def _simulation_process(self, cycle: int, proc: int):
|
||||||
|
setproctitle(f"diceplayer-step{cycle:0d}-p{proc:0d}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._make_proc_dir(cycle, proc)
|
||||||
|
self._make_dice_inputs(cycle, proc)
|
||||||
|
self._run_dice(cycle, proc)
|
||||||
|
except Exception as err:
|
||||||
|
sys.exit(err)
|
||||||
|
|
||||||
|
def _make_proc_dir(self, cycle, proc):
|
||||||
|
simulation_dir = Path(self.step.simulation_dir)
|
||||||
|
if not simulation_dir.exists():
|
||||||
|
simulation_dir.mkdir(parents=True)
|
||||||
|
|
||||||
|
proc_dir = Path(
|
||||||
|
simulation_dir,
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
f"p{proc:02d}"
|
||||||
|
)
|
||||||
|
proc_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
def _make_dice_inputs(self, cycle, proc):
|
||||||
|
proc_dir = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
f"p{proc:02d}"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._make_potentials(proc_dir)
|
||||||
|
|
||||||
|
random.seed(self._make_dice_seed())
|
||||||
|
|
||||||
|
# This is logic is used to make the initial configuration file
|
||||||
|
# for the next cycle using the last.xyz file from the previous cycle.
|
||||||
|
if self.step.dice.randominit == 'first' and cycle > 1:
|
||||||
|
last_xyz = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{(cycle - 1):02d}",
|
||||||
|
f"p{proc:02d}",
|
||||||
|
"last.xyz"
|
||||||
|
)
|
||||||
|
if not last_xyz.exists():
|
||||||
|
raise FileNotFoundError(f"File {last_xyz} not found.")
|
||||||
|
|
||||||
|
with open(last_xyz, 'r') as last_xyz_file:
|
||||||
|
self._make_init_file(proc_dir, last_xyz_file)
|
||||||
|
last_xyz_file.seek(0)
|
||||||
|
self.step.dice.dens = self._new_density(last_xyz_file)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._make_nvt_ter(cycle, proc_dir)
|
||||||
|
|
||||||
|
if len(self.step.dice.nstep) == 2:
|
||||||
|
self._make_nvt_eq(cycle, proc_dir)
|
||||||
|
|
||||||
|
elif len(self.step.dice.nstep) == 3:
|
||||||
|
self._make_npt_ter(cycle, proc_dir)
|
||||||
|
self._make_npt_eq(proc_dir)
|
||||||
|
|
||||||
|
def _run_dice(self, cycle: int, proc: int):
|
||||||
|
working_dir = os.getcwd()
|
||||||
|
|
||||||
|
proc_dir = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
f"p{proc:02d}"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Simulation process {str(proc_dir)} initiated with pid {os.getpid()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
os.chdir(proc_dir)
|
||||||
|
|
||||||
|
if not (self.step.dice.randominit == 'first' and cycle > 1):
|
||||||
|
self.run_dice_file(cycle, proc, "NVT.ter")
|
||||||
|
|
||||||
|
if len(self.step.dice.nstep) == 2:
|
||||||
|
self.run_dice_file(cycle, proc, "NVT.eq")
|
||||||
|
|
||||||
|
elif len(self.step.dice.nstep) == 3:
|
||||||
|
self.run_dice_file(cycle, proc, "NPT.ter")
|
||||||
|
self.run_dice_file(cycle, proc, "NPT.eq")
|
||||||
|
|
||||||
|
os.chdir(working_dir)
|
||||||
|
|
||||||
|
xyz_file = Path(proc_dir, "phb.xyz")
|
||||||
|
last_xyz_file = Path(proc_dir, "last.xyz")
|
||||||
|
|
||||||
|
if xyz_file.exists():
|
||||||
|
shutil.copy(xyz_file, last_xyz_file)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"File {xyz_file} not found.")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _make_dice_seed() -> int:
|
||||||
|
num = time.time()
|
||||||
|
num = (num - int(num)) * 1e6
|
||||||
|
|
||||||
|
num = int((num - int(num)) * 1e6)
|
||||||
|
|
||||||
|
return (os.getpid() * num) % (MAX_SEED + 1)
|
||||||
|
|
||||||
|
def _make_init_file(self, proc_dir: Path, last_xyz_file: TextIO):
|
||||||
|
xyz_lines = last_xyz_file.readlines()
|
||||||
|
|
||||||
|
SECONDARY_MOLECULE_LENGTH = 0
|
||||||
|
for i in range(1, len(self.step.dice.nmol)):
|
||||||
|
SECONDARY_MOLECULE_LENGTH += self.step.dice.nmol[i] * len(self.system.molecule[i].atom)
|
||||||
|
|
||||||
|
xyz_lines = xyz_lines[-SECONDARY_MOLECULE_LENGTH:]
|
||||||
|
|
||||||
|
input_file = Path(proc_dir, self.step.dice.outname + ".xy")
|
||||||
|
with open(input_file, 'w') as f:
|
||||||
|
|
||||||
|
for atom in self.system.molecule[0].atom:
|
||||||
|
f.write(
|
||||||
|
f"{atom.rx:>10.6f} {atom.ry:>10.6f} {atom.rz:>10.6f}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
for line in xyz_lines:
|
||||||
|
atom = line.split()
|
||||||
|
rx = float(atom[1])
|
||||||
|
ry = float(atom[2])
|
||||||
|
rz = float(atom[3])
|
||||||
|
f.write(f"{rx:>10.6f} {ry:>10.6f} {rz:>10.6f}\n")
|
||||||
|
|
||||||
|
f.write("$end")
|
||||||
|
|
||||||
|
def _new_density(self, last_xyz_file: TextIO):
|
||||||
|
last_xyz_lines = last_xyz_file.readlines()
|
||||||
|
|
||||||
|
box = last_xyz_lines[1].split()
|
||||||
|
volume = float(box[-3]) * float(box[-2]) * float(box[-1])
|
||||||
|
|
||||||
|
total_mass = 0
|
||||||
|
for i in range(len(self.system.molecule)):
|
||||||
|
total_mass += self.system.molecule[i].total_mass * self.step.dice.nmol[i]
|
||||||
|
|
||||||
|
density = (total_mass / volume) * UMAANG3_TO_GCM3
|
||||||
|
|
||||||
|
return density
|
||||||
|
|
||||||
|
def _make_nvt_ter(self, cycle, proc_dir):
|
||||||
|
file = Path(proc_dir, "NVT.ter")
|
||||||
|
with open(file, 'w') as f:
|
||||||
|
f.write(f"title = {self.title} - NVT Thermalization\n")
|
||||||
|
f.write(f"ncores = {self.step.ncores}\n")
|
||||||
|
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||||
|
f.write(f"outname = {self.step.dice.outname}\n")
|
||||||
|
|
||||||
|
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||||
|
f.write(f"nmol = {mol_string}\n")
|
||||||
|
|
||||||
|
f.write(f"dens = {self.step.dice.dens}\n")
|
||||||
|
f.write(f"temp = {self.step.dice.temp}\n")
|
||||||
|
|
||||||
|
if self.step.dice.randominit == "first" and cycle > 1:
|
||||||
|
f.write(f"init = yesreadxyz\n")
|
||||||
|
f.write(f"nstep = {self.step.altsteps}\n")
|
||||||
|
else:
|
||||||
|
f.write(f"init = yes\n")
|
||||||
|
f.write(f"nstep = {self.step.dice.nstep[0]}\n")
|
||||||
|
|
||||||
|
f.write("vstep = 0\n")
|
||||||
|
f.write("mstop = 1\n")
|
||||||
|
f.write("accum = no\n")
|
||||||
|
f.write("iprint = 1\n")
|
||||||
|
f.write("isave = 0\n")
|
||||||
|
f.write("irdf = 0\n")
|
||||||
|
|
||||||
|
seed = int(1e6 * random.random())
|
||||||
|
f.write(f"seed = {seed}\n")
|
||||||
|
f.write(f"upbuf = {self.step.dice.upbuf}")
|
||||||
|
|
||||||
|
def _make_nvt_eq(self, cycle, proc_dir):
|
||||||
|
|
||||||
|
file = Path(proc_dir, "NVT.eq")
|
||||||
|
with open(file, 'w') as f:
|
||||||
|
f.write(f"title = {self.title} - NVT Production\n")
|
||||||
|
f.write(f"ncores = {self.step.ncores}\n")
|
||||||
|
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||||
|
f.write(f"outname = {self.step.dice.outname}\n")
|
||||||
|
|
||||||
|
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||||
|
f.write(f"nmol = {mol_string}\n")
|
||||||
|
|
||||||
|
f.write(f"dens = {self.step.dice.dens}\n")
|
||||||
|
f.write(f"temp = {self.step.dice.temp}\n")
|
||||||
|
|
||||||
|
if self.step.dice.randominit == "first" and cycle > 1:
|
||||||
|
f.write("init = yesreadxyz\n")
|
||||||
|
else:
|
||||||
|
f.write("init = no\n")
|
||||||
|
|
||||||
|
f.write(f"nstep = {self.step.dice.nstep[1]}\n")
|
||||||
|
|
||||||
|
f.write("vstep = 0\n")
|
||||||
|
f.write("mstop = 1\n")
|
||||||
|
f.write("accum = no\n")
|
||||||
|
f.write("iprint = 1\n")
|
||||||
|
|
||||||
|
f.write(f"isave = {self.step.dice.isave}\n")
|
||||||
|
f.write(f"irdf = {10 * self.step.nprocs}\n")
|
||||||
|
|
||||||
|
seed = int(1e6 * random.random())
|
||||||
|
f.write("seed = {}\n".format(seed))
|
||||||
|
|
||||||
|
def _make_npt_ter(self, cycle, proc_dir):
|
||||||
|
|
||||||
|
file = Path(proc_dir, "NPT.ter")
|
||||||
|
with open(file, 'w') as f:
|
||||||
|
f.write(f"title = {self.title} - NPT Thermalization\n")
|
||||||
|
f.write(f"ncores = {self.step.ncores}\n")
|
||||||
|
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||||
|
f.write(f"outname = {self.step.dice.outname}\n")
|
||||||
|
|
||||||
|
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||||
|
f.write(f"nmol = {mol_string}\n")
|
||||||
|
|
||||||
|
f.write(f"press = {self.step.dice.press}\n")
|
||||||
|
f.write(f"temp = {self.step.dice.temp}\n")
|
||||||
|
|
||||||
|
if self.step.dice.randominit == "first" and cycle > 1:
|
||||||
|
f.write("init = yesreadxyz\n")
|
||||||
|
f.write(f"dens = {self.step.dice.dens:<8.4f}\n")
|
||||||
|
f.write(f"vstep = {int(self.step.altsteps / 5)}\n")
|
||||||
|
else:
|
||||||
|
f.write("init = no\n")
|
||||||
|
f.write(f"vstep = {int(self.step.dice.nstep[1] / 5)}\n")
|
||||||
|
|
||||||
|
f.write("nstep = 5\n")
|
||||||
|
f.write("mstop = 1\n")
|
||||||
|
f.write("accum = no\n")
|
||||||
|
f.write("iprint = 1\n")
|
||||||
|
f.write("isave = 0\n")
|
||||||
|
f.write("irdf = 0\n")
|
||||||
|
|
||||||
|
seed = int(1e6 * random.random())
|
||||||
|
f.write(f"seed = {seed}\n")
|
||||||
|
|
||||||
|
def _make_npt_eq(self, proc_dir):
|
||||||
|
file = Path(proc_dir, "NPT.eq")
|
||||||
|
with open(file, 'w') as f:
|
||||||
|
f.write(f"title = {self.title} - NPT Production\n")
|
||||||
|
f.write(f"ncores = {self.step.ncores}\n")
|
||||||
|
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||||
|
f.write(f"outname = {self.step.dice.outname}\n")
|
||||||
|
|
||||||
|
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||||
|
f.write(f"nmol = {mol_string}\n")
|
||||||
|
|
||||||
|
f.write(f"press = {self.step.dice.press}\n")
|
||||||
|
f.write(f"temp = {self.step.dice.temp}\n")
|
||||||
|
|
||||||
|
f.write(f"nstep = 5\n")
|
||||||
|
|
||||||
|
f.write(f"vstep = {int(self.step.dice.nstep[2] / 5)}\n")
|
||||||
|
f.write("init = no\n")
|
||||||
|
f.write("mstop = 1\n")
|
||||||
|
f.write("accum = no\n")
|
||||||
|
f.write("iprint = 1\n")
|
||||||
|
f.write(f"isave = {self.step.dice.isave}\n")
|
||||||
|
f.write(f"irdf = {10 * self.step.nprocs}\n")
|
||||||
|
|
||||||
|
seed = int(1e6 * random.random())
|
||||||
|
f.write(f"seed = {seed}\n")
|
||||||
|
|
||||||
|
def _make_potentials(self, proc_dir):
|
||||||
|
fstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f}\n"
|
||||||
|
|
||||||
|
file = Path(proc_dir, self.step.dice.ljname)
|
||||||
|
with open(file, 'w') as f:
|
||||||
|
f.write(f"{self.step.dice.combrule}\n")
|
||||||
|
f.write(f"{len(self.step.dice.nmol)}\n")
|
||||||
|
|
||||||
|
nsites_qm = len(self.system.molecule[0].atom)
|
||||||
|
f.write(f"{nsites_qm} {self.system.molecule[0].molname}\n")
|
||||||
|
|
||||||
|
for atom in self.system.molecule[0].atom:
|
||||||
|
f.write(
|
||||||
|
fstr.format(
|
||||||
|
atom.lbl,
|
||||||
|
atom.na,
|
||||||
|
atom.rx,
|
||||||
|
atom.ry,
|
||||||
|
atom.rz,
|
||||||
|
atom.chg,
|
||||||
|
atom.eps,
|
||||||
|
atom.sig,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for mol in self.system.molecule[1:]:
|
||||||
|
f.write(f"{len(mol.atom)} {mol.molname}\n")
|
||||||
|
for atom in mol.atom:
|
||||||
|
f.write(
|
||||||
|
fstr.format(
|
||||||
|
atom.lbl,
|
||||||
|
atom.na,
|
||||||
|
atom.rx,
|
||||||
|
atom.ry,
|
||||||
|
atom.rz,
|
||||||
|
atom.chg,
|
||||||
|
atom.eps,
|
||||||
|
atom.sig,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def run_dice_file(self, cycle: int, proc: int, file_name: str):
|
||||||
|
with open(Path(file_name), 'r') as infile, open(Path(file_name + ".out"), 'w') as outfile:
|
||||||
|
if shutil.which("bash") is not None:
|
||||||
|
exit_status = subprocess.call(
|
||||||
|
[
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
f"exec -a dice-step{cycle}-p{proc} {self.step.dice.progname} < {infile.name} > {outfile.name}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
exit_status = subprocess.call(
|
||||||
|
self.step.dice.progname, stdin=infile, stdout=outfile
|
||||||
|
)
|
||||||
|
|
||||||
|
if exit_status != 0:
|
||||||
|
raise RuntimeError(f"Dice process step{cycle:02d}-p{proc:02d} did not exit properly")
|
||||||
|
|
||||||
|
with open(Path(file_name + ".out"), 'r') as outfile:
|
||||||
|
flag = outfile.readlines()[DICE_FLAG_LINE].strip()
|
||||||
|
if flag != DICE_END_FLAG:
|
||||||
|
raise RuntimeError(f"Dice process step{cycle:02d}-p{proc:02d} did not exit properly")
|
||||||
|
|
||||||
|
logger.info(f"Dice {file_name} - step{cycle:02d}-p{proc:02d} exited properly")
|
||||||
380
diceplayer/shared/interface/gaussian_interface.py
Normal file
380
diceplayer/shared/interface/gaussian_interface.py
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from diceplayer.shared.config.player_config import PlayerConfig
|
||||||
|
from diceplayer.shared.environment.molecule import Molecule
|
||||||
|
from diceplayer.shared.environment.system import System
|
||||||
|
from diceplayer.shared.environment.atom import Atom
|
||||||
|
from diceplayer.shared.utils.misc import date_time
|
||||||
|
from diceplayer.shared.utils.ptable import atomsymb
|
||||||
|
from diceplayer.shared.interface import Interface
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
from typing import Tuple, List, Dict, Any
|
||||||
|
|
||||||
|
from nptyping import NDArray
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class GaussianInterface(Interface):
|
||||||
|
def configure(self, step_dto: PlayerConfig, system: System):
|
||||||
|
self.system = system
|
||||||
|
self.step = step_dto
|
||||||
|
|
||||||
|
def start(self, cycle: int) -> Dict[str, NDArray]:
|
||||||
|
self._make_qm_dir(cycle)
|
||||||
|
|
||||||
|
if cycle > 1:
|
||||||
|
self._copy_chk_file_from_previous_step(cycle)
|
||||||
|
|
||||||
|
asec_charges = self.populate_asec_vdw(cycle)
|
||||||
|
self._make_gaussian_input_file(
|
||||||
|
cycle,
|
||||||
|
asec_charges
|
||||||
|
)
|
||||||
|
|
||||||
|
self._run_gaussian(cycle)
|
||||||
|
self._run_formchk(cycle)
|
||||||
|
|
||||||
|
return_value = {}
|
||||||
|
if self.step.opt:
|
||||||
|
# return_value['position'] = np.array(
|
||||||
|
# self._run_optimization(cycle)
|
||||||
|
# )
|
||||||
|
raise NotImplementedError("Optimization not implemented yet.")
|
||||||
|
|
||||||
|
else:
|
||||||
|
return_value['charges'] = np.array(
|
||||||
|
self._read_charges_from_fchk(cycle)
|
||||||
|
)
|
||||||
|
|
||||||
|
return return_value
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
del self.step
|
||||||
|
del self.system
|
||||||
|
|
||||||
|
def _make_qm_dir(self, cycle: int):
|
||||||
|
qm_dir_path = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
"qm"
|
||||||
|
)
|
||||||
|
if not qm_dir_path.exists():
|
||||||
|
qm_dir_path.mkdir()
|
||||||
|
|
||||||
|
def _copy_chk_file_from_previous_step(self, cycle: int):
|
||||||
|
current_chk_file_path = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
"qm",
|
||||||
|
f"asec.chk"
|
||||||
|
)
|
||||||
|
if current_chk_file_path.exists():
|
||||||
|
raise FileExistsError(
|
||||||
|
f"File {current_chk_file_path} already exists."
|
||||||
|
)
|
||||||
|
|
||||||
|
previous_chk_file_path = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{(cycle - 1):02d}",
|
||||||
|
"qm",
|
||||||
|
f"asec.chk"
|
||||||
|
)
|
||||||
|
if not previous_chk_file_path.exists():
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"File {previous_chk_file_path} does not exist."
|
||||||
|
)
|
||||||
|
|
||||||
|
shutil.copy(previous_chk_file_path, current_chk_file_path)
|
||||||
|
|
||||||
|
def populate_asec_vdw(self, cycle: int) -> list[dict]:
|
||||||
|
norm_factor = self._calculate_norm_factor()
|
||||||
|
|
||||||
|
nsitesref = len(self.system.molecule[0].atom)
|
||||||
|
|
||||||
|
nsites_total = self._calculate_total_number_of_sites(nsitesref)
|
||||||
|
|
||||||
|
proc_charges = []
|
||||||
|
for proc in range(1, self.step.nprocs + 1):
|
||||||
|
proc_charges.append(self._read_charges_from_last_step(cycle, proc))
|
||||||
|
|
||||||
|
asec_charges, thickness, picked_mols = \
|
||||||
|
self._evaluate_proc_charges(nsites_total, proc_charges)
|
||||||
|
|
||||||
|
logger.info(f"In average, {(sum(picked_mols) / norm_factor):^7.2f} molecules\n"
|
||||||
|
f"were selected from each of the {len(picked_mols)} configurations\n"
|
||||||
|
f"of the production simulations to form the ASEC, comprising a shell with\n"
|
||||||
|
f"minimum thickness of {(sum(thickness) / norm_factor):>6.2f} Angstrom\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
for charge in asec_charges:
|
||||||
|
charge['chg'] = charge['chg'] / norm_factor
|
||||||
|
|
||||||
|
return asec_charges
|
||||||
|
|
||||||
|
def _calculate_norm_factor(self) -> int:
|
||||||
|
if self.step.dice.nstep[-1] % self.step.dice.isave == 0:
|
||||||
|
nconfigs = round(self.step.dice.nstep[-1] / self.step.dice.isave)
|
||||||
|
else:
|
||||||
|
nconfigs = int(self.step.dice.nstep[-1] / self.step.dice.isave)
|
||||||
|
|
||||||
|
return nconfigs * self.step.nprocs
|
||||||
|
|
||||||
|
def _calculate_total_number_of_sites(self, nsitesref) -> int:
|
||||||
|
nsites_total = self.step.dice.nmol[0] * nsitesref
|
||||||
|
for i in range(1, len(self.step.dice.nmol)):
|
||||||
|
nsites_total += self.step.dice.nmol[i] * len(self.system.molecule[i].atom)
|
||||||
|
|
||||||
|
return nsites_total
|
||||||
|
|
||||||
|
def _read_charges_from_last_step(self, cycle: int, proc: int) -> list[str]:
|
||||||
|
last_xyz_file_path = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
f"p{proc:02d}",
|
||||||
|
"last.xyz"
|
||||||
|
)
|
||||||
|
if not last_xyz_file_path.exists():
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"File {last_xyz_file_path} does not exist."
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(last_xyz_file_path, 'r') as last_xyz_file:
|
||||||
|
lines = last_xyz_file.readlines()
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def _evaluate_proc_charges(self, total_nsites: int, proc_charges: list[list[str]]) -> Tuple[
|
||||||
|
List[Dict[str, float | Any]], List[float], List[int]]:
|
||||||
|
asec_charges = []
|
||||||
|
|
||||||
|
thickness = []
|
||||||
|
picked_mols = []
|
||||||
|
|
||||||
|
for charges in proc_charges:
|
||||||
|
charges_nsites = int(charges.pop(0))
|
||||||
|
if int(charges_nsites) != total_nsites:
|
||||||
|
raise ValueError(
|
||||||
|
f"Number of sites does not match total number of sites."
|
||||||
|
)
|
||||||
|
|
||||||
|
thickness.append(
|
||||||
|
self._calculate_proc_thickness(charges)
|
||||||
|
)
|
||||||
|
nsites_ref_mol = len(self.system.molecule[0].atom)
|
||||||
|
charges = charges[nsites_ref_mol:]
|
||||||
|
|
||||||
|
mol_count = 0
|
||||||
|
for type in range(len(self.step.dice.nmol)):
|
||||||
|
if type == 0:
|
||||||
|
# Reference Molecule must be ignored from type 0
|
||||||
|
nmols = self.step.dice.nmol[type] - 1
|
||||||
|
else:
|
||||||
|
nmols = self.step.dice.nmol[type]
|
||||||
|
|
||||||
|
for mol in range(nmols):
|
||||||
|
new_molecule = Molecule("ASEC TMP MOLECULE")
|
||||||
|
for site in range(len(self.system.molecule[type].atom)):
|
||||||
|
line = charges.pop(0).split()
|
||||||
|
|
||||||
|
if line[0].title() != atomsymb[self.system.molecule[type].atom[site].na].strip():
|
||||||
|
raise SyntaxError(
|
||||||
|
f"Error: Invalid Dice Output. Atom type does not match."
|
||||||
|
)
|
||||||
|
|
||||||
|
new_molecule.add_atom(
|
||||||
|
Atom(
|
||||||
|
self.system.molecule[type].atom[site].lbl,
|
||||||
|
self.system.molecule[type].atom[site].na,
|
||||||
|
float(line[1]),
|
||||||
|
float(line[2]),
|
||||||
|
float(line[3]),
|
||||||
|
self.system.molecule[type].atom[site].chg,
|
||||||
|
self.system.molecule[type].atom[site].eps,
|
||||||
|
self.system.molecule[type].atom[site].sig,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
distance = self.system.molecule[0] \
|
||||||
|
.minimum_distance(new_molecule)
|
||||||
|
|
||||||
|
if distance < thickness[-1]:
|
||||||
|
for atom in new_molecule.atom:
|
||||||
|
asec_charges.append(
|
||||||
|
{"lbl": atomsymb[atom.na], "rx": atom.rx, "ry": atom.ry, "rz": atom.rz, "chg": atom.chg}
|
||||||
|
)
|
||||||
|
mol_count += 1
|
||||||
|
|
||||||
|
picked_mols.append(mol_count)
|
||||||
|
|
||||||
|
return asec_charges, thickness, picked_mols
|
||||||
|
|
||||||
|
def _calculate_proc_thickness(self, charges: list[str]) -> float:
|
||||||
|
box = charges.pop(0).split()[-3:]
|
||||||
|
box = [float(box[0]), float(box[1]), float(box[2])]
|
||||||
|
sizes = self.system.molecule[0].sizes_of_molecule()
|
||||||
|
|
||||||
|
return min(
|
||||||
|
[
|
||||||
|
(box[0] - sizes[0]) / 2,
|
||||||
|
(box[1] - sizes[1]) / 2,
|
||||||
|
(box[2] - sizes[2]) / 2,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def _make_gaussian_input_file(self, cycle: int, asec_charges: list[dict]) -> None:
|
||||||
|
gaussian_input_file_path = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
"qm",
|
||||||
|
f"asec.gjf"
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(gaussian_input_file_path, 'w') as gaussian_input_file:
|
||||||
|
gaussian_input_file.writelines(
|
||||||
|
self._generate_gaussian_input(cycle, asec_charges)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _generate_gaussian_input(self, cycle: int, asec_charges: list[dict]) -> list[str]:
|
||||||
|
gaussian_input = ["%Chk=asec.chk\n"]
|
||||||
|
|
||||||
|
if self.step.mem is not None:
|
||||||
|
gaussian_input.append(f"%Mem={self.step.mem}GB\n")
|
||||||
|
|
||||||
|
gaussian_input.append(f"%Nprocs={self.step.nprocs * self.step.ncores}\n")
|
||||||
|
|
||||||
|
kwords_line = f"#P {self.step.gaussian.level}"
|
||||||
|
|
||||||
|
if self.step.gaussian.keywords:
|
||||||
|
kwords_line += " " + self.step.gaussian.keywords
|
||||||
|
|
||||||
|
if self.step.opt == "yes":
|
||||||
|
kwords_line += " Force"
|
||||||
|
|
||||||
|
kwords_line += " NoSymm"
|
||||||
|
kwords_line += f" Pop={self.step.gaussian.pop} Density=Current"
|
||||||
|
|
||||||
|
if cycle > 1:
|
||||||
|
kwords_line += " Guess=Read"
|
||||||
|
|
||||||
|
gaussian_input.append(textwrap.fill(kwords_line, 90))
|
||||||
|
gaussian_input.append("\n")
|
||||||
|
|
||||||
|
gaussian_input.append("\nForce calculation - Cycle number {}\n".format(cycle))
|
||||||
|
gaussian_input.append("\n")
|
||||||
|
gaussian_input.append(f"{self.step.gaussian.chgmult[0]},{self.step.gaussian.chgmult[1]}\n")
|
||||||
|
|
||||||
|
for atom in self.system.molecule[0].atom:
|
||||||
|
symbol = atomsymb[atom.na]
|
||||||
|
gaussian_input.append(
|
||||||
|
"{:<2s} {:>10.5f} {:>10.5f} {:>10.5f}\n".format(
|
||||||
|
symbol, atom.rx, atom.ry, atom.rz
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
gaussian_input.append("\n")
|
||||||
|
|
||||||
|
for charge in asec_charges:
|
||||||
|
gaussian_input.append(
|
||||||
|
"{:>10.5f} {:>10.5f} {:>10.5f} {:>11.8f}\n".format(
|
||||||
|
charge['rx'], charge['ry'], charge['rz'], charge['chg']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
gaussian_input.append("\n")
|
||||||
|
|
||||||
|
return gaussian_input
|
||||||
|
|
||||||
|
def _run_gaussian(self, cycle: int) -> None:
|
||||||
|
qm_dir = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{(cycle):02d}",
|
||||||
|
"qm"
|
||||||
|
)
|
||||||
|
|
||||||
|
working_dir = os.getcwd()
|
||||||
|
os.chdir(qm_dir)
|
||||||
|
|
||||||
|
infile = "asec.gjf"
|
||||||
|
|
||||||
|
operation = None
|
||||||
|
if self.step.opt:
|
||||||
|
operation = "forces"
|
||||||
|
else:
|
||||||
|
operation = "charges"
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Calculation of {operation} initiated with Gaussian on {date_time()}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
if shutil.which("bash") is not None:
|
||||||
|
exit_status = subprocess.call(
|
||||||
|
[
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"exec -a {}-step{} {} {}".format(
|
||||||
|
self.step.gaussian.qmprog, cycle, self.step.gaussian.qmprog, infile
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
exit_status = subprocess.call([self.step.gaussian.qmprog, infile])
|
||||||
|
|
||||||
|
if exit_status != 0:
|
||||||
|
raise SystemError("Gaussian process did not exit properly")
|
||||||
|
|
||||||
|
logger.info(f"Calculation of {operation} finished on {date_time()}")
|
||||||
|
|
||||||
|
os.chdir(working_dir)
|
||||||
|
|
||||||
|
def _run_formchk(self, cycle: int):
|
||||||
|
qm_dir = Path(
|
||||||
|
self.step.simulation_dir,
|
||||||
|
f"step{(cycle):02d}",
|
||||||
|
"qm"
|
||||||
|
)
|
||||||
|
|
||||||
|
work_dir = os.getcwd()
|
||||||
|
os.chdir(qm_dir)
|
||||||
|
|
||||||
|
logger.info("Formatting the checkpoint file... \n")
|
||||||
|
|
||||||
|
exit_status = subprocess.call(["formchk", "asec.chk"], stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
if exit_status != 0:
|
||||||
|
raise SystemError("Formchk process did not exit properly")
|
||||||
|
|
||||||
|
logger.info("Done\n")
|
||||||
|
|
||||||
|
os.chdir(work_dir)
|
||||||
|
|
||||||
|
def _read_charges_from_fchk(self, cycle: int):
|
||||||
|
fchk_file_path = Path(
|
||||||
|
"simfiles",
|
||||||
|
f"step{cycle:02d}",
|
||||||
|
"qm",
|
||||||
|
"asec.fchk"
|
||||||
|
)
|
||||||
|
with open(fchk_file_path) as fchk:
|
||||||
|
fchkfile = fchk.readlines()
|
||||||
|
|
||||||
|
if self.step.gaussian.pop in ["chelpg", "mk"]:
|
||||||
|
CHARGE_FLAG = "ESP Charges"
|
||||||
|
else:
|
||||||
|
CHARGE_FLAG = "ESP Charges"
|
||||||
|
|
||||||
|
start = fchkfile.pop(0).strip()
|
||||||
|
while start.find(CHARGE_FLAG) != 0: # expression in begining of line
|
||||||
|
start = fchkfile.pop(0).strip()
|
||||||
|
|
||||||
|
charges: List[float] = []
|
||||||
|
while len(charges) < len(self.system.molecule[0].atom):
|
||||||
|
charges.extend([float(x) for x in fchkfile.pop(0).split()])
|
||||||
|
|
||||||
|
return charges
|
||||||
6
diceplayer/shared/utils/dataclass_protocol.py
Normal file
6
diceplayer/shared/utils/dataclass_protocol.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from typing import runtime_checkable, Protocol
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class Dataclass(Protocol):
|
||||||
|
__dataclass_fields__: dict
|
||||||
73
diceplayer/shared/utils/logger.py
Normal file
73
diceplayer/shared/utils/logger.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def valid_logger(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
logger = args[0]
|
||||||
|
assert logger._was_set, \
|
||||||
|
"Logger is not set. Please call set_logger() first."
|
||||||
|
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class Logger:
|
||||||
|
outfile = None
|
||||||
|
|
||||||
|
_logger = None
|
||||||
|
|
||||||
|
_was_set = False
|
||||||
|
|
||||||
|
def __init__(self, logger_name):
|
||||||
|
if self._logger is None:
|
||||||
|
self._logger = logging.getLogger(logger_name)
|
||||||
|
|
||||||
|
def set_logger(self, outfile='run.log', level=logging.INFO, stream=None):
|
||||||
|
outfile_path = None
|
||||||
|
if outfile is not None and stream is None:
|
||||||
|
outfile_path = Path(outfile)
|
||||||
|
if outfile_path.exists():
|
||||||
|
outfile_path.rename(str(outfile_path) + ".backup")
|
||||||
|
|
||||||
|
if level is not None:
|
||||||
|
self._logger.setLevel(level)
|
||||||
|
|
||||||
|
self._create_handlers(outfile_path, stream)
|
||||||
|
|
||||||
|
self._was_set = True
|
||||||
|
|
||||||
|
@valid_logger
|
||||||
|
def info(self, message):
|
||||||
|
self._logger.info(message)
|
||||||
|
|
||||||
|
@valid_logger
|
||||||
|
def debug(self, message):
|
||||||
|
self._logger.debug(message)
|
||||||
|
|
||||||
|
@valid_logger
|
||||||
|
def warning(self, message):
|
||||||
|
self._logger.warning(message)
|
||||||
|
|
||||||
|
@valid_logger
|
||||||
|
def error(self, message):
|
||||||
|
self._logger.error(message)
|
||||||
|
|
||||||
|
def _create_handlers(self, outfile_path: Path, stream):
|
||||||
|
handlers = []
|
||||||
|
if outfile_path is not None:
|
||||||
|
handlers.append(logging.FileHandler(outfile_path, mode='a+'))
|
||||||
|
elif stream is not None:
|
||||||
|
handlers.append(logging.StreamHandler(stream))
|
||||||
|
else:
|
||||||
|
handlers.append(logging.StreamHandler())
|
||||||
|
|
||||||
|
for handler in handlers:
|
||||||
|
handler.setFormatter(logging.Formatter('%(message)s'))
|
||||||
|
self._logger.addHandler(handler)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
for handler in self._logger.handlers:
|
||||||
|
handler.close()
|
||||||
|
self._logger.removeHandler(handler)
|
||||||
64
diceplayer/shared/utils/misc.py
Normal file
64
diceplayer/shared/utils/misc.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import gzip
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
####################################### constants ######################################
|
||||||
|
|
||||||
|
|
||||||
|
BOHR2ANG: Final[float] = 0.52917721092
|
||||||
|
ANG2BOHR: Final[float] = 1 / BOHR2ANG
|
||||||
|
|
||||||
|
|
||||||
|
####################################### functions ######################################
|
||||||
|
|
||||||
|
def weekday_date_time():
|
||||||
|
return time.strftime("%A, %d %b %Y at %H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
|
def date_time():
|
||||||
|
return time.strftime("%d %b %Y at %H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
|
def compress_files_1mb(path):
|
||||||
|
working_dir = os.getcwd()
|
||||||
|
os.chdir(path)
|
||||||
|
|
||||||
|
files = filter(os.path.isfile, os.listdir(os.curdir))
|
||||||
|
for file in files:
|
||||||
|
if os.path.getsize(file) > 1024 * 1024: ## If bigger than 1MB
|
||||||
|
filegz = file + ".gz"
|
||||||
|
try:
|
||||||
|
with open(file, 'rb') as f_in:
|
||||||
|
with gzip.open(filegz, 'wb') as f_out:
|
||||||
|
shutil.copyfileobj(f_in, f_out)
|
||||||
|
except:
|
||||||
|
sys.exit("Error: cannot compress file {}".format(file))
|
||||||
|
|
||||||
|
os.chdir(working_dir)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def make_step_dir(cycle):
|
||||||
|
sim_dir = "simfiles"
|
||||||
|
step_dir = "step{:02d}".format(cycle)
|
||||||
|
path = sim_dir + os.sep + step_dir
|
||||||
|
if os.path.exists(path):
|
||||||
|
sys.exit("Error: a file or directory {} already exists".format(step_dir))
|
||||||
|
try:
|
||||||
|
os.makedirs(path)
|
||||||
|
except:
|
||||||
|
sys.exit("Error: cannot make directory {}".format(step_dir))
|
||||||
|
|
||||||
|
|
||||||
|
def make_qm_dir(cycle):
|
||||||
|
sim_dir = "simfiles"
|
||||||
|
step_dir = "step{:02d}".format(cycle)
|
||||||
|
path = sim_dir + os.sep + step_dir + os.sep + "qm"
|
||||||
|
try:
|
||||||
|
os.makedirs(path)
|
||||||
|
except:
|
||||||
|
sys.exit("Error: cannot make directory {}".format(path))
|
||||||
313
poetry.lock
generated
Normal file
313
poetry.lock
generated
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argparse"
|
||||||
|
version = "1.4.0"
|
||||||
|
description = "Python command-line parsing library"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"},
|
||||||
|
{file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "coverage"
|
||||||
|
version = "7.2.7"
|
||||||
|
description = "Code coverage measurement for Python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"},
|
||||||
|
{file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"},
|
||||||
|
{file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"},
|
||||||
|
{file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"},
|
||||||
|
{file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"},
|
||||||
|
{file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"},
|
||||||
|
{file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"},
|
||||||
|
{file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"},
|
||||||
|
{file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
toml = ["tomli"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dacite"
|
||||||
|
version = "1.8.1"
|
||||||
|
description = "Simple creation of data classes from dictionaries."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "dacite-1.8.1-py3-none-any.whl", hash = "sha256:cc31ad6fdea1f49962ea42db9421772afe01ac5442380d9a99fcf3d188c61afe"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["black", "coveralls", "mypy", "pre-commit", "pylint", "pytest (>=5)", "pytest-benchmark", "pytest-cov"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nptyping"
|
||||||
|
version = "2.5.0"
|
||||||
|
description = "Type hints for NumPy."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "nptyping-2.5.0-py3-none-any.whl", hash = "sha256:764e51836faae33a7ae2e928af574cfb701355647accadcc89f2ad793630b7c8"},
|
||||||
|
{file = "nptyping-2.5.0.tar.gz", hash = "sha256:e3d35b53af967e6fb407c3016ff9abae954d3a0568f7cc13a461084224e8e20a"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
numpy = {version = ">=1.20.0,<2.0.0", markers = "python_version >= \"3.8\""}
|
||||||
|
typing-extensions = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.10\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
build = ["invoke (>=1.6.0)", "pip-tools (>=6.5.0)"]
|
||||||
|
complete = ["pandas", "pandas-stubs-fork"]
|
||||||
|
dev = ["autoflake", "beartype (<0.10.0)", "beartype (>=0.10.0)", "black", "codecov (>=2.1.0)", "coverage", "feedparser", "invoke (>=1.6.0)", "isort", "mypy", "pandas", "pandas-stubs-fork", "pip-tools (>=6.5.0)", "pylint", "pyright", "setuptools", "typeguard", "wheel"]
|
||||||
|
pandas = ["pandas", "pandas-stubs-fork"]
|
||||||
|
qa = ["autoflake", "beartype (<0.10.0)", "beartype (>=0.10.0)", "black", "codecov (>=2.1.0)", "coverage", "feedparser", "isort", "mypy", "pylint", "pyright", "setuptools", "typeguard", "wheel"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numpy"
|
||||||
|
version = "1.24.3"
|
||||||
|
description = "Fundamental package for array computing in Python"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "numpy-1.24.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c1104d3c036fb81ab923f507536daedc718d0ad5a8707c6061cdfd6d184e570"},
|
||||||
|
{file = "numpy-1.24.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:202de8f38fc4a45a3eea4b63e2f376e5f2dc64ef0fa692838e31a808520efaf7"},
|
||||||
|
{file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8535303847b89aa6b0f00aa1dc62867b5a32923e4d1681a35b5eef2d9591a463"},
|
||||||
|
{file = "numpy-1.24.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d926b52ba1367f9acb76b0df6ed21f0b16a1ad87c6720a1121674e5cf63e2b6"},
|
||||||
|
{file = "numpy-1.24.3-cp310-cp310-win32.whl", hash = "sha256:f21c442fdd2805e91799fbe044a7b999b8571bb0ab0f7850d0cb9641a687092b"},
|
||||||
|
{file = "numpy-1.24.3-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f23af8c16022663a652d3b25dcdc272ac3f83c3af4c02eb8b824e6b3ab9d7"},
|
||||||
|
{file = "numpy-1.24.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a7721ec204d3a237225db3e194c25268faf92e19338a35f3a224469cb6039a3"},
|
||||||
|
{file = "numpy-1.24.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d6cc757de514c00b24ae8cf5c876af2a7c3df189028d68c0cb4eaa9cd5afc2bf"},
|
||||||
|
{file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e3f4e85fc5d4fd311f6e9b794d0c00e7002ec122be271f2019d63376f1d385"},
|
||||||
|
{file = "numpy-1.24.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1d3c026f57ceaad42f8231305d4653d5f05dc6332a730ae5c0bea3513de0950"},
|
||||||
|
{file = "numpy-1.24.3-cp311-cp311-win32.whl", hash = "sha256:c91c4afd8abc3908e00a44b2672718905b8611503f7ff87390cc0ac3423fb096"},
|
||||||
|
{file = "numpy-1.24.3-cp311-cp311-win_amd64.whl", hash = "sha256:5342cf6aad47943286afa6f1609cad9b4266a05e7f2ec408e2cf7aea7ff69d80"},
|
||||||
|
{file = "numpy-1.24.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7776ea65423ca6a15255ba1872d82d207bd1e09f6d0894ee4a64678dd2204078"},
|
||||||
|
{file = "numpy-1.24.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ae8d0be48d1b6ed82588934aaaa179875e7dc4f3d84da18d7eae6eb3f06c242c"},
|
||||||
|
{file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecde0f8adef7dfdec993fd54b0f78183051b6580f606111a6d789cd14c61ea0c"},
|
||||||
|
{file = "numpy-1.24.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4749e053a29364d3452c034827102ee100986903263e89884922ef01a0a6fd2f"},
|
||||||
|
{file = "numpy-1.24.3-cp38-cp38-win32.whl", hash = "sha256:d933fabd8f6a319e8530d0de4fcc2e6a61917e0b0c271fded460032db42a0fe4"},
|
||||||
|
{file = "numpy-1.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:56e48aec79ae238f6e4395886b5eaed058abb7231fb3361ddd7bfdf4eed54289"},
|
||||||
|
{file = "numpy-1.24.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4719d5aefb5189f50887773699eaf94e7d1e02bf36c1a9d353d9f46703758ca4"},
|
||||||
|
{file = "numpy-1.24.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ec87a7084caa559c36e0a2309e4ecb1baa03b687201d0a847c8b0ed476a7187"},
|
||||||
|
{file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea8282b9bcfe2b5e7d491d0bf7f3e2da29700cec05b49e64d6246923329f2b02"},
|
||||||
|
{file = "numpy-1.24.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210461d87fb02a84ef243cac5e814aad2b7f4be953b32cb53327bb49fd77fbb4"},
|
||||||
|
{file = "numpy-1.24.3-cp39-cp39-win32.whl", hash = "sha256:784c6da1a07818491b0ffd63c6bbe5a33deaa0e25a20e1b3ea20cf0e43f8046c"},
|
||||||
|
{file = "numpy-1.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:d5036197ecae68d7f491fcdb4df90082b0d4960ca6599ba2659957aafced7c17"},
|
||||||
|
{file = "numpy-1.24.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:352ee00c7f8387b44d19f4cada524586f07379c0d49270f87233983bc5087ca0"},
|
||||||
|
{file = "numpy-1.24.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d6acc2e7524c9955e5c903160aa4ea083736fde7e91276b0e5d98e6332812"},
|
||||||
|
{file = "numpy-1.24.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:35400e6a8d102fd07c71ed7dcadd9eb62ee9a6e84ec159bd48c28235bbb0f8e4"},
|
||||||
|
{file = "numpy-1.24.3.tar.gz", hash = "sha256:ab344f1bf21f140adab8e47fdbc7c35a477dc01408791f8ba00d018dd0bc5155"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyyaml"
|
||||||
|
version = "6.0"
|
||||||
|
description = "YAML parser and emitter for Python"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
|
||||||
|
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
|
||||||
|
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
|
||||||
|
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
|
||||||
|
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
|
||||||
|
{file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
|
||||||
|
{file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
|
||||||
|
{file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
|
||||||
|
{file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
|
||||||
|
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
|
||||||
|
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
|
||||||
|
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
|
||||||
|
{file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
|
||||||
|
{file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
|
||||||
|
{file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
|
||||||
|
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
|
||||||
|
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
|
||||||
|
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
|
||||||
|
{file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
|
||||||
|
{file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
|
||||||
|
{file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
|
||||||
|
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
|
||||||
|
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
|
||||||
|
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
|
||||||
|
{file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
|
||||||
|
{file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
|
||||||
|
{file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
|
||||||
|
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
|
||||||
|
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
|
||||||
|
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
|
||||||
|
{file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
|
||||||
|
{file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
|
||||||
|
{file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
|
||||||
|
{file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
|
||||||
|
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
|
||||||
|
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
|
||||||
|
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
|
||||||
|
{file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
|
||||||
|
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
||||||
|
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "setproctitle"
|
||||||
|
version = "1.3.2"
|
||||||
|
description = "A Python module to customize the process title"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:288943dec88e178bb2fd868adf491197cc0fc8b6810416b1c6775e686bab87fe"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:630f6fe5e24a619ccf970c78e084319ee8be5be253ecc9b5b216b0f474f5ef18"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c877691b90026670e5a70adfbcc735460a9f4c274d35ec5e8a43ce3f8443005"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a55fe05f15c10e8c705038777656fe45e3bd676d49ad9ac8370b75c66dd7cd7"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab45146c71ca6592c9cc8b354a2cc9cc4843c33efcbe1d245d7d37ce9696552d"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00c9d5c541a2713ba0e657e0303bf96ddddc412ef4761676adc35df35d7c246"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:265ecbe2c6eafe82e104f994ddd7c811520acdd0647b73f65c24f51374cf9494"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c2c46200656280a064073447ebd363937562debef329482fd7e570c8d498f806"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:fa2f50678f04fda7a75d0fe5dd02bbdd3b13cbe6ed4cf626e4472a7ccf47ae94"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f2719a398e1a2c01c2a63bf30377a34d0b6ef61946ab9cf4d550733af8f1ef1"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-win32.whl", hash = "sha256:e425be62524dc0c593985da794ee73eb8a17abb10fe692ee43bb39e201d7a099"},
|
||||||
|
{file = "setproctitle-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:e85e50b9c67854f89635a86247412f3ad66b132a4d8534ac017547197c88f27d"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a97d51c17d438cf5be284775a322d57b7ca9505bb7e118c28b1824ecaf8aeaa"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587c7d6780109fbd8a627758063d08ab0421377c0853780e5c356873cdf0f077"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d17c8bd073cbf8d141993db45145a70b307385b69171d6b54bcf23e5d644de"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e932089c35a396dc31a5a1fc49889dd559548d14cb2237adae260382a090382e"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e4f8f12258a8739c565292a551c3db62cca4ed4f6b6126664e2381acb4931bf"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:570d255fd99c7f14d8f91363c3ea96bd54f8742275796bca67e1414aeca7d8c3"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a8e0881568c5e6beff91ef73c0ec8ac2a9d3ecc9edd6bd83c31ca34f770910c4"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4bba3be4c1fabf170595b71f3af46c6d482fbe7d9e0563999b49999a31876f77"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:37ece938110cab2bb3957e3910af8152ca15f2b6efdf4f2612e3f6b7e5459b80"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db684d6bbb735a80bcbc3737856385b55d53f8a44ce9b46e9a5682c5133a9bf7"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-win32.whl", hash = "sha256:ca58cd260ea02759238d994cfae844fc8b1e206c684beb8f38877dcab8451dfc"},
|
||||||
|
{file = "setproctitle-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:88486e6cce2a18a033013d17b30a594f1c5cb42520c49c19e6ade40b864bb7ff"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:92c626edc66169a1b09e9541b9c0c9f10488447d8a2b1d87c8f0672e771bc927"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710e16fa3bade3b026907e4a5e841124983620046166f355bbb84be364bf2a02"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f29b75e86260b0ab59adb12661ef9f113d2f93a59951373eb6d68a852b13e83"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c8d9650154afaa86a44ff195b7b10d683c73509d085339d174e394a22cccbb9"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0452282258dfcc01697026a8841258dd2057c4438b43914b611bccbcd048f10"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e49ae693306d7624015f31cb3e82708916759d592c2e5f72a35c8f4cc8aef258"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1ff863a20d1ff6ba2c24e22436a3daa3cd80be1dfb26891aae73f61b54b04aca"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:55ce1e9925ce1765865442ede9dca0ba9bde10593fcd570b1f0fa25d3ec6b31c"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7fe9df7aeb8c64db6c34fc3b13271a363475d77bc157d3f00275a53910cb1989"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-win32.whl", hash = "sha256:e5c50e164cd2459bc5137c15288a9ef57160fd5cbf293265ea3c45efe7870865"},
|
||||||
|
{file = "setproctitle-1.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a499fff50387c1520c085a07578a000123f519e5f3eee61dd68e1d301659651f"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b932c3041aa924163f4aab970c2f0e6b4d9d773f4d50326e0ea1cd69240e5c5"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4bfc89bd33ebb8e4c0e9846a09b1f5a4a86f5cb7a317e75cc42fee1131b4f4f"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd3cf4286a60fdc95451d8d14e0389a6b4f5cebe02c7f2609325eb016535963"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fb4f769c02f63fac90989711a3fee83919f47ae9afd4758ced5d86596318c65"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5194b4969f82ea842a4f6af2f82cd16ebdc3f1771fb2771796e6add9835c1973"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cde41857a644b7353a0060b5f94f7ba7cf593ebde5a1094da1be581ac9a31"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9124bedd8006b0e04d4e8a71a0945da9b67e7a4ab88fdad7b1440dc5b6122c42"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c8a09d570b39517de10ee5b718730e171251ce63bbb890c430c725c8c53d4484"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8ff3c8cb26afaed25e8bca7b9dd0c1e36de71f35a3a0706b5c0d5172587a3827"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:589be87172b238f839e19f146b9ea47c71e413e951ef0dc6db4218ddacf3c202"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-win32.whl", hash = "sha256:4749a2b0c9ac52f864d13cee94546606f92b981b50e46226f7f830a56a9dc8e1"},
|
||||||
|
{file = "setproctitle-1.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:e43f315c68aa61cbdef522a2272c5a5b9b8fd03c301d3167b5e1343ef50c676c"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:de3a540cd1817ede31f530d20e6a4935bbc1b145fd8f8cf393903b1e02f1ae76"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4058564195b975ddc3f0462375c533cce310ccdd41b80ac9aed641c296c3eff4"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c5d5dad7c28bdd1ec4187d818e43796f58a845aa892bb4481587010dc4d362b"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffc61a388a5834a97953d6444a2888c24a05f2e333f9ed49f977a87bb1ad4761"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fa1a0fbee72b47dc339c87c890d3c03a72ea65c061ade3204f285582f2da30f"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8a988c7220c002c45347430993830666e55bc350179d91fcee0feafe64e1d4"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bae283e85fc084b18ffeb92e061ff7ac5af9e183c9d1345c93e178c3e5069cbe"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fed18e44711c5af4b681c2b3b18f85e6f0f1b2370a28854c645d636d5305ccd8"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b34baef93bfb20a8ecb930e395ccd2ae3268050d8cf4fe187de5e2bd806fd796"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7f0bed90a216ef28b9d227d8d73e28a8c9b88c0f48a082d13ab3fa83c581488f"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-win32.whl", hash = "sha256:4d8938249a7cea45ab7e1e48b77685d0f2bab1ebfa9dde23e94ab97968996a7c"},
|
||||||
|
{file = "setproctitle-1.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:a47d97a75fd2d10c37410b180f67a5835cb1d8fdea2648fd7f359d4277f180b9"},
|
||||||
|
{file = "setproctitle-1.3.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:dad42e676c5261eb50fdb16bdf3e2771cf8f99a79ef69ba88729aeb3472d8575"},
|
||||||
|
{file = "setproctitle-1.3.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c91b9bc8985d00239f7dc08a49927a7ca1ca8a6af2c3890feec3ed9665b6f91e"},
|
||||||
|
{file = "setproctitle-1.3.2-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8579a43eafd246e285eb3a5b939e7158073d5087aacdd2308f23200eac2458b"},
|
||||||
|
{file = "setproctitle-1.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:2fbd8187948284293f43533c150cd69a0e4192c83c377da837dbcd29f6b83084"},
|
||||||
|
{file = "setproctitle-1.3.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:faec934cfe5fd6ac1151c02e67156c3f526e82f96b24d550b5d51efa4a5527c6"},
|
||||||
|
{file = "setproctitle-1.3.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1aafc91cbdacc9e5fe712c52077369168e6b6c346f3a9d51bf600b53eae56bb"},
|
||||||
|
{file = "setproctitle-1.3.2-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b617f12c9be61e8f4b2857be4a4319754756845dbbbd9c3718f468bbb1e17bcb"},
|
||||||
|
{file = "setproctitle-1.3.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b2c9cb2705fc84cb8798f1ba74194f4c080aaef19d9dae843591c09b97678e98"},
|
||||||
|
{file = "setproctitle-1.3.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a149a5f7f2c5a065d4e63cb0d7a4b6d3b66e6e80f12e3f8827c4f63974cbf122"},
|
||||||
|
{file = "setproctitle-1.3.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e3ac25bfc4a0f29d2409650c7532d5ddfdbf29f16f8a256fc31c47d0dc05172"},
|
||||||
|
{file = "setproctitle-1.3.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65d884e22037b23fa25b2baf1a3316602ed5c5971eb3e9d771a38c3a69ce6e13"},
|
||||||
|
{file = "setproctitle-1.3.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7aa0aac1711fadffc1d51e9d00a3bea61f68443d6ac0241a224e4d622489d665"},
|
||||||
|
{file = "setproctitle-1.3.2.tar.gz", hash = "sha256:b9fb97907c830d260fa0658ed58afd48a86b2b88aac521135c352ff7fd3477fd"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
test = ["pytest"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.6.3"
|
||||||
|
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"},
|
||||||
|
{file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.0"
|
||||||
|
python-versions = "^3.8"
|
||||||
|
content-hash = "5767445dc10a28eeb01ba23bfbf781b68d8c158df80be696d53cb0c962d409ab"
|
||||||
24
pyproject.toml
Normal file
24
pyproject.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "diceplayer"
|
||||||
|
version = "0.0.1"
|
||||||
|
description = ""
|
||||||
|
authors = ["Vitor Hideyoshi <vitor.h.n.batista@gmail.com>", "Herbert Georg <hcgeorg@ufg.br>"]
|
||||||
|
license = "GPL-2.0-only"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.8"
|
||||||
|
numpy = "^1.24.3"
|
||||||
|
argparse = "^1.4.0"
|
||||||
|
setproctitle = "^1.3.2"
|
||||||
|
pyyaml = "^6.0"
|
||||||
|
dacite = "^1.8.1"
|
||||||
|
nptyping = "^2.5.0"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
coverage = "^7.2.7"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
62
setup.py
62
setup.py
@@ -1,62 +0,0 @@
|
|||||||
import argparse
|
|
||||||
from distutils.command.clean import clean
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import PyInstaller.__main__
|
|
||||||
|
|
||||||
name = "diceplayer"
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog="Diceplayer Setup")
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-b", "--build",
|
|
||||||
dest="build",
|
|
||||||
default=False,
|
|
||||||
action="store_true",
|
|
||||||
help="Builds the Diceplayer Binary",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-c", "--clean",
|
|
||||||
dest="clean",
|
|
||||||
default=False,
|
|
||||||
action="store_true",
|
|
||||||
help="Cleans the Development Environment"
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def __build():
|
|
||||||
|
|
||||||
PyInstaller.__main__.run(
|
|
||||||
["diceplayer/__main__.py", "--onefile", "-n{}".format(name)]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def __clean():
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
shutil.rmtree("build")
|
|
||||||
shutil.rmtree("dist")
|
|
||||||
os.remove("diceplayer.spec")
|
|
||||||
|
|
||||||
except:
|
|
||||||
|
|
||||||
print("Workspace clean.")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
if args.build:
|
|
||||||
__build()
|
|
||||||
elif args.clean:
|
|
||||||
__clean()
|
|
||||||
else:
|
|
||||||
parser.print_help(sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
0
tests/mocks/__init__.py
Normal file
0
tests/mocks/__init__.py
Normal file
113
tests/mocks/mock_inputs.py
Normal file
113
tests/mocks/mock_inputs.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_example():
|
||||||
|
return """
|
||||||
|
diceplayer:
|
||||||
|
opt: no
|
||||||
|
mem: 12
|
||||||
|
maxcyc: 3
|
||||||
|
ncores: 4
|
||||||
|
nprocs: 4
|
||||||
|
qmprog: 'g16'
|
||||||
|
lps: no
|
||||||
|
ghosts: no
|
||||||
|
altsteps: 20000
|
||||||
|
|
||||||
|
dice:
|
||||||
|
nmol: [1, 50]
|
||||||
|
dens: 0.75
|
||||||
|
nstep: [2000, 3000, 4000]
|
||||||
|
isave: 1000
|
||||||
|
outname: 'phb'
|
||||||
|
progname: '~/.local/bin/dice'
|
||||||
|
ljname: 'phb.ljc'
|
||||||
|
randominit: 'first'
|
||||||
|
|
||||||
|
gaussian:
|
||||||
|
qmprog: 'g16'
|
||||||
|
level: 'MP2/aug-cc-pVDZ'
|
||||||
|
keywords: 'freq'
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_potentials_exemple():
|
||||||
|
return """\
|
||||||
|
*
|
||||||
|
2
|
||||||
|
1 TEST
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
1 PLACEHOLDER
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_potentials_error_combrule():
|
||||||
|
return """\
|
||||||
|
.
|
||||||
|
2
|
||||||
|
1 TEST
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
1 PLACEHOLDER
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_potentials_error_ntypes():
|
||||||
|
return """\
|
||||||
|
*
|
||||||
|
a
|
||||||
|
1 TEST
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
1 PLACEHOLDER
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_potentials_error_ntypes_config():
|
||||||
|
return """\
|
||||||
|
*
|
||||||
|
3
|
||||||
|
1 TEST
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
1 PLACEHOLDER
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_potentials_error_nsites():
|
||||||
|
return """\
|
||||||
|
*
|
||||||
|
2
|
||||||
|
. TEST
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
1 PLACEHOLDER
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_potentials_error_molname():
|
||||||
|
return """\
|
||||||
|
*
|
||||||
|
2
|
||||||
|
1
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
1 PLACEHOLDER
|
||||||
|
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def mock_open(file, *args, **kwargs):
|
||||||
|
values = {
|
||||||
|
"control.test.yml": get_config_example(),
|
||||||
|
"phb.ljc": get_potentials_exemple(),
|
||||||
|
"phb.error.combrule.ljc": get_potentials_error_combrule(),
|
||||||
|
"phb.error.ntypes.ljc": get_potentials_error_ntypes(),
|
||||||
|
"phb.error.ntypes.config.ljc": get_potentials_error_ntypes_config(),
|
||||||
|
"phb.error.nsites.ljc": get_potentials_error_nsites(),
|
||||||
|
"phb.error.molname.ljc": get_potentials_error_molname(),
|
||||||
|
}
|
||||||
|
if file in values:
|
||||||
|
return mock.mock_open(read_data=values[file])()
|
||||||
|
|
||||||
|
return mock.mock_open(read_data="")()
|
||||||
31
tests/mocks/mock_proc.py
Normal file
31
tests/mocks/mock_proc.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from typing import List
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
|
class MockProc:
|
||||||
|
pid_counter = itertools.count()
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.pid = next(MockProc.pid_counter)
|
||||||
|
|
||||||
|
if 'exitcode' in kwargs:
|
||||||
|
self.exitcode = kwargs['exitcode']
|
||||||
|
else:
|
||||||
|
self.exitcode = 0
|
||||||
|
|
||||||
|
self.sentinel = self.pid
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MockConnection:
|
||||||
|
@staticmethod
|
||||||
|
def wait(sentinels: List[int]):
|
||||||
|
return sentinels
|
||||||
0
tests/shared/__init__.py
Normal file
0
tests/shared/__init__.py
Normal file
0
tests/shared/config/__init__.py
Normal file
0
tests/shared/config/__init__.py
Normal file
82
tests/shared/config/test_dice_dto.py
Normal file
82
tests/shared/config/test_dice_dto.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
from diceplayer.shared.config.dice_config import DiceConfig
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiceDto(unittest.TestCase):
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
dice_dto = DiceConfig(
|
||||||
|
ljname='test',
|
||||||
|
outname='test',
|
||||||
|
dens=1.0,
|
||||||
|
nmol=[1],
|
||||||
|
nstep=[1, 1],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(dice_dto, DiceConfig)
|
||||||
|
|
||||||
|
def test_validate_jname(self):
|
||||||
|
with self.assertRaises(ValueError) as ex:
|
||||||
|
DiceConfig(
|
||||||
|
ljname=None,
|
||||||
|
outname='test',
|
||||||
|
dens=1.0,
|
||||||
|
nmol=[1],
|
||||||
|
nstep=[1, 1],
|
||||||
|
)
|
||||||
|
self.assertEqual(ex.exception, "Error: 'ljname' keyword not specified in config file")
|
||||||
|
|
||||||
|
def test_validate_outname(self):
|
||||||
|
with self.assertRaises(ValueError) as ex:
|
||||||
|
DiceConfig(
|
||||||
|
ljname='test',
|
||||||
|
outname=None,
|
||||||
|
dens=1.0,
|
||||||
|
nmol=[1],
|
||||||
|
nstep=[1, 1],
|
||||||
|
)
|
||||||
|
self.assertEqual(ex.exception, "Error: 'outname' keyword not specified in config file")
|
||||||
|
|
||||||
|
def test_validate_dens(self):
|
||||||
|
with self.assertRaises(ValueError) as ex:
|
||||||
|
DiceConfig(
|
||||||
|
ljname='test',
|
||||||
|
outname='test',
|
||||||
|
dens=None,
|
||||||
|
nmol=[1],
|
||||||
|
nstep=[1, 1],
|
||||||
|
)
|
||||||
|
self.assertEqual(ex.exception, "Error: 'dens' keyword not specified in config file")
|
||||||
|
|
||||||
|
def test_validate_nmol(self):
|
||||||
|
with self.assertRaises(ValueError) as ex:
|
||||||
|
DiceConfig(
|
||||||
|
ljname='test',
|
||||||
|
outname='test',
|
||||||
|
dens=1.0,
|
||||||
|
nmol=0,
|
||||||
|
nstep=[1, 1],
|
||||||
|
)
|
||||||
|
self.assertEqual(ex.exception, "Error: 'nmol' keyword not defined appropriately in config file")
|
||||||
|
|
||||||
|
def test_validate_nstep(self):
|
||||||
|
with self.assertRaises(ValueError) as ex:
|
||||||
|
DiceConfig(
|
||||||
|
ljname='test',
|
||||||
|
outname='test',
|
||||||
|
dens=1.0,
|
||||||
|
nmol=[1],
|
||||||
|
nstep=0,
|
||||||
|
)
|
||||||
|
self.assertEqual(ex.exception, "Error: 'nstep' keyword not defined appropriately in config file")
|
||||||
|
|
||||||
|
def test_from_dict(self):
|
||||||
|
dice_dto = DiceConfig.from_dict({
|
||||||
|
'ljname': 'test',
|
||||||
|
'outname': 'test',
|
||||||
|
'dens': 1.0,
|
||||||
|
'nmol': [1],
|
||||||
|
'nstep': [1, 1],
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertIsInstance(dice_dto, DiceConfig)
|
||||||
45
tests/shared/config/test_gaussian_dto.py
Normal file
45
tests/shared/config/test_gaussian_dto.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestGaussianDTO(unittest.TestCase):
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
gaussian_dto = GaussianDTO(
|
||||||
|
level='test',
|
||||||
|
qmprog='g16',
|
||||||
|
keywords='test',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(gaussian_dto, GaussianDTO)
|
||||||
|
|
||||||
|
def test_is_valid_qmprog(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
gaussian_dto = GaussianDTO(
|
||||||
|
level='test',
|
||||||
|
qmprog='test',
|
||||||
|
keywords='test',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_is_valid_level(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
gaussian_dto = GaussianDTO(
|
||||||
|
level=None,
|
||||||
|
qmprog='g16',
|
||||||
|
keywords='test',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_from_dict(self):
|
||||||
|
gaussian_dto = GaussianDTO.from_dict(
|
||||||
|
{
|
||||||
|
'level': 'test',
|
||||||
|
'qmprog': 'g16',
|
||||||
|
'keywords': 'test',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(gaussian_dto, GaussianDTO)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
85
tests/shared/config/test_player_dto.py
Normal file
85
tests/shared/config/test_player_dto.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||||
|
from diceplayer.shared.config.player_config import PlayerConfig
|
||||||
|
from diceplayer.shared.config.dice_config import DiceConfig
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_dict():
|
||||||
|
return {
|
||||||
|
'opt': True,
|
||||||
|
'mem': 12,
|
||||||
|
'maxcyc': 100,
|
||||||
|
'nprocs': 4,
|
||||||
|
'ncores': 4,
|
||||||
|
'dice': {
|
||||||
|
'ljname': 'test',
|
||||||
|
'outname': 'test',
|
||||||
|
'dens': 1.0,
|
||||||
|
'nmol': [1],
|
||||||
|
'nstep': [1, 1],
|
||||||
|
},
|
||||||
|
'gaussian': {
|
||||||
|
'level': 'test',
|
||||||
|
'qmprog': 'g16',
|
||||||
|
'keywords': 'test',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestPlayerDTO(unittest.TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.dice_dto = DiceConfig(
|
||||||
|
ljname='test',
|
||||||
|
outname='test',
|
||||||
|
dens=1.0,
|
||||||
|
nmol=[1],
|
||||||
|
nstep=[1, 1],
|
||||||
|
)
|
||||||
|
self.gaussian_dto = GaussianDTO(
|
||||||
|
level='test',
|
||||||
|
qmprog='g16',
|
||||||
|
keywords='test',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
player_dto = PlayerConfig(
|
||||||
|
opt=True,
|
||||||
|
mem=12,
|
||||||
|
maxcyc=100,
|
||||||
|
nprocs=4,
|
||||||
|
ncores=4,
|
||||||
|
dice=self.dice_dto,
|
||||||
|
gaussian=self.gaussian_dto
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(player_dto, PlayerConfig)
|
||||||
|
self.assertIsInstance(player_dto.dice, DiceConfig)
|
||||||
|
self.assertIsInstance(player_dto.gaussian, GaussianDTO)
|
||||||
|
|
||||||
|
def test_min_altsteps(self):
|
||||||
|
player_dto = PlayerConfig(
|
||||||
|
opt=True,
|
||||||
|
mem=12,
|
||||||
|
maxcyc=100,
|
||||||
|
nprocs=4,
|
||||||
|
ncores=4,
|
||||||
|
altsteps=100,
|
||||||
|
dice=self.dice_dto,
|
||||||
|
gaussian=self.gaussian_dto
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(player_dto.altsteps, 20000)
|
||||||
|
|
||||||
|
def test_from_dict(self):
|
||||||
|
player_dto = PlayerConfig.from_dict(
|
||||||
|
get_config_dict()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(player_dto, PlayerConfig)
|
||||||
|
self.assertIsInstance(player_dto.dice, DiceConfig)
|
||||||
|
self.assertIsInstance(player_dto.gaussian, GaussianDTO)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
0
tests/shared/environment/__init__.py
Normal file
0
tests/shared/environment/__init__.py
Normal file
19
tests/shared/environment/test_atom.py
Normal file
19
tests/shared/environment/test_atom.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from diceplayer.shared.environment.atom import Atom
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestAtom(unittest.TestCase):
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
atom = Atom(
|
||||||
|
lbl=1,
|
||||||
|
na=1,
|
||||||
|
rx=1.0,
|
||||||
|
ry=1.0,
|
||||||
|
rz=1.0,
|
||||||
|
chg=1.0,
|
||||||
|
eps=1.0,
|
||||||
|
sig=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(atom, Atom)
|
||||||
212
tests/shared/environment/test_molecule.py
Normal file
212
tests/shared/environment/test_molecule.py
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from diceplayer.shared.environment.molecule import Molecule
|
||||||
|
from diceplayer.shared.environment.atom import Atom
|
||||||
|
|
||||||
|
import numpy.testing as npt
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestMolecule(unittest.TestCase):
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
self.assertIsInstance(mol, Molecule)
|
||||||
|
|
||||||
|
def test_add_atom(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(len(mol.atom), 1)
|
||||||
|
npt.assert_equal(mol.com, [1., 1., 1.])
|
||||||
|
|
||||||
|
def test_center_of_mass(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
npt.assert_equal(mol.com, [.5, .5, .5])
|
||||||
|
|
||||||
|
def test_center_of_mass_to_origin(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
mol.center_of_mass_to_origin()
|
||||||
|
|
||||||
|
npt.assert_equal(mol.com, [0, 0, 0])
|
||||||
|
|
||||||
|
def test_charges_and_dipole(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
actual_charge_dipole_array = mol.charges_and_dipole()
|
||||||
|
|
||||||
|
expected_charge_dipole_array = [1.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
npt.assert_equal(
|
||||||
|
actual_charge_dipole_array,
|
||||||
|
expected_charge_dipole_array
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_distances_between_atoms(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_distance_between_atoms = [[1.73205081], [1.73205081]]
|
||||||
|
actual_distance_between_atoms = mol.distances_between_atoms()
|
||||||
|
|
||||||
|
npt.assert_almost_equal(
|
||||||
|
expected_distance_between_atoms,
|
||||||
|
actual_distance_between_atoms
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_inertia_tensor(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_inertia_tensor = [[1.00790, -0.50395, -0.50395],
|
||||||
|
[-0.50395, 1.0079, -0.50395],
|
||||||
|
[-0.50395, -0.50395, 1.0079]]
|
||||||
|
|
||||||
|
actual_inertia_tensor = mol.inertia_tensor()
|
||||||
|
|
||||||
|
npt.assert_equal(
|
||||||
|
expected_inertia_tensor,
|
||||||
|
actual_inertia_tensor
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_principal_axes(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_evals, expected_evecs = [0., 0., 0.], [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]
|
||||||
|
|
||||||
|
evals, evecs = mol.principal_axes()
|
||||||
|
|
||||||
|
npt.assert_equal(expected_evals, evals)
|
||||||
|
npt.assert_equal(expected_evecs, evecs)
|
||||||
|
|
||||||
|
def test_read_position(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_position = mol.read_position()
|
||||||
|
|
||||||
|
actual_position = mol.read_position()
|
||||||
|
|
||||||
|
npt.assert_equal(
|
||||||
|
expected_position,
|
||||||
|
actual_position
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_update_charges(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_charges = [2.]
|
||||||
|
mol.update_charges(expected_charges)
|
||||||
|
|
||||||
|
actual_charges = list(map(lambda a: a.chg, mol.atom))
|
||||||
|
|
||||||
|
npt.assert_equal(
|
||||||
|
expected_charges,
|
||||||
|
actual_charges
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_sizes_of_molecule(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
sizes = mol.sizes_of_molecule()
|
||||||
|
|
||||||
|
expected_sizes = [0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
npt.assert_equal(sizes, expected_sizes)
|
||||||
|
|
||||||
|
def test_standard_orientation(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
mol.standard_orientation()
|
||||||
|
|
||||||
|
expected_position = [0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
self.assertEqual(mol.read_position().tolist(), expected_position)
|
||||||
|
|
||||||
|
def test_translate(self):
|
||||||
|
mol = Molecule('test')
|
||||||
|
|
||||||
|
mol.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
new_mol = mol.translate(np.array([-1, -1, -1]))
|
||||||
|
|
||||||
|
expected_position = [0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
new_mol.read_position().tolist(),
|
||||||
|
expected_position
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_minimum_distance(self):
|
||||||
|
mol1 = Molecule('test1')
|
||||||
|
mol1.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
mol2 = Molecule('test2')
|
||||||
|
mol2.add_atom(
|
||||||
|
Atom(lbl=1, na=1, rx=1.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_distance = 1.0
|
||||||
|
|
||||||
|
actual_distance = mol1.minimum_distance(mol2)
|
||||||
|
|
||||||
|
self.assertEqual(expected_distance, actual_distance)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
27
tests/shared/environment/test_system.py
Normal file
27
tests/shared/environment/test_system.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from diceplayer.shared.environment.atom import Atom
|
||||||
|
from diceplayer.shared.environment.molecule import Molecule
|
||||||
|
from diceplayer.shared.environment.system import System
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestSystem(unittest.TestCase):
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
system = System()
|
||||||
|
|
||||||
|
self.assertIsInstance(system, System)
|
||||||
|
|
||||||
|
def test_add_type(self):
|
||||||
|
system = System()
|
||||||
|
system.add_type(Molecule('test'))
|
||||||
|
|
||||||
|
self.assertIsInstance(system.molecule, list)
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError) as ex:
|
||||||
|
system.add_type('test')
|
||||||
|
self.assertEqual(ex.exception, 'Error: molecule is not a Molecule instance')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
0
tests/shared/interface/__init__.py
Normal file
0
tests/shared/interface/__init__.py
Normal file
540
tests/shared/interface/test_dice_interface.py
Normal file
540
tests/shared/interface/test_dice_interface.py
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
from diceplayer.shared.interface.dice_interface import DiceInterface
|
||||||
|
from diceplayer.shared.config.player_config import PlayerConfig
|
||||||
|
from diceplayer.shared.environment.molecule import Molecule
|
||||||
|
from diceplayer.shared.environment.system import System
|
||||||
|
from diceplayer.shared.environment.atom import Atom
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
import io
|
||||||
|
|
||||||
|
from tests.mocks.mock_inputs import get_config_example
|
||||||
|
from tests.mocks.mock_proc import MockConnection, MockProc
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiceInterface(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
config = yaml.load(get_config_example(), Loader=yaml.Loader)
|
||||||
|
self.config = PlayerConfig.from_dict(config['diceplayer'])
|
||||||
|
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
|
||||||
|
self.assertIsInstance(dice, DiceInterface)
|
||||||
|
|
||||||
|
def test_configure(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
|
||||||
|
self.assertIsNone(dice.step)
|
||||||
|
self.assertIsNone(dice.system)
|
||||||
|
|
||||||
|
# Ignoring the types for testing purposes
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
self.assertIsNotNone(dice.step)
|
||||||
|
self.assertIsNotNone(dice.system)
|
||||||
|
|
||||||
|
def test_reset(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
self.assertTrue(hasattr(dice, 'step'))
|
||||||
|
self.assertTrue(hasattr(dice, 'system'))
|
||||||
|
|
||||||
|
dice.reset()
|
||||||
|
|
||||||
|
self.assertFalse(hasattr(dice, 'step'))
|
||||||
|
self.assertFalse(hasattr(dice, 'system'))
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Process', MockProc())
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.connection', MockConnection)
|
||||||
|
def test_start(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.start(1)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.connection', MockConnection)
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Process', MockProc(exitcode=1))
|
||||||
|
def test_start_with_process_error(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
dice.start(1)
|
||||||
|
|
||||||
|
def test_simulation_process_raises_exception(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
dice._simulation_process(1, 1)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.DiceInterface._make_proc_dir')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.DiceInterface._make_dice_inputs')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.DiceInterface._run_dice')
|
||||||
|
def test_simulation_process(self, mock_run_dice, mock_make_dice_inputs, mock_make_proc_dir):
|
||||||
|
dice = DiceInterface()
|
||||||
|
|
||||||
|
dice._simulation_process(1, 1)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_proc_dir.called)
|
||||||
|
self.assertTrue(dice._make_dice_inputs.called)
|
||||||
|
self.assertTrue(dice._run_dice.called)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.mkdir')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.exists')
|
||||||
|
def test_make_proc_dir_if_simdir_exists(self, mock_path_exists, mock_path_mkdir):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
mock_path_exists.return_value = False
|
||||||
|
|
||||||
|
dice._make_proc_dir(1, 1)
|
||||||
|
|
||||||
|
self.assertEqual(mock_path_mkdir.call_count, 2)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.mkdir')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.exists')
|
||||||
|
def test_make_proc_dir_if_simdir_doesnt_exists(self, mock_path_exists, mock_path_mkdir):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
mock_path_exists.return_value = False
|
||||||
|
|
||||||
|
dice._make_proc_dir(1, 1)
|
||||||
|
|
||||||
|
self.assertEqual(mock_path_mkdir.call_count, 2)
|
||||||
|
|
||||||
|
def test_make_dice_seed(self):
|
||||||
|
seed = DiceInterface._make_dice_seed()
|
||||||
|
|
||||||
|
self.assertIsInstance(seed, int)
|
||||||
|
|
||||||
|
def test_make_dice_inputs_nstep_len_two_with_randoninit_first_cycle_one(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.step.dice.nstep = [1, 1]
|
||||||
|
|
||||||
|
dice._make_potentials = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_init_file = mock.Mock()
|
||||||
|
dice._new_density = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_nvt_ter = mock.Mock()
|
||||||
|
dice._make_nvt_eq = mock.Mock()
|
||||||
|
dice._make_npt_ter = mock.Mock()
|
||||||
|
dice._make_npt_eq = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_dice_inputs(1, 1)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_potentials.called)
|
||||||
|
|
||||||
|
self.assertFalse(dice._make_init_file.called)
|
||||||
|
self.assertFalse(dice._new_density.called)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_nvt_ter.called)
|
||||||
|
self.assertTrue(dice._make_nvt_eq.called)
|
||||||
|
|
||||||
|
self.assertFalse(dice._make_npt_ter.called)
|
||||||
|
self.assertFalse(dice._make_npt_eq.called)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open, read_data='test')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.exists', return_value=True)
|
||||||
|
def test_make_dice_inputs_nstep_len_two_with_randoninit_first_cycle_two(self, mock_path_exists, mock_open):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.step.dice.nstep = [1, 1]
|
||||||
|
|
||||||
|
dice._make_potentials = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_init_file = mock.Mock()
|
||||||
|
dice._new_density = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_nvt_ter = mock.Mock()
|
||||||
|
dice._make_nvt_eq = mock.Mock()
|
||||||
|
dice._make_npt_ter = mock.Mock()
|
||||||
|
dice._make_npt_eq = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_dice_inputs(2, 1)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_potentials.called)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_init_file.called)
|
||||||
|
self.assertTrue(dice._new_density.called)
|
||||||
|
|
||||||
|
self.assertFalse(dice._make_nvt_ter.called)
|
||||||
|
self.assertTrue(dice._make_nvt_eq.called)
|
||||||
|
|
||||||
|
self.assertFalse(dice._make_npt_ter.called)
|
||||||
|
self.assertFalse(dice._make_npt_eq.called)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.exists', return_value=False)
|
||||||
|
def test_make_dice_inputs_raises_exception_on_last_not_found(self, mock_path_exists):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.step.dice.nstep = [1, 1]
|
||||||
|
|
||||||
|
dice._make_potentials = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_init_file = mock.Mock()
|
||||||
|
dice._new_density = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_nvt_ter = mock.Mock()
|
||||||
|
dice._make_nvt_eq = mock.Mock()
|
||||||
|
dice._make_npt_ter = mock.Mock()
|
||||||
|
dice._make_npt_eq = mock.Mock()
|
||||||
|
|
||||||
|
with self.assertRaises(FileNotFoundError):
|
||||||
|
dice._make_dice_inputs(2, 1)
|
||||||
|
|
||||||
|
def test_make_dice_inputs_nstep_len_three_with_randoninit_first_cycle_one(self):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice._make_potentials = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_init_file = mock.Mock()
|
||||||
|
dice._new_density = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_nvt_ter = mock.Mock()
|
||||||
|
dice._make_nvt_eq = mock.Mock()
|
||||||
|
dice._make_npt_ter = mock.Mock()
|
||||||
|
dice._make_npt_eq = mock.Mock()
|
||||||
|
|
||||||
|
dice._make_dice_inputs(1, 1)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_potentials.called)
|
||||||
|
|
||||||
|
self.assertFalse(dice._make_init_file.called)
|
||||||
|
self.assertFalse(dice._new_density.called)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_nvt_ter.called)
|
||||||
|
self.assertFalse(dice._make_nvt_eq.called)
|
||||||
|
|
||||||
|
self.assertTrue(dice._make_npt_ter.called)
|
||||||
|
self.assertTrue(dice._make_npt_eq.called)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.os')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.shutil')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.exists', return_value=True)
|
||||||
|
def test_run_dice_on_first_cycle_run_successful(self, mock_path_exists, mock_shutils, mock_os):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.step.dice.nstep = [1, 1, 1]
|
||||||
|
|
||||||
|
dice.run_dice_file = mock.Mock()
|
||||||
|
|
||||||
|
dice._run_dice(1, 1)
|
||||||
|
|
||||||
|
self.assertTrue(mock_os.getcwd.called)
|
||||||
|
self.assertTrue(mock_os.chdir.called)
|
||||||
|
|
||||||
|
self.assertEqual(dice.run_dice_file.call_count, 3)
|
||||||
|
self.assertTrue(mock_shutils.copy.called)
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.step.dice.nstep = [1, 1]
|
||||||
|
|
||||||
|
dice.run_dice_file = mock.Mock()
|
||||||
|
|
||||||
|
dice._run_dice(1, 1)
|
||||||
|
|
||||||
|
self.assertTrue(mock_os.getcwd.called)
|
||||||
|
self.assertTrue(mock_os.chdir.called)
|
||||||
|
|
||||||
|
self.assertEqual(dice.run_dice_file.call_count, 2)
|
||||||
|
self.assertTrue(mock_shutils.copy.called)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.os')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.shutil')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.exists', return_value=True)
|
||||||
|
def test_run_dice_on_second_cycle_run_successful(self, mock_path_exists, mock_shutils, mock_os):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.run_dice_file = mock.Mock()
|
||||||
|
|
||||||
|
dice._run_dice(2, 1)
|
||||||
|
|
||||||
|
self.assertTrue(mock_os.getcwd.called)
|
||||||
|
self.assertTrue(mock_os.chdir.called)
|
||||||
|
|
||||||
|
self.assertEqual(dice.run_dice_file.call_count, 2)
|
||||||
|
self.assertTrue(mock_shutils.copy.called)
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.run_dice_file = mock.Mock()
|
||||||
|
|
||||||
|
dice._run_dice(2, 1)
|
||||||
|
|
||||||
|
self.assertTrue(mock_os.getcwd.called)
|
||||||
|
self.assertTrue(mock_os.chdir.called)
|
||||||
|
|
||||||
|
self.assertEqual(dice.run_dice_file.call_count, 1)
|
||||||
|
self.assertTrue(mock_shutils.copy.called)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.os')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.shutil')
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.Path.exists', return_value=False)
|
||||||
|
def test_run_dice_on_second_cycle_run_successful(self, mock_path_exists, mock_shutils, mock_os):
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.run_dice_file = mock.Mock()
|
||||||
|
|
||||||
|
with self.assertRaises(FileNotFoundError):
|
||||||
|
dice._run_dice(1, 1)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open)
|
||||||
|
def test_make_init_file(self, mock_open):
|
||||||
|
example_atom = Atom(
|
||||||
|
lbl=1,
|
||||||
|
na=1,
|
||||||
|
rx=1.0,
|
||||||
|
ry=1.0,
|
||||||
|
rz=1.0,
|
||||||
|
chg=1.0,
|
||||||
|
eps=1.0,
|
||||||
|
sig=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
main_molecule = Molecule('main_molecule')
|
||||||
|
main_molecule.add_atom(example_atom)
|
||||||
|
|
||||||
|
secondary_molecule = Molecule('secondary_molecule')
|
||||||
|
secondary_molecule.add_atom(example_atom)
|
||||||
|
|
||||||
|
system = System()
|
||||||
|
system.add_type(main_molecule)
|
||||||
|
system.add_type(secondary_molecule)
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, system)
|
||||||
|
|
||||||
|
dice.step.dice.nmol = [1, 1]
|
||||||
|
|
||||||
|
last_xyz_file = io.StringIO()
|
||||||
|
last_xyz_file.writelines([
|
||||||
|
' TEST\n',
|
||||||
|
' Configuration number : TEST = TEST TEST TEST\n',
|
||||||
|
' H 1.00000 1.00000 1.00000\n',
|
||||||
|
' H 1.00000 1.00000 1.00000\n',
|
||||||
|
])
|
||||||
|
last_xyz_file.seek(0)
|
||||||
|
|
||||||
|
dice._make_init_file('test', last_xyz_file)
|
||||||
|
|
||||||
|
mock_handler = mock_open()
|
||||||
|
calls = mock_handler.write.call_args_list
|
||||||
|
|
||||||
|
lines = list(map(lambda x: x[0][0], calls))
|
||||||
|
|
||||||
|
expected_lines = [
|
||||||
|
' 1.000000 1.000000 1.000000\n',
|
||||||
|
' 1.000000 1.000000 1.000000\n',
|
||||||
|
'$end'
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(lines, expected_lines)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open)
|
||||||
|
def test_new_density(self, mock_open):
|
||||||
|
example_atom = Atom(
|
||||||
|
lbl=1,
|
||||||
|
na=1,
|
||||||
|
rx=1.0,
|
||||||
|
ry=1.0,
|
||||||
|
rz=1.0,
|
||||||
|
chg=1.0,
|
||||||
|
eps=1.0,
|
||||||
|
sig=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
main_molecule = Molecule('main_molecule')
|
||||||
|
main_molecule.add_atom(example_atom)
|
||||||
|
|
||||||
|
secondary_molecule = Molecule('secondary_molecule')
|
||||||
|
secondary_molecule.add_atom(example_atom)
|
||||||
|
|
||||||
|
system = System()
|
||||||
|
system.add_type(main_molecule)
|
||||||
|
system.add_type(secondary_molecule)
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, system)
|
||||||
|
|
||||||
|
last_xyz_file = io.StringIO()
|
||||||
|
last_xyz_file.writelines([
|
||||||
|
' TEST\n',
|
||||||
|
' Configuration number : TEST = 1 1 1\n',
|
||||||
|
' H 1.00000 1.00000 1.00000\n',
|
||||||
|
' H 1.00000 1.00000 1.00000\n',
|
||||||
|
])
|
||||||
|
last_xyz_file.seek(0)
|
||||||
|
|
||||||
|
density = dice._new_density(last_xyz_file)
|
||||||
|
|
||||||
|
self.assertEqual(density, 85.35451545000001)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open)
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.random')
|
||||||
|
def test_make_nvt_ter(self, mock_random, mock_open):
|
||||||
|
mock_random.random.return_value = 1
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice._make_nvt_ter(1, 'test')
|
||||||
|
|
||||||
|
mock_handler = mock_open()
|
||||||
|
calls = mock_handler.write.call_args_list
|
||||||
|
|
||||||
|
lines = list(map(lambda x: x[0][0], calls))
|
||||||
|
|
||||||
|
expected_lines = ['title = Diceplayer run - NVT Thermalization\n', 'ncores = 4\n', 'ljname = phb.ljc\n', 'outname = phb\n', 'nmol = 1 50\n', 'dens = 0.75\n', 'temp = 300.0\n', 'init = yes\n', 'nstep = 2000\n', 'vstep = 0\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 0\n', 'irdf = 0\n', 'seed = 1000000\n', 'upbuf = 360']
|
||||||
|
|
||||||
|
self.assertEqual(lines, expected_lines)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open)
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.random')
|
||||||
|
def test_make_nvt_eq(self, mock_random, mock_open):
|
||||||
|
mock_random.random.return_value = 1
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice._make_nvt_eq(1, 'test')
|
||||||
|
|
||||||
|
mock_handler = mock_open()
|
||||||
|
calls = mock_handler.write.call_args_list
|
||||||
|
|
||||||
|
lines = list(map(lambda x: x[0][0], calls))
|
||||||
|
|
||||||
|
expected_lines = ['title = Diceplayer run - NVT Production\n', 'ncores = 4\n', 'ljname = phb.ljc\n', 'outname = phb\n', 'nmol = 1 50\n', 'dens = 0.75\n', 'temp = 300.0\n', 'init = no\n', 'nstep = 3000\n', 'vstep = 0\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 1000\n', 'irdf = 40\n', 'seed = 1000000\n']
|
||||||
|
|
||||||
|
self.assertEqual(lines, expected_lines)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open)
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.random')
|
||||||
|
def test_make_npt_ter(self, mock_random, mock_open):
|
||||||
|
mock_random.random.return_value = 1
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice._make_npt_ter(1, 'test')
|
||||||
|
|
||||||
|
mock_handler = mock_open()
|
||||||
|
calls = mock_handler.write.call_args_list
|
||||||
|
|
||||||
|
lines = list(map(lambda x: x[0][0], calls))
|
||||||
|
|
||||||
|
expected_lines = ['title = Diceplayer run - NPT Thermalization\n', 'ncores = 4\n', 'ljname = phb.ljc\n', 'outname = phb\n', 'nmol = 1 50\n', 'press = 1.0\n', 'temp = 300.0\n', 'init = no\n', 'vstep = 600\n', 'nstep = 5\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 0\n', 'irdf = 0\n', 'seed = 1000000\n']
|
||||||
|
|
||||||
|
self.assertEqual(lines, expected_lines)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open)
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.random')
|
||||||
|
def test_make_npt_eq(self, mock_random, mock_open):
|
||||||
|
mock_random.random.return_value = 1
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice._make_npt_eq('test')
|
||||||
|
|
||||||
|
mock_handler = mock_open()
|
||||||
|
calls = mock_handler.write.call_args_list
|
||||||
|
|
||||||
|
lines = list(map(lambda x: x[0][0], calls))
|
||||||
|
|
||||||
|
expected_lines = ['title = Diceplayer run - NPT Production\n', 'ncores = 4\n', 'ljname = phb.ljc\n', 'outname = phb\n', 'nmol = 1 50\n', 'press = 1.0\n', 'temp = 300.0\n', 'nstep = 5\n', 'vstep = 800\n', 'init = no\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 1000\n', 'irdf = 40\n', 'seed = 1000000\n']
|
||||||
|
|
||||||
|
self.assertEqual(lines, expected_lines)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open)
|
||||||
|
def test_make_potentials(self, mock_open):
|
||||||
|
example_atom = Atom(
|
||||||
|
lbl=1,
|
||||||
|
na=1,
|
||||||
|
rx=1.0,
|
||||||
|
ry=1.0,
|
||||||
|
rz=1.0,
|
||||||
|
chg=1.0,
|
||||||
|
eps=1.0,
|
||||||
|
sig=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
main_molecule = Molecule('main_molecule')
|
||||||
|
main_molecule.add_atom(example_atom)
|
||||||
|
|
||||||
|
secondary_molecule = Molecule('secondary_molecule')
|
||||||
|
secondary_molecule.add_atom(example_atom)
|
||||||
|
|
||||||
|
system = System()
|
||||||
|
system.add_type(main_molecule)
|
||||||
|
system.add_type(secondary_molecule)
|
||||||
|
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, system)
|
||||||
|
|
||||||
|
dice._make_potentials('test')
|
||||||
|
|
||||||
|
mock_handler = mock_open()
|
||||||
|
calls = mock_handler.write.call_args_list
|
||||||
|
|
||||||
|
lines = list(map(lambda x: x[0][0], calls))
|
||||||
|
|
||||||
|
expected_lines = ['*\n', '2\n', '1 main_molecule\n', '1 1 1.00000 1.00000 1.00000 1.000000 1.00000 1.0000\n', '1 secondary_molecule\n', '1 1 1.00000 1.00000 1.00000 1.000000 1.00000 1.0000\n']
|
||||||
|
|
||||||
|
self.assertEqual(lines, expected_lines)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.subprocess')
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open, read_data='End of simulation\nBLABLA')
|
||||||
|
def test_run_dice_file(self, mock_open, mock_subprocess):
|
||||||
|
mock_subprocess.call.return_value = 0
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
dice.run_dice_file(1, 1, 'test')
|
||||||
|
|
||||||
|
self.assertTrue(mock_subprocess.call.called)
|
||||||
|
self.assertTrue(mock_open.called)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.subprocess')
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open, read_data='Error\nBLABLA')
|
||||||
|
def test_run_dice_file_raises_runtime_error_on_dice_file(self, mock_open, mock_subprocess):
|
||||||
|
mock_subprocess.call.return_value = 0
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
dice.run_dice_file(1, 1, 'test')
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.dice_interface.subprocess')
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open, read_data='End of simulation\nBLABLA')
|
||||||
|
def test_run_dice_file_raises_runtime_error_of_dice_exit_code(self, mock_open, mock_subprocess):
|
||||||
|
mock_subprocess.call.return_value = 1
|
||||||
|
dice = DiceInterface()
|
||||||
|
dice.configure(self.config, System())
|
||||||
|
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
dice.run_dice_file(1, 1, 'test')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
113
tests/shared/interface/test_gaussian_interface.py
Normal file
113
tests/shared/interface/test_gaussian_interface.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
from diceplayer.shared.interface.gaussian_interface import GaussianInterface
|
||||||
|
from diceplayer.shared.config.player_config import PlayerConfig
|
||||||
|
from diceplayer.shared.environment.system import System
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
from tests.mocks.mock_inputs import get_config_example
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
import io
|
||||||
|
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestGaussianInterface(unittest.TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
config = yaml.load(get_config_example(), Loader=yaml.Loader)
|
||||||
|
self.config = PlayerConfig.from_dict(config['diceplayer'])
|
||||||
|
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
gaussian_interface = GaussianInterface()
|
||||||
|
self.assertIsInstance(gaussian_interface, GaussianInterface)
|
||||||
|
|
||||||
|
def test_configure(self):
|
||||||
|
gaussian_interface = GaussianInterface()
|
||||||
|
|
||||||
|
self.assertIsNone(gaussian_interface.step)
|
||||||
|
self.assertIsNone(gaussian_interface.system)
|
||||||
|
|
||||||
|
gaussian_interface.configure(self.config, System())
|
||||||
|
|
||||||
|
self.assertIsNotNone(gaussian_interface.step)
|
||||||
|
self.assertIsNotNone(gaussian_interface.system)
|
||||||
|
|
||||||
|
def test_reset(self):
|
||||||
|
gaussian_interface = GaussianInterface()
|
||||||
|
|
||||||
|
gaussian_interface.configure(self.config, System())
|
||||||
|
|
||||||
|
self.assertIsNotNone(gaussian_interface.step)
|
||||||
|
self.assertIsNotNone(gaussian_interface.system)
|
||||||
|
|
||||||
|
gaussian_interface.reset()
|
||||||
|
|
||||||
|
self.assertFalse(hasattr(gaussian_interface, 'step'))
|
||||||
|
self.assertFalse(hasattr(gaussian_interface, 'system'))
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.Path.mkdir')
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.Path.exists')
|
||||||
|
def test_make_qm_dir(self, mock_exists, mock_mkdir):
|
||||||
|
mock_exists.return_value = False
|
||||||
|
|
||||||
|
gaussian_interface = GaussianInterface()
|
||||||
|
gaussian_interface.configure(self.config, System())
|
||||||
|
|
||||||
|
gaussian_interface._make_qm_dir(1)
|
||||||
|
|
||||||
|
mock_exists.assert_called_once()
|
||||||
|
mock_mkdir.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.shutil.copy')
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.Path.exists')
|
||||||
|
def test_copy_chk_file_from_previous_step(self, mock_exists, mock_copy):
|
||||||
|
gaussian_interface = GaussianInterface()
|
||||||
|
gaussian_interface.configure(self.config, System())
|
||||||
|
|
||||||
|
mock_exists.side_effect = [False, True]
|
||||||
|
|
||||||
|
gaussian_interface._copy_chk_file_from_previous_step(2)
|
||||||
|
|
||||||
|
self.assertTrue(mock_exists.called)
|
||||||
|
self.assertTrue(mock_copy.called)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.shutil.copy')
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.Path.exists')
|
||||||
|
def test_copy_chk_file_from_previous_step_no_previous_step(self, mock_exists, mock_copy):
|
||||||
|
gaussian_interface = GaussianInterface()
|
||||||
|
gaussian_interface.configure(self.config, System())
|
||||||
|
|
||||||
|
mock_exists.side_effect = [False, False]
|
||||||
|
|
||||||
|
with self.assertRaises(FileNotFoundError):
|
||||||
|
gaussian_interface._copy_chk_file_from_previous_step(2)
|
||||||
|
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.shutil.copy')
|
||||||
|
@mock.patch('diceplayer.shared.interface.gaussian_interface.Path.exists')
|
||||||
|
def test_copy_chk_file_from_previous_step_current_exists(self, mock_exists, mock_copy):
|
||||||
|
gaussian_interface = GaussianInterface()
|
||||||
|
gaussian_interface.configure(self.config, System())
|
||||||
|
|
||||||
|
mock_exists.side_effect = [True, True]
|
||||||
|
|
||||||
|
with self.assertRaises(FileExistsError):
|
||||||
|
gaussian_interface._copy_chk_file_from_previous_step(2)
|
||||||
|
|
||||||
|
# def test_start(self):
|
||||||
|
# gaussian_interface = GaussianInterface()
|
||||||
|
# gaussian_interface.configure(self.config, System())
|
||||||
|
#
|
||||||
|
# gaussian_interface._make_qm_dir = mock.Mock()
|
||||||
|
# gaussian_interface._copy_chk_file_from_previous_step = mock.Mock()
|
||||||
|
#
|
||||||
|
# gaussian_interface.start(2)
|
||||||
|
#
|
||||||
|
# gaussian_interface._make_qm_dir.assert_called_once_with(2)
|
||||||
|
# gaussian_interface._copy_chk_file_from_previous_step.assert_called_once_with(2)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
0
tests/shared/utils/__init__.py
Normal file
0
tests/shared/utils/__init__.py
Normal file
133
tests/shared/utils/test_logger.py
Normal file
133
tests/shared/utils/test_logger.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
from diceplayer.shared.utils.logger import Logger, valid_logger
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import io
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestValidateLogger(unittest.TestCase):
|
||||||
|
def test_validate_logger(self):
|
||||||
|
class MockLogger:
|
||||||
|
_was_set = True
|
||||||
|
|
||||||
|
@valid_logger
|
||||||
|
def test_func(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
MockLogger().test_func()
|
||||||
|
|
||||||
|
def test_validate_logger_exception(self):
|
||||||
|
class MockLogger:
|
||||||
|
_was_set = False
|
||||||
|
|
||||||
|
@valid_logger
|
||||||
|
def test_func(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertRaises(AssertionError):
|
||||||
|
MockLogger().test_func()
|
||||||
|
|
||||||
|
|
||||||
|
class TestLogger(unittest.TestCase):
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
|
||||||
|
self.assertIsInstance(logger, Logger)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
def test_set_logger_to_file(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
self.assertIsNotNone(logger._logger)
|
||||||
|
self.assertEqual(logger._logger.name, 'test')
|
||||||
|
|
||||||
|
def test_set_logger_to_stream(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
self.assertIsNotNone(logger._logger)
|
||||||
|
self.assertEqual(logger._logger.name, 'test')
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
@mock.patch('diceplayer.shared.utils.logger.Path.exists')
|
||||||
|
@mock.patch('diceplayer.shared.utils.logger.Path.rename')
|
||||||
|
def test_set_logger_if_file_exists(self, mock_rename, mock_exists):
|
||||||
|
logger = Logger('test')
|
||||||
|
|
||||||
|
mock_exists.return_value = True
|
||||||
|
logger.set_logger()
|
||||||
|
|
||||||
|
self.assertTrue(mock_rename.called)
|
||||||
|
self.assertIsNotNone(logger._logger)
|
||||||
|
self.assertEqual(logger._logger.name, 'test')
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
@mock.patch('diceplayer.shared.utils.logger.Path.exists')
|
||||||
|
@mock.patch('diceplayer.shared.utils.logger.Path.rename')
|
||||||
|
def test_set_logger_if_file_not_exists(self, mock_rename, mock_exists):
|
||||||
|
logger = Logger('test')
|
||||||
|
|
||||||
|
mock_exists.return_value = False
|
||||||
|
logger.set_logger()
|
||||||
|
|
||||||
|
self.assertFalse(mock_rename.called)
|
||||||
|
self.assertIsNotNone(logger._logger)
|
||||||
|
self.assertEqual(logger._logger.name, 'test')
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
def test_close(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
|
||||||
|
logger.set_logger()
|
||||||
|
logger.close()
|
||||||
|
|
||||||
|
self.assertEqual(len(logger._logger.handlers), 0)
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
def test_info(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
with self.assertLogs(level='INFO') as cm:
|
||||||
|
logger.info('test')
|
||||||
|
|
||||||
|
self.assertEqual(cm.output, ['INFO:test:test'])
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
def test_debug(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
logger.set_logger(stream=io.StringIO(), level=logging.DEBUG)
|
||||||
|
|
||||||
|
with self.assertLogs(level='DEBUG') as cm:
|
||||||
|
logger.debug('test')
|
||||||
|
|
||||||
|
self.assertEqual(cm.output, ['DEBUG:test:test'])
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
def test_warning(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
with self.assertLogs(level='WARNING') as cm:
|
||||||
|
logger.warning('test')
|
||||||
|
|
||||||
|
self.assertEqual(cm.output, ['WARNING:test:test'])
|
||||||
|
|
||||||
|
@mock.patch('builtins.open', mock.mock_open())
|
||||||
|
def test_error(self):
|
||||||
|
logger = Logger('test')
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
with self.assertLogs(level='ERROR') as cm:
|
||||||
|
logger.error('test')
|
||||||
|
|
||||||
|
self.assertEqual(cm.output, ['ERROR:test:test'])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
340
tests/test_player.py
Normal file
340
tests/test_player.py
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
from diceplayer.player import Player
|
||||||
|
from diceplayer import logger
|
||||||
|
|
||||||
|
import io
|
||||||
|
|
||||||
|
from tests.mocks.mock_inputs import mock_open
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestPlayer(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
logger.set_logger(stream=io.StringIO())
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
def test_class_instantiation(self):
|
||||||
|
# This file does not exist and it will be mocked
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
|
||||||
|
self.assertIsInstance(player, Player)
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
def test_start(self):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
|
||||||
|
player.gaussian_start = mock.MagicMock()
|
||||||
|
player.dice_start = mock.MagicMock()
|
||||||
|
|
||||||
|
player.start()
|
||||||
|
|
||||||
|
self.assertEqual(player.dice_start.call_count, 3)
|
||||||
|
self.assertEqual(player.gaussian_start.call_count, 3)
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
@mock.patch("diceplayer.player.Path")
|
||||||
|
def test_create_simulation_dir_if_already_exists(self, mock_path):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
mock_path.return_value.exists.return_value = True
|
||||||
|
|
||||||
|
with self.assertRaises(FileExistsError):
|
||||||
|
player.create_simulation_dir()
|
||||||
|
|
||||||
|
self.assertTrue(mock_path.called)
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
@mock.patch("diceplayer.player.Path")
|
||||||
|
def test_create_simulation_dir_if_not_exists(self, mock_path):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
mock_path.return_value.exists.return_value = False
|
||||||
|
|
||||||
|
player.create_simulation_dir()
|
||||||
|
|
||||||
|
self.assertTrue(mock_path.called)
|
||||||
|
|
||||||
|
@mock.patch("diceplayer.player.sys")
|
||||||
|
@mock.patch("diceplayer.player.weekday_date_time")
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
def test_print_keywords(self, mock_date_func, mock_sys):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
|
||||||
|
mock_sys.version = 'TEST'
|
||||||
|
mock_date_func.return_value = '00 Test 0000 at 00:00:00'
|
||||||
|
|
||||||
|
with self.assertLogs() as cm:
|
||||||
|
player.print_keywords()
|
||||||
|
|
||||||
|
expected_output = [
|
||||||
|
'INFO:diceplayer:##########################################################################################\n############# Welcome to DICEPLAYER version 1.0 #############\n##########################################################################################\n',
|
||||||
|
'INFO:diceplayer:Your python version is TEST\n',
|
||||||
|
'INFO:diceplayer:Program started on 00 Test 0000 at 00:00:00\n',
|
||||||
|
'INFO:diceplayer:Environment variables:',
|
||||||
|
'INFO:diceplayer:OMP_STACKSIZE = Not set\n',
|
||||||
|
'INFO:diceplayer:------------------------------------------------------------------------------------------\n DICE variables being used in this run:\n------------------------------------------------------------------------------------------\n',
|
||||||
|
'INFO:diceplayer:dens = 0.75',
|
||||||
|
'INFO:diceplayer:isave = 1000',
|
||||||
|
'INFO:diceplayer:ljname = phb.ljc',
|
||||||
|
'INFO:diceplayer:nmol = [ 1 50 ]',
|
||||||
|
'INFO:diceplayer:nstep = [ 2000 3000 4000 ]',
|
||||||
|
'INFO:diceplayer:outname = phb',
|
||||||
|
'INFO:diceplayer:press = 1.0',
|
||||||
|
'INFO:diceplayer:progname = ~/.local/bin/dice',
|
||||||
|
'INFO:diceplayer:randominit = first',
|
||||||
|
'INFO:diceplayer:temp = 300.0',
|
||||||
|
'INFO:diceplayer:------------------------------------------------------------------------------------------\n GAUSSIAN variables being used in this run:\n------------------------------------------------------------------------------------------\n',
|
||||||
|
'INFO:diceplayer:chg_tol = 0.01',
|
||||||
|
'INFO:diceplayer:keywords = freq',
|
||||||
|
'INFO:diceplayer:level = MP2/aug-cc-pVDZ',
|
||||||
|
'INFO:diceplayer:pop = chelpg',
|
||||||
|
'INFO:diceplayer:qmprog = g16',
|
||||||
|
'INFO:diceplayer:\n'
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(cm.output, expected_output)
|
||||||
|
|
||||||
|
def test_validate_atom_dict(self):
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 0, "na": 1, "rx": 1.0, "ry": 1.0, "rz": 1.0, "chg": 1.0, "eps": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid number of fields for site 1 for molecule type 1."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": '', "na": 1, "rx": 1.0, "ry": 1.0, "rz": 1.0, "chg": 1.0, "eps": 1.0, "sig": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid lbl fields for site 1 for molecule type 1."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 1.0, "na": '', "rx": 1.0, "ry": 1.0, "rz": 1.0, "chg": 1.0, "eps": 1.0, "sig": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid na fields for site 1 for molecule type 1."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 1.0, "na": 1, "rx": '', "ry": 1.0, "rz": 1.0, "chg": 1.0, "eps": 1.0, "sig": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid rx fields for site 1 for molecule type 1. Value must be a float."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 1.0, "na": 1, "rx": 1.0, "ry": '', "rz": 1.0, "chg": 1.0, "eps": 1.0, "sig": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid ry fields for site 1 for molecule type 1. Value must be a float."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 1.0, "na": 1, "rx": 1.0, "ry": 1.0, "rz": '', "chg": 1.0, "eps": 1.0, "sig": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid rz fields for site 1 for molecule type 1. Value must be a float."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 1.0, "na": 1, "rx": 1.0, "ry": 1.0, "rz": 1.0, "chg": '', "eps": 1.0, "sig": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid chg fields for site 1 for molecule type 1. Value must be a float."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 1.0, "na": 1, "rx": 1.0, "ry": 1.0, "rz": 1.0, "chg": 1.0, "eps": '', "sig": 1.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid eps fields for site 1 for molecule type 1. Value must be a float."
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
Player.validate_atom_dict(
|
||||||
|
molecule_type=0,
|
||||||
|
molecule_site=0,
|
||||||
|
atom_dict={
|
||||||
|
"lbl": 1.0, "na": 1, "rx": 1.0, "ry": 1.0, "rz": 1.0, "chg": 1.0, "eps": 1.0, "sig": ''
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Invalid sig fields for site 1 for molecule type 1. Value must be a float."
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
@mock.patch("diceplayer.player.Path.exists", return_value=True)
|
||||||
|
def test_read_potentials(self, mock_path_exists):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
self.assertEqual(player.system.molecule[0].molname, "TEST")
|
||||||
|
self.assertEqual(len(player.system.molecule[0].atom), 1)
|
||||||
|
|
||||||
|
self.assertEqual(player.system.molecule[1].molname, "PLACEHOLDER")
|
||||||
|
self.assertEqual(len(player.system.molecule[1].atom), 1)
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
@mock.patch("diceplayer.player.Path.exists")
|
||||||
|
def test_read_potentials_error(self, mock_path_exists):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
|
||||||
|
# Testing file not found error
|
||||||
|
mock_path_exists.return_value = False
|
||||||
|
with self.assertRaises(RuntimeError) as context:
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Potential file phb.ljc not found."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enabling file found for next tests
|
||||||
|
mock_path_exists.return_value = True
|
||||||
|
|
||||||
|
# Testing combrule error
|
||||||
|
with self.assertRaises(SystemExit) as context:
|
||||||
|
player.config.dice.ljname = "phb.error.combrule.ljc"
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Error: expected a '*' or a '+' sign in 1st line of file phb.error.combrule.ljc"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Testing ntypes error
|
||||||
|
with self.assertRaises(SystemExit) as context:
|
||||||
|
player.config.dice.ljname = "phb.error.ntypes.ljc"
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Error: expected an integer in the 2nd line of file phb.error.ntypes.ljc"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Testing ntypes error on config
|
||||||
|
with self.assertRaises(SystemExit) as context:
|
||||||
|
player.config.dice.ljname = "phb.error.ntypes.config.ljc"
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Error: number of molecule types in file phb.error.ntypes.config.ljc "
|
||||||
|
"must match that of 'nmol' keyword in config file"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Testing nsite error
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
player.config.dice.ljname = "phb.error.nsites.ljc"
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Error: expected nsites to be an integer for molecule type 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Testing molname error
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
player.config.dice.ljname = "phb.error.molname.ljc"
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(context.exception),
|
||||||
|
"Error: expected nsites and molname for the molecule type 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
@mock.patch("diceplayer.player.Path.exists", return_value=True)
|
||||||
|
def test_print_potentials(self, mock_path_exists):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
player.read_potentials()
|
||||||
|
|
||||||
|
with self.assertLogs(level='INFO') as context:
|
||||||
|
player.print_potentials()
|
||||||
|
|
||||||
|
expected_output = [
|
||||||
|
'INFO:diceplayer:==========================================================================================\n Potential parameters from file phb.ljc:\n------------------------------------------------------------------------------------------\n',
|
||||||
|
'INFO:diceplayer:Combination rule: *',
|
||||||
|
'INFO:diceplayer:Types of molecules: 2\n',
|
||||||
|
'INFO:diceplayer:1 atoms in molecule type 1:',
|
||||||
|
'INFO:diceplayer:---------------------------------------------------------------------------------',
|
||||||
|
'INFO:diceplayer:Lbl AN X Y Z Charge Epsilon Sigma Mass',
|
||||||
|
'INFO:diceplayer:---------------------------------------------------------------------------------',
|
||||||
|
'INFO:diceplayer:1 1 0.00000 0.00000 0.00000 0.000000 0.00000 0.0000 1.0079',
|
||||||
|
'INFO:diceplayer:\n',
|
||||||
|
'INFO:diceplayer:1 atoms in molecule type 2:',
|
||||||
|
'INFO:diceplayer:---------------------------------------------------------------------------------',
|
||||||
|
'INFO:diceplayer:Lbl AN X Y Z Charge Epsilon Sigma Mass',
|
||||||
|
'INFO:diceplayer:---------------------------------------------------------------------------------',
|
||||||
|
'INFO:diceplayer:1 1 0.00000 0.00000 0.00000 0.000000 0.00000 0.0000 1.0079',
|
||||||
|
'INFO:diceplayer:\n'
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
context.output,
|
||||||
|
expected_output
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch("builtins.open", mock_open)
|
||||||
|
def test_dice_start(self):
|
||||||
|
player = Player("control.test.yml")
|
||||||
|
player.dice_interface = mock.MagicMock()
|
||||||
|
player.dice_interface.start = mock.MagicMock()
|
||||||
|
|
||||||
|
player.dice_start(1)
|
||||||
|
|
||||||
|
player.dice_interface.start.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user