testPyPDEVS.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #!/usr/bin/env python3
  2. import subprocess
  3. import csv
  4. import re
  5. from argparse import ArgumentParser, Action
  6. from itertools import product
  7. from datetime import datetime
  8. from pandas.errors import ParserError
  9. MODEL_TYPES = ("LI", "HI", "HO", "HOmod")
  10. DS_MODEL_TYPES = ("LI2HI", "HI2LI", "LI-HI", "dLI", "dHI")
  11. GEN_METHODS = ("start", "uniform", "end")
  12. INT_CYCLES = 0
  13. EXT_CYCLES = 0
  14. PyPDEVS = "./pythonpdevs/main.py"
  15. MinimalPyPDEVS = "./pythonpdevs-minimal/main.py"
  16. # Regex to capture times from script output
  17. TIME_PATTERN = re.compile(
  18. r"Model creation time: ([\d.eE+-]+).*"
  19. r"Engine setup time: ([\d.eE+-]+).*"
  20. r"Simulation time: ([\d.eE+-]+)",
  21. re.S
  22. )
  23. def run_devstone(model_type, depth, width, minimal=False, classic=False, dynamic=False, gen_mode="uniform"):
  24. """Runs the devstone_driver.py script and returns timing results as floats."""
  25. SCRIPT_PATH = PyPDEVS
  26. if minimal:
  27. SCRIPT_PATH = MinimalPyPDEVS
  28. cmd = [
  29. "python3", SCRIPT_PATH,
  30. "-m", model_type,
  31. "-d", str(depth),
  32. "-w", str(width),
  33. "-i", str(INT_CYCLES),
  34. "-e", str(EXT_CYCLES),
  35. ]
  36. if dynamic and not minimal:
  37. cmd.extend(["-D"])
  38. cmd.extend(["-g", gen_mode])
  39. if classic and not minimal:
  40. cmd.extend(["-C"])
  41. result = subprocess.run(cmd, capture_output=True, text=True)
  42. output = result.stdout.strip()
  43. # print(output)
  44. errors = result.stderr.strip()
  45. match = TIME_PATTERN.search(output)
  46. if match:
  47. creation, setup, sim = map(float, match.groups())
  48. return creation, setup, sim, output
  49. print("Failed to run: ", cmd)
  50. print(output)
  51. print(errors)
  52. return None, None, None, output
  53. def ensureDS(args):
  54. intersection = list(set(args.model_structure) & set(DS_MODEL_TYPES))
  55. if intersection and not args.dynamic:
  56. raise ParserError(f"Dynamic Models ({DS_MODEL_TYPES}) can only be used with --dynamic (-D)")
  57. def parse_args():
  58. parser = ArgumentParser()
  59. parser.add_argument("-p", "--path", type=str, default="./Results-PyPDEVS/", help="Path to the results directory")
  60. parser.add_argument("-f", '--filename', type=str, default="", help="Name of the output file")
  61. parser.add_argument("-M", "--minimal", action="store_true", default=False, help="Uses the minimal DEVS kernel. (Less features => less overhead)")
  62. parser.add_argument("-C", "--classic", action="store_true", default=False, help="Selects the Classic DEVS Simulator")
  63. parser.add_argument("-D", "--dynamic", action="store_true", default=False, help="Enables Dynamic Structure DEVS")
  64. parser.add_argument("-g", "--gen-mode", choices=GEN_METHODS, default="uniform", help="Determines the spread of model creation.")
  65. parser.add_argument("-ms", "--model-structure", nargs="+", default=MODEL_TYPES+DS_MODEL_TYPES, choices=MODEL_TYPES+DS_MODEL_TYPES, help="Select one or more model structures")
  66. parser.add_argument("-md", "--max-depth", default=8, type=int, help="Max depth for the generated models")
  67. parser.add_argument("-ds", "--depth-stepsize", default=1, type=int, help="Determines for which depths a model is generated.")
  68. parser.add_argument("-mw", "--max-width", default=8, type=int, help="Max width for the generated models")
  69. parser.add_argument("-ws", "--width-stepsize", default=1, type=int, help="Determines for which depths a model is generated.")
  70. parser.add_argument("-r", "--repetitions", default=1, type=int, help="The number of times a simulation is repeated")
  71. args = parser.parse_args()
  72. ensureDS(args)
  73. return args
  74. if __name__ == "__main__":
  75. args = parse_args()
  76. print(args)
  77. d = (args.max_depth, args.depth_stepsize)
  78. w = (args.max_width, args.width_stepsize)
  79. if d[0] % d[1] != 0:
  80. raise RuntimeError("Invalid depth stepsize.")
  81. if w[0] % w[1] != 0:
  82. raise RuntimeError("Invalid width stepsize.")
  83. fieldnames = ["model_type", "depth", "width", "run",
  84. "creation_time", "setup_time", "simulation_time", "generator_mode"]
  85. for model_type in args.model_structure:
  86. model_filename = f"{args.path}{model_type}"
  87. if args.filename:
  88. model_filename += f"_{args.filename}"
  89. else:
  90. model_filename += ".csv"
  91. with open(model_filename, "w", newline="") as csvfile:
  92. writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
  93. writer.writeheader()
  94. for depth in range(d[1], d[0] + 1, d[1]):
  95. for width in range(w[1], w[0] + 1, w[1]):
  96. for run in range(args.repetitions):
  97. creation, setup, sim, output = run_devstone(
  98. model_type, depth, width,
  99. args.minimal, args.classic, args.dynamic, args.gen_mode
  100. )
  101. if creation is not None:
  102. writer.writerow({
  103. "model_type": model_type,
  104. "depth": depth,
  105. "width": width,
  106. "run": run,
  107. "creation_time": creation,
  108. "setup_time": setup,
  109. "simulation_time": sim,
  110. "generator_mode": args.gen_mode
  111. })
  112. csvfile.flush()
  113. print(f"✅ Saved {model_type} results to {model_filename}")