Compare commits
12 Commits
main
...
refactor/r
| Author | SHA1 | Date | |
|---|---|---|---|
|
2802f10013
|
|||
|
7e66c98f26
|
|||
|
0470200d00
|
|||
|
0763c4a9e1
|
|||
|
30be88e6b4
|
|||
|
6a154429e9
|
|||
|
4c8cbc821d
|
|||
|
9f22304dd8
|
|||
|
53eb34a83e
|
|||
|
06ae9b41f0
|
|||
|
11ff4c0c21
|
|||
|
c59f0d6516
|
@@ -1 +1 @@
|
|||||||
3.10
|
3.12
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
diceplayer:
|
diceplayer:
|
||||||
opt: no
|
type: both
|
||||||
|
switch_cyc: 3
|
||||||
|
max_cyc: 5
|
||||||
mem: 24
|
mem: 24
|
||||||
maxcyc: 5
|
ncores: 20
|
||||||
ncores: 5
|
|
||||||
nprocs: 4
|
|
||||||
qmprog: 'g16'
|
qmprog: 'g16'
|
||||||
lps: no
|
lps: no
|
||||||
ghosts: no
|
ghosts: no
|
||||||
altsteps: 2000
|
altsteps: 2000
|
||||||
|
|
||||||
dice:
|
dice:
|
||||||
nmol: [1, 100]
|
nprocs: 1
|
||||||
|
nmol: [1, 200]
|
||||||
dens: 1.5
|
dens: 1.5
|
||||||
nstep: [2000, 3000]
|
nstep: [200, 200]
|
||||||
isave: 1000
|
vstep: 1000
|
||||||
|
isave: 30
|
||||||
outname: 'phb'
|
outname: 'phb'
|
||||||
progname: '~/.local/bin/dice'
|
progname: '/home/hideyoshi/.local/bin/dice'
|
||||||
ljname: 'phb.ljc'
|
ljname: 'phb.ljc.example'
|
||||||
randominit: 'always'
|
randominit: 'always'
|
||||||
|
seed: 12345
|
||||||
|
|
||||||
gaussian:
|
gaussian:
|
||||||
qmprog: 'g16'
|
qmprog: 'g16'
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
from diceplayer.utils 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
|
from diceplayer.player import Player
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
from importlib import metadata
|
||||||
|
|
||||||
|
|
||||||
|
VERSION = metadata.version("diceplayer")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""
|
|
||||||
Read and store the arguments passed to the program
|
|
||||||
and set the usage and help messages
|
|
||||||
"""
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog="Diceplayer")
|
parser = argparse.ArgumentParser(prog="Diceplayer")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-c", "--continue", dest="opt_continue", default=False, action="store_true"
|
"-v", "--version", action="version", version="diceplayer-" + VERSION
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-v", "--version", action="version", version="diceplayer-" + VERSION
|
"-c", "--continue", dest="continuation", default=False, action="store_true"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-i",
|
"-i",
|
||||||
@@ -34,36 +34,26 @@ def main():
|
|||||||
metavar="OUTFILE",
|
metavar="OUTFILE",
|
||||||
help="output file of diceplayer [default = run.log]",
|
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_output_file(args.outfile)
|
||||||
logger.set_logger(args.outfile, logging.INFO)
|
|
||||||
|
|
||||||
if args.opt_continue:
|
config: PlayerConfig
|
||||||
player = Player.from_save()
|
try:
|
||||||
else:
|
config = read_input(args.infile)
|
||||||
player = Player.from_file(args.infile)
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to read input file: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
player.read_potentials()
|
Player(config).play(continuation=args.continuation, force=args.force)
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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"])
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from .dice_config import DiceConfig
|
||||||
|
from .gaussian_config import GaussianConfig
|
||||||
|
from .player_config import PlayerConfig
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DiceConfig",
|
||||||
|
"GaussianConfig",
|
||||||
|
"PlayerConfig",
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
from typing_extensions import List, Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
import random
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class DiceConfig(BaseModel):
|
class DiceConfig(BaseModel):
|
||||||
@@ -7,15 +10,22 @@ class DiceConfig(BaseModel):
|
|||||||
Data Transfer Object for the Dice configuration.
|
Data Transfer Object for the Dice configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ljname: str = Field(..., description="Name of the Lennard-Jones potential file")
|
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(
|
outname: str = Field(
|
||||||
..., description="Name of the output file for the simulation results"
|
..., description="Name of the output file for the simulation results"
|
||||||
)
|
)
|
||||||
dens: float = Field(..., description="Density of the system")
|
dens: float = Field(..., description="Density of the system")
|
||||||
nmol: List[int] = Field(
|
nmol: list[int] = Field(
|
||||||
..., description="List of the number of molecules for each component"
|
..., description="List of the number of molecules for each component"
|
||||||
)
|
)
|
||||||
nstep: List[int] = Field(
|
nstep: list[int] = Field(
|
||||||
...,
|
...,
|
||||||
description="List of the number of steps for each component",
|
description="List of the number of steps for each component",
|
||||||
min_length=2,
|
min_length=2,
|
||||||
@@ -25,6 +35,13 @@ class DiceConfig(BaseModel):
|
|||||||
upbuf: int = Field(
|
upbuf: int = Field(
|
||||||
360, description="Buffer size for the potential energy calculations"
|
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(
|
combrule: Literal["+", "*"] = Field(
|
||||||
"*", description="Combination rule for the Lennard-Jones potential"
|
"*", description="Combination rule for the Lennard-Jones potential"
|
||||||
)
|
)
|
||||||
@@ -37,4 +54,7 @@ class DiceConfig(BaseModel):
|
|||||||
randominit: str = Field(
|
randominit: str = Field(
|
||||||
"first", description="Method for initializing the random number generator"
|
"first", description="Method for initializing the random number generator"
|
||||||
)
|
)
|
||||||
seed: int | None = Field(None, description="Seed for the random number generator")
|
seed: int = Field(
|
||||||
|
default_factory=lambda: int(1e6 * random.random()),
|
||||||
|
description="Seed for the random number generator",
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
|
||||||
@@ -7,10 +7,14 @@ class GaussianConfig(BaseModel):
|
|||||||
Data Transfer Object for the Gaussian configuration.
|
Data Transfer Object for the Gaussian configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
level: str = Field(..., description="Level of theory for the QM calculations")
|
model_config = ConfigDict(
|
||||||
|
frozen=True,
|
||||||
|
)
|
||||||
|
|
||||||
qmprog: Literal["g03", "g09", "g16"] = Field(
|
qmprog: Literal["g03", "g09", "g16"] = Field(
|
||||||
"g16", description="QM program to use for the calculations"
|
"g16", description="QM program to use for the calculations"
|
||||||
)
|
)
|
||||||
|
level: str = Field(..., description="Level of theory for the QM calculations")
|
||||||
|
|
||||||
chgmult: list[int] = Field(
|
chgmult: list[int] = Field(
|
||||||
default_factory=lambda: [0, 1],
|
default_factory=lambda: [0, 1],
|
||||||
@@ -20,6 +24,6 @@ class GaussianConfig(BaseModel):
|
|||||||
"chelpg", description="Population analysis method for the QM calculations"
|
"chelpg", description="Population analysis method for the QM calculations"
|
||||||
)
|
)
|
||||||
chg_tol: float = Field(0.01, description="Charge tolerance for the QM calculations")
|
chg_tol: float = Field(0.01, description="Charge tolerance for the QM calculations")
|
||||||
keywords: str = Field(
|
keywords: str | None = Field(
|
||||||
None, description="Additional keywords for the QM calculations"
|
None, description="Additional keywords for the QM calculations"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,27 +1,52 @@
|
|||||||
from diceplayer.config.dice_config import DiceConfig
|
from diceplayer.config.dice_config import DiceConfig
|
||||||
from diceplayer.config.gaussian_config import GaussianConfig
|
from diceplayer.config.gaussian_config import GaussianConfig
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, model_validator
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||||
from typing_extensions import Self
|
from typing_extensions import Any
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
MIN_STEP = 20000
|
MIN_STEP = 20000
|
||||||
|
STEP_INCREMENT = 1000
|
||||||
|
|
||||||
|
|
||||||
|
class RoutineType(str, Enum):
|
||||||
|
CHARGE = "charge"
|
||||||
|
GEOMETRY = "geometry"
|
||||||
|
BOTH = "both"
|
||||||
|
|
||||||
|
|
||||||
class PlayerConfig(BaseModel):
|
class PlayerConfig(BaseModel):
|
||||||
"""
|
"""
|
||||||
Data Transfer Object for the player configuration.
|
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").
|
||||||
"""
|
"""
|
||||||
|
|
||||||
opt: bool = Field(..., description="Whether to perform geometry optimization")
|
model_config = ConfigDict(
|
||||||
maxcyc: int = Field(
|
frozen=True,
|
||||||
..., description="Maximum number of cycles for the geometry optimization"
|
|
||||||
)
|
)
|
||||||
nprocs: int = Field(
|
|
||||||
..., description="Number of processors to use for the QM calculations"
|
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(
|
ncores: int = Field(
|
||||||
..., description="Number of cores to use for the QM calculations"
|
..., description="Number of cores to use for the QM calculations"
|
||||||
)
|
)
|
||||||
@@ -29,20 +54,37 @@ class PlayerConfig(BaseModel):
|
|||||||
dice: DiceConfig = Field(..., description="Dice configuration")
|
dice: DiceConfig = Field(..., description="Dice configuration")
|
||||||
gaussian: GaussianConfig = Field(..., description="Gaussian configuration")
|
gaussian: GaussianConfig = Field(..., description="Gaussian configuration")
|
||||||
|
|
||||||
mem: int = Field(None, description="Memory configuration")
|
|
||||||
switchcyc: int = Field(3, description="Switch cycle configuration")
|
|
||||||
qmprog: str = Field("g16", description="QM program to use for the calculations")
|
|
||||||
altsteps: int = Field(
|
altsteps: int = Field(
|
||||||
20000, description="Number of steps for the alternate simulation"
|
20000, description="Number of steps for the alternate simulation"
|
||||||
)
|
)
|
||||||
geoms_file: Path = Field(
|
geoms_file: Path = Field(
|
||||||
"geoms.xyz", description="File name for the geometries output"
|
Path("geoms.xyz"), description="File name for the geometries output"
|
||||||
)
|
)
|
||||||
simulation_dir: Path = Field(
|
simulation_dir: Path = Field(
|
||||||
"simfiles", description="Directory name for the simulation files"
|
Path("simfiles"), description="Directory name for the simulation files"
|
||||||
)
|
)
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="before")
|
||||||
def validate_altsteps(self) -> Self:
|
@staticmethod
|
||||||
self.altsteps = round(max(MIN_STEP, self.altsteps) / 1000) * 1000
|
def validate_altsteps(fields) -> dict[str, Any]:
|
||||||
return self
|
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.
|
||||||
|
"""
|
||||||
186
diceplayer/dice/dice_handler.py
Normal file
186
diceplayer/dice/dice_handler.py
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
import warnings
|
||||||
|
|
||||||
|
from diceplayer.dice.dice_input import (
|
||||||
|
NPTEqConfig,
|
||||||
|
NPTTerConfig,
|
||||||
|
NVTEqConfig,
|
||||||
|
NVTTerConfig,
|
||||||
|
)
|
||||||
|
from diceplayer.dice.dice_wrapper import (
|
||||||
|
DiceEnvironment,
|
||||||
|
DiceWrapper,
|
||||||
|
)
|
||||||
|
from diceplayer.environment import Atom, Molecule
|
||||||
|
from diceplayer.logger import logger
|
||||||
|
from diceplayer.state.state_model import StateModel
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
from itertools import batched, chain, islice
|
||||||
|
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) -> list[Atom]:
|
||||||
|
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)
|
||||||
|
|
||||||
|
return self.run_simulations(state, cycle)
|
||||||
|
|
||||||
|
def run_simulations(self, state: StateModel, cycle: int) -> list[Atom]:
|
||||||
|
results: list[list[Atom]] = []
|
||||||
|
|
||||||
|
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 self._aggregate_results(state, results)
|
||||||
|
|
||||||
|
def _simulation_process(
|
||||||
|
self,
|
||||||
|
state: StateModel,
|
||||||
|
cycle: int,
|
||||||
|
proc: int,
|
||||||
|
results: list[list[Atom]],
|
||||||
|
) -> 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.extend(
|
||||||
|
[
|
||||||
|
self._filter_environment_sites(state, environment)
|
||||||
|
for environment in dice.parse_results()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@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: ...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _filter_environment_sites(
|
||||||
|
state: StateModel, environment: DiceEnvironment
|
||||||
|
) -> list[Atom]:
|
||||||
|
picked_environment = []
|
||||||
|
|
||||||
|
ref_molecule = state.system.molecule[0]
|
||||||
|
ref_molecule_sizes = ref_molecule.sizes_of_molecule()
|
||||||
|
ref_n_sites = len(ref_molecule.atom) * state.config.dice.nmol[0]
|
||||||
|
|
||||||
|
min_distance = min(
|
||||||
|
(environment.thickness[i] - ref_molecule_sizes[i]) / 2 for i in range(3)
|
||||||
|
)
|
||||||
|
|
||||||
|
site_iter = iter(environment.items)
|
||||||
|
_ = list(islice(site_iter, ref_n_sites))
|
||||||
|
|
||||||
|
for molecule_index, molecule in enumerate(state.system.molecule[1:], start=1):
|
||||||
|
molecule_n_atoms = len(molecule.atom)
|
||||||
|
molecule_n_sites = molecule_n_atoms * state.config.dice.nmol[molecule_index]
|
||||||
|
|
||||||
|
sites = list(islice(site_iter, molecule_n_sites))
|
||||||
|
|
||||||
|
for molecule_sites in batched(sites, molecule_n_atoms):
|
||||||
|
new_molecule = Molecule("ASEC TMP MOLECULE")
|
||||||
|
|
||||||
|
for site_index, atom_site in enumerate(molecule_sites):
|
||||||
|
new_molecule.add_atom(
|
||||||
|
Atom(
|
||||||
|
molecule.atom[site_index].lbl,
|
||||||
|
molecule.atom[site_index].na,
|
||||||
|
atom_site.x,
|
||||||
|
atom_site.y,
|
||||||
|
atom_site.z,
|
||||||
|
molecule.atom[site_index].chg,
|
||||||
|
molecule.atom[site_index].eps,
|
||||||
|
molecule.atom[site_index].sig,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if molecule.signature() != new_molecule.signature():
|
||||||
|
_message = f"Skipping sites because the molecule signature does not match the reference molecule. Expected {molecule.signature()} but got {new_molecule.signature()}"
|
||||||
|
warnings.warn(_message)
|
||||||
|
logger.warning(_message)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ref_molecule.minimum_distance(new_molecule) >= min_distance:
|
||||||
|
continue
|
||||||
|
|
||||||
|
picked_environment.extend(new_molecule.atom)
|
||||||
|
|
||||||
|
return picked_environment
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _aggregate_results(state: StateModel, results: list[list[Atom]]) -> list[Atom]:
|
||||||
|
norm_factor = round(state.config.dice.nstep[-1] / state.config.dice.isave)
|
||||||
|
|
||||||
|
agg_results = []
|
||||||
|
for atom in chain(*[r for r in results]):
|
||||||
|
atom.chg = atom.chg * norm_factor
|
||||||
|
agg_results.append(atom)
|
||||||
|
|
||||||
|
return agg_results
|
||||||
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
|
||||||
103
diceplayer/dice/dice_wrapper.py
Normal file
103
diceplayer/dice/dice_wrapper.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import diceplayer.dice.dice_input as dice_input
|
||||||
|
from diceplayer.config import DiceConfig
|
||||||
|
|
||||||
|
from pydantic import TypeAdapter
|
||||||
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
from itertools import islice
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Final, List, Self
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True, frozen=True)
|
||||||
|
class DiceEnvironmentItem:
|
||||||
|
atom: str
|
||||||
|
x: float
|
||||||
|
y: float
|
||||||
|
z: float
|
||||||
|
|
||||||
|
|
||||||
|
DiceEnvironmentItemAdapter = TypeAdapter(DiceEnvironmentItem)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True)
|
||||||
|
class DiceEnvironment:
|
||||||
|
number_of_sites: int
|
||||||
|
thickness: List[float]
|
||||||
|
items: List[DiceEnvironmentItem]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, thickness: List[float]) -> Self:
|
||||||
|
return cls(number_of_sites=0, thickness=thickness, items=[])
|
||||||
|
|
||||||
|
def add_site(self, site: DiceEnvironmentItem):
|
||||||
|
self.items.append(site)
|
||||||
|
self.number_of_sites += 1
|
||||||
|
|
||||||
|
|
||||||
|
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) -> list[DiceEnvironment]:
|
||||||
|
results = []
|
||||||
|
|
||||||
|
positions_file = self.working_directory / "phb.xyz"
|
||||||
|
if not positions_file.exists():
|
||||||
|
raise RuntimeError(f"Positions file not found at {self.working_directory}")
|
||||||
|
|
||||||
|
with open(positions_file, "r") as f:
|
||||||
|
while True:
|
||||||
|
line = f.readline()
|
||||||
|
if not line.startswith(" "):
|
||||||
|
break
|
||||||
|
|
||||||
|
environment = DiceEnvironment(
|
||||||
|
number_of_sites=int(line.strip()),
|
||||||
|
thickness=[float(n) for n in f.readline().split()[-3:]],
|
||||||
|
items=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Skip the comment line
|
||||||
|
|
||||||
|
environment.items.extend(
|
||||||
|
[
|
||||||
|
DiceEnvironmentItemAdapter.validate_python(
|
||||||
|
{"atom": site[0], "x": site[1], "y": site[2], "z": site[3]}
|
||||||
|
)
|
||||||
|
for atom in islice(f, environment.number_of_sites)
|
||||||
|
if (site := atom.strip().split()) and len(site) == 4
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
results.append(environment)
|
||||||
|
|
||||||
|
return results
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from diceplayer.utils.ptable import AtomInfo, PTable
|
from diceplayer.utils.ptable import AtomInfo, PTable
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass(slots=True)
|
||||||
class Atom:
|
class Atom:
|
||||||
"""
|
"""
|
||||||
Atom class declaration. This class is used throughout the DicePlayer program to represent atoms.
|
Atom class declaration. This class is used throughout the DicePlayer program to represent atoms.
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from diceplayer import logger
|
|
||||||
from diceplayer.environment import Atom
|
from diceplayer.environment import Atom
|
||||||
from diceplayer.utils.cache import invalidate_computed_properties
|
from diceplayer.logger import logger
|
||||||
from diceplayer.utils.misc import BOHR2ANG, EA_2_DEBYE
|
from diceplayer.utils.misc import BOHR2ANG, EA_2_DEBYE
|
||||||
from diceplayer.utils.ptable import GHOST_NUMBER
|
from diceplayer.utils.ptable import GHOST_NUMBER
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
|
||||||
import numpy.linalg as linalg
|
import numpy.linalg as linalg
|
||||||
|
import numpy.typing as npt
|
||||||
|
from pydantic.dataclasses import dataclass
|
||||||
from typing_extensions import List, Self, Tuple
|
from typing_extensions import List, Self, Tuple
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import field
|
||||||
from functools import cached_property
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -33,11 +32,11 @@ class Molecule:
|
|||||||
molname: str
|
molname: str
|
||||||
atom: List[Atom] = field(default_factory=list)
|
atom: List[Atom] = field(default_factory=list)
|
||||||
|
|
||||||
@cached_property
|
@property
|
||||||
def total_mass(self) -> float:
|
def total_mass(self) -> float:
|
||||||
return sum(atom.mass for atom in self.atom)
|
return sum(atom.mass for atom in self.atom)
|
||||||
|
|
||||||
@cached_property
|
@property
|
||||||
def com(self) -> npt.NDArray[np.float64]:
|
def com(self) -> npt.NDArray[np.float64]:
|
||||||
com = np.zeros(3)
|
com = np.zeros(3)
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ class Molecule:
|
|||||||
|
|
||||||
return com
|
return com
|
||||||
|
|
||||||
@cached_property
|
@property
|
||||||
def inertia_tensor(self) -> npt.NDArray[np.float64]:
|
def inertia_tensor(self) -> npt.NDArray[np.float64]:
|
||||||
"""
|
"""
|
||||||
Calculates the inertia tensor of the molecule.
|
Calculates the inertia tensor of the molecule.
|
||||||
@@ -78,7 +77,6 @@ class Molecule:
|
|||||||
|
|
||||||
return inertia_tensor
|
return inertia_tensor
|
||||||
|
|
||||||
@invalidate_computed_properties()
|
|
||||||
def add_atom(self, a: Atom) -> None:
|
def add_atom(self, a: Atom) -> None:
|
||||||
"""
|
"""
|
||||||
Adds Atom instance to the molecule.
|
Adds Atom instance to the molecule.
|
||||||
@@ -89,7 +87,6 @@ class Molecule:
|
|||||||
|
|
||||||
self.atom.append(a)
|
self.atom.append(a)
|
||||||
|
|
||||||
@invalidate_computed_properties()
|
|
||||||
def remove_atom(self, a: Atom) -> None:
|
def remove_atom(self, a: Atom) -> None:
|
||||||
"""
|
"""
|
||||||
Removes Atom instance from the molecule.
|
Removes Atom instance from the molecule.
|
||||||
@@ -100,7 +97,6 @@ class Molecule:
|
|||||||
|
|
||||||
self.atom.remove(a)
|
self.atom.remove(a)
|
||||||
|
|
||||||
@invalidate_computed_properties()
|
|
||||||
def move_center_of_mass_to_origin(self) -> None:
|
def move_center_of_mass_to_origin(self) -> None:
|
||||||
"""
|
"""
|
||||||
Updated positions based on the center of mass of the molecule
|
Updated positions based on the center of mass of the molecule
|
||||||
@@ -110,7 +106,6 @@ class Molecule:
|
|||||||
atom.ry -= self.com[1]
|
atom.ry -= self.com[1]
|
||||||
atom.rz -= self.com[2]
|
atom.rz -= self.com[2]
|
||||||
|
|
||||||
@invalidate_computed_properties()
|
|
||||||
def rotate_to_standard_orientation(self) -> None:
|
def rotate_to_standard_orientation(self) -> None:
|
||||||
"""
|
"""
|
||||||
Rotates the molecule to the standard orientation
|
Rotates the molecule to the standard orientation
|
||||||
@@ -314,3 +309,12 @@ class Molecule:
|
|||||||
diff = coords_a[:, None, :] - coords_b[None, :, :]
|
diff = coords_a[:, None, :] - coords_b[None, :, :]
|
||||||
d2 = np.sum(diff**2, axis=-1)
|
d2 = np.sum(diff**2, axis=-1)
|
||||||
return np.sqrt(d2.min())
|
return np.sqrt(d2.min())
|
||||||
|
|
||||||
|
def signature(self) -> List[int]:
|
||||||
|
"""
|
||||||
|
Returns the signature of the molecule, which is a list of the number of atoms of each type in the molecule.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[int]: signature of the molecule
|
||||||
|
"""
|
||||||
|
return [a.lbl for a in self.atom]
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
from .__interface import Interface
|
|
||||||
from .dice_interface import DiceInterface
|
|
||||||
from .gaussian_interface import GaussianInterface
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Interface", "DiceInterface", "GaussianInterface"]
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from diceplayer.config.player_config import PlayerConfig
|
|
||||||
from diceplayer.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,389 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from diceplayer import logger
|
|
||||||
from diceplayer.config.player_config import PlayerConfig
|
|
||||||
from diceplayer.environment.system import System
|
|
||||||
from diceplayer.interface import Interface
|
|
||||||
|
|
||||||
from setproctitle import setproctitle
|
|
||||||
from typing_extensions import Final, TextIO
|
|
||||||
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from multiprocessing import Process, connection
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
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("init = yesreadxyz\n")
|
|
||||||
f.write(f"nstep = {self.step.altsteps}\n")
|
|
||||||
else:
|
|
||||||
f.write("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("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.config.player_config import PlayerConfig
|
|
||||||
from diceplayer.environment import Atom
|
|
||||||
from diceplayer.environment.molecule import Molecule
|
|
||||||
from diceplayer.environment.system import System
|
|
||||||
from diceplayer.interface import Interface
|
|
||||||
from diceplayer.utils.misc import date_time
|
|
||||||
from diceplayer.utils.ptable import PTable
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
import numpy.typing as npt
|
|
||||||
from typing_extensions import Any, Dict, List, Tuple
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import textwrap
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
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, 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", "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", "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(
|
|
||||||
"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()
|
|
||||||
!= PTable.get_atomic_symbol(
|
|
||||||
self.system.molecule[type].atom[site].na
|
|
||||||
).strip()
|
|
||||||
):
|
|
||||||
raise SyntaxError(
|
|
||||||
"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": PTable.get_atomic_symbol(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", "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 = PTable.get_atomic_symbol(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
|
|
||||||
4
diceplayer/logger.py
Normal file
4
diceplayer/logger.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from diceplayer.utils import RunLogger
|
||||||
|
|
||||||
|
|
||||||
|
logger = RunLogger("diceplayer")
|
||||||
37
diceplayer/optimization/optimization_handler.py
Normal file
37
diceplayer/optimization/optimization_handler.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from diceplayer.config.player_config import RoutineType
|
||||||
|
from diceplayer.dice.dice_wrapper import DiceEnvironmentItem
|
||||||
|
from diceplayer.environment import Atom
|
||||||
|
from diceplayer.logger import logger
|
||||||
|
from diceplayer.state.state_model import StateModel
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class OptimizationHandler:
|
||||||
|
def __init__(self, step_directory: Path):
|
||||||
|
self.optimization_directory = step_directory / "optimization"
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self,
|
||||||
|
state: StateModel,
|
||||||
|
current_cycle: int,
|
||||||
|
dice_environment: list[Atom],
|
||||||
|
) -> StateModel:
|
||||||
|
routine = self._fetch_current_routine(state, current_cycle)
|
||||||
|
logger.info(
|
||||||
|
f"Running Optimization - {current_cycle} - {routine} - {self.optimization_directory}"
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(dice_environment)
|
||||||
|
|
||||||
|
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,465 +1,60 @@
|
|||||||
from diceplayer import VERSION, logger
|
|
||||||
from diceplayer.config.player_config import PlayerConfig
|
from diceplayer.config.player_config import PlayerConfig
|
||||||
from diceplayer.environment import Atom, Molecule, System
|
from diceplayer.dice.dice_handler import DiceHandler
|
||||||
from diceplayer.interface import DiceInterface, GaussianInterface
|
from diceplayer.logger import logger
|
||||||
from diceplayer.utils import PTable, weekday_date_time
|
from diceplayer.optimization.optimization_handler import OptimizationHandler
|
||||||
|
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
|
||||||
from pydantic import BaseModel
|
|
||||||
from typing_extensions import Tuple
|
|
||||||
|
|
||||||
import os
|
|
||||||
import pickle
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
ENV = ["OMP_STACKSIZE"]
|
class PlayerFlags(TypedDict):
|
||||||
|
continuation: bool
|
||||||
|
force: bool
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
def __init__(self, infile: str = None, optimization: bool = False):
|
def __init__(self, config: PlayerConfig):
|
||||||
if infile is None and optimization is False:
|
self.config = config
|
||||||
raise ValueError("Must specify either infile or optimization")
|
self._state_handler = StateHandler(config.simulation_dir)
|
||||||
|
|
||||||
elif infile is not None:
|
def play(self, **flags: Unpack[PlayerFlags]):
|
||||||
self.config = self.set_config(self.read_keywords(infile))
|
continuation = flags.get("continuation", False)
|
||||||
|
force = flags.get("force", False)
|
||||||
|
|
||||||
self.system = System()
|
state = self._state_handler.get(self.config, force=force)
|
||||||
|
if not continuation and state is not None:
|
||||||
self.initial_cycle = 1
|
logger.info(
|
||||||
|
"Continuation flag is not set. Starting a new simulation and deleting any existing state."
|
||||||
elif optimization is True:
|
)
|
||||||
save = self.load_run_from_pickle()
|
self._state_handler.delete()
|
||||||
|
state = None
|
||||||
self.config = save[0]
|
|
||||||
|
|
||||||
self.system = save[1]
|
|
||||||
|
|
||||||
self.initial_cycle = save[2] + 1
|
|
||||||
|
|
||||||
|
if state is None:
|
||||||
|
system = read_system_from_phb(self.config)
|
||||||
|
state = StateModel(config=self.config, system=system)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Must specify either infile or config")
|
logger.info("Resuming from existing state.")
|
||||||
|
|
||||||
self.dice_interface = DiceInterface()
|
while state.current_cycle < self.config.max_cyc:
|
||||||
self.gaussian_interface = GaussianInterface()
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
logger.info(
|
|
||||||
"==========================================================================================\n"
|
|
||||||
"Starting the iterative process.\n"
|
|
||||||
"==========================================================================================\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
for cycle in range(self.initial_cycle, self.initial_cycle + self.config.maxcyc):
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"------------------------------------------------------------------------------------------\n"
|
f"Starting cycle {state.current_cycle + 1} of {self.config.max_cyc}."
|
||||||
f" Step # {cycle}\n"
|
|
||||||
f"------------------------------------------------------------------------------------------\n"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.dice_start(cycle)
|
step_directory = self.config.simulation_dir / f"{state.current_cycle:02d}"
|
||||||
|
if not step_directory.exists():
|
||||||
|
step_directory.mkdir(parents=True)
|
||||||
|
|
||||||
try:
|
dice_environment = DiceHandler(step_directory).run(
|
||||||
self.gaussian_start(cycle)
|
state, state.current_cycle
|
||||||
except StopIteration:
|
|
||||||
break
|
|
||||||
|
|
||||||
self.save_run_in_pickle(cycle)
|
|
||||||
|
|
||||||
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.rotate_to_standard_orientation()
|
state = OptimizationHandler(step_directory).run(
|
||||||
logger.info("\n Done")
|
state, state.current_cycle, dice_environment
|
||||||
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: BaseModel):
|
|
||||||
for key, value in sorted(config.model_dump().items()):
|
|
||||||
if value is None:
|
|
||||||
continue
|
|
||||||
if isinstance(value, list):
|
|
||||||
string = " ".join(str(x) for x in value)
|
|
||||||
logger.info(f"{key} = [ {string} ]")
|
|
||||||
else:
|
|
||||||
logger.info(f"{key} = {value}")
|
|
||||||
|
|
||||||
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(
|
state.current_cycle += 1
|
||||||
"------------------------------------------------------------------------------------------\n"
|
self._state_handler.save(state)
|
||||||
" DICE variables being used in this run:\n"
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
log_keywords(self.config.dice)
|
logger.info("Reached maximum number of cycles. Simulation complete.")
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
" GAUSSIAN variables being used in this run:\n"
|
|
||||||
"------------------------------------------------------------------------------------------\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
log_keywords(self.config.gaussian)
|
|
||||||
|
|
||||||
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.")
|
|
||||||
|
|
||||||
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.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_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.system.molecule[0].atom)))
|
|
||||||
|
|
||||||
chargesAndDipole = self.system.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],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
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 = PTable.get_atomic_symbol(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("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.model_validate(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)
|
|
||||||
|
|||||||
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,
|
||||||
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from .logger import Logger, valid_logger
|
from .logger import RunLogger
|
||||||
from .misc import (
|
from .misc import (
|
||||||
compress_files_1mb,
|
compress_files_1mb,
|
||||||
date_time,
|
date_time,
|
||||||
@@ -10,8 +10,7 @@ from .ptable import AtomInfo, PTable
|
|||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Logger",
|
"RunLogger",
|
||||||
"valid_logger",
|
|
||||||
"PTable",
|
"PTable",
|
||||||
"AtomInfo",
|
"AtomInfo",
|
||||||
"weekday_date_time",
|
"weekday_date_time",
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
from typing_extensions import Protocol, runtime_checkable
|
|
||||||
|
|
||||||
|
|
||||||
@runtime_checkable
|
|
||||||
class Dataclass(Protocol):
|
|
||||||
__dataclass_fields__: dict
|
|
||||||
@@ -1,72 +1,44 @@
|
|||||||
|
from typing_extensions import TypeVar
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def valid_logger(func):
|
H = TypeVar("H", bound=logging.Handler)
|
||||||
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:
|
class RunLogger(logging.Logger):
|
||||||
outfile = None
|
def __init__(self, name, level=logging.INFO, stream=sys.stdout):
|
||||||
|
super().__init__(name, level)
|
||||||
|
|
||||||
_logger = None
|
self.handlers.clear()
|
||||||
|
|
||||||
_was_set = False
|
self.handlers.append(
|
||||||
|
self._configure_handler(logging.StreamHandler(stream), level)
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, logger_name):
|
def set_output_file(self, outfile: Path, level=logging.INFO):
|
||||||
if self._logger is None:
|
for handler in list(self.handlers):
|
||||||
self._logger = logging.getLogger(logger_name)
|
if not isinstance(handler, logging.FileHandler):
|
||||||
|
continue
|
||||||
|
self.handlers.remove(handler)
|
||||||
|
|
||||||
def set_logger(self, outfile="run.log", level=logging.INFO, stream=None):
|
self.handlers.append(self._create_file_handler(outfile, level))
|
||||||
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:
|
@staticmethod
|
||||||
self._logger.setLevel(level)
|
def _create_file_handler(file: str | Path, level) -> logging.FileHandler:
|
||||||
|
file = Path(file)
|
||||||
|
|
||||||
self._create_handlers(outfile_path, stream)
|
if file.exists():
|
||||||
|
file.rename(file.with_suffix(".log.backup"))
|
||||||
|
|
||||||
self._was_set = True
|
handler = logging.FileHandler(file)
|
||||||
|
return RunLogger._configure_handler(handler, level)
|
||||||
|
|
||||||
@valid_logger
|
@staticmethod
|
||||||
def info(self, message):
|
def _configure_handler(handler: H, level) -> H:
|
||||||
self._logger.info(message)
|
handler.setLevel(level)
|
||||||
|
formatter = logging.Formatter("%(message)s")
|
||||||
@valid_logger
|
handler.setFormatter(formatter)
|
||||||
def debug(self, message):
|
return handler
|
||||||
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)
|
|
||||||
|
|||||||
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")
|
||||||
@@ -8,7 +8,7 @@ authors = [
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "GPL-2.0-only"
|
license = "GPL-2.0-only"
|
||||||
scripts = { diceplayer = "diceplayer.__main__:main" }
|
scripts = { diceplayer = "diceplayer.__main__:main" }
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.12"
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
|
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@ dev = [
|
|||||||
"black>=24.4.2",
|
"black>=24.4.2",
|
||||||
"pre-commit>=3.7.1",
|
"pre-commit>=3.7.1",
|
||||||
"poethepoet>=0.27.0",
|
"poethepoet>=0.27.0",
|
||||||
"ruff>=0.15.2"
|
"ruff>=0.15.2",
|
||||||
|
"pytest>=9.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
tests/_assets/__init__.py
Normal file
4
tests/_assets/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ASSETS_DIR = Path(__file__).parent
|
||||||
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.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):
|
def test_class_instantiation(self):
|
||||||
dice_dto = DiceConfig(
|
dice_dto = DiceConfig(
|
||||||
|
nprocs=1,
|
||||||
ljname="test",
|
ljname="test",
|
||||||
outname="test",
|
outname="test",
|
||||||
dens=1.0,
|
dens=1.0,
|
||||||
@@ -13,78 +14,77 @@ class TestDiceDto(unittest.TestCase):
|
|||||||
nstep=[1, 1],
|
nstep=[1, 1],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertIsInstance(dice_dto, DiceConfig)
|
assert isinstance(dice_dto, DiceConfig)
|
||||||
|
|
||||||
def test_validate_jname(self):
|
def test_validate_jname(self):
|
||||||
with self.assertRaises(ValueError) as ex:
|
with pytest.raises(ValueError) as ex:
|
||||||
DiceConfig(
|
DiceConfig(
|
||||||
|
nprocs=1,
|
||||||
ljname=None,
|
ljname=None,
|
||||||
outname="test",
|
outname="test",
|
||||||
dens=1.0,
|
dens=1.0,
|
||||||
nmol=[1],
|
nmol=[1],
|
||||||
nstep=[1, 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):
|
def test_validate_outname(self):
|
||||||
with self.assertRaises(ValueError) as ex:
|
with pytest.raises(ValueError) as ex:
|
||||||
DiceConfig(
|
DiceConfig(
|
||||||
|
nprocs=1,
|
||||||
ljname="test",
|
ljname="test",
|
||||||
outname=None,
|
outname=None,
|
||||||
dens=1.0,
|
dens=1.0,
|
||||||
nmol=[1],
|
nmol=[1],
|
||||||
nstep=[1, 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):
|
def test_validate_dens(self):
|
||||||
with self.assertRaises(ValueError) as ex:
|
with pytest.raises(ValueError) as ex:
|
||||||
DiceConfig(
|
DiceConfig(
|
||||||
|
nprocs=1,
|
||||||
ljname="test",
|
ljname="test",
|
||||||
outname="test",
|
outname="test",
|
||||||
dens=None,
|
dens=None,
|
||||||
nmol=[1],
|
nmol=[1],
|
||||||
nstep=[1, 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):
|
def test_validate_nmol(self):
|
||||||
with self.assertRaises(ValueError) as ex:
|
with pytest.raises(ValueError) as ex:
|
||||||
DiceConfig(
|
DiceConfig(
|
||||||
|
nprocs=1,
|
||||||
ljname="test",
|
ljname="test",
|
||||||
outname="test",
|
outname="test",
|
||||||
dens=1.0,
|
dens=1.0,
|
||||||
nmol=0,
|
nmol=0,
|
||||||
nstep=[1, 1],
|
nstep=[1, 1],
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
|
||||||
ex.exception,
|
assert ex.value == "Error: 'nmol' keyword not specified in config file"
|
||||||
"Error: 'nmol' keyword not defined appropriately in config file",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_validate_nstep(self):
|
def test_validate_nstep(self):
|
||||||
with self.assertRaises(ValueError) as ex:
|
with pytest.raises(ValueError) as ex:
|
||||||
DiceConfig(
|
DiceConfig(
|
||||||
|
nprocs=1,
|
||||||
ljname="test",
|
ljname="test",
|
||||||
outname="test",
|
outname="test",
|
||||||
dens=1.0,
|
dens=1.0,
|
||||||
nmol=[1],
|
nmol=[1],
|
||||||
nstep=0,
|
nstep=0,
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
|
||||||
ex.exception,
|
assert ex.value == "Error: 'nstep' keyword not specified in config file"
|
||||||
"Error: 'nstep' keyword not defined appropriately in config file",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_from_dict(self):
|
def test_from_dict(self):
|
||||||
dice_dto = DiceConfig.model_validate(
|
dice_dto = DiceConfig.model_validate(
|
||||||
{
|
{
|
||||||
|
"nprocs": 1,
|
||||||
"ljname": "test",
|
"ljname": "test",
|
||||||
"outname": "test",
|
"outname": "test",
|
||||||
"dens": 1.0,
|
"dens": 1.0,
|
||||||
@@ -93,4 +93,4 @@ class TestDiceDto(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertIsInstance(dice_dto, DiceConfig)
|
assert isinstance(dice_dto, DiceConfig)
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from diceplayer.config.gaussian_config import GaussianConfig
|
from diceplayer.config.gaussian_config import GaussianConfig
|
||||||
|
|
||||||
import unittest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class TestGaussianDTO(unittest.TestCase):
|
class TestGaussianConfig:
|
||||||
def test_class_instantiation(self):
|
def test_class_instantiation(self):
|
||||||
gaussian_dto = GaussianConfig(
|
gaussian_dto = GaussianConfig(
|
||||||
level="test",
|
level="test",
|
||||||
@@ -11,10 +11,10 @@ class TestGaussianDTO(unittest.TestCase):
|
|||||||
keywords="test",
|
keywords="test",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertIsInstance(gaussian_dto, GaussianConfig)
|
assert isinstance(gaussian_dto, GaussianConfig)
|
||||||
|
|
||||||
def test_is_valid_qmprog(self):
|
def test_is_valid_qmprog(self):
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
GaussianConfig(
|
GaussianConfig(
|
||||||
level="test",
|
level="test",
|
||||||
qmprog="test",
|
qmprog="test",
|
||||||
@@ -22,7 +22,7 @@ class TestGaussianDTO(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_is_valid_level(self):
|
def test_is_valid_level(self):
|
||||||
with self.assertRaises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
GaussianConfig(
|
GaussianConfig(
|
||||||
level=None,
|
level=None,
|
||||||
qmprog="g16",
|
qmprog="g16",
|
||||||
@@ -38,8 +38,4 @@ class TestGaussianDTO(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertIsInstance(gaussian_dto, GaussianConfig)
|
assert isinstance(gaussian_dto, GaussianConfig)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
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)
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
from diceplayer.config.dice_config import DiceConfig
|
|
||||||
from diceplayer.config.gaussian_config import GaussianConfig
|
|
||||||
from diceplayer.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 = GaussianConfig(
|
|
||||||
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, GaussianConfig)
|
|
||||||
|
|
||||||
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.model_validate(get_config_dict())
|
|
||||||
|
|
||||||
self.assertIsInstance(player_dto, PlayerConfig)
|
|
||||||
self.assertIsInstance(player_dto.dice, DiceConfig)
|
|
||||||
self.assertIsInstance(player_dto.gaussian, GaussianConfig)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
92
tests/dice/test_dice_handler.py
Normal file
92
tests/dice/test_dice_handler.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
from diceplayer.config import DiceConfig, GaussianConfig, PlayerConfig
|
||||||
|
from diceplayer.dice.dice_handler import DiceHandler
|
||||||
|
from diceplayer.dice.dice_wrapper import DiceWrapper
|
||||||
|
from diceplayer.state.state_model import StateModel
|
||||||
|
from diceplayer.utils.potential import read_system_from_phb
|
||||||
|
from tests._assets import ASSETS_DIR
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiceHandler:
|
||||||
|
@pytest.fixture
|
||||||
|
def working_directory(self) -> Path:
|
||||||
|
return ASSETS_DIR
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def player_config(self, working_directory: Path) -> PlayerConfig:
|
||||||
|
return PlayerConfig(
|
||||||
|
type="both",
|
||||||
|
mem=12,
|
||||||
|
max_cyc=100,
|
||||||
|
switch_cyc=50,
|
||||||
|
ncores=4,
|
||||||
|
dice=DiceConfig(
|
||||||
|
nprocs=4,
|
||||||
|
ljname=working_directory / "phb.ljc",
|
||||||
|
outname="test",
|
||||||
|
dens=1.0,
|
||||||
|
nmol=[1, 200],
|
||||||
|
nstep=[1000, 1000],
|
||||||
|
),
|
||||||
|
gaussian=GaussianConfig(
|
||||||
|
level="test",
|
||||||
|
qmprog="g16",
|
||||||
|
keywords="test",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def state_model(self, player_config: PlayerConfig) -> StateModel:
|
||||||
|
return StateModel(
|
||||||
|
config=player_config,
|
||||||
|
system=read_system_from_phb(player_config),
|
||||||
|
current_cycle=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dice_wrapper(
|
||||||
|
self, player_config: PlayerConfig, working_directory: Path
|
||||||
|
) -> DiceWrapper:
|
||||||
|
return DiceWrapper(player_config.dice, working_directory)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dice_handler(self, tmp_path) -> DiceHandler:
|
||||||
|
return DiceHandler(tmp_path)
|
||||||
|
|
||||||
|
def test_filter_environment_sites(
|
||||||
|
self,
|
||||||
|
dice_handler: DiceHandler,
|
||||||
|
dice_wrapper: DiceWrapper,
|
||||||
|
state_model: StateModel,
|
||||||
|
) -> None:
|
||||||
|
environment = dice_wrapper.parse_results()[0]
|
||||||
|
|
||||||
|
filtered_environment = dice_handler._filter_environment_sites(
|
||||||
|
state_model, environment
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(filtered_environment) < environment.number_of_sites
|
||||||
|
|
||||||
|
def test_aggregate_results(
|
||||||
|
self,
|
||||||
|
dice_handler: DiceHandler,
|
||||||
|
dice_wrapper: DiceWrapper,
|
||||||
|
state_model: StateModel,
|
||||||
|
) -> None:
|
||||||
|
environments = dice_wrapper.parse_results()
|
||||||
|
|
||||||
|
picked_environments = [
|
||||||
|
dice_handler._filter_environment_sites(state_model, env)
|
||||||
|
for env in environments
|
||||||
|
]
|
||||||
|
|
||||||
|
aggregated_environment = dice_handler._aggregate_results(
|
||||||
|
state_model, picked_environments
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(aggregated_environment) == sum(
|
||||||
|
len(env) for env in picked_environments
|
||||||
|
)
|
||||||
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()
|
||||||
38
tests/dice/test_dice_wrapper.py
Normal file
38
tests/dice/test_dice_wrapper.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from diceplayer.config import DiceConfig
|
||||||
|
from diceplayer.dice.dice_wrapper import DiceEnvironment, DiceWrapper
|
||||||
|
from tests._assets import ASSETS_DIR
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiceWrapper:
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def dice_config(self) -> DiceConfig:
|
||||||
|
return DiceConfig.model_validate(
|
||||||
|
{
|
||||||
|
"nprocs": 4,
|
||||||
|
"ljname": "test",
|
||||||
|
"outname": "test",
|
||||||
|
"dens": 1.0,
|
||||||
|
"nmol": [1],
|
||||||
|
"nstep": [1, 1],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def working_directory(self) -> Path:
|
||||||
|
return ASSETS_DIR
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def dice_wrapper(
|
||||||
|
self, dice_config: DiceConfig, working_directory: Path
|
||||||
|
) -> DiceWrapper:
|
||||||
|
return DiceWrapper(dice_config, working_directory)
|
||||||
|
|
||||||
|
def test_parse_results(self, dice_wrapper: DiceWrapper) -> None:
|
||||||
|
results = dice_wrapper.parse_results()
|
||||||
|
|
||||||
|
assert isinstance(results, list)
|
||||||
|
assert all(isinstance(env, DiceEnvironment) for env in results)
|
||||||
@@ -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,32 +0,0 @@
|
|||||||
from typing_extensions import List
|
|
||||||
|
|
||||||
import itertools
|
|
||||||
|
|
||||||
|
|
||||||
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,643 +0,0 @@
|
|||||||
from diceplayer import logger
|
|
||||||
from diceplayer.config.player_config import PlayerConfig
|
|
||||||
from diceplayer.environment import Atom, Molecule, System
|
|
||||||
from diceplayer.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.model_validate(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.interface.dice_interface.Process", MockProc())
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.connection", MockConnection)
|
|
||||||
def test_start(self):
|
|
||||||
dice = DiceInterface()
|
|
||||||
dice.configure(self.config, System())
|
|
||||||
|
|
||||||
dice.start(1)
|
|
||||||
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.connection", MockConnection)
|
|
||||||
@mock.patch("diceplayer.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.interface.dice_interface.DiceInterface._make_proc_dir")
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.DiceInterface._make_dice_inputs")
|
|
||||||
@mock.patch("diceplayer.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.interface.dice_interface.Path.mkdir")
|
|
||||||
@mock.patch("diceplayer.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.interface.dice_interface.Path.mkdir")
|
|
||||||
@mock.patch("diceplayer.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.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.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.interface.dice_interface.os")
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.shutil")
|
|
||||||
@mock.patch("diceplayer.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.interface.dice_interface.os")
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.shutil")
|
|
||||||
@mock.patch("diceplayer.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, 2)
|
|
||||||
self.assertTrue(mock_shutils.copy.called)
|
|
||||||
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.os")
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.shutil")
|
|
||||||
@mock.patch("diceplayer.interface.dice_interface.Path.exists", return_value=False)
|
|
||||||
def test_run_dice_raises_filenotfound_on_invalid_file(
|
|
||||||
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.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.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.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.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.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.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.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.config.player_config import PlayerConfig
|
|
||||||
from diceplayer.environment import System
|
|
||||||
from diceplayer.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.model_validate(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.interface.gaussian_interface.Path.mkdir")
|
|
||||||
@mock.patch("diceplayer.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.interface.gaussian_interface.shutil.copy")
|
|
||||||
@mock.patch("diceplayer.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.interface.gaussian_interface.shutil.copy")
|
|
||||||
@mock.patch("diceplayer.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.interface.gaussian_interface.shutil.copy")
|
|
||||||
@mock.patch("diceplayer.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.utils 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.utils.logger.Path.exists")
|
|
||||||
@mock.patch("diceplayer.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.utils.logger.Path.exists")
|
|
||||||
@mock.patch("diceplayer.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()
|
|
||||||
0
tests/state/__init__.py
Normal file
0
tests/state/__init__.py
Normal file
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,396 +0,0 @@
|
|||||||
from diceplayer import 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:combrule = *",
|
|
||||||
"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:upbuf = 360",
|
|
||||||
"INFO:diceplayer:------------------------------------------------------------------------------------------\n GAUSSIAN variables being used in this run:\n------------------------------------------------------------------------------------------\n",
|
|
||||||
"INFO:diceplayer:chg_tol = 0.01",
|
|
||||||
"INFO:diceplayer:chgmult = [ 0 1 ]",
|
|
||||||
"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()
|
|
||||||
0
tests/utils/__init__.py
Normal file
0
tests/utils/__init__.py
Normal file
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)
|
||||||
338
uv.lock
generated
338
uv.lock
generated
@@ -1,10 +1,6 @@
|
|||||||
version = 1
|
version = 1
|
||||||
revision = 3
|
revision = 3
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.12"
|
||||||
resolution-markers = [
|
|
||||||
"python_full_version >= '3.11'",
|
|
||||||
"python_full_version < '3.11'",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "annotated-types"
|
name = "annotated-types"
|
||||||
@@ -35,21 +31,9 @@ dependencies = [
|
|||||||
{ name = "pathspec" },
|
{ name = "pathspec" },
|
||||||
{ name = "platformdirs" },
|
{ name = "platformdirs" },
|
||||||
{ name = "pytokens" },
|
{ name = "pytokens" },
|
||||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
||||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/51/1b/523329e713f965ad0ea2b7a047eeb003007792a0353622ac7a8cb2ee6fef/black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168", size = 1849661, upload-time = "2026-01-18T04:59:12.425Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/14/82/94c0640f7285fa71c2f32879f23e609dd2aa39ba2641f395487f24a578e7/black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d", size = 1689065, upload-time = "2026-01-18T04:59:13.993Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f0/78/474373cbd798f9291ed8f7107056e343fd39fef42de4a51c7fd0d360840c/black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0", size = 1751502, upload-time = "2026-01-18T04:59:15.971Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/29/89/59d0e350123f97bc32c27c4d79563432d7f3530dca2bff64d855c178af8b/black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24", size = 1400102, upload-time = "2026-01-18T04:59:17.8Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e1/bc/5d866c7ae1c9d67d308f83af5462ca7046760158bbf142502bad8f22b3a1/black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89", size = 1207038, upload-time = "2026-01-18T04:59:19.543Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/30/83/f05f22ff13756e1a8ce7891db517dbc06200796a16326258268f4658a745/black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5", size = 1831956, upload-time = "2026-01-18T04:59:21.38Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/7d/f2/b2c570550e39bedc157715e43927360312d6dd677eed2cc149a802577491/black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68", size = 1672499, upload-time = "2026-01-18T04:59:23.257Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/7a/d7/990d6a94dc9e169f61374b1c3d4f4dd3037e93c2cc12b6f3b12bc663aa7b/black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14", size = 1735431, upload-time = "2026-01-18T04:59:24.729Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/36/1c/cbd7bae7dd3cb315dfe6eeca802bb56662cc92b89af272e014d98c1f2286/black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c", size = 1400468, upload-time = "2026-01-18T04:59:27.381Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/59/b1/9fe6132bb2d0d1f7094613320b56297a108ae19ecf3041d9678aec381b37/black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4", size = 1207332, upload-time = "2026-01-18T04:59:28.711Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189, upload-time = "2026-01-18T04:59:30.682Z" },
|
{ url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189, upload-time = "2026-01-18T04:59:30.682Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178, upload-time = "2026-01-18T04:59:32.387Z" },
|
{ url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178, upload-time = "2026-01-18T04:59:32.387Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029, upload-time = "2026-01-18T04:59:33.767Z" },
|
{ url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029, upload-time = "2026-01-18T04:59:33.767Z" },
|
||||||
@@ -104,35 +88,6 @@ version = "7.13.4"
|
|||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/44/d4/7827d9ffa34d5d4d752eec907022aa417120936282fc488306f5da08c292/coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415", size = 219152, upload-time = "2026-02-09T12:56:11.974Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/35/b0/d69df26607c64043292644dbb9dc54b0856fabaa2cbb1eeee3331cc9e280/coverage-7.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b", size = 219667, upload-time = "2026-02-09T12:56:13.33Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/82/a4/c1523f7c9e47b2271dbf8c2a097e7a1f89ef0d66f5840bb59b7e8814157b/coverage-7.13.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a", size = 246425, upload-time = "2026-02-09T12:56:14.552Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/02/aa7ec01d1a5023c4b680ab7257f9bfde9defe8fdddfe40be096ac19e8177/coverage-7.13.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f", size = 248229, upload-time = "2026-02-09T12:56:16.31Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/35/98/85aba0aed5126d896162087ef3f0e789a225697245256fc6181b95f47207/coverage-7.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012", size = 250106, upload-time = "2026-02-09T12:56:18.024Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/96/72/1db59bd67494bc162e3e4cd5fbc7edba2c7026b22f7c8ef1496d58c2b94c/coverage-7.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def", size = 252021, upload-time = "2026-02-09T12:56:19.272Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/9d/97/72899c59c7066961de6e3daa142d459d47d104956db43e057e034f015c8a/coverage-7.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256", size = 247114, upload-time = "2026-02-09T12:56:21.051Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/39/1f/f1885573b5970235e908da4389176936c8933e86cb316b9620aab1585fa2/coverage-7.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda", size = 248143, upload-time = "2026-02-09T12:56:22.585Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a8/cf/e80390c5b7480b722fa3e994f8202807799b85bc562aa4f1dde209fbb7be/coverage-7.13.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92", size = 246152, upload-time = "2026-02-09T12:56:23.748Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/44/bf/f89a8350d85572f95412debb0fb9bb4795b1d5b5232bd652923c759e787b/coverage-7.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c", size = 249959, upload-time = "2026-02-09T12:56:25.209Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f7/6e/612a02aece8178c818df273e8d1642190c4875402ca2ba74514394b27aba/coverage-7.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58", size = 246416, upload-time = "2026-02-09T12:56:26.475Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/cb/98/b5afc39af67c2fa6786b03c3a7091fc300947387ce8914b096db8a73d67a/coverage-7.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9", size = 247025, upload-time = "2026-02-09T12:56:27.727Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/51/30/2bba8ef0682d5bd210c38fe497e12a06c9f8d663f7025e9f5c2c31ce847d/coverage-7.13.4-cp310-cp310-win32.whl", hash = "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf", size = 221758, upload-time = "2026-02-09T12:56:29.051Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/13/331f94934cf6c092b8ea59ff868eb587bc8fe0893f02c55bc6c0183a192e/coverage-7.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95", size = 222693, upload-time = "2026-02-09T12:56:30.366Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b4/ad/b59e5b451cf7172b8d1043dc0fa718f23aab379bc1521ee13d4bd9bfa960/coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053", size = 219278, upload-time = "2026-02-09T12:56:31.673Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f1/17/0cb7ca3de72e5f4ef2ec2fa0089beafbcaaaead1844e8b8a63d35173d77d/coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11", size = 219783, upload-time = "2026-02-09T12:56:33.104Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ab/63/325d8e5b11e0eaf6d0f6a44fad444ae58820929a9b0de943fa377fe73e85/coverage-7.13.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa", size = 250200, upload-time = "2026-02-09T12:56:34.474Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/53/c16972708cbb79f2942922571a687c52bd109a7bd51175aeb7558dff2236/coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7", size = 252114, upload-time = "2026-02-09T12:56:35.749Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/eb/c2/7ab36d8b8cc412bec9ea2d07c83c48930eb4ba649634ba00cb7e4e0f9017/coverage-7.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00", size = 254220, upload-time = "2026-02-09T12:56:37.796Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d6/4d/cf52c9a3322c89a0e6febdfbc83bb45c0ed3c64ad14081b9503adee702e7/coverage-7.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef", size = 256164, upload-time = "2026-02-09T12:56:39.016Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/e9/eb1dd17bd6de8289df3580e967e78294f352a5df8a57ff4671ee5fc3dcd0/coverage-7.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903", size = 250325, upload-time = "2026-02-09T12:56:40.668Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/71/07/8c1542aa873728f72267c07278c5cc0ec91356daf974df21335ccdb46368/coverage-7.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f", size = 251913, upload-time = "2026-02-09T12:56:41.97Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/74/d7/c62e2c5e4483a748e27868e4c32ad3daa9bdddbba58e1bc7a15e252baa74/coverage-7.13.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299", size = 249974, upload-time = "2026-02-09T12:56:43.323Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/98/9f/4c5c015a6e98ced54efd0f5cf8d31b88e5504ecb6857585fc0161bb1e600/coverage-7.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505", size = 253741, upload-time = "2026-02-09T12:56:45.155Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/bd/59/0f4eef89b9f0fcd9633b5d350016f54126ab49426a70ff4c4e87446cabdc/coverage-7.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6", size = 249695, upload-time = "2026-02-09T12:56:46.636Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b5/2c/b7476f938deb07166f3eb281a385c262675d688ff4659ad56c6c6b8e2e70/coverage-7.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9", size = 250599, upload-time = "2026-02-09T12:56:48.13Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b8/34/c3420709d9846ee3785b9f2831b4d94f276f38884032dca1457fa83f7476/coverage-7.13.4-cp311-cp311-win32.whl", hash = "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9", size = 221780, upload-time = "2026-02-09T12:56:50.479Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/61/08/3d9c8613079d2b11c185b865de9a4c1a68850cfda2b357fae365cf609f29/coverage-7.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f", size = 222715, upload-time = "2026-02-09T12:56:51.815Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/18/1a/54c3c80b2f056164cc0a6cdcb040733760c7c4be9d780fe655f356f433e4/coverage-7.13.4-cp311-cp311-win_arm64.whl", hash = "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f", size = 221385, upload-time = "2026-02-09T12:56:53.194Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/81/4ce2fdd909c5a0ed1f6dedb88aa57ab79b6d1fbd9b588c1ac7ef45659566/coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", size = 219449, upload-time = "2026-02-09T12:56:54.889Z" },
|
{ url = "https://files.pythonhosted.org/packages/d1/81/4ce2fdd909c5a0ed1f6dedb88aa57ab79b6d1fbd9b588c1ac7ef45659566/coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", size = 219449, upload-time = "2026-02-09T12:56:54.889Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", size = 219810, upload-time = "2026-02-09T12:56:56.33Z" },
|
{ url = "https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", size = 219810, upload-time = "2026-02-09T12:56:56.33Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/72/2f372b726d433c9c35e56377cf1d513b4c16fe51841060d826b95caacec1/coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", size = 251308, upload-time = "2026-02-09T12:56:57.858Z" },
|
{ url = "https://files.pythonhosted.org/packages/78/72/2f372b726d433c9c35e56377cf1d513b4c16fe51841060d826b95caacec1/coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", size = 251308, upload-time = "2026-02-09T12:56:57.858Z" },
|
||||||
@@ -216,8 +171,7 @@ name = "diceplayer"
|
|||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "argparse" },
|
{ name = "argparse" },
|
||||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
{ name = "numpy" },
|
||||||
{ name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "pyyaml" },
|
{ name = "pyyaml" },
|
||||||
{ name = "setproctitle" },
|
{ name = "setproctitle" },
|
||||||
@@ -231,6 +185,7 @@ dev = [
|
|||||||
{ name = "isort" },
|
{ name = "isort" },
|
||||||
{ name = "poethepoet" },
|
{ name = "poethepoet" },
|
||||||
{ name = "pre-commit" },
|
{ name = "pre-commit" },
|
||||||
|
{ name = "pytest" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -251,6 +206,7 @@ dev = [
|
|||||||
{ name = "isort", specifier = ">=5.13.2" },
|
{ name = "isort", specifier = ">=5.13.2" },
|
||||||
{ name = "poethepoet", specifier = ">=0.27.0" },
|
{ name = "poethepoet", specifier = ">=0.27.0" },
|
||||||
{ name = "pre-commit", specifier = ">=3.7.1" },
|
{ name = "pre-commit", specifier = ">=3.7.1" },
|
||||||
|
{ name = "pytest", specifier = ">=9.0.2" },
|
||||||
{ name = "ruff", specifier = ">=0.15.2" },
|
{ name = "ruff", specifier = ">=0.15.2" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -281,6 +237,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" },
|
{ url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "isort"
|
name = "isort"
|
||||||
version = "8.0.1"
|
version = "8.0.1"
|
||||||
@@ -308,91 +273,12 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
|
{ url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "numpy"
|
|
||||||
version = "2.2.6"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
resolution-markers = [
|
|
||||||
"python_full_version < '3.11'",
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numpy"
|
name = "numpy"
|
||||||
version = "2.4.2"
|
version = "2.4.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
resolution-markers = [
|
|
||||||
"python_full_version >= '3.11'",
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d3/44/71852273146957899753e69986246d6a176061ea183407e95418c2aa4d9a/numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825", size = 16955478, upload-time = "2026-01-31T23:10:25.623Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/74/41/5d17d4058bd0cd96bcbd4d9ff0fb2e21f52702aab9a72e4a594efa18692f/numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1", size = 14965467, upload-time = "2026-01-31T23:10:28.186Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/49/48/fb1ce8136c19452ed15f033f8aee91d5defe515094e330ce368a0647846f/numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7", size = 5475172, upload-time = "2026-01-31T23:10:30.848Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/40/a9/3feb49f17bbd1300dd2570432961f5c8a4ffeff1db6f02c7273bd020a4c9/numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73", size = 6805145, upload-time = "2026-01-31T23:10:32.352Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3f/39/fdf35cbd6d6e2fcad42fcf85ac04a85a0d0fbfbf34b30721c98d602fd70a/numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1", size = 15966084, upload-time = "2026-01-31T23:10:34.502Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1b/46/6fa4ea94f1ddf969b2ee941290cca6f1bfac92b53c76ae5f44afe17ceb69/numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32", size = 16899477, upload-time = "2026-01-31T23:10:37.075Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/09/a1/2a424e162b1a14a5bd860a464ab4e07513916a64ab1683fae262f735ccd2/numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390", size = 17323429, upload-time = "2026-01-31T23:10:39.704Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ce/a2/73014149ff250628df72c58204822ac01d768697913881aacf839ff78680/numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413", size = 18635109, upload-time = "2026-01-31T23:10:41.924Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6c/0c/73e8be2f1accd56df74abc1c5e18527822067dced5ec0861b5bb882c2ce0/numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda", size = 6237915, upload-time = "2026-01-31T23:10:45.26Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/ae/e0265e0163cf127c24c3969d29f1c4c64551a1e375d95a13d32eab25d364/numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695", size = 12607972, upload-time = "2026-01-31T23:10:47.021Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/29/a5/c43029af9b8014d6ea157f192652c50042e8911f4300f8f6ed3336bf437f/numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3", size = 10485763, upload-time = "2026-01-31T23:10:50.087Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" },
|
{ url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" },
|
{ url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" },
|
{ url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" },
|
||||||
@@ -446,13 +332,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" },
|
{ url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" },
|
{ url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" },
|
{ url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f4/f8/50e14d36d915ef64d8f8bc4a087fc8264d82c785eda6711f80ab7e620335/numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082", size = 16833179, upload-time = "2026-01-31T23:12:53.5Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/17/809b5cad63812058a8189e91a1e2d55a5a18fd04611dbad244e8aeae465c/numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a", size = 14889755, upload-time = "2026-01-31T23:12:55.933Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3e/ea/181b9bcf7627fc8371720316c24db888dcb9829b1c0270abf3d288b2e29b/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920", size = 5399500, upload-time = "2026-01-31T23:12:58.671Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/33/9f/413adf3fc955541ff5536b78fcf0754680b3c6d95103230252a2c9408d23/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821", size = 6714252, upload-time = "2026-01-31T23:13:00.518Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/91/da/643aad274e29ccbdf42ecd94dafe524b81c87bcb56b83872d54827f10543/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb", size = 15797142, upload-time = "2026-01-31T23:13:02.219Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/66/27/965b8525e9cb5dc16481b30a1b3c21e50c7ebf6e9dbd48d0c4d0d5089c7e/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0", size = 16727979, upload-time = "2026-01-31T23:13:04.62Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/de/e5/b7d20451657664b07986c2f6e3be564433f5dcaf3482d68eaecd79afaf03/numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", size = 12502577, upload-time = "2026-01-31T23:13:07.08Z" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -491,6 +370,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" },
|
{ url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "poethepoet"
|
name = "poethepoet"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
@@ -498,7 +386,6 @@ source = { registry = "https://pypi.org/simple" }
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pastel" },
|
{ name = "pastel" },
|
||||||
{ name = "pyyaml" },
|
{ name = "pyyaml" },
|
||||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/05/9b/e717572686bbf23e17483389c1bf3a381ca2427c84c7e0af0cdc0f23fccc/poethepoet-0.42.1.tar.gz", hash = "sha256:205747e276062c2aaba8afd8a98838f8a3a0237b7ab94715fab8d82718aac14f", size = 93209, upload-time = "2026-02-26T22:57:50.883Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/05/9b/e717572686bbf23e17483389c1bf3a381ca2427c84c7e0af0cdc0f23fccc/poethepoet-0.42.1.tar.gz", hash = "sha256:205747e276062c2aaba8afd8a98838f8a3a0237b7ab94715fab8d82718aac14f", size = 93209, upload-time = "2026-02-26T22:57:50.883Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
@@ -545,33 +432,6 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
|
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
|
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
|
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
|
||||||
@@ -628,30 +488,35 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
|
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
|
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
|
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
|
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
|
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
|
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" },
|
]
|
||||||
{ url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" },
|
[[package]]
|
||||||
{ url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" },
|
name = "pygments"
|
||||||
{ url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" },
|
version = "2.19.2"
|
||||||
{ url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" },
|
source = { registry = "https://pypi.org/simple" }
|
||||||
{ url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" },
|
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
||||||
{ url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" },
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
|
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
|
]
|
||||||
{ url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
|
[[package]]
|
||||||
{ url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
|
name = "pytest"
|
||||||
{ url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
|
version = "9.0.2"
|
||||||
{ url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
|
source = { registry = "https://pypi.org/simple" }
|
||||||
{ url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "iniconfig" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -673,16 +538,6 @@ version = "0.4.1"
|
|||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" },
|
{ url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" },
|
{ url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" },
|
{ url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" },
|
||||||
@@ -712,24 +567,6 @@ version = "6.0.3"
|
|||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
||||||
@@ -801,26 +638,6 @@ version = "1.3.7"
|
|||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/8d/48/49393a96a2eef1ab418b17475fb92b8fcfad83d099e678751b05472e69de/setproctitle-1.3.7.tar.gz", hash = "sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e", size = 27002, upload-time = "2025-09-05T12:51:25.278Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f2/48/fb401ec8c4953d519d05c87feca816ad668b8258448ff60579ac7a1c1386/setproctitle-1.3.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cf555b6299f10a6eb44e4f96d2f5a3884c70ce25dc5c8796aaa2f7b40e72cb1b", size = 18079, upload-time = "2025-09-05T12:49:07.732Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/cc/a3/c2b0333c2716fb3b4c9a973dd113366ac51b4f8d56b500f4f8f704b4817a/setproctitle-1.3.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:690b4776f9c15aaf1023bb07d7c5b797681a17af98a4a69e76a1d504e41108b7", size = 13099, upload-time = "2025-09-05T12:49:09.222Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/0e/f8/17bda581c517678260e6541b600eeb67745f53596dc077174141ba2f6702/setproctitle-1.3.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:00afa6fc507967d8c9d592a887cdc6c1f5742ceac6a4354d111ca0214847732c", size = 31793, upload-time = "2025-09-05T12:49:10.297Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/27/d1/76a33ae80d4e788ecab9eb9b53db03e81cfc95367ec7e3fbf4989962fedd/setproctitle-1.3.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e02667f6b9fc1238ba753c0f4b0a37ae184ce8f3bbbc38e115d99646b3f4cd3", size = 32779, upload-time = "2025-09-05T12:49:12.157Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/59/27/1a07c38121967061564f5e0884414a5ab11a783260450172d4fc68c15621/setproctitle-1.3.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83fcd271567d133eb9532d3b067c8a75be175b2b3b271e2812921a05303a693f", size = 34578, upload-time = "2025-09-05T12:49:13.393Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d8/d4/725e6353935962d8bb12cbf7e7abba1d0d738c7f6935f90239d8e1ccf913/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13fe37951dda1a45c35d77d06e3da5d90e4f875c4918a7312b3b4556cfa7ff64", size = 32030, upload-time = "2025-09-05T12:49:15.362Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/67/24/e4677ae8e1cb0d549ab558b12db10c175a889be0974c589c428fece5433e/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a05509cfb2059e5d2ddff701d38e474169e9ce2a298cf1b6fd5f3a213a553fe5", size = 33363, upload-time = "2025-09-05T12:49:16.829Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/55/d4/69ce66e4373a48fdbb37489f3ded476bb393e27f514968c3a69a67343ae0/setproctitle-1.3.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6da835e76ae18574859224a75db6e15c4c2aaa66d300a57efeaa4c97ca4c7381", size = 31508, upload-time = "2025-09-05T12:49:18.032Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/4b/5a/42c1ed0e9665d068146a68326529b5686a1881c8b9197c2664db4baf6aeb/setproctitle-1.3.7-cp310-cp310-win32.whl", hash = "sha256:9e803d1b1e20240a93bac0bc1025363f7f80cb7eab67dfe21efc0686cc59ad7c", size = 12558, upload-time = "2025-09-05T12:49:19.742Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/dc/fe/dd206cc19a25561921456f6cb12b405635319299b6f366e0bebe872abc18/setproctitle-1.3.7-cp310-cp310-win_amd64.whl", hash = "sha256:a97200acc6b64ec4cada52c2ecaf1fba1ef9429ce9c542f8a7db5bcaa9dcbd95", size = 13245, upload-time = "2025-09-05T12:49:21.023Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/04/cd/1b7ba5cad635510720ce19d7122154df96a2387d2a74217be552887c93e5/setproctitle-1.3.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0", size = 18085, upload-time = "2025-09-05T12:49:22.183Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/8f/1a/b2da0a620490aae355f9d72072ac13e901a9fec809a6a24fc6493a8f3c35/setproctitle-1.3.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4", size = 13097, upload-time = "2025-09-05T12:49:23.322Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/18/2e/bd03ff02432a181c1787f6fc2a678f53b7dacdd5ded69c318fe1619556e8/setproctitle-1.3.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f", size = 32191, upload-time = "2025-09-05T12:49:24.567Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/28/78/1e62fc0937a8549f2220445ed2175daacee9b6764c7963b16148119b016d/setproctitle-1.3.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9", size = 33203, upload-time = "2025-09-05T12:49:25.871Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a0/3c/65edc65db3fa3df400cf13b05e9d41a3c77517b4839ce873aa6b4043184f/setproctitle-1.3.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba", size = 34963, upload-time = "2025-09-05T12:49:27.044Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a1/32/89157e3de997973e306e44152522385f428e16f92f3cf113461489e1e2ee/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307", size = 32398, upload-time = "2025-09-05T12:49:28.909Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/4a/18/77a765a339ddf046844cb4513353d8e9dcd8183da9cdba6e078713e6b0b2/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee", size = 33657, upload-time = "2025-09-05T12:49:30.323Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/6b/63/f0b6205c64d74d2a24a58644a38ec77bdbaa6afc13747e75973bf8904932/setproctitle-1.3.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1", size = 31836, upload-time = "2025-09-05T12:49:32.309Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ba/51/e1277f9ba302f1a250bbd3eedbbee747a244b3cc682eb58fb9733968f6d8/setproctitle-1.3.7-cp311-cp311-win32.whl", hash = "sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d", size = 12556, upload-time = "2025-09-05T12:49:33.529Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b6/7b/822a23f17e9003dfdee92cd72758441ca2a3680388da813a371b716fb07f/setproctitle-1.3.7-cp311-cp311-win_amd64.whl", hash = "sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4", size = 13243, upload-time = "2025-09-05T12:49:34.553Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" },
|
{ url = "https://files.pythonhosted.org/packages/fb/f0/2dc88e842077719d7384d86cc47403e5102810492b33680e7dadcee64cd8/setproctitle-1.3.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e", size = 18049, upload-time = "2025-09-05T12:49:36.241Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" },
|
{ url = "https://files.pythonhosted.org/packages/f0/b4/50940504466689cda65680c9e9a1e518e5750c10490639fa687489ac7013/setproctitle-1.3.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798", size = 13079, upload-time = "2025-09-05T12:49:38.088Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" },
|
{ url = "https://files.pythonhosted.org/packages/d0/99/71630546b9395b095f4082be41165d1078204d1696c2d9baade3de3202d0/setproctitle-1.3.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629", size = 32932, upload-time = "2025-09-05T12:49:39.271Z" },
|
||||||
@@ -871,66 +688,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e7/e3/54b496ac724e60e61cc3447f02690105901ca6d90da0377dffe49ff99fc7/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1fae595d032b30dab4d659bece20debd202229fce12b55abab978b7f30783d73", size = 33958, upload-time = "2025-09-05T12:50:39.841Z" },
|
{ url = "https://files.pythonhosted.org/packages/e7/e3/54b496ac724e60e61cc3447f02690105901ca6d90da0377dffe49ff99fc7/setproctitle-1.3.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1fae595d032b30dab4d659bece20debd202229fce12b55abab978b7f30783d73", size = 33958, upload-time = "2025-09-05T12:50:39.841Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ea/a8/c84bb045ebf8c6fdc7f7532319e86f8380d14bbd3084e6348df56bdfe6fd/setproctitle-1.3.7-cp314-cp314t-win32.whl", hash = "sha256:02432f26f5d1329ab22279ff863c83589894977063f59e6c4b4845804a08f8c2", size = 12745, upload-time = "2025-09-05T12:50:41.377Z" },
|
{ url = "https://files.pythonhosted.org/packages/ea/a8/c84bb045ebf8c6fdc7f7532319e86f8380d14bbd3084e6348df56bdfe6fd/setproctitle-1.3.7-cp314-cp314t-win32.whl", hash = "sha256:02432f26f5d1329ab22279ff863c83589894977063f59e6c4b4845804a08f8c2", size = 12745, upload-time = "2025-09-05T12:50:41.377Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/08/b6/3a5a4f9952972791a9114ac01dfc123f0df79903577a3e0a7a404a695586/setproctitle-1.3.7-cp314-cp314t-win_amd64.whl", hash = "sha256:cbc388e3d86da1f766d8fc2e12682e446064c01cea9f88a88647cfe7c011de6a", size = 13469, upload-time = "2025-09-05T12:50:42.67Z" },
|
{ url = "https://files.pythonhosted.org/packages/08/b6/3a5a4f9952972791a9114ac01dfc123f0df79903577a3e0a7a404a695586/setproctitle-1.3.7-cp314-cp314t-win_amd64.whl", hash = "sha256:cbc388e3d86da1f766d8fc2e12682e446064c01cea9f88a88647cfe7c011de6a", size = 13469, upload-time = "2025-09-05T12:50:42.67Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/34/8a/aff5506ce89bc3168cb492b18ba45573158d528184e8a9759a05a09088a9/setproctitle-1.3.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:eb440c5644a448e6203935ed60466ec8d0df7278cd22dc6cf782d07911bcbea6", size = 12654, upload-time = "2025-09-05T12:51:17.141Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/41/89/5b6f2faedd6ced3d3c085a5efbd91380fb1f61f4c12bc42acad37932f4e9/setproctitle-1.3.7-pp310-pypy310_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:502b902a0e4c69031b87870ff4986c290ebbb12d6038a70639f09c331b18efb2", size = 14284, upload-time = "2025-09-05T12:51:18.393Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/0a/c0/4312fed3ca393a29589603fd48f17937b4ed0638b923bac75a728382e730/setproctitle-1.3.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f6f268caeabb37ccd824d749e7ce0ec6337c4ed954adba33ec0d90cc46b0ab78", size = 13282, upload-time = "2025-09-05T12:51:19.703Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/c3/5b/5e1c117ac84e3cefcf8d7a7f6b2461795a87e20869da065a5c087149060b/setproctitle-1.3.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1", size = 12587, upload-time = "2025-09-05T12:51:21.195Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/73/02/b9eadc226195dcfa90eed37afe56b5dd6fa2f0e5220ab8b7867b8862b926/setproctitle-1.3.7-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65", size = 14286, upload-time = "2025-09-05T12:51:22.61Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/28/26/1be1d2a53c2a91ec48fa2ff4a409b395f836798adf194d99de9c059419ea/setproctitle-1.3.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a", size = 13282, upload-time = "2025-09-05T12:51:24.094Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tomli"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -963,7 +720,6 @@ dependencies = [
|
|||||||
{ name = "filelock" },
|
{ name = "filelock" },
|
||||||
{ name = "platformdirs" },
|
{ name = "platformdirs" },
|
||||||
{ name = "python-discovery" },
|
{ name = "python-discovery" },
|
||||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/2f/c9/18d4b36606d6091844daa3bd93cf7dc78e6f5da21d9f21d06c221104b684/virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44", size = 5840471, upload-time = "2026-02-27T08:49:29.702Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/2f/c9/18d4b36606d6091844daa3bd93cf7dc78e6f5da21d9f21d06c221104b684/virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44", size = 5840471, upload-time = "2026-02-27T08:49:29.702Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
|
|||||||
Reference in New Issue
Block a user