pyvale 2025.5.3__cp311-cp311-win32.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 (174) hide show
  1. pyvale/__init__.py +89 -0
  2. pyvale/analyticmeshgen.py +102 -0
  3. pyvale/analyticsimdatafactory.py +91 -0
  4. pyvale/analyticsimdatagenerator.py +323 -0
  5. pyvale/blendercalibrationdata.py +15 -0
  6. pyvale/blenderlightdata.py +26 -0
  7. pyvale/blendermaterialdata.py +15 -0
  8. pyvale/blenderrenderdata.py +30 -0
  9. pyvale/blenderscene.py +488 -0
  10. pyvale/blendertools.py +420 -0
  11. pyvale/camera.py +146 -0
  12. pyvale/cameradata.py +69 -0
  13. pyvale/cameradata2d.py +84 -0
  14. pyvale/camerastereo.py +217 -0
  15. pyvale/cameratools.py +522 -0
  16. pyvale/cython/rastercyth.c +32211 -0
  17. pyvale/cython/rastercyth.cp311-win32.pyd +0 -0
  18. pyvale/cython/rastercyth.py +640 -0
  19. pyvale/data/__init__.py +5 -0
  20. pyvale/data/cal_target.tiff +0 -0
  21. pyvale/data/case00_HEX20_out.e +0 -0
  22. pyvale/data/case00_HEX27_out.e +0 -0
  23. pyvale/data/case00_HEX8_out.e +0 -0
  24. pyvale/data/case00_TET10_out.e +0 -0
  25. pyvale/data/case00_TET14_out.e +0 -0
  26. pyvale/data/case00_TET4_out.e +0 -0
  27. pyvale/data/case13_out.e +0 -0
  28. pyvale/data/case16_out.e +0 -0
  29. pyvale/data/case17_out.e +0 -0
  30. pyvale/data/case18_1_out.e +0 -0
  31. pyvale/data/case18_2_out.e +0 -0
  32. pyvale/data/case18_3_out.e +0 -0
  33. pyvale/data/case25_out.e +0 -0
  34. pyvale/data/case26_out.e +0 -0
  35. pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
  36. pyvale/dataset.py +325 -0
  37. pyvale/errorcalculator.py +109 -0
  38. pyvale/errordriftcalc.py +146 -0
  39. pyvale/errorintegrator.py +336 -0
  40. pyvale/errorrand.py +607 -0
  41. pyvale/errorsyscalib.py +134 -0
  42. pyvale/errorsysdep.py +327 -0
  43. pyvale/errorsysfield.py +414 -0
  44. pyvale/errorsysindep.py +808 -0
  45. pyvale/examples/__init__.py +5 -0
  46. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
  47. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
  48. pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
  49. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
  50. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
  51. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
  52. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
  53. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
  54. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
  55. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
  56. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
  57. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
  58. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
  59. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
  60. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
  61. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
  62. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
  63. pyvale/examples/genanalyticdata/ex1_1_scalarvisualisation.py +35 -0
  64. pyvale/examples/genanalyticdata/ex1_2_scalarcasebuild.py +43 -0
  65. pyvale/examples/genanalyticdata/ex2_1_analyticsensors.py +80 -0
  66. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +79 -0
  67. pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
  68. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
  69. pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
  70. pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
  71. pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
  72. pyvale/examples/renderrasterisation/ex_rastenp.py +153 -0
  73. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +218 -0
  74. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +187 -0
  75. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +190 -0
  76. pyvale/examples/visualisation/ex1_1_plot_traces.py +102 -0
  77. pyvale/examples/visualisation/ex2_1_animate_sim.py +89 -0
  78. pyvale/experimentsimulator.py +175 -0
  79. pyvale/field.py +128 -0
  80. pyvale/fieldconverter.py +351 -0
  81. pyvale/fieldsampler.py +111 -0
  82. pyvale/fieldscalar.py +166 -0
  83. pyvale/fieldtensor.py +218 -0
  84. pyvale/fieldtransform.py +388 -0
  85. pyvale/fieldvector.py +213 -0
  86. pyvale/generatorsrandom.py +505 -0
  87. pyvale/imagedef2d.py +569 -0
  88. pyvale/integratorfactory.py +240 -0
  89. pyvale/integratorquadrature.py +217 -0
  90. pyvale/integratorrectangle.py +165 -0
  91. pyvale/integratorspatial.py +89 -0
  92. pyvale/integratortype.py +43 -0
  93. pyvale/output.py +17 -0
  94. pyvale/pyvaleexceptions.py +11 -0
  95. pyvale/raster.py +31 -0
  96. pyvale/rastercy.py +77 -0
  97. pyvale/rasternp.py +603 -0
  98. pyvale/rendermesh.py +147 -0
  99. pyvale/sensorarray.py +178 -0
  100. pyvale/sensorarrayfactory.py +196 -0
  101. pyvale/sensorarraypoint.py +278 -0
  102. pyvale/sensordata.py +71 -0
  103. pyvale/sensordescriptor.py +213 -0
  104. pyvale/sensortools.py +142 -0
  105. pyvale/simcases/case00_HEX20.i +242 -0
  106. pyvale/simcases/case00_HEX27.i +242 -0
  107. pyvale/simcases/case00_HEX8.i +242 -0
  108. pyvale/simcases/case00_TET10.i +242 -0
  109. pyvale/simcases/case00_TET14.i +242 -0
  110. pyvale/simcases/case00_TET4.i +242 -0
  111. pyvale/simcases/case01.i +101 -0
  112. pyvale/simcases/case02.i +156 -0
  113. pyvale/simcases/case03.i +136 -0
  114. pyvale/simcases/case04.i +181 -0
  115. pyvale/simcases/case05.i +234 -0
  116. pyvale/simcases/case06.i +305 -0
  117. pyvale/simcases/case07.geo +135 -0
  118. pyvale/simcases/case07.i +87 -0
  119. pyvale/simcases/case08.geo +144 -0
  120. pyvale/simcases/case08.i +153 -0
  121. pyvale/simcases/case09.geo +204 -0
  122. pyvale/simcases/case09.i +87 -0
  123. pyvale/simcases/case10.geo +204 -0
  124. pyvale/simcases/case10.i +257 -0
  125. pyvale/simcases/case11.geo +337 -0
  126. pyvale/simcases/case11.i +147 -0
  127. pyvale/simcases/case12.geo +388 -0
  128. pyvale/simcases/case12.i +329 -0
  129. pyvale/simcases/case13.i +140 -0
  130. pyvale/simcases/case14.i +159 -0
  131. pyvale/simcases/case15.geo +337 -0
  132. pyvale/simcases/case15.i +150 -0
  133. pyvale/simcases/case16.geo +391 -0
  134. pyvale/simcases/case16.i +357 -0
  135. pyvale/simcases/case17.geo +135 -0
  136. pyvale/simcases/case17.i +144 -0
  137. pyvale/simcases/case18.i +254 -0
  138. pyvale/simcases/case18_1.i +254 -0
  139. pyvale/simcases/case18_2.i +254 -0
  140. pyvale/simcases/case18_3.i +254 -0
  141. pyvale/simcases/case19.geo +252 -0
  142. pyvale/simcases/case19.i +99 -0
  143. pyvale/simcases/case20.geo +252 -0
  144. pyvale/simcases/case20.i +250 -0
  145. pyvale/simcases/case21.geo +74 -0
  146. pyvale/simcases/case21.i +155 -0
  147. pyvale/simcases/case22.geo +82 -0
  148. pyvale/simcases/case22.i +140 -0
  149. pyvale/simcases/case23.geo +164 -0
  150. pyvale/simcases/case23.i +140 -0
  151. pyvale/simcases/case24.geo +79 -0
  152. pyvale/simcases/case24.i +123 -0
  153. pyvale/simcases/case25.geo +82 -0
  154. pyvale/simcases/case25.i +140 -0
  155. pyvale/simcases/case26.geo +166 -0
  156. pyvale/simcases/case26.i +140 -0
  157. pyvale/simcases/run_1case.py +61 -0
  158. pyvale/simcases/run_all_cases.py +69 -0
  159. pyvale/simcases/run_build_case.py +64 -0
  160. pyvale/simcases/run_example_cases.py +69 -0
  161. pyvale/simtools.py +67 -0
  162. pyvale/visualexpplotter.py +191 -0
  163. pyvale/visualimagedef.py +74 -0
  164. pyvale/visualimages.py +76 -0
  165. pyvale/visualopts.py +493 -0
  166. pyvale/visualsimanimator.py +111 -0
  167. pyvale/visualsimsensors.py +318 -0
  168. pyvale/visualtools.py +136 -0
  169. pyvale/visualtraceplotter.py +142 -0
  170. pyvale-2025.5.3.dist-info/METADATA +144 -0
  171. pyvale-2025.5.3.dist-info/RECORD +174 -0
  172. pyvale-2025.5.3.dist-info/WHEEL +5 -0
  173. pyvale-2025.5.3.dist-info/licenses/LICENSE +21 -0
  174. pyvale-2025.5.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,336 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ import copy
