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
pyvale/dataset.py ADDED
@@ -0,0 +1,325 @@
1
+ #===============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ #===============================================================================
6
+
7
+ """
8
+ Accesors for data that comes pre-packaged with pyvale for demonstrating its
9
+ functionality. This includes moose simulation outputs as exodus files, input
10
+ files for moose and gmsh for additional simulation cases, and images required
11
+ for testing the image deformation and digital image correlation modules.
12
+ """
13
+
14
+ from enum import Enum
15
+ from pathlib import Path
16
+ from importlib.resources import files
17
+
18
+
19
+ SIM_CASE_COUNT = 26
20
+ """Constant describing the number of simulation test case input files for moose
21
+ and gmsh that come packaged with pyvale.
22
+ """
23
+
24
+ class EElemTest(Enum):
25
+ """Enumeration used to specify different 3D element types for extracting
26
+ specific test simulation datasets.
27
+ """
28
+
29
+ TET4 = "TET4"
30
+ """Tetrahedral element, linear with 4 nodes.
31
+ """
32
+
33
+ TET10 = "TET10"
34
+ """Tetrahedral element, quadratic with 10 nodes.
35
+ """
36
+
37
+ TET14 = "TET14"
38
+ """Tetrahedral element, quadratic with 14 nodes.
39
+ """
40
+
41
+ HEX8 = "HEX8"
42
+ """Hexahedral element, linear with 8 nodes.
43
+ """
44
+
45
+ HEX20 = "HEX20"
46
+ """Hexahedral element, quadratic with 20 nodes.
47
+ """
48
+
49
+ HEX27 = "HEX27"
50
+ """Hexahedral element, quadratic with 27 nodes.
51
+ """
52
+
53
+ def __str__(self):
54
+ return self.value
55
+
56
+
57
+ class DataSetError(Exception):
58
+ """Custom error class for file io errors associated with retrieving datasets
59
+ and files packaged with pyvale.
60
+ """
61
+
62
+
63
+ class DataSet:
64
+ @staticmethod
65
+ def sim_case_input_file_path(case_num: int) -> Path:
66
+ """Gets the path to MOOSE input file (*.i) for a particular simulation
67
+ case.
68
+
69
+ Parameters
70
+ ----------
71
+ case_num : int
72
+ Integer defining the case number to be retrieved. Must be greater
73
+ than 0 and less than the number of simulation cases.
74
+
75
+ Returns
76
+ -------
77
+ Path
78
+ Path object to the MOOSE *.i file for the selected simulation case.
79
+
80
+ Raises
81
+ ------
82
+ DataSetError
83
+ Raised if an invalid simulation case number is specified.
84
+ """
85
+ if case_num <= 0:
86
+ raise DataSetError("Simulation case number must be greater than 0")
87
+ elif case_num > SIM_CASE_COUNT:
88
+ raise DataSetError("Simulation case number must be less than " \
89
+ + f"{SIM_CASE_COUNT}")
90
+
91
+ case_num_str = str(case_num).zfill(2)
92
+ case_file = f"case{case_num_str}.i"
93
+ return Path(files("pyvale.simcases").joinpath(case_file))
94
+
95
+
96
+ @staticmethod
97
+ def sim_case_gmsh_file_path(case_num: int) -> Path | None:
98
+ """Gets the path to Gmsh input file (*.geo) for a particular simulation
99
+ case. Note that not all simulation cases use Gmsh for geometry and mesh
100
+ generation. If the specified simulation case does not have an associated
101
+ Gmsh *.geo file. In this case 'None' is returned
102
+
103
+ Parameters
104
+ ----------
105
+ case_num : int
106
+ Integer defining the case number to be retrieved. Must be greater
107
+ than 0 and less than the number of simulation cases.
108
+
109
+ Returns
110
+ -------
111
+ Path | None
112
+ Path object to the Gmsh *.geo file for the selected simulation case.
113
+ Returns None if there is no *.geo for this simulation case.
114
+
115
+ Raises
116
+ ------
117
+ DataSetError
118
+ Raised if an invalid simulation case number is specified.
119
+ """
120
+ if case_num <= 0:
121
+ raise DataSetError("Simulation case number must be greater than 0")
122
+ elif case_num > SIM_CASE_COUNT:
123
+ raise DataSetError("Simulation case number must be less than " \
124
+ + f"{SIM_CASE_COUNT}")
125
+
126
+ case_num_str = str(case_num).zfill(2)
127
+ case_file = f"case{case_num_str}.geo"
128
+ case_path = Path(files("pyvale.simcases").joinpath(case_file))
129
+
130
+ if case_path.is_file():
131
+ return case_path
132
+
133
+ return None
134
+
135
+
136
+ @staticmethod
137
+ def dic_pattern_5mpx_path() -> Path:
138
+ """Path to a 5 mega-pixel speckle pattern image (2464 x 2056 pixels)
139
+ with 8 bit resolution stored as a *.tiff. Speckles are sampled by
140
+ 5 pixels. A gaussian blur has been applied to the image to remove sharp
141
+ transitions from black to white.
142
+
143
+ Path
144
+ Path to the *.tiff file containing the speckle pattern.
145
+ """
146
+ return Path(files("pyvale.data")
147
+ .joinpath("optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff"))
148
+
149
+ @staticmethod
150
+ def thermal_2d_path() -> Path:
151
+ """Path to a MOOSE simulation output in exodus format. This case is a
152
+ thermal problem solving for a scalar temperature field. The geometry is
153
+ a 2D plate (in x,y) with a heat flux applied on one edge and a heat
154
+ transfer coefficient applied on the opposite edge inducing a temperature
155
+ gradient along the x axis of the plate.
156
+
157
+ The simulation parameters can be found in the corresponding MOOSE input
158
+ file: case13.i which can be retrieved using `sim_ca_summary_
159
+
160
+ Parameters
161
+ ----------
162
+ Exception : _type_
163
+ _description_se_input_file_path`
164
+ in this class.
165
+
166
+ Returns
167
+ -------
168
+ Path
169
+ Path to the exodus (*.e) output file for this simulation case.
170
+ """
171
+ return Path(files("pyvale.data").joinpath("case13_out.e"))
172
+
173
+ @staticmethod
174
+ def thermal_3d_path() -> Path:
175
+ """Path to a MOOSE simulation output in exodus format. This case is a 3D
176
+ thermal problem solving for a scalar temperature field. The model is a
177
+ divertor armour monoblock composed of a tungsten block bonded to a
178
+ copper-chromium-zirconium pipe with a pure copper interlayer. A heat
179
+ flux is applied to the top surface of the block and a heat transfer
180
+ coefficient for cooling water is applied to the inner surface of the
181
+ pipe inducing a temperature gradient from the top of the block to the
182
+ pipe.
183
+
184
+ The simulation parameters can be found in the corresponding MOOSE input
185
+ file: case16.i which can be retrieved using `sim_case_input_file_path`
186
+ in this class. Note that this case uses a Gmsh *.geo file for geometry
187
+ and mesh creation.
188
+
189
+ Returns
190
+ -------
191
+ Path
192
+ Path to the exodus (*.e) output file for this simulation case.
193
+ """
194
+ return Path(files("pyvale.data").joinpath("case16_out.e"))
195
+
196
+ @staticmethod
197
+ def mechanical_2d_path() -> Path:
198
+ """Path to a MOOSE simulation output in exodus format. This case is a 2D
199
+ plate with a hole in the center with the bottom edge fixed and a
200
+ displacement applied to the top edge. This is a mechanical problem and
201
+ solves for the displacement vector field and the tensorial strain field.
202
+
203
+ The simulation parameters can be found in the corresponding MOOSE input
204
+ file: case17.i which can be retrieved using `sim_case_input_file_path`
205
+ in this class. Note that this case uses a Gmsh *.geo file for geometry
206
+ and mesh creation.
207
+
208
+ Returns
209
+ -------
210
+ Path
211
+ Path to the exodus (*.e) output file for this simulation case.
212
+ """
213
+ return Path(files("pyvale.data").joinpath("case17_out.e"))
214
+
215
+ @staticmethod
216
+ def thermomechanical_2d_path() -> Path:
217
+ """Path to a MOOSE simulation output in exodus format. This case is a
218
+ thermo-mechanical analysis of a 2D plate with a heat flux applied on one
219
+ edge and a heat transfer coefficient applied on the opposing edge. The
220
+ mechanical deformation results from thermal expansion due to the imposed
221
+ temperature gradient. This model is solved for the scalar temperature
222
+ field, vector displacement and tensor strain field.
223
+
224
+ Returns
225
+ -------
226
+ Path
227
+ Path to the exodus (*.e) output file for this simulation case.
228
+ """
229
+ return Path(files("pyvale.data").joinpath("case18_1_out.e"))
230
+
231
+ @staticmethod
232
+ def thermomechanical_3d_path() -> Path:
233
+ """Path to a MOOSE simulation output in exodus format. This case is a
234
+ thermo-mechanical analysis of a 3D monoblock divertor armour with a heat
235
+ flux applied on the top surface and a heat transfer coefficient applied
236
+ on the inner surface of the pipe. The mechanical deformation results
237
+ from thermal expansion due to the imposed temperature gradient.
238
+ This model is solved for the scalar temperature field, vector
239
+ displacement and tensor strain field.
240
+
241
+ Returns
242
+ -------
243
+ Path
244
+ Path to the exodus (*.e) output file for this simulation case.
245
+ """
246
+ return Path(files("pyvale.data").joinpath("case16_out.e"))
247
+
248
+ @staticmethod
249
+ def thermomechanical_2d_experiment_paths() -> list[Path]:
250
+ """Path to a MOOSE simulation output in exodus format. This case is a
251
+ thermo-mechanical analysis of a 2D plate with a heat flux applied on one
252
+ edge and a heat transfer coefficient applied on the opposing edge. The
253
+ mechanical deformation results from thermal expansion due to the imposed
254
+ temperature gradient. This model is solved for the scalar temperature
255
+ field, vector temperature and tensor strain field.
256
+
257
+ Here we analyse 3 separate experiments where the thermal conductivity of
258
+ the material is perturbed from the nominal case by +/-10%.
259
+
260
+ The simulation parameters can be found in the corresponding MOOSE input
261
+ file: case18.i which can be retrieved using `sim_case_input_file_path`
262
+ in this class.
263
+
264
+ Returns
265
+ -------
266
+ Path
267
+ Path to the exodus (*.e) output file for this simulation case.
268
+ """
269
+ return [Path(files("pyvale.data").joinpath("case18_1_out.e")),
270
+ Path(files("pyvale.data").joinpath("case18_2_out.e")),
271
+ Path(files("pyvale.data").joinpath("case18_3_out.e"))]
272
+
273
+ @staticmethod
274
+ def render_mechanical_3d_path() -> Path:
275
+ """Path to a MOOSE simulation output in exodus format. This case is a
276
+ purely mechanical test case in 3D meant for testing image rendering
277
+ algorithms for digital image correlation simulation. The simulation
278
+ consists of a linear elastic thin plate with a hole loaded in tension.
279
+ The simulation uses linear tetrahedral elements for rendering tests.
280
+
281
+ Returns
282
+ -------
283
+ Path
284
+ Path to the exodus (*.e) output file for this simulation case.
285
+ """
286
+ return Path(files("pyvale.data").joinpath("case26_out.e"))
287
+
288
+ @staticmethod
289
+ def render_simple_block_path() -> Path:
290
+ """Path to a MOOSE simulation output in exodus format. This case is a
291
+ a simple rectangular block in 3D loaded in tension. It uses a minimum
292
+ number of elements and is intended purely for testing image rendering
293
+ algorithms. This simulation uses linear tetrahedral elements.
294
+
295
+ Returns
296
+ -------
297
+ Path
298
+ Path to the exodus (*.e) output file for this simulation case.
299
+ """
300
+ return Path(files("pyvale.data").joinpath("case25_out.e"))
301
+
302
+ @staticmethod
303
+ def element_case_path(elem_type: EElemTest) -> Path:
304
+ """Path to a MOOSE simulation output in exodus format. This case is a
305
+ 10mm cube undergoing thermo-mechanical loading solved for the
306
+ temperature displacement and strain fields. This case is solved using a
307
+ variety of tetrahedral and hexahedral elements with linear or quadratic
308
+ shapes functions. These simulation cases are intended for testing
309
+ purposes and contain a minimal number of elements.
310
+
311
+ Parameters
312
+ ----------
313
+ elem_type : EElemTest
314
+ Enumeration specifying the element type for this test case.
315
+
316
+ Returns
317
+ -------
318
+ Path
319
+ Path to the exodus (*.e) output file for this simulation case.
320
+ """
321
+ return Path(files("pyvale.data")
322
+ .joinpath(f"case00_{elem_type.value}_out.e"))
323
+
324
+
325
+
@@ -0,0 +1,109 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ import enum
8
+ from abc import ABC, abstractmethod
9
+ import numpy as np
10
+ from pyvale.sensordata import SensorData
11
+
12
+
13
+ class EErrType(enum.Enum):
14
+ """Enumeration defining the error type for separation of error types for
15
+ later analysis.
16
+
17
+ EErrType.SYSTEMATIC:
18
+ Also known as an epistemic error and is due to a lack of
19
+ knowledge. Common examples include spatial or temporal averaging,
20
+ digitisation / round off error and calibration errors.
21
+
22
+ EErrType.RANDOM:
23
+ Also known as aleatory error and is generally a result of sensor
24
+ noise.
25
+ """
26
+ SYSTEMATIC = enum.auto()
27
+ RANDOM = enum.auto()
28
+
29
+
30
+ class EErrDep(enum.Enum):
31
+ """Enumeration defining error dependence.
32
+
33
+ EErrDep.INDEPENDENT:
34
+ Errors are calculated based on the ground truth sensor values
35
+ interpolated from the input simulation.
36
+
37
+ EErrDep.DEPENDENT:
38
+ Errors are calculated based on the accumulated sensor reading due
39
+ to all preceeding errors in the chain.
40
+ """
41
+ INDEPENDENT = enum.auto()
42
+ DEPENDENT = enum.auto()
43
+
44
+
45
+ class IErrCalculator(ABC):
46
+ """Interface (abstract base class) for sensor error calculation allowing for
47
+ chaining of errors.
48
+ """
49
+
50
+ @abstractmethod
51
+ def get_error_type(self) -> EErrType:
52
+ """Abstract method for getting the error type.
53
+
54
+ Returns
55
+ -------
56
+ EErrType
57
+ Enumeration definining RANDOM or SYSTEMATIC error types.
58
+ """
59
+
60
+ @abstractmethod
61
+ def get_error_dep(self) -> EErrDep:
62
+ """Abstract method for getting the error dependence.
63
+
64
+ Returns
65
+ -------
66
+ EErrDependence
67
+ Enumeration definining RANDOM or SYSTEMATIC error types.
68
+ """
69
+
70
+ @abstractmethod
71
+ def set_error_dep(self, dependence: EErrDep) -> None:
72
+ """Abstract method for setting the error dependence.
73
+
74
+ Parameters
75
+ ----------
76
+ dependence : EErrDependence
77
+ Enumeration definining RANDOM or SYSTEMATIC error types.
78
+ """
79
+
80
+ @abstractmethod
81
+ def calc_errs(self,
82
+ err_basis: np.ndarray,
83
+ sens_data: SensorData,
84
+ ) -> tuple[np.ndarray, SensorData]:
85
+ """Abstract method that calculates the error array based on the input
86
+ err_basis array. The output error array will be the same shape as the
87
+ input err_basis array.
88
+
89
+ Parameters
90
+ ----------
91
+ err_basis : np.ndarray
92
+ Used as the base array for calculating the returned error
93
+ sens_data : SensorData
94
+ Sensor data object holding the current sensor state before applying
95
+ this error calculation.
96
+
97
+ Returns
98
+ -------
99
+ tuple[np.ndarray, SensorData]
100
+ Tuple containing the error array from this calculator and a
101
+ SensorData object with the current accumulated sensor state starting
102
+ from the nominal state up to and including this error calculator in
103
+ the error chain. Note that many errors do not modify the sensor data
104
+ so the sensor data class is passed through this function unchanged.
105
+ """
106
+
107
+
108
+
109
+
@@ -0,0 +1,146 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ from abc import ABC, abstractmethod
8
+ import numpy as np
9
+
10
+
11
+ class IDriftCalculator(ABC):
12
+ """Interface (abstract base class) for applying a function to cause a sensor
13
+ array measurement to drift (normally over time). The initialiser for the
14
+ concrete implementation of this class specifies the function parameters and
15
+ a unified method is provided to calculate the drift based on the input.
16
+ """
17
+
18
+ @abstractmethod
19
+ def calc_drift(self, drift_var: np.ndarray) -> np.ndarray:
20
+ """Abstract method. Used to calculate the drift function based on the
21
+ input drift variable (useually the time steps).
22
+
23
+ Parameters
24
+ ----------
25
+ drift_var : np.ndarray
26
+ Array of values (normally time steps) that are used to calculate the
27
+ drift errors.
28
+
29
+ Returns
30
+ -------
31
+ np.ndarray
32
+ Array of drift errors having the same shape as the input drift_var
33
+ array.
34
+ """
35
+
36
+
37
+ class DriftConstant(IDriftCalculator):
38
+ """Class for applying a constant drift error over time.
39
+
40
+ Implements the IDriftCalculator interface.
41
+ """
42
+ __slots__ = ("_offset",)
43
+
44
+ def __init__(self, offset: float) -> None:
45
+ """
46
+ Parameters
47
+ ----------
48
+ offset : float
49
+ Constant drift offset.
50
+ """
51
+ self._offset = offset
52
+
53
+ def calc_drift(self, drift_var: np.ndarray) -> np.ndarray:
54
+ """Calculates the drift errors based on the input drift variable array.
55
+
56
+ Parameters
57
+ ----------
58
+ drift_var : np.ndarray
59
+ Array of values (normally time steps) that are used to calculate the
60
+ drift errors.
61
+
62
+ Returns
63
+ -------
64
+ np.ndarray
65
+ Array of drift errors having the same shape as the input drift_var
66
+ array.
67
+ """
68
+ return self._offset*np.ones_like(drift_var)
69
+
70
+
71
+ class DriftLinear(IDriftCalculator):
72
+ """Class for applying a linear drift error over time.
73
+
74
+ Implements the IDriftCalculator interface.
75
+ """
76
+ __slots__ = ("_slope","_offset")
77
+
78
+ def __init__(self, slope: float, offset: float = 0.0) -> None:
79
+ """
80
+ Parameters
81
+ ----------
82
+ slope : float
83
+ Slope of the drift error function.
84
+ offset : float, optional
85
+ Offset (intercept) of the drift error function, by default 0.0.
86
+ """
87
+ self._slope = slope
88
+ self._offset = offset
89
+
90
+ def calc_drift(self, drift_var: np.ndarray) -> np.ndarray:
91
+ """Calculates the drift errors based on the input drift variable array.
92
+
93
+ Parameters
94
+ ----------
95
+ drift_var : np.ndarray
96
+ Array of values (normally time steps) that are used to calculate the
97
+ drift errors.
98
+
99
+ Returns
100
+ -------
101
+ np.ndarray
102
+ Array of drift errors having the same shape as the input drift_var
103
+ array.
104
+ """
105
+ return self._slope*drift_var + self._offset
106
+
107
+
108
+ class DriftPolynomial(IDriftCalculator):
109
+ """Class for applying a polynomial drift error over time. The coefficients
110
+ of the polynomial are specified with a numpy array from constant term to
111
+ highest power.
112
+
113
+ Implements the IDriftCalculator interface.
114
+ """
115
+
116
+ __slots__ = ("_coeffs",)
117
+
118
+ def __init__(self, coeffs: np.ndarray) -> None:
119
+ """
120
+ Parameters
121
+ ----------
122
+ coeffs : np.ndarray
123
+ Array of polynomial coefficients from constant to highest power.
124
+ """
125
+ self._coeffs = coeffs
126
+
127
+ def calc_drift(self, drift_var: np.ndarray) -> np.ndarray:
128
+ """Calculates the drift errors based on the input drift variable array.
129
+
130
+ Parameters
131
+ ----------
132
+ drift_var : np.ndarray
133
+ Array of values (normally time steps) that are used to calculate the
134
+ drift errors.
135
+
136
+ Returns
137
+ -------
138
+ np.ndarray
139
+ Array of drift errors having the same shape as the input drift_var
140
+ array.
141
+ """
142
+ poly = np.zeros_like(drift_var)
143
+ for ii,cc in enumerate(self._coeffs):
144
+ poly += cc*drift_var**ii
145
+
146
+ return poly