From 33612f2d7b690cd876781b215bf2df88c5f0666c Mon Sep 17 00:00:00 2001 From: Vitor Hideyoshi Date: Fri, 2 Jun 2023 20:20:38 -0300 Subject: [PATCH] Implements Refactoring in Player Class and Initial Working Version --- control.example.yml | 30 +- diceplayer/__main__.py | 17 + diceplayer/player.py | 198 ++++-- .../config/{dice_dto.py => dice_config.py} | 4 +- .../{gaussian_dto.py => gaussian_config.py} | 3 +- diceplayer/shared/config/player_config.py | 47 ++ diceplayer/shared/config/player_dto.py | 28 - diceplayer/shared/config/step_dto.py | 22 - diceplayer/shared/environment/molecule.py | 64 +- diceplayer/shared/environment/system.py | 92 ++- diceplayer/shared/interface/__interface.py | 17 +- diceplayer/shared/interface/dice_interface.py | 134 ++-- .../shared/interface/gaussian_interface.py | 380 ++++++++++- tests/mocks/mock_inputs.py | 111 ++++ tests/shared/config/test_dice_dto.py | 20 +- tests/shared/config/test_gaussian_dto.py | 2 +- tests/shared/config/test_player_dto.py | 82 ++- tests/shared/environment/test_system.py | 8 +- tests/shared/interface/test_dice_interface.py | 613 +++--------------- .../interface/test_gaussian_interface.py | 113 ++++ tests/test_player.py | 191 ++---- 21 files changed, 1193 insertions(+), 983 deletions(-) rename diceplayer/shared/config/{dice_dto.py => dice_config.py} (95%) rename diceplayer/shared/config/{gaussian_dto.py => gaussian_config.py} (93%) create mode 100644 diceplayer/shared/config/player_config.py delete mode 100644 diceplayer/shared/config/player_dto.py delete mode 100644 diceplayer/shared/config/step_dto.py create mode 100644 tests/mocks/mock_inputs.py create mode 100644 tests/shared/interface/test_gaussian_interface.py diff --git a/control.example.yml b/control.example.yml index b664d27..f475b7d 100644 --- a/control.example.yml +++ b/control.example.yml @@ -1,24 +1,24 @@ diceplayer: - maxcyc: 3 opt: no - ncores: 4 + mem: 24 + maxcyc: 10 + ncores: 5 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' + dice: + nmol: [1, 1000] + dens: 1.0 + nstep: [2000, 3000] + isave: 1000 + outname: 'phb' + progname: '~/.local/bin/dice' + ljname: 'phb.ljc' + randominit: 'first' -gaussian: - qmprog: 'g16' - level: 'MP2/aug-cc-pVDZ' - keywords: 'freq' + gaussian: + qmprog: 'g16' + level: 'MP2/aug-cc-pVDZ' \ No newline at end of file diff --git a/diceplayer/__main__.py b/diceplayer/__main__.py index 9a3071c..3bfd18d 100644 --- a/diceplayer/__main__.py +++ b/diceplayer/__main__.py @@ -55,8 +55,25 @@ def main(): player = Player(args.infile) + player.print_keywords() + + player.create_simulation_dir() + + player.read_potentials() + player.print_potentials() + + player.prepare_system() + player.start() + logger.info("\n+" + 88 * "-" + "+\n") + + player.print_results() + + logger.info("\n+" + 88 * "-" + "+\n") + + logger.info("Diceplayer finished successfully \n") + if __name__ == "__main__": main() diff --git a/diceplayer/player.py b/diceplayer/player.py index 25c10a5..7300bd3 100644 --- a/diceplayer/player.py +++ b/diceplayer/player.py @@ -1,19 +1,18 @@ 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_dto import GaussianDTO -from diceplayer.shared.environment.molecule import Molecule +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.config.player_dto import PlayerDTO +from diceplayer.shared.environment.molecule import Molecule from diceplayer.shared.environment.system import System -from diceplayer.shared.config.step_dto import StepDTO -from diceplayer.shared.config.dice_dto import DiceDTO from diceplayer.shared.environment.atom import Atom from diceplayer import logger from dataclasses import fields from pathlib import Path -from typing import Type +from typing import Type, List import logging import yaml import sys @@ -33,20 +32,50 @@ class Player: config_data.get("diceplayer") ) - self.gaussian = GaussianInterface(config_data.get("gaussian")) - self.dice = DiceInterface(config_data.get("dice")) + self.dice_interface = DiceInterface() + self.gaussian_interface = GaussianInterface() def start(self, initial_cycle: int = 1): - self.print_keywords() - - self.create_simulation_dir() - - self.read_potentials() - self.print_potentials() + logger.info( + "==========================================================================================\n" + "Starting the iterative process.\n" + "==========================================================================================\n" + ) for cycle in range(initial_cycle, self.config.maxcyc + 1): + + logger.info( + f"------------------------------------------------------------------------------------------\n" + f" Step # {cycle}\n" + f"------------------------------------------------------------------------------------------\n" + ) + self.dice_start(cycle) + try: + self.gaussian_start(cycle) + except StopIteration as e: + break + + 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(): @@ -71,13 +100,10 @@ class Player: "##########################################################################################\n" "############# Welcome to DICEPLAYER version 1.0 #############\n" "##########################################################################################\n" - "\n" ) logger.info("Your python version is {}\n".format(sys.version)) - logger.info("\n") logger.info("Program started on {}\n".format(weekday_date_time())) - logger.info("\n") - logger.info("Environment variables:\n") + logger.info("Environment variables:") for var in ENV: logger.info( "{} = {}\n".format( @@ -85,68 +111,55 @@ class Player: ) ) - logger.info( - "\n==========================================================================================\n" - " CONTROL variables being used in this run:\n" - "------------------------------------------------------------------------------------------\n" - "\n" - ) - - logger.info("\n") - logger.info( "------------------------------------------------------------------------------------------\n" " DICE variables being used in this run:\n" "------------------------------------------------------------------------------------------\n" - "\n" ) - log_keywords(self.dice.config, DiceDTO) - - logger.info("\n") + log_keywords(self.config.dice, DiceConfig) logger.info( "------------------------------------------------------------------------------------------\n" " GAUSSIAN variables being used in this run:\n" "------------------------------------------------------------------------------------------\n" - "\n" ) - log_keywords(self.gaussian.config, GaussianDTO) + log_keywords(self.config.gaussian, GaussianDTO) logger.info("\n") def read_potentials(self): - ljname_path = Path(self.dice.config.ljname) + ljname_path = Path(self.config.dice.ljname) if ljname_path.exists(): - with open(self.dice.config.ljname) as file: + with open(self.config.dice.ljname) as file: ljc_data = file.readlines() else: raise RuntimeError( - f"Potential file {self.dice.config.ljname} not found." + 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.dice.config.ljname + self.config.dice.ljname ) ) - self.dice.config.combrule = combrule + 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.dice.config.ljname + self.config.dice.ljname ) ) ntypes = int(ntypes) - if ntypes != len(self.dice.config.nmol): + if ntypes != len(self.config.dice.nmol): sys.exit( - f"Error: number of molecule types in file {self.dice.config.ljname} " + f"Error: number of molecule types in file {self.config.dice.ljname} " f"must match that of 'nmol' keyword in config file" ) @@ -165,7 +178,7 @@ class Player: ) nsites = int(nsites) - self.system.add_type(nsites, Molecule(molname)) + self.system.add_type(Molecule(molname)) atom_fields = ["lbl", "na", "rx", "ry", "rz", "chg", "eps", "sig"] for j in range(nsites): @@ -178,20 +191,15 @@ class Player: ) 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" - ) - logger.info( - f" Potential parameters from file {self.dice.config.ljname}:" - ) - logger.info( + f" Potential parameters from file {self.config.dice.ljname}:\n" "------------------------------------------------------------------------------------------" "\n" ) - logger.info(f"Combination rule: {self.dice.config.combrule}") + logger.info(f"Combination rule: {self.config.dice.combrule}") logger.info( f"Types of molecules: {len(self.system.molecule)}\n" ) @@ -229,28 +237,50 @@ class Player: logger.info("\n") - logger.info( - "==========================================================================================" - ) - 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_interface.configure( + self.config, + self.system, ) - self.dice.start(cycle) + self.dice_interface.start(cycle) - self.dice.reset() + self.dice_interface.reset() def gaussian_start(self, cycle: int): - self.gaussian.start(cycle) + 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) + + if diff < self.config.gaussian.chg_tol: + logger.info( + f'Charges converged after {cycle} cycles.' + ) + raise StopIteration() @staticmethod def validate_atom_dict(molecule_type, molecule_site, atom_dict: dict) -> dict: @@ -326,9 +356,43 @@ class Player: 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") + @staticmethod - def set_config(data: dict) -> PlayerDTO: - return PlayerDTO.from_dict(data) + def set_config(data: dict) -> PlayerConfig: + return PlayerConfig.from_dict(data) @staticmethod def read_keywords(infile) -> dict: diff --git a/diceplayer/shared/config/dice_dto.py b/diceplayer/shared/config/dice_config.py similarity index 95% rename from diceplayer/shared/config/dice_dto.py rename to diceplayer/shared/config/dice_config.py index 07feab0..b2a5b66 100644 --- a/diceplayer/shared/config/dice_dto.py +++ b/diceplayer/shared/config/dice_config.py @@ -6,7 +6,7 @@ from typing import List @dataclass -class DiceDTO(Dataclass): +class DiceConfig(Dataclass): """ Data Transfer Object for the Dice configuration. """ @@ -53,4 +53,4 @@ class DiceDTO(Dataclass): @classmethod def from_dict(cls, param: dict): - return from_dict(DiceDTO, param) + return from_dict(DiceConfig, param) diff --git a/diceplayer/shared/config/gaussian_dto.py b/diceplayer/shared/config/gaussian_config.py similarity index 93% rename from diceplayer/shared/config/gaussian_dto.py rename to diceplayer/shared/config/gaussian_config.py index 7ede18f..b5b24ce 100644 --- a/diceplayer/shared/config/gaussian_dto.py +++ b/diceplayer/shared/config/gaussian_config.py @@ -11,10 +11,11 @@ class GaussianDTO(Dataclass): """ level: str qmprog: str - keywords: 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"): diff --git a/diceplayer/shared/config/player_config.py b/diceplayer/shared/config/player_config.py new file mode 100644 index 0000000..75e2b12 --- /dev/null +++ b/diceplayer/shared/config/player_config.py @@ -0,0 +1,47 @@ +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 + 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) diff --git a/diceplayer/shared/config/player_dto.py b/diceplayer/shared/config/player_dto.py deleted file mode 100644 index e4fa7bf..0000000 --- a/diceplayer/shared/config/player_dto.py +++ /dev/null @@ -1,28 +0,0 @@ -from diceplayer.shared.utils.dataclass_protocol import Dataclass - -from dataclasses import dataclass -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 - 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): - return from_dict(PlayerDTO, param) diff --git a/diceplayer/shared/config/step_dto.py b/diceplayer/shared/config/step_dto.py deleted file mode 100644 index 32ab6d9..0000000 --- a/diceplayer/shared/config/step_dto.py +++ /dev/null @@ -1,22 +0,0 @@ -from diceplayer.shared.environment.molecule import Molecule -from diceplayer.shared.config.player_dto import PlayerDTO - -from dataclasses import dataclass -from typing import List - - -@dataclass -class StepDTO: - """ - 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 diff --git a/diceplayer/shared/environment/molecule.py b/diceplayer/shared/environment/molecule.py index 390f3fc..6b89278 100644 --- a/diceplayer/shared/environment/molecule.py +++ b/diceplayer/shared/environment/molecule.py @@ -1,15 +1,18 @@ -import logging -import math -from copy import deepcopy -from typing import List, Any, Tuple, Final, Union - -import numpy as np -from nptyping import NDArray, Shape, Float -from numpy.linalg import linalg +from __future__ import annotations +from diceplayer.shared.utils.ptable import ghost_number from diceplayer.shared.environment.atom import Atom from diceplayer.shared.utils.misc import BOHR2ANG -from diceplayer.shared.utils.ptable import ghost_number +from diceplayer import logger + +from nptyping import NDArray, Shape, Float +from numpy.linalg import linalg +import numpy as np + +from typing import List, Any, Tuple, Union +from copy import deepcopy +import logging +import math class Molecule: @@ -185,11 +188,18 @@ class Molecule: return position - def update_charges(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): + diff = max(diff, abs(atom.chg - charges[i])) atom.chg = charges[i] + return diff + # @staticmethod # def update_hessian( # step: np.ndarray, @@ -299,48 +309,48 @@ class Molecule: Prints the Molecule information into a Output File """ - logging.info( - " Center of mass = ( {:>10.4f} , {:>10.4f} , {:>10.4f} )\n".format( + logger.info( + " Center of mass = ( {:>10.4f} , {:>10.4f} , {:>10.4f} )".format( self.com[0], self.com[1], self.com[2] ) ) inertia = self.inertia_tensor() evals, evecs = self.principal_axes() - logging.info( - " Moments of inertia = {:>9E} {:>9E} {:>9E}\n".format( + logger.info( + " Moments of inertia = {:>9E} {:>9E} {:>9E}".format( evals[0], evals[1], evals[2] ) ) - logging.info( - " Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format( + logger.info( + " Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format( evecs[0, 0], evecs[1, 0], evecs[2, 0] ) ) - logging.info( - " Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format( + logger.info( + " Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format( evecs[0, 1], evecs[1, 1], evecs[2, 1] ) ) - logging.info( - " Minor principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )\n".format( + logger.info( + " Minor principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format( evecs[0, 2], evecs[1, 2], evecs[2, 2] ) ) sizes = self.sizes_of_molecule() - logging.info( - " Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )\n".format( + logger.info( + " Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )".format( sizes[0], sizes[1], sizes[2] ) ) - logging.info(" 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() - logging.info(" Total charge = {:>8.4f} e\n".format(chg_dip[0])) - logging.info( - " Dipole moment = ( {:>9.4f} , {:>9.4f} , {:>9.4f} ) Total = {:>9.4f} Debye\n\n".format( + logger.info(" Total charge = {:>8.4f} e".format(chg_dip[0])) + logger.info( + " 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] ) ) diff --git a/diceplayer/shared/environment/system.py b/diceplayer/shared/environment/system.py index 1a6aff7..8034877 100644 --- a/diceplayer/shared/environment/system.py +++ b/diceplayer/shared/environment/system.py @@ -1,6 +1,7 @@ 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 @@ -11,50 +12,42 @@ 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 - """ - - self.molecule: List[Molecule] = [] - self.nmols: List[int] = [] - - def add_type(self, nmols: int, m: Molecule) -> None: + Initializes an empty system object that will be populated afterwards """ - Adds a new molecule type to the system + self.nmols: List[int] = [] + self.molecule: List[Molecule] = [] - Args: - nmols (int): Number of molecules of the new type in the system - m (Molecule): The instance of the new type of 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) - if isinstance(nmols, int) is False: - raise TypeError("Error: nmols is not an integer") - self.nmols.append(nmols) - - def update_molecule(self, position: np.ndarray, fh: TextIO) -> None: + 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 - fh (TextIO): Output file - """ + Args: + position (np.ndarray): numpy position vector + """ position_in_ang = (position * BOHR2ANG).tolist() - self.add_type(self.nmols[0], deepcopy(self.molecule[0])) + 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) @@ -62,8 +55,8 @@ class System: 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)) + 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]: @@ -200,7 +193,6 @@ class System: # # return min_dist, nearestmol - # def print_geom(self, cycle: int, fh: TextIO) -> None: # """ # Print the geometry of the molecule in the Output file @@ -220,22 +212,22 @@ class System: # ) # ) # - # 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] - # ) - # ) + 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] + ) + ) diff --git a/diceplayer/shared/interface/__interface.py b/diceplayer/shared/interface/__interface.py index c468cf4..bede652 100644 --- a/diceplayer/shared/interface/__interface.py +++ b/diceplayer/shared/interface/__interface.py @@ -1,20 +1,23 @@ -from diceplayer.shared.utils.dataclass_protocol import Dataclass +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__ = [ - 'config' + 'step', + 'system' ] - @abstractmethod - def __init__(self, data: dict): - pass + def __init__(self): + self.system: System | None = None + self.step: PlayerConfig | None = None - @staticmethod @abstractmethod - def set_config(data: dict) -> Dataclass: + def configure(self, step: PlayerConfig, system: System): pass @abstractmethod diff --git a/diceplayer/shared/interface/dice_interface.py b/diceplayer/shared/interface/dice_interface.py index 68d4be8..5b754a6 100644 --- a/diceplayer/shared/interface/dice_interface.py +++ b/diceplayer/shared/interface/dice_interface.py @@ -1,7 +1,7 @@ from __future__ import annotations -from diceplayer.shared.config.dice_dto import DiceDTO -from diceplayer.shared.config.step_dto import StepDTO +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 @@ -26,23 +26,14 @@ MAX_SEED: Final[int] = 4294967295 class DiceInterface(Interface): 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): + def configure(self, step: PlayerConfig, system: System): self.step = step + self.system = system def start(self, cycle: int): procs = [] sentinels = [] - logger.info(f"---------------------- DICE - CYCLE {cycle} --------------------------\n") - for proc in range(1, self.step.nprocs + 1): p = Process(target=self._simulation_process, args=(cycle, proc)) p.start() @@ -66,6 +57,7 @@ class DiceInterface(Interface): 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}") @@ -102,7 +94,7 @@ class DiceInterface(Interface): # 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: + if self.step.dice.randominit == 'first' and cycle > 1: last_xyz = Path( self.step.simulation_dir, f"step{(cycle - 1):02d}", @@ -115,15 +107,15 @@ class DiceInterface(Interface): 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) + self.step.dice.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) + if len(self.step.dice.nstep) == 2: + self._make_nvt_eq(cycle, proc_dir) - elif len(self.config.nstep) == 3: + elif len(self.step.dice.nstep) == 3: self._make_npt_ter(cycle, proc_dir) self._make_npt_eq(proc_dir) @@ -142,13 +134,13 @@ class DiceInterface(Interface): os.chdir(proc_dir) - if not (self.config.randominit == 'first' and cycle > 1): + if not (self.step.dice.randominit == 'first' and cycle > 1): self.run_dice_file(cycle, proc, "NVT.ter") - if len(self.config.nstep) == 2: + if len(self.step.dice.nstep) == 2: self.run_dice_file(cycle, proc, "NVT.eq") - elif len(self.config.nstep) == 3: + elif len(self.step.dice.nstep) == 3: self.run_dice_file(cycle, proc, "NPT.ter") self.run_dice_file(cycle, proc, "NPT.eq") @@ -175,15 +167,15 @@ class DiceInterface(Interface): xyz_lines = last_xyz_file.readlines() nsites_mm = 0 - for i in range(1, len(self.step.nmol)): - nsites_mm += self.step.nmol[i] * len(self.step.molecule[i].atom) + for i in range(1, len(self.step.dice.nmol)): + nsites_mm += self.step.dice.nmol[i] * len(self.system.molecule[i].atom) xyz_lines = xyz_lines[-nsites_mm:] - input_file = Path(proc_dir, self.config.outname + ".xy") + input_file = Path(proc_dir, self.step.dice.outname + ".xy") with open(input_file, 'w') as f: - for atom in self.step.molecule[0].atom: + for atom in self.system.molecule[0].atom: f.write( f"{atom.rx:>10.6f} {atom.ry:>10.6f} {atom.rz:>10.6f}\n" ) @@ -204,8 +196,8 @@ class DiceInterface(Interface): 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] + 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 @@ -216,21 +208,21 @@ class DiceInterface(Interface): 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") + 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.config.nmol) + mol_string = " ".join(str(x) for x in self.step.dice.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(f"dens = {self.step.dice.dens}\n") + f.write(f"temp = {self.step.dice.temp}\n") - if self.config.randominit == "first" and cycle > 1: + 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.config.nstep[0]}\n") + f.write(f"nstep = {self.step.dice.nstep[0]}\n") f.write("vstep = 0\n") f.write("mstop = 1\n") @@ -241,30 +233,36 @@ class DiceInterface(Interface): seed = int(1e6 * random.random()) f.write(f"seed = {seed}\n") - f.write(f"upbuf = {self.config.upbuf}") + f.write(f"upbuf = {self.step.dice.upbuf}") - def _make_nvt_eq(self, proc_dir): + 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.config.ljname}\n") - f.write(f"outname = {self.config.outname}\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.config.nmol) + mol_string = " ".join(str(x) for x in self.step.dice.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(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.config.isave}\n") + f.write(f"isave = {self.step.dice.isave}\n") f.write(f"irdf = {10 * self.step.nprocs}\n") seed = int(1e6 * random.random()) @@ -276,22 +274,22 @@ class DiceInterface(Interface): 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") + 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.config.nmol) + mol_string = " ".join(str(x) for x in self.step.dice.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"press = {self.step.dice.press}\n") + f.write(f"temp = {self.step.dice.temp}\n") - if self.config.randominit == "first" and cycle > 1: + if self.step.dice.randominit == "first" and cycle > 1: f.write("init = yesreadxyz\n") - f.write(f"dens = {self.config.dens:<8.4f}\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.config.nstep[1] / 5)}\n") + f.write(f"vstep = {int(self.step.dice.nstep[1] / 5)}\n") f.write("nstep = 5\n") f.write("mstop = 1\n") @@ -308,23 +306,23 @@ class DiceInterface(Interface): 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") + 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.config.nmol) + mol_string = " ".join(str(x) for x in self.step.dice.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"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.config.nstep[2] / 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.config.isave}\n") + f.write(f"isave = {self.step.dice.isave}\n") f.write(f"irdf = {10 * self.step.nprocs}\n") seed = int(1e6 * random.random()) @@ -333,15 +331,15 @@ class DiceInterface(Interface): 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) + file = Path(proc_dir, self.step.dice.ljname) with open(file, 'w') as f: - f.write(f"{self.config.combrule}\n") - f.write(f"{len(self.step.nmol)}\n") + f.write(f"{self.step.dice.combrule}\n") + f.write(f"{len(self.step.dice.nmol)}\n") - nsites_qm = len(self.step.molecule[0].atom) - f.write(f"{nsites_qm} {self.step.molecule[0].molname}\n") + nsites_qm = len(self.system.molecule[0].atom) + f.write(f"{nsites_qm} {self.system.molecule[0].molname}\n") - for atom in self.step.molecule[0].atom: + for atom in self.system.molecule[0].atom: f.write( fstr.format( atom.lbl, @@ -355,7 +353,7 @@ class DiceInterface(Interface): ) ) - for mol in self.step.molecule[1:]: + for mol in self.system.molecule[1:]: f.write(f"{len(mol.atom)} {mol.molname}\n") for atom in mol.atom: f.write( @@ -378,12 +376,12 @@ class DiceInterface(Interface): [ "bash", "-c", - f"exec -a dice-step{cycle}-p{proc} {self.config.progname} < {infile.name} > {outfile.name}", + f"exec -a dice-step{cycle}-p{proc} {self.step.dice.progname} < {infile.name} > {outfile.name}", ] ) else: exit_status = subprocess.call( - self.config.progname, stdin=infile, stdout=outfile + self.step.dice.progname, stdin=infile, stdout=outfile ) if exit_status != 0: diff --git a/diceplayer/shared/interface/gaussian_interface.py b/diceplayer/shared/interface/gaussian_interface.py index 113878a..ee9dc00 100644 --- a/diceplayer/shared/interface/gaussian_interface.py +++ b/diceplayer/shared/interface/gaussian_interface.py @@ -1,22 +1,380 @@ -from diceplayer.shared.config.gaussian_dto import GaussianDTO +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 __init__(self, data: dict): - self.config: GaussianDTO = self.set_config(data) + def start(self, cycle: int) -> Dict[str, NDArray]: + self._make_qm_dir(cycle) - @staticmethod - def set_config(data: dict) -> GaussianDTO: - return GaussianDTO.from_dict(data) + if cycle > 1: + self._copy_chk_file_from_previous_step(cycle) - def configure(self): - pass + asec_charges = self.populate_asec_vdw(cycle) + self._make_gaussian_input_file( + cycle, + asec_charges + ) - def start(self, cycle: int): - pass + 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): - pass + 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 diff --git a/tests/mocks/mock_inputs.py b/tests/mocks/mock_inputs.py new file mode 100644 index 0000000..406a35a --- /dev/null +++ b/tests/mocks/mock_inputs.py @@ -0,0 +1,111 @@ +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(), + } + mock_file = mock.mock_open(read_data=values[file]) + return mock_file() \ No newline at end of file diff --git a/tests/shared/config/test_dice_dto.py b/tests/shared/config/test_dice_dto.py index 553e1f1..9117e46 100644 --- a/tests/shared/config/test_dice_dto.py +++ b/tests/shared/config/test_dice_dto.py @@ -1,11 +1,11 @@ -from diceplayer.shared.config.dice_dto import DiceDTO +from diceplayer.shared.config.dice_config import DiceConfig import unittest class TestDiceDto(unittest.TestCase): def test_class_instantiation(self): - dice_dto = DiceDTO( + dice_dto = DiceConfig( ljname='test', outname='test', dens=1.0, @@ -13,11 +13,11 @@ class TestDiceDto(unittest.TestCase): nstep=[1, 1], ) - self.assertIsInstance(dice_dto, DiceDTO) + self.assertIsInstance(dice_dto, DiceConfig) def test_validate_jname(self): with self.assertRaises(ValueError) as ex: - DiceDTO( + DiceConfig( ljname=None, outname='test', dens=1.0, @@ -28,7 +28,7 @@ class TestDiceDto(unittest.TestCase): def test_validate_outname(self): with self.assertRaises(ValueError) as ex: - DiceDTO( + DiceConfig( ljname='test', outname=None, dens=1.0, @@ -39,7 +39,7 @@ class TestDiceDto(unittest.TestCase): def test_validate_dens(self): with self.assertRaises(ValueError) as ex: - DiceDTO( + DiceConfig( ljname='test', outname='test', dens=None, @@ -50,7 +50,7 @@ class TestDiceDto(unittest.TestCase): def test_validate_nmol(self): with self.assertRaises(ValueError) as ex: - DiceDTO( + DiceConfig( ljname='test', outname='test', dens=1.0, @@ -61,7 +61,7 @@ class TestDiceDto(unittest.TestCase): def test_validate_nstep(self): with self.assertRaises(ValueError) as ex: - DiceDTO( + DiceConfig( ljname='test', outname='test', dens=1.0, @@ -71,7 +71,7 @@ class TestDiceDto(unittest.TestCase): self.assertEqual(ex.exception, "Error: 'nstep' keyword not defined appropriately in config file") def test_from_dict(self): - dice_dto = DiceDTO.from_dict({ + dice_dto = DiceConfig.from_dict({ 'ljname': 'test', 'outname': 'test', 'dens': 1.0, @@ -79,4 +79,4 @@ class TestDiceDto(unittest.TestCase): 'nstep': [1, 1], }) - self.assertIsInstance(dice_dto, DiceDTO) \ No newline at end of file + self.assertIsInstance(dice_dto, DiceConfig) \ No newline at end of file diff --git a/tests/shared/config/test_gaussian_dto.py b/tests/shared/config/test_gaussian_dto.py index 67d013f..e719786 100644 --- a/tests/shared/config/test_gaussian_dto.py +++ b/tests/shared/config/test_gaussian_dto.py @@ -1,4 +1,4 @@ -from diceplayer.shared.config.gaussian_dto import GaussianDTO +from diceplayer.shared.config.gaussian_config import GaussianDTO import unittest diff --git a/tests/shared/config/test_player_dto.py b/tests/shared/config/test_player_dto.py index 98440af..909e5fd 100644 --- a/tests/shared/config/test_player_dto.py +++ b/tests/shared/config/test_player_dto.py @@ -1,28 +1,84 @@ -from diceplayer.shared.config.player_dto import PlayerDTO +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 -class TestPlayerDTO(unittest.TestCase): - def test_class_instantiation(self): - player_dto = PlayerDTO(opt=True, maxcyc=100, nprocs=4, ncores=4) +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', + } + } - self.assertIsInstance(player_dto, PlayerDTO) + +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 = PlayerDTO(opt=True, maxcyc=100, nprocs=4, ncores=4, altsteps=100) + 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 = PlayerDTO.from_dict({ - 'opt': True, - 'maxcyc': 100, - 'nprocs': 4, - 'ncores': 4, - }) + player_dto = PlayerConfig.from_dict( + get_config_dict() + ) - self.assertIsInstance(player_dto, PlayerDTO) + self.assertIsInstance(player_dto, PlayerConfig) + self.assertIsInstance(player_dto.dice, DiceConfig) + self.assertIsInstance(player_dto.gaussian, GaussianDTO) if __name__ == '__main__': diff --git a/tests/shared/environment/test_system.py b/tests/shared/environment/test_system.py index cff560e..8fad46c 100644 --- a/tests/shared/environment/test_system.py +++ b/tests/shared/environment/test_system.py @@ -13,18 +13,14 @@ class TestSystem(unittest.TestCase): def test_add_type(self): system = System() - system.add_type(0, Molecule('test')) + system.add_type(Molecule('test')) self.assertIsInstance(system.molecule, list) - self.assertIsInstance(system.nmols, list) with self.assertRaises(TypeError) as ex: - system.add_type(0, 'test') + system.add_type('test') self.assertEqual(ex.exception, 'Error: molecule is not a Molecule instance') - with self.assertRaises(TypeError) as ex: - system.add_type('test', Molecule('test')) - self.assertEqual(ex.exception, 'Error: nmols is not an integer') if __name__ == '__main__': diff --git a/tests/shared/interface/test_dice_interface.py b/tests/shared/interface/test_dice_interface.py index 6d39c4f..4a0ec90 100644 --- a/tests/shared/interface/test_dice_interface.py +++ b/tests/shared/interface/test_dice_interface.py @@ -1,11 +1,14 @@ 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.shared.config.step_dto import StepDTO 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 @@ -16,122 +19,58 @@ 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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() self.assertIsInstance(dice, DiceInterface) def test_configure(self): - dice = DiceInterface( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() self.assertIsNone(dice.step) + self.assertIsNone(dice.system) - dice.configure('test') + # 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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() - dice.configure('test') + 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( - { - '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=[], - ) - ) + 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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) - dice.configure( - StepDTO( - ncores=1, - nprocs=2, - simulation_dir='test', - altsteps=1, - molecule=[], - nmol=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) with self.assertRaises(SystemExit): dice.start(1) def test_simulation_process_raises_exception(self): - dice = DiceInterface( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() with self.assertRaises(SystemExit): dice._simulation_process(1, 1) @@ -140,16 +79,7 @@ class TestDiceInterface(unittest.TestCase): @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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() dice._simulation_process(1, 1) @@ -160,26 +90,8 @@ class TestDiceInterface(unittest.TestCase): @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( - { - '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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) mock_path_exists.return_value = False @@ -190,26 +102,8 @@ class TestDiceInterface(unittest.TestCase): @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( - { - '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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) mock_path_exists.return_value = False @@ -218,42 +112,15 @@ class TestDiceInterface(unittest.TestCase): 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() + 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( - { - '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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) + + dice.step.dice.nstep = [1, 1] dice._make_potentials = mock.Mock() @@ -281,26 +148,10 @@ class TestDiceInterface(unittest.TestCase): @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( - { - '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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) + + dice.step.dice.nstep = [1, 1] dice._make_potentials = mock.Mock() @@ -327,26 +178,10 @@ class TestDiceInterface(unittest.TestCase): @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( - { - '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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) + + dice.step.dice.nstep = [1, 1] dice._make_potentials = mock.Mock() @@ -362,26 +197,8 @@ class TestDiceInterface(unittest.TestCase): dice._make_dice_inputs(2, 1) def test_make_dice_inputs_nstep_len_three_with_randoninit_first_cycle_one(self): - dice = DiceInterface( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1, 1], - } - ) - dice.configure( - StepDTO( - ncores=1, - nprocs=1, - simulation_dir='test', - altsteps=1, - molecule=[], - nmol=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice._make_potentials = mock.Mock() @@ -410,26 +227,10 @@ class TestDiceInterface(unittest.TestCase): @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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1, 1], - } - ) - dice.configure( - StepDTO( - ncores=1, - nprocs=1, - simulation_dir='test', - altsteps=1, - molecule=[], - nmol=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) + + dice.step.dice.nstep = [1, 1, 1] dice.run_dice_file = mock.Mock() @@ -441,26 +242,10 @@ class TestDiceInterface(unittest.TestCase): self.assertEqual(dice.run_dice_file.call_count, 3) self.assertTrue(mock_shutils.copy.called) - 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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) + + dice.step.dice.nstep = [1, 1] dice.run_dice_file = mock.Mock() @@ -476,26 +261,8 @@ class TestDiceInterface(unittest.TestCase): @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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1, 1], - } - ) - dice.configure( - StepDTO( - ncores=1, - nprocs=1, - simulation_dir='test', - altsteps=1, - molecule=[], - nmol=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice.run_dice_file = mock.Mock() @@ -507,26 +274,8 @@ class TestDiceInterface(unittest.TestCase): self.assertEqual(dice.run_dice_file.call_count, 2) self.assertTrue(mock_shutils.copy.called) - 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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice.run_dice_file = mock.Mock() @@ -542,26 +291,8 @@ class TestDiceInterface(unittest.TestCase): @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( - { - '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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice.run_dice_file = mock.Mock() @@ -587,32 +318,14 @@ class TestDiceInterface(unittest.TestCase): secondary_molecule = Molecule('secondary_molecule') secondary_molecule.add_atom(example_atom) - 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=[ - main_molecule, - secondary_molecule, - ], - nmol=[ - len(main_molecule.atom), - len(secondary_molecule.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([ @@ -657,32 +370,12 @@ class TestDiceInterface(unittest.TestCase): secondary_molecule = Molecule('secondary_molecule') secondary_molecule.add_atom(example_atom) - 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=[ - main_molecule, - secondary_molecule, - ], - nmol=[ - len(main_molecule.atom), - len(secondary_molecule.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([ @@ -695,33 +388,15 @@ class TestDiceInterface(unittest.TestCase): density = dice._new_density(last_xyz_file) - self.assertEqual(density, 3.3472359000000003) + 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( - { - '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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice._make_nvt_ter(1, 'test') @@ -730,7 +405,7 @@ class TestDiceInterface(unittest.TestCase): lines = list(map(lambda x: x[0][0], calls)) - expected_lines = ['title = Diceplayer run - NVT Thermalization\n', 'ncores = 1\n', 'ljname = test\n', 'outname = test\n', 'nmol = 1\n', 'dens = 1.0\n', 'temp = 300.0\n', 'init = yes\n', 'nstep = 1\n', 'vstep = 0\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 0\n', 'irdf = 0\n', 'seed = 1000000\n', 'upbuf = 360'] + 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) @@ -739,26 +414,8 @@ class TestDiceInterface(unittest.TestCase): def test_make_nvt_eq(self, mock_random, mock_open): mock_random.random.return_value = 1 - 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=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice._make_nvt_eq('test') @@ -767,7 +424,7 @@ class TestDiceInterface(unittest.TestCase): lines = list(map(lambda x: x[0][0], calls)) - expected_lines = ['title = Diceplayer run - NVT Production\n', 'ncores = 1\n', 'ljname = test\n', 'outname = test\n', 'nmol = 1\n', 'dens = 1.0\n', 'temp = 300.0\n', 'init = no\n', 'nstep = 1\n', 'vstep = 0\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 1000\n', 'irdf = 10\n', 'seed = 1000000\n'] + 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) @@ -776,26 +433,8 @@ class TestDiceInterface(unittest.TestCase): def test_make_npt_ter(self, mock_random, mock_open): mock_random.random.return_value = 1 - dice = DiceInterface( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1, 1], - } - ) - dice.configure( - StepDTO( - ncores=1, - nprocs=1, - simulation_dir='test', - altsteps=1, - molecule=[], - nmol=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice._make_npt_ter(1, 'test') @@ -804,7 +443,7 @@ class TestDiceInterface(unittest.TestCase): lines = list(map(lambda x: x[0][0], calls)) - expected_lines = ['title = Diceplayer run - NPT Thermalization\n', 'ncores = 1\n', 'ljname = test\n', 'outname = test\n', 'nmol = 1\n', 'press = 1.0\n', 'temp = 300.0\n', 'init = no\n', 'vstep = 0\n', 'nstep = 5\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 0\n', 'irdf = 0\n', 'seed = 1000000\n'] + 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) @@ -813,26 +452,8 @@ class TestDiceInterface(unittest.TestCase): def test_make_npt_eq(self, mock_random, mock_open): mock_random.random.return_value = 1 - dice = DiceInterface( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1, 1], - } - ) - dice.configure( - StepDTO( - ncores=1, - nprocs=1, - simulation_dir='test', - altsteps=1, - molecule=[], - nmol=[], - ) - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice._make_npt_eq('test') @@ -841,7 +462,7 @@ class TestDiceInterface(unittest.TestCase): lines = list(map(lambda x: x[0][0], calls)) - expected_lines = ['title = Diceplayer run - NPT Production\n', 'ncores = 1\n', 'ljname = test\n', 'outname = test\n', 'nmol = 1\n', 'press = 1.0\n', 'temp = 300.0\n', 'nstep = 5\n', 'vstep = 0\n', 'init = no\n', 'mstop = 1\n', 'accum = no\n', 'iprint = 1\n', 'isave = 1000\n', 'irdf = 10\n', 'seed = 1000000\n'] + 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) @@ -864,32 +485,12 @@ class TestDiceInterface(unittest.TestCase): secondary_molecule = Molecule('secondary_molecule') secondary_molecule.add_atom(example_atom) - 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=[ - main_molecule, - secondary_molecule, - ], - nmol=[ - len(main_molecule.atom), - len(secondary_molecule.atom), - ], - ) - ) + system = System() + system.add_type(main_molecule) + system.add_type(secondary_molecule) + + dice = DiceInterface() + dice.configure(self.config, system) dice._make_potentials('test') @@ -906,16 +507,8 @@ class TestDiceInterface(unittest.TestCase): @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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() + dice.configure(self.config, System()) dice.run_dice_file(1, 1, 'test') @@ -926,16 +519,8 @@ class TestDiceInterface(unittest.TestCase): @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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() + dice.configure(self.config, System()) with self.assertRaises(RuntimeError): dice.run_dice_file(1, 1, 'test') @@ -944,16 +529,8 @@ class TestDiceInterface(unittest.TestCase): @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( - { - 'ljname': 'test', - 'outname': 'test', - 'ncores': 1, - 'dens': 1.0, - 'nmol': [1], - 'nstep': [1, 1], - } - ) + dice = DiceInterface() + dice.configure(self.config, System()) with self.assertRaises(RuntimeError): dice.run_dice_file(1, 1, 'test') diff --git a/tests/shared/interface/test_gaussian_interface.py b/tests/shared/interface/test_gaussian_interface.py new file mode 100644 index 0000000..b59cd93 --- /dev/null +++ b/tests/shared/interface/test_gaussian_interface.py @@ -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() diff --git a/tests/test_player.py b/tests/test_player.py index e421331..65e45d5 100644 --- a/tests/test_player.py +++ b/tests/test_player.py @@ -3,119 +3,12 @@ from diceplayer import logger import io +from tests.mocks.mock_inputs import mock_open + from unittest import mock import unittest -def get_config_example(): - return """ -diceplayer: - maxcyc: 3 - opt: no - 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(), - } - mock_file = mock.mock_open(read_data=values[file]) - return mock_file() - - class TestPlayer(unittest.TestCase): def setUp(self): logger.set_logger(stream=io.StringIO()) @@ -131,19 +24,13 @@ class TestPlayer(unittest.TestCase): def test_start(self): player = Player("control.test.yml") - player.print_keywords = mock.MagicMock() - player.create_simulation_dir = mock.MagicMock() - player.read_potentials = mock.MagicMock() - player.print_potentials = mock.MagicMock() + player.gaussian_start = mock.MagicMock() player.dice_start = mock.MagicMock() player.start(1) - self.assertTrue(player.print_keywords.called) - self.assertTrue(player.create_simulation_dir.called) - self.assertTrue(player.read_potentials.called) - self.assertTrue(player.print_potentials.called) 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") @@ -178,7 +65,22 @@ class TestPlayer(unittest.TestCase): with self.assertLogs() as cm: player.print_keywords() - expected_output = ['INFO:diceplayer:##########################################################################################\n############# Welcome to DICEPLAYER version 1.0 #############\n##########################################################################################\n\n', 'INFO:diceplayer:Your python version is TEST\n', 'INFO:diceplayer:\n', 'INFO:diceplayer:Program started on 00 Test 0000 at 00:00:00\n', 'INFO:diceplayer:\n', 'INFO:diceplayer:Environment variables:\n', 'INFO:diceplayer:OMP_STACKSIZE = Not set\n', 'INFO:diceplayer:\n==========================================================================================\n CONTROL variables being used in this run:\n------------------------------------------------------------------------------------------\n\n', 'INFO:diceplayer:\n', 'INFO:diceplayer:------------------------------------------------------------------------------------------\n DICE variables being used in this run:\n------------------------------------------------------------------------------------------\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', 'INFO:diceplayer:------------------------------------------------------------------------------------------\n GAUSSIAN variables being used in this run:\n------------------------------------------------------------------------------------------\n\n', 'INFO:diceplayer:keywords = freq', 'INFO:diceplayer:level = MP2/aug-cc-pVDZ', 'INFO:diceplayer:pop = chelpg', 'INFO:diceplayer:qmprog = g16', 'INFO:diceplayer:\n'] + expected_output = [ + 'INFO:diceplayer:##########################################################################################\n############# Welcome to DICEPLAYER version 1.0 #############\n##########################################################################################\n\n', + 'INFO:diceplayer:Your python version is TEST\n', 'INFO:diceplayer:\n', + 'INFO:diceplayer:Program started on 00 Test 0000 at 00:00:00\n', 'INFO:diceplayer:\n', + 'INFO:diceplayer:Environment variables:\n', 'INFO:diceplayer:OMP_STACKSIZE = Not set\n', + 'INFO:diceplayer:\n==========================================================================================\n CONTROL variables being used in this run:\n------------------------------------------------------------------------------------------\n\n', + 'INFO:diceplayer:\n', + 'INFO:diceplayer:------------------------------------------------------------------------------------------\n DICE variables being used in this run:\n------------------------------------------------------------------------------------------\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', + 'INFO:diceplayer:------------------------------------------------------------------------------------------\n GAUSSIAN variables being used in this run:\n------------------------------------------------------------------------------------------\n\n', + '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) @@ -333,7 +235,7 @@ class TestPlayer(unittest.TestCase): # Testing combrule error with self.assertRaises(SystemExit) as context: - player.dice.config.ljname = "phb.error.combrule.ljc" + player.config.dice.ljname = "phb.error.combrule.ljc" player.read_potentials() self.assertEqual( @@ -343,7 +245,7 @@ class TestPlayer(unittest.TestCase): # Testing ntypes error with self.assertRaises(SystemExit) as context: - player.dice.config.ljname = "phb.error.ntypes.ljc" + player.config.dice.ljname = "phb.error.ntypes.ljc" player.read_potentials() self.assertEqual( @@ -353,7 +255,7 @@ class TestPlayer(unittest.TestCase): # Testing ntypes error on config with self.assertRaises(SystemExit) as context: - player.dice.config.ljname = "phb.error.ntypes.config.ljc" + player.config.dice.ljname = "phb.error.ntypes.config.ljc" player.read_potentials() self.assertEqual( @@ -364,7 +266,7 @@ class TestPlayer(unittest.TestCase): # Testing nsite error with self.assertRaises(ValueError) as context: - player.dice.config.ljname = "phb.error.nsites.ljc" + player.config.dice.ljname = "phb.error.nsites.ljc" player.read_potentials() self.assertEqual( @@ -374,7 +276,7 @@ class TestPlayer(unittest.TestCase): # Testing molname error with self.assertRaises(ValueError) as context: - player.dice.config.ljname = "phb.error.molname.ljc" + player.config.dice.ljname = "phb.error.molname.ljc" player.read_potentials() self.assertEqual( @@ -391,7 +293,23 @@ class TestPlayer(unittest.TestCase): with self.assertLogs(level='INFO') as context: player.print_potentials() - expected_output = ['INFO:diceplayer:==========================================================================================\n', 'INFO:diceplayer: Potential parameters from file phb.ljc:', 'INFO:diceplayer:------------------------------------------------------------------------------------------\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', 'INFO:diceplayer:=========================================================================================='] + expected_output = [ + 'INFO:diceplayer:==========================================================================================\n', + 'INFO:diceplayer: Potential parameters from file phb.ljc:', + 'INFO:diceplayer:------------------------------------------------------------------------------------------\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', + 'INFO:diceplayer:=========================================================================================='] self.assertEqual( context.output, @@ -401,23 +319,22 @@ class TestPlayer(unittest.TestCase): @mock.patch("builtins.open", mock_open) def test_dice_start(self): player = Player("control.test.yml") - player.dice = mock.MagicMock() - player.dice.start = mock.MagicMock() + player.dice_interface = mock.MagicMock() + player.dice_interface.start = mock.MagicMock() player.dice_start(1) - player.dice.start.assert_called_once() - - @mock.patch("builtins.open", mock_open) - def test_gaussian_start(self): - player = Player("control.test.yml") - player.gaussian = mock.MagicMock() - player.gaussian.start = mock.MagicMock() - - player.gaussian_start(1) - - player.gaussian.start.assert_called_once() + player.dice_interface.start.assert_called_once() + # @mock.patch("builtins.open", mock_open) + # def test_gaussian_start(self): + # player = Player("control.test.yml") + # player.gaussian_interface = mock.MagicMock() + # player.gaussian_interface.start = mock.MagicMock() + # + # player.gaussian_start(1) + # + # player.gaussian_interface.start.assert_called_once() if __name__ == '__main__':