8
+ from dataclasses import dataclass
9
+ import numpy as np
10
+ from pyvale.errorcalculator import (IErrCalculator,
11
+ EErrType,
12
+ EErrDep)
13
+ from pyvale.sensordata import SensorData
14
+
15
+
16
+ @dataclass(slots=True)
17
+ class ErrIntOpts:
18
+ """Error integration options dataclass. Allows the user to control how
19
+ errors are calculated and stored in memory for later use.
20
+ """
21
+
22
+ force_dependence: EErrDep | None = None
23
+ """Forces all errors to be calculated with the specified dependence. If set
24
+ to None then all errors will use their default/preset dependence.
25
+
26
+ Note that some errors are inherently independent so will not change. For
27
+ example: `ErrRandNormal` is purely independent whereas `ErrRandNormPercent`
28
+ can have the percentage error calculated based on the ground truth
29
+ (independent) or based on the accumulated sensor measurement (dependent).
30
+ """
31
+
32
+ store_all_errs: bool = False
33
+ """Stores all errors for individual error in the chain if True. Also stores
34
+ a list of SensorData objects showing perturbations to the sensor array
35
+ parameters caused by individual errors. Consumes significantly more memory
36
+ but is useful for finding which errors contribute most to the total
37
+ measurement error. For large sensor arrays (>100 sensors)
38
+ """
39
+
40
+
41
+ class ErrIntegrator:
42
+ """Class for managing sensor error integration. Takes a list of objects that
43
+ implement the `IErrCalculator` interface (i.e. the error chain) and loops
44
+ through them calculating each errors contribution to the total measurement
45
+ error and sums this over all errors in the chain. In addition to the total
46
+ error a sum of the random and systematic errors (see `EErrType`) is
47
+ calculated and stored.
48
+
49
+ This class also accumulates perturbations to the sensor array parameters due
50
+ to errors (i.e. sensor positioning error or temporal drift). The accumulated
51
+ sensor array parameters are stored as a `SensorData` object.
52
+
53
+ The errors are calculated in the order specified in the list. For dependent
54
+ errors (`EErrDependence.DEPENDENT`) the position of the error within the
55
+ error chain determines the accumulated sensor measurement that will be used
56
+ to calculate the error.
57
+
58
+ The user can control how the errors are calculated using the `ErrIntOpts`
59
+ dataclass.
60
+ """
61
+ __slots__ = ("_err_chain","_meas_shape","_errs_by_chain",
62
+ "_errs_systematic","_errs_random","_errs_total",
63
+ "_sens_data_by_chain","_err_int_opts","_sens_data_accumulated",
64
+ "_sens_data_initial")
65
+
66
+ def __init__(self,
67
+ err_chain: list[IErrCalculator],
68
+ sensor_data_initial: SensorData,
69
+ meas_shape: tuple[int,int,int],
70
+ err_int_opts: ErrIntOpts | None = None) -> None:
71
+ """
72
+ Parameters
73
+ ----------
74
+ err_chain : list[IErrCalculator]
75
+ List of error objects implementing the IErrCalculator interface.
76
+ sensor_data_initial : SensorData
77
+ Object holding the initial sensor array parameters before they are
78
+ modified by the error chain.
79
+ meas_shape : tuple[int,int,int]
80
+ Shape of the sensor measurement array. shape=(num_sensors,
81
+ num_field_components,num_time_steps)
82
+ err_int_opts : ErrIntOpts | None, optional
83
+ Options for controlling how errors are calculated/summed and how
84
+ they are store in memory, by default None. If None then the default
85
+ options dataclass is used.
86
+ """
87
+
88
+ if err_int_opts is None:
89
+ self._err_int_opts = ErrIntOpts()
90
+ else:
91
+ self._err_int_opts = err_int_opts
92
+
93
+ self.set_error_chain(err_chain)
94
+ self._meas_shape = meas_shape
95
+
96
+ self._sens_data_initial = copy.deepcopy(sensor_data_initial)
97
+ self._sens_data_accumulated = copy.deepcopy(sensor_data_initial)
98
+
99
+ if self._err_int_opts.store_all_errs:
100
+ self._sens_data_by_chain = []
101
+ self._errs_by_chain = np.zeros((len(self._err_chain),)+ \
102
+ self._meas_shape)
103
+ else:
104
+ self._sens_data_by_chain = None
105
+ self._errs_by_chain = None
106
+
107
+ self._errs_systematic = np.zeros(meas_shape)
108
+ self._errs_random = np.zeros(meas_shape)
109
+ self._errs_total = np.zeros(meas_shape)
110
+
111
+
112
+ def set_error_chain(self, err_chain: list[IErrCalculator]) -> None:
113
+ """Sets the error chain that will be looped over to calculate the sensor
114
+ measurement errors. If the error integration options are forcing error
115
+ dependence then all errors in the chain will have their dependence set
116
+ to the specified value.
117
+
118
+ Parameters
119
+ ----------
120
+ err_chain : list[IErrCalculator]
121
+ List of error calculators implementing the IErrCalculator interface.
122
+ """
123
+ self._err_chain = err_chain
124
+
125
+ if self._err_int_opts.force_dependence is not None:
126
+ for ee in self._err_chain:
127
+ ee.set_error_dep(self._err_int_opts.force_dependence)
128
+
129
+
130
+ def calc_errors_from_chain(self, truth: np.ndarray) -> np.ndarray:
131
+ """Calculates all errors by looping over the error chain. The total
132
+ measurement error is summed as each error is calculated in order. Note
133
+ that this causes all errors based on probability distributions to be
134
+ resampled and any required interpolations to be performed (e.g. from
135
+ randomly perturbing the sensor positions). Accumulated errors are also
136
+ stored for random and systematic errors separately (see `EErrType`).
137
+
138
+ If the `store_all_errs = True` in the `ErrIntOpts` dataclass then each
139
+ individual error is stored in a numpy array (see `get_errs_by_chain()`)
140
+ along with the accumulated errors in another numpy array.
141
+
142
+ Parameters
143
+ ----------
144
+ truth : np.ndarray
145
+ Array of ground truth sensor measurements interpolated from the
146
+ simulated physical field. shape=(num_sensors,num_field_components,
147
+ num_time_steps).
148
+
149
+ Returns
150
+ -------
151
+ np.ndarray
152
+ Array of total errors summed over all errors in the chain. shape=(
153
+ num_sensors,num_field_components,num_time_steps).
154
+ """
155
+ if self._err_int_opts.store_all_errs:
156
+ return self._calc_errors_store_by_chain(truth)
157
+
158
+ return self._calc_errors_mem_eff(truth)
159
+
160
+
161
+ def _calc_errors_store_by_chain(self, truth: np.ndarray) -> np.ndarray:
162
+ """Helper function for calculating all errors in the chain and summing
163
+ them. Returns the total error and stores sums of the random and
164
+ systematic errors in member variables. This function also stores each
165
+ individual error calculation in a separate numpy array for analysis.
166
+
167
+ Parameters
168
+ ----------
169
+ truth : np.ndarray
170
+ Array of ground truth sensor measurements interpolated from the
171
+ simulated physical field. shape=(num_sensors,num_field_components,
172
+ num_time_steps).
173
+
174
+ Returns
175
+ -------
176
+ np.ndarray
177
+ Array of total errors summed over all errors in the chain. shape=(
178
+ num_sensors,num_field_components,num_time_steps).
179
+ """
180
+ accumulated_error = np.zeros_like(truth)
181
+ self._errs_by_chain = np.zeros((len(self._err_chain),) + \
182
+ self._meas_shape)
183
+
184
+ for ii,ee in enumerate(self._err_chain):
185
+
186
+ if ee.get_error_dep() == EErrDep.DEPENDENT:
187
+ (error_array,sens_data) = ee.calc_errs(truth+accumulated_error,
188
+ self._sens_data_accumulated)
189
+
190
+ else:
191
+ (error_array,sens_data) = ee.calc_errs(truth,
192
+ self._sens_data_initial)
193
+
194
+ self._sens_data_accumulated = sens_data
195
+ self._sens_data_by_chain.append(sens_data)
196
+
197
+ if ee.get_error_type() == EErrType.SYSTEMATIC:
198
+ self._errs_systematic = self._errs_systematic + error_array
199
+ else:
200
+ self._errs_random = self._errs_random + error_array
201
+
202
+ accumulated_error = accumulated_error + error_array
203
+ self._errs_by_chain[ii,:,:,:] = error_array
204
+
205
+ self._errs_total = accumulated_error
206
+ return self._errs_total
207
+
208
+
209
+ def _calc_errors_mem_eff(self, truth: np.ndarray) -> np.ndarray:
210
+ """Helper function for calculating all errors in the chain and summing
211
+ them. Returns the total error and stores sums of the random and
212
+ systematic errors in member variables. The individual error
213
+ contributions are not stored in this case for memory efficiency, only
214
+ the summed total, random and systematic error arrays are stored.
215
+
216
+ Parameters
217
+ ----------
218
+ truth : np.ndarray
219
+ Array of ground truth sensor measurements interpolated from the
220
+ simulated physical field. shape=(num_sensors,num_field_components,
221
+ num_time_steps).
222
+
223
+ Returns
224
+ -------
225
+ np.ndarray
226
+ Array of total errors summed over all errors in the chain. shape=(
227
+ num_sensors,num_field_components,num_time_steps).
228
+ """
229
+ accumulated_error = np.zeros_like(truth)
230
+
231
+ for ee in self._err_chain:
232
+
233
+ if ee.get_error_dep() == EErrDep.DEPENDENT:
234
+ (error_array,sens_data) = ee.calc_errs(
235
+ truth+accumulated_error,
236
+ self._sens_data_accumulated
237
+ )
238
+ else:
239
+ (error_array,sens_data) = ee.calc_errs(truth,
240
+ self._sens_data_initial)
241
+
242
+ self._sens_data_accumulated = sens_data
243
+
244
+ if ee.get_error_type() == EErrType.SYSTEMATIC:
245
+ self._errs_systematic = self._errs_systematic + error_array
246
+ else:
247
+ self._errs_random = self._errs_random + error_array
248
+
249
+ accumulated_error = accumulated_error + error_array
250
+
251
+ self._errs_total = accumulated_error
252
+ return self._errs_total
253
+
254
+
255
+ def get_errs_by_chain(self) -> np.ndarray | None:
256
+ """Gets the array of errors for each error in chain. If `store_all_errs`
257
+ is False in `ErrIntOpts` then this will return None.
258
+
259
+ Returns
260
+ -------
261
+ np.ndarray | None
262
+ Array of all errors in the chain. shape=(num_errs_in_chain,
263
+ num_sensors,num_field_components,num_time_steps). Returns None if
264
+ `ErrIntOpts.store_all_errs=False`.
265
+ """
266
+ return self._errs_by_chain
267
+
268
+ def get_sens_data_by_chain(self) -> list[SensorData] | None:
269
+ """Gets the list of sensor data objects storing how each error in the
270
+ chain has perturbed the underlying sensor parameters. If
271
+ `store_all_errs` is False in `ErrIntOpts` then this will return None.
272
+ If no sensor array parameters are modified by the error chain then all
273
+ SensorData objects in the list will be identical to the SensorData
274
+ object used to create the sensor array.
275
+
276
+ Returns
277
+ -------
278
+ list[SensorData] | None
279
+ List of perturbed sensors array parameters for each error in the
280
+ chain. Returns None if `ErrIntOpts.store_all_errs=False`.
281
+ """
282
+ return self._sens_data_by_chain
283
+
284
+ def get_sens_data_accumulated(self) -> SensorData:
285
+ """Gets the final accumulated sensor array parameters based on all
286
+ errors in the chain as a SensorData object. If no errors modify the
287
+ sensor array parameters then the SensorData object returns will be
288
+ identical to the SensorData object used to create the sensor array.
289
+
290
+ Returns
291
+ -------
292
+ SensorData
293
+ The final sensor array parameters based on accumulating all
294
+ perturbations from all errors in the error chain.
295
+ """
296
+ return self._sens_data_accumulated
297
+
298
+ def get_errs_systematic(self) -> np.ndarray:
299
+ """Gets the array of summed systematic errors over the error chain. If
300
+ the errors have not been calculated then an array of zeros is returned.
301
+
302
+ Returns
303
+ -------
304
+ np.ndarray
305
+ Array of total systematic errors. shape=(num_sensors,
306
+ num_field_components,num_time_steps)
307
+ """
308
+ return self._errs_systematic
309
+
310
+ def get_errs_random(self) -> np.ndarray:
311
+ """Gets the array of summed random errors over the error chain. If the
312
+ errors have not been calculated then an array of zeros is returned.
313
+
314
+ Returns
315
+ -------
316
+ np.ndarray
317
+ Array of total random errors. shape=(num_sensors,
318
+ num_field_components,num_time_steps)
319
+ """
320
+ return self._errs_random
321
+
322
+ def get_errs_total(self) -> np.ndarray:
323
+ """Gets the array of total errors. If the errors have not been
324
+ calculated then an array of zeros is returned. Note that this function
325
+ just returns the most recently calculated errors and will not resample
326
+ from probability distributions.
327
+
328
+ Returns
329
+ -------
330
+ np.ndarray
331
+ Array of total errors. shape=(num_sensors,num_field_components,
332
+ num_time_steps)
333
+ """
334
+ return self._errs_total
335
+
336
+