squish/squish/common.py

99 lines
2.7 KiB
Python

from __future__ import annotations
from typing import List, Tuple, Union, Optional, Iterator, Generator
import pickle, numpy as np
from math import gcd
from pathlib import Path
from ._squish import VoronoiContainer, AreaEnergy, RadialALEnergy, RadialTEnergy
OUTPUT_DIR = Path("squish_output")
STR_TO_ENERGY = {
"area": AreaEnergy,
"radial-al": RadialALEnergy,
"radial-t": RadialTEnergy,
}
def generate_filepath(
sim: SimulationMode, fol: Union[str, Path], prec: int = 2
) -> Path:
energy = sim.energy.title_str
width, height = round(sim.domain.w, prec), round(sim.domain.h, prec)
base_path = (
f"{fol}/{energy}{sim.title_str} - "
+ f"N{sim.domain.n} - {width:.{prec}f}x{height:.{prec}f}"
)
i = 1
real_path = Path(base_path)
while real_path.is_dir():
real_path = Path(f"{base_path}({i})")
i += 1
return real_path
class DomainParams:
"""Container for basic domain parameters
Attributes:
n (int): Number of sites in simulation.
w (float): width of the bounding domain.
h (float): height of the bounding domain.
r (float): natural radius of the objects.
dim (np.ndarray): dimensions, w x h.
"""
__slots__ = ["n", "w", "h", "r", "dim"]
def __init__(self, n: int, w: float, h: float, r: float) -> None:
if n < 2:
raise ValueError("Number of objects should be larger than 2!")
if w <= 0:
raise ValueError("Width needs to be nonzero and positive!")
if h <= 0:
raise ValueError("Height needs to be nonzero and positive!")
self.n, self.w, self.h, self.r = int(n), float(w), float(h), float(r)
self.dim = np.array([self.w, self.h])
def __iter__(self) -> Iterator:
return iter((self.n, self.w, self.h, self.r))
def __str__(self) -> str:
return f"N = {self.n}, R = {self.r}, {self.w} X {self.h}"
class Energy:
"""Generic container for energies.
Attributes:
mode (VoronoiContainer): VoronoiContainer for the chosen energy.
"""
__slots__ = ["mode"]
def __init__(self, mode: Union[str, VoronoiContainer]) -> None:
if isinstance(mode, str):
try:
self.mode = STR_TO_ENERGY[mode.lower()]
except KeyError:
raise ValueError(f"'{mode}' is not a valid energy!")
else:
if mode is not VoronoiContainer and issubclass(mode, VoronoiContainer):
raise ValueError("Provided class is not a valid energy!")
self.mode = mode
@property
def attr_str(self) -> str:
return self.mode.attr_str
@property
def title_str(self) -> str:
return self.mode.title_str