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,808 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ import numpy as np
8
+ from pyvale.errorcalculator import (IErrCalculator,
9
+ EErrType,
10
+ EErrDep)
11
+ from pyvale.generatorsrandom import IGenRandom
12
+ from pyvale.sensordata import SensorData
13
+
14
+
15
+ class ErrSysOffset(IErrCalculator):
16
+ """Systematic error calculator applying a constant offset to all simulated
17
+ sensor measurements. Implements the `IErrCalculator` interface.
18
+ """
19
+ __slots__ = ("_offset","_err_dep")
20
+
21
+ def __init__(self,
22
+ offset: float,
23
+ err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
24
+ """
25
+ Parameters
26
+ ----------
27
+ offset : float
28
+ Constant offset to apply to all simulated measurements from the
29
+ sensor array.
30
+ err_dep : EErrDependence, optional
31
+ Error calculation dependence, by default EErrDependence.INDEPENDENT.
32
+ """
33
+ self._offset = offset
34
+ self._err_dep = err_dep
35
+
36
+ def get_error_dep(self) -> EErrDep:
37
+ """Gets the error dependence state for this error calculator. An
38
+ independent error is calculated based on the input truth values as the
39
+ error basis. A dependent error is calculated based on the accumulated
40
+ sensor reading from all preceeding errors in the chain.
41
+
42
+ NOTE: for this error the calculation is independent regardless of this
43
+ setting as the offset is constant.
44
+
45
+ Returns
46
+ -------
47
+ EErrDependence
48
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
49
+ """
50
+ return self._err_dep
51
+
52
+ def set_error_dep(self, dependence: EErrDep) -> None:
53
+ """Sets the error dependence state for this error calculator. An
54
+ independent error is calculated based on the input truth values as the
55
+ error basis. A dependent error is calculated based on the accumulated
56
+ sensor reading from all preceeding errors in the chain.
57
+
58
+ NOTE: for this error the calculation is independent regardless of this
59
+ setting as the offset is constant.
60
+
61
+ Parameters
62
+ ----------
63
+ dependence : EErrDependence
64
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
65
+ """
66
+ self._err_dep = dependence
67
+
68
+ def get_error_type(self) -> EErrType:
69
+ """Gets the error type.
70
+
71
+ Returns
72
+ -------
73
+ EErrType
74
+ Enumeration definining RANDOM or SYSTEMATIC error types.
75
+ """
76
+ return EErrType.SYSTEMATIC
77
+
78
+ def calc_errs(self,
79
+ err_basis: np.ndarray,
80
+ sens_data: SensorData,
81
+ ) -> tuple[np.ndarray, SensorData]:
82
+ """Calculates the error array based on the size of the input.
83
+
84
+ Parameters
85
+ ----------
86
+ err_basis : np.ndarray
87
+ Array of values with the same dimensions as the sensor measurement
88
+ matrix.
89
+ sens_data : SensorData
90
+ The accumulated sensor state data for all errors prior to this one.
91
+
92
+ Returns
93
+ -------
94
+ tuple[np.ndarray, SensorData]
95
+ Tuple containing the calculated error array and pass through of the
96
+ sensor data object as it is not modified by this class. The returned
97
+ error array has the same shape as the input error basis.
98
+ """
99
+ return (self._offset*np.ones(shape=err_basis.shape),sens_data)
100
+
101
+
102
+ class ErrSysOffsetPercent(IErrCalculator):
103
+ """Systematic error calculator applying a constant offset as a percentage of
104
+ the sensor reading to each individual simulated sensor measurement.
105
+ Implements the `IErrCalculator` interface.
106
+ """
107
+ __slots__ = ("_offset_percent","_err_dep")
108
+
109
+ def __init__(self,
110
+ offset_percent: float,
111
+ err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
112
+ """
113
+ Parameters
114
+ ----------
115
+ offset_percent : float
116
+ Percentage offset to apply to apply to all simulated measurements
117
+ from the sensor array.
118
+ err_dep : EErrDependence, optional
119
+ Error calculation dependence, by default EErrDependence.INDEPENDENT
120
+ """
121
+ self._offset_percent = offset_percent
122
+ self._err_dep = err_dep
123
+
124
+ def get_error_dep(self) -> EErrDep:
125
+ """Gets the error dependence state for this error calculator. An
126
+ independent error is calculated based on the input truth values as the
127
+ error basis. A dependent error is calculated based on the accumulated
128
+ sensor reading from all preceeding errors in the chain.
129
+
130
+ Returns
131
+ -------
132
+ EErrDependence
133
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
134
+ """
135
+ return self._err_dep
136
+
137
+ def set_error_dep(self, dependence: EErrDep) -> None:
138
+ """Sets the error dependence state for this error calculator. An
139
+ independent error is calculated based on the input truth values as the
140
+ error basis. A dependent error is calculated based on the accumulated
141
+ sensor reading from all preceeding errors in the chain.
142
+
143
+ Parameters
144
+ ----------
145
+ dependence : EErrDependence
146
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
147
+ """
148
+ self._err_dep = dependence
149
+
150
+ def get_error_type(self) -> EErrType:
151
+ """Gets the error type.
152
+
153
+ Returns
154
+ -------
155
+ EErrType
156
+ Enumeration definining RANDOM or SYSTEMATIC error types.
157
+ """
158
+ return EErrType.SYSTEMATIC
159
+
160
+ def calc_errs(self,
161
+ err_basis: np.ndarray,
162
+ sens_data: SensorData,
163
+ ) -> tuple[np.ndarray, SensorData]:
164
+ """Calculates the error array based on the size of the input.
165
+
166
+ Parameters
167
+ ----------
168
+ err_basis : np.ndarray
169
+ Array of values with the same dimensions as the sensor measurement
170
+ matrix.
171
+ sens_data : SensorData
172
+ The accumulated sensor state data for all errors prior to this one.
173
+
174
+ Returns
175
+ -------
176
+ tuple[np.ndarray, SensorData]
177
+ Tuple containing the calculated error array and pass through of the
178
+ sensor data object as it is not modified by this class. The returned
179
+ error array has the same shape as the input error basis.
180
+ """
181
+ return (self._offset_percent/100 *
182
+ err_basis *
183
+ np.ones(shape=err_basis.shape),
184
+ sens_data)
185
+
186
+
187
+ class ErrSysUnif(IErrCalculator):
188
+ """Systematic error calculator for applying an offset to each sensor that is
189
+ sampled from a uniform probability distribution specified by its upper and
190
+ lower bounds. Implements the `IErrCalculator` interface.
191
+ """
192
+ __slots__ = ("_low","_high","_rng","_err_dep")
193
+
194
+ def __init__(self,
195
+ low: float,
196
+ high: float,
197
+ err_dep: EErrDep = EErrDep.INDEPENDENT,
198
+ seed: int | None = None) -> None:
199
+ """
200
+ Parameters
201
+ ----------
202
+ low : float
203
+ Lower bound of the uniform probability distribution in the same
204
+ units as the physical field the sensor array is sampling.
205
+ high : float
206
+ Upper bound of the uniform probability distribution in the same
207
+ units as the physical field the sensor array is sampling.
208
+ err_dep : EErrDependence, optional
209
+ Error calculation dependence, by default EErrDependence.INDEPENDENT.
210
+ seed : int | None, optional
211
+ Optional seed for the random generator to allow for replicable
212
+ behaviour, by default None.
213
+ """
214
+ self._low = low
215
+ self._high = high
216
+ self._rng = np.random.default_rng(seed)
217
+ self._err_dep = err_dep
218
+
219
+ def get_error_dep(self) -> EErrDep:
220
+ """Gets the error dependence state for this error calculator. An
221
+ independent error is calculated based on the input truth values as the
222
+ error basis. A dependent error is calculated based on the accumulated
223
+ sensor reading from all preceeding errors in the chain.
224
+
225
+ NOTE: for this error the calculation is independent regardless of this
226
+ setting as the offset is constant.
227
+
228
+ Returns
229
+ -------
230
+ EErrDependence
231
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
232
+ """
233
+ return self._err_dep
234
+
235
+ def set_error_dep(self, dependence: EErrDep) -> None:
236
+ """Sets the error dependence state for this error calculator. An
237
+ independent error is calculated based on the input truth values as the
238
+ error basis. A dependent error is calculated based on the accumulated
239
+ sensor reading from all preceeding errors in the chain.
240
+
241
+ NOTE: for this error the calculation is independent regardless of this
242
+ setting as the offset is constant.
243
+
244
+ Parameters
245
+ ----------
246
+ dependence : EErrDependence
247
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
248
+ """
249
+ self._err_dep = dependence
250
+
251
+ def get_error_type(self) -> EErrType:
252
+ """Gets the error type.
253
+
254
+ Returns
255
+ -------
256
+ EErrType
257
+ Enumeration definining RANDOM or SYSTEMATIC error types.
258
+ """
259
+ return EErrType.SYSTEMATIC
260
+
261
+ def calc_errs(self,
262
+ err_basis: np.ndarray,
263
+ sens_data: SensorData,
264
+ ) -> tuple[np.ndarray, SensorData]:
265
+ """Calculates the error array based on the size of the input.
266
+
267
+ Parameters
268
+ ----------
269
+ err_basis : np.ndarray
270
+ Array of values with the same dimensions as the sensor measurement
271
+ matrix.
272
+ sens_data : SensorData
273
+ The accumulated sensor state data for all errors prior to this one.
274
+
275
+ Returns
276
+ -------
277
+ tuple[np.ndarray, SensorData]
278
+ Tuple containing the calculated error array and pass through of the
279
+ sensor data object as it is not modified by this class. The returned
280
+ error array has the same shape as the input error basis.
281
+ """
282
+ err_shape = np.array(err_basis.shape)
283
+ err_shape[-1] = 1
284
+ sys_errs = self._rng.uniform(low=self._low,
285
+ high=self._high,
286
+ size=err_shape)
287
+
288
+ tile_shape = np.array(err_basis.shape)
289
+ tile_shape[0:-1] = 1
290
+ sys_errs = np.tile(sys_errs,tuple(tile_shape))
291
+
292
+ return (sys_errs,sens_data)
293
+
294
+
295
+ class ErrSysUnifPercent(IErrCalculator):
296
+ """Systematic error calculator for applying a percentage offset to each
297
+ sensor that is sampled from a uniform probability distribution specified by
298
+ its upper and lower bounds.
299
+
300
+ The percentage offset is calculated based on the ground truth if the error
301
+ dependence is `INDEPENDENT` or based on the accumulated sensor measurement
302
+ if the dependence is `DEPENDENT`.
303
+
304
+ Implements the `IErrCalculator` interface.
305
+ """
306
+ __slots__ = ("_low","_high","_rng","_err_dep")
307
+
308
+ def __init__(self,
309
+ low_percent: float,
310
+ high_percent: float,
311
+ err_dep: EErrDep = EErrDep.INDEPENDENT,
312
+ seed: int | None = None) -> None:
313
+ """
314
+ Parameters
315
+ ----------
316
+ low_percent : float
317
+ Lower percentage bound for the uniform probability distribution.
318
+ high_percent : float
319
+ Upper percentage bound for the uniform probability distribution.
320
+ err_dep : EErrDependence, optional
321
+ Error calculation dependence, by default EErrDependence.INDEPENDENT
322
+ seed : int | None, optional
323
+ Optional seed for the random generator to allow for replicable
324
+ behaviour, by default None.
325
+ """
326
+ self._low = low_percent/100
327
+ self._high = high_percent/100
328
+ self._rng = np.random.default_rng(seed)
329
+ self._err_dep = err_dep
330
+
331
+ def get_error_dep(self) -> EErrDep:
332
+ """Gets the error dependence state for this error calculator. An
333
+ independent error is calculated based on the input truth values as the
334
+ error basis. A dependent error is calculated based on the accumulated
335
+ sensor reading from all preceeding errors in the chain.
336
+
337
+ Returns
338
+ -------
339
+ EErrDependence
340
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
341
+ """
342
+ return self._err_dep
343
+
344
+ def set_error_dep(self, dependence: EErrDep) -> None:
345
+ """Sets the error dependence state for this error calculator. An
346
+ independent error is calculated based on the input truth values as the
347
+ error basis. A dependent error is calculated based on the accumulated
348
+ sensor reading from all preceeding errors in the chain.
349
+
350
+ Parameters
351
+ ----------
352
+ dependence : EErrDependence
353
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
354
+ """
355
+ self._err_dep = dependence
356
+
357
+ def get_error_type(self) -> EErrType:
358
+ """Gets the error type.
359
+
360
+ Returns
361
+ -------
362
+ EErrType
363
+ Enumeration definining RANDOM or SYSTEMATIC error types.
364
+ """
365
+ return EErrType.SYSTEMATIC
366
+
367
+ def calc_errs(self,
368
+ err_basis: np.ndarray,
369
+ sens_data: SensorData,
370
+ ) -> tuple[np.ndarray, SensorData]:
371
+ """Calculates the error array based on the size of the input.
372
+
373
+ Parameters
374
+ ----------
375
+ err_basis : np.ndarray
376
+ Array of values with the same dimensions as the sensor measurement
377
+ matrix.
378
+ sens_data : SensorData
379
+ The accumulated sensor state data for all errors prior to this one.
380
+
381
+ Returns
382
+ -------
383
+ tuple[np.ndarray, SensorData]
384
+ Tuple containing the calculated error array and pass through of the
385
+ sensor data object as it is not modified by this class. The returned
386
+ error array has the same shape as the input error basis.
387
+ """
388
+ err_shape = np.array(err_basis.shape)
389
+ err_shape[-1] = 1
390
+ sys_errs = self._rng.uniform(low=self._low,
391
+ high=self._high,
392
+ size=err_shape)
393
+
394
+ tile_shape = np.array(err_basis.shape)
395
+ tile_shape[0:-1] = 1
396
+ sys_errs = np.tile(sys_errs,tuple(tile_shape))
397
+
398
+ return (err_basis*sys_errs,sens_data)
399
+
400
+
401
+ class ErrSysNorm(IErrCalculator):
402
+ """Systematic error calculator for applying an offset to each individual
403
+ sensor in the array based on sampling from a normal distribution specified
404
+ by its standard deviation and mean. Note that the offset is constant for
405
+ each sensor over time. Implements the `IErrCalculator` interface.
406
+ """
407
+ __slots__ = ("_std","_rng","_err_dep")
408
+
409
+ def __init__(self,
410
+ std: float,
411
+ mean: float = 0.0,
412
+ err_dep: EErrDep = EErrDep.INDEPENDENT,
413
+ seed: int | None = None) -> None:
414
+ """
415
+ Parameters
416
+ ----------
417
+ std : float
418
+ Standard deviation of the normal distribution to sample.
419
+ mean : float, optional
420
+ Mean of the normal distribution to sample, by default 0.0.
421
+ err_dep : EErrDependence, optional
422
+ Error calculation dependence, by default EErrDependence.INDEPENDENT.
423
+ seed : int | None, optional
424
+ Optional seed for the random generator to allow for replicable
425
+ behaviour, by default None.
426
+ """
427
+ self._std = std
428
+ self._mean = mean
429
+ self._rng = np.random.default_rng(seed)
430
+ self._err_dep = err_dep
431
+
432
+ def get_error_dep(self) -> EErrDep:
433
+ """Gets the error dependence state for this error calculator. An
434
+ independent error is calculated based on the input truth values as the
435
+ error basis. A dependent error is calculated based on the accumulated
436
+ sensor reading from all preceeding errors in the chain.
437
+
438
+ NOTE: for this error the calculation is independent regardless of this
439
+ setting as the offset is constant.
440
+
441
+ Returns
442
+ -------
443
+ EErrDependence
444
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
445
+ """
446
+ return self._err_dep
447
+
448
+ def set_error_dep(self, dependence: EErrDep) -> None:
449
+ """Sets the error dependence state for this error calculator. An
450
+ independent error is calculated based on the input truth values as the
451
+ error basis. A dependent error is calculated based on the accumulated
452
+ sensor reading from all preceeding errors in the chain.
453
+
454
+ NOTE: for this error the calculation is independent regardless of this
455
+ setting as the offset is constant.
456
+
457
+ Parameters
458
+ ----------
459
+ dependence : EErrDependence
460
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
461
+ """
462
+ self._err_dep = dependence
463
+
464
+ def get_error_type(self) -> EErrType:
465
+ """Gets the error type.
466
+
467
+ Returns
468
+ -------
469
+ EErrType
470
+ Enumeration definining RANDOM or SYSTEMATIC error types.
471
+ """
472
+ return EErrType.SYSTEMATIC
473
+
474
+ def calc_errs(self,
475
+ err_basis: np.ndarray,
476
+ sens_data: SensorData,
477
+ ) -> tuple[np.ndarray, SensorData]:
478
+ """Calculates the error array based on the size of the input.
479
+
480
+ Parameters
481
+ ----------
482
+ err_basis : np.ndarray
483
+ Array of values with the same dimensions as the sensor measurement
484
+ matrix.
485
+ sens_data : SensorData
486
+ The accumulated sensor state data for all errors prior to this one.
487
+
488
+ Returns
489
+ -------
490
+ tuple[np.ndarray, SensorData]
491
+ Tuple containing the calculated error array and pass through of the
492
+ sensor data object as it is not modified by this class. The returned
493
+ error array has the same shape as the input error basis.
494
+ """
495
+ err_shape = np.array(err_basis.shape)
496
+ err_shape[-1] = 1
497
+ sys_errs = self._rng.normal(loc=self._mean,
498
+ scale=self._std,
499
+ size=err_shape)
500
+
501
+ tile_shape = np.array(err_basis.shape)
502
+ tile_shape[0:-1] = 1
503
+ sys_errs = np.tile(sys_errs,tuple(tile_shape))
504
+
505
+ return (sys_errs,sens_data)
506
+
507
+
508
+ class ErrSysNormPercent(IErrCalculator):
509
+ """Systematic error calculator for applying a percentage offset to each
510
+ individual sensor in the array based on sampling from a normal distribution
511
+ specified by its standard deviation and mean. Note that the offset is
512
+ constant for each sensor over time.
513
+
514
+ The percentage offset is calculated based on the ground truth if the error
515
+ dependence is `INDEPENDENT` or based on the accumulated sensor measurement
516
+ if the dependence is `DEPENDENT`.
517
+
518
+ Implements the `IErrCalculator` interface.
519
+ """
520
+ __slots__ = ("_std","_rng","_err_dep")
521
+
522
+ def __init__(self,
523
+ std_percent: float,
524
+ err_dep: EErrDep = EErrDep.INDEPENDENT,
525
+ seed: int | None = None) -> None:
526
+ """
527
+ Parameters
528
+ ----------
529
+ std_percent : float
530
+ Standard deviation as a percentage in the range (0,100).
531
+ err_dep : EErrDependence, optional
532
+ Error calculation dependence, by default EErrDependence.INDEPENDENT.
533
+ seed : int | None, optional
534
+ Optional seed for the random generator to allow for replicable
535
+ behaviour, by default None.
536
+ """
537
+ self._std = std_percent/100
538
+ self._rng = np.random.default_rng(seed)
539
+ self._err_dep = err_dep
540
+
541
+ def get_error_dep(self) -> EErrDep:
542
+ """Gets the error dependence state for this error calculator. An
543
+ independent error is calculated based on the input truth values as the
544
+ error basis. A dependent error is calculated based on the accumulated
545
+ sensor reading from all preceeding errors in the chain.
546
+
547
+ Returns
548
+ -------
549
+ EErrDependence
550
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
551
+ """
552
+ return self._err_dep
553
+
554
+ def set_error_dep(self, dependence: EErrDep) -> None:
555
+ """Sets the error dependence state for this error calculator. An
556
+ independent error is calculated based on the input truth values as the
557
+ error basis. A dependent error is calculated based on the accumulated
558
+ sensor reading from all preceeding errors in the chain.
559
+
560
+ Parameters
561
+ ----------
562
+ dependence : EErrDependence
563
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
564
+ """
565
+ self._err_dep = dependence
566
+
567
+ def get_error_type(self) -> EErrType:
568
+ """Gets the error type.
569
+
570
+ Returns
571
+ -------
572
+ EErrType
573
+ Enumeration definining RANDOM or SYSTEMATIC error types.
574
+ """
575
+ return EErrType.SYSTEMATIC
576
+
577
+ def calc_errs(self,
578
+ err_basis: np.ndarray,
579
+ sens_data: SensorData,
580
+ ) -> tuple[np.ndarray, SensorData]:
581
+ """Calculates the error array based on the size of the input.
582
+
583
+ Parameters
584
+ ----------
585
+ err_basis : np.ndarray
586
+ Array of values with the same dimensions as the sensor measurement
587
+ matrix.
588
+ sens_data : SensorData
589
+ The accumulated sensor state data for all errors prior to this one.
590
+
591
+ Returns
592
+ -------
593
+ tuple[np.ndarray, SensorData]
594
+ Tuple containing the calculated error array and pass through of the
595
+ sensor data object as it is not modified by this class. The returned
596
+ error array has the same shape as the input error basis.
597
+ """
598
+
599
+ err_shape = np.array(err_basis.shape)
600
+ err_shape[-1] = 1
601
+ sys_errs = self._rng.normal(loc=0.0,
602
+ scale=self._std,
603
+ size=err_shape)
604
+
605
+ tile_shape = np.array(err_basis.shape)
606
+ tile_shape[0:-1] = 1
607
+ sys_errs = np.tile(sys_errs,tuple(tile_shape))
608
+
609
+ return (err_basis*sys_errs,sens_data)
610
+
611
+
612
+ class ErrSysGen(IErrCalculator):
613
+ """Systematic error calculator for applying a unique offset to each sensor
614
+ by sample from a user specified probability distribution (an implementation
615
+ of the `IGeneratorRandom` interface).
616
+
617
+ Implements the `IErrCalculator` interface.
618
+ """
619
+ __slots__ = ("_generator","_err_dep")
620
+
621
+ def __init__(self,
622
+ generator: IGenRandom,
623
+ err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
624
+ """
625
+ Parameters
626
+ ----------
627
+ generator : IGenRandom
628
+ Random generator object used to calculate the systematic error in
629
+ simulation units.
630
+ err_dep : EErrDependence, optional
631
+ Error calculation dependence, by default EErrDependence.INDEPENDENT.
632
+ """
633
+ self._generator = generator
634
+ self._err_dep = err_dep
635
+
636
+ def get_error_dep(self) -> EErrDep:
637
+ """Gets the error dependence state for this error calculator. An
638
+ independent error is calculated based on the input truth values as the
639
+ error basis. A dependent error is calculated based on the accumulated
640
+ sensor reading from all preceeding errors in the chain.
641
+
642
+ Returns
643
+ -------
644
+ EErrDependence
645
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
646
+ """
647
+ return self._err_dep
648
+
649
+ def set_error_dep(self, dependence: EErrDep) -> None:
650
+ """Sets the error dependence state for this error calculator. An
651
+ independent error is calculated based on the input truth values as the
652
+ error basis. A dependent error is calculated based on the accumulated
653
+ sensor reading from all preceeding errors in the chain.
654
+
655
+ Parameters
656
+ ----------
657
+ dependence : EErrDependence
658
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
659
+ """
660
+ self._err_dep = dependence
661
+
662
+ def get_error_type(self) -> EErrType:
663
+ """Gets the error type.
664
+
665
+ Returns
666
+ -------
667
+ EErrType
668
+ Enumeration definining RANDOM or SYSTEMATIC error types.
669
+ """
670
+ return EErrType.SYSTEMATIC
671
+
672
+ def calc_errs(self,
673
+ err_basis: np.ndarray,
674
+ sens_data: SensorData,
675
+ ) -> tuple[np.ndarray, SensorData]:
676
+ """Calculates the error array based on the size of the input.
677
+
678
+ Parameters
679
+ ----------
680
+ err_basis : np.ndarray
681
+ Array of values with the same dimensions as the sensor measurement
682
+ matrix.
683
+ sens_data : SensorData
684
+ The accumulated sensor state data for all errors prior to this one.
685
+
686
+ Returns
687
+ -------
688
+ tuple[np.ndarray, SensorData]
689
+ Tuple containing the calculated error array and pass through of the
690
+ sensor data object as it is not modified by this class. The returned
691
+ error array has the same shape as the input error basis.
692
+ """
693
+ err_shape = np.array(err_basis.shape)
694
+ err_shape[-1] = 1
695
+
696
+ sys_errs = self._generator.generate(shape=err_shape)
697
+
698
+ tile_shape = np.array(err_basis.shape)
699
+ tile_shape[0:-1] = 1
700
+ sys_errs = np.tile(sys_errs,tuple(tile_shape))
701
+
702
+ return (sys_errs,sens_data)
703
+
704
+
705
+ class ErrSysGenPercent(IErrCalculator):
706
+ """Systematic error calculator for applying a unique percentage offset to
707
+ each sensor by sample from a user specified probability distribution (an
708
+ implementation of the `IGeneratorRandom` interface). This class assumes the
709
+ random generator is for a percentage error based on the input error basis
710
+ and therefore it supports error dependence.
711
+
712
+ The percentage error is calculated based on the ground truth if the error
713
+ dependence is `INDEPENDENT` or based on the accumulated sensor measurement
714
+ if the dependence is `DEPENDENT`.
715
+
716
+ Implements the `IErrCalculator` interface.
717
+ """
718
+ __slots__ = ("_generator","_err_dep")
719
+
720
+ def __init__(self,
721
+ generator: IGenRandom,
722
+ err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
723
+ """
724
+ Parameters
725
+ ----------
726
+ generator : IGenRandom
727
+ Random generator which returns a percentage error in the range
728
+ (0,100)
729
+ err_dep : EErrDep, optional
730
+ Error calculation dependence, by default EErrDep.INDEPENDENT
731
+ """
732
+ self._generator = generator
733
+ self._err_dep = err_dep
734
+
735
+ def get_error_dep(self) -> EErrDep:
736
+ """Gets the error dependence state for this error calculator. An
737
+ independent error is calculated based on the input truth values as the
738
+ error basis. A dependent error is calculated based on the accumulated
739
+ sensor reading from all preceeding errors in the chain.
740
+
741
+ Returns
742
+ -------
743
+ EErrDependence
744
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
745
+ """
746
+ return self._err_dep
747
+
748
+ def set_error_dep(self, dependence: EErrDep) -> None:
749
+ """Sets the error dependence state for this error calculator. An
750
+ independent error is calculated based on the input truth values as the
751
+ error basis. A dependent error is calculated based on the accumulated
752
+ sensor reading from all preceeding errors in the chain.
753
+
754
+ Parameters
755
+ ----------
756
+ dependence : EErrDependence
757
+ Enumeration defining INDEPENDENT or DEPENDENT behaviour.
758
+ """
759
+ self._err_dep = dependence
760
+
761
+ def get_error_type(self) -> EErrType:
762
+ """Gets the error type.
763
+
764
+ Returns
765
+ -------
766
+ EErrType
767
+ Enumeration definining RANDOM or SYSTEMATIC error types.
768
+ """
769
+ return EErrType.SYSTEMATIC
770
+
771
+ def calc_errs(self,
772
+ err_basis: np.ndarray,
773
+ sens_data: SensorData,
774
+ ) -> tuple[np.ndarray, SensorData]:
775
+ """Calculates the error array based on the size of the input.
776
+
777
+ Parameters
778
+ ----------
779
+ err_basis : np.ndarray
780
+ Array of values with the same dimensions as the sensor measurement
781
+ matrix.
782
+ sens_data : SensorData
783
+ The accumulated sensor state data for all errors prior to this one.
784
+
785
+ Returns
786
+ -------
787
+ tuple[np.ndarray, SensorData]
788
+ Tuple containing the calculated error array and pass through of the
789
+ sensor data object as it is not modified by this class. The returned
790
+ error array has the same shape as the input error basis.
791
+ """
792
+ err_shape = np.array(err_basis.shape)
793
+ err_shape[-1] = 1
794
+
795
+ sys_errs = self._generator.generate(shape=err_shape)
796
+ # Convert percent to decimal
797
+ sys_errs = sys_errs/100.0
798
+
799
+ tile_shape = np.array(err_basis.shape)
800
+ tile_shape[0:-1] = 1
801
+ sys_errs = np.tile(sys_errs,tuple(tile_shape))
802
+ sys_errs = err_basis * sys_errs
803
+
804
+ return (sys_errs,sens_data)
805
+
806
+
807
+
808
+