Compare commits
26 Commits
develop
...
0470200d00
| Author | SHA1 | Date | |
|---|---|---|---|
|
0470200d00
|
|||
|
0763c4a9e1
|
|||
|
30be88e6b4
|
|||
|
6a154429e9
|
|||
|
4c8cbc821d
|
|||
|
9f22304dd8
|
|||
|
53eb34a83e
|
|||
|
06ae9b41f0
|
|||
|
11ff4c0c21
|
|||
|
c59f0d6516
|
|||
| 53f75b44a5 | |||
|
34e8758ff8
|
|||
|
2765e5b86c
|
|||
|
636c65c07c
|
|||
|
a5504b0435
|
|||
|
d400970e8f
|
|||
| b6e57bc1c5 | |||
|
cb4b21ab6c
|
|||
| 250dd4acf4 | |||
|
5d76e49f89
|
|||
| d89124b1e8 | |||
|
f0e5bbeaee
|
|||
|
c51d07cff2
|
|||
|
e5c6282c86
|
|||
| f56d6b6b36 | |||
|
0699791091
|
47
.github/workflows/build-and-uplod.yml
vendored
47
.github/workflows/build-and-uplod.yml
vendored
@@ -1,47 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Build and Upload to PyPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
pypi-upload:
|
||||
needs: [test]
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install poetry
|
||||
run: pipx install poetry
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install dependencies
|
||||
run: poetry install
|
||||
|
||||
- name: Build Python Package
|
||||
run: |
|
||||
poetry build -f sdist
|
||||
poetry install
|
||||
echo "Builded DicePlayer - $(poetry version)"
|
||||
|
||||
- name: Configure PyPI
|
||||
run: |
|
||||
poetry config repositories.pypi https://upload.pypi.org/legacy/
|
||||
poetry config pypi-token.pypi ${{secrets.PYPI_TOKEN}}
|
||||
|
||||
- name: Upload Python Package
|
||||
run: |
|
||||
poetry publish --repository pypi
|
||||
77
.github/workflows/python-test-and-build.yml
vendored
Normal file
77
.github/workflows/python-test-and-build.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: build and upload
|
||||
|
||||
on:
|
||||
push
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version:
|
||||
- "3.10"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
# Install a specific version of uv.
|
||||
version: "0.9.15"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --all-extras --dev
|
||||
|
||||
- name: Run unittest
|
||||
run: |
|
||||
uv run python -m unittest
|
||||
|
||||
pypi-upload:
|
||||
needs: [test]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
with:
|
||||
# Install a specific version of uv.
|
||||
version: "0.9.15"
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
- name: Build source distribution
|
||||
run: uv build --sdist
|
||||
|
||||
- name: Upload Python Package
|
||||
run: |
|
||||
uv publish -t ${{ secrets.PYPI_TOKEN }}
|
||||
36
.github/workflows/run-tests.yml
vendored
36
.github/workflows/run-tests.yml
vendored
@@ -1,36 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
||||
|
||||
name: Run Tests
|
||||
|
||||
on:
|
||||
push
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python: ["3.10", "3.11", "3.12", "3.13"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install poetry
|
||||
run: pipx install poetry
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
poetry self add "poetry-dynamic-versioning[plugin]"
|
||||
if [ -f pyproject.toml ]; then poetry install; fi
|
||||
- name: Run unittest
|
||||
run: |
|
||||
poetry run python -m unittest
|
||||
184
.gitignore
vendored
184
.gitignore
vendored
@@ -1,20 +1,184 @@
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.vscode/settings.json
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode
|
||||
|
||||
*.ljc
|
||||
*.log
|
||||
*.log.backup
|
||||
|
||||
simfiles/*
|
||||
|
||||
.vscode/*
|
||||
.idea/*
|
||||
*.pkl
|
||||
|
||||
*.xyz
|
||||
|
||||
dist/
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 24.4.2
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.14.7
|
||||
hooks:
|
||||
- id: black
|
||||
args: [--config=pyproject.toml]
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
files: "\\.(py)$"
|
||||
args: [--settings-path=pyproject.toml]
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -1,23 +1,25 @@
|
||||
diceplayer:
|
||||
opt: no
|
||||
type: both
|
||||
switch_cyc: 3
|
||||
max_cyc: 5
|
||||
mem: 24
|
||||
maxcyc: 5
|
||||
ncores: 5
|
||||
nprocs: 4
|
||||
ncores: 20
|
||||
qmprog: 'g16'
|
||||
lps: no
|
||||
ghosts: no
|
||||
altsteps: 2000
|
||||
|
||||
dice:
|
||||
nmol: [1, 100]
|
||||
nprocs: 1
|
||||
nmol: [1, 200]
|
||||
dens: 1.5
|
||||
nstep: [2000, 3000]
|
||||
isave: 1000
|
||||
nstep: [200, 300]
|
||||
isave: 100
|
||||
outname: 'phb'
|
||||
progname: '~/.local/bin/dice'
|
||||
ljname: 'phb.ljc'
|
||||
progname: 'dice'
|
||||
ljname: 'phb.ljc.example'
|
||||
randominit: 'always'
|
||||
seed: 12345
|
||||
|
||||
gaussian:
|
||||
qmprog: 'g16'
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
from diceplayer.shared.utils.logger import Logger
|
||||
|
||||
from importlib import metadata
|
||||
|
||||
VERSION = metadata.version("diceplayer")
|
||||
|
||||
logger = Logger(__name__)
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
from diceplayer import VERSION, logger
|
||||
from diceplayer.cli import ArgsModel, read_input
|
||||
from diceplayer.config import PlayerConfig
|
||||
from diceplayer.logger import logger
|
||||
from diceplayer.player import Player
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from importlib import metadata
|
||||
|
||||
|
||||
VERSION = metadata.version("diceplayer")
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Read and store the arguments passed to the program
|
||||
and set the usage and help messages
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(prog="Diceplayer")
|
||||
parser.add_argument(
|
||||
"-c", "--continue", dest="opt_continue", default=False, action="store_true"
|
||||
"-v", "--version", action="version", version="diceplayer-" + VERSION
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", "--version", action="version", version="diceplayer-" + VERSION
|
||||
"-c", "--continue", dest="continuation", default=False, action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
@@ -34,36 +34,26 @@ def main():
|
||||
metavar="OUTFILE",
|
||||
help="output file of diceplayer [default = run.log]",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--force",
|
||||
dest="force",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="force overwrite existing state file if it exists [default = False]",
|
||||
)
|
||||
args = ArgsModel.from_args(parser.parse_args())
|
||||
|
||||
# Open OUTFILE for writing and print keywords and initial info
|
||||
logger.set_logger(args.outfile, logging.INFO)
|
||||
logger.set_output_file(args.outfile)
|
||||
|
||||
if args.opt_continue:
|
||||
player = Player.from_save()
|
||||
else:
|
||||
player = Player.from_file(args.infile)
|
||||
config: PlayerConfig
|
||||
try:
|
||||
config = read_input(args.infile)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to read input file: {e}")
|
||||
return
|
||||
|
||||
player.read_potentials()
|
||||
|
||||
player.create_simulation_dir()
|
||||
player.create_geoms_file()
|
||||
|
||||
player.print_keywords()
|
||||
|
||||
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")
|
||||
Player(config).play(continuation=args.continuation, force=args.force)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
5
diceplayer/cli/__init__.py
Normal file
5
diceplayer/cli/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .args_model import ArgsModel
|
||||
from .read_input_file import read_input
|
||||
|
||||
|
||||
__all__ = ["ArgsModel", "read_input"]
|
||||
12
diceplayer/cli/args_model.py
Normal file
12
diceplayer/cli/args_model.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ArgsModel(BaseModel):
|
||||
outfile: str
|
||||
infile: str
|
||||
continuation: bool
|
||||
force: bool
|
||||
|
||||
@classmethod
|
||||
def from_args(cls, args):
|
||||
return cls(**vars(args))
|
||||
9
diceplayer/cli/read_input_file.py
Normal file
9
diceplayer/cli/read_input_file.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from diceplayer.config import PlayerConfig
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def read_input(infile) -> PlayerConfig:
|
||||
with open(infile, "r") as f:
|
||||
values = yaml.safe_load(f)
|
||||
return PlayerConfig.model_validate(values["diceplayer"])
|
||||
10
diceplayer/config/__init__.py
Normal file
10
diceplayer/config/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from .dice_config import DiceConfig
|
||||
from .gaussian_config import GaussianConfig
|
||||
from .player_config import PlayerConfig
|
||||
|
||||
|
||||
__all__ = [
|
||||
"DiceConfig",
|
||||
"GaussianConfig",
|
||||
"PlayerConfig",
|
||||
]
|
||||
60
diceplayer/config/dice_config.py
Normal file
60
diceplayer/config/dice_config.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from typing_extensions import Literal
|
||||
|
||||
import random
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class DiceConfig(BaseModel):
|
||||
"""
|
||||
Data Transfer Object for the Dice configuration.
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
frozen=True,
|
||||
)
|
||||
nprocs: int = Field(
|
||||
..., description="Number of processes to use for the DICE simulations"
|
||||
)
|
||||
|
||||
ljname: Path = Field(..., description="Name of the Lennard-Jones potential file")
|
||||
outname: str = Field(
|
||||
..., description="Name of the output file for the simulation results"
|
||||
)
|
||||
dens: float = Field(..., description="Density of the system")
|
||||
nmol: list[int] = Field(
|
||||
..., description="List of the number of molecules for each component"
|
||||
)
|
||||
nstep: list[int] = Field(
|
||||
...,
|
||||
description="List of the number of steps for each component",
|
||||
min_length=2,
|
||||
max_length=3,
|
||||
)
|
||||
|
||||
upbuf: int = Field(
|
||||
360, description="Buffer size for the potential energy calculations"
|
||||
)
|
||||
irdf: int = Field(
|
||||
0,
|
||||
description="Controls the interval of Monte Carlo steps at which configurations are used at computation of radial distribution functions",
|
||||
)
|
||||
vstep: int = Field(
|
||||
5000, description="Frequency of volume change moves in NPT simulations"
|
||||
)
|
||||
combrule: Literal["+", "*"] = Field(
|
||||
"*", description="Combination rule for the Lennard-Jones potential"
|
||||
)
|
||||
isave: int = Field(1000, description="Frequency of saving the simulation results")
|
||||
press: float = Field(1.0, description="Pressure of the system")
|
||||
temp: float = Field(300.0, description="Temperature of the system")
|
||||
progname: str = Field(
|
||||
"dice", description="Name of the program to run the simulation"
|
||||
)
|
||||
randominit: str = Field(
|
||||
"first", description="Method for initializing the random number generator"
|
||||
)
|
||||
seed: int = Field(
|
||||
default_factory=lambda: int(1e6 * random.random()),
|
||||
description="Seed for the random number generator",
|
||||
)
|
||||
29
diceplayer/config/gaussian_config.py
Normal file
29
diceplayer/config/gaussian_config.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from typing_extensions import Literal
|
||||
|
||||
|
||||
class GaussianConfig(BaseModel):
|
||||
"""
|
||||
Data Transfer Object for the Gaussian configuration.
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
frozen=True,
|
||||
)
|
||||
|
||||
qmprog: Literal["g03", "g09", "g16"] = Field(
|
||||
"g16", description="QM program to use for the calculations"
|
||||
)
|
||||
level: str = Field(..., description="Level of theory for the QM calculations")
|
||||
|
||||
chgmult: list[int] = Field(
|
||||
default_factory=lambda: [0, 1],
|
||||
description="List of charge and multiplicity for the QM calculations",
|
||||
)
|
||||
pop: str = Field(
|
||||
"chelpg", description="Population analysis method for the QM calculations"
|
||||
)
|
||||
chg_tol: float = Field(0.01, description="Charge tolerance for the QM calculations")
|
||||
keywords: str | None = Field(
|
||||
None, description="Additional keywords for the QM calculations"
|
||||
)
|
||||
90
diceplayer/config/player_config.py
Normal file
90
diceplayer/config/player_config.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from diceplayer.config.dice_config import DiceConfig
|
||||
from diceplayer.config.gaussian_config import GaussianConfig
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from typing_extensions import Any
|
||||
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MIN_STEP = 20000
|
||||
STEP_INCREMENT = 1000
|
||||
|
||||
|
||||
class RoutineType(str, Enum):
|
||||
CHARGE = "charge"
|
||||
GEOMETRY = "geometry"
|
||||
BOTH = "both"
|
||||
|
||||
|
||||
class PlayerConfig(BaseModel):
|
||||
"""
|
||||
Configuration for DICEPlayer simulations.
|
||||
|
||||
Attributes:
|
||||
type: Type of simulation to perform (charge, geometry, or both).
|
||||
max_cyc: Maximum number of cycles for the geometry optimization.
|
||||
switch_cyc: Cycle at which to switch from charge to geometry optimization (if type is "both").
|
||||
mem: Memory configuration for QM calculations.
|
||||
nprocs: Number of processors to use for QM calculations.
|
||||
ncores: Number of cores to use for QM calculations.
|
||||
dice: Configuration parameters specific to DICE simulations.
|
||||
gaussian: Configuration parameters specific to Gaussian calculations.
|
||||
altsteps: Number of steps for the alternate simulation (default: 20000).
|
||||
geoms_file: File name for the geometries output (default: "geoms.xyz").
|
||||
simulation_dir: Directory name for the simulation files (default: "simfiles").
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
frozen=True,
|
||||
)
|
||||
|
||||
type: RoutineType = Field(..., description="Type of simulation to perform")
|
||||
max_cyc: int = Field(
|
||||
..., description="Maximum number of cycles for the geometry optimization", gt=0
|
||||
)
|
||||
switch_cyc: int = Field(..., description="Switch cycle configuration")
|
||||
|
||||
mem: int = Field(None, description="Memory configuration")
|
||||
ncores: int = Field(
|
||||
..., description="Number of cores to use for the QM calculations"
|
||||
)
|
||||
|
||||
dice: DiceConfig = Field(..., description="Dice configuration")
|
||||
gaussian: GaussianConfig = Field(..., description="Gaussian configuration")
|
||||
|
||||
altsteps: int = Field(
|
||||
20000, description="Number of steps for the alternate simulation"
|
||||
)
|
||||
geoms_file: Path = Field(
|
||||
Path("geoms.xyz"), description="File name for the geometries output"
|
||||
)
|
||||
simulation_dir: Path = Field(
|
||||
Path("simfiles"), description="Directory name for the simulation files"
|
||||
)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@staticmethod
|
||||
def validate_altsteps(fields) -> dict[str, Any]:
|
||||
altsteps = fields.pop("altsteps", MIN_STEP)
|
||||
fields["altsteps"] = (
|
||||
round(max(MIN_STEP, altsteps) / STEP_INCREMENT) * STEP_INCREMENT
|
||||
)
|
||||
return fields
|
||||
|
||||
@model_validator(mode="before")
|
||||
@staticmethod
|
||||
def validate_switch_cyc(fields: dict[str, Any]) -> dict[str, Any]:
|
||||
max_cyc = int(fields.get("max_cyc", 0))
|
||||
switch_cyc = int(fields.get("switch_cyc", max_cyc))
|
||||
|
||||
if fields.get("type") == "both" and not switch_cyc < max_cyc:
|
||||
raise ValueError("switch_cyc must be less than max_cyc when type='both'.")
|
||||
|
||||
if fields.get("type") != "both" and switch_cyc != max_cyc:
|
||||
raise ValueError(
|
||||
"switch_cyc must be equal to max_cyc when type is not 'both'."
|
||||
)
|
||||
|
||||
return fields
|
||||
174
diceplayer/dice/__init__.py
Normal file
174
diceplayer/dice/__init__.py
Normal file
@@ -0,0 +1,174 @@
|
||||
"""
|
||||
DICE Monte Carlo Simulation Interface
|
||||
=====================================
|
||||
|
||||
This package provides utilities for configuring and running simulations with
|
||||
the DICE Monte Carlo molecular simulation program.
|
||||
|
||||
DICE performs statistical sampling of molecular systems using the Metropolis
|
||||
Monte Carlo algorithm. Simulations are defined by text input files containing
|
||||
keywords that control the thermodynamic ensemble, system composition, and
|
||||
simulation parameters.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Simulation Ensembles
|
||||
--------------------
|
||||
|
||||
DICE supports multiple statistical ensembles.
|
||||
|
||||
NVT
|
||||
Canonical ensemble where the following properties remain constant:
|
||||
|
||||
- N: number of molecules
|
||||
- V: system volume
|
||||
- T: temperature
|
||||
|
||||
The system density is fixed and the simulation box volume does not change
|
||||
during the simulation.
|
||||
|
||||
NPT
|
||||
Isothermal–isobaric ensemble where the following properties remain constant:
|
||||
|
||||
- N: number of molecules
|
||||
- P: pressure
|
||||
- T: temperature
|
||||
|
||||
The simulation box volume is allowed to fluctuate in order to maintain the
|
||||
target pressure.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Simulation Stages
|
||||
-----------------
|
||||
|
||||
Simulations are typically executed in multiple stages.
|
||||
|
||||
Thermalization (TER)
|
||||
Initial phase where the system relaxes to the desired thermodynamic
|
||||
conditions. Molecular configurations stabilize and the system reaches
|
||||
equilibrium.
|
||||
|
||||
During this stage statistical properties are **not accumulated**.
|
||||
|
||||
Production / Equilibration (EQ)
|
||||
Main sampling phase after the system has equilibrated.
|
||||
|
||||
Statistical properties such as energies, densities, and radial
|
||||
distribution functions are collected and configurations may be saved
|
||||
for later analysis.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Typical Simulation Pipeline
|
||||
---------------------------
|
||||
|
||||
Two common execution workflows are used.
|
||||
|
||||
NVT Simulation
|
||||
Used when the system density is known.
|
||||
|
||||
1. NVT.ter → thermalization at constant density
|
||||
2. NVT.eq → production sampling
|
||||
|
||||
NPT Simulation
|
||||
Used when the equilibrium density is unknown.
|
||||
|
||||
1. NVT.ter → initial thermalization at approximate density
|
||||
2. NPT.ter → pressure relaxation (volume adjustment)
|
||||
3. NPT.eq → production sampling at target pressure
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
DICE Input Keywords
|
||||
-------------------
|
||||
|
||||
The following keywords are used in the generated input files.
|
||||
|
||||
title
|
||||
Descriptive title printed in the simulation output.
|
||||
|
||||
ncores
|
||||
Number of CPU cores used by the DICE executable.
|
||||
|
||||
ljname
|
||||
File containing Lennard-Jones parameters and molecular topology.
|
||||
|
||||
outname
|
||||
Prefix used for simulation output files.
|
||||
|
||||
nmol
|
||||
Number of molecules of each species in the system.
|
||||
|
||||
dens
|
||||
System density (g/cm³). Used only in NVT simulations or for
|
||||
initialization of NPT runs.
|
||||
|
||||
press
|
||||
Target pressure used in NPT simulations.
|
||||
|
||||
temp
|
||||
Simulation temperature.
|
||||
|
||||
nstep
|
||||
Number of Monte Carlo cycles executed in the simulation stage.
|
||||
|
||||
init
|
||||
Defines how the simulation initializes molecular coordinates.
|
||||
|
||||
yes
|
||||
Random initial configuration.
|
||||
|
||||
no
|
||||
Continue from a previous configuration.
|
||||
|
||||
yesreadxyz
|
||||
Read coordinates from a previously saved XYZ configuration.
|
||||
|
||||
vstep
|
||||
Frequency of volume-change moves in NPT simulations.
|
||||
|
||||
mstop
|
||||
Molecule displacement control flag used internally by DICE.
|
||||
|
||||
accum
|
||||
Enables or disables accumulation of statistical averages.
|
||||
|
||||
iprint
|
||||
Frequency of simulation information printed to the output.
|
||||
|
||||
isave
|
||||
Frequency at which configurations are written to trajectory files.
|
||||
|
||||
irdf
|
||||
Controls calculation of radial distribution functions.
|
||||
|
||||
seed
|
||||
Random number generator seed used by the Monte Carlo algorithm.
|
||||
|
||||
upbuf
|
||||
Buffer size parameter used internally by DICE during thermalization.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Output Files
|
||||
------------
|
||||
|
||||
Important output files produced during the simulation include:
|
||||
|
||||
phb.xyz
|
||||
XYZ trajectory containing sampled molecular configurations.
|
||||
|
||||
last.xyz
|
||||
Final configuration of the simulation, often used as the starting
|
||||
configuration for the next simulation cycle.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
DICE is a Monte Carlo molecular simulation program developed primarily
|
||||
by researchers at the University of São Paulo (USP) for studying liquids,
|
||||
solutions, and solvation phenomena.
|
||||
"""
|
||||
115
diceplayer/dice/dice_handler.py
Normal file
115
diceplayer/dice/dice_handler.py
Normal file
@@ -0,0 +1,115 @@
|
||||
from diceplayer.dice.dice_input import (
|
||||
NPTEqConfig,
|
||||
NPTTerConfig,
|
||||
NVTEqConfig,
|
||||
NVTTerConfig,
|
||||
)
|
||||
from diceplayer.dice.dice_wrapper import DiceWrapper
|
||||
from diceplayer.logger import logger
|
||||
from diceplayer.state.state_model import StateModel
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
|
||||
|
||||
class DiceHandler:
|
||||
def __init__(self, step_directory: Path):
|
||||
self.dice_directory = step_directory / "dice"
|
||||
|
||||
def run(self, state: StateModel, cycle: int) -> StateModel:
|
||||
if self.dice_directory.exists():
|
||||
logger.info(
|
||||
f"Found dice directory: {self.dice_directory}, this directory will be purged for a clean state"
|
||||
)
|
||||
shutil.rmtree(self.dice_directory)
|
||||
self.dice_directory.mkdir(parents=True)
|
||||
|
||||
simulation_results = self.run_simulations(state, cycle)
|
||||
|
||||
result = self.aggregate_results(simulation_results)
|
||||
|
||||
return self.commit_simulation_state(state, result)
|
||||
|
||||
def run_simulations(self, state: StateModel, cycle: int) -> list[dict]:
|
||||
results = []
|
||||
|
||||
threads = []
|
||||
for p in range(state.config.dice.nprocs):
|
||||
t = Thread(target=self._simulation_process, args=(state, cycle, p, results))
|
||||
threads.append(t)
|
||||
t.start()
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
if len(results) != state.config.dice.nprocs:
|
||||
raise RuntimeError(
|
||||
f"Expected {state.config.dice.nprocs} simulation results, but got {len(results)}"
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
def aggregate_results(self, simulation_results: list[dict]) -> dict: ...
|
||||
|
||||
def commit_simulation_state(self, state: StateModel, result: dict) -> StateModel:
|
||||
return state
|
||||
|
||||
def _simulation_process(
|
||||
self, state: StateModel, cycle: int, proc: int, results: list[dict]
|
||||
) -> None:
|
||||
proc_directory = self.dice_directory / f"{proc:02d}"
|
||||
if proc_directory.exists():
|
||||
shutil.rmtree(proc_directory)
|
||||
proc_directory.mkdir(parents=True)
|
||||
|
||||
dice = DiceWrapper(state.config.dice, proc_directory)
|
||||
|
||||
self._generate_phb_file(state, proc_directory)
|
||||
|
||||
if state.config.dice.randominit == "first" and cycle >= 0:
|
||||
self._generate_last_xyz(state, proc_directory)
|
||||
else:
|
||||
nvt_ter_config = NVTTerConfig.from_config(state.config)
|
||||
dice.run(nvt_ter_config)
|
||||
|
||||
if len(state.config.dice.nstep) == 2:
|
||||
nvt_eq_config = NVTEqConfig.from_config(state.config)
|
||||
dice.run(nvt_eq_config)
|
||||
|
||||
elif len(state.config.dice.nstep) == 3:
|
||||
npt_ter_config = NPTTerConfig.from_config(state.config)
|
||||
dice.run(npt_ter_config)
|
||||
|
||||
npt_eq_config = NPTEqConfig.from_config(state.config)
|
||||
dice.run(npt_eq_config)
|
||||
|
||||
results.append(dice.parse_results(state.system))
|
||||
|
||||
@staticmethod
|
||||
def _generate_phb_file(state: StateModel, proc_directory: Path) -> None:
|
||||
fstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f}\n"
|
||||
|
||||
phb_file = proc_directory / state.config.dice.ljname
|
||||
|
||||
with open(phb_file, "w") as f:
|
||||
f.write(f"{state.config.dice.combrule}\n")
|
||||
f.write(f"{len(state.config.dice.nmol)}\n")
|
||||
|
||||
for molecule in state.system.molecule:
|
||||
f.write(f"{len(molecule.atom)} {molecule.molname}\n")
|
||||
for atom in molecule.atom:
|
||||
f.write(
|
||||
fstr.format(
|
||||
atom.lbl,
|
||||
atom.na,
|
||||
atom.rx,
|
||||
atom.ry,
|
||||
atom.rz,
|
||||
atom.chg,
|
||||
atom.eps,
|
||||
atom.sig,
|
||||
)
|
||||
)
|
||||
|
||||
def _generate_last_xyz(self, state: StateModel, proc_directory: Path) -> None: ...
|
||||
257
diceplayer/dice/dice_input.py
Normal file
257
diceplayer/dice/dice_input.py
Normal file
@@ -0,0 +1,257 @@
|
||||
from diceplayer.config import PlayerConfig
|
||||
from diceplayer.logger import logger
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing_extensions import Self
|
||||
|
||||
import random
|
||||
from enum import StrEnum
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Any, Literal, TextIO
|
||||
|
||||
|
||||
_ALLOWED_DICE_KEYWORD_IN_ORDER = [
|
||||
"title",
|
||||
"ncores",
|
||||
"ljname",
|
||||
"outname",
|
||||
"nmol",
|
||||
"dens",
|
||||
"temp",
|
||||
"press",
|
||||
"seed",
|
||||
"init",
|
||||
"nstep",
|
||||
"vstep",
|
||||
"mstop",
|
||||
"accum",
|
||||
"iprint",
|
||||
"isave",
|
||||
"irdf",
|
||||
"upbuf",
|
||||
]
|
||||
|
||||
|
||||
class DiceRoutineType(StrEnum):
|
||||
NVT_TER = "nvt.ter"
|
||||
NVT_EQ = "nvt.eq"
|
||||
NPT_TER = "npt.ter"
|
||||
NPT_EQ = "npt.eq"
|
||||
|
||||
|
||||
def get_nstep(config, idx: int) -> int:
|
||||
if len(config.dice.nstep) > idx:
|
||||
return config.dice.nstep[idx]
|
||||
return config.dice.nstep[-1]
|
||||
|
||||
|
||||
def get_seed(config) -> int:
|
||||
return config.dice.seed or random.randint(0, 2**32 - 1)
|
||||
|
||||
|
||||
def get_ncores(config) -> int:
|
||||
return max(1, int(config.ncores / config.dice.nprocs))
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# NVT THERMALIZATION
|
||||
# -----------------------------------------------------
|
||||
class NVTTerConfig(BaseModel):
|
||||
type: Literal[DiceRoutineType.NVT_TER] = DiceRoutineType.NVT_TER
|
||||
|
||||
title: str = "NVT Thermalization"
|
||||
ncores: int
|
||||
ljname: str
|
||||
outname: str
|
||||
nmol: list[int]
|
||||
dens: float
|
||||
temp: float
|
||||
seed: int
|
||||
init: Literal["yes"] = "yes"
|
||||
nstep: int
|
||||
vstep: Literal[0] = 0
|
||||
isave: int
|
||||
upbuf: int
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config: PlayerConfig, **kwargs) -> Self:
|
||||
return cls(
|
||||
ncores=get_ncores(config),
|
||||
ljname=str(config.dice.ljname),
|
||||
outname=config.dice.outname,
|
||||
nmol=config.dice.nmol,
|
||||
dens=config.dice.dens,
|
||||
temp=config.dice.temp,
|
||||
seed=get_seed(config),
|
||||
nstep=get_nstep(config, 0),
|
||||
isave=config.dice.isave,
|
||||
upbuf=config.dice.upbuf,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# NVT PRODUCTION
|
||||
# -----------------------------------------------------
|
||||
class NVTEqConfig(BaseModel):
|
||||
type: Literal[DiceRoutineType.NVT_EQ] = DiceRoutineType.NVT_EQ
|
||||
|
||||
title: str = "NVT Production"
|
||||
ncores: int
|
||||
ljname: str
|
||||
outname: str
|
||||
nmol: list[int]
|
||||
dens: float
|
||||
temp: float
|
||||
seed: int
|
||||
init: Literal["no", "yesreadxyz"] = "no"
|
||||
nstep: int
|
||||
vstep: int
|
||||
isave: int
|
||||
irdf: Literal[0] = 0
|
||||
upbuf: int
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config: PlayerConfig, **kwargs) -> Self:
|
||||
return cls(
|
||||
ncores=get_ncores(config),
|
||||
ljname=str(config.dice.ljname),
|
||||
outname=config.dice.outname,
|
||||
nmol=config.dice.nmol,
|
||||
dens=config.dice.dens,
|
||||
temp=config.dice.temp,
|
||||
seed=get_seed(config),
|
||||
nstep=get_nstep(config, 1),
|
||||
vstep=config.dice.vstep,
|
||||
isave=max(1, get_nstep(config, 1) // 10),
|
||||
upbuf=config.dice.upbuf,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# NPT THERMALIZATION
|
||||
# -----------------------------------------------------
|
||||
class NPTTerConfig(BaseModel):
|
||||
type: Literal[DiceRoutineType.NPT_TER] = DiceRoutineType.NPT_TER
|
||||
|
||||
title: str = "NPT Thermalization"
|
||||
ncores: int
|
||||
ljname: str
|
||||
outname: str
|
||||
nmol: list[int]
|
||||
dens: float
|
||||
temp: float
|
||||
press: float
|
||||
seed: int
|
||||
init: Literal["yes", "yesreadxyz"] = "yes"
|
||||
nstep: int
|
||||
vstep: int
|
||||
isave: int
|
||||
upbuf: int
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config: PlayerConfig, **kwargs) -> Self:
|
||||
return cls(
|
||||
ncores=get_ncores(config),
|
||||
ljname=str(config.dice.ljname),
|
||||
outname=config.dice.outname,
|
||||
nmol=config.dice.nmol,
|
||||
dens=config.dice.dens,
|
||||
temp=config.dice.temp,
|
||||
press=config.dice.press,
|
||||
seed=get_seed(config),
|
||||
nstep=get_nstep(config, 1),
|
||||
vstep=max(1, config.dice.vstep),
|
||||
isave=config.dice.isave,
|
||||
upbuf=config.dice.upbuf,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# NPT PRODUCTION
|
||||
# -----------------------------------------------------
|
||||
class NPTEqConfig(BaseModel):
|
||||
type: Literal[DiceRoutineType.NPT_EQ] = DiceRoutineType.NPT_EQ
|
||||
|
||||
title: str = "NPT Production"
|
||||
ncores: int
|
||||
ljname: str
|
||||
outname: str
|
||||
nmol: list[int]
|
||||
dens: float
|
||||
temp: float
|
||||
press: float
|
||||
seed: int
|
||||
init: Literal["yes", "yesreadxyz"] = "yes"
|
||||
nstep: int
|
||||
vstep: int
|
||||
isave: int
|
||||
irdf: Literal[0] = 0
|
||||
upbuf: int
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config: PlayerConfig, **kwargs) -> Self:
|
||||
return cls(
|
||||
ncores=get_ncores(config),
|
||||
ljname=str(config.dice.ljname),
|
||||
outname=config.dice.outname,
|
||||
nmol=config.dice.nmol,
|
||||
dens=config.dice.dens,
|
||||
temp=config.dice.temp,
|
||||
press=config.dice.press,
|
||||
seed=get_seed(config),
|
||||
nstep=get_nstep(config, 2),
|
||||
vstep=config.dice.vstep,
|
||||
isave=max(1, get_nstep(config, 2) // 10),
|
||||
upbuf=config.dice.upbuf,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
DiceInputConfig = Annotated[
|
||||
NVTTerConfig | NVTEqConfig | NPTTerConfig | NPTEqConfig,
|
||||
Field(discriminator="type"),
|
||||
]
|
||||
|
||||
|
||||
def _serialize_value(value: Any) -> str:
|
||||
if value is None:
|
||||
raise ValueError("DICE configuration cannot serialize None values")
|
||||
|
||||
if isinstance(value, bool):
|
||||
return "yes" if value else "no"
|
||||
|
||||
if isinstance(value, (list, tuple)):
|
||||
return " ".join(str(v) for v in value)
|
||||
|
||||
return str(value)
|
||||
|
||||
|
||||
def write_dice_config(obj: DiceInputConfig, io_writer: TextIO) -> None:
|
||||
values = {f: getattr(obj, f) for f in obj.__class__.model_fields}
|
||||
|
||||
for key in _ALLOWED_DICE_KEYWORD_IN_ORDER:
|
||||
value = values.pop(key, None)
|
||||
if value is None:
|
||||
continue
|
||||
io_writer.write(f"{key} = {_serialize_value(value)}\n")
|
||||
|
||||
io_writer.write("$end\n")
|
||||
|
||||
|
||||
def write_config(config: DiceInputConfig, directory: Path) -> Path:
|
||||
input_path = directory / config.type
|
||||
|
||||
if input_path.exists():
|
||||
logger.info(
|
||||
f"Dice input file {input_path} already exists and will be overwritten"
|
||||
)
|
||||
input_path.unlink()
|
||||
input_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(input_path, "w") as io:
|
||||
write_dice_config(config, io)
|
||||
|
||||
return input_path
|
||||
43
diceplayer/dice/dice_wrapper.py
Normal file
43
diceplayer/dice/dice_wrapper.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import diceplayer.dice.dice_input as dice_input
|
||||
from diceplayer.config import DiceConfig
|
||||
from diceplayer.environment import System
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
|
||||
DICE_FLAG_LINE: Final[int] = -2
|
||||
DICE_END_FLAG: Final[str] = "End of simulation"
|
||||
|
||||
|
||||
class DiceWrapper:
|
||||
def __init__(self, dice_config: DiceConfig, working_directory: Path):
|
||||
self.dice_config = dice_config
|
||||
self.working_directory = working_directory
|
||||
|
||||
def run(self, dice_config: dice_input.DiceInputConfig) -> None:
|
||||
input_path = dice_input.write_config(dice_config, self.working_directory)
|
||||
output_path = input_path.parent / (input_path.name + ".out")
|
||||
|
||||
with open(output_path, "w") as outfile, open(input_path, "r") as infile:
|
||||
exit_status = subprocess.call(
|
||||
self.dice_config.progname, stdin=infile, stdout=outfile, cwd=self.working_directory
|
||||
)
|
||||
|
||||
if exit_status != 0:
|
||||
raise RuntimeError(f"Dice simulation failed with exit status {exit_status}")
|
||||
|
||||
with open(output_path, "r") as outfile:
|
||||
line = outfile.readlines()[DICE_FLAG_LINE]
|
||||
if line.strip() == DICE_END_FLAG:
|
||||
return
|
||||
|
||||
raise RuntimeError(f"Dice simulation failed with exit status {exit_status}")
|
||||
|
||||
def parse_results(self, system: System) -> dict:
|
||||
results = {}
|
||||
for output_file in sorted(self.working_directory.glob("phb*.xyz")):
|
||||
...
|
||||
|
||||
return results
|
||||
6
diceplayer/environment/__init__.py
Normal file
6
diceplayer/environment/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from .atom import Atom
|
||||
from .molecule import Molecule
|
||||
from .system import System
|
||||
|
||||
|
||||
__all__ = ["Atom", "Molecule", "System"]
|
||||
27
diceplayer/environment/atom.py
Normal file
27
diceplayer/environment/atom.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from diceplayer.utils.ptable import AtomInfo, PTable
|
||||
|
||||
from pydantic.dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Atom:
|
||||
"""
|
||||
Atom class declaration. This class is used throughout the DicePlayer program to represent atoms.
|
||||
"""
|
||||
|
||||
lbl: int
|
||||
na: int
|
||||
rx: float
|
||||
ry: float
|
||||
rz: float
|
||||
chg: float
|
||||
eps: float
|
||||
sig: float
|
||||
|
||||
@property
|
||||
def mass(self) -> float:
|
||||
return PTable.get_atomic_mass(self.na)
|
||||
|
||||
@property
|
||||
def atom_info(self) -> AtomInfo:
|
||||
return PTable.get_from_atomic_number(self.na)
|
||||
317
diceplayer/environment/molecule.py
Normal file
317
diceplayer/environment/molecule.py
Normal file
@@ -0,0 +1,317 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from diceplayer.environment import Atom
|
||||
from diceplayer.logger import logger
|
||||
from diceplayer.utils.cache import invalidate_computed_properties
|
||||
from diceplayer.utils.misc import BOHR2ANG, EA_2_DEBYE
|
||||
from diceplayer.utils.ptable import GHOST_NUMBER
|
||||
|
||||
import numpy as np
|
||||
import numpy.linalg as linalg
|
||||
import numpy.typing as npt
|
||||
from pydantic.dataclasses import dataclass
|
||||
from typing_extensions import List, Self, Tuple
|
||||
|
||||
import math
|
||||
from copy import deepcopy
|
||||
from dataclasses import field
|
||||
from functools import cached_property
|
||||
|
||||
|
||||
@dataclass
|
||||
class Molecule:
|
||||
"""
|
||||
Molecule class declaration. This class is used throughout the DicePlayer program to represent molecules.
|
||||
|
||||
Atributes:
|
||||
molname (str): The name of the represented molecule
|
||||
atom (List[Atom]): List of atoms of the represented molecule
|
||||
total_mass (int): The total mass of the molecule
|
||||
com (npt.NDArray[np.float64]): The center of mass of the molecule
|
||||
inertia_tensor (npt.NDArray[np.float64]): The inertia tensor of the molecule
|
||||
"""
|
||||
|
||||
molname: str
|
||||
atom: List[Atom] = field(default_factory=list)
|
||||
|
||||
@cached_property
|
||||
def total_mass(self) -> float:
|
||||
return sum(atom.mass for atom in self.atom)
|
||||
|
||||
@cached_property
|
||||
def com(self) -> npt.NDArray[np.float64]:
|
||||
com = np.zeros(3)
|
||||
|
||||
for atom in self.atom:
|
||||
com += atom.mass * np.array([atom.rx, atom.ry, atom.rz])
|
||||
|
||||
com = com / self.total_mass
|
||||
|
||||
return com
|
||||
|
||||
@cached_property
|
||||
def inertia_tensor(self) -> npt.NDArray[np.float64]:
|
||||
"""
|
||||
Calculates the inertia tensor of the molecule.
|
||||
|
||||
Returns:
|
||||
npt.NDArray[np.float64]: inertia tensor of the molecule.
|
||||
"""
|
||||
inertia_tensor = np.zeros((3, 3), dtype=np.float64)
|
||||
|
||||
for atom in self.atom:
|
||||
dx = atom.rx - self.com[0]
|
||||
dy = atom.ry - self.com[1]
|
||||
dz = atom.rz - self.com[2]
|
||||
|
||||
inertia_tensor[0, 0] += atom.mass * (dy**2 + dz**2)
|
||||
inertia_tensor[1, 1] += atom.mass * (dz**2 + dx**2)
|
||||
inertia_tensor[2, 2] += atom.mass * (dx**2 + dy**2)
|
||||
|
||||
inertia_tensor[0, 1] -= atom.mass * dx * dy
|
||||
inertia_tensor[0, 2] -= atom.mass * dx * dz
|
||||
inertia_tensor[1, 2] -= atom.mass * dy * dz
|
||||
|
||||
# enforce symmetry
|
||||
inertia_tensor[1, 0] = inertia_tensor[0, 1]
|
||||
inertia_tensor[2, 0] = inertia_tensor[0, 2]
|
||||
inertia_tensor[2, 1] = inertia_tensor[1, 2]
|
||||
|
||||
return inertia_tensor
|
||||
|
||||
@invalidate_computed_properties()
|
||||
def add_atom(self, a: Atom) -> None:
|
||||
"""
|
||||
Adds Atom instance to the molecule.
|
||||
|
||||
Args:
|
||||
a (Atom): Atom instance to be added to atom list.
|
||||
"""
|
||||
|
||||
self.atom.append(a)
|
||||
|
||||
@invalidate_computed_properties()
|
||||
def remove_atom(self, a: Atom) -> None:
|
||||
"""
|
||||
Removes Atom instance from the molecule.
|
||||
|
||||
Args:
|
||||
a (Atom): Atom instance to be removed from atom list.
|
||||
"""
|
||||
|
||||
self.atom.remove(a)
|
||||
|
||||
@invalidate_computed_properties()
|
||||
def move_center_of_mass_to_origin(self) -> None:
|
||||
"""
|
||||
Updated positions based on the center of mass of the molecule
|
||||
"""
|
||||
for atom in self.atom:
|
||||
atom.rx -= self.com[0]
|
||||
atom.ry -= self.com[1]
|
||||
atom.rz -= self.com[2]
|
||||
|
||||
@invalidate_computed_properties()
|
||||
def rotate_to_standard_orientation(self) -> None:
|
||||
"""
|
||||
Rotates the molecule to the standard orientation
|
||||
"""
|
||||
|
||||
self.move_center_of_mass_to_origin()
|
||||
evals, evecs = self.principal_axes()
|
||||
|
||||
if np.isclose(linalg.det(evecs), -1):
|
||||
evecs[:, 2] *= -1
|
||||
|
||||
if not np.isclose(linalg.det(evecs), 1):
|
||||
raise RuntimeError(
|
||||
"Error: could not make a rotation matrix while adopting the standard orientation"
|
||||
)
|
||||
|
||||
coords = np.array([(a.rx, a.ry, a.rz) for a in self.atom])
|
||||
rotated = coords @ evecs.T
|
||||
|
||||
for atom, pos in zip(self.atom, rotated):
|
||||
atom.rx, atom.ry, atom.rz = pos
|
||||
|
||||
def charges_and_dipole(self) -> List[float]:
|
||||
"""
|
||||
Calculates the charges and dipole of the molecule atoms
|
||||
|
||||
Returns:
|
||||
List[float]: Respectivly magnitude of the: charge magnitude, first dipole,
|
||||
second dipole, third dipole and total dipole.
|
||||
"""
|
||||
|
||||
charge = 0
|
||||
dipole = np.zeros(3)
|
||||
for atom in self.atom:
|
||||
position = np.array([atom.rx, atom.ry, atom.rz])
|
||||
dipole += atom.chg * position
|
||||
charge += atom.chg
|
||||
|
||||
dipole *= EA_2_DEBYE
|
||||
total_dipole = math.sqrt(dipole[0] ** 2 + dipole[1] ** 2 + dipole[2] ** 2)
|
||||
|
||||
return [charge, dipole[0], dipole[1], dipole[2], total_dipole]
|
||||
|
||||
def distances_between_atoms(self) -> npt.NDArray[np.float64]:
|
||||
"""
|
||||
Calculates distances between the atoms of the molecule
|
||||
|
||||
Returns:
|
||||
NDArray[Shape["Any,Any"],Float]: distances between the atoms.
|
||||
"""
|
||||
coords = np.array([(a.rx, a.ry, a.rz) for a in self.atom], dtype=np.float64)
|
||||
diff = coords[:, None, :] - coords[None, :, :]
|
||||
return np.linalg.norm(diff, axis=-1)
|
||||
|
||||
def principal_axes(self) -> Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
|
||||
"""
|
||||
Calculates the principal axes of the molecule
|
||||
|
||||
Returns:
|
||||
Tuple[np.ndarray, np.ndarray]: Tuple where the first value is the Eigen Values and the second is the Eigen Vectors,
|
||||
representing the principal axes of the molecule.
|
||||
"""
|
||||
|
||||
try:
|
||||
evals, evecs = linalg.eigh(self.inertia_tensor)
|
||||
|
||||
idx = np.argsort(evals)
|
||||
evals = evals[idx]
|
||||
evecs = evecs[:, idx]
|
||||
except ValueError:
|
||||
raise RuntimeError(
|
||||
"Error: diagonalization of inertia tensor did not converge"
|
||||
)
|
||||
|
||||
return evals, evecs
|
||||
|
||||
def read_position(self) -> npt.NDArray[np.float64]:
|
||||
"""Reads the position of the molecule from the position values of the atoms
|
||||
|
||||
Returns:
|
||||
np.ndarray: internal position relative to atoms of the molecule
|
||||
"""
|
||||
coords = np.array([(a.rx, a.ry, a.rz) for a in self.atom], dtype=np.float64)
|
||||
return coords.ravel() * BOHR2ANG
|
||||
|
||||
def update_charges(self, charges: npt.NDArray[np.float64]) -> 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
|
||||
|
||||
def sizes_of_molecule(self) -> List[float]:
|
||||
"""
|
||||
Calculates sides of the smallest box that the molecule could fit
|
||||
|
||||
Returns:
|
||||
List[float]: list of the sizes of the molecule
|
||||
"""
|
||||
coords = np.array([(a.rx, a.ry, a.rz) for a in self.atom], dtype=np.float64)
|
||||
return (coords.max(axis=0) - coords.min(axis=0)).tolist()
|
||||
|
||||
def translate(self, vector: np.ndarray) -> Self:
|
||||
"""
|
||||
Creates a new Molecule object where its' atoms has been translated by a vector
|
||||
|
||||
Args:
|
||||
vector (np.ndarray): translation vector
|
||||
|
||||
Returns:
|
||||
Molecule: new Molecule object translated by a vector
|
||||
"""
|
||||
vec = np.asarray(vector, dtype=np.float64)
|
||||
if vec.shape != (3,):
|
||||
raise ValueError("translation vector must be shape (3,)")
|
||||
|
||||
new_molecule = deepcopy(self)
|
||||
|
||||
for atom in new_molecule.atom:
|
||||
atom.rx += vector[0]
|
||||
atom.ry += vector[1]
|
||||
atom.rz += vector[2]
|
||||
|
||||
return new_molecule
|
||||
|
||||
def print_mol_info(self) -> None:
|
||||
"""
|
||||
Prints the Molecule information into a Output File
|
||||
"""
|
||||
|
||||
logger.info(
|
||||
" Center of mass = ( {:>10.4f} , {:>10.4f} , {:>10.4f} )".format(
|
||||
self.com[0], self.com[1], self.com[2]
|
||||
)
|
||||
)
|
||||
evals, evecs = self.principal_axes()
|
||||
|
||||
logger.info(
|
||||
" Moments of inertia = {:>9E} {:>9E} {:>9E}".format(
|
||||
evals[0], evals[1], evals[2]
|
||||
)
|
||||
)
|
||||
|
||||
logger.info(
|
||||
" Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format(
|
||||
evecs[0, 0], evecs[1, 0], evecs[2, 0]
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
" Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format(
|
||||
evecs[0, 1], evecs[1, 1], evecs[2, 1]
|
||||
)
|
||||
)
|
||||
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()
|
||||
logger.info(
|
||||
" Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )".format(
|
||||
sizes[0], sizes[1], sizes[2]
|
||||
)
|
||||
)
|
||||
logger.info(" Total mass = {:>8.2f} au".format(self.total_mass))
|
||||
|
||||
chg_dip = self.charges_and_dipole()
|
||||
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]
|
||||
)
|
||||
)
|
||||
|
||||
def minimum_distance(self, molec: Self) -> float:
|
||||
"""
|
||||
Return the minimum distance between two molecules
|
||||
|
||||
Args:
|
||||
molec (Molecule): Molecule object to be compared
|
||||
|
||||
Returns:
|
||||
float: minimum distance between the two molecules
|
||||
"""
|
||||
coords_a = np.array(
|
||||
[(a.rx, a.ry, a.rz) for a in self.atom if a.na != GHOST_NUMBER]
|
||||
)
|
||||
coords_b = np.array(
|
||||
[(a.rx, a.ry, a.rz) for a in molec.atom if a.na != GHOST_NUMBER]
|
||||
)
|
||||
|
||||
if len(coords_a) == 0 or len(coords_b) == 0:
|
||||
raise ValueError("No real atoms to compare")
|
||||
|
||||
diff = coords_a[:, None, :] - coords_b[None, :, :]
|
||||
d2 = np.sum(diff**2, axis=-1)
|
||||
return np.sqrt(d2.min())
|
||||
30
diceplayer/environment/system.py
Normal file
30
diceplayer/environment/system.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from diceplayer.environment.molecule import Molecule
|
||||
|
||||
from typing_extensions import List
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class System:
|
||||
"""
|
||||
System class declaration. This class is used throughout the DicePlayer program to represent the system containing the molecules.
|
||||
|
||||
Atributes:
|
||||
molecule (List[Molecule]): List of molecules of the system
|
||||
nmols (List[int]): List of number of molecules in the system
|
||||
"""
|
||||
|
||||
nmols: List[int] = field(default_factory=list)
|
||||
molecule: List[Molecule] = field(default_factory=list)
|
||||
|
||||
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 not isinstance(m, Molecule):
|
||||
raise TypeError("Error: molecule is not a Molecule instance")
|
||||
self.molecule.append(m)
|
||||
4
diceplayer/logger.py
Normal file
4
diceplayer/logger.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from diceplayer.utils import RunLogger
|
||||
|
||||
|
||||
logger = RunLogger("diceplayer")
|
||||
19
diceplayer/optimization/optimization_handler.py
Normal file
19
diceplayer/optimization/optimization_handler.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from diceplayer.config.player_config import RoutineType
|
||||
from diceplayer.state.state_model import StateModel
|
||||
|
||||
|
||||
class OptimizationHandler:
|
||||
@staticmethod
|
||||
def run(state: StateModel, current_cycle: int) -> StateModel:
|
||||
print(f"Running Optimization - {current_cycle}")
|
||||
return state
|
||||
|
||||
@staticmethod
|
||||
def _fetch_current_routine(state: StateModel, current_cycle: int) -> RoutineType:
|
||||
if state.config.type != RoutineType.BOTH:
|
||||
return state.config.type
|
||||
|
||||
if current_cycle < state.config.switch_cyc:
|
||||
return RoutineType.CHARGE
|
||||
|
||||
return RoutineType.GEOMETRY
|
||||
@@ -1,448 +1,55 @@
|
||||
from diceplayer import VERSION, logger
|
||||
from diceplayer.shared.config.dice_config import DiceConfig
|
||||
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||
from diceplayer.shared.config.player_config import PlayerConfig
|
||||
from diceplayer.shared.environment.atom import Atom
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
from diceplayer.shared.environment.system import System
|
||||
from diceplayer.shared.interface.dice_interface import DiceInterface
|
||||
from diceplayer.shared.interface.gaussian_interface import GaussianInterface
|
||||
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||
from diceplayer.shared.utils.misc import weekday_date_time
|
||||
from diceplayer.shared.utils.ptable import atomsymb
|
||||
from diceplayer.config.player_config import PlayerConfig
|
||||
from diceplayer.dice.dice_handler import DiceHandler
|
||||
from diceplayer.logger import logger
|
||||
from diceplayer.state.state_handler import StateHandler
|
||||
from diceplayer.state.state_model import StateModel
|
||||
from diceplayer.utils.potential import read_system_from_phb
|
||||
|
||||
import yaml
|
||||
from typing_extensions import TypedDict, Unpack
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
from dataclasses import fields
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Type
|
||||
|
||||
ENV = ["OMP_STACKSIZE"]
|
||||
class PlayerFlags(TypedDict):
|
||||
continuation: bool
|
||||
force: bool
|
||||
|
||||
|
||||
class Player:
|
||||
def __init__(self, infile: str = None, optimization: bool = False):
|
||||
if infile is None and optimization is False:
|
||||
raise ValueError("Must specify either infile or optimization")
|
||||
def __init__(self, config: PlayerConfig):
|
||||
self.config = config
|
||||
self._state_handler = StateHandler(config.simulation_dir)
|
||||
|
||||
elif infile is not None:
|
||||
self.config = self.set_config(self.read_keywords(infile))
|
||||
def play(self, **flags: Unpack[PlayerFlags]):
|
||||
continuation = flags.get("continuation", False)
|
||||
force = flags.get("force", False)
|
||||
|
||||
self.system = System()
|
||||
|
||||
self.initial_cycle = 1
|
||||
|
||||
elif optimization is True:
|
||||
save = self.load_run_from_pickle()
|
||||
|
||||
self.config = save[0]
|
||||
|
||||
self.system = save[1]
|
||||
|
||||
self.initial_cycle = save[2] + 1
|
||||
state = self._state_handler.get(self.config, force=force)
|
||||
if not continuation and state is not None:
|
||||
logger.info(
|
||||
"Continuation flag is not set. Starting a new simulation and deleting any existing state."
|
||||
)
|
||||
self._state_handler.delete()
|
||||
state = None
|
||||
|
||||
if state is None:
|
||||
system = read_system_from_phb(self.config)
|
||||
state = StateModel(config=self.config, system=system)
|
||||
else:
|
||||
raise ValueError("Must specify either infile or config")
|
||||
logger.info("Resuming from existing state.")
|
||||
|
||||
self.dice_interface = DiceInterface()
|
||||
self.gaussian_interface = GaussianInterface()
|
||||
|
||||
def start(self):
|
||||
while state.current_cycle < self.config.max_cyc:
|
||||
logger.info(
|
||||
"==========================================================================================\n"
|
||||
"Starting the iterative process.\n"
|
||||
"==========================================================================================\n"
|
||||
f"Starting cycle {state.current_cycle + 1} of {self.config.max_cyc}."
|
||||
)
|
||||
|
||||
for cycle in range(self.initial_cycle, self.initial_cycle + self.config.maxcyc):
|
||||
logger.info(
|
||||
f"------------------------------------------------------------------------------------------\n"
|
||||
f" Step # {cycle}\n"
|
||||
f"------------------------------------------------------------------------------------------\n"
|
||||
)
|
||||
step_directory = self.config.simulation_dir / f"{state.current_cycle:02d}"
|
||||
if not step_directory.exists():
|
||||
step_directory.mkdir(parents=True)
|
||||
|
||||
self.dice_start(cycle)
|
||||
state = DiceHandler(step_directory).run(state, state.current_cycle)
|
||||
|
||||
try:
|
||||
self.gaussian_start(cycle)
|
||||
except StopIteration:
|
||||
break
|
||||
# state = OptimizationHandler.run(state, state.current_cycle)
|
||||
|
||||
self.save_run_in_pickle(cycle)
|
||||
state.current_cycle += 1
|
||||
self._state_handler.save(state)
|
||||
|
||||
def prepare_system(self):
|
||||
for i, mol in enumerate(self.system.molecule):
|
||||
logger.info(f"Molecule {i + 1} - {mol.molname}")
|
||||
|
||||
mol.print_mol_info()
|
||||
logger.info(
|
||||
"\n Translating and rotating molecule to standard orientation..."
|
||||
)
|
||||
|
||||
mol.standard_orientation()
|
||||
logger.info("\n Done")
|
||||
logger.info("\nNew values:\n")
|
||||
mol.print_mol_info()
|
||||
|
||||
logger.info("\n")
|
||||
|
||||
def create_simulation_dir(self):
|
||||
simulation_dir_path = Path(self.config.simulation_dir)
|
||||
if simulation_dir_path.exists():
|
||||
raise FileExistsError(
|
||||
f"Error: a file or a directory {self.config.simulation_dir} already exists,"
|
||||
f" move or delete the simfiles directory to continue."
|
||||
)
|
||||
simulation_dir_path.mkdir()
|
||||
|
||||
def create_geoms_file(self):
|
||||
geoms_file_path = Path(self.config.geoms_file)
|
||||
if geoms_file_path.exists():
|
||||
raise FileExistsError(
|
||||
f"Error: a file or a directory {self.config.geoms_file} already exists,"
|
||||
f" move or delete the simfiles directory to continue."
|
||||
)
|
||||
geoms_file_path.touch()
|
||||
|
||||
def print_keywords(self) -> None:
|
||||
def log_keywords(config: Dataclass, dto: Type[Dataclass]):
|
||||
for key in sorted(list(map(lambda f: f.name, fields(dto)))):
|
||||
if getattr(config, key) is not None:
|
||||
if isinstance(getattr(config, key), list):
|
||||
string = " ".join(str(x) for x in getattr(config, key))
|
||||
logger.info(f"{key} = [ {string} ]")
|
||||
else:
|
||||
logger.info(f"{key} = {getattr(config, key)}")
|
||||
|
||||
logger.info(
|
||||
f"##########################################################################################\n"
|
||||
f"############# Welcome to DICEPLAYER version {VERSION} #############\n"
|
||||
f"##########################################################################################\n"
|
||||
)
|
||||
logger.info("Your python version is {}\n".format(sys.version))
|
||||
logger.info("Program started on {}\n".format(weekday_date_time()))
|
||||
logger.info("Environment variables:")
|
||||
for var in ENV:
|
||||
logger.info(
|
||||
"{} = {}\n".format(
|
||||
var, (os.environ[var] if var in os.environ else "Not set")
|
||||
)
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"------------------------------------------------------------------------------------------\n"
|
||||
" DICE variables being used in this run:\n"
|
||||
"------------------------------------------------------------------------------------------\n"
|
||||
)
|
||||
|
||||
log_keywords(self.config.dice, DiceConfig)
|
||||
|
||||
logger.info(
|
||||
"------------------------------------------------------------------------------------------\n"
|
||||
" GAUSSIAN variables being used in this run:\n"
|
||||
"------------------------------------------------------------------------------------------\n"
|
||||
)
|
||||
|
||||
log_keywords(self.config.gaussian, GaussianDTO)
|
||||
|
||||
logger.info("\n")
|
||||
|
||||
def read_potentials(self):
|
||||
ljname_path = Path(self.config.dice.ljname)
|
||||
if ljname_path.exists():
|
||||
with open(self.config.dice.ljname) as file:
|
||||
ljc_data = file.readlines()
|
||||
else:
|
||||
raise RuntimeError(f"Potential file {self.config.dice.ljname} not found.")
|
||||
|
||||
combrule = ljc_data.pop(0).split()[0]
|
||||
if combrule not in ("*", "+"):
|
||||
sys.exit(
|
||||
"Error: expected a '*' or a '+' sign in 1st line of file {}".format(
|
||||
self.config.dice.ljname
|
||||
)
|
||||
)
|
||||
self.config.dice.combrule = combrule
|
||||
|
||||
ntypes = ljc_data.pop(0).split()[0]
|
||||
if not ntypes.isdigit():
|
||||
sys.exit(
|
||||
"Error: expected an integer in the 2nd line of file {}".format(
|
||||
self.config.dice.ljname
|
||||
)
|
||||
)
|
||||
ntypes = int(ntypes)
|
||||
|
||||
if ntypes != len(self.config.dice.nmol):
|
||||
sys.exit(
|
||||
f"Error: number of molecule types in file {self.config.dice.ljname} "
|
||||
f"must match that of 'nmol' keyword in config file"
|
||||
)
|
||||
|
||||
for i in range(ntypes):
|
||||
try:
|
||||
nsites, molname = ljc_data.pop(0).split()[:2]
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Error: expected nsites and molname for the molecule type {i + 1}"
|
||||
)
|
||||
|
||||
if not nsites.isdigit():
|
||||
raise ValueError(
|
||||
f"Error: expected nsites to be an integer for molecule type {i + 1}"
|
||||
)
|
||||
|
||||
nsites = int(nsites)
|
||||
self.system.add_type(Molecule(molname))
|
||||
|
||||
atom_fields = ["lbl", "na", "rx", "ry", "rz", "chg", "eps", "sig"]
|
||||
for j in range(nsites):
|
||||
new_atom = dict(zip(atom_fields, ljc_data.pop(0).split()))
|
||||
self.system.molecule[i].add_atom(
|
||||
Atom(**self.validate_atom_dict(i, j, new_atom))
|
||||
)
|
||||
|
||||
def print_potentials(self) -> None:
|
||||
formatstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f} {:>9.4f}"
|
||||
logger.info(
|
||||
"==========================================================================================\n"
|
||||
f" Potential parameters from file {self.config.dice.ljname}:\n"
|
||||
"------------------------------------------------------------------------------------------"
|
||||
"\n"
|
||||
)
|
||||
|
||||
logger.info(f"Combination rule: {self.config.dice.combrule}")
|
||||
logger.info(f"Types of molecules: {len(self.system.molecule)}\n")
|
||||
|
||||
i = 0
|
||||
for mol in self.system.molecule:
|
||||
i += 1
|
||||
logger.info("{} atoms in molecule type {}:".format(len(mol.atom), i))
|
||||
logger.info(
|
||||
"---------------------------------------------------------------------------------"
|
||||
)
|
||||
logger.info(
|
||||
"Lbl AN X Y Z Charge Epsilon Sigma Mass"
|
||||
)
|
||||
logger.info(
|
||||
"---------------------------------------------------------------------------------"
|
||||
)
|
||||
|
||||
for atom in mol.atom:
|
||||
logger.info(
|
||||
formatstr.format(
|
||||
atom.lbl,
|
||||
atom.na,
|
||||
atom.rx,
|
||||
atom.ry,
|
||||
atom.rz,
|
||||
atom.chg,
|
||||
atom.eps,
|
||||
atom.sig,
|
||||
atom.mass,
|
||||
)
|
||||
)
|
||||
|
||||
logger.info("\n")
|
||||
|
||||
def dice_start(self, cycle: int):
|
||||
self.dice_interface.configure(
|
||||
self.config,
|
||||
self.system,
|
||||
)
|
||||
|
||||
self.dice_interface.start(cycle)
|
||||
|
||||
self.dice_interface.reset()
|
||||
|
||||
def gaussian_start(self, cycle: int):
|
||||
self.gaussian_interface.configure(
|
||||
self.config,
|
||||
self.system,
|
||||
)
|
||||
|
||||
result = self.gaussian_interface.start(cycle)
|
||||
|
||||
self.gaussian_interface.reset()
|
||||
|
||||
if self.config.opt:
|
||||
if "position" not in result:
|
||||
raise RuntimeError("Optimization failed. No position found in result.")
|
||||
|
||||
self.system.update_molecule(result["position"])
|
||||
|
||||
else:
|
||||
if "charges" not in result:
|
||||
raise RuntimeError(
|
||||
"Charges optimization failed. No charges found in result."
|
||||
)
|
||||
|
||||
diff = self.system.molecule[0].update_charges(result["charges"])
|
||||
|
||||
self.system.print_charges_and_dipole(cycle)
|
||||
self.print_geoms(cycle)
|
||||
|
||||
if diff < self.config.gaussian.chg_tol:
|
||||
logger.info(f"Charges converged after {cycle} cycles.")
|
||||
raise StopIteration()
|
||||
|
||||
def print_geoms(self, cycle: int):
|
||||
with open(self.config.geoms_file, "a") as file:
|
||||
file.write(f"Cycle # {cycle}\n")
|
||||
|
||||
for atom in self.system.molecule[0].atom:
|
||||
symbol = atomsymb[atom.na]
|
||||
file.write(
|
||||
f"{symbol:<2s} {atom.rx:>10.6f} {atom.ry:>10.6f} {atom.rz:>10.6f}\n"
|
||||
)
|
||||
|
||||
file.write("\n")
|
||||
|
||||
@staticmethod
|
||||
def validate_atom_dict(molecule_type, molecule_site, atom_dict: dict) -> dict:
|
||||
molecule_type += 1
|
||||
molecule_site += 1
|
||||
|
||||
if len(atom_dict) < 8:
|
||||
raise ValueError(
|
||||
f"Invalid number of fields for site {molecule_site} for molecule type {molecule_type}."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["lbl"] = int(atom_dict["lbl"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid lbl fields for site {molecule_site} for molecule type {molecule_type}."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["na"] = int(atom_dict["na"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid na fields for site {molecule_site} for molecule type {molecule_type}."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["rx"] = float(atom_dict["rx"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid rx fields for site {molecule_site} for molecule type {molecule_type}. "
|
||||
f"Value must be a float."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["ry"] = float(atom_dict["ry"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid ry fields for site {molecule_site} for molecule type {molecule_type}. "
|
||||
f"Value must be a float."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["rz"] = float(atom_dict["rz"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid rz fields for site {molecule_site} for molecule type {molecule_type}. "
|
||||
f"Value must be a float."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["chg"] = float(atom_dict["chg"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid chg fields for site {molecule_site} for molecule type {molecule_type}. "
|
||||
f"Value must be a float."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["eps"] = float(atom_dict["eps"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid eps fields for site {molecule_site} for molecule type {molecule_type}. "
|
||||
f"Value must be a float."
|
||||
)
|
||||
|
||||
try:
|
||||
atom_dict["sig"] = float(atom_dict["sig"])
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
f"Invalid sig fields for site {molecule_site} for molecule type {molecule_type}. "
|
||||
f"Value must be a float."
|
||||
)
|
||||
|
||||
return atom_dict
|
||||
|
||||
def print_results(self):
|
||||
formatstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f} {:>9.4f}"
|
||||
|
||||
mol = self.system.molecule[0]
|
||||
logger.info("{} atoms in molecule type {}:".format(len(mol.atom), 1))
|
||||
logger.info(
|
||||
"---------------------------------------------------------------------------------"
|
||||
)
|
||||
logger.info(
|
||||
"Lbl AN X Y Z Charge Epsilon Sigma Mass"
|
||||
)
|
||||
logger.info(
|
||||
"---------------------------------------------------------------------------------"
|
||||
)
|
||||
|
||||
for atom in mol.atom:
|
||||
logger.info(
|
||||
formatstr.format(
|
||||
atom.lbl,
|
||||
atom.na,
|
||||
atom.rx,
|
||||
atom.ry,
|
||||
atom.rz,
|
||||
atom.chg,
|
||||
atom.eps,
|
||||
atom.sig,
|
||||
atom.mass,
|
||||
)
|
||||
)
|
||||
|
||||
logger.info("\n")
|
||||
|
||||
def save_run_in_pickle(self, cycle):
|
||||
try:
|
||||
with open("latest-step.pkl", "wb") as pickle_file:
|
||||
pickle.dump((self.config, self.system, cycle), pickle_file)
|
||||
except Exception:
|
||||
raise RuntimeError(f"Could not save pickle file latest-step.pkl.")
|
||||
|
||||
@staticmethod
|
||||
def load_run_from_pickle() -> Tuple[PlayerConfig, System, int]:
|
||||
pickle_path = Path("latest-step.pkl")
|
||||
try:
|
||||
with open(pickle_path, "rb") as pickle_file:
|
||||
save = pickle.load(pickle_file)
|
||||
return save[0], save[1], save[2] + 1
|
||||
|
||||
except Exception:
|
||||
raise RuntimeError(f"Could not load pickle file {pickle_path}.")
|
||||
|
||||
@staticmethod
|
||||
def set_config(data: dict) -> PlayerConfig:
|
||||
return PlayerConfig.from_dict(data)
|
||||
|
||||
@staticmethod
|
||||
def read_keywords(infile) -> dict:
|
||||
with open(infile, "r") as yml_file:
|
||||
config = yaml.load(yml_file, Loader=yaml.SafeLoader)
|
||||
|
||||
if "diceplayer" in config:
|
||||
return config.get("diceplayer")
|
||||
|
||||
raise RuntimeError(f"Could not find diceplayer section in {infile}.")
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, infile: str) -> "Player":
|
||||
return cls(infile=infile)
|
||||
|
||||
@classmethod
|
||||
def from_save(cls):
|
||||
return cls(optimization=True)
|
||||
logger.info("Reached maximum number of cycles. Simulation complete.")
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||
|
||||
from dataclasses import dataclass, fields
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass
|
||||
class DiceConfig(Dataclass):
|
||||
"""
|
||||
Data Transfer Object for the Dice configuration.
|
||||
"""
|
||||
|
||||
ljname: str
|
||||
outname: str
|
||||
dens: float
|
||||
nmol: List[int]
|
||||
nstep: List[int]
|
||||
|
||||
upbuf = 360
|
||||
combrule = "*"
|
||||
isave: int = 1000
|
||||
press: float = 1.0
|
||||
temp: float = 300.0
|
||||
progname: str = "dice"
|
||||
randominit: str = "first"
|
||||
|
||||
def __post_init__(self):
|
||||
if not isinstance(self.ljname, str):
|
||||
raise ValueError("Error: 'ljname' keyword not specified in config file")
|
||||
|
||||
if not isinstance(self.outname, str):
|
||||
raise ValueError("Error: 'outname' keyword not specified in config file")
|
||||
|
||||
if not isinstance(self.dens, float):
|
||||
raise ValueError("Error: 'dens' keyword not specified in config file")
|
||||
|
||||
if not isinstance(self.nmol, list):
|
||||
raise ValueError(
|
||||
"Error: 'nmol' keyword not defined appropriately in config file"
|
||||
)
|
||||
|
||||
if not isinstance(self.nstep, list) or len(self.nstep) not in (2, 3):
|
||||
raise ValueError(
|
||||
"Error: 'nstep' keyword not defined appropriately in config file"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, params: dict):
|
||||
params = {f.name: params[f.name] for f in fields(cls) if f.name in params}
|
||||
|
||||
return cls(**params)
|
||||
@@ -1,30 +0,0 @@
|
||||
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||
|
||||
from dataclasses import dataclass, fields
|
||||
|
||||
|
||||
@dataclass
|
||||
class GaussianDTO(Dataclass):
|
||||
"""
|
||||
Data Transfer Object for the Gaussian configuration.
|
||||
"""
|
||||
|
||||
level: str
|
||||
qmprog: str
|
||||
|
||||
chgmult = [0, 1]
|
||||
pop: str = "chelpg"
|
||||
chg_tol: float = 0.01
|
||||
keywords: str = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.qmprog not in ("g03", "g09", "g16"):
|
||||
raise ValueError("Error: invalid qmprog value.")
|
||||
if self.level is None:
|
||||
raise ValueError("Error: 'level' keyword not specified in config file.")
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, params: dict):
|
||||
params = {f.name: params[f.name] for f in fields(cls) if f.name in params}
|
||||
|
||||
return cls(**params)
|
||||
@@ -1,46 +0,0 @@
|
||||
from diceplayer.shared.config.dice_config import DiceConfig
|
||||
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||
from diceplayer.shared.utils.dataclass_protocol import Dataclass
|
||||
|
||||
from dataclasses import dataclass, fields
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlayerConfig(Dataclass):
|
||||
"""
|
||||
Data Transfer Object for the player configuration.
|
||||
"""
|
||||
|
||||
opt: bool
|
||||
maxcyc: int
|
||||
nprocs: int
|
||||
ncores: int
|
||||
|
||||
dice: DiceConfig
|
||||
gaussian: GaussianDTO
|
||||
|
||||
mem: int = None
|
||||
switchcyc: int = 3
|
||||
qmprog: str = "g16"
|
||||
altsteps: int = 20000
|
||||
geoms_file = "geoms.xyz"
|
||||
simulation_dir = "simfiles"
|
||||
|
||||
def __post_init__(self):
|
||||
MIN_STEP = 20000
|
||||
# altsteps value is always the nearest multiple of 1000
|
||||
self.altsteps = round(max(MIN_STEP, self.altsteps) / 1000) * 1000
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, params: dict):
|
||||
if params["dice"] is None:
|
||||
raise ValueError("Error: 'dice' keyword not specified in config file.")
|
||||
params["dice"] = DiceConfig.from_dict(params["dice"])
|
||||
|
||||
if params["gaussian"] is None:
|
||||
raise ValueError("Error: 'gaussian' keyword not specified in config file.")
|
||||
params["gaussian"] = GaussianDTO.from_dict(params["gaussian"])
|
||||
|
||||
params = {f.name: params[f.name] for f in fields(cls) if f.name in params}
|
||||
|
||||
return cls(**params)
|
||||
@@ -1,52 +0,0 @@
|
||||
from diceplayer.shared.utils.ptable import atommass
|
||||
|
||||
|
||||
class Atom:
|
||||
"""
|
||||
Atom class declaration. This class is used throughout the DicePlayer program to represent atoms.
|
||||
|
||||
Atributes:
|
||||
lbl (int): Dice derived variable used to represent atoms with identical energies and simetric positions.
|
||||
na (int): Atomic number of the represented atom.
|
||||
rx (float): x cartesian coordinates of the represented atom.
|
||||
ry (float): y cartesian coordinates of the represented atom.
|
||||
rz (float): z cartesian coordinates of the represented atom.
|
||||
chg (float): charge of the represented atom.
|
||||
eps (float): quantum number epsilon of the represented atom.
|
||||
sig (float): quantum number sigma of the represented atom.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
lbl: int,
|
||||
na: int,
|
||||
rx: float,
|
||||
ry: float,
|
||||
rz: float,
|
||||
chg: float,
|
||||
eps: float,
|
||||
sig: float,
|
||||
) -> None:
|
||||
"""
|
||||
The constructor function __init__ is used to create new instances of the Atom class.
|
||||
|
||||
Args:
|
||||
lbl (int): Dice derived variable used to represent atoms with identical energies and simetric positions.
|
||||
na (int): Atomic number of the represented atom.
|
||||
rx (float): x cartesian coordinates of the represented atom.
|
||||
ry (float): y cartesian coordinates of the represented atom.
|
||||
rz (float): z cartesian coordinates of the represented atom.
|
||||
chg (float): charge of the represented atom.
|
||||
eps (float): quantum number epsilon of the represented atom.
|
||||
sig (float): quantum number sigma of the represented atom.
|
||||
"""
|
||||
|
||||
self.lbl = lbl
|
||||
self.na = na
|
||||
self.rx = rx
|
||||
self.ry = ry
|
||||
self.rz = rz
|
||||
self.chg = chg
|
||||
self.eps = eps
|
||||
self.sig = sig
|
||||
self.mass = atommass[self.na]
|
||||
@@ -1,381 +0,0 @@
|
||||
# from __future__ import annotations
|
||||
|
||||
import numpy.typing as npt
|
||||
|
||||
from diceplayer import logger
|
||||
from diceplayer.shared.environment.atom import Atom
|
||||
from diceplayer.shared.utils.misc import BOHR2ANG
|
||||
from diceplayer.shared.utils.ptable import ghost_number
|
||||
|
||||
import numpy as np
|
||||
from numpy.linalg import linalg
|
||||
|
||||
import math
|
||||
from copy import deepcopy
|
||||
from typing import Any, List, Tuple, Union
|
||||
|
||||
|
||||
class Molecule:
|
||||
"""
|
||||
Molecule class declaration. This class is used throughout the DicePlayer program to represent molecules.
|
||||
|
||||
Atributes:
|
||||
molname (str): The name of the represented molecule
|
||||
atom (List[Atom]): List of atoms of the represented molecule
|
||||
position (npt.NDArray[Any, Any]): The position relative to the internal atoms of the represented molecule
|
||||
energy (npt.NDArray[Any, Any]): The energy of the represented molecule
|
||||
gradient (npt.NDArray[Any, Any]): The first derivative of the energy relative to the position
|
||||
hessian (npt.NDArray[Any, Any]): The second derivative of the energy relative to the position
|
||||
total_mass (int): The total mass of the molecule
|
||||
com (npt.NDArray[Any, Any]): The center of mass of the molecule
|
||||
"""
|
||||
|
||||
def __init__(self, molname: str) -> None:
|
||||
"""
|
||||
The constructor function __init__ is used to create new instances of the Molecule class.
|
||||
|
||||
Args:
|
||||
molname (str): Molecule name
|
||||
"""
|
||||
self.molname: str = molname
|
||||
|
||||
self.atom: List[Atom] = []
|
||||
self.position: npt.NDArray[Any, Any]
|
||||
self.energy: npt.NDArray[Any, Any]
|
||||
self.gradient: npt.NDArray[Any, Any]
|
||||
self.hessian: npt.NDArray[Any, Any]
|
||||
|
||||
self.ghost_atoms: List[Atom] = []
|
||||
self.lp_atoms: List[Atom] = []
|
||||
|
||||
self.total_mass: int = 0
|
||||
self.com: Union[None, npt.NDArray[Any, Any]] = None
|
||||
|
||||
def add_atom(self, a: Atom) -> None:
|
||||
"""
|
||||
Adds Atom instance to the molecule.
|
||||
|
||||
Args:
|
||||
a (Atom): Atom instance to be added to atom list.
|
||||
"""
|
||||
|
||||
self.atom.append(a)
|
||||
self.total_mass += a.mass
|
||||
|
||||
self.center_of_mass()
|
||||
|
||||
def center_of_mass(self) -> npt.NDArray[Any]:
|
||||
"""
|
||||
Calculates the center of mass of the molecule
|
||||
"""
|
||||
|
||||
self.com = np.zeros(3)
|
||||
|
||||
for atom in self.atom:
|
||||
self.com += atom.mass * np.array([atom.rx, atom.ry, atom.rz])
|
||||
|
||||
self.com = self.com / self.total_mass
|
||||
|
||||
return self.com
|
||||
|
||||
def center_of_mass_to_origin(self) -> None:
|
||||
"""
|
||||
Updated positions based on the center of mass of the molecule
|
||||
"""
|
||||
for atom in self.atom:
|
||||
atom.rx -= self.com[0]
|
||||
atom.ry -= self.com[1]
|
||||
atom.rz -= self.com[2]
|
||||
|
||||
self.center_of_mass()
|
||||
|
||||
def charges_and_dipole(self) -> List[float]:
|
||||
"""
|
||||
Calculates the charges and dipole of the molecule atoms
|
||||
|
||||
Returns:
|
||||
List[npt.Float]: Respectivly magnitude of the: charge magnitude, first dipole,
|
||||
second dipole, third dipole and total dipole.
|
||||
"""
|
||||
|
||||
eA_to_Debye = 1 / 0.20819434
|
||||
charge = 0
|
||||
dipole = np.zeros(3)
|
||||
for atom in self.atom:
|
||||
position = np.array([atom.rx, atom.ry, atom.rz])
|
||||
dipole += atom.chg * position
|
||||
charge += atom.chg
|
||||
|
||||
dipole *= eA_to_Debye
|
||||
total_dipole = math.sqrt(dipole[0] ** 2 + dipole[1] ** 2 + dipole[2] ** 2)
|
||||
|
||||
return [charge, dipole[0], dipole[1], dipole[2], total_dipole]
|
||||
|
||||
def distances_between_atoms(self) -> npt.NDArray[np.float64]:
|
||||
"""
|
||||
Calculates distances between the atoms of the molecule
|
||||
|
||||
Returns:
|
||||
npt.NDArray[npt.Shape["Any,Any"],npt.Float]: distances between the atoms.
|
||||
"""
|
||||
|
||||
distances = []
|
||||
dim = len(self.atom)
|
||||
for index1, atom1 in enumerate(self.atom):
|
||||
for index2, atom2 in enumerate(self.atom):
|
||||
if index1 != index2:
|
||||
dx = atom1.rx - atom2.rx
|
||||
dy = atom1.ry - atom2.ry
|
||||
dz = atom1.rz - atom2.rz
|
||||
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
|
||||
|
||||
return np.array(distances).reshape(dim, dim - 1)
|
||||
|
||||
def inertia_tensor(self) -> npt.NDArray[np.float64]:
|
||||
"""
|
||||
Calculates the inertia tensor of the molecule.
|
||||
|
||||
Returns:
|
||||
npt.NDArray[npt.Shape["3, 3"], npt.Float]: inertia tensor of the molecule.
|
||||
"""
|
||||
|
||||
self.center_of_mass()
|
||||
Ixx = Ixy = Ixz = Iyy = Iyz = Izz = 0.0
|
||||
|
||||
for atom in self.atom:
|
||||
dx = atom.rx - self.com[0]
|
||||
dy = atom.ry - self.com[1]
|
||||
dz = atom.rz - self.com[2]
|
||||
|
||||
Ixx += atom.mass * (dy**2 + dz**2)
|
||||
Iyy += atom.mass * (dz**2 + dx**2)
|
||||
Izz += atom.mass * (dx**2 + dy**2)
|
||||
|
||||
Ixy += atom.mass * dx * dy * -1
|
||||
Ixz += atom.mass * dx * dz * -1
|
||||
Iyz += atom.mass * dy * dz * -1
|
||||
|
||||
return np.array([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]])
|
||||
|
||||
def principal_axes(self) -> Tuple[npt.NDArray, npt.NDArray]:
|
||||
"""
|
||||
Calculates the principal axes of the molecule
|
||||
|
||||
Returns:
|
||||
Tuple[npt.NDArray, npt.NDArray]: Tuple where the first value is the Eigen Values and the second is the Eigen Vectors,
|
||||
representing the principal axes of the molecule.
|
||||
"""
|
||||
|
||||
try:
|
||||
evals, evecs = linalg.eigh(self.inertia_tensor())
|
||||
except ValueError:
|
||||
raise RuntimeError(
|
||||
"Error: diagonalization of inertia tensor did not converge"
|
||||
)
|
||||
|
||||
return evals, evecs
|
||||
|
||||
def read_position(self) -> npt.NDArray:
|
||||
"""Reads the position of the molecule from the position values of the atoms
|
||||
|
||||
Returns:
|
||||
npt.NDArray: internal position relative to atoms of the molecule
|
||||
"""
|
||||
|
||||
position_list = []
|
||||
for atom in self.atom:
|
||||
position_list.extend([atom.rx, atom.ry, atom.rz])
|
||||
position = np.array(position_list)
|
||||
position *= BOHR2ANG
|
||||
|
||||
return position
|
||||
|
||||
def update_charges(self, charges: npt.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: npt.NDArray,
|
||||
# cur_gradient: npt.NDArray,
|
||||
# old_gradient: npt.NDArray,
|
||||
# hessian: npt.NDArray,
|
||||
# ) -> npt.NDArray:
|
||||
# """
|
||||
# Updates the Hessian of the molecule based on the current hessian, the current gradient and the previous gradient
|
||||
#
|
||||
# Args:
|
||||
# step (npt.NDArray): step value of the iteration
|
||||
# cur_gradient (npt.NDArray): current gradient
|
||||
# old_gradient (npt.NDArray): previous gradient
|
||||
# hessian (npt.NDArray): current hessian
|
||||
#
|
||||
# Returns:
|
||||
# npt.NDArray: updated hessian of the molecule
|
||||
# """
|
||||
#
|
||||
# dif_gradient = cur_gradient - old_gradient
|
||||
#
|
||||
# mat1 = 1 / np.dot(dif_gradient, step) * np.matmul(dif_gradient.T, dif_gradient)
|
||||
# mat2 = 1 / np.dot(step, np.matmul(hessian, step.T).T)
|
||||
# mat2 *= np.matmul(np.matmul(hessian, step.T), np.matmul(step, hessian))
|
||||
#
|
||||
# return hessian + mat1 - mat2
|
||||
|
||||
def sizes_of_molecule(self) -> List[float]:
|
||||
"""
|
||||
Calculates sides of the smallest box that the molecule could fit
|
||||
|
||||
Returns:
|
||||
List[npt.Float]: list of the sizes of the molecule
|
||||
"""
|
||||
|
||||
x_list = []
|
||||
y_list = []
|
||||
z_list = []
|
||||
|
||||
for atom in self.atom:
|
||||
x_list.append(atom.rx)
|
||||
y_list.append(atom.ry)
|
||||
z_list.append(atom.rz)
|
||||
|
||||
x_max = max(x_list)
|
||||
x_min = min(x_list)
|
||||
y_max = max(y_list)
|
||||
y_min = min(y_list)
|
||||
z_max = max(z_list)
|
||||
z_min = min(z_list)
|
||||
|
||||
sizes = [x_max - x_min, y_max - y_min, z_max - z_min]
|
||||
|
||||
return sizes
|
||||
|
||||
def standard_orientation(self) -> None:
|
||||
"""
|
||||
Rotates the molecule to the standard orientation
|
||||
"""
|
||||
|
||||
self.center_of_mass_to_origin()
|
||||
evals, evecs = self.principal_axes()
|
||||
|
||||
if round(linalg.det(evecs)) == -1:
|
||||
evecs[0, 2] *= -1
|
||||
evecs[1, 2] *= -1
|
||||
evecs[2, 2] *= -1
|
||||
|
||||
if round(linalg.det(evecs)) != 1:
|
||||
raise RuntimeError(
|
||||
"Error: could not make a rotation matrix while adopting the standard orientation"
|
||||
)
|
||||
|
||||
rot_matrix = evecs.T
|
||||
|
||||
for atom in self.atom:
|
||||
position = np.array([atom.rx, atom.ry, atom.rz])
|
||||
new_position = np.matmul(rot_matrix, position.T).T
|
||||
|
||||
atom.rx = new_position[0]
|
||||
atom.ry = new_position[1]
|
||||
atom.rz = new_position[2]
|
||||
|
||||
def translate(self, vector: npt.NDArray) -> "Molecule":
|
||||
"""
|
||||
Creates a new Molecule object where its' atoms has been translated by a vector
|
||||
|
||||
Args:
|
||||
vector (npt.NDArray): translation vector
|
||||
|
||||
Returns:
|
||||
Molecule: new Molecule object translated by a vector
|
||||
"""
|
||||
|
||||
new_molecule = deepcopy(self)
|
||||
|
||||
for atom in new_molecule.atom:
|
||||
atom.rx += vector[0]
|
||||
atom.ry += vector[1]
|
||||
atom.rz += vector[2]
|
||||
|
||||
return new_molecule
|
||||
|
||||
def print_mol_info(self) -> None:
|
||||
"""
|
||||
Prints the Molecule information into a Output File
|
||||
"""
|
||||
|
||||
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()
|
||||
|
||||
logger.info(
|
||||
" Moments of inertia = {:>9E} {:>9E} {:>9E}".format(
|
||||
evals[0], evals[1], evals[2]
|
||||
)
|
||||
)
|
||||
|
||||
logger.info(
|
||||
" Major principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format(
|
||||
evecs[0, 0], evecs[1, 0], evecs[2, 0]
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
" Inter principal axis = ( {:>10.6f} , {:>10.6f} , {:>10.6f} )".format(
|
||||
evecs[0, 1], evecs[1, 1], evecs[2, 1]
|
||||
)
|
||||
)
|
||||
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()
|
||||
logger.info(
|
||||
" Characteristic lengths = ( {:>6.2f} , {:>6.2f} , {:>6.2f} )".format(
|
||||
sizes[0], sizes[1], sizes[2]
|
||||
)
|
||||
)
|
||||
logger.info(" Total mass = {:>8.2f} au".format(self.total_mass))
|
||||
|
||||
chg_dip = self.charges_and_dipole()
|
||||
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]
|
||||
)
|
||||
)
|
||||
|
||||
def minimum_distance(self, molec: "Molecule") -> float:
|
||||
"""
|
||||
Return the minimum distance between two molecules
|
||||
|
||||
Args:
|
||||
molec (Molecule): Molecule object to be compared
|
||||
|
||||
Returns:
|
||||
npt.Float: minimum distance between the two molecules
|
||||
"""
|
||||
|
||||
distances = []
|
||||
for atom1 in self.atom:
|
||||
if atom1.na != ghost_number:
|
||||
for atom2 in molec.atom:
|
||||
if atom2.na != ghost_number:
|
||||
dx = atom1.rx - atom2.rx
|
||||
dy = atom1.ry - atom2.ry
|
||||
dz = atom1.rz - atom2.rz
|
||||
distances.append(math.sqrt(dx**2 + dy**2 + dz**2))
|
||||
|
||||
return min(distances)
|
||||
@@ -1,237 +0,0 @@
|
||||
from diceplayer import logger
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
from diceplayer.shared.utils.misc import BOHR2ANG
|
||||
from diceplayer.shared.utils.ptable import atomsymb
|
||||
|
||||
import numpy as np
|
||||
from numpy import linalg
|
||||
|
||||
import math
|
||||
from copy import deepcopy
|
||||
from typing import List, TextIO, Tuple
|
||||
|
||||
|
||||
class System:
|
||||
"""
|
||||
System class declaration. This class is used throughout the DicePlayer program to represent the system containing the molecules.
|
||||
|
||||
Atributes:
|
||||
molecule (List[Molecule]): List of molecules of the system
|
||||
nmols (List[int]): List of number of molecules in the system
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Initializes an empty system object that will be populated afterwards
|
||||
"""
|
||||
self.nmols: List[int] = []
|
||||
self.molecule: List[Molecule] = []
|
||||
|
||||
def add_type(self, m: Molecule) -> None:
|
||||
"""
|
||||
Adds a new molecule type to the system
|
||||
|
||||
Args:
|
||||
m (Molecule): The instance of the new type of molecule
|
||||
"""
|
||||
if isinstance(m, Molecule) is False:
|
||||
raise TypeError("Error: molecule is not a Molecule instance")
|
||||
self.molecule.append(m)
|
||||
|
||||
def update_molecule(self, position: np.ndarray) -> None:
|
||||
"""Updates the position of the molecule in the Output file
|
||||
|
||||
Args:
|
||||
position (np.ndarray): numpy position vector
|
||||
"""
|
||||
|
||||
position_in_ang = (position * BOHR2ANG).tolist()
|
||||
self.add_type(deepcopy(self.molecule[0]))
|
||||
|
||||
for atom in self.molecule[-1].atom:
|
||||
atom.rx = position_in_ang.pop(0)
|
||||
atom.ry = position_in_ang.pop(0)
|
||||
atom.rz = position_in_ang.pop(0)
|
||||
|
||||
rmsd, self.molecule[0] = self.rmsd_fit(-1, 0)
|
||||
self.molecule.pop(-1)
|
||||
|
||||
logger.info("Projected new conformation of reference molecule with RMSD fit")
|
||||
logger.info(f"RMSD = {rmsd:>8.5f} Angstrom")
|
||||
|
||||
def rmsd_fit(self, p_index: int, r_index: int) -> Tuple[float, Molecule]:
|
||||
projecting_mol = self.molecule[p_index]
|
||||
reference_mol = self.molecule[r_index]
|
||||
|
||||
if len(projecting_mol.atom) != len(reference_mol.atom):
|
||||
raise RuntimeError(
|
||||
"Error in RMSD fit procedure: molecules have different number of atoms"
|
||||
)
|
||||
dim = len(projecting_mol.atom)
|
||||
|
||||
new_projecting_mol = deepcopy(projecting_mol)
|
||||
new_reference_mol = deepcopy(reference_mol)
|
||||
|
||||
new_projecting_mol.center_of_mass_to_origin()
|
||||
new_reference_mol.center_of_mass_to_origin()
|
||||
|
||||
x = []
|
||||
y = []
|
||||
|
||||
for atom in new_projecting_mol.atom:
|
||||
x.extend([atom.rx, atom.ry, atom.rz])
|
||||
|
||||
for atom in new_reference_mol.atom:
|
||||
y.extend([atom.rx, atom.ry, atom.rz])
|
||||
|
||||
x = np.array(x).reshape(dim, 3)
|
||||
y = np.array(y).reshape(dim, 3)
|
||||
|
||||
r = np.matmul(y.T, x)
|
||||
rr = np.matmul(r.T, r)
|
||||
|
||||
try:
|
||||
evals, evecs = linalg.eigh(rr)
|
||||
except:
|
||||
raise RuntimeError("Error: diagonalization of RR matrix did not converge")
|
||||
|
||||
a1 = evecs[:, 2].T
|
||||
a2 = evecs[:, 1].T
|
||||
a3 = np.cross(a1, a2)
|
||||
|
||||
A = np.array([a1[0], a1[1], a1[2], a2[0], a2[1], a2[2], a3[0], a3[1], a3[2]])
|
||||
A = A.reshape(3, 3)
|
||||
|
||||
b1 = np.matmul(r, a1.T).T # or np.dot(r, a1)
|
||||
b1 /= linalg.norm(b1)
|
||||
b2 = np.matmul(r, a2.T).T # or np.dot(r, a2)
|
||||
b2 /= linalg.norm(b2)
|
||||
b3 = np.cross(b1, b2)
|
||||
|
||||
B = np.array([b1[0], b1[1], b1[2], b2[0], b2[1], b2[2], b3[0], b3[1], b3[2]])
|
||||
B = B.reshape(3, 3).T
|
||||
|
||||
rot_matrix = np.matmul(B, A)
|
||||
x = np.matmul(rot_matrix, x.T).T
|
||||
|
||||
rmsd = 0
|
||||
for i in range(dim):
|
||||
rmsd += (
|
||||
(x[i, 0] - y[i, 0]) ** 2
|
||||
+ (x[i, 1] - y[i, 1]) ** 2
|
||||
+ (x[i, 2] - y[i, 2]) ** 2
|
||||
)
|
||||
rmsd = math.sqrt(rmsd / dim)
|
||||
|
||||
for i in range(dim):
|
||||
new_projecting_mol.atom[i].rx = x[i, 0]
|
||||
new_projecting_mol.atom[i].ry = x[i, 1]
|
||||
new_projecting_mol.atom[i].rz = x[i, 2]
|
||||
|
||||
reference_mol.center_of_mass()
|
||||
|
||||
projected_mol = new_projecting_mol.translate(reference_mol.com)
|
||||
|
||||
return rmsd, projected_mol
|
||||
|
||||
# def center_of_mass_distance(self, a: int, b: int) -> float:
|
||||
# """
|
||||
# Calculates the distance between the center of mass of two molecules
|
||||
#
|
||||
# Args:
|
||||
# a (Molecule): First Molecule Instance
|
||||
# b (Molecule): Second Molecule Instance
|
||||
#
|
||||
# Returns:
|
||||
# float: module of the distance between the two center of masses
|
||||
# """
|
||||
#
|
||||
# com1 = self.molecule[a].center_of_mass()
|
||||
# com2 = self.molecule[b].center_of_mass()
|
||||
# dx = com1[0] - com2[0]
|
||||
# dy = com1[1] - com2[1]
|
||||
# dz = com1[2] - com2[2]
|
||||
# distance = math.sqrt(dx**2 + dy**2 + dz**2)
|
||||
#
|
||||
# return distance
|
||||
|
||||
# def nearest_image(
|
||||
# self,
|
||||
# index_r: int,
|
||||
# index_m: int,
|
||||
# lx: float,
|
||||
# ly: float,
|
||||
# lz: float,
|
||||
# criterium=None,
|
||||
# ) -> Tuple[float, Molecule]:
|
||||
#
|
||||
# if criterium in None:
|
||||
# criterium = "com"
|
||||
#
|
||||
# if criterium != "com" and criterium != "min":
|
||||
# raise RuntimeError("Error in value passed to function nearest_image")
|
||||
#
|
||||
# min_dist = 1e20
|
||||
#
|
||||
# for i in range(-1, 2):
|
||||
# for j in range(-1, 2):
|
||||
# for k in range(-1, 2):
|
||||
#
|
||||
# tr_vector = [i * lx, j * ly, k * lz]
|
||||
# self.add_molecule(self.molecule[index_m].translate(tr_vector))
|
||||
#
|
||||
# if criterium == "com":
|
||||
# dist = self.center_of_mass_distance(index_r, -1)
|
||||
# else:
|
||||
# dist = self.minimum_distance(index_r, -1)
|
||||
#
|
||||
# if dist < min_dist:
|
||||
# min_dist = dist
|
||||
# nearestmol = deepcopy(self.molecule[-1])
|
||||
#
|
||||
# self.molecule.pop(-1)
|
||||
#
|
||||
# return min_dist, nearestmol
|
||||
|
||||
# def print_geom(self, cycle: int, fh: TextIO) -> None:
|
||||
# """
|
||||
# Print the geometry of the molecule in the Output file
|
||||
#
|
||||
# Args:
|
||||
# cycle (int): Number of the cycle
|
||||
# fh (TextIO): Output file
|
||||
# """
|
||||
#
|
||||
# fh.write("Cycle # {}\n".format(cycle))
|
||||
# fh.write("Number of site: {}\n".format(len(self.molecule[0].atom)))
|
||||
# for atom in self.molecule[0].atom:
|
||||
# symbol = atomsymb[atom.na]
|
||||
# fh.write(
|
||||
# "{:<2s} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(
|
||||
# symbol, atom.rx, atom.ry, atom.rz
|
||||
# )
|
||||
# )
|
||||
#
|
||||
def print_charges_and_dipole(self, cycle: int) -> None:
|
||||
"""
|
||||
Print the charges and dipole of the molecule in the Output file
|
||||
|
||||
Args:
|
||||
cycle (int): Number of the cycle
|
||||
fh (TextIO): Output file
|
||||
"""
|
||||
|
||||
logger.info("Cycle # {}\n".format(cycle))
|
||||
logger.info("Number of site: {}\n".format(len(self.molecule[0].atom)))
|
||||
|
||||
chargesAndDipole = self.molecule[0].charges_and_dipole()
|
||||
|
||||
logger.info(
|
||||
"{:>10.6f} {:>10.6f} {:>10.6f} {:>10.6f} {:>10.6f}\n".format(
|
||||
chargesAndDipole[0],
|
||||
chargesAndDipole[1],
|
||||
chargesAndDipole[2],
|
||||
chargesAndDipole[3],
|
||||
chargesAndDipole[4],
|
||||
)
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
from .__interface import Interface
|
||||
@@ -1,26 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from diceplayer.shared.config.player_config import PlayerConfig
|
||||
from diceplayer.shared.environment.system import System
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class Interface(ABC):
|
||||
__slots__ = ["step", "system"]
|
||||
|
||||
def __init__(self):
|
||||
self.system: System | None = None
|
||||
self.step: PlayerConfig | None = None
|
||||
|
||||
@abstractmethod
|
||||
def configure(self, step: PlayerConfig, system: System):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def start(self, cycle: int):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def reset(self):
|
||||
pass
|
||||
@@ -1,387 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from diceplayer import logger
|
||||
from diceplayer.shared.config.player_config import PlayerConfig
|
||||
from diceplayer.shared.environment.system import System
|
||||
from diceplayer.shared.interface import Interface
|
||||
|
||||
from setproctitle import setproctitle
|
||||
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from multiprocessing import Process, connection
|
||||
from pathlib import Path
|
||||
from typing import Final, TextIO
|
||||
|
||||
DICE_END_FLAG: Final[str] = "End of simulation"
|
||||
DICE_FLAG_LINE: Final[int] = -2
|
||||
UMAANG3_TO_GCM3: Final[float] = 1.6605
|
||||
|
||||
MAX_SEED: Final[int] = 4294967295
|
||||
|
||||
|
||||
class DiceInterface(Interface):
|
||||
title = "Diceplayer run"
|
||||
|
||||
def configure(self, step: PlayerConfig, system: System):
|
||||
self.step = step
|
||||
self.system = system
|
||||
|
||||
def start(self, cycle: int):
|
||||
procs = []
|
||||
sentinels = []
|
||||
|
||||
for proc in range(1, self.step.nprocs + 1):
|
||||
p = Process(target=self._simulation_process, args=(cycle, proc))
|
||||
p.start()
|
||||
|
||||
procs.append(p)
|
||||
sentinels.append(p.sentinel)
|
||||
|
||||
while procs:
|
||||
finished = connection.wait(sentinels)
|
||||
for proc_sentinel in finished:
|
||||
i = sentinels.index(proc_sentinel)
|
||||
status = procs[i].exitcode
|
||||
procs.pop(i)
|
||||
sentinels.pop(i)
|
||||
if status != 0:
|
||||
for p in procs:
|
||||
p.terminate()
|
||||
sys.exit(status)
|
||||
|
||||
logger.info("\n")
|
||||
|
||||
def reset(self):
|
||||
del self.step
|
||||
del self.system
|
||||
|
||||
def _simulation_process(self, cycle: int, proc: int):
|
||||
setproctitle(f"diceplayer-step{cycle:0d}-p{proc:0d}")
|
||||
|
||||
try:
|
||||
self._make_proc_dir(cycle, proc)
|
||||
self._make_dice_inputs(cycle, proc)
|
||||
self._run_dice(cycle, proc)
|
||||
except Exception as err:
|
||||
sys.exit(err)
|
||||
|
||||
def _make_proc_dir(self, cycle, proc):
|
||||
simulation_dir = Path(self.step.simulation_dir)
|
||||
if not simulation_dir.exists():
|
||||
simulation_dir.mkdir(parents=True)
|
||||
|
||||
proc_dir = Path(simulation_dir, f"step{cycle:02d}", f"p{proc:02d}")
|
||||
proc_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def _make_dice_inputs(self, cycle, proc):
|
||||
proc_dir = Path(self.step.simulation_dir, f"step{cycle:02d}", f"p{proc:02d}")
|
||||
|
||||
self._make_potentials(proc_dir)
|
||||
|
||||
random.seed(self._make_dice_seed())
|
||||
|
||||
# This is logic is used to make the initial configuration file
|
||||
# for the next cycle using the last.xyz file from the previous cycle.
|
||||
if self.step.dice.randominit == "first" and cycle > 1:
|
||||
last_xyz = Path(
|
||||
self.step.simulation_dir,
|
||||
f"step{(cycle - 1):02d}",
|
||||
f"p{proc:02d}",
|
||||
"last.xyz",
|
||||
)
|
||||
if not last_xyz.exists():
|
||||
raise FileNotFoundError(f"File {last_xyz} not found.")
|
||||
|
||||
with open(last_xyz, "r") as last_xyz_file:
|
||||
self._make_init_file(proc_dir, last_xyz_file)
|
||||
last_xyz_file.seek(0)
|
||||
self.step.dice.dens = self._new_density(last_xyz_file)
|
||||
|
||||
else:
|
||||
self._make_nvt_ter(cycle, proc_dir)
|
||||
|
||||
if len(self.step.dice.nstep) == 2:
|
||||
self._make_nvt_eq(cycle, proc_dir)
|
||||
|
||||
elif len(self.step.dice.nstep) == 3:
|
||||
self._make_npt_ter(cycle, proc_dir)
|
||||
self._make_npt_eq(proc_dir)
|
||||
|
||||
def _run_dice(self, cycle: int, proc: int):
|
||||
working_dir = os.getcwd()
|
||||
|
||||
proc_dir = Path(self.step.simulation_dir, f"step{cycle:02d}", f"p{proc:02d}")
|
||||
|
||||
logger.info(
|
||||
f"Simulation process {str(proc_dir)} initiated with pid {os.getpid()}"
|
||||
)
|
||||
|
||||
os.chdir(proc_dir)
|
||||
|
||||
if not (self.step.dice.randominit == "first" and cycle > 1):
|
||||
self.run_dice_file(cycle, proc, "NVT.ter")
|
||||
|
||||
if len(self.step.dice.nstep) == 2:
|
||||
self.run_dice_file(cycle, proc, "NVT.eq")
|
||||
|
||||
elif len(self.step.dice.nstep) == 3:
|
||||
self.run_dice_file(cycle, proc, "NPT.ter")
|
||||
self.run_dice_file(cycle, proc, "NPT.eq")
|
||||
|
||||
os.chdir(working_dir)
|
||||
|
||||
xyz_file = Path(proc_dir, "phb.xyz")
|
||||
last_xyz_file = Path(proc_dir, "last.xyz")
|
||||
|
||||
if xyz_file.exists():
|
||||
shutil.copy(xyz_file, last_xyz_file)
|
||||
else:
|
||||
raise FileNotFoundError(f"File {xyz_file} not found.")
|
||||
|
||||
@staticmethod
|
||||
def _make_dice_seed() -> int:
|
||||
num = time.time()
|
||||
num = (num - int(num)) * 1e6
|
||||
|
||||
num = int((num - int(num)) * 1e6)
|
||||
|
||||
return (os.getpid() * num) % (MAX_SEED + 1)
|
||||
|
||||
def _make_init_file(self, proc_dir: Path, last_xyz_file: TextIO):
|
||||
xyz_lines = last_xyz_file.readlines()
|
||||
|
||||
SECONDARY_MOLECULE_LENGTH = 0
|
||||
for i in range(1, len(self.step.dice.nmol)):
|
||||
SECONDARY_MOLECULE_LENGTH += self.step.dice.nmol[i] * len(
|
||||
self.system.molecule[i].atom
|
||||
)
|
||||
|
||||
xyz_lines = xyz_lines[-SECONDARY_MOLECULE_LENGTH:]
|
||||
|
||||
input_file = Path(proc_dir, self.step.dice.outname + ".xy")
|
||||
with open(input_file, "w") as f:
|
||||
for atom in self.system.molecule[0].atom:
|
||||
f.write(f"{atom.rx:>10.6f} {atom.ry:>10.6f} {atom.rz:>10.6f}\n")
|
||||
|
||||
for line in xyz_lines:
|
||||
atom = line.split()
|
||||
rx = float(atom[1])
|
||||
ry = float(atom[2])
|
||||
rz = float(atom[3])
|
||||
f.write(f"{rx:>10.6f} {ry:>10.6f} {rz:>10.6f}\n")
|
||||
|
||||
f.write("$end")
|
||||
|
||||
def _new_density(self, last_xyz_file: TextIO):
|
||||
last_xyz_lines = last_xyz_file.readlines()
|
||||
|
||||
box = last_xyz_lines[1].split()
|
||||
volume = float(box[-3]) * float(box[-2]) * float(box[-1])
|
||||
|
||||
total_mass = 0
|
||||
for i in range(len(self.system.molecule)):
|
||||
total_mass += self.system.molecule[i].total_mass * self.step.dice.nmol[i]
|
||||
|
||||
density = (total_mass / volume) * UMAANG3_TO_GCM3
|
||||
|
||||
return density
|
||||
|
||||
def _make_nvt_ter(self, cycle, proc_dir):
|
||||
file = Path(proc_dir, "NVT.ter")
|
||||
with open(file, "w") as f:
|
||||
f.write(f"title = {self.title} - NVT Thermalization\n")
|
||||
f.write(f"ncores = {self.step.ncores}\n")
|
||||
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||
f.write(f"outname = {self.step.dice.outname}\n")
|
||||
|
||||
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||
f.write(f"nmol = {mol_string}\n")
|
||||
|
||||
f.write(f"dens = {self.step.dice.dens}\n")
|
||||
f.write(f"temp = {self.step.dice.temp}\n")
|
||||
|
||||
if self.step.dice.randominit == "first" and cycle > 1:
|
||||
f.write(f"init = yesreadxyz\n")
|
||||
f.write(f"nstep = {self.step.altsteps}\n")
|
||||
else:
|
||||
f.write(f"init = yes\n")
|
||||
f.write(f"nstep = {self.step.dice.nstep[0]}\n")
|
||||
|
||||
f.write("vstep = 0\n")
|
||||
f.write("mstop = 1\n")
|
||||
f.write("accum = no\n")
|
||||
f.write("iprint = 1\n")
|
||||
f.write("isave = 0\n")
|
||||
f.write("irdf = 0\n")
|
||||
|
||||
seed = int(1e6 * random.random())
|
||||
f.write(f"seed = {seed}\n")
|
||||
f.write(f"upbuf = {self.step.dice.upbuf}")
|
||||
|
||||
def _make_nvt_eq(self, cycle, proc_dir):
|
||||
file = Path(proc_dir, "NVT.eq")
|
||||
with open(file, "w") as f:
|
||||
f.write(f"title = {self.title} - NVT Production\n")
|
||||
f.write(f"ncores = {self.step.ncores}\n")
|
||||
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||
f.write(f"outname = {self.step.dice.outname}\n")
|
||||
|
||||
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||
f.write(f"nmol = {mol_string}\n")
|
||||
|
||||
f.write(f"dens = {self.step.dice.dens}\n")
|
||||
f.write(f"temp = {self.step.dice.temp}\n")
|
||||
|
||||
if self.step.dice.randominit == "first" and cycle > 1:
|
||||
f.write("init = yesreadxyz\n")
|
||||
else:
|
||||
f.write("init = no\n")
|
||||
|
||||
f.write(f"nstep = {self.step.dice.nstep[1]}\n")
|
||||
|
||||
f.write("vstep = 0\n")
|
||||
f.write("mstop = 1\n")
|
||||
f.write("accum = no\n")
|
||||
f.write("iprint = 1\n")
|
||||
|
||||
f.write(f"isave = {self.step.dice.isave}\n")
|
||||
f.write(f"irdf = {10 * self.step.nprocs}\n")
|
||||
|
||||
seed = int(1e6 * random.random())
|
||||
f.write("seed = {}\n".format(seed))
|
||||
|
||||
def _make_npt_ter(self, cycle, proc_dir):
|
||||
file = Path(proc_dir, "NPT.ter")
|
||||
with open(file, "w") as f:
|
||||
f.write(f"title = {self.title} - NPT Thermalization\n")
|
||||
f.write(f"ncores = {self.step.ncores}\n")
|
||||
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||
f.write(f"outname = {self.step.dice.outname}\n")
|
||||
|
||||
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||
f.write(f"nmol = {mol_string}\n")
|
||||
|
||||
f.write(f"press = {self.step.dice.press}\n")
|
||||
f.write(f"temp = {self.step.dice.temp}\n")
|
||||
|
||||
if self.step.dice.randominit == "first" and cycle > 1:
|
||||
f.write("init = yesreadxyz\n")
|
||||
f.write(f"dens = {self.step.dice.dens:<8.4f}\n")
|
||||
f.write(f"vstep = {int(self.step.altsteps / 5)}\n")
|
||||
else:
|
||||
f.write("init = no\n")
|
||||
f.write(f"vstep = {int(self.step.dice.nstep[1] / 5)}\n")
|
||||
|
||||
f.write("nstep = 5\n")
|
||||
f.write("mstop = 1\n")
|
||||
f.write("accum = no\n")
|
||||
f.write("iprint = 1\n")
|
||||
f.write("isave = 0\n")
|
||||
f.write("irdf = 0\n")
|
||||
|
||||
seed = int(1e6 * random.random())
|
||||
f.write(f"seed = {seed}\n")
|
||||
|
||||
def _make_npt_eq(self, proc_dir):
|
||||
file = Path(proc_dir, "NPT.eq")
|
||||
with open(file, "w") as f:
|
||||
f.write(f"title = {self.title} - NPT Production\n")
|
||||
f.write(f"ncores = {self.step.ncores}\n")
|
||||
f.write(f"ljname = {self.step.dice.ljname}\n")
|
||||
f.write(f"outname = {self.step.dice.outname}\n")
|
||||
|
||||
mol_string = " ".join(str(x) for x in self.step.dice.nmol)
|
||||
f.write(f"nmol = {mol_string}\n")
|
||||
|
||||
f.write(f"press = {self.step.dice.press}\n")
|
||||
f.write(f"temp = {self.step.dice.temp}\n")
|
||||
|
||||
f.write(f"nstep = 5\n")
|
||||
|
||||
f.write(f"vstep = {int(self.step.dice.nstep[2] / 5)}\n")
|
||||
f.write("init = no\n")
|
||||
f.write("mstop = 1\n")
|
||||
f.write("accum = no\n")
|
||||
f.write("iprint = 1\n")
|
||||
f.write(f"isave = {self.step.dice.isave}\n")
|
||||
f.write(f"irdf = {10 * self.step.nprocs}\n")
|
||||
|
||||
seed = int(1e6 * random.random())
|
||||
f.write(f"seed = {seed}\n")
|
||||
|
||||
def _make_potentials(self, proc_dir):
|
||||
fstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f}\n"
|
||||
|
||||
file = Path(proc_dir, self.step.dice.ljname)
|
||||
with open(file, "w") as f:
|
||||
f.write(f"{self.step.dice.combrule}\n")
|
||||
f.write(f"{len(self.step.dice.nmol)}\n")
|
||||
|
||||
nsites_qm = len(self.system.molecule[0].atom)
|
||||
f.write(f"{nsites_qm} {self.system.molecule[0].molname}\n")
|
||||
|
||||
for atom in self.system.molecule[0].atom:
|
||||
f.write(
|
||||
fstr.format(
|
||||
atom.lbl,
|
||||
atom.na,
|
||||
atom.rx,
|
||||
atom.ry,
|
||||
atom.rz,
|
||||
atom.chg,
|
||||
atom.eps,
|
||||
atom.sig,
|
||||
)
|
||||
)
|
||||
|
||||
for mol in self.system.molecule[1:]:
|
||||
f.write(f"{len(mol.atom)} {mol.molname}\n")
|
||||
for atom in mol.atom:
|
||||
f.write(
|
||||
fstr.format(
|
||||
atom.lbl,
|
||||
atom.na,
|
||||
atom.rx,
|
||||
atom.ry,
|
||||
atom.rz,
|
||||
atom.chg,
|
||||
atom.eps,
|
||||
atom.sig,
|
||||
)
|
||||
)
|
||||
|
||||
def run_dice_file(self, cycle: int, proc: int, file_name: str):
|
||||
with open(Path(file_name), "r") as infile, open(
|
||||
Path(file_name + ".out"), "w"
|
||||
) as outfile:
|
||||
if shutil.which("bash") is not None:
|
||||
exit_status = subprocess.call(
|
||||
[
|
||||
"bash",
|
||||
"-c",
|
||||
f"exec -a dice-step{cycle}-p{proc} {self.step.dice.progname} < {infile.name} > {outfile.name}",
|
||||
]
|
||||
)
|
||||
else:
|
||||
exit_status = subprocess.call(
|
||||
self.step.dice.progname, stdin=infile, stdout=outfile
|
||||
)
|
||||
|
||||
if exit_status != 0:
|
||||
raise RuntimeError(
|
||||
f"Dice process step{cycle:02d}-p{proc:02d} did not exit properly"
|
||||
)
|
||||
|
||||
with open(Path(file_name + ".out"), "r") as outfile:
|
||||
flag = outfile.readlines()[DICE_FLAG_LINE].strip()
|
||||
if flag != DICE_END_FLAG:
|
||||
raise RuntimeError(
|
||||
f"Dice process step{cycle:02d}-p{proc:02d} did not exit properly"
|
||||
)
|
||||
|
||||
logger.info(f"Dice {file_name} - step{cycle:02d}-p{proc:02d} exited properly")
|
||||
@@ -1,359 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from diceplayer import logger
|
||||
from diceplayer.shared.config.player_config import PlayerConfig
|
||||
from diceplayer.shared.environment.atom import Atom
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
from diceplayer.shared.environment.system import System
|
||||
from diceplayer.shared.interface import Interface
|
||||
from diceplayer.shared.utils.misc import date_time
|
||||
from diceplayer.shared.utils.ptable import atomsymb
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
|
||||
class GaussianInterface(Interface):
|
||||
def configure(self, step_dto: PlayerConfig, system: System):
|
||||
self.system = system
|
||||
self.step = step_dto
|
||||
|
||||
def start(self, cycle: int) -> Dict[str, npt.NDArray]:
|
||||
self._make_qm_dir(cycle)
|
||||
|
||||
if cycle > 1:
|
||||
self._copy_chk_file_from_previous_step(cycle)
|
||||
|
||||
asec_charges = self.populate_asec_vdw(cycle)
|
||||
self._make_gaussian_input_file(cycle, asec_charges)
|
||||
|
||||
self._run_gaussian(cycle)
|
||||
self._run_formchk(cycle)
|
||||
|
||||
return_value = {}
|
||||
if self.step.opt:
|
||||
# return_value['position'] = np.array(
|
||||
# self._run_optimization(cycle)
|
||||
# )
|
||||
raise NotImplementedError("Optimization not implemented yet.")
|
||||
|
||||
else:
|
||||
return_value["charges"] = np.array(self._read_charges_from_fchk(cycle))
|
||||
|
||||
return return_value
|
||||
|
||||
def reset(self):
|
||||
del self.step
|
||||
del self.system
|
||||
|
||||
def _make_qm_dir(self, cycle: int):
|
||||
qm_dir_path = Path(self.step.simulation_dir, f"step{cycle:02d}", "qm")
|
||||
if not qm_dir_path.exists():
|
||||
qm_dir_path.mkdir()
|
||||
|
||||
def _copy_chk_file_from_previous_step(self, cycle: int):
|
||||
current_chk_file_path = Path(
|
||||
self.step.simulation_dir, f"step{cycle:02d}", "qm", f"asec.chk"
|
||||
)
|
||||
if current_chk_file_path.exists():
|
||||
raise FileExistsError(f"File {current_chk_file_path} already exists.")
|
||||
|
||||
previous_chk_file_path = Path(
|
||||
self.step.simulation_dir, f"step{(cycle - 1):02d}", "qm", f"asec.chk"
|
||||
)
|
||||
if not previous_chk_file_path.exists():
|
||||
raise FileNotFoundError(f"File {previous_chk_file_path} does not exist.")
|
||||
|
||||
shutil.copy(previous_chk_file_path, current_chk_file_path)
|
||||
|
||||
def populate_asec_vdw(self, cycle: int) -> list[dict]:
|
||||
norm_factor = self._calculate_norm_factor()
|
||||
|
||||
nsitesref = len(self.system.molecule[0].atom)
|
||||
|
||||
nsites_total = self._calculate_total_number_of_sites(nsitesref)
|
||||
|
||||
proc_charges = []
|
||||
for proc in range(1, self.step.nprocs + 1):
|
||||
proc_charges.append(self._read_charges_from_last_step(cycle, proc))
|
||||
|
||||
asec_charges, thickness, picked_mols = self._evaluate_proc_charges(
|
||||
nsites_total, proc_charges
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"In average, {(sum(picked_mols) / norm_factor):^7.2f} molecules\n"
|
||||
f"were selected from each of the {len(picked_mols)} configurations\n"
|
||||
f"of the production simulations to form the ASEC, comprising a shell with\n"
|
||||
f"minimum thickness of {(sum(thickness) / norm_factor):>6.2f} Angstrom\n"
|
||||
)
|
||||
|
||||
for charge in asec_charges:
|
||||
charge["chg"] = charge["chg"] / norm_factor
|
||||
|
||||
return asec_charges
|
||||
|
||||
def _calculate_norm_factor(self) -> int:
|
||||
if self.step.dice.nstep[-1] % self.step.dice.isave == 0:
|
||||
nconfigs = round(self.step.dice.nstep[-1] / self.step.dice.isave)
|
||||
else:
|
||||
nconfigs = int(self.step.dice.nstep[-1] / self.step.dice.isave)
|
||||
|
||||
return nconfigs * self.step.nprocs
|
||||
|
||||
def _calculate_total_number_of_sites(self, nsitesref) -> int:
|
||||
nsites_total = self.step.dice.nmol[0] * nsitesref
|
||||
for i in range(1, len(self.step.dice.nmol)):
|
||||
nsites_total += self.step.dice.nmol[i] * len(self.system.molecule[i].atom)
|
||||
|
||||
return nsites_total
|
||||
|
||||
def _read_charges_from_last_step(self, cycle: int, proc: int) -> list[str]:
|
||||
last_xyz_file_path = Path(
|
||||
self.step.simulation_dir, f"step{cycle:02d}", f"p{proc:02d}", "last.xyz"
|
||||
)
|
||||
if not last_xyz_file_path.exists():
|
||||
raise FileNotFoundError(f"File {last_xyz_file_path} does not exist.")
|
||||
|
||||
with open(last_xyz_file_path, "r") as last_xyz_file:
|
||||
lines = last_xyz_file.readlines()
|
||||
|
||||
return lines
|
||||
|
||||
def _evaluate_proc_charges(
|
||||
self, total_nsites: int, proc_charges: list[list[str]]
|
||||
) -> Tuple[List[Dict[str, float | Any]], List[float], List[int]]:
|
||||
asec_charges = []
|
||||
|
||||
thickness = []
|
||||
picked_mols = []
|
||||
|
||||
for charges in proc_charges:
|
||||
charges_nsites = int(charges.pop(0))
|
||||
if int(charges_nsites) != total_nsites:
|
||||
raise ValueError(
|
||||
f"Number of sites does not match total number of sites."
|
||||
)
|
||||
|
||||
thickness.append(self._calculate_proc_thickness(charges))
|
||||
nsites_ref_mol = len(self.system.molecule[0].atom)
|
||||
charges = charges[nsites_ref_mol:]
|
||||
|
||||
mol_count = 0
|
||||
for type in range(len(self.step.dice.nmol)):
|
||||
if type == 0:
|
||||
# Reference Molecule must be ignored from type 0
|
||||
nmols = self.step.dice.nmol[type] - 1
|
||||
else:
|
||||
nmols = self.step.dice.nmol[type]
|
||||
|
||||
for mol in range(nmols):
|
||||
new_molecule = Molecule("ASEC TMP MOLECULE")
|
||||
for site in range(len(self.system.molecule[type].atom)):
|
||||
line = charges.pop(0).split()
|
||||
|
||||
if (
|
||||
line[0].title()
|
||||
!= atomsymb[
|
||||
self.system.molecule[type].atom[site].na
|
||||
].strip()
|
||||
):
|
||||
raise SyntaxError(
|
||||
f"Error: Invalid Dice Output. Atom type does not match."
|
||||
)
|
||||
|
||||
new_molecule.add_atom(
|
||||
Atom(
|
||||
self.system.molecule[type].atom[site].lbl,
|
||||
self.system.molecule[type].atom[site].na,
|
||||
float(line[1]),
|
||||
float(line[2]),
|
||||
float(line[3]),
|
||||
self.system.molecule[type].atom[site].chg,
|
||||
self.system.molecule[type].atom[site].eps,
|
||||
self.system.molecule[type].atom[site].sig,
|
||||
)
|
||||
)
|
||||
|
||||
distance = self.system.molecule[0].minimum_distance(new_molecule)
|
||||
|
||||
if distance < thickness[-1]:
|
||||
for atom in new_molecule.atom:
|
||||
asec_charges.append(
|
||||
{
|
||||
"lbl": atomsymb[atom.na],
|
||||
"rx": atom.rx,
|
||||
"ry": atom.ry,
|
||||
"rz": atom.rz,
|
||||
"chg": atom.chg,
|
||||
}
|
||||
)
|
||||
mol_count += 1
|
||||
|
||||
picked_mols.append(mol_count)
|
||||
|
||||
return asec_charges, thickness, picked_mols
|
||||
|
||||
def _calculate_proc_thickness(self, charges: list[str]) -> float:
|
||||
box = charges.pop(0).split()[-3:]
|
||||
box = [float(box[0]), float(box[1]), float(box[2])]
|
||||
sizes = self.system.molecule[0].sizes_of_molecule()
|
||||
|
||||
return min(
|
||||
[
|
||||
(box[0] - sizes[0]) / 2,
|
||||
(box[1] - sizes[1]) / 2,
|
||||
(box[2] - sizes[2]) / 2,
|
||||
]
|
||||
)
|
||||
|
||||
def _make_gaussian_input_file(self, cycle: int, asec_charges: list[dict]) -> None:
|
||||
gaussian_input_file_path = Path(
|
||||
self.step.simulation_dir, f"step{cycle:02d}", "qm", f"asec.gjf"
|
||||
)
|
||||
|
||||
with open(gaussian_input_file_path, "w") as gaussian_input_file:
|
||||
gaussian_input_file.writelines(
|
||||
self._generate_gaussian_input(cycle, asec_charges)
|
||||
)
|
||||
|
||||
def _generate_gaussian_input(
|
||||
self, cycle: int, asec_charges: list[dict]
|
||||
) -> list[str]:
|
||||
gaussian_input = ["%Chk=asec.chk\n"]
|
||||
|
||||
if self.step.mem is not None:
|
||||
gaussian_input.append(f"%Mem={self.step.mem}GB\n")
|
||||
|
||||
gaussian_input.append(f"%Nprocs={self.step.nprocs * self.step.ncores}\n")
|
||||
|
||||
kwords_line = f"#P {self.step.gaussian.level}"
|
||||
|
||||
if self.step.gaussian.keywords:
|
||||
kwords_line += " " + self.step.gaussian.keywords
|
||||
|
||||
if self.step.opt == "yes":
|
||||
kwords_line += " Force"
|
||||
|
||||
kwords_line += " NoSymm"
|
||||
kwords_line += f" Pop={self.step.gaussian.pop} Density=Current"
|
||||
|
||||
if cycle > 1:
|
||||
kwords_line += " Guess=Read"
|
||||
|
||||
gaussian_input.append(textwrap.fill(kwords_line, 90))
|
||||
gaussian_input.append("\n")
|
||||
|
||||
gaussian_input.append("\nForce calculation - Cycle number {}\n".format(cycle))
|
||||
gaussian_input.append("\n")
|
||||
gaussian_input.append(
|
||||
f"{self.step.gaussian.chgmult[0]},{self.step.gaussian.chgmult[1]}\n"
|
||||
)
|
||||
|
||||
for atom in self.system.molecule[0].atom:
|
||||
symbol = atomsymb[atom.na]
|
||||
gaussian_input.append(
|
||||
"{:<2s} {:>10.5f} {:>10.5f} {:>10.5f}\n".format(
|
||||
symbol, atom.rx, atom.ry, atom.rz
|
||||
)
|
||||
)
|
||||
|
||||
gaussian_input.append("\n")
|
||||
|
||||
for charge in asec_charges:
|
||||
gaussian_input.append(
|
||||
"{:>10.5f} {:>10.5f} {:>10.5f} {:>11.8f}\n".format(
|
||||
charge["rx"], charge["ry"], charge["rz"], charge["chg"]
|
||||
)
|
||||
)
|
||||
|
||||
gaussian_input.append("\n")
|
||||
|
||||
return gaussian_input
|
||||
|
||||
def _run_gaussian(self, cycle: int) -> None:
|
||||
qm_dir = Path(self.step.simulation_dir, f"step{(cycle):02d}", "qm")
|
||||
|
||||
working_dir = os.getcwd()
|
||||
os.chdir(qm_dir)
|
||||
|
||||
infile = "asec.gjf"
|
||||
|
||||
operation = None
|
||||
if self.step.opt:
|
||||
operation = "forces"
|
||||
else:
|
||||
operation = "charges"
|
||||
|
||||
logger.info(
|
||||
f"Calculation of {operation} initiated with Gaussian on {date_time()}\n"
|
||||
)
|
||||
|
||||
if shutil.which("bash") is not None:
|
||||
exit_status = subprocess.call(
|
||||
[
|
||||
"bash",
|
||||
"-c",
|
||||
"exec -a {}-step{} {} {}".format(
|
||||
self.step.gaussian.qmprog,
|
||||
cycle,
|
||||
self.step.gaussian.qmprog,
|
||||
infile,
|
||||
),
|
||||
]
|
||||
)
|
||||
else:
|
||||
exit_status = subprocess.call([self.step.gaussian.qmprog, infile])
|
||||
|
||||
if exit_status != 0:
|
||||
raise SystemError("Gaussian process did not exit properly")
|
||||
|
||||
logger.info(f"Calculation of {operation} finished on {date_time()}")
|
||||
|
||||
os.chdir(working_dir)
|
||||
|
||||
def _run_formchk(self, cycle: int):
|
||||
qm_dir = Path(self.step.simulation_dir, f"step{(cycle):02d}", "qm")
|
||||
|
||||
work_dir = os.getcwd()
|
||||
os.chdir(qm_dir)
|
||||
|
||||
logger.info("Formatting the checkpoint file... \n")
|
||||
|
||||
exit_status = subprocess.call(
|
||||
["formchk", "asec.chk"], stdout=subprocess.DEVNULL
|
||||
)
|
||||
|
||||
if exit_status != 0:
|
||||
raise SystemError("Formchk process did not exit properly")
|
||||
|
||||
logger.info("Done\n")
|
||||
|
||||
os.chdir(work_dir)
|
||||
|
||||
def _read_charges_from_fchk(self, cycle: int):
|
||||
fchk_file_path = Path("simfiles", f"step{cycle:02d}", "qm", "asec.fchk")
|
||||
with open(fchk_file_path) as fchk:
|
||||
fchkfile = fchk.readlines()
|
||||
|
||||
if self.step.gaussian.pop in ["chelpg", "mk"]:
|
||||
CHARGE_FLAG = "ESP Charges"
|
||||
else:
|
||||
CHARGE_FLAG = "ESP Charges"
|
||||
|
||||
start = fchkfile.pop(0).strip()
|
||||
while start.find(CHARGE_FLAG) != 0: # expression in begining of line
|
||||
start = fchkfile.pop(0).strip()
|
||||
|
||||
charges: List[float] = []
|
||||
while len(charges) < len(self.system.molecule[0].atom):
|
||||
charges.extend([float(x) for x in fchkfile.pop(0).split()])
|
||||
|
||||
return charges
|
||||
@@ -1,6 +0,0 @@
|
||||
from typing import Protocol, runtime_checkable
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Dataclass(Protocol):
|
||||
__dataclass_fields__: dict
|
||||
@@ -1,72 +0,0 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def valid_logger(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
logger = args[0]
|
||||
assert logger._was_set, "Logger is not set. Please call set_logger() first."
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class Logger:
|
||||
outfile = None
|
||||
|
||||
_logger = None
|
||||
|
||||
_was_set = False
|
||||
|
||||
def __init__(self, logger_name):
|
||||
if self._logger is None:
|
||||
self._logger = logging.getLogger(logger_name)
|
||||
|
||||
def set_logger(self, outfile="run.log", level=logging.INFO, stream=None):
|
||||
outfile_path = None
|
||||
if outfile is not None and stream is None:
|
||||
outfile_path = Path(outfile)
|
||||
if outfile_path.exists():
|
||||
outfile_path.rename(str(outfile_path) + ".backup")
|
||||
|
||||
if level is not None:
|
||||
self._logger.setLevel(level)
|
||||
|
||||
self._create_handlers(outfile_path, stream)
|
||||
|
||||
self._was_set = True
|
||||
|
||||
@valid_logger
|
||||
def info(self, message):
|
||||
self._logger.info(message)
|
||||
|
||||
@valid_logger
|
||||
def debug(self, message):
|
||||
self._logger.debug(message)
|
||||
|
||||
@valid_logger
|
||||
def warning(self, message):
|
||||
self._logger.warning(message)
|
||||
|
||||
@valid_logger
|
||||
def error(self, message):
|
||||
self._logger.error(message)
|
||||
|
||||
def _create_handlers(self, outfile_path: Path, stream):
|
||||
handlers = []
|
||||
if outfile_path is not None:
|
||||
handlers.append(logging.FileHandler(outfile_path, mode="a+"))
|
||||
elif stream is not None:
|
||||
handlers.append(logging.StreamHandler(stream))
|
||||
else:
|
||||
handlers.append(logging.StreamHandler())
|
||||
|
||||
for handler in handlers:
|
||||
handler.setFormatter(logging.Formatter("%(message)s"))
|
||||
self._logger.addHandler(handler)
|
||||
|
||||
def close(self):
|
||||
for handler in self._logger.handlers:
|
||||
handler.close()
|
||||
self._logger.removeHandler(handler)
|
||||
@@ -1,223 +0,0 @@
|
||||
#### Label used in Dice for a ghost atom
|
||||
dice_ghost_label = "Xx"
|
||||
|
||||
#### Tuple of atom symbols
|
||||
atomsymb = (
|
||||
"00",
|
||||
"H ",
|
||||
"He",
|
||||
"Li",
|
||||
"Be",
|
||||
"B ",
|
||||
"C ",
|
||||
"N ",
|
||||
"O ",
|
||||
"F ",
|
||||
"Ne",
|
||||
"Na",
|
||||
"Mg",
|
||||
"Al",
|
||||
"Si",
|
||||
"P ",
|
||||
"S ",
|
||||
"Cl",
|
||||
"Ar",
|
||||
"K ",
|
||||
"Ca",
|
||||
"Sc",
|
||||
"Ti",
|
||||
"V ",
|
||||
"Cr",
|
||||
"Mn",
|
||||
"Fe",
|
||||
"Co",
|
||||
"Ni",
|
||||
"Cu",
|
||||
"Zn",
|
||||
"Ga",
|
||||
"Ge",
|
||||
"As",
|
||||
"Se",
|
||||
"Br",
|
||||
"Kr",
|
||||
"Rb",
|
||||
"Sr",
|
||||
"Y ",
|
||||
"Zr",
|
||||
"Nb",
|
||||
"Mo",
|
||||
"Tc",
|
||||
"Ru",
|
||||
"Rh",
|
||||
"Pd",
|
||||
"Ag",
|
||||
"Cd",
|
||||
"In",
|
||||
"Sn",
|
||||
"Sb",
|
||||
"Te",
|
||||
"I ",
|
||||
"Xe",
|
||||
"Cs",
|
||||
"Ba",
|
||||
"La",
|
||||
"Ce",
|
||||
"Pr",
|
||||
"Nd",
|
||||
"Pm",
|
||||
"Sm",
|
||||
"Eu",
|
||||
"Gd",
|
||||
"Tb",
|
||||
"Dy",
|
||||
"Ho",
|
||||
"Er",
|
||||
"Tm",
|
||||
"Yb",
|
||||
"Lu",
|
||||
"Hf",
|
||||
"Ta",
|
||||
"W ",
|
||||
"Re",
|
||||
"Os",
|
||||
"Ir",
|
||||
"Pt",
|
||||
"Au",
|
||||
"Hg",
|
||||
"Ti",
|
||||
"Pb",
|
||||
"Bi",
|
||||
"Po",
|
||||
"At",
|
||||
"Rn",
|
||||
"Fr",
|
||||
"Ra",
|
||||
"Ac",
|
||||
"Th",
|
||||
"Pa",
|
||||
"U ",
|
||||
"Np",
|
||||
"Pu",
|
||||
"Am",
|
||||
"Cm",
|
||||
"Bk",
|
||||
"Cf",
|
||||
"Es",
|
||||
"Fm",
|
||||
"Md",
|
||||
"No",
|
||||
"Lr",
|
||||
dice_ghost_label,
|
||||
)
|
||||
|
||||
#### Tuple of atom masses
|
||||
atommass = (
|
||||
0.0,
|
||||
1.0079,
|
||||
4.0026,
|
||||
6.9410,
|
||||
9.0122,
|
||||
10.811,
|
||||
12.011,
|
||||
14.007,
|
||||
15.999,
|
||||
18.998,
|
||||
20.180,
|
||||
22.990,
|
||||
24.305,
|
||||
26.982,
|
||||
28.086,
|
||||
30.974,
|
||||
32.065,
|
||||
35.453,
|
||||
39.948,
|
||||
39.098,
|
||||
40.078,
|
||||
44.956,
|
||||
47.867,
|
||||
50.942,
|
||||
51.996,
|
||||
54.938,
|
||||
55.845,
|
||||
58.933,
|
||||
58.693,
|
||||
63.546,
|
||||
65.409,
|
||||
69.723,
|
||||
72.640,
|
||||
74.922,
|
||||
78.960,
|
||||
79.904,
|
||||
83.798,
|
||||
85.468,
|
||||
87.620,
|
||||
88.906,
|
||||
91.224,
|
||||
92.906,
|
||||
95.940,
|
||||
98.000,
|
||||
101.07,
|
||||
102.91,
|
||||
106.42,
|
||||
107.87,
|
||||
112.41,
|
||||
114.82,
|
||||
118.71,
|
||||
121.76,
|
||||
127.60,
|
||||
126.90,
|
||||
131.29,
|
||||
132.91,
|
||||
137.33,
|
||||
138.91,
|
||||
140.12,
|
||||
140.91,
|
||||
144.24,
|
||||
145.00,
|
||||
150.36,
|
||||
151.96,
|
||||
157.25,
|
||||
158.93,
|
||||
162.50,
|
||||
164.93,
|
||||
167.26,
|
||||
168.93,
|
||||
173.04,
|
||||
174.97,
|
||||
178.49,
|
||||
180.95,
|
||||
183.84,
|
||||
186.21,
|
||||
190.23,
|
||||
192.22,
|
||||
195.08,
|
||||
196.97,
|
||||
200.59,
|
||||
204.38,
|
||||
207.20,
|
||||
208.98,
|
||||
209.00,
|
||||
210.00,
|
||||
222.00,
|
||||
223.00,
|
||||
226.00,
|
||||
227.00,
|
||||
232.04,
|
||||
231.04,
|
||||
238.03,
|
||||
237.00,
|
||||
244.00,
|
||||
243.00,
|
||||
247.00,
|
||||
247.00,
|
||||
251.00,
|
||||
252.00,
|
||||
257.00,
|
||||
258.00,
|
||||
259.00,
|
||||
262.00,
|
||||
0.000,
|
||||
)
|
||||
|
||||
#### Number of the ghost atom
|
||||
ghost_number = len(atomsymb) - 1
|
||||
37
diceplayer/state/state_handler.py
Normal file
37
diceplayer/state/state_handler.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from diceplayer.config import PlayerConfig
|
||||
from diceplayer.logger import logger
|
||||
from diceplayer.state.state_model import StateModel
|
||||
|
||||
import pickle
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class StateHandler:
|
||||
def __init__(self, sim_dir: Path, state_file: str = "state.pkl"):
|
||||
if not sim_dir.exists():
|
||||
sim_dir.mkdir(parents=True, exist_ok=True)
|
||||
self._state_file = sim_dir / state_file
|
||||
|
||||
def get(self, config: PlayerConfig, force=False) -> StateModel | None:
|
||||
if not self._state_file.exists():
|
||||
return None
|
||||
|
||||
with open(self._state_file, mode="rb") as file:
|
||||
data = pickle.load(file)
|
||||
model = StateModel.model_validate(data)
|
||||
|
||||
if config != model.config and not force:
|
||||
logger.warning(
|
||||
"The configuration in the state file does not match the provided configuration."
|
||||
)
|
||||
return None
|
||||
|
||||
return model
|
||||
|
||||
def save(self, state: StateModel) -> None:
|
||||
with self._state_file.open(mode="wb") as f:
|
||||
pickle.dump(state.model_dump(), f)
|
||||
|
||||
def delete(self) -> None:
|
||||
if self._state_file.exists():
|
||||
self._state_file.unlink()
|
||||
19
diceplayer/state/state_model.py
Normal file
19
diceplayer/state/state_model.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from diceplayer.config import PlayerConfig
|
||||
from diceplayer.environment import System
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
class StateModel(BaseModel):
|
||||
config: PlayerConfig
|
||||
system: System
|
||||
current_cycle: int = 0
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config: PlayerConfig) -> Self:
|
||||
return cls(
|
||||
config=config,
|
||||
system=System(),
|
||||
current_cycle=0,
|
||||
)
|
||||
21
diceplayer/utils/__init__.py
Normal file
21
diceplayer/utils/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from .logger import RunLogger
|
||||
from .misc import (
|
||||
compress_files_1mb,
|
||||
date_time,
|
||||
make_qm_dir,
|
||||
make_step_dir,
|
||||
weekday_date_time,
|
||||
)
|
||||
from .ptable import AtomInfo, PTable
|
||||
|
||||
|
||||
__all__ = [
|
||||
"RunLogger",
|
||||
"PTable",
|
||||
"AtomInfo",
|
||||
"weekday_date_time",
|
||||
"date_time",
|
||||
"compress_files_1mb",
|
||||
"make_step_dir",
|
||||
"make_qm_dir",
|
||||
]
|
||||
29
diceplayer/utils/cache.py
Normal file
29
diceplayer/utils/cache.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from functools import cached_property
|
||||
|
||||
|
||||
def invalidate_computed_properties():
|
||||
"""
|
||||
Decorator function to invalidate the cached properties of the molecule when a new atom is added
|
||||
|
||||
Args:
|
||||
properties (list[str]): list of the names of the properties to be invalidated
|
||||
"""
|
||||
|
||||
def get_cached_properies(cls: type) -> set[str]:
|
||||
return {
|
||||
name
|
||||
for name, value in cls.__dict__.items()
|
||||
if isinstance(value, cached_property)
|
||||
}
|
||||
|
||||
def decorator(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
result = func(self, *args, **kwargs)
|
||||
for prop in get_cached_properies(self.__class__):
|
||||
if hasattr(self, prop):
|
||||
delattr(self, prop)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
44
diceplayer/utils/logger.py
Normal file
44
diceplayer/utils/logger.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
H = TypeVar("H", bound=logging.Handler)
|
||||
|
||||
|
||||
class RunLogger(logging.Logger):
|
||||
def __init__(self, name, level=logging.INFO, stream=sys.stdout):
|
||||
super().__init__(name, level)
|
||||
|
||||
self.handlers.clear()
|
||||
|
||||
self.handlers.append(
|
||||
self._configure_handler(logging.StreamHandler(stream), level)
|
||||
)
|
||||
|
||||
def set_output_file(self, outfile: Path, level=logging.INFO):
|
||||
for handler in list(self.handlers):
|
||||
if not isinstance(handler, logging.FileHandler):
|
||||
continue
|
||||
self.handlers.remove(handler)
|
||||
|
||||
self.handlers.append(self._create_file_handler(outfile, level))
|
||||
|
||||
@staticmethod
|
||||
def _create_file_handler(file: str | Path, level) -> logging.FileHandler:
|
||||
file = Path(file)
|
||||
|
||||
if file.exists():
|
||||
file.rename(file.with_suffix(".log.backup"))
|
||||
|
||||
handler = logging.FileHandler(file)
|
||||
return RunLogger._configure_handler(handler, level)
|
||||
|
||||
@staticmethod
|
||||
def _configure_handler(handler: H, level) -> H:
|
||||
handler.setLevel(level)
|
||||
formatter = logging.Formatter("%(message)s")
|
||||
handler.setFormatter(formatter)
|
||||
return handler
|
||||
@@ -1,9 +1,11 @@
|
||||
from typing_extensions import Final
|
||||
|
||||
import gzip
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
from typing import Final
|
||||
|
||||
|
||||
####################################### constants ######################################
|
||||
|
||||
@@ -11,6 +13,8 @@ from typing import Final
|
||||
BOHR2ANG: Final[float] = 0.52917721092
|
||||
ANG2BOHR: Final[float] = 1 / BOHR2ANG
|
||||
|
||||
EA_2_DEBYE = 1 / 0.20819434
|
||||
|
||||
|
||||
####################################### functions ######################################
|
||||
|
||||
@@ -35,7 +39,7 @@ def compress_files_1mb(path):
|
||||
with open(file, "rb") as f_in:
|
||||
with gzip.open(filegz, "wb") as f_out:
|
||||
shutil.copyfileobj(f_in, f_out)
|
||||
except:
|
||||
except Exception as _:
|
||||
sys.exit("Error: cannot compress file {}".format(file))
|
||||
|
||||
os.chdir(working_dir)
|
||||
@@ -51,7 +55,7 @@ def make_step_dir(cycle):
|
||||
sys.exit("Error: a file or directory {} already exists".format(step_dir))
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except:
|
||||
except Exception as _:
|
||||
sys.exit("Error: cannot make directory {}".format(step_dir))
|
||||
|
||||
|
||||
@@ -61,5 +65,5 @@ def make_qm_dir(cycle):
|
||||
path = sim_dir + os.sep + step_dir + os.sep + "qm"
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except:
|
||||
except Exception as _:
|
||||
sys.exit("Error: cannot make directory {}".format(path))
|
||||
55
diceplayer/utils/potential.py
Normal file
55
diceplayer/utils/potential.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from diceplayer.config import PlayerConfig
|
||||
from diceplayer.environment import Atom, Molecule, System
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def read_system_from_phb(config: PlayerConfig) -> System:
|
||||
phb_file = Path(config.dice.ljname)
|
||||
if not phb_file.exists():
|
||||
raise FileNotFoundError
|
||||
|
||||
ljc_data = phb_file.read_text(encoding="utf-8").splitlines()
|
||||
|
||||
combrule = ljc_data.pop(0).strip()
|
||||
if combrule != config.dice.combrule:
|
||||
raise ValueError(
|
||||
f"Invalid combrule defined in {phb_file}. Expected the same value configured in the config file"
|
||||
)
|
||||
|
||||
ntypes = ljc_data.pop(0).strip()
|
||||
if not ntypes.isdigit():
|
||||
raise ValueError(f"Invalid ntypes defined in {phb_file}")
|
||||
|
||||
nmol = int(ntypes)
|
||||
if nmol != len(config.dice.nmol):
|
||||
raise ValueError(f"Invalid nmol defined in {phb_file}")
|
||||
|
||||
sys = System()
|
||||
|
||||
for i in range(nmol):
|
||||
nsites, molname = ljc_data.pop(0).split()
|
||||
|
||||
if not nsites.isdigit():
|
||||
raise ValueError(f"Invalid nsites defined in {phb_file}")
|
||||
nsites = int(nsites)
|
||||
|
||||
mol = Molecule(molname)
|
||||
|
||||
for j in range(nsites):
|
||||
_fields = ljc_data.pop(0).split()
|
||||
mol.add_atom(Atom(*_fields))
|
||||
|
||||
sys.add_type(mol)
|
||||
|
||||
return sys
|
||||
|
||||
|
||||
# def write_phb(phb_file: Path, state: StateModel) -> None:
|
||||
# if phb_file.exists():
|
||||
# raise RuntimeError(f"File {phb_file} already exists")
|
||||
#
|
||||
# with open(phb_file, "w") as f:
|
||||
# f.write(f"{state.config.dice.combrule}\n")
|
||||
# f.write(f"{len(state.system.nmols)}\n")
|
||||
# f.write(f"{state.config.dice.nmol}\n")
|
||||
143
diceplayer/utils/ptable.py
Normal file
143
diceplayer/utils/ptable.py
Normal file
@@ -0,0 +1,143 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
|
||||
DICE_GHOST_LABEL = "Xx"
|
||||
|
||||
#### Number of the ghost atom
|
||||
GHOST_NUMBER = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class AtomInfo:
|
||||
atomic_number: int
|
||||
symbol: str
|
||||
mass: float
|
||||
|
||||
|
||||
class PTable(Enum):
|
||||
Xx = AtomInfo(GHOST_NUMBER, DICE_GHOST_LABEL, 0.0)
|
||||
H = AtomInfo(1, "H", 1.0079)
|
||||
He = AtomInfo(2, "He", 4.0026)
|
||||
Li = AtomInfo(3, "Li", 6.9410)
|
||||
Be = AtomInfo(4, "Be", 9.0122)
|
||||
B = AtomInfo(5, "B", 10.811)
|
||||
C = AtomInfo(6, "C", 12.011)
|
||||
N = AtomInfo(7, "N", 14.007)
|
||||
O = AtomInfo(8, "O", 15.999)
|
||||
F = AtomInfo(9, "F", 18.998)
|
||||
Ne = AtomInfo(10, "Ne", 20.180)
|
||||
Na = AtomInfo(11, "Na", 22.990)
|
||||
Mg = AtomInfo(12, "Mg", 24.305)
|
||||
Al = AtomInfo(13, "Al", 26.982)
|
||||
Si = AtomInfo(14, "Si", 28.086)
|
||||
P = AtomInfo(15, "P", 30.974)
|
||||
S = AtomInfo(16, "S", 32.065)
|
||||
Cl = AtomInfo(17, "Cl", 35.453)
|
||||
Ar = AtomInfo(18, "Ar", 39.948)
|
||||
K = AtomInfo(19, "K", 39.098)
|
||||
Ca = AtomInfo(20, "Ca", 40.078)
|
||||
Sc = AtomInfo(21, "Sc", 44.956)
|
||||
Ti = AtomInfo(22, "Ti", 47.867)
|
||||
V = AtomInfo(23, "V", 50.942)
|
||||
Cr = AtomInfo(24, "Cr", 51.996)
|
||||
Mn = AtomInfo(25, "Mn", 54.938)
|
||||
Fe = AtomInfo(26, "Fe", 55.845)
|
||||
Co = AtomInfo(27, "Co", 58.933)
|
||||
Ni = AtomInfo(28, "Ni", 58.693)
|
||||
Cu = AtomInfo(29, "Cu", 63.546)
|
||||
Zn = AtomInfo(30, "Zn", 65.409)
|
||||
Ga = AtomInfo(31, "Ga", 69.723)
|
||||
Ge = AtomInfo(32, "Ge", 72.640)
|
||||
As = AtomInfo(33, "As", 74.922)
|
||||
Se = AtomInfo(34, "Se", 78.960)
|
||||
Br = AtomInfo(35, "Br", 79.904)
|
||||
Kr = AtomInfo(36, "Kr", 83.798)
|
||||
Rb = AtomInfo(37, "Rb", 85.468)
|
||||
Sr = AtomInfo(38, "Sr", 87.620)
|
||||
Y = AtomInfo(39, "Y", 88.906)
|
||||
Zr = AtomInfo(40, "Zr", 91.224)
|
||||
Nb = AtomInfo(41, "Nb", 92.906)
|
||||
Mo = AtomInfo(42, "Mo", 95.940)
|
||||
Tc = AtomInfo(43, "Tc", 98.000)
|
||||
Ru = AtomInfo(44, "Ru", 101.07)
|
||||
Rh = AtomInfo(45, "Rh", 102.91)
|
||||
Pd = AtomInfo(46, "Pd", 106.42)
|
||||
Ag = AtomInfo(47, "Ag", 107.87)
|
||||
Cd = AtomInfo(48, "Cd", 112.41)
|
||||
In = AtomInfo(49, "In", 114.82)
|
||||
Sn = AtomInfo(50, "Sn", 118.71)
|
||||
Sb = AtomInfo(51, "Sb", 121.76)
|
||||
Te = AtomInfo(52, "Te", 127.60)
|
||||
I = AtomInfo(53, "I", 126.90)
|
||||
Xe = AtomInfo(54, "Xe", 131.29)
|
||||
Cs = AtomInfo(55, "Cs", 132.91)
|
||||
Ba = AtomInfo(56, "Ba", 137.33)
|
||||
La = AtomInfo(57, "La", 138.91)
|
||||
Ce = AtomInfo(58, "Ce", 140.12)
|
||||
Pr = AtomInfo(59, "Pr", 140.91)
|
||||
Nd = AtomInfo(60, "Nd", 144.24)
|
||||
Pm = AtomInfo(61, "Pm", 145.00)
|
||||
Sm = AtomInfo(62, "Sm", 150.36)
|
||||
Eu = AtomInfo(63, "Eu", 151.96)
|
||||
Gd = AtomInfo(64, "Gd", 157.25)
|
||||
Tb = AtomInfo(65, "Tb", 158.93)
|
||||
Dy = AtomInfo(66, "Dy", 162.50)
|
||||
Ho = AtomInfo(67, "Ho", 164.93)
|
||||
Er = AtomInfo(68, "Er", 167.26)
|
||||
Tm = AtomInfo(69, "Tm", 168.93)
|
||||
Yb = AtomInfo(70, "Yb", 173.04)
|
||||
Lu = AtomInfo(71, "Lu", 174.97)
|
||||
Hf = AtomInfo(72, "Hf", 178.49)
|
||||
Ta = AtomInfo(73, "Ta", 180.95)
|
||||
W = AtomInfo(74, "W", 183.84)
|
||||
Re = AtomInfo(75, "Re", 186.21)
|
||||
Os = AtomInfo(76, "Os", 190.23)
|
||||
Ir = AtomInfo(77, "Ir", 192.22)
|
||||
Pt = AtomInfo(78, "Pt", 195.08)
|
||||
Au = AtomInfo(79, "Au", 196.97)
|
||||
Hg = AtomInfo(80, "Hg", 200.59)
|
||||
Tl = AtomInfo(81, "Tl", 204.38)
|
||||
Pb = AtomInfo(82, "Pb", 207.20)
|
||||
Bi = AtomInfo(83, "Bi", 208.98)
|
||||
Po = AtomInfo(84, "Po", 209.00)
|
||||
At = AtomInfo(85, "At", 210.00)
|
||||
Rn = AtomInfo(86, "Rn", 222.00)
|
||||
Fr = AtomInfo(87, "Fr", 223.00)
|
||||
Ra = AtomInfo(88, "Ra", 226.00)
|
||||
Ac = AtomInfo(89, "Ac", 227.00)
|
||||
Th = AtomInfo(90, "Th", 232.04)
|
||||
Pa = AtomInfo(91, "Pa", 231.04)
|
||||
U = AtomInfo(92, "U", 238.03)
|
||||
Np = AtomInfo(93, "Np", 237.00)
|
||||
Pu = AtomInfo(94, "Pu", 244.00)
|
||||
Am = AtomInfo(95, "Am", 243.00)
|
||||
Cm = AtomInfo(96, "Cm", 247.00)
|
||||
Bk = AtomInfo(97, "Bk", 247.00)
|
||||
Cf = AtomInfo(98, "Cf", 251.00)
|
||||
Es = AtomInfo(99, "Es", 252.00)
|
||||
Fm = AtomInfo(100, "Fm", 257.00)
|
||||
Md = AtomInfo(101, "Md", 258.00)
|
||||
No = AtomInfo(102, "No", 259.00)
|
||||
Lr = AtomInfo(103, "Lr", 262.00)
|
||||
|
||||
@classmethod
|
||||
def get_atomic_symbol(cls, atomic_number: int) -> str:
|
||||
for element in cls:
|
||||
if element.value.atomic_number == atomic_number:
|
||||
return element.value.symbol
|
||||
raise ValueError(f"Atomic number {atomic_number} not found in PTable.")
|
||||
|
||||
@classmethod
|
||||
def get_atomic_mass(cls, atomic_number: int) -> float:
|
||||
for element in cls:
|
||||
if element.value.atomic_number == atomic_number:
|
||||
return element.value.mass
|
||||
raise ValueError(f"Atomic number {atomic_number} not found in PTable.")
|
||||
|
||||
@classmethod
|
||||
def get_from_atomic_number(cls, atomic_number: int) -> AtomInfo:
|
||||
for element in cls:
|
||||
if element.value.atomic_number == atomic_number:
|
||||
return element.value
|
||||
raise ValueError(f"Atomic number {atomic_number} not found in PTable.")
|
||||
706
poetry.lock
generated
706
poetry.lock
generated
@@ -1,706 +0,0 @@
|
||||
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "argparse"
|
||||
version = "1.4.0"
|
||||
description = "Python command-line parsing library"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"},
|
||||
{file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.10.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"},
|
||||
{file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"},
|
||||
{file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"},
|
||||
{file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"},
|
||||
{file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"},
|
||||
{file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"},
|
||||
{file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"},
|
||||
{file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"},
|
||||
{file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"},
|
||||
{file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"},
|
||||
{file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"},
|
||||
{file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"},
|
||||
{file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"},
|
||||
{file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"},
|
||||
{file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"},
|
||||
{file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"},
|
||||
{file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"},
|
||||
{file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"},
|
||||
{file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"},
|
||||
{file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"},
|
||||
{file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"},
|
||||
{file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.10)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "cfgv"
|
||||
version = "3.4.0"
|
||||
description = "Validate configuration and produce human readable error messages."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
|
||||
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
|
||||
{file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
groups = ["dev"]
|
||||
markers = "platform_system == \"Windows\""
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.10.7"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"},
|
||||
{file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"},
|
||||
{file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"},
|
||||
{file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"},
|
||||
{file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"},
|
||||
{file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"},
|
||||
{file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"},
|
||||
{file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"},
|
||||
{file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"},
|
||||
{file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"},
|
||||
{file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.4.0"
|
||||
description = "Distribution utilities"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"},
|
||||
{file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.19.1"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"},
|
||||
{file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.6.14"
|
||||
description = "File identification library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "identify-2.6.14-py2.py3-none-any.whl", hash = "sha256:11a073da82212c6646b1f39bb20d4483bfb9543bd5566fec60053c4bb309bf2e"},
|
||||
{file = "identify-2.6.14.tar.gz", hash = "sha256:663494103b4f717cb26921c52f8751363dc89db64364cd836a9bf1535f53cd6a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
license = ["ukkonen"]
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.13.2"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
|
||||
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
colors = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.1.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
|
||||
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.9.1"
|
||||
description = "Node.js virtual environment builder"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
|
||||
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.2.6"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"},
|
||||
{file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"},
|
||||
{file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"},
|
||||
{file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"},
|
||||
{file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"},
|
||||
{file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"},
|
||||
{file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"},
|
||||
{file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"},
|
||||
{file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"},
|
||||
{file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"},
|
||||
{file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
|
||||
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pastel"
|
||||
version = "0.2.1"
|
||||
description = "Bring colors to your terminal."
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"},
|
||||
{file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.4.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"},
|
||||
{file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"]
|
||||
type = ["mypy (>=1.14.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "poethepoet"
|
||||
version = "0.27.0"
|
||||
description = "A task runner that works well with poetry."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "poethepoet-0.27.0-py3-none-any.whl", hash = "sha256:0032d980a623b96e26dc7450ae200b0998be523f27d297d799b97510fe252a24"},
|
||||
{file = "poethepoet-0.27.0.tar.gz", hash = "sha256:907ab4dc1bc6326be5a3b10d2aa39d1acc0ca12024317d9506fbe9c0cdc912c9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pastel = ">=0.2.1,<0.3.0"
|
||||
tomli = ">=1.2.2"
|
||||
|
||||
[package.extras]
|
||||
poetry-plugin = ["poetry (>=1.0,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.8.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"},
|
||||
{file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cfgv = ">=2.0.0"
|
||||
identify = ">=1.0.0"
|
||||
nodeenv = ">=0.11.1"
|
||||
pyyaml = ">=5.1"
|
||||
virtualenv = ">=20.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
description = "YAML parser and emitter for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main", "dev"]
|
||||
files = [
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
|
||||
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setproctitle"
|
||||
version = "1.3.7"
|
||||
description = "A Python module to customize the process title"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cf555b6299f10a6eb44e4f96d2f5a3884c70ce25dc5c8796aaa2f7b40e72cb1b"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:690b4776f9c15aaf1023bb07d7c5b797681a17af98a4a69e76a1d504e41108b7"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:00afa6fc507967d8c9d592a887cdc6c1f5742ceac6a4354d111ca0214847732c"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e02667f6b9fc1238ba753c0f4b0a37ae184ce8f3bbbc38e115d99646b3f4cd3"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83fcd271567d133eb9532d3b067c8a75be175b2b3b271e2812921a05303a693f"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13fe37951dda1a45c35d77d06e3da5d90e4f875c4918a7312b3b4556cfa7ff64"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a05509cfb2059e5d2ddff701d38e474169e9ce2a298cf1b6fd5f3a213a553fe5"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6da835e76ae18574859224a75db6e15c4c2aaa66d300a57efeaa4c97ca4c7381"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-win32.whl", hash = "sha256:9e803d1b1e20240a93bac0bc1025363f7f80cb7eab67dfe21efc0686cc59ad7c"},
|
||||
{file = "setproctitle-1.3.7-cp310-cp310-win_amd64.whl", hash = "sha256:a97200acc6b64ec4cada52c2ecaf1fba1ef9429ce9c542f8a7db5bcaa9dcbd95"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-win32.whl", hash = "sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d"},
|
||||
{file = "setproctitle-1.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-win32.whl", hash = "sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f"},
|
||||
{file = "setproctitle-1.3.7-cp312-cp312-win_amd64.whl", hash = "sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c3736b2a423146b5e62230502e47e08e68282ff3b69bcfe08a322bee73407922"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3384e682b158d569e85a51cfbde2afd1ab57ecf93ea6651fe198d0ba451196ee"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0564a936ea687cd24dffcea35903e2a20962aa6ac20e61dd3a207652401492dd"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5d1cb3f81531f0eb40e13246b679a1bdb58762b170303463cb06ecc296f26d0"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a7d159e7345f343b44330cbba9194169b8590cb13dae940da47aa36a72aa9929"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0b5074649797fd07c72ca1f6bff0406f4a42e1194faac03ecaab765ce605866f"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:61e96febced3f61b766115381d97a21a6265a0f29188a791f6df7ed777aef698"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:047138279f9463f06b858e579cc79580fbf7a04554d24e6bddf8fe5dddbe3d4c"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-win32.whl", hash = "sha256:7f47accafac7fe6535ba8ba9efd59df9d84a6214565108d0ebb1199119c9cbbd"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313-win_amd64.whl", hash = "sha256:fe5ca35aeec6dc50cabab9bf2d12fbc9067eede7ff4fe92b8f5b99d92e21263f"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:10e92915c4b3086b1586933a36faf4f92f903c5554f3c34102d18c7d3f5378e9"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:de879e9c2eab637f34b1a14c4da1e030c12658cdc69ee1b3e5be81b380163ce5"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c18246d88e227a5b16248687514f95642505000442165f4b7db354d39d0e4c29"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7081f193dab22df2c36f9fc6d113f3793f83c27891af8fe30c64d89d9a37e152"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9cc9b901ce129350637426a89cfd650066a4adc6899e47822e2478a74023ff7c"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:80e177eff2d1ec172188d0d7fd9694f8e43d3aab76a6f5f929bee7bf7894e98b"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:23e520776c445478a67ee71b2a3c1ffdafbe1f9f677239e03d7e2cc635954e18"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5fa1953126a3b9bd47049d58c51b9dac72e78ed120459bd3aceb1bacee72357c"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-win32.whl", hash = "sha256:4a5e212bf438a4dbeece763f4962ad472c6008ff6702e230b4f16a037e2f6f29"},
|
||||
{file = "setproctitle-1.3.7-cp313-cp313t-win_amd64.whl", hash = "sha256:cf2727b733e90b4f874bac53e3092aa0413fe1ea6d4f153f01207e6ce65034d9"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:80c36c6a87ff72eabf621d0c79b66f3bdd0ecc79e873c1e9f0651ee8bf215c63"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b53602371a52b91c80aaf578b5ada29d311d12b8a69c0c17fbc35b76a1fd4f2e"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fcb966a6c57cf07cc9448321a08f3be6b11b7635be502669bc1d8745115d7e7f"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46178672599b940368d769474fe13ecef1b587d58bb438ea72b9987f74c56ea5"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7f9e9e3ff135cbcc3edd2f4cf29b139f4aca040d931573102742db70ff428c17"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14c7eba8d90c93b0e79c01f0bd92a37b61983c27d6d7d5a3b5defd599113d60e"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9e64e98077fb30b6cf98073d6c439cd91deb8ebbf8fc62d9dbf52bd38b0c6ac0"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b91387cc0f02a00ac95dcd93f066242d3cca10ff9e6153de7ee07069c6f0f7c8"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-win32.whl", hash = "sha256:52b054a61c99d1b72fba58b7f5486e04b20fefc6961cd76722b424c187f362ed"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314-win_amd64.whl", hash = "sha256:5818e4080ac04da1851b3ec71e8a0f64e3748bf9849045180566d8b736702416"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6fc87caf9e323ac426910306c3e5d3205cd9f8dcac06d233fcafe9337f0928a3"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6134c63853d87a4897ba7d5cc0e16abfa687f6c66fc09f262bb70d67718f2309"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1403d2abfd32790b6369916e2313dffbe87d6b11dca5bbd898981bcde48e7a2b"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7c5bfe4228ea22373e3025965d1a4116097e555ee3436044f5c954a5e63ac45"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:585edf25e54e21a94ccb0fe81ad32b9196b69ebc4fc25f81da81fb8a50cca9e4"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:96c38cdeef9036eb2724c2210e8d0b93224e709af68c435d46a4733a3675fee1"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:45e3ef48350abb49cf937d0a8ba15e42cee1e5ae13ca41a77c66d1abc27a5070"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1fae595d032b30dab4d659bece20debd202229fce12b55abab978b7f30783d73"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-win32.whl", hash = "sha256:02432f26f5d1329ab22279ff863c83589894977063f59e6c4b4845804a08f8c2"},
|
||||
{file = "setproctitle-1.3.7-cp314-cp314t-win_amd64.whl", hash = "sha256:cbc388e3d86da1f766d8fc2e12682e446064c01cea9f88a88647cfe7c011de6a"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:376761125ab5dab822d40eaa7d9b7e876627ecd41de8fa5336713b611b47ccef"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a4e03bd9aa5d10b8702f00ec1b740691da96b5003432f3000d60c56f1c2b4d3"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:47d36e418ab86b3bc7946e27155e281a743274d02cd7e545f5d628a2875d32f9"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a74714ce836914063c36c8a26ae11383cf8a379698c989fe46883e38a8faa5be"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f2ae6c3f042fc866cc0fa2bc35ae00d334a9fa56c9d28dfc47d1b4f5ed23e375"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:be7e01f3ad8d0e43954bebdb3088cb466633c2f4acdd88647e7fbfcfe9b9729f"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:35a2cabcfdea4643d7811cfe9f3d92366d282b38ef5e7e93e25dafb6f97b0a59"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8ce2e39a40fca82744883834683d833e0eb28623752cc1c21c2ec8f06a890b39"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-win32.whl", hash = "sha256:6f1be447456fe1e16c92f5fb479404a850d8f4f4ff47192fde14a59b0bae6a0a"},
|
||||
{file = "setproctitle-1.3.7-cp38-cp38-win_amd64.whl", hash = "sha256:5ce2613e1361959bff81317dc30a60adb29d8132b6159608a783878fc4bc4bbc"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:deda9d79d1eb37b688729cac2dba0c137e992ebea960eadb7c2c255524c869e0"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a93e4770ac22794cfa651ee53f092d7de7105c76b9fc088bb81ca0dcf698f704"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:134e7f66703a1d92c0a9a0a417c580f2cc04b93d31d3fc0dd43c3aa194b706e1"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9796732a040f617fc933f9531c9a84bb73c5c27b8074abbe52907076e804b2b7"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ff3c1c32382fb71a200db8bab3df22f32e6ac7ec3170e92fa5b542cf42eed9a2"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:01f27b5b72505b304152cb0bd7ff410cc4f2d69ac70c21a7fdfa64400a68642d"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:80b6a562cbc92b289c28f34ce709a16b26b1696e9b9a0542a675ce3a788bdf3f"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c4fb90174d176473122e7eef7c6492d53761826f34ff61c81a1c1d66905025d3"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-win32.whl", hash = "sha256:c77b3f58a35f20363f6e0a1219b367fbf7e2d2efe3d2c32e1f796447e6061c10"},
|
||||
{file = "setproctitle-1.3.7-cp39-cp39-win_amd64.whl", hash = "sha256:318ddcf88dafddf33039ad41bc933e1c49b4cb196fe1731a209b753909591680"},
|
||||
{file = "setproctitle-1.3.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:eb440c5644a448e6203935ed60466ec8d0df7278cd22dc6cf782d07911bcbea6"},
|
||||
{file = "setproctitle-1.3.7-pp310-pypy310_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:502b902a0e4c69031b87870ff4986c290ebbb12d6038a70639f09c331b18efb2"},
|
||||
{file = "setproctitle-1.3.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f6f268caeabb37ccd824d749e7ce0ec6337c4ed954adba33ec0d90cc46b0ab78"},
|
||||
{file = "setproctitle-1.3.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1"},
|
||||
{file = "setproctitle-1.3.7-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65"},
|
||||
{file = "setproctitle-1.3.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a"},
|
||||
{file = "setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.2.1"
|
||||
description = "A lil' TOML parser"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
|
||||
{file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
|
||||
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.9+"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
|
||||
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.34.0"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026"},
|
||||
{file = "virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
distlib = ">=0.3.7,<1"
|
||||
filelock = ">=3.12.2,<4"
|
||||
platformdirs = ">=3.9.1,<5"
|
||||
typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.10,<4.0"
|
||||
content-hash = "f90241137e4e1197ca6a78485387f4227bbcb8ab61f83955c23cb2479ad3c8c4"
|
||||
@@ -1,2 +0,0 @@
|
||||
[virtualenvs]
|
||||
in-project = true
|
||||
113
pyproject.toml
113
pyproject.toml
@@ -1,66 +1,83 @@
|
||||
[project]
|
||||
name = "diceplayer"
|
||||
description = ""
|
||||
description = "Python Program for Molecular Optimization in Non-Vacuum Mediums"
|
||||
authors = [
|
||||
{name="Vitor Hideyoshi", email="vitor.h.n.batista@gmail.com"},
|
||||
{name="Herbert Georg", email="hcgeorg@ufg.br"}
|
||||
{ name = "Vitor Hideyoshi", email = "vitor.h.n.batista@gmail.com" },
|
||||
{ name = "Herbert Georg", email = "hcgeorg@ufg.br" }
|
||||
]
|
||||
license = "GPL-2.0-only"
|
||||
readme = "README.md"
|
||||
dynamic = [
|
||||
"version",
|
||||
"dependencies",
|
||||
"requires-python"
|
||||
license = "GPL-2.0-only"
|
||||
scripts = { diceplayer = "diceplayer.__main__:main" }
|
||||
requires-python = ">=3.10"
|
||||
dynamic = ["version"]
|
||||
|
||||
|
||||
dependencies = [
|
||||
"numpy>=2.2.0",
|
||||
"argparse>=1.4.0",
|
||||
"setproctitle>=1.3.2",
|
||||
"pyyaml>=6.0",
|
||||
"pydantic>=2.12.5",
|
||||
"typing-extensions>=4.15.0"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
diceplayer = "diceplayer.__main__:main"
|
||||
|
||||
|
||||
# POETRY
|
||||
[tool.poetry]
|
||||
packages = [
|
||||
{ include = "diceplayer" }
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"coverage>=7.2.7",
|
||||
"isort>=5.13.2",
|
||||
"black>=24.4.2",
|
||||
"pre-commit>=3.7.1",
|
||||
"poethepoet>=0.27.0",
|
||||
"ruff>=0.15.2",
|
||||
"pytest>=9.0.2",
|
||||
]
|
||||
version = "0.0.0"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<4.0"
|
||||
numpy = "^2.2.6"
|
||||
argparse = "^1.4.0"
|
||||
setproctitle = "^1.3.2"
|
||||
pyyaml = "^6.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
coverage = "^7.2.7"
|
||||
isort = "^5.13.2"
|
||||
black = "^24.4.2"
|
||||
pre-commit = "^3.7.1"
|
||||
poethepoet = "^0.27.0"
|
||||
|
||||
[tool.poetry.requires-plugins]
|
||||
poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }
|
||||
|
||||
[tool.poetry-dynamic-versioning]
|
||||
enable = true
|
||||
vcs = "git"
|
||||
# Build system
|
||||
[tool.hatch.version]
|
||||
source = "vcs"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
||||
build-backend = "poetry_dynamic_versioning.backend"
|
||||
requires = ["hatchling", "hatch-vcs"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
|
||||
# UTILS
|
||||
# POE Tasks
|
||||
[tool.poe.tasks]
|
||||
hooks = "pre-commit install --config .pre-commit-config.yaml"
|
||||
create-hooks = "bash .githooks/set-hooks.sh"
|
||||
tests = "python -m coverage run -m unittest discover -v"
|
||||
tests-report = "python -m coverage xml"
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
line_length = 79
|
||||
sections=[
|
||||
"FUTURE",
|
||||
"FIRSTPARTY",
|
||||
"LOCALFOLDER",
|
||||
"THIRDPARTY",
|
||||
"STDLIB",
|
||||
|
||||
|
||||
# Tests
|
||||
[tool.coverage.run]
|
||||
omit = [
|
||||
"tests/*",
|
||||
]
|
||||
|
||||
|
||||
|
||||
# Linters
|
||||
[tool.ruff.lint]
|
||||
extend-select = ["I"]
|
||||
ignore = [
|
||||
"E741" # ambiguous variable name 'l', 'O', or 'I'
|
||||
]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
known-first-party = ["jambo"]
|
||||
section-order=[
|
||||
"future",
|
||||
"first-party",
|
||||
"local-folder",
|
||||
"third-party",
|
||||
"standard-library",
|
||||
]
|
||||
lines-after-imports = 2
|
||||
|
||||
|
||||
[tool.pyright]
|
||||
venvPath = "."
|
||||
venv = ".venv"
|
||||
|
||||
30
tests/cli/test_read_input_file.py
Normal file
30
tests/cli/test_read_input_file.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import diceplayer
|
||||
from diceplayer.cli import read_input
|
||||
from diceplayer.config import PlayerConfig
|
||||
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestReadInputFile:
|
||||
@pytest.fixture
|
||||
def example_config(self) -> Path:
|
||||
return Path(diceplayer.__path__[0]).parent / "control.example.yml"
|
||||
|
||||
def test_read_input_file(self, example_config: Path):
|
||||
config = read_input(example_config)
|
||||
|
||||
assert config is not None
|
||||
assert isinstance(config, PlayerConfig)
|
||||
|
||||
def test_read_input_non_existing_file(self):
|
||||
with pytest.raises(FileNotFoundError):
|
||||
read_input("nonexistent_file.yml")
|
||||
|
||||
def test_read_input_invalid_yaml(self, tmp_path: Path):
|
||||
invalid_yaml_file = tmp_path / "invalid.yml"
|
||||
invalid_yaml_file.write_text("This is not valid YAML: [unbalanced brackets")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
read_input(invalid_yaml_file)
|
||||
@@ -1,11 +1,12 @@
|
||||
from diceplayer.shared.config.dice_config import DiceConfig
|
||||
from diceplayer.config.dice_config import DiceConfig
|
||||
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
|
||||
class TestDiceDto(unittest.TestCase):
|
||||
class TestDiceConfig:
|
||||
def test_class_instantiation(self):
|
||||
dice_dto = DiceConfig(
|
||||
nprocs=1,
|
||||
ljname="test",
|
||||
outname="test",
|
||||
dens=1.0,
|
||||
@@ -13,78 +14,77 @@ class TestDiceDto(unittest.TestCase):
|
||||
nstep=[1, 1],
|
||||
)
|
||||
|
||||
self.assertIsInstance(dice_dto, DiceConfig)
|
||||
assert isinstance(dice_dto, DiceConfig)
|
||||
|
||||
def test_validate_jname(self):
|
||||
with self.assertRaises(ValueError) as ex:
|
||||
with pytest.raises(ValueError) as ex:
|
||||
DiceConfig(
|
||||
nprocs=1,
|
||||
ljname=None,
|
||||
outname="test",
|
||||
dens=1.0,
|
||||
nmol=[1],
|
||||
nstep=[1, 1],
|
||||
)
|
||||
self.assertEqual(
|
||||
ex.exception, "Error: 'ljname' keyword not specified in config file"
|
||||
)
|
||||
|
||||
assert ex.value == "Error: 'ljname' keyword not specified in config file"
|
||||
|
||||
def test_validate_outname(self):
|
||||
with self.assertRaises(ValueError) as ex:
|
||||
with pytest.raises(ValueError) as ex:
|
||||
DiceConfig(
|
||||
nprocs=1,
|
||||
ljname="test",
|
||||
outname=None,
|
||||
dens=1.0,
|
||||
nmol=[1],
|
||||
nstep=[1, 1],
|
||||
)
|
||||
self.assertEqual(
|
||||
ex.exception, "Error: 'outname' keyword not specified in config file"
|
||||
)
|
||||
|
||||
assert ex.value == "Error: 'outname' keyword not specified in config file"
|
||||
|
||||
def test_validate_dens(self):
|
||||
with self.assertRaises(ValueError) as ex:
|
||||
with pytest.raises(ValueError) as ex:
|
||||
DiceConfig(
|
||||
nprocs=1,
|
||||
ljname="test",
|
||||
outname="test",
|
||||
dens=None,
|
||||
nmol=[1],
|
||||
nstep=[1, 1],
|
||||
)
|
||||
self.assertEqual(
|
||||
ex.exception, "Error: 'dens' keyword not specified in config file"
|
||||
)
|
||||
|
||||
assert ex.value == "Error: 'dens' keyword not specified in config file"
|
||||
|
||||
def test_validate_nmol(self):
|
||||
with self.assertRaises(ValueError) as ex:
|
||||
with pytest.raises(ValueError) as ex:
|
||||
DiceConfig(
|
||||
nprocs=1,
|
||||
ljname="test",
|
||||
outname="test",
|
||||
dens=1.0,
|
||||
nmol=0,
|
||||
nstep=[1, 1],
|
||||
)
|
||||
self.assertEqual(
|
||||
ex.exception,
|
||||
"Error: 'nmol' keyword not defined appropriately in config file",
|
||||
)
|
||||
|
||||
assert ex.value == "Error: 'nmol' keyword not specified in config file"
|
||||
|
||||
def test_validate_nstep(self):
|
||||
with self.assertRaises(ValueError) as ex:
|
||||
with pytest.raises(ValueError) as ex:
|
||||
DiceConfig(
|
||||
nprocs=1,
|
||||
ljname="test",
|
||||
outname="test",
|
||||
dens=1.0,
|
||||
nmol=[1],
|
||||
nstep=0,
|
||||
)
|
||||
self.assertEqual(
|
||||
ex.exception,
|
||||
"Error: 'nstep' keyword not defined appropriately in config file",
|
||||
)
|
||||
|
||||
assert ex.value == "Error: 'nstep' keyword not specified in config file"
|
||||
|
||||
def test_from_dict(self):
|
||||
dice_dto = DiceConfig.from_dict(
|
||||
dice_dto = DiceConfig.model_validate(
|
||||
{
|
||||
"nprocs": 1,
|
||||
"ljname": "test",
|
||||
"outname": "test",
|
||||
"dens": 1.0,
|
||||
@@ -93,4 +93,4 @@ class TestDiceDto(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
|
||||
self.assertIsInstance(dice_dto, DiceConfig)
|
||||
assert isinstance(dice_dto, DiceConfig)
|
||||
@@ -1,36 +1,36 @@
|
||||
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||
from diceplayer.config.gaussian_config import GaussianConfig
|
||||
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
|
||||
class TestGaussianDTO(unittest.TestCase):
|
||||
class TestGaussianConfig:
|
||||
def test_class_instantiation(self):
|
||||
gaussian_dto = GaussianDTO(
|
||||
gaussian_dto = GaussianConfig(
|
||||
level="test",
|
||||
qmprog="g16",
|
||||
keywords="test",
|
||||
)
|
||||
|
||||
self.assertIsInstance(gaussian_dto, GaussianDTO)
|
||||
assert isinstance(gaussian_dto, GaussianConfig)
|
||||
|
||||
def test_is_valid_qmprog(self):
|
||||
with self.assertRaises(ValueError):
|
||||
gaussian_dto = GaussianDTO(
|
||||
with pytest.raises(ValueError):
|
||||
GaussianConfig(
|
||||
level="test",
|
||||
qmprog="test",
|
||||
keywords="test",
|
||||
)
|
||||
|
||||
def test_is_valid_level(self):
|
||||
with self.assertRaises(ValueError):
|
||||
gaussian_dto = GaussianDTO(
|
||||
with pytest.raises(ValueError):
|
||||
GaussianConfig(
|
||||
level=None,
|
||||
qmprog="g16",
|
||||
keywords="test",
|
||||
)
|
||||
|
||||
def test_from_dict(self):
|
||||
gaussian_dto = GaussianDTO.from_dict(
|
||||
gaussian_dto = GaussianConfig.model_validate(
|
||||
{
|
||||
"level": "test",
|
||||
"qmprog": "g16",
|
||||
@@ -38,8 +38,4 @@ class TestGaussianDTO(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
|
||||
self.assertIsInstance(gaussian_dto, GaussianDTO)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
assert isinstance(gaussian_dto, GaussianConfig)
|
||||
90
tests/config/test_player_config.py
Normal file
90
tests/config/test_player_config.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from diceplayer.config.dice_config import DiceConfig
|
||||
from diceplayer.config.gaussian_config import GaussianConfig
|
||||
from diceplayer.config.player_config import PlayerConfig, RoutineType
|
||||
|
||||
import pytest
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
class TestPlayerConfig:
|
||||
@pytest.fixture
|
||||
def dice_payload(self) -> dict[str, Any]:
|
||||
return {
|
||||
"nprocs": 4,
|
||||
"ljname": "test",
|
||||
"outname": "test",
|
||||
"dens": 1.0,
|
||||
"nmol": [1],
|
||||
"nstep": [1, 1],
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def gaussian_payload(self) -> dict[str, Any]:
|
||||
return {
|
||||
"level": "test",
|
||||
"qmprog": "g16",
|
||||
"keywords": "test",
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def player_payload(
|
||||
self, dice_payload: dict[str, Any], gaussian_payload: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
return {
|
||||
"type": "both",
|
||||
"mem": 12,
|
||||
"max_cyc": 100,
|
||||
"switch_cyc": 50,
|
||||
"ncores": 4,
|
||||
"dice": dice_payload,
|
||||
"gaussian": gaussian_payload,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def dice_config(self, dice_payload: dict[str, Any]) -> DiceConfig:
|
||||
return DiceConfig.model_validate(dice_payload)
|
||||
|
||||
@pytest.fixture
|
||||
def gaussian_config(self, gaussian_payload: dict[str, Any]):
|
||||
return GaussianConfig.model_validate(gaussian_payload)
|
||||
|
||||
def test_class_instantiation(
|
||||
self, dice_config: DiceConfig, gaussian_config: GaussianConfig
|
||||
):
|
||||
player_dto = PlayerConfig(
|
||||
type=RoutineType.BOTH,
|
||||
mem=12,
|
||||
max_cyc=100,
|
||||
switch_cyc=50,
|
||||
ncores=4,
|
||||
dice=dice_config,
|
||||
gaussian=gaussian_config,
|
||||
)
|
||||
|
||||
assert isinstance(player_dto, PlayerConfig)
|
||||
assert isinstance(player_dto.dice, DiceConfig)
|
||||
assert isinstance(player_dto.gaussian, GaussianConfig)
|
||||
|
||||
def test_min_altsteps(
|
||||
self, dice_config: DiceConfig, gaussian_config: GaussianConfig
|
||||
):
|
||||
player_dto = PlayerConfig(
|
||||
type=RoutineType.BOTH,
|
||||
mem=12,
|
||||
max_cyc=100,
|
||||
switch_cyc=50,
|
||||
ncores=4,
|
||||
altsteps=0,
|
||||
dice=dice_config,
|
||||
gaussian=gaussian_config,
|
||||
)
|
||||
|
||||
assert player_dto.altsteps == 20000
|
||||
|
||||
def test_from_dict(self, player_payload: dict[str, Any]):
|
||||
player_dto = PlayerConfig.model_validate(player_payload)
|
||||
|
||||
assert isinstance(player_dto, PlayerConfig)
|
||||
assert isinstance(player_dto.dice, DiceConfig)
|
||||
assert isinstance(player_dto.gaussian, GaussianConfig)
|
||||
67
tests/dice/test_dice_input.py
Normal file
67
tests/dice/test_dice_input.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from diceplayer.config import PlayerConfig
|
||||
from diceplayer.dice.dice_input import (
|
||||
NPTEqConfig,
|
||||
NPTTerConfig,
|
||||
NVTEqConfig,
|
||||
NVTTerConfig,
|
||||
write_config,
|
||||
)
|
||||
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestDiceInput:
|
||||
@pytest.fixture
|
||||
def player_config(self) -> PlayerConfig:
|
||||
return PlayerConfig.model_validate(
|
||||
{
|
||||
"type": "both",
|
||||
"mem": 12,
|
||||
"max_cyc": 100,
|
||||
"switch_cyc": 50,
|
||||
"ncores": 4,
|
||||
"dice": {
|
||||
"nprocs": 4,
|
||||
"ljname": "test",
|
||||
"outname": "test",
|
||||
"dens": 1.0,
|
||||
"nmol": [1],
|
||||
"nstep": [1, 1],
|
||||
},
|
||||
"gaussian": {
|
||||
"level": "test",
|
||||
"qmprog": "g16",
|
||||
"keywords": "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
def test_generate_nvt_ter_input(self, player_config: PlayerConfig):
|
||||
dice_input = NVTTerConfig.from_config(player_config)
|
||||
|
||||
assert isinstance(dice_input, NVTTerConfig)
|
||||
|
||||
def test_generate_nvt_eq_input(self, player_config: PlayerConfig):
|
||||
dice_input = NVTEqConfig.from_config(player_config)
|
||||
|
||||
assert isinstance(dice_input, NVTEqConfig)
|
||||
|
||||
def test_generate_npt_ter_input(self, player_config: PlayerConfig):
|
||||
dice_input = NPTTerConfig.from_config(player_config)
|
||||
|
||||
assert isinstance(dice_input, NPTTerConfig)
|
||||
|
||||
def test_generate_npt_eq_input(self, player_config: PlayerConfig):
|
||||
dice_input = NPTEqConfig.from_config(player_config)
|
||||
|
||||
assert isinstance(dice_input, NPTEqConfig)
|
||||
|
||||
def test_write_dice_config(self, player_config: PlayerConfig, tmp_path: Path):
|
||||
dice_input = NVTTerConfig.from_config(player_config)
|
||||
|
||||
output_file = tmp_path / dice_input.type
|
||||
write_config(dice_input, tmp_path)
|
||||
|
||||
assert output_file.exists()
|
||||
@@ -1,4 +1,4 @@
|
||||
from diceplayer.shared.environment.atom import Atom
|
||||
from diceplayer.environment import Atom
|
||||
|
||||
import unittest
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from diceplayer.shared.environment.atom import Atom
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
from diceplayer.environment import Atom, Molecule
|
||||
|
||||
import numpy as np
|
||||
import numpy.testing as npt
|
||||
@@ -42,7 +41,7 @@ class TestMolecule(unittest.TestCase):
|
||||
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||
)
|
||||
|
||||
mol.center_of_mass_to_origin()
|
||||
mol.move_center_of_mass_to_origin()
|
||||
|
||||
npt.assert_equal(mol.com, [0, 0, 0])
|
||||
|
||||
@@ -69,12 +68,10 @@ class TestMolecule(unittest.TestCase):
|
||||
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||
)
|
||||
|
||||
expected_distance_between_atoms = [[1.73205081], [1.73205081]]
|
||||
actual_distance_between_atoms = mol.distances_between_atoms()
|
||||
expected = [[0.0, 1.73205081], [1.73205081, 0.0]]
|
||||
actual = mol.distances_between_atoms()
|
||||
|
||||
npt.assert_almost_equal(
|
||||
expected_distance_between_atoms, actual_distance_between_atoms
|
||||
)
|
||||
npt.assert_almost_equal(expected, actual)
|
||||
|
||||
def test_inertia_tensor(self):
|
||||
mol = Molecule("test")
|
||||
@@ -92,7 +89,7 @@ class TestMolecule(unittest.TestCase):
|
||||
[-0.50395, -0.50395, 1.0079],
|
||||
]
|
||||
|
||||
actual_inertia_tensor = mol.inertia_tensor()
|
||||
actual_inertia_tensor = mol.inertia_tensor
|
||||
|
||||
npt.assert_equal(expected_inertia_tensor, actual_inertia_tensor)
|
||||
|
||||
@@ -103,11 +100,14 @@ class TestMolecule(unittest.TestCase):
|
||||
Atom(lbl=1, na=1, rx=0.0, ry=0.0, rz=0.0, chg=1.0, eps=1.0, sig=1.0)
|
||||
)
|
||||
|
||||
expected_evals, expected_evecs = [0.0, 0.0, 0.0], [
|
||||
expected_evals, expected_evecs = (
|
||||
[0.0, 0.0, 0.0],
|
||||
[
|
||||
[1.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 1.0],
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
evals, evecs = mol.principal_axes()
|
||||
|
||||
@@ -161,7 +161,7 @@ class TestMolecule(unittest.TestCase):
|
||||
Atom(lbl=1, na=1, rx=1.0, ry=1.0, rz=1.0, chg=1.0, eps=1.0, sig=1.0)
|
||||
)
|
||||
|
||||
mol.standard_orientation()
|
||||
mol.rotate_to_standard_orientation()
|
||||
|
||||
expected_position = [0.0, 0.0, 0.0]
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
from diceplayer.shared.environment.atom import Atom
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
from diceplayer.shared.environment.system import System
|
||||
from diceplayer.environment import Molecule, System
|
||||
|
||||
import unittest
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
from unittest import mock
|
||||
|
||||
|
||||
def get_config_example():
|
||||
return """
|
||||
diceplayer:
|
||||
opt: no
|
||||
mem: 12
|
||||
maxcyc: 3
|
||||
ncores: 4
|
||||
nprocs: 4
|
||||
qmprog: 'g16'
|
||||
lps: no
|
||||
ghosts: no
|
||||
altsteps: 20000
|
||||
|
||||
dice:
|
||||
nmol: [1, 50]
|
||||
dens: 0.75
|
||||
nstep: [2000, 3000, 4000]
|
||||
isave: 1000
|
||||
outname: 'phb'
|
||||
progname: '~/.local/bin/dice'
|
||||
ljname: 'phb.ljc'
|
||||
randominit: 'first'
|
||||
|
||||
gaussian:
|
||||
qmprog: 'g16'
|
||||
level: 'MP2/aug-cc-pVDZ'
|
||||
keywords: 'freq'
|
||||
"""
|
||||
|
||||
|
||||
def get_potentials_exemple():
|
||||
return """\
|
||||
*
|
||||
2
|
||||
1 TEST
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
1 PLACEHOLDER
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
"""
|
||||
|
||||
|
||||
def get_potentials_error_combrule():
|
||||
return """\
|
||||
.
|
||||
2
|
||||
1 TEST
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
1 PLACEHOLDER
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
"""
|
||||
|
||||
|
||||
def get_potentials_error_ntypes():
|
||||
return """\
|
||||
*
|
||||
a
|
||||
1 TEST
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
1 PLACEHOLDER
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
"""
|
||||
|
||||
|
||||
def get_potentials_error_ntypes_config():
|
||||
return """\
|
||||
*
|
||||
3
|
||||
1 TEST
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
1 PLACEHOLDER
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
"""
|
||||
|
||||
|
||||
def get_potentials_error_nsites():
|
||||
return """\
|
||||
*
|
||||
2
|
||||
. TEST
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
1 PLACEHOLDER
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
"""
|
||||
|
||||
|
||||
def get_potentials_error_molname():
|
||||
return """\
|
||||
*
|
||||
2
|
||||
1
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
1 PLACEHOLDER
|
||||
1 1 0.000000 0.000000 0.000000 0.000000 0.0000 0.0000
|
||||
"""
|
||||
|
||||
|
||||
def mock_open(file, *args, **kwargs):
|
||||
values = {
|
||||
"control.test.yml": get_config_example(),
|
||||
"phb.ljc": get_potentials_exemple(),
|
||||
"phb.error.combrule.ljc": get_potentials_error_combrule(),
|
||||
"phb.error.ntypes.ljc": get_potentials_error_ntypes(),
|
||||
"phb.error.ntypes.config.ljc": get_potentials_error_ntypes_config(),
|
||||
"phb.error.nsites.ljc": get_potentials_error_nsites(),
|
||||
"phb.error.molname.ljc": get_potentials_error_molname(),
|
||||
}
|
||||
if file in values:
|
||||
return mock.mock_open(read_data=values[file])()
|
||||
|
||||
return mock.mock_open(read_data="")()
|
||||
@@ -1,31 +0,0 @@
|
||||
import itertools
|
||||
from typing import List
|
||||
|
||||
|
||||
class MockProc:
|
||||
pid_counter = itertools.count()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.pid = next(MockProc.pid_counter)
|
||||
|
||||
if "exitcode" in kwargs:
|
||||
self.exitcode = kwargs["exitcode"]
|
||||
else:
|
||||
self.exitcode = 0
|
||||
|
||||
self.sentinel = self.pid
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
def terminate(self):
|
||||
pass
|
||||
|
||||
|
||||
class MockConnection:
|
||||
@staticmethod
|
||||
def wait(sentinels: List[int]):
|
||||
return sentinels
|
||||
@@ -1,83 +0,0 @@
|
||||
from diceplayer.shared.config.dice_config import DiceConfig
|
||||
from diceplayer.shared.config.gaussian_config import GaussianDTO
|
||||
from diceplayer.shared.config.player_config import PlayerConfig
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
def get_config_dict():
|
||||
return {
|
||||
"opt": True,
|
||||
"mem": 12,
|
||||
"maxcyc": 100,
|
||||
"nprocs": 4,
|
||||
"ncores": 4,
|
||||
"dice": {
|
||||
"ljname": "test",
|
||||
"outname": "test",
|
||||
"dens": 1.0,
|
||||
"nmol": [1],
|
||||
"nstep": [1, 1],
|
||||
},
|
||||
"gaussian": {
|
||||
"level": "test",
|
||||
"qmprog": "g16",
|
||||
"keywords": "test",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestPlayerDTO(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.dice_dto = DiceConfig(
|
||||
ljname="test",
|
||||
outname="test",
|
||||
dens=1.0,
|
||||
nmol=[1],
|
||||
nstep=[1, 1],
|
||||
)
|
||||
self.gaussian_dto = GaussianDTO(
|
||||
level="test",
|
||||
qmprog="g16",
|
||||
keywords="test",
|
||||
)
|
||||
|
||||
def test_class_instantiation(self):
|
||||
player_dto = PlayerConfig(
|
||||
opt=True,
|
||||
mem=12,
|
||||
maxcyc=100,
|
||||
nprocs=4,
|
||||
ncores=4,
|
||||
dice=self.dice_dto,
|
||||
gaussian=self.gaussian_dto,
|
||||
)
|
||||
|
||||
self.assertIsInstance(player_dto, PlayerConfig)
|
||||
self.assertIsInstance(player_dto.dice, DiceConfig)
|
||||
self.assertIsInstance(player_dto.gaussian, GaussianDTO)
|
||||
|
||||
def test_min_altsteps(self):
|
||||
player_dto = PlayerConfig(
|
||||
opt=True,
|
||||
mem=12,
|
||||
maxcyc=100,
|
||||
nprocs=4,
|
||||
ncores=4,
|
||||
altsteps=100,
|
||||
dice=self.dice_dto,
|
||||
gaussian=self.gaussian_dto,
|
||||
)
|
||||
|
||||
self.assertEqual(player_dto.altsteps, 20000)
|
||||
|
||||
def test_from_dict(self):
|
||||
player_dto = PlayerConfig.from_dict(get_config_dict())
|
||||
|
||||
self.assertIsInstance(player_dto, PlayerConfig)
|
||||
self.assertIsInstance(player_dto.dice, DiceConfig)
|
||||
self.assertIsInstance(player_dto.gaussian, GaussianDTO)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,661 +0,0 @@
|
||||
from diceplayer import logger
|
||||
from diceplayer.shared.config.player_config import PlayerConfig
|
||||
from diceplayer.shared.environment.atom import Atom
|
||||
from diceplayer.shared.environment.molecule import Molecule
|
||||
from diceplayer.shared.environment.system import System
|
||||
from diceplayer.shared.interface.dice_interface import DiceInterface
|
||||
from tests.mocks.mock_inputs import get_config_example
|
||||
from tests.mocks.mock_proc import MockConnection, MockProc
|
||||
|
||||
import yaml
|
||||
|
||||
import io
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
|
||||
class TestDiceInterface(unittest.TestCase):
|
||||
def setUp(self):
|
||||
logger.set_logger(stream=io.StringIO())
|
||||
|
||||
config = yaml.load(get_config_example(), Loader=yaml.Loader)
|
||||
self.config = PlayerConfig.from_dict(config["diceplayer"])
|
||||
|
||||
def test_class_instantiation(self):
|
||||
dice = DiceInterface()
|
||||
|
||||
self.assertIsInstance(dice, DiceInterface)
|
||||
|
||||
def test_configure(self):
|
||||
dice = DiceInterface()
|
||||
|
||||
self.assertIsNone(dice.step)
|
||||
self.assertIsNone(dice.system)
|
||||
|
||||
# Ignoring the types for testing purposes
|
||||
dice.configure(self.config, System())
|
||||
|
||||
self.assertIsNotNone(dice.step)
|
||||
self.assertIsNotNone(dice.system)
|
||||
|
||||
def test_reset(self):
|
||||
dice = DiceInterface()
|
||||
|
||||
dice.configure(self.config, System())
|
||||
|
||||
self.assertTrue(hasattr(dice, "step"))
|
||||
self.assertTrue(hasattr(dice, "system"))
|
||||
|
||||
dice.reset()
|
||||
|
||||
self.assertFalse(hasattr(dice, "step"))
|
||||
self.assertFalse(hasattr(dice, "system"))
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.Process", MockProc())
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.connection", MockConnection)
|
||||
def test_start(self):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.start(1)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.connection", MockConnection)
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.Process", MockProc(exitcode=1)
|
||||
)
|
||||
def test_start_with_process_error(self):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
with self.assertRaises(SystemExit):
|
||||
dice.start(1)
|
||||
|
||||
def test_simulation_process_raises_exception(self):
|
||||
dice = DiceInterface()
|
||||
|
||||
with self.assertRaises(SystemExit):
|
||||
dice._simulation_process(1, 1)
|
||||
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.DiceInterface._make_proc_dir"
|
||||
)
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.DiceInterface._make_dice_inputs"
|
||||
)
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.DiceInterface._run_dice")
|
||||
def test_simulation_process(
|
||||
self, mock_run_dice, mock_make_dice_inputs, mock_make_proc_dir
|
||||
):
|
||||
dice = DiceInterface()
|
||||
|
||||
dice._simulation_process(1, 1)
|
||||
|
||||
self.assertTrue(dice._make_proc_dir.called)
|
||||
self.assertTrue(dice._make_dice_inputs.called)
|
||||
self.assertTrue(dice._run_dice.called)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.Path.mkdir")
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.Path.exists")
|
||||
def test_make_proc_dir_if_simdir_exists(self, mock_path_exists, mock_path_mkdir):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
mock_path_exists.return_value = False
|
||||
|
||||
dice._make_proc_dir(1, 1)
|
||||
|
||||
self.assertEqual(mock_path_mkdir.call_count, 2)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.Path.mkdir")
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.Path.exists")
|
||||
def test_make_proc_dir_if_simdir_doesnt_exists(
|
||||
self, mock_path_exists, mock_path_mkdir
|
||||
):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
mock_path_exists.return_value = False
|
||||
|
||||
dice._make_proc_dir(1, 1)
|
||||
|
||||
self.assertEqual(mock_path_mkdir.call_count, 2)
|
||||
|
||||
def test_make_dice_seed(self):
|
||||
seed = DiceInterface._make_dice_seed()
|
||||
|
||||
self.assertIsInstance(seed, int)
|
||||
|
||||
def test_make_dice_inputs_nstep_len_two_with_randoninit_first_cycle_one(self):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.step.dice.nstep = [1, 1]
|
||||
|
||||
dice._make_potentials = mock.Mock()
|
||||
|
||||
dice._make_init_file = mock.Mock()
|
||||
dice._new_density = mock.Mock()
|
||||
|
||||
dice._make_nvt_ter = mock.Mock()
|
||||
dice._make_nvt_eq = mock.Mock()
|
||||
dice._make_npt_ter = mock.Mock()
|
||||
dice._make_npt_eq = mock.Mock()
|
||||
|
||||
dice._make_dice_inputs(1, 1)
|
||||
|
||||
self.assertTrue(dice._make_potentials.called)
|
||||
|
||||
self.assertFalse(dice._make_init_file.called)
|
||||
self.assertFalse(dice._new_density.called)
|
||||
|
||||
self.assertTrue(dice._make_nvt_ter.called)
|
||||
self.assertTrue(dice._make_nvt_eq.called)
|
||||
|
||||
self.assertFalse(dice._make_npt_ter.called)
|
||||
self.assertFalse(dice._make_npt_eq.called)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="test")
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.Path.exists", return_value=True
|
||||
)
|
||||
def test_make_dice_inputs_nstep_len_two_with_randoninit_first_cycle_two(
|
||||
self, mock_path_exists, mock_open
|
||||
):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.step.dice.nstep = [1, 1]
|
||||
|
||||
dice._make_potentials = mock.Mock()
|
||||
|
||||
dice._make_init_file = mock.Mock()
|
||||
dice._new_density = mock.Mock()
|
||||
|
||||
dice._make_nvt_ter = mock.Mock()
|
||||
dice._make_nvt_eq = mock.Mock()
|
||||
dice._make_npt_ter = mock.Mock()
|
||||
dice._make_npt_eq = mock.Mock()
|
||||
|
||||
dice._make_dice_inputs(2, 1)
|
||||
|
||||
self.assertTrue(dice._make_potentials.called)
|
||||
|
||||
self.assertTrue(dice._make_init_file.called)
|
||||
self.assertTrue(dice._new_density.called)
|
||||
|
||||
self.assertFalse(dice._make_nvt_ter.called)
|
||||
self.assertTrue(dice._make_nvt_eq.called)
|
||||
|
||||
self.assertFalse(dice._make_npt_ter.called)
|
||||
self.assertFalse(dice._make_npt_eq.called)
|
||||
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.Path.exists", return_value=False
|
||||
)
|
||||
def test_make_dice_inputs_raises_exception_on_last_not_found(
|
||||
self, mock_path_exists
|
||||
):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.step.dice.nstep = [1, 1]
|
||||
|
||||
dice._make_potentials = mock.Mock()
|
||||
|
||||
dice._make_init_file = mock.Mock()
|
||||
dice._new_density = mock.Mock()
|
||||
|
||||
dice._make_nvt_ter = mock.Mock()
|
||||
dice._make_nvt_eq = mock.Mock()
|
||||
dice._make_npt_ter = mock.Mock()
|
||||
dice._make_npt_eq = mock.Mock()
|
||||
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
dice._make_dice_inputs(2, 1)
|
||||
|
||||
def test_make_dice_inputs_nstep_len_three_with_randoninit_first_cycle_one(self):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice._make_potentials = mock.Mock()
|
||||
|
||||
dice._make_init_file = mock.Mock()
|
||||
dice._new_density = mock.Mock()
|
||||
|
||||
dice._make_nvt_ter = mock.Mock()
|
||||
dice._make_nvt_eq = mock.Mock()
|
||||
dice._make_npt_ter = mock.Mock()
|
||||
dice._make_npt_eq = mock.Mock()
|
||||
|
||||
dice._make_dice_inputs(1, 1)
|
||||
|
||||
self.assertTrue(dice._make_potentials.called)
|
||||
|
||||
self.assertFalse(dice._make_init_file.called)
|
||||
self.assertFalse(dice._new_density.called)
|
||||
|
||||
self.assertTrue(dice._make_nvt_ter.called)
|
||||
self.assertFalse(dice._make_nvt_eq.called)
|
||||
|
||||
self.assertTrue(dice._make_npt_ter.called)
|
||||
self.assertTrue(dice._make_npt_eq.called)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.os")
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.shutil")
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.Path.exists", return_value=True
|
||||
)
|
||||
def test_run_dice_on_first_cycle_run_successful(
|
||||
self, mock_path_exists, mock_shutils, mock_os
|
||||
):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.step.dice.nstep = [1, 1, 1]
|
||||
|
||||
dice.run_dice_file = mock.Mock()
|
||||
|
||||
dice._run_dice(1, 1)
|
||||
|
||||
self.assertTrue(mock_os.getcwd.called)
|
||||
self.assertTrue(mock_os.chdir.called)
|
||||
|
||||
self.assertEqual(dice.run_dice_file.call_count, 3)
|
||||
self.assertTrue(mock_shutils.copy.called)
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.step.dice.nstep = [1, 1]
|
||||
|
||||
dice.run_dice_file = mock.Mock()
|
||||
|
||||
dice._run_dice(1, 1)
|
||||
|
||||
self.assertTrue(mock_os.getcwd.called)
|
||||
self.assertTrue(mock_os.chdir.called)
|
||||
|
||||
self.assertEqual(dice.run_dice_file.call_count, 2)
|
||||
self.assertTrue(mock_shutils.copy.called)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.os")
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.shutil")
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.Path.exists", return_value=True
|
||||
)
|
||||
def test_run_dice_on_second_cycle_run_successful(
|
||||
self, mock_path_exists, mock_shutils, mock_os
|
||||
):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.run_dice_file = mock.Mock()
|
||||
|
||||
dice._run_dice(2, 1)
|
||||
|
||||
self.assertTrue(mock_os.getcwd.called)
|
||||
self.assertTrue(mock_os.chdir.called)
|
||||
|
||||
self.assertEqual(dice.run_dice_file.call_count, 2)
|
||||
self.assertTrue(mock_shutils.copy.called)
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.run_dice_file = mock.Mock()
|
||||
|
||||
dice._run_dice(2, 1)
|
||||
|
||||
self.assertTrue(mock_os.getcwd.called)
|
||||
self.assertTrue(mock_os.chdir.called)
|
||||
|
||||
self.assertEqual(dice.run_dice_file.call_count, 1)
|
||||
self.assertTrue(mock_shutils.copy.called)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.os")
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.shutil")
|
||||
@mock.patch(
|
||||
"diceplayer.shared.interface.dice_interface.Path.exists", return_value=False
|
||||
)
|
||||
def test_run_dice_on_second_cycle_run_successful(
|
||||
self, mock_path_exists, mock_shutils, mock_os
|
||||
):
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.run_dice_file = mock.Mock()
|
||||
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
dice._run_dice(1, 1)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
||||
def test_make_init_file(self, mock_open):
|
||||
example_atom = Atom(
|
||||
lbl=1,
|
||||
na=1,
|
||||
rx=1.0,
|
||||
ry=1.0,
|
||||
rz=1.0,
|
||||
chg=1.0,
|
||||
eps=1.0,
|
||||
sig=1.0,
|
||||
)
|
||||
|
||||
main_molecule = Molecule("main_molecule")
|
||||
main_molecule.add_atom(example_atom)
|
||||
|
||||
secondary_molecule = Molecule("secondary_molecule")
|
||||
secondary_molecule.add_atom(example_atom)
|
||||
|
||||
system = System()
|
||||
system.add_type(main_molecule)
|
||||
system.add_type(secondary_molecule)
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, system)
|
||||
|
||||
dice.step.dice.nmol = [1, 1]
|
||||
|
||||
last_xyz_file = io.StringIO()
|
||||
last_xyz_file.writelines(
|
||||
[
|
||||
" TEST\n",
|
||||
" Configuration number : TEST = TEST TEST TEST\n",
|
||||
" H 1.00000 1.00000 1.00000\n",
|
||||
" H 1.00000 1.00000 1.00000\n",
|
||||
]
|
||||
)
|
||||
last_xyz_file.seek(0)
|
||||
|
||||
dice._make_init_file("test", last_xyz_file)
|
||||
|
||||
mock_handler = mock_open()
|
||||
calls = mock_handler.write.call_args_list
|
||||
|
||||
lines = list(map(lambda x: x[0][0], calls))
|
||||
|
||||
expected_lines = [
|
||||
" 1.000000 1.000000 1.000000\n",
|
||||
" 1.000000 1.000000 1.000000\n",
|
||||
"$end",
|
||||
]
|
||||
|
||||
self.assertEqual(lines, expected_lines)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
||||
def test_new_density(self, mock_open):
|
||||
example_atom = Atom(
|
||||
lbl=1,
|
||||
na=1,
|
||||
rx=1.0,
|
||||
ry=1.0,
|
||||
rz=1.0,
|
||||
chg=1.0,
|
||||
eps=1.0,
|
||||
sig=1.0,
|
||||
)
|
||||
|
||||
main_molecule = Molecule("main_molecule")
|
||||
main_molecule.add_atom(example_atom)
|
||||
|
||||
secondary_molecule = Molecule("secondary_molecule")
|
||||
secondary_molecule.add_atom(example_atom)
|
||||
|
||||
system = System()
|
||||
system.add_type(main_molecule)
|
||||
system.add_type(secondary_molecule)
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, system)
|
||||
|
||||
last_xyz_file = io.StringIO()
|
||||
last_xyz_file.writelines(
|
||||
[
|
||||
" TEST\n",
|
||||
" Configuration number : TEST = 1 1 1\n",
|
||||
" H 1.00000 1.00000 1.00000\n",
|
||||
" H 1.00000 1.00000 1.00000\n",
|
||||
]
|
||||
)
|
||||
last_xyz_file.seek(0)
|
||||
|
||||
density = dice._new_density(last_xyz_file)
|
||||
|
||||
self.assertEqual(density, 85.35451545000001)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.random")
|
||||
def test_make_nvt_ter(self, mock_random, mock_open):
|
||||
mock_random.random.return_value = 1
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice._make_nvt_ter(1, "test")
|
||||
|
||||
mock_handler = mock_open()
|
||||
calls = mock_handler.write.call_args_list
|
||||
|
||||
lines = list(map(lambda x: x[0][0], calls))
|
||||
|
||||
expected_lines = [
|
||||
"title = Diceplayer run - NVT Thermalization\n",
|
||||
"ncores = 4\n",
|
||||
"ljname = phb.ljc\n",
|
||||
"outname = phb\n",
|
||||
"nmol = 1 50\n",
|
||||
"dens = 0.75\n",
|
||||
"temp = 300.0\n",
|
||||
"init = yes\n",
|
||||
"nstep = 2000\n",
|
||||
"vstep = 0\n",
|
||||
"mstop = 1\n",
|
||||
"accum = no\n",
|
||||
"iprint = 1\n",
|
||||
"isave = 0\n",
|
||||
"irdf = 0\n",
|
||||
"seed = 1000000\n",
|
||||
"upbuf = 360",
|
||||
]
|
||||
|
||||
self.assertEqual(lines, expected_lines)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.random")
|
||||
def test_make_nvt_eq(self, mock_random, mock_open):
|
||||
mock_random.random.return_value = 1
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice._make_nvt_eq(1, "test")
|
||||
|
||||
mock_handler = mock_open()
|
||||
calls = mock_handler.write.call_args_list
|
||||
|
||||
lines = list(map(lambda x: x[0][0], calls))
|
||||
|
||||
expected_lines = [
|
||||
"title = Diceplayer run - NVT Production\n",
|
||||
"ncores = 4\n",
|
||||
"ljname = phb.ljc\n",
|
||||
"outname = phb\n",
|
||||
"nmol = 1 50\n",
|
||||
"dens = 0.75\n",
|
||||
"temp = 300.0\n",
|
||||
"init = no\n",
|
||||
"nstep = 3000\n",
|
||||
"vstep = 0\n",
|
||||
"mstop = 1\n",
|
||||
"accum = no\n",
|
||||
"iprint = 1\n",
|
||||
"isave = 1000\n",
|
||||
"irdf = 40\n",
|
||||
"seed = 1000000\n",
|
||||
]
|
||||
|
||||
self.assertEqual(lines, expected_lines)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.random")
|
||||
def test_make_npt_ter(self, mock_random, mock_open):
|
||||
mock_random.random.return_value = 1
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice._make_npt_ter(1, "test")
|
||||
|
||||
mock_handler = mock_open()
|
||||
calls = mock_handler.write.call_args_list
|
||||
|
||||
lines = list(map(lambda x: x[0][0], calls))
|
||||
|
||||
expected_lines = [
|
||||
"title = Diceplayer run - NPT Thermalization\n",
|
||||
"ncores = 4\n",
|
||||
"ljname = phb.ljc\n",
|
||||
"outname = phb\n",
|
||||
"nmol = 1 50\n",
|
||||
"press = 1.0\n",
|
||||
"temp = 300.0\n",
|
||||
"init = no\n",
|
||||
"vstep = 600\n",
|
||||
"nstep = 5\n",
|
||||
"mstop = 1\n",
|
||||
"accum = no\n",
|
||||
"iprint = 1\n",
|
||||
"isave = 0\n",
|
||||
"irdf = 0\n",
|
||||
"seed = 1000000\n",
|
||||
]
|
||||
|
||||
self.assertEqual(lines, expected_lines)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.random")
|
||||
def test_make_npt_eq(self, mock_random, mock_open):
|
||||
mock_random.random.return_value = 1
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice._make_npt_eq("test")
|
||||
|
||||
mock_handler = mock_open()
|
||||
calls = mock_handler.write.call_args_list
|
||||
|
||||
lines = list(map(lambda x: x[0][0], calls))
|
||||
|
||||
expected_lines = [
|
||||
"title = Diceplayer run - NPT Production\n",
|
||||
"ncores = 4\n",
|
||||
"ljname = phb.ljc\n",
|
||||
"outname = phb\n",
|
||||
"nmol = 1 50\n",
|
||||
"press = 1.0\n",
|
||||
"temp = 300.0\n",
|
||||
"nstep = 5\n",
|
||||
"vstep = 800\n",
|
||||
"init = no\n",
|
||||
"mstop = 1\n",
|
||||
"accum = no\n",
|
||||
"iprint = 1\n",
|
||||
"isave = 1000\n",
|
||||
"irdf = 40\n",
|
||||
"seed = 1000000\n",
|
||||
]
|
||||
|
||||
self.assertEqual(lines, expected_lines)
|
||||
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open)
|
||||
def test_make_potentials(self, mock_open):
|
||||
example_atom = Atom(
|
||||
lbl=1,
|
||||
na=1,
|
||||
rx=1.0,
|
||||
ry=1.0,
|
||||
rz=1.0,
|
||||
chg=1.0,
|
||||
eps=1.0,
|
||||
sig=1.0,
|
||||
)
|
||||
|
||||
main_molecule = Molecule("main_molecule")
|
||||
main_molecule.add_atom(example_atom)
|
||||
|
||||
secondary_molecule = Molecule("secondary_molecule")
|
||||
secondary_molecule.add_atom(example_atom)
|
||||
|
||||
system = System()
|
||||
system.add_type(main_molecule)
|
||||
system.add_type(secondary_molecule)
|
||||
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, system)
|
||||
|
||||
dice._make_potentials("test")
|
||||
|
||||
mock_handler = mock_open()
|
||||
calls = mock_handler.write.call_args_list
|
||||
|
||||
lines = list(map(lambda x: x[0][0], calls))
|
||||
|
||||
expected_lines = [
|
||||
"*\n",
|
||||
"2\n",
|
||||
"1 main_molecule\n",
|
||||
"1 1 1.00000 1.00000 1.00000 1.000000 1.00000 1.0000\n",
|
||||
"1 secondary_molecule\n",
|
||||
"1 1 1.00000 1.00000 1.00000 1.000000 1.00000 1.0000\n",
|
||||
]
|
||||
|
||||
self.assertEqual(lines, expected_lines)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.subprocess")
|
||||
@mock.patch(
|
||||
"builtins.open",
|
||||
new_callable=mock.mock_open,
|
||||
read_data="End of simulation\nBLABLA",
|
||||
)
|
||||
def test_run_dice_file(self, mock_open, mock_subprocess):
|
||||
mock_subprocess.call.return_value = 0
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
dice.run_dice_file(1, 1, "test")
|
||||
|
||||
self.assertTrue(mock_subprocess.call.called)
|
||||
self.assertTrue(mock_open.called)
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.subprocess")
|
||||
@mock.patch("builtins.open", new_callable=mock.mock_open, read_data="Error\nBLABLA")
|
||||
def test_run_dice_file_raises_runtime_error_on_dice_file(
|
||||
self, mock_open, mock_subprocess
|
||||
):
|
||||
mock_subprocess.call.return_value = 0
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
dice.run_dice_file(1, 1, "test")
|
||||
|
||||
@mock.patch("diceplayer.shared.interface.dice_interface.subprocess")
|
||||
@mock.patch(
|
||||
"builtins.open",
|
||||
new_callable=mock.mock_open,
|
||||
read_data="End of simulation\nBLABLA",
|
||||
)
|
||||
def test_run_dice_file_raises_runtime_error_of_dice_exit_code(
|
||||
self, mock_open, mock_subprocess
|
||||
):
|
||||
mock_subprocess.call.return_value = 1
|
||||
dice = DiceInterface()
|
||||
dice.configure(self.config, System())
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
dice.run_dice_file(1, 1, "test")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,115 +0,0 @@
|
||||
from diceplayer import logger
|
||||
from diceplayer.shared.config.player_config import PlayerConfig
|
||||
from diceplayer.shared.environment.system import System
|
||||
from diceplayer.shared.interface.gaussian_interface import GaussianInterface
|
||||
from tests.mocks.mock_inputs import get_config_example
|
||||
|
||||
import yaml
|
||||
|
||||
import io
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
|
||||
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()
|
||||
@@ -1,132 +0,0 @@
|
||||
from diceplayer.shared.utils.logger import Logger, valid_logger
|
||||
|
||||
import io
|
||||
import logging
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
|
||||
class TestValidateLogger(unittest.TestCase):
|
||||
def test_validate_logger(self):
|
||||
class MockLogger:
|
||||
_was_set = True
|
||||
|
||||
@valid_logger
|
||||
def test_func(self):
|
||||
pass
|
||||
|
||||
MockLogger().test_func()
|
||||
|
||||
def test_validate_logger_exception(self):
|
||||
class MockLogger:
|
||||
_was_set = False
|
||||
|
||||
@valid_logger
|
||||
def test_func(self):
|
||||
pass
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
MockLogger().test_func()
|
||||
|
||||
|
||||
class TestLogger(unittest.TestCase):
|
||||
def test_class_instantiation(self):
|
||||
logger = Logger("test")
|
||||
|
||||
self.assertIsInstance(logger, Logger)
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
def test_set_logger_to_file(self):
|
||||
logger = Logger("test")
|
||||
|
||||
logger.set_logger(stream=io.StringIO())
|
||||
|
||||
self.assertIsNotNone(logger._logger)
|
||||
self.assertEqual(logger._logger.name, "test")
|
||||
|
||||
def test_set_logger_to_stream(self):
|
||||
logger = Logger("test")
|
||||
|
||||
logger.set_logger(stream=io.StringIO())
|
||||
|
||||
self.assertIsNotNone(logger._logger)
|
||||
self.assertEqual(logger._logger.name, "test")
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
@mock.patch("diceplayer.shared.utils.logger.Path.exists")
|
||||
@mock.patch("diceplayer.shared.utils.logger.Path.rename")
|
||||
def test_set_logger_if_file_exists(self, mock_rename, mock_exists):
|
||||
logger = Logger("test")
|
||||
|
||||
mock_exists.return_value = True
|
||||
logger.set_logger()
|
||||
|
||||
self.assertTrue(mock_rename.called)
|
||||
self.assertIsNotNone(logger._logger)
|
||||
self.assertEqual(logger._logger.name, "test")
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
@mock.patch("diceplayer.shared.utils.logger.Path.exists")
|
||||
@mock.patch("diceplayer.shared.utils.logger.Path.rename")
|
||||
def test_set_logger_if_file_not_exists(self, mock_rename, mock_exists):
|
||||
logger = Logger("test")
|
||||
|
||||
mock_exists.return_value = False
|
||||
logger.set_logger()
|
||||
|
||||
self.assertFalse(mock_rename.called)
|
||||
self.assertIsNotNone(logger._logger)
|
||||
self.assertEqual(logger._logger.name, "test")
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
def test_close(self):
|
||||
logger = Logger("test")
|
||||
|
||||
logger.set_logger()
|
||||
logger.close()
|
||||
|
||||
self.assertEqual(len(logger._logger.handlers), 0)
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
def test_info(self):
|
||||
logger = Logger("test")
|
||||
logger.set_logger(stream=io.StringIO())
|
||||
|
||||
with self.assertLogs(level="INFO") as cm:
|
||||
logger.info("test")
|
||||
|
||||
self.assertEqual(cm.output, ["INFO:test:test"])
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
def test_debug(self):
|
||||
logger = Logger("test")
|
||||
logger.set_logger(stream=io.StringIO(), level=logging.DEBUG)
|
||||
|
||||
with self.assertLogs(level="DEBUG") as cm:
|
||||
logger.debug("test")
|
||||
|
||||
self.assertEqual(cm.output, ["DEBUG:test:test"])
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
def test_warning(self):
|
||||
logger = Logger("test")
|
||||
logger.set_logger(stream=io.StringIO())
|
||||
|
||||
with self.assertLogs(level="WARNING") as cm:
|
||||
logger.warning("test")
|
||||
|
||||
self.assertEqual(cm.output, ["WARNING:test:test"])
|
||||
|
||||
@mock.patch("builtins.open", mock.mock_open())
|
||||
def test_error(self):
|
||||
logger = Logger("test")
|
||||
logger.set_logger(stream=io.StringIO())
|
||||
|
||||
with self.assertLogs(level="ERROR") as cm:
|
||||
logger.error("test")
|
||||
|
||||
self.assertEqual(cm.output, ["ERROR:test:test"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
118
tests/state/test_state_handler.py
Normal file
118
tests/state/test_state_handler.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from diceplayer.config import DiceConfig, GaussianConfig, PlayerConfig
|
||||
from diceplayer.environment import System
|
||||
from diceplayer.state.state_handler import StateHandler
|
||||
from diceplayer.state.state_model import StateModel
|
||||
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestStateHandler:
|
||||
@pytest.fixture
|
||||
def player_config(self) -> PlayerConfig:
|
||||
return PlayerConfig(
|
||||
type="both",
|
||||
mem=12,
|
||||
max_cyc=100,
|
||||
switch_cyc=50,
|
||||
ncores=4,
|
||||
dice=DiceConfig(
|
||||
nprocs=4,
|
||||
ljname="test",
|
||||
outname="test",
|
||||
dens=1.0,
|
||||
nmol=[1],
|
||||
nstep=[1, 1],
|
||||
),
|
||||
gaussian=GaussianConfig(
|
||||
level="test",
|
||||
qmprog="g16",
|
||||
keywords="test",
|
||||
),
|
||||
)
|
||||
|
||||
def test_initialization(self, tmp_path: Path):
|
||||
state_handler = StateHandler(tmp_path)
|
||||
|
||||
assert isinstance(state_handler, StateHandler)
|
||||
|
||||
def test_save(self, tmp_path: Path, player_config: PlayerConfig):
|
||||
state_handler = StateHandler(tmp_path)
|
||||
|
||||
state = StateModel(
|
||||
config=player_config,
|
||||
system=System(),
|
||||
current_cycle=0,
|
||||
)
|
||||
|
||||
state_handler.save(state)
|
||||
|
||||
assert (tmp_path / "state.pkl").exists()
|
||||
|
||||
def test_get_when_empty(self, tmp_path: Path, player_config: PlayerConfig):
|
||||
state_handler = StateHandler(tmp_path)
|
||||
|
||||
state = state_handler.get(player_config)
|
||||
|
||||
assert state is None
|
||||
|
||||
def test_get(self, tmp_path: Path, player_config: PlayerConfig):
|
||||
state_handler = StateHandler(tmp_path)
|
||||
|
||||
state = StateModel(
|
||||
config=player_config,
|
||||
system=System(),
|
||||
current_cycle=0,
|
||||
)
|
||||
|
||||
state_handler.save(state)
|
||||
|
||||
retrieved_state = state_handler.get(player_config)
|
||||
|
||||
assert retrieved_state is not None
|
||||
assert retrieved_state.config == state.config
|
||||
assert retrieved_state.system == state.system
|
||||
assert retrieved_state.current_cycle == state.current_cycle
|
||||
|
||||
def test_get_with_different_config(
|
||||
self, tmp_path: Path, player_config: PlayerConfig
|
||||
):
|
||||
state_handler = StateHandler(tmp_path)
|
||||
|
||||
state = StateModel(
|
||||
config=player_config,
|
||||
system=System(),
|
||||
current_cycle=0,
|
||||
)
|
||||
|
||||
state_handler.save(state)
|
||||
|
||||
different_config = player_config.model_copy(update={"max_cyc": 200})
|
||||
|
||||
retrieved_state = state_handler.get(different_config)
|
||||
|
||||
assert retrieved_state is None
|
||||
|
||||
def test_get_with_different_config_force(
|
||||
self, tmp_path: Path, player_config: PlayerConfig
|
||||
):
|
||||
state_handler = StateHandler(tmp_path)
|
||||
|
||||
state = StateModel(
|
||||
config=player_config,
|
||||
system=System(),
|
||||
current_cycle=0,
|
||||
)
|
||||
|
||||
state_handler.save(state)
|
||||
|
||||
different_config = player_config.model_copy(update={"max_cyc": 200})
|
||||
|
||||
retrieved_state = state_handler.get(different_config, force=True)
|
||||
|
||||
assert retrieved_state is not None
|
||||
assert retrieved_state.config == state.config
|
||||
assert retrieved_state.config != different_config
|
||||
assert retrieved_state.system == state.system
|
||||
assert retrieved_state.current_cycle == state.current_cycle
|
||||
@@ -1,393 +0,0 @@
|
||||
from diceplayer import VERSION, logger
|
||||
from diceplayer.player import Player
|
||||
from tests.mocks.mock_inputs import mock_open
|
||||
|
||||
import io
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
|
||||
class TestPlayer(unittest.TestCase):
|
||||
def setUp(self):
|
||||
logger.set_logger(stream=io.StringIO())
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
def test_class_instantiation(self):
|
||||
# This file does not exist and it will be mocked
|
||||
player = Player("control.test.yml")
|
||||
|
||||
self.assertIsInstance(player, Player)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
def test_start(self):
|
||||
player = Player("control.test.yml")
|
||||
|
||||
player.gaussian_start = mock.MagicMock()
|
||||
player.dice_start = mock.MagicMock()
|
||||
|
||||
player.start()
|
||||
|
||||
self.assertEqual(player.dice_start.call_count, 3)
|
||||
self.assertEqual(player.gaussian_start.call_count, 3)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
@mock.patch("diceplayer.player.Path")
|
||||
def test_create_simulation_dir_if_already_exists(self, mock_path):
|
||||
player = Player("control.test.yml")
|
||||
mock_path.return_value.exists.return_value = True
|
||||
|
||||
with self.assertRaises(FileExistsError):
|
||||
player.create_simulation_dir()
|
||||
|
||||
self.assertTrue(mock_path.called)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
@mock.patch("diceplayer.player.Path")
|
||||
def test_create_simulation_dir_if_not_exists(self, mock_path):
|
||||
player = Player("control.test.yml")
|
||||
mock_path.return_value.exists.return_value = False
|
||||
|
||||
player.create_simulation_dir()
|
||||
|
||||
self.assertTrue(mock_path.called)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
@mock.patch("diceplayer.player.VERSION", "test")
|
||||
@mock.patch("diceplayer.player.sys")
|
||||
@mock.patch("diceplayer.player.weekday_date_time")
|
||||
def test_print_keywords(self, mock_date_func, mock_sys):
|
||||
player = Player("control.test.yml")
|
||||
|
||||
mock_sys.version = "TEST"
|
||||
mock_date_func.return_value = "00 Test 0000 at 00:00:00"
|
||||
|
||||
with self.assertLogs() as cm:
|
||||
player.print_keywords()
|
||||
|
||||
expected_output = [
|
||||
"INFO:diceplayer:##########################################################################################\n############# Welcome to DICEPLAYER version test #############\n##########################################################################################\n",
|
||||
"INFO:diceplayer:Your python version is TEST\n",
|
||||
"INFO:diceplayer:Program started on 00 Test 0000 at 00:00:00\n",
|
||||
"INFO:diceplayer:Environment variables:",
|
||||
"INFO:diceplayer:OMP_STACKSIZE = Not set\n",
|
||||
"INFO:diceplayer:------------------------------------------------------------------------------------------\n DICE variables being used in this run:\n------------------------------------------------------------------------------------------\n",
|
||||
"INFO:diceplayer:dens = 0.75",
|
||||
"INFO:diceplayer:isave = 1000",
|
||||
"INFO:diceplayer:ljname = phb.ljc",
|
||||
"INFO:diceplayer:nmol = [ 1 50 ]",
|
||||
"INFO:diceplayer:nstep = [ 2000 3000 4000 ]",
|
||||
"INFO:diceplayer:outname = phb",
|
||||
"INFO:diceplayer:press = 1.0",
|
||||
"INFO:diceplayer:progname = ~/.local/bin/dice",
|
||||
"INFO:diceplayer:randominit = first",
|
||||
"INFO:diceplayer:temp = 300.0",
|
||||
"INFO:diceplayer:------------------------------------------------------------------------------------------\n GAUSSIAN variables being used in this run:\n------------------------------------------------------------------------------------------\n",
|
||||
"INFO:diceplayer:chg_tol = 0.01",
|
||||
"INFO:diceplayer:keywords = freq",
|
||||
"INFO:diceplayer:level = MP2/aug-cc-pVDZ",
|
||||
"INFO:diceplayer:pop = chelpg",
|
||||
"INFO:diceplayer:qmprog = g16",
|
||||
"INFO:diceplayer:\n",
|
||||
]
|
||||
|
||||
self.assertEqual(cm.output, expected_output)
|
||||
|
||||
def test_validate_atom_dict(self):
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 0,
|
||||
"na": 1,
|
||||
"rx": 1.0,
|
||||
"ry": 1.0,
|
||||
"rz": 1.0,
|
||||
"chg": 1.0,
|
||||
"eps": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Invalid number of fields for site 1 for molecule type 1.",
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": "",
|
||||
"na": 1,
|
||||
"rx": 1.0,
|
||||
"ry": 1.0,
|
||||
"rz": 1.0,
|
||||
"chg": 1.0,
|
||||
"eps": 1.0,
|
||||
"sig": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception), "Invalid lbl fields for site 1 for molecule type 1."
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 1.0,
|
||||
"na": "",
|
||||
"rx": 1.0,
|
||||
"ry": 1.0,
|
||||
"rz": 1.0,
|
||||
"chg": 1.0,
|
||||
"eps": 1.0,
|
||||
"sig": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception), "Invalid na fields for site 1 for molecule type 1."
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 1.0,
|
||||
"na": 1,
|
||||
"rx": "",
|
||||
"ry": 1.0,
|
||||
"rz": 1.0,
|
||||
"chg": 1.0,
|
||||
"eps": 1.0,
|
||||
"sig": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Invalid rx fields for site 1 for molecule type 1. Value must be a float.",
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 1.0,
|
||||
"na": 1,
|
||||
"rx": 1.0,
|
||||
"ry": "",
|
||||
"rz": 1.0,
|
||||
"chg": 1.0,
|
||||
"eps": 1.0,
|
||||
"sig": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Invalid ry fields for site 1 for molecule type 1. Value must be a float.",
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 1.0,
|
||||
"na": 1,
|
||||
"rx": 1.0,
|
||||
"ry": 1.0,
|
||||
"rz": "",
|
||||
"chg": 1.0,
|
||||
"eps": 1.0,
|
||||
"sig": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Invalid rz fields for site 1 for molecule type 1. Value must be a float.",
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 1.0,
|
||||
"na": 1,
|
||||
"rx": 1.0,
|
||||
"ry": 1.0,
|
||||
"rz": 1.0,
|
||||
"chg": "",
|
||||
"eps": 1.0,
|
||||
"sig": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Invalid chg fields for site 1 for molecule type 1. Value must be a float.",
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 1.0,
|
||||
"na": 1,
|
||||
"rx": 1.0,
|
||||
"ry": 1.0,
|
||||
"rz": 1.0,
|
||||
"chg": 1.0,
|
||||
"eps": "",
|
||||
"sig": 1.0,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Invalid eps fields for site 1 for molecule type 1. Value must be a float.",
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
Player.validate_atom_dict(
|
||||
molecule_type=0,
|
||||
molecule_site=0,
|
||||
atom_dict={
|
||||
"lbl": 1.0,
|
||||
"na": 1,
|
||||
"rx": 1.0,
|
||||
"ry": 1.0,
|
||||
"rz": 1.0,
|
||||
"chg": 1.0,
|
||||
"eps": 1.0,
|
||||
"sig": "",
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Invalid sig fields for site 1 for molecule type 1. Value must be a float.",
|
||||
)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
@mock.patch("diceplayer.player.Path.exists", return_value=True)
|
||||
def test_read_potentials(self, mock_path_exists):
|
||||
player = Player("control.test.yml")
|
||||
|
||||
player.read_potentials()
|
||||
|
||||
self.assertEqual(player.system.molecule[0].molname, "TEST")
|
||||
self.assertEqual(len(player.system.molecule[0].atom), 1)
|
||||
|
||||
self.assertEqual(player.system.molecule[1].molname, "PLACEHOLDER")
|
||||
self.assertEqual(len(player.system.molecule[1].atom), 1)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
@mock.patch("diceplayer.player.Path.exists")
|
||||
def test_read_potentials_error(self, mock_path_exists):
|
||||
player = Player("control.test.yml")
|
||||
|
||||
# Testing file not found error
|
||||
mock_path_exists.return_value = False
|
||||
with self.assertRaises(RuntimeError) as context:
|
||||
player.read_potentials()
|
||||
|
||||
self.assertEqual(str(context.exception), "Potential file phb.ljc not found.")
|
||||
|
||||
# Enabling file found for next tests
|
||||
mock_path_exists.return_value = True
|
||||
|
||||
# Testing combrule error
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
player.config.dice.ljname = "phb.error.combrule.ljc"
|
||||
player.read_potentials()
|
||||
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Error: expected a '*' or a '+' sign in 1st line of file phb.error.combrule.ljc",
|
||||
)
|
||||
|
||||
# Testing ntypes error
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
player.config.dice.ljname = "phb.error.ntypes.ljc"
|
||||
player.read_potentials()
|
||||
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Error: expected an integer in the 2nd line of file phb.error.ntypes.ljc",
|
||||
)
|
||||
|
||||
# Testing ntypes error on config
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
player.config.dice.ljname = "phb.error.ntypes.config.ljc"
|
||||
player.read_potentials()
|
||||
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Error: number of molecule types in file phb.error.ntypes.config.ljc "
|
||||
"must match that of 'nmol' keyword in config file",
|
||||
)
|
||||
|
||||
# Testing nsite error
|
||||
with self.assertRaises(ValueError) as context:
|
||||
player.config.dice.ljname = "phb.error.nsites.ljc"
|
||||
player.read_potentials()
|
||||
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Error: expected nsites to be an integer for molecule type 1",
|
||||
)
|
||||
|
||||
# Testing molname error
|
||||
with self.assertRaises(ValueError) as context:
|
||||
player.config.dice.ljname = "phb.error.molname.ljc"
|
||||
player.read_potentials()
|
||||
|
||||
self.assertEqual(
|
||||
str(context.exception),
|
||||
"Error: expected nsites and molname for the molecule type 1",
|
||||
)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
@mock.patch("diceplayer.player.Path.exists", return_value=True)
|
||||
def test_print_potentials(self, mock_path_exists):
|
||||
player = Player("control.test.yml")
|
||||
player.read_potentials()
|
||||
|
||||
with self.assertLogs(level="INFO") as context:
|
||||
player.print_potentials()
|
||||
|
||||
expected_output = [
|
||||
"INFO:diceplayer:==========================================================================================\n Potential parameters from file phb.ljc:\n------------------------------------------------------------------------------------------\n",
|
||||
"INFO:diceplayer:Combination rule: *",
|
||||
"INFO:diceplayer:Types of molecules: 2\n",
|
||||
"INFO:diceplayer:1 atoms in molecule type 1:",
|
||||
"INFO:diceplayer:---------------------------------------------------------------------------------",
|
||||
"INFO:diceplayer:Lbl AN X Y Z Charge Epsilon Sigma Mass",
|
||||
"INFO:diceplayer:---------------------------------------------------------------------------------",
|
||||
"INFO:diceplayer:1 1 0.00000 0.00000 0.00000 0.000000 0.00000 0.0000 1.0079",
|
||||
"INFO:diceplayer:\n",
|
||||
"INFO:diceplayer:1 atoms in molecule type 2:",
|
||||
"INFO:diceplayer:---------------------------------------------------------------------------------",
|
||||
"INFO:diceplayer:Lbl AN X Y Z Charge Epsilon Sigma Mass",
|
||||
"INFO:diceplayer:---------------------------------------------------------------------------------",
|
||||
"INFO:diceplayer:1 1 0.00000 0.00000 0.00000 0.000000 0.00000 0.0000 1.0079",
|
||||
"INFO:diceplayer:\n",
|
||||
]
|
||||
|
||||
self.assertEqual(context.output, expected_output)
|
||||
|
||||
@mock.patch("builtins.open", mock_open)
|
||||
def test_dice_start(self):
|
||||
player = Player("control.test.yml")
|
||||
player.dice_interface = mock.MagicMock()
|
||||
player.dice_interface.start = mock.MagicMock()
|
||||
|
||||
player.dice_start(1)
|
||||
|
||||
player.dice_interface.start.assert_called_once()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
37
tests/utils/test_potential.py
Normal file
37
tests/utils/test_potential.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from diceplayer.config import PlayerConfig
|
||||
from diceplayer.environment import System
|
||||
from diceplayer.utils.potential import read_system_from_phb
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class TestPotential:
|
||||
@pytest.fixture
|
||||
def player_config(self) -> PlayerConfig:
|
||||
return PlayerConfig.model_validate(
|
||||
{
|
||||
"type": "both",
|
||||
"mem": 12,
|
||||
"max_cyc": 100,
|
||||
"switch_cyc": 50,
|
||||
"ncores": 4,
|
||||
"dice": {
|
||||
"nprocs": 4,
|
||||
"ljname": "phb.ljc.example",
|
||||
"outname": "test",
|
||||
"dens": 1.0,
|
||||
"nmol": [12, 16],
|
||||
"nstep": [1, 1],
|
||||
},
|
||||
"gaussian": {
|
||||
"level": "test",
|
||||
"qmprog": "g16",
|
||||
"keywords": "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
def test_read_phb(self, player_config: PlayerConfig):
|
||||
system = read_system_from_phb(player_config)
|
||||
|
||||
assert isinstance(system, System)
|
||||
Reference in New Issue
Block a user