pyvale 2025.7.1__cp311-cp311-macosx_14_0_arm64.whl → 2025.8.1__cp311-cp311-macosx_14_0_arm64.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.

Potentially problematic release.


This version of pyvale might be problematic. Click here for more details.

Files changed (186) hide show
  1. pyvale/__init__.py +12 -92
  2. pyvale/blender/__init__.py +23 -0
  3. pyvale/{pyvaleexceptions.py → blender/blenderexceptions.py} +0 -3
  4. pyvale/{blenderlightdata.py → blender/blenderlightdata.py} +3 -3
  5. pyvale/{blendermaterialdata.py → blender/blendermaterialdata.py} +1 -1
  6. pyvale/{blenderrenderdata.py → blender/blenderrenderdata.py} +5 -3
  7. pyvale/{blenderscene.py → blender/blenderscene.py} +33 -30
  8. pyvale/{blendertools.py → blender/blendertools.py} +14 -10
  9. pyvale/dataset/__init__.py +7 -0
  10. pyvale/dataset/dataset.py +443 -0
  11. pyvale/dic/__init__.py +20 -0
  12. pyvale/dic/cpp/dicfourier.cpp +36 -4
  13. pyvale/dic/cpp/dicinterpolator.cpp +56 -1
  14. pyvale/dic/cpp/dicmain.cpp +24 -19
  15. pyvale/dic/cpp/dicoptimizer.cpp +6 -1
  16. pyvale/dic/cpp/dicscanmethod.cpp +32 -32
  17. pyvale/dic/cpp/dicsignalhandler.cpp +16 -0
  18. pyvale/dic/cpp/dicstrain.cpp +7 -3
  19. pyvale/dic/cpp/dicutil.cpp +79 -23
  20. pyvale/{dic2d.py → dic/dic2d.py} +51 -29
  21. pyvale/dic/dic2dconv.py +6 -0
  22. pyvale/dic/dic2dcpp.cpython-311-darwin.so +0 -0
  23. pyvale/{dicchecks.py → dic/dicchecks.py} +28 -16
  24. pyvale/dic/dicdataimport.py +370 -0
  25. pyvale/{dicregionofinterest.py → dic/dicregionofinterest.py} +169 -12
  26. pyvale/{dicresults.py → dic/dicresults.py} +4 -1
  27. pyvale/{dicstrain.py → dic/dicstrain.py} +9 -9
  28. pyvale/examples/basics/{ex1_1_basicscalars_therm2d.py → ex1a_basicscalars_therm2d.py} +12 -9
  29. pyvale/examples/basics/{ex1_2_sensormodel_therm2d.py → ex1b_sensormodel_therm2d.py} +17 -14
  30. pyvale/examples/basics/{ex1_3_customsens_therm3d.py → ex1c_customsens_therm3d.py} +27 -24
  31. pyvale/examples/basics/{ex1_4_basicerrors_therm3d.py → ex1d_basicerrors_therm3d.py} +32 -29
  32. pyvale/examples/basics/{ex1_5_fielderrs_therm3d.py → ex1e_fielderrs_therm3d.py} +19 -15
  33. pyvale/examples/basics/{ex1_6_caliberrs_therm2d.py → ex1f_caliberrs_therm2d.py} +20 -16
  34. pyvale/examples/basics/{ex1_7_spatavg_therm2d.py → ex1g_spatavg_therm2d.py} +19 -16
  35. pyvale/examples/basics/{ex2_1_basicvectors_disp2d.py → ex2a_basicvectors_disp2d.py} +13 -10
  36. pyvale/examples/basics/{ex2_2_vectorsens_disp2d.py → ex2b_vectorsens_disp2d.py} +19 -15
  37. pyvale/examples/basics/{ex2_3_sensangle_disp2d.py → ex2c_sensangle_disp2d.py} +21 -18
  38. pyvale/examples/basics/{ex2_4_chainfielderrs_disp2d.py → ex2d_chainfielderrs_disp2d.py} +31 -29
  39. pyvale/examples/basics/{ex2_5_vectorfields3d_disp3d.py → ex2e_vectorfields3d_disp3d.py} +21 -18
  40. pyvale/examples/basics/{ex3_1_basictensors_strain2d.py → ex3a_basictensors_strain2d.py} +16 -14
  41. pyvale/examples/basics/{ex3_2_tensorsens2d_strain2d.py → ex3b_tensorsens2d_strain2d.py} +17 -14
  42. pyvale/examples/basics/{ex3_3_tensorsens3d_strain3d.py → ex3c_tensorsens3d_strain3d.py} +25 -22
  43. pyvale/examples/basics/{ex4_1_expsim2d_thermmech2d.py → ex4a_expsim2d_thermmech2d.py} +17 -14
  44. pyvale/examples/basics/{ex4_2_expsim3d_thermmech3d.py → ex4b_expsim3d_thermmech3d.py} +37 -34
  45. pyvale/examples/basics/ex5_nomesh.py +24 -0
  46. pyvale/examples/dic/ex1_2_blenderdeformed.py +174 -0
  47. pyvale/examples/dic/ex1_region_of_interest.py +6 -3
  48. pyvale/examples/dic/ex2_plate_with_hole.py +21 -18
  49. pyvale/examples/dic/ex3_plate_with_hole_strain.py +8 -6
  50. pyvale/examples/dic/ex4_dic_blender.py +17 -15
  51. pyvale/examples/dic/ex5_dic_challenge.py +19 -14
  52. pyvale/examples/genanalyticdata/ex1_1_scalarvisualisation.py +16 -10
  53. pyvale/examples/genanalyticdata/ex1_2_scalarcasebuild.py +3 -3
  54. pyvale/examples/genanalyticdata/ex2_1_analyticsensors.py +29 -23
  55. pyvale/examples/genanalyticdata/ex2_2_analyticsensors_nomesh.py +67 -0
  56. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +12 -9
  57. pyvale/examples/mooseherder/ex0_create_moose_config.py +65 -0
  58. pyvale/examples/mooseherder/ex1a_modify_moose_input.py +71 -0
  59. pyvale/examples/mooseherder/ex1b_modify_gmsh_input.py +69 -0
  60. pyvale/examples/mooseherder/ex2a_run_moose_once.py +80 -0
  61. pyvale/examples/mooseherder/ex2b_run_gmsh_once.py +64 -0
  62. pyvale/examples/mooseherder/ex2c_run_both_once.py +114 -0
  63. pyvale/examples/mooseherder/ex3_run_moose_seq_para.py +157 -0
  64. pyvale/examples/mooseherder/ex4_run_gmsh-moose_seq_para.py +176 -0
  65. pyvale/examples/mooseherder/ex5_run_moose_paramulti.py +136 -0
  66. pyvale/examples/mooseherder/ex6_read_moose_exodus.py +163 -0
  67. pyvale/examples/mooseherder/ex7a_read_moose_herd_results.py +153 -0
  68. pyvale/examples/mooseherder/ex7b_read_multi_herd_results.py +116 -0
  69. pyvale/examples/mooseherder/ex7c_read_multi_gmshmoose_results.py +127 -0
  70. pyvale/examples/mooseherder/ex7d_readconfig_multi_gmshmoose_results.py +143 -0
  71. pyvale/examples/mooseherder/ex8_read_existing_sweep_output.py +72 -0
  72. pyvale/examples/renderblender/ex1_1_blenderscene.py +24 -20
  73. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +22 -18
  74. pyvale/examples/renderblender/ex2_1_stereoscene.py +36 -29
  75. pyvale/examples/renderblender/ex2_2_stereodeformed.py +26 -20
  76. pyvale/examples/renderblender/ex3_1_blendercalibration.py +24 -17
  77. pyvale/examples/renderrasterisation/ex_rastenp.py +14 -12
  78. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +14 -15
  79. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +13 -11
  80. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +13 -11
  81. pyvale/mooseherder/__init__.py +32 -0
  82. pyvale/mooseherder/directorymanager.py +416 -0
  83. pyvale/mooseherder/exodusreader.py +763 -0
  84. pyvale/mooseherder/gmshrunner.py +163 -0
  85. pyvale/mooseherder/inputmodifier.py +236 -0
  86. pyvale/mooseherder/mooseconfig.py +226 -0
  87. pyvale/mooseherder/mooseherd.py +527 -0
  88. pyvale/mooseherder/mooserunner.py +303 -0
  89. pyvale/mooseherder/outputreader.py +22 -0
  90. pyvale/mooseherder/simdata.py +92 -0
  91. pyvale/mooseherder/simrunner.py +31 -0
  92. pyvale/mooseherder/sweepreader.py +356 -0
  93. pyvale/mooseherder/sweeptools.py +76 -0
  94. pyvale/sensorsim/__init__.py +82 -0
  95. pyvale/{camera.py → sensorsim/camera.py} +7 -7
  96. pyvale/{camerasensor.py → sensorsim/camerasensor.py} +7 -7
  97. pyvale/{camerastereo.py → sensorsim/camerastereo.py} +2 -2
  98. pyvale/{cameratools.py → sensorsim/cameratools.py} +4 -4
  99. pyvale/{cython → sensorsim/cython}/rastercyth.c +596 -596
  100. pyvale/{cython → sensorsim/cython}/rastercyth.cpython-311-darwin.so +0 -0
  101. pyvale/{cython → sensorsim/cython}/rastercyth.py +16 -17
  102. pyvale/{errorcalculator.py → sensorsim/errorcalculator.py} +1 -1
  103. pyvale/{errorintegrator.py → sensorsim/errorintegrator.py} +2 -2
  104. pyvale/{errorrand.py → sensorsim/errorrand.py} +4 -4
  105. pyvale/{errorsyscalib.py → sensorsim/errorsyscalib.py} +2 -2
  106. pyvale/{errorsysdep.py → sensorsim/errorsysdep.py} +2 -2
  107. pyvale/{errorsysfield.py → sensorsim/errorsysfield.py} +8 -8
  108. pyvale/{errorsysindep.py → sensorsim/errorsysindep.py} +3 -3
  109. pyvale/sensorsim/exceptions.py +8 -0
  110. pyvale/{experimentsimulator.py → sensorsim/experimentsimulator.py} +23 -3
  111. pyvale/{field.py → sensorsim/field.py} +1 -1
  112. pyvale/{fieldconverter.py → sensorsim/fieldconverter.py} +72 -19
  113. pyvale/sensorsim/fieldinterp.py +37 -0
  114. pyvale/sensorsim/fieldinterpmesh.py +124 -0
  115. pyvale/sensorsim/fieldinterppoints.py +55 -0
  116. pyvale/{fieldsampler.py → sensorsim/fieldsampler.py} +4 -4
  117. pyvale/{fieldscalar.py → sensorsim/fieldscalar.py} +28 -24
  118. pyvale/{fieldtensor.py → sensorsim/fieldtensor.py} +33 -31
  119. pyvale/{fieldvector.py → sensorsim/fieldvector.py} +33 -31
  120. pyvale/{imagedef2d.py → sensorsim/imagedef2d.py} +9 -5
  121. pyvale/{integratorfactory.py → sensorsim/integratorfactory.py} +6 -6
  122. pyvale/{integratorquadrature.py → sensorsim/integratorquadrature.py} +3 -3
  123. pyvale/{integratorrectangle.py → sensorsim/integratorrectangle.py} +3 -3
  124. pyvale/{integratorspatial.py → sensorsim/integratorspatial.py} +1 -1
  125. pyvale/{rastercy.py → sensorsim/rastercy.py} +5 -5
  126. pyvale/{rasternp.py → sensorsim/rasternp.py} +9 -9
  127. pyvale/{rasteropts.py → sensorsim/rasteropts.py} +1 -1
  128. pyvale/{renderer.py → sensorsim/renderer.py} +1 -1
  129. pyvale/{rendermesh.py → sensorsim/rendermesh.py} +5 -5
  130. pyvale/{renderscene.py → sensorsim/renderscene.py} +2 -2
  131. pyvale/{sensorarray.py → sensorsim/sensorarray.py} +1 -1
  132. pyvale/{sensorarrayfactory.py → sensorsim/sensorarrayfactory.py} +12 -12
  133. pyvale/{sensorarraypoint.py → sensorsim/sensorarraypoint.py} +10 -8
  134. pyvale/{sensordata.py → sensorsim/sensordata.py} +1 -1
  135. pyvale/{sensortools.py → sensorsim/sensortools.py} +2 -20
  136. pyvale/sensorsim/simtools.py +174 -0
  137. pyvale/{visualexpplotter.py → sensorsim/visualexpplotter.py} +3 -3
  138. pyvale/{visualimages.py → sensorsim/visualimages.py} +2 -2
  139. pyvale/{visualsimanimator.py → sensorsim/visualsimanimator.py} +4 -4
  140. pyvale/{visualsimplotter.py → sensorsim/visualsimplotter.py} +5 -5
  141. pyvale/{visualsimsensors.py → sensorsim/visualsimsensors.py} +12 -12
  142. pyvale/{visualtools.py → sensorsim/visualtools.py} +1 -1
  143. pyvale/{visualtraceplotter.py → sensorsim/visualtraceplotter.py} +2 -2
  144. pyvale/simcases/case17.geo +3 -0
  145. pyvale/simcases/case17.i +4 -4
  146. pyvale/simcases/run_1case.py +1 -9
  147. pyvale/simcases/run_all_cases.py +1 -1
  148. pyvale/simcases/run_build_case.py +1 -1
  149. pyvale/simcases/run_example_cases.py +1 -1
  150. pyvale/verif/__init__.py +12 -0
  151. pyvale/{analyticsimdatafactory.py → verif/analyticsimdatafactory.py} +2 -2
  152. pyvale/{analyticsimdatagenerator.py → verif/analyticsimdatagenerator.py} +2 -2
  153. pyvale/verif/psens.py +125 -0
  154. pyvale/verif/psensconst.py +18 -0
  155. pyvale/verif/psensmech.py +227 -0
  156. pyvale/verif/psensmultiphys.py +187 -0
  157. pyvale/verif/psensscalar.py +347 -0
  158. pyvale/verif/psenstensor.py +123 -0
  159. pyvale/verif/psensvector.py +116 -0
  160. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/METADATA +6 -7
  161. pyvale-2025.8.1.dist-info/RECORD +262 -0
  162. pyvale/dataset.py +0 -415
  163. pyvale/dic2dcpp.cpython-311-darwin.so +0 -0
  164. pyvale/dicdataimport.py +0 -247
  165. pyvale/simtools.py +0 -67
  166. pyvale-2025.7.1.dist-info/RECORD +0 -213
  167. /pyvale/{blendercalibrationdata.py → blender/blendercalibrationdata.py} +0 -0
  168. /pyvale/{dicspecklegenerator.py → dic/dicspecklegenerator.py} +0 -0
  169. /pyvale/{dicspecklequality.py → dic/dicspecklequality.py} +0 -0
  170. /pyvale/{dicstrainresults.py → dic/dicstrainresults.py} +0 -0
  171. /pyvale/{cameradata.py → sensorsim/cameradata.py} +0 -0
  172. /pyvale/{cameradata2d.py → sensorsim/cameradata2d.py} +0 -0
  173. /pyvale/{errordriftcalc.py → sensorsim/errordriftcalc.py} +0 -0
  174. /pyvale/{fieldtransform.py → sensorsim/fieldtransform.py} +0 -0
  175. /pyvale/{generatorsrandom.py → sensorsim/generatorsrandom.py} +0 -0
  176. /pyvale/{imagetools.py → sensorsim/imagetools.py} +0 -0
  177. /pyvale/{integratortype.py → sensorsim/integratortype.py} +0 -0
  178. /pyvale/{output.py → sensorsim/output.py} +0 -0
  179. /pyvale/{raster.py → sensorsim/raster.py} +0 -0
  180. /pyvale/{sensordescriptor.py → sensorsim/sensordescriptor.py} +0 -0
  181. /pyvale/{visualimagedef.py → sensorsim/visualimagedef.py} +0 -0
  182. /pyvale/{visualopts.py → sensorsim/visualopts.py} +0 -0
  183. /pyvale/{analyticmeshgen.py → verif/analyticmeshgen.py} +0 -0
  184. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/WHEEL +0 -0
  185. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/licenses/LICENSE +0 -0
  186. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,416 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ import os
