squish/packsim.py

156 lines
5.3 KiB
Python

from __future__ import annotations
from typing import Dict
import argparse, json, numpy as np, os
from shutil import which
from pathlib import Path
from simulation import Diagram, Flow, Search, Shrink
from packsim_core import RadialTEnergy
dia_presets = {
"animate": [["voronoi"]],
"energy": [["voronoi", "energy"]],
"stats": [
["voronoi", "eigs", "site_edge_count"],
["site_isos", "site_energies", "edge_lengths"]
],
"eigs": [["voronoi", "eigs"]]
}
def check_params(container: Dict, needed: List[str], valid: Dict):
"""
Checks container for the necessary items, and raises
an error if the parameter is not found.
:param container: [Dict] contains the submitted parameters.
:param needed: [List[str]] contains the needed parameters.
:param valid: [Dict] if there are specific valid parameters,
will also check for those.
"""
for need in needed:
if need not in container:
raise ValueError(f"Parameter \'{need}\' is required.")
if need in valid:
if type(valid[need]) is list:
if container[need] not in valid[need]:
raise ValueError(f"Parameter \'{need}\' must be one of these values: " + \
f"{str(valid[need])[1:-1]}.")
elif valid[need] == "positive":
if container[need] < 0:
raise ValueError(f"Parameter \'{need}\' must be positive.")
def main():
# Loading configuration and settings.
Path('simulations').mkdir(exist_ok=True)
Path('figures').mkdir(exist_ok=True)
parser = argparse.ArgumentParser("PackSim")
parser.add_argument('sim_conf', metavar='/path/to/config.json',
help="configuration file for a simulation")
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', default=False,
help="suppress all normal output")
parser.add_argument('-l', '--log', dest='log_steps', default=50, type=int,
help="number of iterations before logging")
parser.add_argument('--n_objects', dest='n', type=int, help="objects in domain")
parser.add_argument('--width', dest='w', type=float, help="width of domain")
parser.add_argument('--height', dest='h', type=float, help="height of domain")
parser.add_argument('--natural_radius', dest='r', type=float, help="natural radius of object")
parser.add_argument('--energy', dest='energy', help="energy type of system")
args = parser.parse_args()
config_sim(args)
# if args.input_file is None:
# config_sim(args)
# else:
# loaded_sim(args)
def config_sim(args):
with open(args.sim_conf) as f:
params = json.load(f)
check_params(params, ["domain", "simulation"], {})
dmn_params, sim_params = params["domain"], params["simulation"]
overrides = {args.n: "n_objects", args.w: "width", args.h: "height", args.r: "natural_radius",
args.energy: "energy"}
for arg, arg_name in overrides.items():
if arg is not None:
dmn_params[arg_name] = arg
check_params(dmn_params, ["n_objects", "width", "height", "natural_radius", "energy"], {
"n_objects": "positive", "width": "positive", "height": "positive",
"natural_radius": "positive", "energy": ["area", "radial-al", "radial-t"]
})
n, w, h, r, energy = dmn_params["n_objects"], dmn_params["width"], dmn_params["height"], \
dmn_params["natural_radius"], dmn_params["energy"]
points = None
if "points" in dmn_params:
points = np.asarray(dmn_params["points"])
check_params(sim_params, ["mode", "step_size", "threshold", "save_sim"], {
"mode": ["flow", "search", "shrink"], "step_size": "positive", "threshold": "positive"
})
mode, step, thres, save_sim = sim_params["mode"], sim_params["step_size"], \
sim_params["threshold"], sim_params["save_sim"]
name = sim_params.get("name")
if mode == "flow":
sim = Flow(n, w, h, r, energy, thres, step)
elif mode == "search":
check_params(sim_params, ["manifold_step_size", "eq_stop_count"], {
"manifold_step_size": "positive", "eq_stop_count": "positive"
})
sim = Search(n, w, h, r, energy, thres, step, sim_params["manifold_step_size"],
sim_params["eq_stop_count"])
elif mode == "shrink":
check_params(sim_params, ["width_change", "width_stop"], {
"width_change": "positive", "width_stop": "positive"
})
sim = Shrink(n, w, h, r, energy, thres, step, sim_params["width_change"],
sim_params["width_stop"])
save_diagram = False
if "diagram" in params:
save_diagram = True
dia_params = params["diagram"]
check_params(dia_params, ["filetype", "figures"], {
"filetype": ["img", "mp4"]
})
if dia_params["filetype"] == "mp4":
if which("ffmpeg") is None:
raise ValueError("The program 'ffmpeg' needs to be installed on your system.")
if type(dia_params["figures"]) is str:
dia_params["figures"] = np.asarray(dia_presets[dia_params["figures"]])
else:
dia_params["figures"] = np.asarray(dia_params["figures"])
sim.initialize(points)
sim.run(not args.quiet, args.log_steps)
if save_sim: sim.save(filename=name)
if save_diagram:
diagram = Diagram(sim, dia_params["figures"])
if dia_params["filetype"] == "img":
diagram.render_static(0, filename=name)
elif dia_params["filetype"] == "mp4":
diagram.render_video(filename=name)
def loaded_sim(args):
pass
if __name__ == '__main__':
os.environ["QT_LOGGING_RULES"] = "*=false"
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")