Implements Additional Logs and class Player Tests

This commit is contained in:
2023-05-03 03:14:26 -03:00
parent 56994dba27
commit b440a0f05d
8 changed files with 586 additions and 80 deletions

View File

@@ -9,7 +9,7 @@ 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.shared.utils.ptable import atommass
from diceplayer import logger
from dataclasses import fields
from pathlib import Path
@@ -24,13 +24,6 @@ ENV = ["OMP_STACKSIZE"]
class Player:
__slots__ = [
'config',
'system',
'dice',
'gaussian',
]
def __init__(self, infile: str):
config_data = self.read_keywords(infile)
@@ -43,16 +36,16 @@ class Player:
self.gaussian = GaussianInterface(config_data.get("gaussian"))
self.dice = DiceInterface(config_data.get("dice"))
def start(self):
def start(self, initial_cycle: int = 1):
self.print_keywords()
self.create_simulation_dir()
self.read_potentials()
# self.print_potentials()
self.print_potentials()
self.dice_start(1)
self.dice_start(2)
for cycle in range(initial_cycle, self.config.maxcyc + 1):
self.dice_start(cycle)
def create_simulation_dir(self):
simulation_dir_path = Path(self.config.simulation_dir)
@@ -61,12 +54,7 @@ class Player:
f"Error: a file or a directory {self.config.simulation_dir} already exists,"
f" move or delete the simfiles directory to continue."
)
try:
simulation_dir_path.mkdir()
except FileExistsError:
OSError(
f"Error: cannot make directory {self.config.simulation_dir}"
)
simulation_dir_path.mkdir()
def print_keywords(self) -> None:
@@ -75,38 +63,38 @@ class Player:
if getattr(config, key) is not None:
if isinstance(getattr(config, key), list):
string = " ".join(str(x) for x in getattr(config, key))
logging.info(f"{key} = [ {string} ]")
logger.info(f"{key} = [ {string} ]")
else:
logging.info(f"{key} = {getattr(config, key)}")
logger.info(f"{key} = {getattr(config, key)}")
logging.info(
logger.info(
"##########################################################################################\n"
"############# Welcome to DICEPLAYER version 1.0 #############\n"
"##########################################################################################\n"
"\n"
)
logging.info("Your python version is {}\n".format(sys.version))
logging.info("\n")
logging.info("Program started on {}\n".format(weekday_date_time()))
logging.info("\n")
logging.info("Environment variables:\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")
for var in ENV:
logging.info(
logger.info(
"{} = {}\n".format(
var, (os.environ[var] if var in os.environ else "Not set")
)
)
logging.info(
logger.info(
"\n==========================================================================================\n"
" CONTROL variables being used in this run:\n"
"------------------------------------------------------------------------------------------\n"
"\n"
)
logging.info("\n")
logger.info("\n")
logging.info(
logger.info(
"------------------------------------------------------------------------------------------\n"
" DICE variables being used in this run:\n"
"------------------------------------------------------------------------------------------\n"
@@ -115,9 +103,9 @@ class Player:
log_keywords(self.dice.config, DiceDTO)
logging.info("\n")
logger.info("\n")
logging.info(
logger.info(
"------------------------------------------------------------------------------------------\n"
" GAUSSIAN variables being used in this run:\n"
"------------------------------------------------------------------------------------------\n"
@@ -126,18 +114,19 @@ class Player:
log_keywords(self.gaussian.config, GaussianDTO)
logging.info("\n")
logger.info("\n")
def read_potentials(self):
try:
ljname_path = Path(self.dice.config.ljname)
if ljname_path.exists():
with open(self.dice.config.ljname) as file:
ljdata = file.readlines()
except FileNotFoundError:
ljc_data = file.readlines()
else:
raise RuntimeError(
f"Potential file {self.dice.config.ljname} not found."
)
combrule = ljdata.pop(0).split()[0]
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(
@@ -146,7 +135,7 @@ class Player:
)
self.dice.config.combrule = combrule
ntypes = ljdata.pop(0).split()[0]
ntypes = ljc_data.pop(0).split()[0]
if not ntypes.isdigit():
sys.exit(
"Error: expected an integer in the 2nd line of file {}".format(
@@ -157,22 +146,22 @@ class Player:
if ntypes != len(self.dice.config.nmol):
sys.exit(
f"Error: number of molecule types in file {self.dice.config.ljname}"
f"Error: number of molecule types in file {self.dice.config.ljname} "
f"must match that of 'nmol' keyword in config file"
)
for i in range(ntypes):
nsites, molname = ljdata.pop(0).split()[:2]
try:
nsites, molname = ljc_data.pop(0).split()[:2]
except ValueError:
raise ValueError(
f"Error: expected nsites and molname for the molecule type {i+1}"
)
if not nsites.isdigit():
raise ValueError(
f"Error: expected nsites to be an integer for molecule type {i}"
)
if molname is None:
raise ValueError(
f"Error: expected molecule name for molecule type {i}"
f"Error: expected nsites to be an integer for molecule type {i+1}"
)
nsites = int(nsites)
@@ -182,12 +171,68 @@ class Player:
for j in range(nsites):
new_atom = dict(zip(
atom_fields,
ljdata.pop(0).split()
ljc_data.pop(0).split()
))
self.system.molecule[i].add_atom(
Atom(**self.validate_atom_dict(i, j, new_atom))
)
def print_potentials(self) -> None:
formatstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f} {:>9.4f}"
logger.info(
"==========================================================================================\n"
)
logger.info(
f" Potential parameters from file {self.dice.config.ljname}:"
)
logger.info(
"------------------------------------------------------------------------------------------"
"\n"
)
logger.info(f"Combination rule: {self.dice.config.combrule}")
logger.info(
f"Types of molecules: {len(self.system.molecule)}\n"
)
i = 0
for mol in self.system.molecule:
i += 1
logger.info(
"{} atoms in molecule type {}:".format(len(mol.atom), i)
)
logger.info(
"---------------------------------------------------------------------------------"
)
logger.info(
"Lbl AN X Y Z Charge Epsilon Sigma Mass"
)
logger.info(
"---------------------------------------------------------------------------------"
)
for atom in mol.atom:
logger.info(
formatstr.format(
atom.lbl,
atom.na,
atom.rx,
atom.ry,
atom.rz,
atom.chg,
atom.eps,
atom.sig,
atom.mass,
)
)
logger.info("\n")
logger.info(
"=========================================================================================="
)
def dice_start(self, cycle: int):
self.dice.configure(
StepDTO(
@@ -204,8 +249,8 @@ class Player:
self.dice.reset()
def gaussian_start(self):
self.gaussian.start()
def gaussian_start(self, cycle: int):
self.gaussian.start(cycle)
@staticmethod
def validate_atom_dict(molecule_type, molecule_site, atom_dict: dict) -> dict:
@@ -219,63 +264,63 @@ class Player:
try:
atom_dict['lbl'] = int(atom_dict['lbl'])
except ValueError:
except Exception:
raise ValueError(
f'Invalid lbl fields for site {molecule_site} for molecule type {molecule_type}.'
)
try:
atom_dict['na'] = int(atom_dict['na'])
except ValueError:
except Exception:
raise ValueError(
f'Invalid na fields for site {molecule_site} for molecule type {molecule_type}.'
)
try:
atom_dict['rx'] = float(atom_dict['rx'])
except ValueError:
except Exception:
raise ValueError(
f'Invalid rx fields for site {molecule_site} for molecule type {molecule_type}.'
f'Invalid rx fields for site {molecule_site} for molecule type {molecule_type}. '
f'Value must be a float.'
)
try:
atom_dict['ry'] = float(atom_dict['ry'])
except ValueError:
except Exception:
raise ValueError(
f'Invalid ry fields for site {molecule_site} for molecule type {molecule_type}.'
f'Invalid ry fields for site {molecule_site} for molecule type {molecule_type}. '
f'Value must be a float.'
)
try:
atom_dict['rz'] = float(atom_dict['rx'])
except ValueError:
atom_dict['rz'] = float(atom_dict['rz'])
except Exception:
raise ValueError(
f'Invalid rz fields for site {molecule_site} for molecule type {molecule_type}.'
f'Invalid rz fields for site {molecule_site} for molecule type {molecule_type}. '
f'Value must be a float.'
)
try:
atom_dict['chg'] = float(atom_dict['chg'])
except ValueError:
except Exception:
raise ValueError(
f'Invalid chg fields for site {molecule_site} for molecule type {molecule_type}.'
f'Invalid chg fields for site {molecule_site} for molecule type {molecule_type}. '
f'Value must be a float.'
)
try:
atom_dict['eps'] = float(atom_dict['eps'])
except ValueError:
except Exception:
raise ValueError(
f'Invalid eps fields for site {molecule_site} for molecule type {molecule_type}.'
f'Invalid eps fields for site {molecule_site} for molecule type {molecule_type}. '
f'Value must be a float.'
)
try:
atom_dict['sig'] = float(atom_dict['sig'])
except ValueError:
except Exception:
raise ValueError(
f'Invalid sig fields for site {molecule_site} for molecule type {molecule_type}.'
f'Invalid sig fields for site {molecule_site} for molecule type {molecule_type}. '
f'Value must be a float.'
)

View File

@@ -3,6 +3,7 @@ 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 diceplayer import logger
from multiprocessing import Process, connection
from setproctitle import setproctitle
@@ -15,7 +16,6 @@ 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
@@ -41,8 +41,9 @@ class DiceInterface(Interface):
procs = []
sentinels = []
for proc in range(1, self.step.nprocs + 1):
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()
@@ -61,6 +62,8 @@ class DiceInterface(Interface):
p.terminate()
sys.exit(status)
logger.info("\n")
def reset(self):
del self.step
@@ -132,6 +135,11 @@ class DiceInterface(Interface):
f"step{cycle:02d}",
f"p{proc:02d}"
)
logger.info(
f"Simulation process {str(proc_dir)} initiated with pid {os.getpid()}"
)
os.chdir(proc_dir)
if not (self.config.randominit == 'first' and cycle > 1):
@@ -385,3 +393,5 @@ class DiceInterface(Interface):
flag = outfile.readlines()[DICE_FLAG_LINE].strip()
if flag != DICE_END_FLAG:
raise RuntimeError(f"Dice process step{cycle:02d}-p{proc:02d} did not exit properly")
logger.info(f"Dice {file_name} - step{cycle:02d}-p{proc:02d} exited properly")

View File

@@ -14,7 +14,7 @@ class GaussianInterface(Interface):
def configure(self):
pass
def start(self):
def start(self, cycle: int):
pass
def reset(self):

View File

@@ -24,15 +24,17 @@ class Logger:
if self._logger is None:
self._logger = logging.getLogger(logger_name)
def set_logger(self, outfile='run.log', level=logging.INFO):
self.outfile = Path(outfile)
if self.outfile.exists():
self.outfile.rename(str(self.outfile) + ".backup")
def set_logger(self, outfile='run.log', level=logging.INFO, stream=None):
outfile_path = None
if outfile is not None and stream is None:
outfile_path = Path(outfile)
if outfile_path.exists():
outfile_path.rename(str(outfile_path) + ".backup")
if level is not None:
self._logger.setLevel(level)
self._create_handlers()
self._create_handlers(outfile_path, stream)
self._was_set = True
@@ -52,10 +54,12 @@ class Logger:
def error(self, message):
self._logger.error(message)
def _create_handlers(self):
def _create_handlers(self, outfile_path: Path, stream):
handlers = []
if self.outfile is not None:
handlers.append(logging.FileHandler(self.outfile, mode='a+'))
if outfile_path is not None:
handlers.append(logging.FileHandler(outfile_path, mode='a+'))
elif stream is not None:
handlers.append(logging.StreamHandler(stream))
else:
handlers.append(logging.StreamHandler())