From 92023318520f92d3e05484ef990fe391dea8168c Mon Sep 17 00:00:00 2001 From: Vitor Hideyoshi Date: Mon, 24 Apr 2023 12:15:51 -0300 Subject: [PATCH] Finishes DiceInterface Refactoring and Continues Tests Implementation --- control.example.yml | 9 +- diceplayer/__main__.py | 8 +- diceplayer/player.py | 31 +- diceplayer/shared/config/dice_dto.py | 8 +- diceplayer/shared/config/gaussian_dto.py | 3 + diceplayer/shared/config/player_dto.py | 4 + diceplayer/shared/config/step_dto.py | 18 +- diceplayer/shared/external/__init__.py | 1 - diceplayer/shared/external/dice.py | 53 --- diceplayer/shared/interface/__init__.py | 1 + .../__interface.py} | 2 +- diceplayer/shared/interface/dice_interface.py | 388 ++++++++++++++++++ .../gaussian_interface.py} | 4 +- setup.py | 62 --- tests/shared/config/test_dice_dto.py | 19 +- tests/shared/config/test_gaussian_dto.py | 45 ++ tests/shared/config/test_player_dto.py | 28 ++ tests/shared/environment/test_system.py | 19 - tests/shared/external/test_dice.py | 116 +++++- 19 files changed, 629 insertions(+), 190 deletions(-) delete mode 100644 diceplayer/shared/external/__init__.py delete mode 100644 diceplayer/shared/external/dice.py create mode 100644 diceplayer/shared/interface/__init__.py rename diceplayer/shared/{external/__external.py => interface/__interface.py} (95%) create mode 100644 diceplayer/shared/interface/dice_interface.py rename diceplayer/shared/{external/gaussian.py => interface/gaussian_interface.py} (81%) delete mode 100755 setup.py create mode 100644 tests/shared/config/test_gaussian_dto.py create mode 100644 tests/shared/config/test_player_dto.py diff --git a/control.example.yml b/control.example.yml index cd02ce9..99f5ae4 100644 --- a/control.example.yml +++ b/control.example.yml @@ -1,21 +1,22 @@ diceplayer: maxcyc: 3 opt: no + ncores: 4 nprocs: 4 qmprog: 'g16' lps: no ghosts: no - altsteps: 20000 + altsteps: 20000 dice: - ncores: 3 nmol: [1, 50] dens: 0.75 - nstep: [2000, 3000] + nstep: [2000, 3000, 4000] isave: 1000 outname: 'phb' + progname: '~/.local/bin/dice' ljname: 'phb.ljc' - randominit: 'always' + randominit: 'first' gaussian: qmprog: 'g16' diff --git a/diceplayer/__main__.py b/diceplayer/__main__.py index c4dd5db..12fa4c3 100644 --- a/diceplayer/__main__.py +++ b/diceplayer/__main__.py @@ -1,4 +1,4 @@ -from diceplayer.shared.external.dice import Dice +from diceplayer.shared.interface.dice_interface import DiceInterface from diceplayer.player import Player from pathlib import Path @@ -7,7 +7,6 @@ import logging import pickle import sys - __VERSION = "v0.0.1" @@ -51,7 +50,7 @@ def main(): output_path = Path(args.outfile) if output_path.exists(): - output_path.rename(str(output_path)+".backup") + output_path.rename(str(output_path) + ".backup") except Exception as err: sys.exit(err) @@ -66,5 +65,6 @@ def main(): player.start() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/diceplayer/player.py b/diceplayer/player.py index 9d196bb..4229e08 100644 --- a/diceplayer/player.py +++ b/diceplayer/player.py @@ -1,3 +1,4 @@ +from diceplayer.shared.config.step_dto import StepDTO from diceplayer.shared.environment.atom import Atom from diceplayer.shared.utils.dataclass_protocol import Dataclass from diceplayer.shared.config.gaussian_dto import GaussianDTO @@ -5,9 +6,9 @@ from diceplayer.shared.environment.molecule import Molecule from diceplayer.shared.environment.system import System from diceplayer.shared.utils.misc import weekday_date_time from diceplayer.shared.config.player_dto import PlayerDTO -from diceplayer.shared.external.gaussian import Gaussian +from diceplayer.shared.interface.gaussian_interface import GaussianInterface from diceplayer.shared.config.dice_dto import DiceDTO -from diceplayer.shared.external.dice import Dice +from diceplayer.shared.interface.dice_interface import DiceInterface from dataclasses import fields from pathlib import Path @@ -39,8 +40,8 @@ class Player: config_data.get("diceplayer") ) - self.gaussian = Gaussian(config_data.get("gaussian")) - self.dice = Dice(config_data.get("dice")) + self.gaussian = GaussianInterface(config_data.get("gaussian")) + self.dice = DiceInterface(config_data.get("dice")) def start(self): self.print_keywords() @@ -50,6 +51,9 @@ class Player: self.read_potentials() # self.print_potentials() + self.dice_start(1) + self.dice_start(2) + def create_simulation_dir(self): simulation_dir_path = Path(self.config.simulation_dir) if simulation_dir_path.exists(): @@ -140,7 +144,7 @@ class Player: self.dice.config.ljname ) ) - self.dice.combrule = combrule + self.dice.config.combrule = combrule ntypes = ljdata.pop(0).split()[0] if not ntypes.isdigit(): @@ -184,8 +188,21 @@ class Player: Atom(**self.validate_atom_dict(i, j, new_atom)) ) - def dice_start(self): - self.dice.start() + def dice_start(self, cycle: int): + self.dice.configure( + StepDTO( + ncores=self.config.ncores, + nprocs=self.config.nprocs, + simulation_dir=self.config.simulation_dir, + altsteps=self.config.altsteps, + molecule=self.system.molecule, + nmol=self.system.nmols, + ) + ) + + self.dice.start(cycle) + + self.dice.reset() def gaussian_start(self): self.gaussian.start() diff --git a/diceplayer/shared/config/dice_dto.py b/diceplayer/shared/config/dice_dto.py index 935665f..07feab0 100644 --- a/diceplayer/shared/config/dice_dto.py +++ b/diceplayer/shared/config/dice_dto.py @@ -7,10 +7,11 @@ from typing import List @dataclass class DiceDTO(Dataclass): - + """ + Data Transfer Object for the Dice configuration. + """ ljname: str outname: str - ncores: int dens: float nmol: List[int] nstep: List[int] @@ -20,6 +21,7 @@ class DiceDTO(Dataclass): isave: int = 1000 press: float = 1.0 temp: float = 300.0 + progname: str = "dice" randominit: str = 'first' def __post_init__(self): @@ -44,7 +46,7 @@ class DiceDTO(Dataclass): "Error: 'nmol' keyword not defined appropriately in config file" ) - if not isinstance(self.nstep, list): + 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" ) diff --git a/diceplayer/shared/config/gaussian_dto.py b/diceplayer/shared/config/gaussian_dto.py index 6e609a6..7ede18f 100644 --- a/diceplayer/shared/config/gaussian_dto.py +++ b/diceplayer/shared/config/gaussian_dto.py @@ -6,6 +6,9 @@ from dacite import from_dict @dataclass class GaussianDTO(Dataclass): + """ + Data Transfer Object for the Gaussian configuration. + """ level: str qmprog: str keywords: str diff --git a/diceplayer/shared/config/player_dto.py b/diceplayer/shared/config/player_dto.py index be9f25a..e4fa7bf 100644 --- a/diceplayer/shared/config/player_dto.py +++ b/diceplayer/shared/config/player_dto.py @@ -6,9 +6,13 @@ from dacite import from_dict @dataclass class PlayerDTO(Dataclass): + """ + Data Transfer Object for the player configuration. + """ opt: bool maxcyc: int nprocs: int + ncores: int qmprog: str = 'g16' altsteps: int = 20000 diff --git a/diceplayer/shared/config/step_dto.py b/diceplayer/shared/config/step_dto.py index 2a1145f..32ab6d9 100644 --- a/diceplayer/shared/config/step_dto.py +++ b/diceplayer/shared/config/step_dto.py @@ -1,4 +1,5 @@ from diceplayer.shared.environment.molecule import Molecule +from diceplayer.shared.config.player_dto import PlayerDTO from dataclasses import dataclass from typing import List @@ -6,13 +7,16 @@ from typing import List @dataclass class StepDTO: - nprocs: int = None - ncores: int = None - altsteps: int = None - switchcyc: int = None - opt: str = None + """ + Data Transfer Object for the step configuration. + """ + ncores: int + nprocs: int + simulation_dir: str + + altsteps: int + nmol: List[int] = None molecule: List[Molecule] = None - charges: List[float] = None - position: List[float] = None \ No newline at end of file + position: List[float] = None diff --git a/diceplayer/shared/external/__init__.py b/diceplayer/shared/external/__init__.py deleted file mode 100644 index 25c400b..0000000 --- a/diceplayer/shared/external/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .__external import External diff --git a/diceplayer/shared/external/dice.py b/diceplayer/shared/external/dice.py deleted file mode 100644 index 464d6eb..0000000 --- a/diceplayer/shared/external/dice.py +++ /dev/null @@ -1,53 +0,0 @@ -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) - - @staticmethod - def set_config(data: dict) -> DiceDTO: - return DiceDTO.from_dict(data) - - def configure(self, step: any): - self.step = step - - 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): - 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 diff --git a/diceplayer/shared/interface/__init__.py b/diceplayer/shared/interface/__init__.py new file mode 100644 index 0000000..4337de4 --- /dev/null +++ b/diceplayer/shared/interface/__init__.py @@ -0,0 +1 @@ +from .__interface import Interface diff --git a/diceplayer/shared/external/__external.py b/diceplayer/shared/interface/__interface.py similarity index 95% rename from diceplayer/shared/external/__external.py rename to diceplayer/shared/interface/__interface.py index 030d4bf..c468cf4 100644 --- a/diceplayer/shared/external/__external.py +++ b/diceplayer/shared/interface/__interface.py @@ -3,7 +3,7 @@ from diceplayer.shared.utils.dataclass_protocol import Dataclass from abc import ABC, abstractmethod -class External(ABC): +class Interface(ABC): __slots__ = [ 'config' ] diff --git a/diceplayer/shared/interface/dice_interface.py b/diceplayer/shared/interface/dice_interface.py new file mode 100644 index 0000000..fbb639a --- /dev/null +++ b/diceplayer/shared/interface/dice_interface.py @@ -0,0 +1,388 @@ +from __future__ import annotations + +from diceplayer.shared.config.dice_dto import DiceDTO +from diceplayer.shared.config.step_dto import StepDTO +from diceplayer.shared.interface import Interface + +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): + __slots__ = ['config', 'step'] + + title = "Diceplayer run" + + def __init__(self, data: dict): + self.config: DiceDTO = self.set_config(data) + self.step: StepDTO | None = None + + @staticmethod + def set_config(data: dict) -> DiceDTO: + return DiceDTO.from_dict(data) + + def configure(self, step: any): + self.step = step + + 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) + + def reset(self): + 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): + 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.config.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.config.dens = self._new_density(last_xyz_file) + + else: + self._make_nvt_ter(cycle, proc_dir) + + if len(self.config.nstep) == 2: + self._make_nvt_eq(proc_dir) + + elif len(self.config.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}" + ) + os.chdir(proc_dir) + + if not (self.config.randominit == 'first' and cycle > 1): + self.run_dice_file(cycle, proc, "NVT.ter") + + if len(self.config.nstep) == 2: + self.run_dice_file(cycle, proc, "NVT.eq") + + elif len(self.config.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() + + nsites_mm = 0 + for i in range(len(self.step.nmol)): + nsites_mm += self.step.nmol[i] * len(self.step.molecule[i].atom) + + xyz_lines = xyz_lines[-nsites_mm:] + + input_file = Path(proc_dir, self.config.outname + ".xy") + with open(input_file, 'w') as f: + + for atom in self.step.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.step.molecule)): + total_mass += self.step.molecule[i].total_mass * self.step.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.config.ljname}\n") + f.write(f"outname = {self.config.outname}\n") + + mol_string = " ".join(str(x) for x in self.config.nmol) + f.write(f"nmol = {mol_string}\n") + + f.write(f"dens = {self.config.dens}\n") + f.write(f"temp = {self.config.temp}\n") + + if self.config.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.config.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.config.upbuf}") + + def _make_nvt_eq(self, 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.config.ljname}\n") + f.write(f"outname = {self.config.outname}\n") + + mol_string = " ".join(str(x) for x in self.config.nmol) + f.write(f"nmol = {mol_string}\n") + + f.write(f"dens = {self.config.dens}\n") + f.write(f"temp = {self.config.temp}\n") + f.write("init = no\n") + f.write(f"nstep = {self.config.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.config.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.config.ljname}\n") + f.write(f"outname = {self.config.outname}\n") + + mol_string = " ".join(str(x) for x in self.config.nmol) + f.write(f"nmol = {mol_string}\n") + + f.write(f"press = {self.config.press}\n") + f.write(f"temp = {self.config.temp}\n") + + if self.config.randominit == "first" and cycle > 1: + f.write("init = yesreadxyz\n") + f.write(f"dens = {self.config.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.config.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.config.ljname}\n") + f.write(f"outname = {self.config.outname}\n") + + mol_string = " ".join(str(x) for x in self.config.nmol) + f.write(f"nmol = {mol_string}\n") + + f.write(f"press = {self.config.press}\n") + f.write(f"temp = {self.config.temp}\n") + + f.write(f"nstep = 5\n") + + f.write(f"vstep = {int(self.config.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.config.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.config.ljname) + with open(file, 'w') as f: + f.write(f"{self.config.combrule}\n") + f.write(f"{len(self.step.nmol)}\n") + + nsites_qm = len(self.step.molecule[0].atom) + f.write(f"{nsites_qm} {self.step.molecule[0].molname}\n") + + for atom in self.step.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.step.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.config.progname} < {infile.name} > {outfile.name}", + ] + ) + else: + exit_status = subprocess.call( + self.config.progname, stdin=infile, stdout=outfile + ) + + if exit_status != 0: + raise Exception(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 Exception(f"Dice process step{cycle:02d}-p{proc:02d} did not exit properly") diff --git a/diceplayer/shared/external/gaussian.py b/diceplayer/shared/interface/gaussian_interface.py similarity index 81% rename from diceplayer/shared/external/gaussian.py rename to diceplayer/shared/interface/gaussian_interface.py index c96ebd4..71bdf54 100644 --- a/diceplayer/shared/external/gaussian.py +++ b/diceplayer/shared/interface/gaussian_interface.py @@ -1,8 +1,8 @@ from diceplayer.shared.config.gaussian_dto import GaussianDTO -from diceplayer.shared.external import External +from diceplayer.shared.interface import Interface -class Gaussian(External): +class GaussianInterface(Interface): def __init__(self, data: dict): self.config: GaussianDTO = self.set_config(data) diff --git a/setup.py b/setup.py deleted file mode 100755 index 925add5..0000000 --- a/setup.py +++ /dev/null @@ -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) diff --git a/tests/shared/config/test_dice_dto.py b/tests/shared/config/test_dice_dto.py index cd6a8c5..553e1f1 100644 --- a/tests/shared/config/test_dice_dto.py +++ b/tests/shared/config/test_dice_dto.py @@ -8,10 +8,9 @@ class TestDiceDto(unittest.TestCase): dice_dto = DiceDTO( ljname='test', outname='test', - ncores=1, dens=1.0, nmol=[1], - nstep=[1], + nstep=[1, 1], ) self.assertIsInstance(dice_dto, DiceDTO) @@ -21,10 +20,9 @@ class TestDiceDto(unittest.TestCase): DiceDTO( ljname=None, outname='test', - ncores=1, dens=1.0, nmol=[1], - nstep=[1], + nstep=[1, 1], ) self.assertEqual(ex.exception, "Error: 'ljname' keyword not specified in config file") @@ -33,10 +31,9 @@ class TestDiceDto(unittest.TestCase): DiceDTO( ljname='test', outname=None, - ncores=1, dens=1.0, nmol=[1], - nstep=[1], + nstep=[1, 1], ) self.assertEqual(ex.exception, "Error: 'outname' keyword not specified in config file") @@ -45,10 +42,9 @@ class TestDiceDto(unittest.TestCase): DiceDTO( ljname='test', outname='test', - ncores=1, dens=None, nmol=[1], - nstep=[1], + nstep=[1, 1], ) self.assertEqual(ex.exception, "Error: 'dens' keyword not specified in config file") @@ -57,10 +53,9 @@ class TestDiceDto(unittest.TestCase): DiceDTO( ljname='test', outname='test', - ncores=1, dens=1.0, nmol=0, - nstep=[1], + nstep=[1, 1], ) self.assertEqual(ex.exception, "Error: 'nmol' keyword not defined appropriately in config file") @@ -69,7 +64,6 @@ class TestDiceDto(unittest.TestCase): DiceDTO( ljname='test', outname='test', - ncores=1, dens=1.0, nmol=[1], nstep=0, @@ -80,10 +74,9 @@ class TestDiceDto(unittest.TestCase): dice_dto = DiceDTO.from_dict({ 'ljname': 'test', 'outname': 'test', - 'ncores': 1, 'dens': 1.0, 'nmol': [1], - 'nstep': [1], + 'nstep': [1, 1], }) self.assertIsInstance(dice_dto, DiceDTO) \ No newline at end of file diff --git a/tests/shared/config/test_gaussian_dto.py b/tests/shared/config/test_gaussian_dto.py new file mode 100644 index 0000000..67d013f --- /dev/null +++ b/tests/shared/config/test_gaussian_dto.py @@ -0,0 +1,45 @@ +from diceplayer.shared.config.gaussian_dto 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() diff --git a/tests/shared/config/test_player_dto.py b/tests/shared/config/test_player_dto.py new file mode 100644 index 0000000..a514fb1 --- /dev/null +++ b/tests/shared/config/test_player_dto.py @@ -0,0 +1,28 @@ +from diceplayer.shared.config.player_dto import PlayerDTO + +import unittest + + +class TestPlayerDTO(unittest.TestCase): + def test_class_instantiation(self): + player_dto = PlayerDTO(opt=True, maxcyc=100, nprocs=4) + + self.assertIsInstance(player_dto, PlayerDTO) + + def test_min_altsteps(self): + player_dto = PlayerDTO(opt=True, maxcyc=100, nprocs=4, altsteps=100) + + self.assertEqual(player_dto.altsteps, 20000) + + def test_from_dict(self): + player_dto = PlayerDTO.from_dict({ + 'opt': True, + 'maxcyc': 100, + 'nprocs': 4 + }) + + self.assertIsInstance(player_dto, PlayerDTO) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/shared/environment/test_system.py b/tests/shared/environment/test_system.py index cab9cbe..cff560e 100644 --- a/tests/shared/environment/test_system.py +++ b/tests/shared/environment/test_system.py @@ -26,25 +26,6 @@ class TestSystem(unittest.TestCase): system.add_type('test', Molecule('test')) self.assertEqual(ex.exception, 'Error: nmols is not an integer') - def test_center_of_mass_distance(self): - system = System() - - a = Molecule('test') - a.add_atom( - Atom(lbl=0, na=1, rx=0, ry=0, rz=0, chg=0, eps=0, sig=0) - ) - system.add_type(1, a) - - b = Molecule('test') - b.add_atom( - Atom(lbl=0, na=1, rx=0, ry=0, rz=0, chg=0, eps=0, sig=0) - ) - system.add_type(1, b) - - self.assertIsInstance(system.center_of_mass_distance(0, 1), float) - - - if __name__ == '__main__': unittest.main() diff --git a/tests/shared/external/test_dice.py b/tests/shared/external/test_dice.py index 8c51f95..d019d85 100644 --- a/tests/shared/external/test_dice.py +++ b/tests/shared/external/test_dice.py @@ -1,4 +1,5 @@ -from diceplayer.shared.external.dice import Dice +from diceplayer.shared.config.step_dto import StepDTO +from diceplayer.shared.interface.dice_interface import DiceInterface from unittest import mock import unittest @@ -6,28 +7,28 @@ import unittest class TestDice(unittest.TestCase): def test_class_instantiation(self): - dice = Dice( + dice = DiceInterface( { 'ljname': 'test', 'outname': 'test', 'ncores': 1, 'dens': 1.0, 'nmol': [1], - 'nstep': [1], + 'nstep': [1, 1], } ) - self.assertIsInstance(dice, Dice) + self.assertIsInstance(dice, DiceInterface) def test_configure(self): - dice = Dice( + dice = DiceInterface( { 'ljname': 'test', 'outname': 'test', 'ncores': 1, 'dens': 1.0, 'nmol': [1], - 'nstep': [1], + 'nstep': [1, 1], } ) @@ -38,14 +39,14 @@ class TestDice(unittest.TestCase): self.assertTrue(hasattr(dice, 'step')) def test_reset(self): - dice = Dice( + dice = DiceInterface( { 'ljname': 'test', 'outname': 'test', 'ncores': 1, 'dens': 1.0, 'nmol': [1], - 'nstep': [1], + 'nstep': [1, 1], } ) @@ -60,30 +61,41 @@ class TestDice(unittest.TestCase): @mock.patch('diceplayer.shared.external.dice.connection') @mock.patch('diceplayer.shared.external.dice.Process') def test_start(self, mock_process, mock_connection): - dice = Dice( + dice = DiceInterface( { 'ljname': 'test', 'outname': 'test', 'ncores': 1, 'dens': 1.0, 'nmol': [1], - 'nstep': [1], + 'nstep': [1, 1], } ) + dice.configure( + StepDTO( + ncores=1, + nprocs=1, + simulation_dir='test', + altsteps=1, + molecule=[], + nmol=[], + ) + ) + dice.start(1) self.assertTrue(mock_process.called) self.assertTrue(mock_connection.wait.called) def test_simulation_process_raises_exception(self): - dice = Dice( + dice = DiceInterface( { 'ljname': 'test', 'outname': 'test', 'ncores': 1, 'dens': 1.0, 'nmol': [1], - 'nstep': [1], + 'nstep': [1, 1], } ) @@ -94,14 +106,14 @@ class TestDice(unittest.TestCase): @mock.patch('diceplayer.shared.external.dice.Dice._make_dice_inputs') @mock.patch('diceplayer.shared.external.dice.Dice._run_dice') def test_simulation_process(self, mock_run_dice, mock_make_dice_inputs, mock_make_proc_dir): - dice = Dice( + dice = DiceInterface( { 'ljname': 'test', 'outname': 'test', 'ncores': 1, 'dens': 1.0, 'nmol': [1], - 'nstep': [1], + 'nstep': [1, 1], } ) @@ -111,6 +123,82 @@ class TestDice(unittest.TestCase): self.assertTrue(dice._make_dice_inputs.called) self.assertTrue(dice._run_dice.called) + @mock.patch('diceplayer.shared.external.dice.Path.mkdir') + @mock.patch('diceplayer.shared.external.dice.Path.exists') + def test_make_proc_dir_if_simdir_exists(self, mock_path_exists, mock_path_mkdir): + dice = DiceInterface( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1, 1], + } + ) + dice.configure( + StepDTO( + ncores=1, + nprocs=1, + simulation_dir='test', + altsteps=1, + molecule=[], + nmol=[], + ) + ) + + mock_path_exists.return_value = False + + dice._make_proc_dir(1, 1) + + self.assertEqual(mock_path_mkdir.call_count, 2) + + @mock.patch('diceplayer.shared.external.dice.Path.mkdir') + @mock.patch('diceplayer.shared.external.dice.Path.exists') + def test_make_proc_dir_if_simdir_doesnt_exists(self, mock_path_exists, mock_path_mkdir): + dice = DiceInterface( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1, 1], + } + ) + dice.configure( + StepDTO( + ncores=1, + nprocs=1, + simulation_dir='test', + altsteps=1, + molecule=[], + nmol=[], + ) + ) + + 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): + dice = DiceInterface( + { + 'ljname': 'test', + 'outname': 'test', + 'ncores': 1, + 'dens': 1.0, + 'nmol': [1], + 'nstep': [1, 1], + } + ) + + seed = dice._make_dice_seed() + + self.assertIsInstance(seed, int) + if __name__ == '__main__': unittest.main()