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
@@ -2,20 +2,19 @@
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
4
 
5
- """Stage 1 of the experimental pipeline: generating experimental inputs.
6
-
7
- """
5
+ """Stage 1 of the experimental pipeline: generating experimental inputs."""
8
6
 
9
7
  # Core packges
10
8
  import logging
9
+ import time
10
+ import datetime
11
11
 
12
12
  # 3rd party packages
13
13
 
14
14
  # Project packages
15
- from sierra.core.generators.exp_generators import BatchExpDefGenerator
16
- from sierra.core.generators.exp_creator import BatchExpCreator
15
+ from sierra.core.generators.experiment import BatchExpDefGenerator, BatchExpCreator
17
16
  import sierra.core.variables.batch_criteria as bc
18
- from sierra.core import types
17
+ from sierra.core import types, batchroot
19
18
 
20
19
 
21
20
  class PipelineStage1:
@@ -28,15 +27,25 @@ class PipelineStage1:
28
27
 
29
28
  """
30
29
 
31
- def __init__(self,
32
- cmdopts: types.Cmdopts,
33
- controller: str,
34
- criteria: bc.IConcreteBatchCriteria) -> None:
35
- self.generator = BatchExpDefGenerator(controller_name=controller,
36
- scenario_basename=cmdopts['scenario'],
37
- criteria=criteria,
38
- cmdopts=cmdopts)
39
- self.creator = BatchExpCreator(criteria=criteria, cmdopts=cmdopts)
30
+ def __init__(
31
+ self,
32
+ cmdopts: types.Cmdopts,
33
+ pathset: batchroot.PathSet,
34
+ controller: str,
35
+ criteria: bc.XVarBatchCriteria,
36
+ ) -> None:
37
+ self.generator = BatchExpDefGenerator(
38
+ controller_name=controller,
39
+ scenario_basename=cmdopts["scenario"],
40
+ criteria=criteria,
41
+ pathset=pathset,
42
+ cmdopts=cmdopts,
43
+ )
44
+
45
+ self.creator = BatchExpCreator(
46
+ criteria=criteria, cmdopts=cmdopts, pathset=pathset
47
+ )
48
+ self.pathset = pathset
40
49
 
41
50
  self.cmdopts = cmdopts
42
51
  self.criteria = criteria
@@ -55,12 +64,12 @@ class PipelineStage1:
55
64
 
56
65
  #. Generate changes to be applied to to the newly written per-experiment
57
66
  "template" XML file for each experiment which are non-:term:`Batch
58
- Criteria` related (e.g., :term:`Platform` changes). These changes
67
+ Criteria` related (e.g., :term:`Engine` changes). These changes
59
68
  apply to all experiment runs in an experiment, but may differ across
60
- experimental runs (e.g., # cores used for a simulator platform).
69
+ experimental runs (e.g., # cores used for a simulator engine).
61
70
 
62
71
  #. Generate per-experimental run changes for each experimental run in
63
- the experiment, according to platform and :term:`Project`
72
+ the experiment, according to engine and :term:`Project`
64
73
  configuration.
65
74
 
66
75
  #. Write the input files for all experimental runs in all experiments to
@@ -68,17 +77,22 @@ class PipelineStage1:
68
77
 
69
78
  """
70
79
 
71
- self.logger.info("Generating input files for batch experiment in %s...",
72
- self.cmdopts['batch_root'])
73
- self.creator.create(self.generator)
74
-
75
- n_exp_in_batch = len(self.criteria.gen_attr_changelist()) + \
76
- len(self.criteria.gen_tag_addlist())
77
- self.logger.info("%d input files generated in %d experiments.",
78
- self.cmdopts['n_runs'] * n_exp_in_batch,
79
- n_exp_in_batch)
80
+ self.logger.info("Generating batch experiment in %s...", self.pathset.root)
80
81
 
