testPyPDEVS.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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, mode="parallel", out=False, *, classic=False, number=1, gen_mode="uniform"):
  24. """Runs the devstone_driver.py script and returns timing results as floats."""
  25. SCRIPT_PATH = PyPDEVS
  26. if mode == "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 mode == "dynamic":
  37. cmd.extend(["-D"])
  38. if classic:
  39. cmd.extend(["-C"])
  40. cmd.extend(["-n", str(number)])
  41. cmd.extend(["-g", gen_mode])
  42. if mode == "classic":
  43. cmd.extend(["-C"])
  44. result = subprocess.run(cmd, capture_output=True, text=True)
  45. output = result.stdout.strip()
  46. if out:
  47. print(output)
  48. errors = result.stderr.strip()
  49. match = TIME_PATTERN.search(output)
  50. if match:
  51. creation, setup, sim = map(float, match.groups())
  52. return creation, setup, sim, output
  53. print("Failed to run: ", cmd)
  54. print(output)
  55. print(errors)
  56. return None, None, None, output
  57. def Minimal(subparsers):
  58. minimal = subparsers.add_parser("minimal", help="Minimal DEVS kernel")
  59. minimal.set_defaults(mode="minimal")
  60. minimal.add_argument(
  61. "-ms", "--model-structure",
  62. nargs="+",
  63. choices=MODEL_TYPES,
  64. default=MODEL_TYPES
  65. )
  66. def Classic(subparsers):
  67. classic = subparsers.add_parser("classic", help="Classic DEVS Simulator")
  68. classic.set_defaults(mode="classic")
  69. classic.add_argument(
  70. "-ms", "--model-structure",
  71. nargs="+",
  72. choices=MODEL_TYPES,
  73. default=MODEL_TYPES
  74. )
  75. def Parallel(subparsers):
  76. parallel = subparsers.add_parser("parallel", help="Parallel DEVS")
  77. parallel.set_defaults(mode="parallel")
  78. parallel.add_argument(
  79. "-ms", "--model-structure",
  80. nargs="+",
  81. choices=MODEL_TYPES,
  82. default=MODEL_TYPES
  83. )
  84. def Dynamic(subparsers):
  85. dynamic = subparsers.add_parser("dynamic", help="Dynamic Structure DEVS")
  86. dynamic.set_defaults(mode="dynamic")
  87. dynamic.add_argument(
  88. "-ms", "--model-structure",
  89. nargs="+",
  90. choices=MODEL_TYPES + DS_MODEL_TYPES,
  91. default=MODEL_TYPES + DS_MODEL_TYPES
  92. )
  93. dynamic.add_argument(
  94. "-C", "--classic",
  95. action="store_true",
  96. default=False,
  97. help="Selects the Classic DEVS Simulator"
  98. )
  99. dynamic.add_argument(
  100. "-n", "--number",
  101. default=1,
  102. type=int,
  103. help="Number of models to be generated in a cycle per coupled model")
  104. dynamic.add_argument(
  105. "-g", "--gen-mode",
  106. choices=GEN_METHODS,
  107. default="uniform"
  108. )
  109. def parse_args():
  110. parser = ArgumentParser()
  111. parser.add_argument("-p", "--path", type=str, default="./Results-PyPDEVS/", help="Path to the results directory")
  112. parser.add_argument("-f", '--filename', type=str, default="", help="Name of the output file")
  113. parser.add_argument("-md", "--max-depth", default=8, type=int, help="Max depth for the generated models")
  114. parser.add_argument("-ds", "--depth-stepsize", default=1, type=int,
  115. help="Determines for which depths a model is generated.")
  116. parser.add_argument("-mw", "--max-width", default=8, type=int, help="Max width for the generated models")
  117. parser.add_argument("-ws", "--width-stepsize", default=1, type=int,
  118. help="Determines for which depths a model is generated.")
  119. parser.add_argument("-r", "--repetitions", default=1, type=int, help="The number of times a simulation is repeated")
  120. parser.add_argument("-O", "--output", action="store_true", help="Print Simulation Outputs")
  121. subparsers = parser.add_subparsers(dest="mode")
  122. Minimal(subparsers)
  123. Parallel(subparsers)
  124. Classic(subparsers)
  125. Dynamic(subparsers)
  126. args = parser.parse_args()
  127. return args
  128. if __name__ == "__main__":
  129. args = parse_args()
  130. print(args)
  131. d = (args.max_depth, args.depth_stepsize)
  132. w = (args.max_width, args.width_stepsize)
  133. if d[0] % d[1] != 0:
  134. raise RuntimeError("Invalid depth stepsize.")
  135. if w[0] % w[1] != 0:
  136. raise RuntimeError("Invalid width stepsize.")
  137. fieldnames = ["model_type", "depth", "width", "run",
  138. "creation_time", "setup_time", "simulation_time"]
  139. if args.mode == "dynamic":
  140. fieldnames += ["number", "generator_mode"]
  141. for model_type in args.model_structure:
  142. model_filename = f"{args.path}{model_type}"
  143. if args.filename:
  144. model_filename += f"_{args.filename}"
  145. else:
  146. model_filename += ".csv"
  147. with open(model_filename, "w", newline="") as csvfile:
  148. writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
  149. writer.writeheader()
  150. for depth in range(d[1], d[0] + 1, d[1]):
  151. for width in range(w[1], w[0] + 1, w[1]):
  152. for run in range(args.repetitions):
  153. extra = {}
  154. if args.mode == "dynamic":
  155. extra["classic"] = args.classic
  156. extra["gen_mode"] = args.gen_mode
  157. extra["number"] = args.number
  158. creation, setup, sim, output = run_devstone(
  159. model_type, depth, width,
  160. args.mode, args.output,
  161. **extra
  162. )
  163. if creation is not None:
  164. row = {
  165. "model_type": model_type,
  166. "depth": depth,
  167. "width": width,
  168. "run": run,
  169. "creation_time": creation,
  170. "setup_time": setup,
  171. "simulation_time": sim,
  172. }
  173. if args.mode == "dynamic":
  174. row["number"] = args.number
  175. row["generator_mode"] = args.gen_mode
  176. writer.writerow(row)
  177. csvfile.flush()
  178. print(f"✅ Saved {model_type} results to {model_filename}")