sierra-research 1.3.6__py3-none-any.whl → 1.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. sierra/__init__.py +3 -3
  2. sierra/core/__init__.py +3 -3
  3. sierra/core/batchroot.py +223 -0
  4. sierra/core/cmdline.py +681 -1057
  5. sierra/core/compare.py +11 -0
  6. sierra/core/config.py +96 -88
  7. sierra/core/engine.py +306 -0
  8. sierra/core/execenv.py +380 -0
  9. sierra/core/expdef.py +11 -0
  10. sierra/core/experiment/__init__.py +1 -0
  11. sierra/core/experiment/bindings.py +150 -101
  12. sierra/core/experiment/definition.py +414 -245
  13. sierra/core/experiment/spec.py +83 -85
  14. sierra/core/exproot.py +44 -0
  15. sierra/core/generators/__init__.py +10 -0
  16. sierra/core/generators/experiment.py +528 -0
  17. sierra/core/generators/generator_factory.py +138 -137
  18. sierra/core/graphs/__init__.py +23 -0
  19. sierra/core/graphs/bcbridge.py +94 -0
  20. sierra/core/graphs/heatmap.py +245 -324
  21. sierra/core/graphs/pathset.py +27 -0
  22. sierra/core/graphs/schema.py +77 -0
  23. sierra/core/graphs/stacked_line.py +341 -0
  24. sierra/core/graphs/summary_line.py +506 -0
  25. sierra/core/logging.py +3 -2
  26. sierra/core/models/__init__.py +3 -1
  27. sierra/core/models/info.py +19 -0
  28. sierra/core/models/interface.py +52 -122
  29. sierra/core/pipeline/__init__.py +2 -5
  30. sierra/core/pipeline/pipeline.py +228 -126
  31. sierra/core/pipeline/stage1/__init__.py +10 -0
  32. sierra/core/pipeline/stage1/pipeline_stage1.py +45 -31
  33. sierra/core/pipeline/stage2/__init__.py +10 -0
  34. sierra/core/pipeline/stage2/pipeline_stage2.py +8 -11
  35. sierra/core/pipeline/stage2/runner.py +401 -0
  36. sierra/core/pipeline/stage3/__init__.py +12 -0
  37. sierra/core/pipeline/stage3/gather.py +321 -0
  38. sierra/core/pipeline/stage3/pipeline_stage3.py +37 -84
  39. sierra/core/pipeline/stage4/__init__.py +12 -2
  40. sierra/core/pipeline/stage4/pipeline_stage4.py +36 -354
  41. sierra/core/pipeline/stage5/__init__.py +12 -0
  42. sierra/core/pipeline/stage5/pipeline_stage5.py +33 -208
  43. sierra/core/pipeline/yaml.py +48 -0
  44. sierra/core/plugin.py +529 -62
  45. sierra/core/proc.py +11 -0
  46. sierra/core/prod.py +11 -0
  47. sierra/core/ros1/__init__.py +5 -1
  48. sierra/core/ros1/callbacks.py +22 -21
  49. sierra/core/ros1/cmdline.py +59 -88
  50. sierra/core/ros1/generators.py +159 -175
  51. sierra/core/ros1/variables/__init__.py +3 -0
  52. sierra/core/ros1/variables/exp_setup.py +122 -116
  53. sierra/core/startup.py +106 -76
  54. sierra/core/stat_kernels.py +4 -5
  55. sierra/core/storage.py +13 -32
  56. sierra/core/trampoline.py +30 -0
  57. sierra/core/types.py +116 -71
  58. sierra/core/utils.py +103 -106
  59. sierra/core/variables/__init__.py +1 -1
  60. sierra/core/variables/base_variable.py +12 -17
  61. sierra/core/variables/batch_criteria.py +387 -481
  62. sierra/core/variables/builtin.py +135 -0
  63. sierra/core/variables/exp_setup.py +19 -39
  64. sierra/core/variables/population_size.py +72 -76
  65. sierra/core/variables/variable_density.py +44 -68
  66. sierra/core/vector.py +1 -1
  67. sierra/main.py +256 -88
  68. sierra/plugins/__init__.py +119 -0
  69. sierra/plugins/compare/__init__.py +14 -0
  70. sierra/plugins/compare/graphs/__init__.py +19 -0
  71. sierra/plugins/compare/graphs/cmdline.py +120 -0
  72. sierra/plugins/compare/graphs/comparator.py +291 -0
  73. sierra/plugins/compare/graphs/inter_controller.py +531 -0
  74. sierra/plugins/compare/graphs/inter_scenario.py +297 -0
  75. sierra/plugins/compare/graphs/namecalc.py +53 -0
  76. sierra/plugins/compare/graphs/outputroot.py +73 -0
  77. sierra/plugins/compare/graphs/plugin.py +147 -0
  78. sierra/plugins/compare/graphs/preprocess.py +172 -0
  79. sierra/plugins/compare/graphs/schema.py +37 -0
  80. sierra/plugins/engine/__init__.py +14 -0
  81. sierra/plugins/engine/argos/__init__.py +18 -0
  82. sierra/plugins/{platform → engine}/argos/cmdline.py +144 -151
  83. sierra/plugins/{platform/argos/variables → engine/argos/generators}/__init__.py +5 -0
  84. sierra/plugins/engine/argos/generators/engine.py +394 -0
  85. sierra/plugins/engine/argos/plugin.py +393 -0
  86. sierra/plugins/{platform/argos/generators → engine/argos/variables}/__init__.py +5 -0
  87. sierra/plugins/engine/argos/variables/arena_shape.py +183 -0
  88. sierra/plugins/engine/argos/variables/cameras.py +240 -0
  89. sierra/plugins/engine/argos/variables/constant_density.py +112 -0
  90. sierra/plugins/engine/argos/variables/exp_setup.py +82 -0
  91. sierra/plugins/{platform → engine}/argos/variables/physics_engines.py +83 -87
  92. sierra/plugins/engine/argos/variables/population_constant_density.py +178 -0
  93. sierra/plugins/engine/argos/variables/population_size.py +115 -0
  94. sierra/plugins/engine/argos/variables/population_variable_density.py +123 -0
  95. sierra/plugins/engine/argos/variables/rendering.py +108 -0
  96. sierra/plugins/engine/ros1gazebo/__init__.py +18 -0
  97. sierra/plugins/engine/ros1gazebo/cmdline.py +175 -0
  98. sierra/plugins/{platform/ros1robot → engine/ros1gazebo}/generators/__init__.py +5 -0
  99. sierra/plugins/engine/ros1gazebo/generators/engine.py +125 -0
  100. sierra/plugins/engine/ros1gazebo/plugin.py +404 -0
  101. sierra/plugins/engine/ros1gazebo/variables/__init__.py +15 -0
  102. sierra/plugins/engine/ros1gazebo/variables/population_size.py +214 -0
  103. sierra/plugins/engine/ros1robot/__init__.py +18 -0
  104. sierra/plugins/engine/ros1robot/cmdline.py +159 -0
  105. sierra/plugins/{platform/ros1gazebo → engine/ros1robot}/generators/__init__.py +4 -0
  106. sierra/plugins/engine/ros1robot/generators/engine.py +95 -0
  107. sierra/plugins/engine/ros1robot/plugin.py +410 -0
  108. sierra/plugins/{hpc/local → engine/ros1robot/variables}/__init__.py +5 -0
  109. sierra/plugins/engine/ros1robot/variables/population_size.py +146 -0
  110. sierra/plugins/execenv/__init__.py +11 -0
  111. sierra/plugins/execenv/hpc/__init__.py +18 -0
  112. sierra/plugins/execenv/hpc/adhoc/__init__.py +18 -0
  113. sierra/plugins/execenv/hpc/adhoc/cmdline.py +30 -0
  114. sierra/plugins/execenv/hpc/adhoc/plugin.py +131 -0
  115. sierra/plugins/execenv/hpc/cmdline.py +137 -0
  116. sierra/plugins/execenv/hpc/local/__init__.py +18 -0
  117. sierra/plugins/execenv/hpc/local/cmdline.py +31 -0
  118. sierra/plugins/execenv/hpc/local/plugin.py +145 -0
  119. sierra/plugins/execenv/hpc/pbs/__init__.py +18 -0
  120. sierra/plugins/execenv/hpc/pbs/cmdline.py +30 -0
  121. sierra/plugins/execenv/hpc/pbs/plugin.py +121 -0
  122. sierra/plugins/execenv/hpc/slurm/__init__.py +18 -0
  123. sierra/plugins/execenv/hpc/slurm/cmdline.py +30 -0
  124. sierra/plugins/execenv/hpc/slurm/plugin.py +133 -0
  125. sierra/plugins/execenv/prefectserver/__init__.py +18 -0
  126. sierra/plugins/execenv/prefectserver/cmdline.py +66 -0
  127. sierra/plugins/execenv/prefectserver/dockerremote/__init__.py +18 -0
  128. sierra/plugins/execenv/prefectserver/dockerremote/cmdline.py +66 -0
  129. sierra/plugins/execenv/prefectserver/dockerremote/plugin.py +132 -0
  130. sierra/plugins/execenv/prefectserver/flow.py +66 -0
  131. sierra/plugins/execenv/prefectserver/local/__init__.py +18 -0
  132. sierra/plugins/execenv/prefectserver/local/cmdline.py +29 -0
  133. sierra/plugins/execenv/prefectserver/local/plugin.py +133 -0
  134. sierra/plugins/{hpc/adhoc → execenv/robot}/__init__.py +1 -0
  135. sierra/plugins/execenv/robot/turtlebot3/__init__.py +18 -0
  136. sierra/plugins/execenv/robot/turtlebot3/plugin.py +204 -0
  137. sierra/plugins/expdef/__init__.py +14 -0
  138. sierra/plugins/expdef/json/__init__.py +14 -0
  139. sierra/plugins/expdef/json/plugin.py +504 -0
  140. sierra/plugins/expdef/xml/__init__.py +14 -0
  141. sierra/plugins/expdef/xml/plugin.py +386 -0
  142. sierra/{core/hpc → plugins/proc}/__init__.py +1 -1
  143. sierra/plugins/proc/collate/__init__.py +15 -0
  144. sierra/plugins/proc/collate/cmdline.py +47 -0
  145. sierra/plugins/proc/collate/plugin.py +271 -0
  146. sierra/plugins/proc/compress/__init__.py +18 -0
  147. sierra/plugins/proc/compress/cmdline.py +47 -0
  148. sierra/plugins/proc/compress/plugin.py +123 -0
  149. sierra/plugins/proc/decompress/__init__.py +18 -0
  150. sierra/plugins/proc/decompress/plugin.py +96 -0
  151. sierra/plugins/proc/imagize/__init__.py +15 -0
  152. sierra/plugins/proc/imagize/cmdline.py +49 -0
  153. sierra/plugins/proc/imagize/plugin.py +270 -0
  154. sierra/plugins/proc/modelrunner/__init__.py +16 -0
  155. sierra/plugins/proc/modelrunner/plugin.py +250 -0
  156. sierra/plugins/proc/statistics/__init__.py +15 -0
  157. sierra/plugins/proc/statistics/cmdline.py +64 -0
  158. sierra/plugins/proc/statistics/plugin.py +390 -0
  159. sierra/plugins/{hpc → prod}/__init__.py +1 -0
  160. sierra/plugins/prod/graphs/__init__.py +18 -0
  161. sierra/plugins/prod/graphs/cmdline.py +269 -0
  162. sierra/plugins/prod/graphs/collate.py +279 -0
  163. sierra/plugins/prod/graphs/inter/__init__.py +13 -0
  164. sierra/plugins/prod/graphs/inter/generate.py +83 -0
  165. sierra/plugins/prod/graphs/inter/heatmap.py +86 -0
  166. sierra/plugins/prod/graphs/inter/line.py +134 -0
  167. sierra/plugins/prod/graphs/intra/__init__.py +15 -0
  168. sierra/plugins/prod/graphs/intra/generate.py +202 -0
  169. sierra/plugins/prod/graphs/intra/heatmap.py +74 -0
  170. sierra/plugins/prod/graphs/intra/line.py +114 -0
  171. sierra/plugins/prod/graphs/plugin.py +103 -0
  172. sierra/plugins/prod/graphs/targets.py +63 -0
  173. sierra/plugins/prod/render/__init__.py +18 -0
  174. sierra/plugins/prod/render/cmdline.py +72 -0
  175. sierra/plugins/prod/render/plugin.py +282 -0
  176. sierra/plugins/storage/__init__.py +5 -0
  177. sierra/plugins/storage/arrow/__init__.py +18 -0
  178. sierra/plugins/storage/arrow/plugin.py +38 -0
  179. sierra/plugins/storage/csv/__init__.py +9 -0
  180. sierra/plugins/storage/csv/plugin.py +12 -5
  181. sierra/version.py +3 -2
  182. sierra_research-1.5.0.dist-info/METADATA +238 -0
  183. sierra_research-1.5.0.dist-info/RECORD +186 -0
  184. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info}/WHEEL +1 -2
  185. sierra/core/experiment/xml.py +0 -454
  186. sierra/core/generators/controller_generator_parser.py +0 -34
  187. sierra/core/generators/exp_creator.py +0 -351
  188. sierra/core/generators/exp_generators.py +0 -142
  189. sierra/core/graphs/scatterplot2D.py +0 -109
  190. sierra/core/graphs/stacked_line_graph.py +0 -249
  191. sierra/core/graphs/stacked_surface_graph.py +0 -220
  192. sierra/core/graphs/summary_line_graph.py +0 -369
  193. sierra/core/hpc/cmdline.py +0 -142
  194. sierra/core/models/graphs.py +0 -87
  195. sierra/core/pipeline/stage2/exp_runner.py +0 -286
  196. sierra/core/pipeline/stage3/imagizer.py +0 -149
  197. sierra/core/pipeline/stage3/run_collator.py +0 -317
  198. sierra/core/pipeline/stage3/statistics_calculator.py +0 -478
  199. sierra/core/pipeline/stage4/graph_collator.py +0 -319
  200. sierra/core/pipeline/stage4/inter_exp_graph_generator.py +0 -240
  201. sierra/core/pipeline/stage4/intra_exp_graph_generator.py +0 -317
  202. sierra/core/pipeline/stage4/model_runner.py +0 -168
  203. sierra/core/pipeline/stage4/rendering.py +0 -283
  204. sierra/core/pipeline/stage4/yaml_config_loader.py +0 -103
  205. sierra/core/pipeline/stage5/inter_scenario_comparator.py +0 -328
  206. sierra/core/pipeline/stage5/intra_scenario_comparator.py +0 -989
  207. sierra/core/platform.py +0 -493
  208. sierra/core/plugin_manager.py +0 -369
  209. sierra/core/root_dirpath_generator.py +0 -241
  210. sierra/plugins/hpc/adhoc/plugin.py +0 -125
  211. sierra/plugins/hpc/local/plugin.py +0 -81
  212. sierra/plugins/hpc/pbs/__init__.py +0 -9
  213. sierra/plugins/hpc/pbs/plugin.py +0 -126
  214. sierra/plugins/hpc/slurm/__init__.py +0 -9
  215. sierra/plugins/hpc/slurm/plugin.py +0 -130
  216. sierra/plugins/platform/__init__.py +0 -9
  217. sierra/plugins/platform/argos/__init__.py +0 -9
  218. sierra/plugins/platform/argos/generators/platform_generators.py +0 -383
  219. sierra/plugins/platform/argos/plugin.py +0 -337
  220. sierra/plugins/platform/argos/variables/arena_shape.py +0 -145
  221. sierra/plugins/platform/argos/variables/cameras.py +0 -243
  222. sierra/plugins/platform/argos/variables/constant_density.py +0 -136
  223. sierra/plugins/platform/argos/variables/exp_setup.py +0 -113
  224. sierra/plugins/platform/argos/variables/population_constant_density.py +0 -175
  225. sierra/plugins/platform/argos/variables/population_size.py +0 -102
  226. sierra/plugins/platform/argos/variables/population_variable_density.py +0 -132
  227. sierra/plugins/platform/argos/variables/rendering.py +0 -104
  228. sierra/plugins/platform/ros1gazebo/__init__.py +0 -9
  229. sierra/plugins/platform/ros1gazebo/cmdline.py +0 -213
  230. sierra/plugins/platform/ros1gazebo/generators/platform_generators.py +0 -137
  231. sierra/plugins/platform/ros1gazebo/plugin.py +0 -335
  232. sierra/plugins/platform/ros1gazebo/variables/__init__.py +0 -10
  233. sierra/plugins/platform/ros1gazebo/variables/population_size.py +0 -204
  234. sierra/plugins/platform/ros1robot/__init__.py +0 -9
  235. sierra/plugins/platform/ros1robot/cmdline.py +0 -175
  236. sierra/plugins/platform/ros1robot/generators/platform_generators.py +0 -112
  237. sierra/plugins/platform/ros1robot/plugin.py +0 -373
  238. sierra/plugins/platform/ros1robot/variables/__init__.py +0 -10
  239. sierra/plugins/platform/ros1robot/variables/population_size.py +0 -146
  240. sierra/plugins/robot/__init__.py +0 -9
  241. sierra/plugins/robot/turtlebot3/__init__.py +0 -9
  242. sierra/plugins/robot/turtlebot3/plugin.py +0 -194
  243. sierra_research-1.3.6.data/data/share/man/man1/sierra-cli.1 +0 -2349
  244. sierra_research-1.3.6.data/data/share/man/man7/sierra-examples.7 +0 -488
  245. sierra_research-1.3.6.data/data/share/man/man7/sierra-exec-envs.7 +0 -331
  246. sierra_research-1.3.6.data/data/share/man/man7/sierra-glossary.7 +0 -285
  247. sierra_research-1.3.6.data/data/share/man/man7/sierra-platforms.7 +0 -358
  248. sierra_research-1.3.6.data/data/share/man/man7/sierra-usage.7 +0 -725
  249. sierra_research-1.3.6.data/data/share/man/man7/sierra.7 +0 -78
  250. sierra_research-1.3.6.dist-info/METADATA +0 -500
  251. sierra_research-1.3.6.dist-info/RECORD +0 -133
  252. sierra_research-1.3.6.dist-info/top_level.txt +0 -1
  253. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info}/entry_points.txt +0 -0
  254. {sierra_research-1.3.6.dist-info → sierra_research-1.5.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,66 @@
1
+ #
2
+ # Copyright 2025 John Harwell, All rights reserved.
3
+ #
4
+ # SPDX-License Identifier: MIT
5
+ #
6
+ """Glue for bridging SIERRA <-> prefect for executing local builds."""
7
+
8
+
9
+ # Core packages
10
+ import pathlib
11
+ import subprocess
12
+
13
+
14
+ # 3rd party packages
15
+ import prefect
16
+
17
+ # Project packages
18
+
19
+
20
+ @prefect.task(tags=["sierra-exec-jobs-per-node"])
21
+ def exec_exp_run(
22
+ cmd: list[str],
23
+ scratch_path: pathlib.Path,
24
+ ) -> None:
25
+ """Execute the given command.
26
+
27
+ Arguments:
28
+ cmd: Command to execute a single :term:`Experimental Run`.
29
+
30
+ scratch_path: Where to write stdout/stderr to.
31
+ """
32
+ flow_run_id = prefect.runtime.task_run.id
33
+
34
+ with (
35
+ open(str(scratch_path) + f"_{flow_run_id}_stdout", "w") as stdout,
36
+ open(str(scratch_path) + f"_{flow_run_id}_stderr", "w") as stderr,
37
+ ):
38
+ subprocess.run(
39
+ cmd,
40
+ stdout=stdout,
41
+ stderr=stderr,
42
+ shell=True,
43
+ check=True,
44
+ )
45
+
46
+
47
+ @prefect.flow
48
+ def sierra(
49
+ input_root: pathlib.Path,
50
+ scratch_root: pathlib.Path,
51
+ ) -> None:
52
+ """Generate commands, execute the simulation, and package the data.
53
+
54
+ Arguments:
55
+ input_root: Path to the input directory for the :term:`Experiment`.
56
+
57
+ scratch_root: Path to the scratch directory for the :term:`Experiment`.
58
+ """
59
+ commands = []
60
+ with open(input_root / "commands.txt") as f:
61
+ commands = [line.strip() for line in f.readlines()]
62
+
63
+ scratch_stdouts = [scratch_root / f"_run{i}" for i in range(0, len(commands))]
64
+
65
+ sim_results = exec_exp_run.map(commands, scratch_stdouts)
66
+ sim_results.wait()
@@ -0,0 +1,18 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ Container module for the local execution environment.
6
+
7
+ See :ref:`plugins/execenv/prefectserver/local`.
8
+ """
9
+
10
+ # Core packages
11
+
12
+ # 3rd party packages
13
+
14
+ # Project packages
15
+
16
+
17
+ def sierra_plugin_type() -> str:
18
+ return "pipeline"
@@ -0,0 +1,29 @@
1
+ #
2
+ # Copyright 2025 John Harwell, All rights reserved.
3
+ #
4
+ # SPDX-License Identifier: MIT
5
+ #
6
+
7
+ # Core packages
8
+ import typing as tp
9
+ import argparse
10
+
11
+ # 3rd party packages
12
+
13
+ # Project packages
14
+ from sierra.plugins.execenv import prefectserver
15
+ from sierra.core import types
16
+ from sierra.plugins import PluginCmdline
17
+
18
+
19
+ def build(
20
+ parents: tp.List[argparse.ArgumentParser], stages: tp.List[int]
21
+ ) -> PluginCmdline:
22
+ """
23
+ Get a cmdline parser supporting the ``prefectserver.local`` execution environment.
24
+ """
25
+ return prefectserver.cmdline.PrefectCmdline(parents, stages)
26
+
27
+
28
+ def to_cmdopts(args: argparse.Namespace) -> types.Cmdopts:
29
+ return prefectserver.cmdline.to_cmdopts(args)
@@ -0,0 +1,133 @@
1
+ # Copyright 2025 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Prefect plugin for running SIERRA locally."""
5
+
6
+ # Core packages
7
+ import typing as tp
8
+ import pathlib
9
+
10
+ # 3rd party packages
11
+ import implements
12
+
13
+ # Project packages
14
+ from sierra.core import types
15
+ from sierra.core.experiment import bindings
16
+
17
+
18
+ @implements.implements(bindings.IBatchShellCmdsGenerator)
19
+ class BatchShellCmdsGenerator:
20
+ """
21
+ Generate commands to invoke :term:`Prefect` for local computing.
22
+ """
23
+
24
+ def __init__(self, cmdopts: types.Cmdopts) -> None:
25
+ self.cmdopts = cmdopts
26
+ self.api_url = "http://127.0.0.1:4200/api"
27
+
28
+ def pre_batch_cmds(self) -> tp.List[types.ShellCmdSpec]:
29
+ flow_path = pathlib.Path(__file__).parent / "../flow.py"
30
+ no_prompt = "PREFECT_CLI_PROMPT=false"
31
+
32
+ return [
33
+ # We obviously have to start a local server. We don't have to wait
34
+ # for the command, because it is backgrounded.
35
+ types.ShellCmdSpec(
36
+ cmd=f"env {no_prompt} prefect server start &",
37
+ shell=True,
38
+ wait=False,
39
+ ),
40
+ types.ShellCmdSpec(
41
+ cmd="sleep 5",
42
+ shell=True,
43
+ wait=True,
44
+ ),
45
+ types.ShellCmdSpec(
46
+ cmd="prefect config set PREFECT_API_URL={0}".format(self.api_url),
47
+ shell=True,
48
+ wait=True,
49
+ ),
50
+ # 2025-07-29 [JRH]: This doesn't work (workers ignore it and start #
51
+ # simultaneous sims corresponding to # cores available on the
52
+ # machine), despite all the docs saying it should. I THINK it's
53
+ # because global concurrency limits/task concurrency limits only
54
+ # work with prefect cloud.
55
+ #
56
+ # Concurrency limit has to be set BEFORE starting workers.
57
+ types.ShellCmdSpec(
58
+ cmd="echo 'y' | prefect gcl delete sierra-exec-jobs-per-node || true",
59
+ shell=True,
60
+ wait=True,
61
+ ),
62
+ types.ShellCmdSpec(
63
+ cmd="prefect gcl create sierra-exec-jobs-per-node --limit {0}".format(
64
+ self.cmdopts["exec_jobs_per_node"]
65
+ ),
66
+ shell=True,
67
+ wait=True,
68
+ ),
69
+ types.ShellCmdSpec(
70
+ cmd="prefect work-pool create --type process {0}".format(
71
+ self.cmdopts["work_pool"],
72
+ ),
73
+ shell=True,
74
+ wait=True,
75
+ ),
76
+ # We only need to start 1 worker, since each worker can
77
+ # handle arbitrary concurrency limits. We use a unique name for the pool
78
+ # and queue names to avoid conflicts if we are on a shared server and
79
+ # trying to run prefect locally.
80
+ types.ShellCmdSpec(
81
+ cmd="{0} prefect worker start "
82
+ "--pool={1} "
83
+ "--work-queue={2} &".format(
84
+ no_prompt,
85
+ self.cmdopts["work_pool"],
86
+ self.cmdopts["work_queue"],
87
+ ),
88
+ shell=True,
89
+ wait=False,
90
+ ),
91
+ types.ShellCmdSpec(
92
+ cmd="until prefect work-pool ls | grep -q 'sierra'; do sleep 1; done",
93
+ shell=True,
94
+ wait=True,
95
+ ),
96
+ # Deploy SIERRA's local experiment execution flow. This flow
97
+ # executes all runs in all experiments in the batch in parallel. We
98
+ # use a unique name for the pool name to avoid conflicts if we are
99
+ # on a shared server and trying to run prefect locally.
100
+ types.ShellCmdSpec(
101
+ cmd="{0} prefect deploy {1}:sierra --name sierra/local --pool {2} --work-queue {3}".format(
102
+ no_prompt,
103
+ flow_path,
104
+ self.cmdopts["work_pool"],
105
+ self.cmdopts["work_queue"],
106
+ ),
107
+ shell=True,
108
+ wait=True,
109
+ ),
110
+ ]
111
+
112
+ def exec_batch_cmds(self, exec_opts: types.StrDict) -> tp.List[types.ShellCmdSpec]:
113
+ # The needed flow is already present on the server as a deployment, so
114
+ # we can just run it directly as many times as we want. You don't need
115
+ # to set the queue/pool--it is inferred from the flow you are running.
116
+ flow_cmd = (
117
+ "prefect deployment run sierra/local "
118
+ '--params=\'{{"input_root": "{0}","scratch_root": "{1}"}}\' --watch'
119
+ )
120
+ # Since this execenv is batch level, so are the outputs and scratch
121
+ # dirs.
122
+ flow = flow_cmd.format(exec_opts["batch_root"], exec_opts["batch_scratch_root"])
123
+ spec = types.ShellCmdSpec(cmd=flow, shell=True, wait=True)
124
+
125
+ return [spec]
126
+
127
+ def post_batch_cmds(self) -> tp.List[types.ShellCmdSpec]:
128
+ return [
129
+ types.ShellCmdSpec(cmd="killall prefect || true", shell=True, wait=True),
130
+ ]
131
+
132
+
133
+ __all__ = ["BatchShellCmdsGenerator"]
@@ -1,6 +1,7 @@
1
1
  # Copyright 2021 John Harwell, All rights reserved.
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
+ """Container module for plugins related to real robot execution environments."""
4
5
 
5
6
  # Core packages
6
7
 
@@ -0,0 +1,18 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ Container module for the Turtlebot3 robot execution environment plugin.
6
+
7
+ See :ref:`plugins/execenv/realrobot/turtlebot3`.
8
+ """
9
+
10
+ # Core packages
11
+
12
+ # 3rd party packages
13
+
14
+ # Project packages
15
+
16
+
17
+ def sierra_plugin_type() -> str:
18
+ return "pipeline"
@@ -0,0 +1,204 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ Robot plugin for running SIERRA with a set of Turtlebot3 robots.
6
+
7
+ """
8
+
9
+ # Core packages
10
+ import os
11
+ import logging
12
+ import typing as tp
13
+ import argparse
14
+ import shutil
15
+ import pathlib
16
+
17
+ # 3rd party packages
18
+ import implements
19
+
20
+ # Project packages
21
+ from sierra.core import types, execenv, utils
22
+ from sierra.core.experiment import bindings
23
+
24
+ _logger = logging.getLogger(__name__)
25
+
26
+
27
+ def cmdline_postparse_configure(args: argparse.Namespace) -> argparse.Namespace:
28
+ """
29
+ Configure SIERRA for the turtlebot3 execution environment.
30
+
31
+ May use the following environment variables:
32
+
33
+ - :envvar:`SIERRA_NODEFILE` - If this is not defined ``--nodefile`` must be
34
+ passed.
35
+ """
36
+ if args.nodefile is None:
37
+ assert "SIERRA_NODEFILE" in os.environ, (
38
+ "Non-robot.turtlebot3 environment detected: --nodefile not "
39
+ "passed and 'SIERRA_NODEFILE' not found"
40
+ )
41
+ args.nodefile = os.environ["SIERRA_NODEFILE"]
42
+
43
+ assert utils.path_exists(
44
+ args.nodefile
45
+ ), f"SIERRA_NODEFILE '{args.nodefile}' does not exist"
46
+ _logger.info("Using '%s' as robot hostnames file", args.nodefile)
47
+
48
+ assert not args.engine_vc, "Engine visual capture not supported on robot.turtlebot3"
49
+
50
+ return args
51
+
52
+
53
+ @implements.implements(bindings.IExpShellCmdsGenerator)
54
+ class ExpShellCmdsGenerator:
55
+ """Generate the cmds to invoke GNU Parallel to launch ROS on the turtlebots."""
56
+
57
+ def __init__(self, cmdopts: types.Cmdopts, exp_num: int) -> None:
58
+ self.cmdopts = cmdopts
59
+ self.exp_num = exp_num
60
+
61
+ def pre_exp_cmds(self) -> tp.List[types.ShellCmdSpec]:
62
+ return []
63
+
64
+ def post_exp_cmds(self) -> tp.List[types.ShellCmdSpec]:
65
+ return []
66
+
67
+ def exec_exp_cmds(self, exec_opts: types.StrDict) -> tp.List[types.ShellCmdSpec]:
68
+ jobid = os.getpid()
69
+
70
+ # Even if we are passed --nodelist, we still make our own copy of it, so
71
+ # that the user can safely modify it (if they want to) after running
72
+ # stage 1.
73
+ nodelist = pathlib.Path(
74
+ exec_opts["exp_input_root"], "{0}-nodelist.txt".format(jobid)
75
+ )
76
+
77
+ resume = ""
78
+ # This can't be --resume, because then GNU parallel looks at the results
79
+ # directory, and if there is stuff in it, (apparently) assumes that the
80
+ # job finished...
81
+ if exec_opts["exec_resume"]:
82
+ resume = "--resume-failed"
83
+
84
+ # Make sure there are no duplicate nodes
85
+ unique_nodes = types.ShellCmdSpec(
86
+ cmd="sort -u {0} > {1}".format(exec_opts["nodefile"], nodelist),
87
+ shell=True,
88
+ wait=True,
89
+ )
90
+
91
+ # Make sure GNU parallel uses the right shell, because it seems to
92
+ # defaults to /bin/sh since all cmds are run in a python shell which
93
+ # does not have $SHELL set.
94
+ use_bash = types.ShellCmdSpec(
95
+ cmd="export PARALLEL_SHELL={0}".format(shutil.which("bash")),
96
+ shell=True,
97
+ env=True,
98
+ wait=True,
99
+ )
100
+
101
+ ret = [use_bash, unique_nodes]
102
+
103
+ # 1 GNU parallel command to launch each experimental run, because each
104
+ # run might use all available nodes/robots.
105
+ for i in range(self.cmdopts["n_runs"]):
106
+ ret.extend(self._generate_for_run(i, exec_opts, resume, nodelist))
107
+
108
+ return ret
109
+
110
+ def _generate_for_run(
111
+ self, i: int, exec_opts: dict, resume: str, nodelist: pathlib.Path
112
+ ) -> tp.List[types.ShellCmdSpec]:
113
+ ret = []
114
+ # GNU parallel cmd for robots (slaves)
115
+ robots = (
116
+ "parallel {2} "
117
+ "--jobs {1} "
118
+ "--results {4} "
119
+ "--joblog {3} "
120
+ "--sshloginfile {0} "
121
+ '--workdir {4} < "{5}"'
122
+ )
123
+
124
+ robots_ipath = (
125
+ exec_opts["cmdfile_stem_path"] + f"_run{i}_slave" + exec_opts["cmdfile_ext"]
126
+ )
127
+
128
+ robot_log = pathlib.Path(
129
+ exec_opts["exp_scratch_root"], f"parallel-slaves-run{i}.log"
130
+ )
131
+
132
+ robots = robots.format(
133
+ nodelist,
134
+ exec_opts["n_jobs"],
135
+ resume,
136
+ robot_log,
137
+ exec_opts["exp_scratch_root"],
138
+ robots_ipath,
139
+ )
140
+
141
+ # If no master is spawned, then we need to wait for this GNU
142
+ # parallel cmd. If the master is spawned, then we wait for THAT
143
+ # command; waiting for both results in the master never starting
144
+ # because that cmd is never run.
145
+ robots_spec = types.ShellCmdSpec(
146
+ cmd=robots, shell=True, wait=self.cmdopts["no_master_node"]
147
+ )
148
+ ret.append(robots_spec)
149
+
150
+ if not self.cmdopts["no_master_node"]:
151
+ ros_master = "parallel {3} --results {1} --joblog {0} '--workdir {1} < {2}'"
152
+
153
+ ros_master_ipath = (
154
+ exec_opts["cmdfile_stem_path"]
155
+ + f"_run{i}_master"
156
+ + exec_opts["cmdfile_ext"]
157
+ )
158
+
159
+ master_log = pathlib.Path(
160
+ exec_opts["exp_scratch_root"], f"parallel-master-run{i}.log"
161
+ )
162
+ ros_master = ros_master.format(
163
+ master_log, exec_opts["exp_scratch_root"], ros_master_ipath, resume
164
+ )
165
+
166
+ master_spec = types.ShellCmdSpec(
167
+ cmd=ros_master, shell=True, wait=not self.cmdopts["no_master_node"]
168
+ )
169
+ ret.append(master_spec)
170
+
171
+ wait = 'echo "{0} seconds until launching next run!"; ' "sleep {0}s ;".format(
172
+ self.cmdopts["exec_inter_run_pause"]
173
+ )
174
+ wait_spec = types.ShellCmdSpec(cmd=wait, shell=True, wait=True)
175
+ ret.append(wait_spec)
176
+
177
+ return ret
178
+
179
+
180
+ def execenv_check(cmdopts: types.Cmdopts) -> None:
181
+ """
182
+ Verify execution environment in stage 2 for the :term:`ROS1+Robot` engine.
183
+
184
+ Checks that a valid list of IPs for robots is set/passed, and checks that
185
+ they are reachable.
186
+ """
187
+ nodes = execenv.parse_nodefile(cmdopts["nodefile"])
188
+ for node in nodes:
189
+ if int(node.n_cores) != 1:
190
+ _logger.warning(
191
+ (
192
+ "Nodefile %s, host %s has multiple "
193
+ "cores; turtlebots are single core"
194
+ ),
195
+ cmdopts["nodefile"],
196
+ node.hostname,
197
+ )
198
+ if not cmdopts["skip_online_check"]:
199
+ execenv.check_connectivity(
200
+ cmdopts, node.login, node.hostname, node.port, "turtlebot3"
201
+ )
202
+
203
+
204
+ __all__ = ["cmdline_postparse_configure", "execenv_check", "ExpShellCmdsGenerator"]
@@ -0,0 +1,14 @@
1
+ # Copyright 2024 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ Container module for plugins related to :term:`Experiment` definitions.
6
+
7
+ Driven by ``--expdef``.
8
+ """
9
+
10
+ # Core packages
11
+
12
+ # 3rd party packages
13
+
14
+ # Project packages
@@ -0,0 +1,14 @@
1
+ # Copyright 2024 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Container module for the JSON expdef plugin."""
5
+
6
+ # Core packages
7
+
8
+ # 3rd party packages
9
+
10
+ # Project packages
11
+
12
+
13
+ def sierra_plugin_type() -> str:
14
+ return "pipeline"