81
-
82
- __api__ = [
83
- 'PipelineStage1'
84
- ]
82
+ start = time.time()
83
+ self.creator.create(self.generator)
84
+ elapsed = int(time.time() - start)
85
+ sec = datetime.timedelta(seconds=elapsed)
86
+ n_exp_in_batch = len(self.criteria.gen_attr_changelist()) + len(
87
+ self.criteria.gen_element_addlist()
88
+ )
89
+ self.logger.info(
90
+ "Generation complete in %s: %d experiments, %d runs per experiment, %d runs total",
91
+ str(sec),
92
+ n_exp_in_batch,
93
+ self.cmdopts["n_runs"],
94
+ self.cmdopts["n_runs"] * n_exp_in_batch,
95
+ )
96
+
97
+
98
+ __all__ = ["PipelineStage1"]
@@ -0,0 +1,10 @@
1
+ # Copyright 2021 John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Container module for stage 2 of the pipeline."""
5
+
6
+ # Core packages
7
+
8
+ # 3rd party packages
9
+
10
+ # Project packages
@@ -2,9 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
4
 
5
- """Stage 2 of the experimental pipeline: running experiments.
6
-
7
- """
5
+ """Stage 2 of the experimental pipeline: running experiments."""
8
6
 
9
7
  # Core packages
10
8
  import time
@@ -15,8 +13,8 @@ import logging
15
13
 
16
14
  # Project packages
17
15
  from sierra.core.variables import batch_criteria as bc
18
- from sierra.core.pipeline.stage2.exp_runner import BatchExpRunner
19
- from sierra.core import types
16
+ from sierra.core.pipeline.stage2.runner import BatchExpRunner
17
+ from sierra.core import types, batchroot
20
18
 
21
19
 
22
20
  class PipelineStage2:
@@ -30,18 +28,17 @@ class PipelineStage2:
30
28
 
