pyvale 2025.7.1__cp311-cp311-musllinux_1_2_i686.whl → 2025.8.1__cp311-cp311-musllinux_1_2_i686.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 (185) 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/{dic2dcpp.cpython-311-i386-linux-musl.so → dic/dic2dcpp.cpython-311-i386-linux-musl.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-i386-linux-musl.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 +263 -0
  162. pyvale/dataset.py +0 -415
  163. pyvale/dicdataimport.py +0 -247
  164. pyvale/simtools.py +0 -67
  165. pyvale-2025.7.1.dist-info/RECORD +0 -214
  166. /pyvale/{blendercalibrationdata.py → blender/blendercalibrationdata.py} +0 -0
  167. /pyvale/{dicspecklegenerator.py → dic/dicspecklegenerator.py} +0 -0
  168. /pyvale/{dicspecklequality.py → dic/dicspecklequality.py} +0 -0
  169. /pyvale/{dicstrainresults.py → dic/dicstrainresults.py} +0 -0
  170. /pyvale/{cameradata.py → sensorsim/cameradata.py} +0 -0
  171. /pyvale/{cameradata2d.py → sensorsim/cameradata2d.py} +0 -0
  172. /pyvale/{errordriftcalc.py → sensorsim/errordriftcalc.py} +0 -0
  173. /pyvale/{fieldtransform.py → sensorsim/fieldtransform.py} +0 -0
  174. /pyvale/{generatorsrandom.py → sensorsim/generatorsrandom.py} +0 -0
  175. /pyvale/{imagetools.py → sensorsim/imagetools.py} +0 -0
  176. /pyvale/{integratortype.py → sensorsim/integratortype.py} +0 -0
  177. /pyvale/{output.py → sensorsim/output.py} +0 -0
  178. /pyvale/{raster.py → sensorsim/raster.py} +0 -0
  179. /pyvale/{sensordescriptor.py → sensorsim/sensordescriptor.py} +0 -0
  180. /pyvale/{visualimagedef.py → sensorsim/visualimagedef.py} +0 -0
  181. /pyvale/{visualopts.py → sensorsim/visualopts.py} +0 -0
  182. /pyvale/{analyticmeshgen.py → verif/analyticmeshgen.py} +0 -0
  183. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/WHEEL +0 -0
  184. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/licenses/LICENSE +0 -0
  185. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,763 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ # ===============================================================================
8
+ # EXODUS READER
9
+ # Used to read output *.e from MOOSE simulations.
10
+
11
+ # There are several different cases that lead to different MOOSE output formats.
12
+
13
+ # 1) Outputs can have 2 or 3 spatial dimensions for nodal DOFs
14
+ # (e.g. disp_x, disp_y and possibly disp_z)
15
+ # 2) Element output may or may not be present (e.g. stress/strain)
16
+ # 2.1) Element outputs might appear as nodal variables if
17
+ # material_output_order = FIRST or greater
18
+ # 2.2) Element output is split by block if material_output_order = CONSTANT
19
+ # 4) Sub-domains may or may not be present but see 2.1 above for exception
20
+
21
+ # Authors: Lloyd Fletcher, Rory Spencer
22
+ # ===============================================================================
23
+
24
+ from pathlib import Path
25
+ import netCDF4 as nc
26
+ import numpy as np
27
+ from pyvale.mooseherder.simdata import SimData, SimReadConfig
28
+ from pyvale.mooseherder.outputreader import OutputReader
29
+
30
+
31
+ class ExodusReader(OutputReader):
32
+ """Class to read exodus files output by MOOSE using the netCDF package.
33
+ This class handles extracting the data from the exodus file and creates
34
+ a SimData object with the required data. Most used cases are covered with
35
+ by creating an ExodusReader and then calling either read_sim_data() or
36
+ read_all_sim_data() specified at the bottom of the class.
37
+
38
+ Parameters
39
+ ----------
40
+
41
+ Returns
42
+ -------
43
+
44
+ """
45
+ def __init__(self, output_file: Path) -> None:
46
+ """__init__: Construct class by reading the exodus file using the
47
+ netCDF package. The exodus file must exist.
48
+
49
+ Args:
50
+ output_file (Path): path to the exodus file to read
51
+
52
+ Raises:
53
+ FileNotFoundError: the specified exodus file does not exist
54
+ """
55
+
56
+ if not output_file.is_file() and output_file.suffix != '.e':
57
+ raise FileNotFoundError('Exodus file not found at specified path')
58
+
59
+ self._exodus_path = output_file
60
+ self._data = nc.Dataset(str(self._exodus_path))
61
+
62
+
63
+ def get_names(self, key: str | None) -> np.ndarray | None:
64
+ """get_names: Extract a list of variable names from the dataset. Useful
65
+ for getting node/element/sideset/global variables names.
66
+
67
+ Parameters
68
+ ----------
69
+ key : str | None
70
+ string key used to extract a list of names from
71
+ the dataset e.g. 'node_var_names'. If key is None returns None.
72
+
73
+
74
+ Returns
75
+ -------
76
+ np.ndarray | None
77
+ numpy array of strings representing the names
78
+ that correspond to the variables in the dataset. Returns None
79
+ if the specified key does not exist in the dataset.
80
+
81
+ """
82
+ if key not in self._data.variables or key is None:
83
+ return None
84
+
85
+ return nc.chartostring(np.array(self._data.variables[key]))
86
+
87
+
88
+ def get_var(self, key: str, time_inds: np.ndarray | None = None
89
+ ) -> np.ndarray:
90
+ """get_var: Extract a numeric variable from the dataset.
91
+
92
+ Parameters
93
+ ----------
94
+ key : str
95
+ key corresponding to the variable in the dataset. e.g.
96
+ 'time_whole'
97
+ key: str :
98
+
99
+ time_inds: np.ndarray | None :
100
+ (Default value = None)
101
+
102
+ Returns
103
+ -------
104
+ np.ndarray
105
+ numpy numeric array containing the variable data.
106
+
107
+ """
108
+ if key not in self._data.variables:
109
+ return np.array([])
110
+
111
+ data = np.array(self._data.variables[key]).T
112
+
113
+ if time_inds is None:
114
+ return data
115
+
116
+ return data[:,time_inds]
117
+
118
+
119
+ def get_key(self,
120
+ name: str,
121
+ all_names: np.ndarray,
122
+ key_tag: str) -> str | None:
123
+ """get_key: builds the key required to extract a given variable from
124
+ the exodus dataset.
125
+
126
+ Parameters
127
+ ----------
128
+ all_names : np.ndarray
129
+ all possible name keys extracted using the
130
+ get names function.
131
+ name : str
132
+ the specific name key that the user wants to extract
133
+ key_tag : str
134
+ the string tag that is prepended to get the variable
135
+ from the dataset.
136
+ name: str :
137
+
138
+ all_names: np.ndarray :
139
+
140
+ key_tag: str :
141
+
142
+
143
+ Returns
144
+ -------
145
+ str | None
146
+ the string key in the dataset to get the variable
147
+
148
+ """
149
+ inds = np.where(all_names == name)[0]
150
+ if inds.shape[0] == 0:
151
+ return None
152
+
153
+ key = f'{key_tag}{inds[0]+1:d}'
154
+ return key
155
+
156
+
157
+ def get_connectivity_names(self) -> np.ndarray:
158
+ """get_connectivity_names: gets the connectivity names in the exodus
159
+ dataset. These are of the form 'connect1', 'connect2' etc.
160
+
161
+ Parameters
162
+ ----------
163
+
164
+ Returns
165
+ -------
166
+ np.ndarray
167
+ array of element connectivity keys as strings of the
168
+ form connectX where X is an integer of 1 or greater e.g.
169
+ connect1.
170
+
171
+ """
172
+ names = np.array([])
173
+ for bb in range(self.get_num_elem_blocks()):
174
+ key = f'connect{bb+1:d}'
175
+ if key in self._data.variables:
176
+ names = np.append(names,key)
177
+
178
+ return names
179
+
180
+
181
+ def get_connectivity(self) -> dict[str,np.ndarray]:
182
+ """get_connectivity: returns the connectivity table as a dictionary
183
+ keyed with the name 'connectX' and the table itseld as numpy array.
184
+
185
+ Parameters
186
+ ----------
187
+
188
+ Returns
189
+ -------
190
+ dict[str,np.ndarray]
191
+ dictionary containing the element
192
+ connectivity tables based on keys related to the subdomain e.g.
193
+ key 'connect1' returns the element connectivity table for
194
+ subdomain 1. The table has dimensions N by n_e where N is the
195
+ total number of nodes in the subdomain and n_e is the number
196
+ of nodes per element.
197
+
198
+ """
199
+ connect = dict({})
200
+ for key in self.get_connectivity_names():
201
+ connect[key] = self.get_var(key)
202
+
203
+ return connect
204
+
205
+
206
+ def get_sideset_names(self) -> np.ndarray | None:
207
+ """get_sideset_names: returns the sideset names as a numpy array of
208
+ strings.
209
+
210
+ Parameters
211
+ ----------
212
+
213
+ Returns
214
+ -------
215
+ np.ndarray | None
216
+ numpy array of strings corresponding to the
217
+ sideset names specified in the simulation. Returns None if no
218
+ sideset names are found.
219
+
220
+ """
221
+ return self.get_names('ss_names')
222
+
223
+
224
+ def get_sidesets(self, names: np.ndarray | None
225
+ ) -> dict[tuple[str,str], np.ndarray] | None:
226
+ """get_sidesets: returns the sidesets as a dictionary keyed by a tuple
227
+ of ('sideset_name', 'node' | 'elem'). Gives either the list of node
228
+ numbers or element numbers based on the specified key.
229
+
230
+ Parameters
231
+ ----------
232
+ names : np.ndarray | None
233
+ numpy array of strings specifying the
234
+ sideset names to extract from the dataset. If None return None.:
235
+
236
+ Returns
237
+ -------
238
+ dict[tuple[str,str], np.ndarray] | None
239
+ dictionary of sideset
240
+ nodes and element sets by name. The key is a tuple with the
241
+ first string being the sideset name and the second being either
242
+ 'node' or 'elem'. Returns None if no sidesets found.
243
+
244
+ """
245
+ all_names = self.get_sideset_names()
246
+
247
+ if names is None or all_names is None:
248
+ return None
249
+
250
+ node_key_tag = 'node_ns'
251
+ elem_key_tag = 'elem_ss'
252
+
253
+ side_sets = dict({})
254
+ for nn in names: # type: ignore
255
+ node_key = self.get_key(nn,all_names,node_key_tag)
256
+ elem_key = self.get_key(nn,all_names,elem_key_tag)
257
+
258
+ if node_key is None:
259
+ side_sets[(nn,'node')] = None
260
+ else:
261
+ side_sets[(nn,'node')] = self.get_var(node_key)
262
+
263
+ if elem_key is None:
264
+ side_sets[(nn,'elem')] = None
265
+ else:
266
+ side_sets[(nn,'elem')] = self.get_var(elem_key)
267
+
268
+ return side_sets
269
+
270
+
271
+ def get_all_sidesets(self) -> dict[tuple[str,str], np.ndarray] | None:
272
+ """get_all_sidesets: returns all sidesets as a dictionary keyed by a tuple
273
+ of ('sideset_name', 'node' | 'elem'). Gives either the list of node
274
+ numbers or element numbers based on the specified key.
275
+
276
+ Parameters
277
+ ----------
278
+
279
+ Returns
280
+ -------
281
+ dict[tuple[str,str], np.ndarray] | None
282
+ dictionary of sideset
283
+ nodes and element sets by name. The key is a tuple with the
284
+ first string being the sideset name and the second being either
285
+ 'node' or 'elem'. Returns None if no sidesets found.
286
+
287
+ """
288
+
289
+ return self.get_sidesets(self.get_sideset_names())
290
+
291
+
292
+ def get_node_var_names(self) -> np.ndarray | None:
293
+ """get_node_var_names: gets the nodal variable names as a numpy array
294
+ of strings e.g. np.array(['disp_x','disp_y'])
295
+
296
+ Parameters
297
+ ----------
298
+
299
+ Returns
300
+ -------
301
+ np.ndarray | None
302
+ numpy array of strings containing the nodal
303
+ variable names. Returns None if no nodal variables are found.
304
+
305
+ """
306
+ return self.get_names('name_nod_var')
307
+
308
+
309
+ def get_node_vars(self,
310
+ names: np.ndarray | None,
311
+ time_inds: np.ndarray | None = None
312
+ ) -> dict[str,np.ndarray] | None:
313
+ """get_node_vars: gets the specified nodal variables as a dictionary
314
+ keyed by the variable name (e.g. 'disp_x') where the nodal variable is
315
+ given as a numpy array of dimensions NxT where N is the number of nodes
316
+ and T is the number of time steps in the simulation.
317
+
318
+ Parameters
319
+ ----------
320
+ names : np.ndarray | None
321
+ numpy array of strings that are the
322
+ variables to be extracted from the exodus dataset.
323
+ time_inds: np.ndarray | None :
324
+ (Default value = None)
325
+
326
+ Returns
327
+ -------
328
+ dict[str,np.ndarray] | None
329
+ dictionary of requested nodal
330
+ variables. Keys are nodal variable names e.g. 'disp_x' and the
331
+ variable data is given as a numpy array. returns None if no
332
+ nodal variables are found.
333
+
334
+ """
335
+ if names is None:
336
+ return None
337
+
338
+ all_names = self.get_node_var_names()
339
+ key_tag = 'vals_nod_var'
340
+ vars = dict({})
341
+
342
+ for nn in names: # type: ignore
343
+ inds = np.where(all_names == nn)[0]
344
+ key = f'{key_tag}{inds[0]+1:d}'
345
+ vars[nn] = self.get_var(key,time_inds)
346
+
347
+ return vars
348
+
349
+
350
+ def get_all_node_vars(self) -> dict[str, np.ndarray] | None:
351
+ """get_all_node_vars: as get_node_vars but returns all nodal variables
352
+ found in the dataset. Gets all specified nodal variables as a dictionary
353
+ keyed by the variable name (e.g. 'disp_x') where the nodal variable is
354
+ given as a numpy array of dimensions NxT where N is the number of nodes
355
+ and T is the number of time steps in the simulation.
356
+
357
+ Parameters
358
+ ----------
359
+
360
+ Returns
361
+ -------
362
+ dict[str, np.ndarray] | None
363
+ dictionary of requested nodal
364
+ variables. Keys are nodal variable names e.g. 'disp_x' and the
365
+ variable data is given as a numpy array. returns None if no
366
+ nodal variables are found.
367
+
368
+ """
369
+ return self.get_node_vars(self.get_node_var_names())
370
+
371
+
372
+ def get_elem_var_names(self) -> np.ndarray | None:
373
+ """get_elem_var_names: gets the element variable names as a numpy array
374
+ of strings if they exist. Note that there are several cases where the
375
+ element variables may be interpolated to nodes and stored as nodal data
376
+
377
+ Parameters
378
+ ----------
379
+
380
+ Returns
381
+ -------
382
+ np.ndarray | None
383
+ element variable names as a numpy array of
384
+ strings. An example variable name is 'strain_xx'. Returns None
385
+ if no element variable names exist in the dataset.
386
+
387
+ """
388
+ return self.get_names('name_elem_var')
389
+
390
+
391
+ def get_num_elem_blocks(self) -> int:
392
+ """get_num_elem_blocks: gets the number of element blocks (i.e.
393
+ sub-domains) in the simulation. These are used to partition the element
394
+ data.
395
+
396
+ Parameters
397
+ ----------
398
+
399
+ Returns
400
+ -------
401
+ int
402
+ number of element blocks/sub-domains in the simulation.
403
+
404
+ """
405
+ return self.get_names('eb_names').shape[0] # type: ignore
406
+
407
+
408
+ def get_elem_var_names_and_blocks(self) -> list[tuple[str,int]] | None:
409
+ """get_elem_var_names_and_blocks: returns a list of all possible
410
+ combinations of element variables names and block numbers present in
411
+ the dataset.
412
+
413
+ Parameters
414
+ ----------
415
+
416
+ Returns
417
+ -------
418
+ list[tuple[str,int]] | None
419
+ list of tuples containing the element
420
+ variable names and block numbers. Returns None if there are no
421
+ element variable name or element blocks.
422
+
423
+ """
424
+ if self.get_elem_var_names() is None or self.get_num_elem_blocks() is None:
425
+ return None
426
+
427
+ blocks = [ii+1 for ii in range(self.get_num_elem_blocks())] # type: ignore
428
+ names_blocks = list([])
429
+
430
+ for nn in self.get_elem_var_names(): # type: ignore
431
+ for bb in blocks:
432
+ names_blocks.append((str(nn),bb))
433
+
434
+ return names_blocks
435
+
436
+
437
+ def get_elem_vars(self,
438
+ names_blocks: list[tuple[str,int]] | None,
439
+ time_inds: np.ndarray | None = None
440
+ ) -> dict[tuple[str,int],np.ndarray] | None:
441
+ """get_elem_vars: gets the element variables as a dictionary keyed by
442
+ tuples which containg the element variable name and the block number.
443
+ For example: ('strain_xx',1). The element data is given as a numpy
444
+ array with dimensions E_bxT where E_b is the number of element in the
445
+ block and T is the number of time steps.
446
+
447
+ Parameters
448
+ ----------
449
+ names_blocks : list[tuple[str,int]] | None :
450
+ list of tuples containing the combination of element variables names
451
+ and blocks to be extracted from the dataset.
452
+ time_inds: np.ndarray | None :
453
+ (Default value = None)
454
+
455
+ Returns
456
+ -------
457
+ dict[tuple[str,int],np.ndarray] | None
458
+ contains the variables requested keyed using the input names_blocks
459
+ with the data given as a numpy array.
460
+
461
+ """
462
+ all_names = self.get_elem_var_names()
463
+
464
+ if all_names is None or names_blocks is None:
465
+ return None
466
+
467
+ key_tag = 'vals_elem_var'
468
+
469
+ vars = dict({})
470
+ for nn in names_blocks:
471
+ key = self.get_key(nn[0],all_names,key_tag) + f'eb{nn[1]:d}' # type: ignore
472
+ vars[nn] = self.get_var(key,time_inds)
473
+
474
+ return vars
475
+
476
+
477
+ def get_all_elem_vars(self) -> dict[tuple[str,int], np.ndarray] | None:
478
+ """get_all_elem_vars: gets all element variables as a dictionary keyed by
479
+ tuples which containg the element variable name and the block number.
480
+ For example: ('strain_xx',1). The element data is given as a numpy
481
+ array with dimensions E_bxT where E_b is the number of element in the
482
+ block and T is the number of time steps.
483
+
484
+ Parameters
485
+ ----------
486
+
487
+ Returns
488
+ -------
489
+ dict[tuple[str,int], np.ndarray] | None
490
+ contains the variables requested keyed using the input names_blocks
491
+ with the data given as a numpy array.
492
+ """
493
+
494
+ return self.get_elem_vars(self.get_elem_var_names_and_blocks())
495
+
496
+
497
+ def get_glob_var_names(self) -> np.ndarray | None:
498
+ """get_glob_var_names: gets the names of all global variables in the
499
+ dataset. Global variables include the output of all MOOSE post-
500
+ processors.
501
+
502
+ Parameters
503
+ ----------
504
+
505
+ Returns
506
+ -------
507
+ np.ndarray | None
508
+ numpy array containing the global variable
509
+ names as strings.
510
+
511
+ """
512
+ return self.get_names('name_glo_var')
513
+
514
+
515
+ def get_glob_vars(self,
516
+ names: np.ndarray | None,
517
+ time_inds: np.ndarray | None = None
518
+ ) -> dict[str, np.ndarray] | None:
519
+ """get_glob_vars: gets the specified global variables as a dictionary
520
+ keyed by the variable name specified in the MOOSE input file. The data
521
+ is given as a numpy array of T dimensions where T is the number of time
522
+ steps.
523
+
524
+ Parameters
525
+ ----------
526
+ names : np.ndarray | None
527
+ numpy array of strings specifying the
528
+ global variable names to extract from the dataset. If this is
529
+ None then return None.
530
+ time_inds: np.ndarray | None :
531
+ (Default value = None)
532
+
533
+ Returns
534
+ -------
535
+ dict[str, np.ndarray] | None
536
+ dictionary keyed with the global
537
+ variable names requested giving the data as a numpy array.
538
+
539
+ """
540
+ all_names = self.get_glob_var_names()
541
+
542
+ if all_names is None or names is None:
543
+ return None
544
+
545
+ key = 'vals_glo_var'
546
+
547
+ glob_vars = dict({})
548
+ for nn in names: # type: ignore
549
+ inds = np.where(all_names == nn)[0]
550
+ if time_inds is None:
551
+ glob_vars[nn] = np.array(self._data.variables[key][:,inds[0]])
552
+ else:
553
+ data = np.array(self._data.variables[key][:,inds[0]])
554
+ data = data[time_inds]
555
+ glob_vars[nn] = data
556
+
557
+ return glob_vars
558
+
559
+
560
+ def get_all_glob_vars(self) -> dict[str, np.ndarray] | None:
561
+ """get_all_glob_vars: gets all global variables as a dictionary
562
+ keyed by the variable name specified in the MOOSE input file. The data
563
+ is given as a numpy array of T dimensions where T is the number of time
564
+ steps.
565
+
566
+ Parameters
567
+ ----------
568
+
569
+ Returns
570
+ -------
571
+ dict[str, np.ndarray] | None
572
+ dictionary keyed with all global
573
+ variable names giving the data as numpy arrays.
574
+
575
+ """
576
+ return self.get_glob_vars(self.get_glob_var_names())
577
+
578
+
579
+ def get_coords(self) -> tuple[np.ndarray,int]:
580
+ """Gets the nodal coordinates in each spatial dimension setting any
581
+ undefined dimensions to zeros.
582
+
583
+ Parameters
584
+ ----------
585
+
586
+ Returns
587
+ -------
588
+ np.array
589
+ returns the nodal coordinates as an array with shape
590
+ (N,3) where N is the number of nodes and the three columns
591
+ are the (x,y,z) spatial dimensions.
592
+
593
+ Raises
594
+ ------
595
+ RuntimeError
596
+ no spatial dimensions found.
597
+
598
+ """
599
+ # If the problem is not 3D any of these could not exist
600
+ x = self.get_var('coordx')
601
+ y = self.get_var('coordy')
602
+ z = self.get_var('coordz')
603
+
604
+ # Problem has to be at least 1D in space if not raise an error
605
+ num_coords = np.max(np.array([x.shape[0],y.shape[0],z.shape[0]]))
606
+ if num_coords == 0:
607
+ raise RuntimeError("No spatial coordinate dimensions detected, problem must be at least 1D.")
608
+
609
+ # Any dimensions that do not exist are assumed to be zeros
610
+ x = self._expand_coord(x,num_coords)
611
+ y = self._expand_coord(y,num_coords)
612
+ z = self._expand_coord(z,num_coords)
613
+
614
+ return (np.vstack((x,y,z)).T,num_coords)
615
+
616
+
617
+ def _expand_coord(self, coord: np.ndarray, dim: int) -> np.ndarray:
618
+ """Helper function to create an array of zeros to pad any spatial
619
+ dimensions that are not defined for the simulation.
620
+
621
+ Parameters
622
+ ----------
623
+ coord : np.array
624
+ the coordinate array.
625
+ dim : int
626
+ the size of the vector of zeros to create if coord is
627
+ empty.
628
+
629
+ Returns
630
+ -------
631
+ np.array
632
+ returns a vector of zeros with shape (dim,) if the
633
+ input array is empty, otherwise return the input coord array.
634
+
635
+ """
636
+ if coord.shape[0] == 0:
637
+ return np.zeros([dim,])
638
+
639
+ return coord
640
+
641
+
642
+ def get_time(self, time_inds: np.ndarray | None = None) -> np.ndarray:
643
+ """Get a vector of simulation time steps.
644
+
645
+ Parameters
646
+ ----------
647
+ time_inds: np.ndarray | None :
648
+ (Default value = None)
649
+
650
+ Returns
651
+ -------
652
+ np.array
653
+ returns an array with shape (T,) where T is the number
654
+ of time steps and the values of the elements are the simulation
655
+ time and each time step.
656
+
657
+ """
658
+ time_steps = np.array([]
659
+ )
660
+ if 'time_whole' in self._data.variables:
661
+ time_steps = np.array(self._data.variables['time_whole'])
662
+
663
+ if time_inds is not None:
664
+ time_steps = time_steps[time_inds]
665
+
666
+ return time_steps
667
+
668
+
669
+ def print_vars(self) -> None:
670
+ """Prints all variable strings in the exodus file to console."""
671
+ for vv in self._data.variables:
672
+ print(vv)
673
+
674
+ def get_read_config(self) -> SimReadConfig:
675
+ """get_read_config: constructs a SimReadConfig object by extracting
676
+ all the variable names found in the exodus dataset. Useful for creating
677
+ a mostly populated SimReadConfig and removing variables that are
678
+ unwanted.
679
+
680
+ Parameters
681
+ ----------
682
+
683
+ Returns
684
+ -------
685
+ SimReadConfig
686
+ data class containing names of variables to be
687
+ extracted from the exodus dataset. See mooseherder.simdata.
688
+
689
+ """
690
+ read_config = SimReadConfig()
691
+
692
+ read_config.sidesets = self.get_sideset_names()
693
+ read_config.node_vars = self.get_node_var_names()
694
+ read_config.elem_vars = self.get_elem_var_names_and_blocks()
695
+ read_config.glob_vars = self.get_glob_var_names()
696
+
697
+ return read_config
698
+
699
+
700
+ def read_sim_data(self,
701
+ read_config: SimReadConfig) -> SimData:
702
+ """read_sim_data: reads the simulation data based on the specified
703
+ SimReadConfig object.
704
+
705
+ Parameters
706
+ ----------
707
+ read_config : SimReadConfig
708
+ data class containing the names of
709
+ the variables that are to be extracted from the exodus dataset.
710
+ read_config: SimReadConfig :
711
+
712
+
713
+ Returns
714
+ -------
715
+ SimData
716
+ data class containing data from the simulation.
717
+
718
+ """
719
+ data = SimData()
720
+
721
+ if read_config.time:
722
+ data.time = self.get_time(read_config.time_inds)
723
+ if read_config.coords:
724
+ (data.coords,data.num_spat_dims) = self.get_coords()
725
+ if read_config.connect:
726
+ data.connect = self.get_connectivity()
727
+
728
+ data.side_sets = self.get_sidesets(read_config.sidesets)
729
+
730
+ data.node_vars = self.get_node_vars(read_config.node_vars,
731
+ read_config.time_inds)
732
+ data.elem_vars = self.get_elem_vars(read_config.elem_vars,
733
+ read_config.time_inds)
734
+ data.glob_vars = self.get_glob_vars(read_config.glob_vars,
735
+ read_config.time_inds)
736
+
737
+ return data
738
+
739
+
740
+ def read_all_sim_data(self) -> SimData:
741
+ """read_all_sim_data: gets all simulation data from the exodus dataset.
742
+
743
+ Parameters
744
+ ----------
745
+
746
+ Returns
747
+ -------
748
+ SimData
749
+ data class containing the data from the simulation.
750
+
751
+ """
752
+ data = SimData()
753
+
754
+ data.time = self.get_time()
755
+ (data.coords,data.num_spat_dims) = self.get_coords()
756
+ data.connect = self.get_connectivity()
757
+ data.side_sets = self.get_all_sidesets()
758
+ data.node_vars = self.get_all_node_vars()
759
+ data.elem_vars = self.get_all_elem_vars()
760
+ data.glob_vars = self.get_all_glob_vars()
761
+
762
+ return data
763
+