Implements More Tests and Begins Dice Refactor Implementation
This commit is contained in:
@@ -24,27 +24,27 @@ class DiceDTO(Dataclass):
|
||||
|
||||
def __post_init__(self):
|
||||
|
||||
if self.ljname is None:
|
||||
if not isinstance(self.ljname, str):
|
||||
raise ValueError(
|
||||
"Error: 'ljname' keyword not specified in config file"
|
||||
)
|
||||
|
||||
if self.outname is None:
|
||||
if not isinstance(self.outname, str):
|
||||
raise ValueError(
|
||||
"Error: 'outname' keyword not specified in config file"
|
||||
)
|
||||
|
||||
if self.dens is None:
|
||||
if not isinstance(self.dens, float):
|
||||
raise ValueError(
|
||||
"Error: 'dens' keyword not specified in config file"
|
||||
)
|
||||
|
||||
if self.nmol == 0:
|
||||
if not isinstance(self.nmol, list):
|
||||
raise ValueError(
|
||||
"Error: 'nmol' keyword not defined appropriately in config file"
|
||||
)
|
||||
|
||||
if self.nstep == 0:
|
||||
if not isinstance(self.nstep, list):
|
||||
raise ValueError(
|
||||
"Error: 'nstep' keyword not defined appropriately in config file"
|
||||
)
|
||||
|
||||
18
diceplayer/shared/config/step_dto.py
Normal file
18
diceplayer/shared/config/step_dto.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass
|
||||
class StepDTO:
|
||||
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
|
||||
@@ -79,14 +79,13 @@ class Molecule:
|
||||
"""
|
||||
Updated positions based on the center of mass of the molecule
|
||||
"""
|
||||
|
||||
self.center_of_mass()
|
||||
|
||||
for atom in self.atom:
|
||||
atom.rx -= self.com[0]
|
||||
atom.ry -= self.com[1]
|
||||
atom.rz -= self.com[2]
|
||||
|
||||
self.center_of_mass()
|
||||
|
||||
def charges_and_dipole(self) -> List[float]:
|
||||
"""
|
||||
Calculates the charges and dipole of the molecule atoms
|
||||
@@ -119,16 +118,15 @@ class Molecule:
|
||||
|
||||
distances = []
|
||||
dim = len(self.atom)
|
||||
for atom1 in self.atom:
|
||||
if atom1.na != ghost_number:
|
||||
for atom2 in self.atom:
|
||||
if atom2.na != ghost_number:
|
||||
dx = atom1.rx - atom2.rx
|
||||
dy = atom1.ry - atom2.ry
|
||||
dz = atom1.rz - atom2.rz
|
||||
distances.append(math.sqrt(dx ** 2 + dy ** 2 + dz ** 2))
|
||||
for index1, atom1 in enumerate(self.atom):
|
||||
for index2, atom2 in enumerate(self.atom):
|
||||
if index1 != index2:
|
||||
dx = atom1.rx - atom2.rx
|
||||
dy = atom1.ry - atom2.ry
|
||||
dz = atom1.rz - atom2.rz
|
||||
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]:
|
||||
"""
|
||||
@@ -156,40 +154,6 @@ class Molecule:
|
||||
|
||||
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]:
|
||||
"""
|
||||
Calculates the principal axes of the molecule
|
||||
@@ -201,7 +165,7 @@ class Molecule:
|
||||
|
||||
try:
|
||||
evals, evecs = linalg.eigh(self.inertia_tensor())
|
||||
except:
|
||||
except ValueError:
|
||||
raise RuntimeError("Error: diagonalization of inertia tensor did not converge")
|
||||
|
||||
return evals, evecs
|
||||
@@ -221,38 +185,38 @@ class Molecule:
|
||||
|
||||
return position
|
||||
|
||||
def updateCharges(self, charges: List[float]) -> None:
|
||||
def update_charges(self, charges: List[float]) -> None:
|
||||
|
||||
for i, atom in enumerate(self.atom):
|
||||
atom.chg = charges[i]
|
||||
|
||||
def update_hessian(
|
||||
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:
|
||||
step (np.ndarray): step value of the iteration
|
||||
cur_gradient (np.ndarray): current gradient
|
||||
old_gradient (np.ndarray): previous gradient
|
||||
hessian (np.ndarray): current hessian
|
||||
|
||||
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
|
||||
# @staticmethod
|
||||
# def update_hessian(
|
||||
# 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:
|
||||
# step (np.ndarray): step value of the iteration
|
||||
# cur_gradient (np.ndarray): current gradient
|
||||
# old_gradient (np.ndarray): previous gradient
|
||||
# hessian (np.ndarray): current hessian
|
||||
#
|
||||
# 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]:
|
||||
"""
|
||||
@@ -267,10 +231,9 @@ class Molecule:
|
||||
z_list = []
|
||||
|
||||
for atom in self.atom:
|
||||
if atom.na != ghost_number:
|
||||
x_list.append(atom.rx)
|
||||
y_list.append(atom.ry)
|
||||
z_list.append(atom.rz)
|
||||
x_list.append(atom.rx)
|
||||
y_list.append(atom.ry)
|
||||
z_list.append(atom.rz)
|
||||
|
||||
x_max = max(x_list)
|
||||
x_min = min(x_list)
|
||||
|
||||
@@ -1,64 +1,47 @@
|
||||
import math
|
||||
from copy import deepcopy
|
||||
from typing import List, Tuple, TextIO
|
||||
|
||||
import numpy as np
|
||||
from numpy import linalg
|
||||
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
from diceplayer.shared.utils.misc import BOHR2ANG
|
||||
from diceplayer.shared.utils.ptable import atomsymb
|
||||
from diceplayer.shared.utils.misc import BOHR2ANG
|
||||
|
||||
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.
|
||||
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
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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
|
||||
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
|
||||
"""
|
||||
Args:
|
||||
nmols (int): Number of molecules of the new type in the system
|
||||
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)
|
||||
|
||||
if isinstance(nmols, int) is False:
|
||||
raise TypeError("Error: nmols is not an integer")
|
||||
self.nmols.append(nmols)
|
||||
|
||||
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 rmsd_fit(self, p_index: int, r_index: int) -> Tuple[float, Molecule]:
|
||||
|
||||
projecting_mol = self.molecule[p_index]
|
||||
@@ -118,9 +101,9 @@ class System:
|
||||
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
|
||||
(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)
|
||||
|
||||
@@ -135,13 +118,71 @@ class System:
|
||||
|
||||
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 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
|
||||
"""
|
||||
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]))
|
||||
@@ -158,52 +199,15 @@ class System:
|
||||
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":
|
||||
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
|
||||
Print the geometry of the molecule in the Output file
|
||||
|
||||
Args:
|
||||
cycle (int): Number of the cycle
|
||||
fh (TextIO): 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)))
|
||||
@@ -217,12 +221,12 @@ class System:
|
||||
|
||||
def printChargesAndDipole(self, cycle: int, fh: TextIO) -> None:
|
||||
"""
|
||||
Print the charges and dipole of the molecule in the Output file
|
||||
Print the charges and dipole of the molecule in the Output file
|
||||
|
||||
Args:
|
||||
cycle (int): Number of the cycle
|
||||
fh (TextIO): 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)))
|
||||
|
||||
2
diceplayer/shared/external/__external.py
vendored
2
diceplayer/shared/external/__external.py
vendored
@@ -18,7 +18,7 @@ class External(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def start(self):
|
||||
def start(self, cycle: int):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
|
||||
1
diceplayer/shared/external/__init__.py
vendored
1
diceplayer/shared/external/__init__.py
vendored
@@ -0,0 +1 @@
|
||||
from .__external import External
|
||||
|
||||
45
diceplayer/shared/external/dice.py
vendored
45
diceplayer/shared/external/dice.py
vendored
@@ -1,9 +1,13 @@
|
||||
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||
from diceplayer.shared.external.__external import External
|
||||
from diceplayer.shared.config.dice_dto import DiceDTO
|
||||
from diceplayer.shared.external import External
|
||||
|
||||
from multiprocessing import Process, connection
|
||||
from setproctitle import setproctitle
|
||||
import sys
|
||||
|
||||
|
||||
class Dice(External):
|
||||
__slots__ = ['config', 'step']
|
||||
|
||||
def __init__(self, data: dict):
|
||||
self.config: DiceDTO = self.set_config(data)
|
||||
@@ -12,11 +16,38 @@ class Dice(External):
|
||||
def set_config(data: dict) -> DiceDTO:
|
||||
return DiceDTO.from_dict(data)
|
||||
|
||||
def configure(self):
|
||||
pass
|
||||
def configure(self, step: any):
|
||||
self.step = step
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
def start(self, cycle: int):
|
||||
procs = [
|
||||
Process(target=self._simulation_process, args=(cycle, proc))
|
||||
for proc in range(1, self.config.ncores+1)
|
||||
]
|
||||
|
||||
for proc in procs:
|
||||
proc.start()
|
||||
|
||||
connection.wait(p.sentinel for p in procs)
|
||||
|
||||
def reset(self):
|
||||
pass
|
||||
del self.step
|
||||
|
||||
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):
|
||||
raise NotImplementedError
|
||||
|
||||
def _make_dice_inputs(self, cycle, proc):
|
||||
raise NotImplementedError
|
||||
|
||||
def _run_dice(self, cycle, proc):
|
||||
raise NotImplementedError
|
||||
|
||||
3
diceplayer/shared/external/gaussian.py
vendored
3
diceplayer/shared/external/gaussian.py
vendored
@@ -1,6 +1,5 @@
|
||||
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||
from diceplayer.shared.config.gaussian_dto import GaussianDTO
|
||||
from diceplayer.shared.external.__external import External
|
||||
from diceplayer.shared.external import External
|
||||
|
||||
|
||||
class Gaussian(External):
|
||||
|
||||
Reference in New Issue
Block a user