8
+ import shutil
9
+ import json
10
+ from pathlib import Path
11
+
12
+ class DirectoryManager:
13
+ """Manages directories for running simulations in parallel with the
14
+ mooseherd.
15
+
16
+ Parameters
17
+ ----------
18
+
19
+ Returns
20
+ -------
21
+
22
+ """
23
+ def __init__(self, n_dirs: int = 1) -> None:
24
+ """__init__
25
+
26
+ Args:
27
+ n_dirs (int, optional): number of directories to be created.
28
+ Defaults to 1.
29
+ """
30
+ self._n_dirs = n_dirs
31
+ self._sub_dir = 'sim-workdir'
32
+ self._base_dir = Path().cwd()
33
+ self._run_dirs = self._set_run_dirs()
34
+ self._output_paths = list([])
35
+ self._output_key_tag = 'output-key'
36
+ self._sweep_var_tag = 'sweep-vars'
37
+
38
+
39
+ def _set_run_dirs(self) -> list[Path]:
40
+ """_set_run_dirs: helper function that populates the list of
41
+ directories that will be created by the manager. Uses the base directory
42
+ at the start of the path and then creates numbered sub-directory paths
43
+ based on the sub directory name and number of directories specified.
44
+
45
+ Parameters
46
+ ----------
47
+
48
+ Returns
49
+ -------
50
+ list[Path]
51
+
52
+
53
+ """
54
+ run_dirs = list([])
55
+ for nn in range(self._n_dirs): # type: ignore
56
+ run_dirs.append(self._base_dir / (self._sub_dir + '-' + str(nn+1)))
57
+
58
+ return run_dirs
59
+
60
+
61
+ def set_sub_dir_name(self, sub_dir_name: str) -> None:
62
+ """set_sub_dir_name: used to set the string used at the start of the
63
+ created sub-directores. default on creation is 'sim-workdir'. Populates
64
+ the list of run directories using the new sub directory name.
65
+
66
+ Parameters
67
+ ----------
68
+ sub_dir_name : str
69
+ string to be used to name the created
70
+ sub-directories within the base directory.
71
+ sub_dir_name: str :
72
+
73
+
74
+ Returns
75
+ -------
76
+
77
+ """
78
+ self._sub_dir = sub_dir_name
79
+ self._run_dirs = self._set_run_dirs()
80
+
81
+
82
+ def set_base_dir(self, base_dir: Path, clear_old_dirs = False) -> None:
83
+ """set_base_dir: sets the base directory to create sub-directors for
84
+ running the simulations. The base directory must exist.
85
+
86
+ Parameters
87
+ ----------
88
+ base_dir : Path
89
+ directory in which the new working directories will
90
+ be created.
91
+ clear_old_dirs : bool
92
+ deletes previous directories in
93
+ the base directory and their contents if they exist. Defaults
94
+ to False.
95
+
96
+ Returns
97
+ -------
98
+
99
+ Raises
100
+ ------
101
+ FileExistsError
102
+ the selected base directory does no exist.
103
+
104
+ """
105
+ if not base_dir.is_dir():
106
+ raise FileExistsError("Specified base directory does not exist.")
107
+
108
+ if clear_old_dirs:
109
+ self.clear_dirs()
110
+
111
+ self._base_dir = base_dir
112
+ self._run_dirs = self._set_run_dirs()
113
+
114
+
115
+ def get_output_key_tag(self) -> str:
116
+ """get_output_key_tag: returns the string used to name the output
117
+ key files that map the simulation outputs to the various directories
118
+ that are being managed.
119
+
120
+ Parameters
121
+ ----------
122
+
123
+ Returns
124
+ -------
125
+ str
126
+ common string used to name the output key json files.
127
+
128
+ """
129
+ return self._output_key_tag
130
+
131
+
132
+ def get_sweep_var_tag(self) -> str:
133
+ """get_sweep_var_tag: returns ths string used to name the sweep
134
+ variable json file that contains a copy of the dictionary the user
135
+ provided as part of the parameter sweep.
136
+
137
+ Parameters
138
+ ----------
139
+
140
+ Returns
141
+ -------
142
+ str
143
+ common string used to name the sweep variable json files.
144
+
145
+ """
146
+ return self._sweep_var_tag
147
+
148
+
149
+ def create_dirs(self) -> list[Path]:
150
+ """Creates the specified number of directories based on
151
+ the sub_dir name within the base directory.
152
+
153
+ Parameters
154
+ ----------
155
+
156
+ Returns
157
+ -------
158
+ list[Path]
159
+ list of paths to the directories to create.
160
+
161
+ """
162
+ for rr in self._run_dirs:
163
+ if not rr.is_dir():
164
+ rr.mkdir()
165
+
166
+ return self._run_dirs
167
+
168
+
169
+ def clear_dirs(self) -> None:
170
+ """Deletes all working directories in the base directory
171
+ that have the corresponding sub-directory name and their contents.
172
+ """
173
+ all_dirs = os.listdir(self._base_dir)
174
+ for dd in all_dirs:
175
+ if os.path.isdir(self._base_dir / dd):
176
+ if self._sub_dir in dd:
177
+ shutil.rmtree(self._base_dir / dd)
178
+
179
+ def reset_dirs(self) -> list[Path]:
180
+ """Helper function that first clears the working directories if they
181
+ exist and then creates new working directories ready for a simulation
182
+ sweep.
183
+
184
+ Returns
185
+ -------
186
+ list[Path]
187
+ List of paths to the created working directories
188
+ """
189
+ self.clear_dirs()
190
+ return self.create_dirs()
191
+
192
+ def get_all_run_dirs(self) -> list[Path]:
193
+ """Returns the list of paths to all the directories
194
+ that can be used to run simulations.
195
+
196
+ Parameters
197
+ ----------
198
+
199
+ Returns
200
+ -------
201
+ list[Path]
202
+ paths to all created directories.
203
+
204
+ """
205
+ return self._run_dirs
206
+
207
+
208
+ def get_run_dir(self, dir_num: int) -> Path:
209
+ """get_run_dir: returns the path to the run directory based on the
210
+ input directory number. The directory number can be greater than the
211
+ number of managed directories in the run_dirs list in this case the
212
+ directory number will wrap and point at an existing run directory
213
+ allowing multiple simulations to be run in the same directory.
214
+
215
+ Parameters
216
+ ----------
217
+ dir_num : int
218
+ number of the directory path to be retrieved. Can be
219
+ greater than the specified number of directories and will wrap
220
+ appropriately
221
+
222
+
223
+ Returns
224
+ -------
225
+ Path
226
+ path to the directory
227
+
228
+ """
229
+ if dir_num < 0:
230
+ dir_num = 0
231
+ elif dir_num >= self._n_dirs:
232
+ dir_num = dir_num % self._n_dirs
233
+
234
+ return self._run_dirs[dir_num]
235
+
236
+
237
+ def set_output_paths(self, output_paths: list[list[Path | None]]) -> None:
238
+ """set_output_paths: sets the list of lists to the simulation output
239
+ based on herder input.
240
+
241
+ Parameters
242
+ ----------
243
+ output_paths : list[list[Path | None]]
244
+ paths to all outputs from the
245
+ variable sweep. Outer list is the variable combination run
246
+ inner list is based on the order the simulations were called.
247
+
248
+
249
+ Returns
250
+ -------
251
+
252
+ """
253
+ self._output_paths = output_paths
254
+
255
+
256
+ def get_output_paths(self) -> list[list[Path | None]]:
257
+ """get_output_paths: returns the list of lists to the simulation output
258
+ files.
259
+
260
+ Parameters
261
+ ----------
262
+
263
+ Returns
264
+ -------
265
+ list[list[Path | None]]
266
+ paths to all outputs from the variable sweep.
267
+ Outer list is the variable combination run inner list is based
268
+ on the order the simulations were called.
269
+
270
+ """
271
+ return self._output_paths
272
+
273
+
274
+ def get_output_key_file(self, sweep_iter: int = 1) -> Path:
275
+ """get_output_key_file: gets the path to the output key file created
276
+ during the variable sweep mapping directories to given combinations
277
+ of variables that were run.
278
+
279
+ Parameters
280
+ ----------
281
+ sweep_iter : int
282
+ number corresponding to the sweep iteration to
283
+ retrieve. Defaults to 1.
284
+
285
+ Returns
286
+ -------
287
+ Path
288
+ path to the output key file that maps output paths to the
289
+ combinations of variables in the sweep.
290
+
291
+ """
292
+ return self._run_dirs[0] / f'{self._output_key_tag}-{sweep_iter:d}.json'
293
+
294
+
295
+ def write_output_key(self, sweep_iter: int) -> None:
296
+ """write_output_key: converts the output paths to strings and saves
297
+ them in json format.
298
+
299
+ Parameters
300
+ ----------
301
+ sweep_iter : int
302
+ number corresponing to the sweep iteration to
303
+ write. The sweep iteration is used to number the output key
304
+ files.
305
+
306
+ Returns
307
+ -------
308
+
309
+ """
310
+ str_output = output_paths_to_str(self._output_paths)
311
+
312
+ with open(self.get_output_key_file(sweep_iter), "w", encoding='utf-8') as okf:
313
+ json.dump(str_output, okf, indent=4)
314
+
315
+
316
+ def get_sweep_var_file(self, sweep_iter: int = 1) -> Path:
317
+ """get_sweep_var_file: path to the json file which contains the
318
+ dictionary of variables that were analysed at the given sweep iteration
319
+
320
+ Parameters
321
+ ----------
322
+ sweep_iter : int
323
+ Sweep iteration (call number to herd
324
+ run_para). Defaults to 1.
325
+
326
+ Returns
327
+ -------
328
+ Path
329
+ path to the json sweep variable file.
330
+
331
+ """
332
+ return self._run_dirs[0] / f'{self._sweep_var_tag}-{sweep_iter:d}.json'
333
+
334
+
335
+ def write_sweep_vars(self,
336
+ sweep_vars: list[list[dict | None]],
337
+ sweep_iter: int = 1) -> None:
338
+ """write_sweep_vars: writes the sweep variable dictionary to a json
339
+ file to log the variables used for each simulation.
340
+
341
+ Parameters
342
+ ----------
343
+ sweep_vars : list[list[dict | None]]
344
+ sweep variables as passed to the herd to run.
345
+ sweep_iter : int
346
+ iteration number for number of calls to the
347
+ herd. Defaults to 1.
348
+
349
+ Returns
350
+ -------
351
+
352
+ """
353
+ with open(self.get_sweep_var_file(sweep_iter), "w", encoding='utf-8') as okf:
354
+ json.dump(sweep_vars, okf, indent=4)
355
+
356
+
357
+
358
+ def output_paths_to_str(output_files: list[list[Path | None]]
359
+ ) -> list[list[str | None]]:
360
+ """output_paths_to_str: helper function for converting the output paths
361
+ to strings to allow them to be saved as json.
362
+
363
+ Parameters
364
+ ----------
365
+ output_files : list[list[Path | None]]
366
+
367
+
368
+ Returns
369
+ -------
370
+ list[list[str]]
371
+ as input with Path converted to str
372
+
373
+ """
374
+ str_output = list([])
375
+ for sim_iter in output_files:
376
+ iter_output = list([])
377
+ for output_path in sim_iter:
378
+ if output_path is None:
379
+ iter_output.append(None)
380
+ else:
381
+ iter_output.append(str(output_path))
382
+
383
+ str_output.append(iter_output)
384
+
385
+ return str_output
386
+
387
+
388
+ def output_str_to_paths(output_files: list[list[str | None]]
389
+ ) -> list[list[Path | None]]:
390
+ """output_str_to_paths: helper function to convert strings read from output
391
+ key json to paths.
392
+
393
+ Parameters
394
+ ----------
395
+ output_files : list[list[str]]
396
+ output file list of path strings as in the output key file.
397
+
398
+ Returns
399
+ -------
400
+ list[list[Path]]
401
+ as input with str converted to Path.
402
+
403
+ """
404
+ str_output = list([])
405
+
406
+ for sim_iter in output_files:
407
+ iter_output = list([])
408
+ for output_path in sim_iter:
409
+ if output_path is None:
410
+ iter_output.append(None)
411
+ else:
412
+ iter_output.append(Path(output_path))
413
+
414
+ str_output.append(iter_output)
415
+
416
+ return str_output