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,404 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ Provides engine-specific callbacks for the :term:`ROS1+Gazebo` engine.
6
+ """
7
+ # Core packages
8
+ import argparse
9
+ import logging
10
+ import os
11
+ import re
12
+ import typing as tp
13
+ import sys
14
+ import pathlib
15
+ import psutil
16
+
17
+ # 3rd party packages
18
+ import packaging.version
19
+ import implements
20
+
21
+ # Project packages
22
+ from sierra.core import config, ros1, types, batchroot, execenv
23
+ from sierra.core.experiment import bindings, definition
24
+ import sierra.core.variables.batch_criteria as bc
25
+
26
+ _logger = logging.getLogger("ros1gazebo.plugin")
27
+
28
+
29
+ @implements.implements(bindings.IExpRunShellCmdsGenerator)
30
+ class ExpRunShellCmdsGenerator:
31
+ def __init__(
32
+ self,
33
+ cmdopts: types.Cmdopts,
34
+ criteria: bc.XVarBatchCriteria,
35
+ n_agents: int,
36
+ exp_num: int,
37
+ ) -> None:
38
+ self.cmdopts = cmdopts
39
+ self.gazebo_port = -1
40
+ self.roscore_port = -1
41
+
42
+ def pre_run_cmds(
43
+ self, host: str, input_fpath: pathlib.Path, run_num: int
44
+ ) -> tp.List[types.ShellCmdSpec]:
45
+ if host == "master":
46
+ return []
47
+
48
+ # First, the cmd to start roscore. We need to be on a unique port so
49
+ # that multiple ROS instances corresponding to multiple Gazebo
50
+ # instances with the same topic names are considered distinct/not
51
+ # accessible between instances of Gazebo.
52
+ self.roscore_port = config.kROS["port_base"] + run_num * 2
53
+
54
+ # roscore will run on each slave node used during stage 2, so we have to
55
+ # use 'localhost' for binding.
56
+ roscore_uri = types.ShellCmdSpec(
57
+ cmd=f"export ROS_MASTER_URI=http://localhost:{self.roscore_port};",
58
+ shell=True,
59
+ wait=True,
60
+ env=True,
61
+ )
62
+ # ROS/Gazebo don't provide options to not print stuff, so we have to use
63
+ # the nuclear option.
64
+ roscore_cmd = f"roscore -p {self.roscore_port} "
65
+ if self.cmdopts["exec_devnull"]:
66
+ roscore_cmd += "> /dev/null 2>&1 "
67
+
68
+ roscore_cmd += "& "
69
+
70
+ # Each experiment gets their own roscore. Because each roscore has a
71
+ # different port, this prevents any robots from pre-emptively starting
72
+ # the next experiment before the rest of the robots have finished the
73
+ # current one.
74
+ roscore_process = types.ShellCmdSpec(
75
+ cmd=roscore_cmd,
76
+ shell=True,
77
+ wait=False,
78
+ )
79
+
80
+ # Second, the command to give Gazebo a unique port on the host during
81
+ # stage 2. We need to be on a unique port so that multiple Gazebo
82
+ # instances can be run in parallel.
83
+ self.gazebo_port = config.kROS["port_base"] + run_num * 2 + 1
84
+
85
+ # 2021/12/13: You can't use HTTPS for some reason or gazebo won't
86
+ # start...
87
+ gazebo_uri = types.ShellCmdSpec(
88
+ cmd=f"export GAZEBO_MASTER_URI=http://localhost:{self.gazebo_port};",
89
+ shell=True,
90
+ env=True,
91
+ wait=True,
92
+ )
93
+
94
+ return [roscore_uri, roscore_process, gazebo_uri]
95
+
96
+ def exec_run_cmds(
97
+ self, host: str, input_fpath: pathlib.Path, run_num: int
98
+ ) -> tp.List[types.ShellCmdSpec]:
99
+ if host == "master":
100
+ return []
101
+
102
+ # For ROS+gazebo, we have two files per experimental run:
103
+ #
104
+ # - One for the stuff that is run on the ROS master (can be
105
+ # nothing/empty file).
106
+ #
107
+ # - One for the stuff that describes what to run on each robot/how many
108
+ # robots to spawn.
109
+ #
110
+ # We COULD do it all in a single file, but in order to share base code
111
+ # with the ROS+robot engine, we need to do it this way.
112
+ #
113
+ # 2022/02/28: I don't use the -u argument here to set ROS_MASTER_URI,
114
+ # because ROS works well enough when only running on the localhost, in
115
+ # terms of respecting whatever the envvar is set to.
116
+ master = str(input_fpath) + "_master" + config.kROS["launch_file_ext"]
117
+ robots = str(input_fpath) + "_robots" + config.kROS["launch_file_ext"]
118
+
119
+ cmd = "{0} --wait {1} {2} ".format(config.kROS["launch_cmd"], master, robots)
120
+
121
+ # ROS/Gazebo don't provide options to not print stuff, so we have to use
122
+ # the nuclear option.
123
+ if self.cmdopts["exec_devnull"]:
124
+ cmd += "> /dev/null 2>&1"
125
+
126
+ cmd += ";"
127
+ return [types.ShellCmdSpec(cmd=cmd, shell=True, wait=True)]
128
+
129
+ def post_run_cmds(
130
+ self, host: str, run_output_root: pathlib.Path
131
+ ) -> tp.List[types.ShellCmdSpec]:
132
+ return []
133
+
134
+
135
+ @implements.implements(bindings.IExpShellCmdsGenerator)
136
+ class ExpShellCmdsGenerator:
137
+ def __init__(self, cmdopts: types.Cmdopts, exp_num: int) -> None:
138
+ self.cmdopts = cmdopts
139
+ self.exp_num = exp_num
140
+
141
+ def pre_exp_cmds(self) -> tp.List[types.ShellCmdSpec]:
142
+ # 2025-09-11 [JRH]: This was a NASTY bug which got triggered when
143
+ # running SIERRA in a venv. ROS is installed system-wide, but mixing
144
+ # venv+system packages via a --system venv caused all sorts of problems
145
+ # because the venv versions were often mixing newer with older packages
146
+ # at load time. So, we have to allow the venv interpreter to find the
147
+ # system-wide packages AFTER everything is loaded by modifying the
148
+ # PYTHONPATH for the sub-shell that SIERRA runs everything in.
149
+ #
150
+ # Hopefully this is better in ROS2.
151
+
152
+ return [
153
+ types.ShellCmdSpec(
154
+ cmd="export PYTHONPATH=$PYTHONPATH:/usr/lib/python3/dist-packages/",
155
+ shell=True,
156
+ wait=True,
157
+ env=True,
158
+ ),
159
+ ]
160
+
161
+ def exec_exp_cmds(self, exec_opts: types.StrDict) -> tp.List[types.ShellCmdSpec]:
162
+ return []
163
+
164
+ def post_exp_cmds(self) -> tp.List[types.ShellCmdSpec]:
165
+ # Cleanup roscore and gazebo processes which are still active because
166
+ # they don't know how to clean up after themselves.
167
+ #
168
+ # This is OK to do even when we have multiple Gazebo+ROS instances on
169
+ # the same machine, because we only run these commands after an
170
+ # experiment finishes, so there isn't anything currently running we
171
+ # might accidentally kill.
172
+ #
173
+ # Can't use killall, because that returns non-zero if things
174
+ # are cleaned up nicely.
175
+ return [
176
+ types.ShellCmdSpec(
177
+ cmd="if pgrep gzserver; then pkill gzserver; fi;", shell=True, wait=True
178
+ ),
179
+ types.ShellCmdSpec(
180
+ cmd="if pgrep rosmaster; then pkill rosmaster; fi;",
181
+ shell=True,
182
+ wait=True,
183
+ ),
184
+ types.ShellCmdSpec(
185
+ cmd="if pgrep roscore; then pkill roscore; fi;", shell=True, wait=True
186
+ ),
187
+ types.ShellCmdSpec(
188
+ cmd="if pgrep rosout; then pkill rosout; fi;", shell=True, wait=True
189
+ ),
190
+ ]
191
+
192
+
193
+ @implements.implements(bindings.IExpConfigurer)
194
+ class ExpConfigurer:
195
+ def __init__(self, cmdopts: types.Cmdopts) -> None:
196
+ self.cmdopts = cmdopts
197
+
198
+ def for_exp_run(
199
+ self, exp_input_root: pathlib.Path, run_output_root: pathlib.Path
200
+ ) -> None:
201
+ pass
202
+
203
+ def for_exp(self, exp_input_root: pathlib.Path) -> None:
204
+ pass
205
+
206
+ def parallelism_paradigm(self) -> str:
207
+ return "per-exp"
208
+
209
+
210
+ def cmdline_postparse_configure(
211
+ env: str, args: argparse.Namespace
212
+ ) -> argparse.Namespace:
213
+ """
214
+ Configure cmdline args after parsing for the :term:`ROS1+Gazebo` engine.
215
+
216
+ This sets arguments appropriately depending on what HPC environment is
217
+ selected with ``--execenv``:
218
+
219
+ - hpc.local
220
+
221
+ - hpc.adhoc
222
+
223
+ - hpc.slurm
224
+
225
+ - hpc.pbs
226
+ """
227
+ # No configuration needed for stages 3-5
228
+ if not any(stage in args.pipeline for stage in [1, 2]):
229
+ return args
230
+
231
+ if env == "hpc.local":
232
+ return _configure_hpc_local(args)
233
+ elif env == "hpc.adhoc":
234
+ return _configure_hpc_adhoc(args)
235
+ elif env == "hpc.slurm":
236
+ return _configure_hpc_slurm(args)
237
+ elif env == "hpc.pbs":
238
+ return _configure_hpc_pbs(args)
239
+
240
+ raise RuntimeError(f"'{env}' unsupported on ROS1+Gazebo")
241
+
242
+
243
+ def _configure_hpc_pbs(args: argparse.Namespace) -> argparse.Namespace:
244
+ # For now, nothing to do. If more stuff with physics engine
245
+ # configuration is implemented, this may change.
246
+ _logger.debug(
247
+ "Allocated %s physics threads/run, %s parallel runs/node",
248
+ args.physics_n_threads,
249
+ args.exec_jobs_per_node,
250
+ )
251
+ return args
252
+
253
+
254
+ def _configure_hpc_slurm(args: argparse.Namespace) -> argparse.Namespace:
255
+ # We rely on the user to request their job intelligently so that
256
+ # SLURM_TASKS_PER_NODE is appropriate.
257
+ if args.exec_jobs_per_node is None:
258
+ res = re.search(r"^[^\(]+", os.environ["SLURM_TASKS_PER_NODE"])
259
+ assert (
260
+ res is not None
261
+ ), "Unexpected format in SLURM_TASKS_PER_NODE: '{0}'".format(
262
+ os.environ["SLURM_TASKS_PER_NODE"]
263
+ )
264
+ args.exec_jobs_per_node = int(res.group(0))
265
+
266
+ _logger.debug(
267
+ "Allocated %s physics threads/run, %s parallel runs/node",
268
+ args.physics_n_threads,
269
+ args.exec_jobs_per_node,
270
+ )
271
+ return args
272
+
273
+
274
+ def _configure_hpc_adhoc(args: argparse.Namespace) -> argparse.Namespace:
275
+ nodes = execenv.parse_nodefile(args.nodefile)
276
+ ppn = sys.maxsize
277
+ for node in nodes:
278
+ ppn = min(ppn, node.n_cores)
279
+
280
+ if args.exec_jobs_per_node is None:
281
+ args.exec_jobs_per_node = int(float(args.n_runs) / len(nodes))
282
+
283
+ _logger.debug(
284
+ "Allocated %s physics threads/run, %s parallel runs/node",
285
+ args.physics_n_threads,
286
+ args.exec_jobs_per_node,
287
+ )
288
+
289
+ return args
290
+
291
+
292
+ def _configure_hpc_local(args: argparse.Namespace) -> argparse.Namespace:
293
+ # For now. If more physics engine configuration is added, this will
294
+ # change.
295
+ ppn_per_run_req = 1
296
+
297
+ if args.exec_jobs_per_node is None:
298
+ parallel_jobs = int(psutil.cpu_count() / float(ppn_per_run_req))
299
+
300
+ if parallel_jobs == 0:
301
+ _logger.warning(
302
+ (
303
+ "Local machine has %s logical cores, but "
304
+ "%s threads/run requested; "
305
+ "allocating anyway"
306
+ ),
307
+ psutil.cpu_count(),
308
+ ppn_per_run_req,
309
+ )
310
+ parallel_jobs = 1
311
+
312
+ # Make sure we don't oversubscribe cores--each simulation needs at
313
+ # least 1 core.
314
+ args.exec_jobs_per_node = min(args.n_runs, parallel_jobs)
315
+
316
+ _logger.debug(
317
+ "Allocated %s physics threads/run, %s parallel runs/node",
318
+ args.physics_n_threads,
319
+ args.exec_jobs_per_node,
320
+ )
321
+ return args
322
+
323
+
324
+ def execenv_check(cmdopts: types.Cmdopts) -> None:
325
+ """
326
+ Verify execution environment in stage 2 for :term:`ROS1+Gazebo`.
327
+
328
+ Check for:
329
+
330
+ - :envvar:`ROS_VERSION` is ROS1.
331
+
332
+ - :envvar:`ROS_DISTRO` is {kinetic/noetic}.
333
+
334
+ - :program:`gazebo` can be found and the version is supported.
335
+ """
336
+ keys = ["ROS_DISTRO", "ROS_VERSION"]
337
+
338
+ for k in keys:
339
+ assert k in os.environ, f"Non-ROS+Gazebo environment detected: '{k}' not found"
340
+
341
+ # Check ROS distro
342
+ assert os.environ["ROS_DISTRO"] in [
343
+ "kinetic",
344
+ "noetic",
345
+ ], "SIERRA only supports ROS1 kinetic,noetic"
346
+
347
+ # Check ROS version
348
+ assert (
349
+ os.environ["ROS_VERSION"] == "1"
350
+ ), "Wrong ROS version: this plugin is for ROS1"
351
+
352
+ # Check we can find Gazebo
353
+ version = execenv.check_for_simulator(
354
+ cmdopts["engine"], cmdopts["execenv"], config.kGazebo["launch_cmd"]
355
+ )
356
+
357
+ # Check Gazebo version
358
+ stdout = version.stdout.decode("utf-8")
359
+ stderr = version.stderr.decode("utf-8")
360
+ res = re.search(r"[0-9]+.[0-9]+.[0-9]+", stdout)
361
+ assert (
362
+ res is not None
363
+ ), f"Gazebo version not in std: have stdout='{stdout}',stderr='{stderr}'"
364
+
365
+ version = packaging.version.parse(res.group(0))
366
+ min_version = packaging.version.parse(config.kGazebo["min_version"])
367
+
368
+ assert (
369
+ version >= min_version
370
+ ), f"Gazebo version {version} < min required {min_version}"
371
+
372
+
373
+ def population_size_from_pickle(
374
+ adds_def: tp.Union[definition.AttrChangeSet, definition.ElementAddList],
375
+ main_config: types.YAMLDict,
376
+ cmdopts: types.Cmdopts,
377
+ ) -> int:
378
+ return ros1.callbacks.population_size_from_pickle(adds_def, main_config, cmdopts)
379
+
380
+
381
+ def population_size_from_def(
382
+ exp_def: definition.BaseExpDef, main_config: types.YAMLDict, cmdopts: types.Cmdopts
383
+ ) -> int:
384
+ return ros1.callbacks.population_size_from_def(exp_def, main_config, cmdopts)
385
+
386
+
387
+ def agent_prefix_extract(main_config: types.YAMLDict, cmdopts: types.Cmdopts) -> str:
388
+ return ros1.callbacks.robot_prefix_extract(main_config, cmdopts)
389
+
390
+
391
+ def pre_exp_diagnostics(
392
+ cmdopts: types.Cmdopts, pathset: batchroot.PathSet, logger: logging.Logger
393
+ ) -> None:
394
+ s = "batch_exp_root='%s',runs/exp=%s,threads/job=%s,n_jobs=%s"
395
+ logger.info(
396
+ s,
397
+ pathset.root,
398
+ cmdopts["n_runs"],
399
+ cmdopts["physics_n_threads"],
400
+ cmdopts["exec_jobs_per_node"],
401
+ )
402
+
403
+
404
+ __all__ = ["cmdline_postparse_configure", "execenv_check"]
@@ -0,0 +1,15 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ Variables module for the :term:`ROS1+Gazebo` engine.
6
+
7
+ See :ref:`plugins/engine/ros1gazebo`.
8
+ """
9
+
10
+ # Core packages
11
+
12
+ # 3rd party packages
13
+
14
+ # Project packages
15
+ from sierra.core.ros1.variables import exp_setup as exp_setup
@@ -0,0 +1,214 @@
1
+ # Copyright 2020 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Classes for the population size batch criteria.
5
+
6
+ See :ref:`plugins/engine/ros1gazebo/bc/population-size` for usage
7
+ documentation.
8
+
9
+ """
10
+
11
+ # Core packages
12
+ import typing as tp
13
+ import random
14
+ import logging
15
+ import pathlib
16
+
17
+ # 3rd party packages
18
+ import implements
19
+
20
+ # Project packages
21
+ from sierra.core.variables import batch_criteria as bc
22
+ from sierra.core.experiment import definition
23
+ from sierra.core import types, utils
24
+ from sierra.core.vector import Vector3D
25
+ from sierra.core.variables import population_size
26
+ from sierra.core.graphs import bcbridge
27
+
28
+
29
+ @implements.implements(bcbridge.IGraphable)
30
+ @implements.implements(bc.IQueryableBatchCriteria)
31
+ class PopulationSize(population_size.PopulationSize):
32
+ """A univariate range of system sizes used to define batch experiments.
33
+
34
+ This class is a base class which should (almost) never be used on its
35
+ own. Instead, the ``factory()`` function should be used to dynamically
36
+ create derived classes expressing the user's desired size distribution.
37
+
38
+ Note: Usage of this class assumes homogeneous systems.
39
+
40
+ Attributes:
41
+ size_list: List of integer system sizes defining the range of the
42
+ variable for the batch experiment.
43
+
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ cli_arg: str,
49
+ main_config: types.YAMLDict,
50
+ batch_input_root: pathlib.Path,
51
+ robot: str,
52
+ sizes: tp.List[int],
53
+ positions: tp.List[Vector3D],
54
+ ) -> None:
55
+ population_size.PopulationSize.__init__(
56
+ self, cli_arg, main_config, batch_input_root
57
+ )
58
+ self.sizes = sizes
59
+ self.robot = robot
60
+ self.positions = positions
61
+ self.logger = logging.getLogger(__name__)
62
+ if len(positions) < self.sizes[-1]:
63
+ self.logger.warning(
64
+ "# possible positions < # robots: %s < %s",
65
+ len(positions),
66
+ self.sizes[-1],
67
+ )
68
+ self.element_adds = [] # type: tp.List[definition.ElementAddList]
69
+
70
+ def gen_element_addlist(self) -> tp.List[definition.ElementAddList]:
71
+ """Generate XML modifications to set system sizes."""
72
+ if not self.element_adds:
73
+ robot_config = self.main_config["ros"]["robots"][self.robot]
74
+ prefix = robot_config["prefix"]
75
+ model_base = robot_config["model"]
76
+ model_variant = robot_config.get("model_variant", "")
77
+
78
+ if model_variant != "":
79
+ model = f"{model_base}_{model_variant}"
80
+ else:
81
+ model = model_base
82
+
83
+ desc_cmd = f"$(find xacro)/xacro $(find {model_base}_description)/urdf/{model}.urdf.xacro"
84
+ for s in self.sizes:
85
+ exp_adds = definition.ElementAddList()
86
+ pos_i = random.randint(0, len(self.positions) - 1)
87
+
88
+ exp_adds.append(definition.ElementAdd(".", "master", {}, True))
89
+ exp_adds.append(
90
+ definition.ElementAdd("./master", "group", {"ns": "sierra"}, False)
91
+ )
92
+ exp_adds.append(
93
+ definition.ElementAdd(
94
+ "./master/group/[@ns='sierra']",
95
+ "param",
96
+ {"name": "experiment/n_agents", "value": str(s)},
97
+ False,
98
+ )
99
+ )
100
+
101
+ for i in range(0, s):
102
+
103
+ ns = f"{prefix}{i}"
104
+ pos = self.positions[pos_i]
105
+ pos_i = (pos_i + 1) % len(self.positions)
106
+ spawn_cmd_args = f"-urdf -model {model}_{ns} -x {pos.x} -y {pos.y} -z {pos.z} -param robot_description"
107
+
108
+ exp_adds.append(
109
+ definition.ElementAdd("./robot", "group", {"ns": ns}, True)
110
+ )
111
+
112
+ exp_adds.append(
113
+ definition.ElementAdd(
114
+ f"./robot/group/[@ns='{ns}']",
115
+ "param",
116
+ {"name": "tf_prefix", "value": ns},
117
+ True,
118
+ )
119
+ )
120
+
121
+ # These two tag adds are OK to use because:
122
+ #
123
+ # - All robots in Gazebo are created using spawn_model
124
+ # initially.
125
+ #
126
+ # - All robots in Gazebo will provide a robot description
127
+ # .urdf.xacro per ROS naming conventions
128
+ exp_adds.append(
129
+ definition.ElementAdd(
130
+ f"./robot/group/[@ns='{ns}']",
131
+ "param",
132
+ {"name": "robot_description", "command": desc_cmd},
133
+ True,
134
+ )
135
+ )
136
+
137
+ exp_adds.append(
138
+ definition.ElementAdd(
139
+ f"./robot/group/[@ns='{ns}']",
140
+ "node",
141
+ {
142
+ "name": "spawn_urdf",
143
+ "pkg": "gazebo_ros",
144
+ "type": "spawn_model",
145
+ "args": spawn_cmd_args,
146
+ },
147
+ True,
148
+ )
149
+ )
150
+
151
+ self.element_adds.append(exp_adds)
152
+
153
+ return self.element_adds
154
+
155
+ def graph_info(
156
+ self,
157
+ cmdopts: types.Cmdopts,
158
+ batch_output_root: tp.Optional[pathlib.Path] = None,
159
+ exp_names: tp.Optional[tp.List[str]] = None,
160
+ ) -> bcbridge.GraphInfo:
161
+ info = bcbridge.GraphInfo(
162
+ cmdopts,
163
+ batch_output_root,
164
+ exp_names if exp_names else self.gen_exp_names(),
165
+ )
166
+
167
+ info.xlabel = super().graph_xlabel(info.cmdopts)
168
+ info.xticklabels = super().graph_xticklabels(
169
+ info.cmdopts, info.batch_output_root, info.exp_names
170
+ )
171
+ info.xticks = super().graph_xticks(
172
+ info.cmdopts, info.batch_output_root, info.exp_names
173
+ )
174
+ return info
175
+
176
+ def n_agents(self, exp_num: int) -> int:
177
+ return int(len(self.element_adds[exp_num]) / len(self.element_adds[0]))
178
+
179
+
180
+ def factory(
181
+ cli_arg: str,
182
+ main_config: types.YAMLDict,
183
+ cmdopts: types.Cmdopts,
184
+ batch_input_root: pathlib.Path,
185
+ **kwargs,
186
+ ) -> PopulationSize:
187
+ """Create a :class:`PopulationSize` derived class from the cmdline definition."""
188
+ max_sizes = population_size.parse(cli_arg)
189
+
190
+ if cmdopts["robot_positions"]:
191
+ positions = [
192
+ Vector3D.from_str(s, astype=float) for s in cmdopts["robot_positions"]
193
+ ]
194
+ else:
195
+ # Get the dimensions of the effective arena from the scenario so we can
196
+ # place robots randomly within it.
197
+ kw = utils.gen_scenario_spec(cmdopts, **kwargs)
198
+
199
+ xs = random.choices(range(0, kw["arena_x"]), k=max_sizes[-1]) # type: ignore
200
+ ys = random.choices(range(0, kw["arena_y"]), k=max_sizes[-1]) # type: ignore
201
+ zs = random.choices(range(0, kw["arena_z"]), k=max_sizes[-1]) # type: ignore
202
+ positions = [Vector3D(x, y, z) for x, y, z in zip(xs, ys, zs)]
203
+
204
+ return PopulationSize(
205
+ cli_arg,
206
+ main_config,
207
+ batch_input_root,
208
+ cmdopts["robot"],
209
+ max_sizes,
210
+ positions,
211
+ )
212
+
213
+
214
+ __all__ = ["PopulationSize"]
@@ -0,0 +1,18 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ Container module for the :term:`ROS1+Robot` engine.
6
+
7
+ See :ref:`plugins/engine/ros1robot`.
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"