31
29
  """
32
30
 
33
- def __init__(self, cmdopts: types.Cmdopts) -> None:
31
+ def __init__(self, cmdopts: types.Cmdopts, pathset: batchroot.PathSet) -> None:
34
32
  self.logger = logging.getLogger(__name__)
35
33
  self.cmdopts = cmdopts
34
+ self.pathset = pathset
36
35
 
37
- def run(self, criteria: bc.BatchCriteria) -> None:
36
+ def run(self, criteria: bc.XVarBatchCriteria) -> None:
38
37
  start = time.time()
39
- BatchExpRunner(self.cmdopts, criteria)()
38
+ BatchExpRunner(self.cmdopts, self.pathset, criteria)()
40
39
  elapsed = int(time.time() - start)
41
40
  sec = datetime.timedelta(seconds=elapsed)
42
41
  self.logger.info("Execution complete in %s", str(sec))
43
42
 
44
43
 
45
- __api__ = [
46
- 'PipelineStage2'
47
- ]
44
+ __all__ = ["PipelineStage2"]
@@ -0,0 +1,401 @@
1
+ # Copyright 2018 London Lowmanstone, John Harwell, All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """Classes for executing experiments via the specified ``--execenv``."""
5
+
6
+ # Core packages
7
+ import os
8
+ import subprocess
9
+ import time
10
+ import sys
11
+ import datetime
12
+ import logging
13
+ import pathlib
14
+ import typing as tp # noqa: F401
15
+
16
+ # 3rd party packages
17
+
18
+ # Project packages
19
+ from sierra.core.variables import batch_criteria as bc
20
+ from sierra.core import types, config, engine, utils, batchroot, execenv
21
+ import sierra.core.plugin as pm
22
+
23
+
24
+ class ExpShell:
25
+ """Launch a shell which persists across experimental runs.
26
+
27
+ Having a persistent shell is necessary so that running pre- and post-run
28
+ shell commands have an effect on the actual commands to execute the run. If
29
+ you set an environment variable before the simulator launches (for example),
30
+ and then the shell containing that change exits, and the simulator launches
31
+ in a new shell, then the configuration has no effect. Thus, a persistent
32
+ shell.
33
+
34
+ """
35
+
36
+ def __init__(self, exec_strict: bool) -> None:
37
+ self.env = os.environ.copy()
38
+ self.logger = logging.getLogger(__name__)
39
+ self.procs = [] # type: tp.List[subprocess.Popen]
40
+ self.exec_strict = exec_strict
41
+
42
+ def run_from_spec(self, spec: types.ShellCmdSpec) -> bool:
43
+ self.logger.trace("Cmd: %s", spec.cmd) # type: ignore
44
+
45
+ # We use a special marker at the end of the cmd's output to know when
46
+ # the environment dump starts.
47
+ if spec.env:
48
+ spec.cmd += " && echo ~~~~ENV_START~~~~ && env"
49
+
50
+ proc = subprocess.Popen(
51
+ spec.cmd,
52
+ shell=spec.shell,
53
+ stdout=subprocess.PIPE,
54
+ stderr=subprocess.PIPE,
55
+ env=self.env,
56
+ )
57
+
58
+ if not spec.wait:
59
+ self.procs.append(proc)
60
+ return True
61
+
62
+ # We use communicate(), not wait() to avoid issues with IO buffers
63
+ # becoming full (e.g., you get deadlocks with wait() regularly).
64
+ stdout_raw, stderr_raw = proc.communicate()
65
+
66
+ # Update the environment for all commands
67
+ if spec.env:
68
+ self._update_env(stdout_raw)
69
+
70
+ # Only show output if the process failed (i.e., did not return 0)
71
+ if proc.returncode != 0:
72
+ self.logger.error("Cmd '%s' failed!", spec.cmd)
73
+ stdout_str = stdout_raw.decode("utf-8")
74
+ stderr_str = stderr_raw.decode("utf-8")
75
+
76
+ if spec.env:
77
+ stdout_str = stdout_str.split("~~~~ENV_START~~~~", maxsplit=1)[0]
78
+ stderr_str = stderr_str.split("~~~~ENV_START~~~~", maxsplit=1)[0]
79
+
80
+ self.logger.error(
81
+ "Cmd stdout (last 10 lines): %s",
82
+ "\n + " "\n".join(stdout_str.split("\n")[-10:]),
83
+ )
84
+ self.logger.error(
85
+ "Cmd stderr (last 10 lines): %s",
86
+ "\n" + "\n".join(stderr_str.split("\n")[-10:]),
87
+ )
88
+ if self.exec_strict:
89
+ raise RuntimeError(
90
+ ("Command failed and strict checking was " "requested")
91
+ )
92
+
93
+ return False
94
+ else:
95
+ return True
96
+
97
+ def _update_env(self, stdout) -> None:
98
+ record = False
99
+ for e in stdout.decode("utf-8").split("\n"):
100
+ if record:
101
+ candidate = e.strip().split("=")
102
+ if len(candidate) != 2:
103
+ continue
104
+
105
+ key = candidate[0]
106
+ value = candidate[1]
107
+
108
+ if key not in self.env or self.env[key] != value:
109
+ self.logger.debug(
110
+ "Update experiment environment: %s=%s", key, value
111
+ )
112
+ self.env[key] = value
113
+ elif e.strip() == "~~~~ENV_START~~~~":
114
+ record = True
115
+
116
+
117
+ class BatchExpRunner:
118
+ """Runs each :term:`Experiment` in :term:`Batch Experiment` in sequence.
119
+
120
+ Attributes:
121
+ batch_exp_root: Absolute path to the root directory for the batch
122
+ experiment inputs (i.e. experiment directories are
123
+ placed in here).
124
+
125
+ batch_stat_root: Absolute path to the root directory for statistics
126
+ which are computed in stage {3,4} (i.e. experiment
127
+ directories are placed in here).
128
+
129
+ batch_stat_exec_root: Absolute path to the root directory for statistics
130
+ which are generated as experiments run during
131
+ stage 2 (e.g., how long each experiment took).
132
+
133
+ cmdopts: Dictionary of parsed cmdline options.
134
+
135
+ criteria: Batch criteria for the experiment.
136
+
137
+ exec_exp_range: The subset of experiments in the batch to run (can be
138
+ None to run all experiments in the batch).
139
+
140
+ """
141
+
142
+ def __init__(
143
+ self,
144
+ cmdopts: types.Cmdopts,
145
+ pathset: batchroot.PathSet,
146
+ criteria: bc.XVarBatchCriteria,
147
+ ) -> None:
148
+ self.cmdopts = cmdopts
149
+ self.criteria = criteria
150
+ self.pathset = pathset
151
+ self.exec_exp_range = self.cmdopts["exp_range"]
152
+
153
+ self.logger = logging.getLogger(__name__)
154
+
155
+ utils.dir_create_checked(self.pathset.stat_exec_root, exist_ok=True)
156
+ utils.dir_create_checked(self.pathset.scratch_root, exist_ok=True)
157
+
158
+ def __call__(self) -> None:
159
+ """
160
+ Execute experiments in the batch according to configuration.
161
+
162
+ """
163
+ self.logger.info(
164
+ "Engine=%s, execenv=%s",
165
+ self.cmdopts["engine"],
166
+ self.cmdopts["execenv"],
167
+ )
168
+
169
+ module = pm.pipeline.get_plugin_module(self.cmdopts["engine"])
170
+
171
+ # Output some useful information before running
172
+ if hasattr(module, "pre_exp_diagnostics"):
173
+ module.pre_exp_diagnostics(self.cmdopts, self.pathset, self.logger)
174
+
175
+ exp_all = [self.pathset.input_root / d for d in self.criteria.gen_exp_names()]
176
+
177
+ exp_to_run = utils.exp_range_calc(
178
+ self.cmdopts["exp_range"],
179
+ self.pathset.input_root,
180
+ self.criteria.gen_exp_names(),
181
+ )
182
+
183
+ # Verify environment is OK before running anything
184
+ self.logger.debug("Checking --engine execution environment")
185
+ engine.execenv_check(self.cmdopts)
186
+
187
+ self.logger.debug("Checking --execenv execution environment")
188
+ execenv.execenv_check(self.cmdopts)
189
+
190
+ # Calculate path for to file for logging execution times
191
+ now = datetime.datetime.now()
192
+ exec_times_fpath = self.pathset.stat_exec_root / now.strftime("%Y-%m-%e-%H:%M")
193
+
194
+ # Start a new process for the experiment shell so pre-run commands have
195
+ # an effect (if they set environment variables, etc.).
196
+ shell = ExpShell(self.cmdopts["exec_strict"])
197
+
198
+ if self.cmdopts["exec_parallelism_paradigm"] is not None:
199
+ self.logger.warning(
200
+ "Overriding engine=%s parallelism paradigm with %s",
201
+ self.cmdopts["engine"],
202
+ self.cmdopts["exec_parallelism_paradigm"],
203
+ )
204
+ parallelism_paradigm = self.cmdopts["exec_parallelism_paradigm"]
205
+ else:
206
+ configurer = engine.ExpConfigurer(self.cmdopts)
207
+ parallelism_paradigm = configurer.parallelism_paradigm()
208
+
209
+ if parallelism_paradigm == "per-batch":
210
+ ParallelRunner(
211
+ self.pathset, self.cmdopts, exec_times_fpath, exp_all, shell
212
+ )(exp_to_run)
213
+
214
+ else:
215
+ # Run the experiment!
216
+ for exp in exp_to_run:
217
+ exp_num = exp_all.index(exp)
218
+
219
+ # Run cmds for engine-specific things to setup the experiment
220
+ # (e.g., start daemons) if needed.
221
+ engine_generator = engine.ExpShellCmdsGenerator(self.cmdopts, exp_num)
222
+ execenv_generator = execenv.ExpShellCmdsGenerator(self.cmdopts, exp_num)
223
+
224
+ for spec in execenv_generator.pre_exp_cmds():
225
+ shell.run_from_spec(spec)
226
+
227
+ for spec in engine_generator.pre_exp_cmds():
228
+ shell.run_from_spec(spec)
229
+
230
+ runner = SequentialRunner(
231
+ self.pathset,
232
+ self.cmdopts,
233
+ exec_times_fpath,
234
+ execenv_generator,
235
+ shell,
236
+ )
237
+ runner(exp.name, exp_num)
238
+
239
+ # Run cmds to cleanup {execenv, engine}-specific things now that
240
+ # the experiment is done (if needed).
241
+ for spec in execenv_generator.post_exp_cmds():
242
+ shell.run_from_spec(spec)
243
+
244
+ for spec in engine_generator.post_exp_cmds():
245
+ shell.run_from_spec(spec)
246
+
247
+
248
+ class SequentialRunner:
249
+ """
250
+ Execute each :term:`Experimental Run` in an :term:`Experiment`.
251
+
252
+ Runs are executed parallel if the selected execution environment supports
253
+ it, otherwise sequentially. This class is meant for executing experiments
254
+ within a batch sequentially.
255
+ """
256
+
257
+ def __init__(
258
+ self,
259
+ pathset: batchroot.PathSet,
260
+ cmdopts: types.Cmdopts,
261
+ exec_times_fpath: pathlib.Path,
262
+ generator: execenv.ExpShellCmdsGenerator,
263
+ shell: ExpShell,
264
+ ) -> None:
265
+
266
+ self.exec_times_fpath = exec_times_fpath
267
+ self.shell = shell
268
+ self.generator = generator
269
+ self.cmdopts = cmdopts
270
+ self.pathset = pathset
271
+ self.logger = logging.getLogger(__name__)
272
+
273
+ def __call__(self, exp_name: str, exp_num: int) -> None:
274
+ """Execute experimental runs for a single experiment."""
275
+ exp_input_root = self.pathset.input_root / exp_name
276
+ exp_scratch_root = self.pathset.scratch_root / exp_name
277
+ self.logger.info(
278
+ "Running exp%s in <batchroot>/%s",
279
+ exp_num,
280
+ exp_input_root.relative_to(self.pathset.root),
281
+ )
282
+ sys.stdout.flush()
283
+
284
+ start = time.time()
285
+
286
+ utils.dir_create_checked(exp_scratch_root, exist_ok=True)
287
+
288
+ # TODO: This restriction should be removed/pushed down to execution
289
+ # environments that require it.
290
+ assert (
291
+ self.cmdopts["exec_jobs_per_node"] is not None
292
+ ), "# parallel jobs can't be None"
293
+
294
+ exec_opts = {
295
+ "exp_input_root": str(exp_input_root),
296
+ "work_dir": exp_input_root,
297
+ "exp_scratch_root": str(exp_scratch_root),
298
+ "cmdfile_stem_path": str(
299
+ exp_input_root / config.kGNUParallel["cmdfile_stem"]
300
+ ),
301
+ "cmdfile_ext": config.kGNUParallel["cmdfile_ext"],
302
+ "exec_resume": self.cmdopts["exec_resume"],
303
+ "n_jobs": self.cmdopts["exec_jobs_per_node"],
304
+ "nodefile": self.cmdopts["nodefile"],
305
+ }
306
+
307
+ for spec in self.generator.exec_exp_cmds(exec_opts):
308
+ if not self.shell.run_from_spec(spec):
309
+ self.logger.error(
310
+ "Check outputs in %s for full details",
311
+ exec_opts["exp_scratch_root"],
312
+ )
313
+
314
+ elapsed = int(time.time() - start)
315
+ sec = datetime.timedelta(seconds=elapsed)
316
+ self.logger.info("Exp%s elapsed time: %s", exp_num, sec)
317
+
318
+ with utils.utf8open(self.exec_times_fpath, "a") as f:
319
+ f.write("exp" + str(exp_num) + ": " + str(sec) + "\n")
320
+
321
+
322
+ class ParallelRunner:
323
+ """
324
+ Execute all runs in all :term:`Experiments <Experiment>` in parallel.
325
+
326
+ This class is meant for executing experiments within a batch concurrently.
327
+ """
328
+
329
+ def __init__(
330
+ self,
331
+ pathset: batchroot.PathSet,
332
+ cmdopts: types.Cmdopts,
333
+ exec_times_fpath: pathlib.Path,
334
+ exp_all: tp.List[pathlib.Path],
335
+ shell: ExpShell,
336
+ ) -> None:
337
+
338
+ self.exec_times_fpath = exec_times_fpath
339
+ self.shell = shell
340
+ self.exp_all = exp_all
341
+ self.cmdopts = cmdopts
342
+ self.pathset = pathset
343
+ self.logger = logging.getLogger(__name__)
344
+
345
+ def __call__(self, exp_to_run: tp.List[pathlib.Path]) -> None:
346
+ """Execute all experimental runs for all experiments."""
347
+
348
+ self.logger.info("Kicking off all experiments in <batchroot>")
349
+ sys.stdout.flush()
350
+
351
+ start = time.time()
352
+
353
+ exp_scratch_root = self.pathset.scratch_root
354
+ utils.dir_create_checked(exp_scratch_root, exist_ok=True)
355
+ exec_opts = {
356
+ "batch_root": str(self.pathset.root),
357
+ "work_dir": str(self.pathset.root),
358
+ "batch_scratch_root": str(self.pathset.scratch_root),
359
+ "cmdfile_stem_path": str(
360
+ self.pathset.root / config.kGNUParallel["cmdfile_stem"]
361
+ ),
362
+ "cmdfile_ext": config.kGNUParallel["cmdfile_ext"],
363
+ "exec_resume": self.cmdopts["exec_resume"],
364
+ "n_jobs": self.cmdopts["exec_jobs_per_node"],
365
+ }
366
+
367
+ # Run cmds for engine-specific things to setup the experiment
368
+ # (e.g., start daemons) if needed.
369
+ engine_generator = engine.BatchShellCmdsGenerator(self.cmdopts)
370
+ execenv_generator = execenv.BatchShellCmdsGenerator(self.cmdopts)
371
+
372
+ for spec in execenv_generator.pre_batch_cmds():
373
+ self.shell.run_from_spec(spec)
374
+
375
+ for spec in engine_generator.pre_batch_cmds():
376
+ self.shell.run_from_spec(spec)
377
+
378
+ for spec in execenv_generator.exec_batch_cmds(exec_opts):
379
+ if not self.shell.run_from_spec(spec):
380
+ self.logger.error(
381
+ "Check outputs in %s for full details",
382
+ exec_opts["batch_scratch_root"],
383
+ )
384
+
385
+ # Run cmds to cleanup {execenv, engine}-specific things now that
386
+ # the experiment is done (if needed).
387
+ for spec in execenv_generator.post_batch_cmds():
388
+ self.shell.run_from_spec(spec)
389
+
390
+ for spec in engine_generator.post_batch_cmds():
391
+ self.shell.run_from_spec(spec)
392
+
393
+ elapsed = int(time.time() - start)
394
+ sec = datetime.timedelta(seconds=elapsed)
395
+ self.logger.info("Elapsed time: %s", sec)
396
+
397
+ with utils.utf8open(self.exec_times_fpath, "a") as f:
398
+ f.write(": " + str(sec) + "\n")
399
+
400
+
401
+ __all__ = ["BatchExpRunner", "SequentialRunner", "ParallelRunner", "ExpShell"]
@@ -0,0 +1,12 @@
1
+ #
2
+ # Copyright 2025 John Harwell, All rights reserved.
3
+ #
4
+ # SPDX-License Identifier: MIT
5
+ #
6
+ """Container module for stage 3 of the pipeline."""
7
+
8
+ # Core packages
9
+
10
+ # 3rd party packages
11
+
12
+ # Project packages