pyvale 2025.4.0__py3-none-any.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.
- pyvale/__init__.py +75 -0
- pyvale/core/__init__.py +7 -0
- pyvale/core/analyticmeshgen.py +59 -0
- pyvale/core/analyticsimdatafactory.py +63 -0
- pyvale/core/analyticsimdatagenerator.py +160 -0
- pyvale/core/camera.py +146 -0
- pyvale/core/cameradata.py +64 -0
- pyvale/core/cameradata2d.py +82 -0
- pyvale/core/cameratools.py +328 -0
- pyvale/core/cython/rastercyth.c +32267 -0
- pyvale/core/cython/rastercyth.py +636 -0
- pyvale/core/dataset.py +250 -0
- pyvale/core/errorcalculator.py +112 -0
- pyvale/core/errordriftcalc.py +146 -0
- pyvale/core/errorintegrator.py +339 -0
- pyvale/core/errorrand.py +614 -0
- pyvale/core/errorsysdep.py +331 -0
- pyvale/core/errorsysfield.py +407 -0
- pyvale/core/errorsysindep.py +905 -0
- pyvale/core/experimentsimulator.py +99 -0
- pyvale/core/field.py +136 -0
- pyvale/core/fieldconverter.py +154 -0
- pyvale/core/fieldsampler.py +112 -0
- pyvale/core/fieldscalar.py +167 -0
- pyvale/core/fieldtensor.py +221 -0
- pyvale/core/fieldtransform.py +384 -0
- pyvale/core/fieldvector.py +215 -0
- pyvale/core/generatorsrandom.py +528 -0
- pyvale/core/imagedef2d.py +566 -0
- pyvale/core/integratorfactory.py +241 -0
- pyvale/core/integratorquadrature.py +192 -0
- pyvale/core/integratorrectangle.py +88 -0
- pyvale/core/integratorspatial.py +90 -0
- pyvale/core/integratortype.py +44 -0
- pyvale/core/optimcheckfuncs.py +153 -0
- pyvale/core/raster.py +31 -0
- pyvale/core/rastercy.py +76 -0
- pyvale/core/rasternp.py +604 -0
- pyvale/core/rendermesh.py +156 -0
- pyvale/core/sensorarray.py +179 -0
- pyvale/core/sensorarrayfactory.py +210 -0
- pyvale/core/sensorarraypoint.py +280 -0
- pyvale/core/sensordata.py +72 -0
- pyvale/core/sensordescriptor.py +101 -0
- pyvale/core/sensortools.py +143 -0
- pyvale/core/visualexpplotter.py +151 -0
- pyvale/core/visualimagedef.py +71 -0
- pyvale/core/visualimages.py +75 -0
- pyvale/core/visualopts.py +180 -0
- pyvale/core/visualsimanimator.py +83 -0
- pyvale/core/visualsimplotter.py +182 -0
- pyvale/core/visualtools.py +81 -0
- pyvale/core/visualtraceplotter.py +256 -0
- pyvale/data/__init__.py +7 -0
- pyvale/data/case13_out.e +0 -0
- pyvale/data/case16_out.e +0 -0
- pyvale/data/case17_out.e +0 -0
- pyvale/data/case18_1_out.e +0 -0
- pyvale/data/case18_2_out.e +0 -0
- pyvale/data/case18_3_out.e +0 -0
- pyvale/data/case25_out.e +0 -0
- pyvale/data/case26_out.e +0 -0
- pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
- pyvale/examples/__init__.py +7 -0
- pyvale/examples/analyticdatagen/__init__.py +7 -0
- pyvale/examples/analyticdatagen/ex1_1_scalarvisualisation.py +38 -0
- pyvale/examples/analyticdatagen/ex1_2_scalarcasebuild.py +46 -0
- pyvale/examples/analyticdatagen/ex2_1_analyticsensors.py +83 -0
- pyvale/examples/ex1_1_thermal2d.py +89 -0
- pyvale/examples/ex1_2_thermal2d.py +111 -0
- pyvale/examples/ex1_3_thermal2d.py +113 -0
- pyvale/examples/ex1_4_thermal2d.py +89 -0
- pyvale/examples/ex1_5_thermal2d.py +105 -0
- pyvale/examples/ex2_1_thermal3d .py +87 -0
- pyvale/examples/ex2_2_thermal3d.py +51 -0
- pyvale/examples/ex2_3_thermal3d.py +109 -0
- pyvale/examples/ex3_1_displacement2d.py +47 -0
- pyvale/examples/ex3_2_displacement2d.py +79 -0
- pyvale/examples/ex3_3_displacement2d.py +104 -0
- pyvale/examples/ex3_4_displacement2d.py +105 -0
- pyvale/examples/ex4_1_strain2d.py +57 -0
- pyvale/examples/ex4_2_strain2d.py +79 -0
- pyvale/examples/ex4_3_strain2d.py +100 -0
- pyvale/examples/ex5_1_multiphysics2d.py +78 -0
- pyvale/examples/ex6_1_multiphysics2d_expsim.py +118 -0
- pyvale/examples/ex6_2_multiphysics3d_expsim.py +158 -0
- pyvale/examples/features/__init__.py +7 -0
- pyvale/examples/features/ex_animation_tools_3dmonoblock.py +83 -0
- pyvale/examples/features/ex_area_avg.py +89 -0
- pyvale/examples/features/ex_calibration_error.py +108 -0
- pyvale/examples/features/ex_chain_field_errs.py +141 -0
- pyvale/examples/features/ex_field_errs.py +78 -0
- pyvale/examples/features/ex_sensor_single_angle_batch.py +110 -0
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +86 -0
- pyvale/examples/rasterisation/ex_rastenp.py +154 -0
- pyvale/examples/rasterisation/ex_rastercyth_oneframe.py +220 -0
- pyvale/examples/rasterisation/ex_rastercyth_static_cypara.py +194 -0
- pyvale/examples/rasterisation/ex_rastercyth_static_pypara.py +193 -0
- pyvale/simcases/case00_HEX20.i +242 -0
- pyvale/simcases/case00_HEX27.i +242 -0
- pyvale/simcases/case00_TET10.i +242 -0
- pyvale/simcases/case00_TET14.i +242 -0
- pyvale/simcases/case01.i +101 -0
- pyvale/simcases/case02.i +156 -0
- pyvale/simcases/case03.i +136 -0
- pyvale/simcases/case04.i +181 -0
- pyvale/simcases/case05.i +234 -0
- pyvale/simcases/case06.i +305 -0
- pyvale/simcases/case07.geo +135 -0
- pyvale/simcases/case07.i +87 -0
- pyvale/simcases/case08.geo +144 -0
- pyvale/simcases/case08.i +153 -0
- pyvale/simcases/case09.geo +204 -0
- pyvale/simcases/case09.i +87 -0
- pyvale/simcases/case10.geo +204 -0
- pyvale/simcases/case10.i +257 -0
- pyvale/simcases/case11.geo +337 -0
- pyvale/simcases/case11.i +147 -0
- pyvale/simcases/case12.geo +388 -0
- pyvale/simcases/case12.i +329 -0
- pyvale/simcases/case13.i +140 -0
- pyvale/simcases/case14.i +159 -0
- pyvale/simcases/case15.geo +337 -0
- pyvale/simcases/case15.i +150 -0
- pyvale/simcases/case16.geo +391 -0
- pyvale/simcases/case16.i +357 -0
- pyvale/simcases/case17.geo +135 -0
- pyvale/simcases/case17.i +144 -0
- pyvale/simcases/case18.i +254 -0
- pyvale/simcases/case18_1.i +254 -0
- pyvale/simcases/case18_2.i +254 -0
- pyvale/simcases/case18_3.i +254 -0
- pyvale/simcases/case19.geo +252 -0
- pyvale/simcases/case19.i +99 -0
- pyvale/simcases/case20.geo +252 -0
- pyvale/simcases/case20.i +250 -0
- pyvale/simcases/case21.geo +74 -0
- pyvale/simcases/case21.i +155 -0
- pyvale/simcases/case22.geo +82 -0
- pyvale/simcases/case22.i +140 -0
- pyvale/simcases/case23.geo +164 -0
- pyvale/simcases/case23.i +140 -0
- pyvale/simcases/case24.geo +79 -0
- pyvale/simcases/case24.i +123 -0
- pyvale/simcases/case25.geo +82 -0
- pyvale/simcases/case25.i +140 -0
- pyvale/simcases/case26.geo +166 -0
- pyvale/simcases/case26.i +140 -0
- pyvale/simcases/run_1case.py +61 -0
- pyvale/simcases/run_all_cases.py +69 -0
- pyvale/simcases/run_build_case.py +64 -0
- pyvale/simcases/run_example_cases.py +69 -0
- pyvale-2025.4.0.dist-info/METADATA +140 -0
- pyvale-2025.4.0.dist-info/RECORD +157 -0
- pyvale-2025.4.0.dist-info/WHEEL +5 -0
- pyvale-2025.4.0.dist-info/licenses/LICENSE +21 -0
- pyvale-2025.4.0.dist-info/top_level.txt +1 -0
pyvale/core/dataset.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from importlib.resources import files
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
SIM_CASE_COUNT = 26
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DataSetError(Exception):
|
|
16
|
+
"""Custom error class for file io errors associated with retrieving datasets
|
|
17
|
+
and files packaged with pyvale.
|
|
18
|
+
"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DataSet:
|
|
23
|
+
"""A static namespace class for handling datasets packaged with pyvale.
|
|
24
|
+
Contains a series of static methods returning a Path object to each data
|
|
25
|
+
file that is packaged with pyvale.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def sim_case_input_file_path(case_num: int) -> Path:
|
|
30
|
+
"""Gets the path to MOOSE input file (*.i) for a particular simulation
|
|
31
|
+
case.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
case_num : int
|
|
36
|
+
Integer defining the case number to be retrieved. Must be greater
|
|
37
|
+
than 0 and less than the number of simulation cases.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
Path
|
|
42
|
+
Path object to the MOOSE *.i file for the selected simulation case.
|
|
43
|
+
|
|
44
|
+
Raises
|
|
45
|
+
------
|
|
46
|
+
DataSetError
|
|
47
|
+
Raised if an invalid simulation case number is specified.
|
|
48
|
+
"""
|
|
49
|
+
if case_num <= 0:
|
|
50
|
+
raise DataSetError("Simulation case number must be greater than 0")
|
|
51
|
+
elif case_num > SIM_CASE_COUNT:
|
|
52
|
+
raise DataSetError("Simulation case number must be less than " \
|
|
53
|
+
+ f"{SIM_CASE_COUNT}")
|
|
54
|
+
|
|
55
|
+
case_num_str = str(case_num).zfill(2)
|
|
56
|
+
case_file = f"case{case_num_str}.i"
|
|
57
|
+
return Path(files("pyvale.simcases").joinpath(case_file))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def sim_case_gmsh_file_path(case_num: int) -> Path | None:
|
|
62
|
+
"""Gets the path to Gmsh input file (*.geo) for a particular simulation
|
|
63
|
+
case. Note that not all simulation cases use Gmsh for geometry and mesh
|
|
64
|
+
generation. If the specified simulation case does not have an associated
|
|
65
|
+
Gmsh *.geo file. In this case 'None' is returned
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
case_num : int
|
|
70
|
+
Integer defining the case number to be retrieved. Must be greater
|
|
71
|
+
than 0 and less than the number of simulation cases.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
Path | None
|
|
76
|
+
Path object to the Gmsh *.geo file for the selected simulation case.
|
|
77
|
+
Returns None if there is no *.geo for this simulation case.
|
|
78
|
+
|
|
79
|
+
Raises
|
|
80
|
+
------
|
|
81
|
+
DataSetError
|
|
82
|
+
Raised if an invalid simulation case number is specified.
|
|
83
|
+
"""
|
|
84
|
+
if case_num <= 0:
|
|
85
|
+
raise DataSetError("Simulation case number must be greater than 0")
|
|
86
|
+
elif case_num > SIM_CASE_COUNT:
|
|
87
|
+
raise DataSetError("Simulation case number must be less than " \
|
|
88
|
+
+ f"{SIM_CASE_COUNT}")
|
|
89
|
+
|
|
90
|
+
case_num_str = str(case_num).zfill(2)
|
|
91
|
+
case_file = f"case{case_num_str}.geo"
|
|
92
|
+
case_path = Path(files("pyvale.simcases").joinpath(case_file))
|
|
93
|
+
|
|
94
|
+
if case_path.is_file():
|
|
95
|
+
return case_path
|
|
96
|
+
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def dic_pattern_5mpx_path() -> Path:
|
|
102
|
+
"""Path to a 5 mega-pixel speckle pattern image (2464 x 2056 pixels)
|
|
103
|
+
with 8 bit resolution stored as a *.tiff. Speckles are sampled by
|
|
104
|
+
5 pixels. A gaussian blur has been applied to the image to remove sharp
|
|
105
|
+
transitions from black to white.
|
|
106
|
+
|
|
107
|
+
Path
|
|
108
|
+
Path to the *.tiff file containing the speckle pattern.
|
|
109
|
+
"""
|
|
110
|
+
return Path(files("pyvale.data")
|
|
111
|
+
.joinpath("optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff"))
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def thermal_2d_path() -> Path:
|
|
115
|
+
"""Path to a MOOSE simulation output in exodus format. This case is a
|
|
116
|
+
thermal problem solving for a scalar temperature field. The geometry is
|
|
117
|
+
a 2D plate (in x,y) with a heat flux applied on one edge and a heat
|
|
118
|
+
transfer coefficient applied on the opposite edge inducing a temperature
|
|
119
|
+
gradient along the x axis of the plate.
|
|
120
|
+
|
|
121
|
+
The simulation parameters can be found in the corresponding MOOSE input
|
|
122
|
+
file: case13.i which can be retrieved using `sim_ca_summary_
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
Exception : _type_
|
|
127
|
+
_description_se_input_file_path`
|
|
128
|
+
in this class.
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
Path
|
|
133
|
+
Path to the exodus (*.e) output file for this simulation case.
|
|
134
|
+
"""
|
|
135
|
+
return Path(files("pyvale.data").joinpath("case13_out.e"))
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def thermal_3d_path() -> Path:
|
|
139
|
+
"""Path to a MOOSE simulation output in exodus format. This case is a 3D
|
|
140
|
+
thermal problem solving for a scalar temperature field. The model is a
|
|
141
|
+
divertor armour monoblock composed of a tungsten block bonded to a
|
|
142
|
+
copper-chromium-zirconium pipe with a pure copper interlayer. A heat
|
|
143
|
+
flux is applied to the top surface of the block and a heat transfer
|
|
144
|
+
coefficient for cooling water is applied to the inner surface of the
|
|
145
|
+
pipe inducing a temperature gradient from the top of the block to the
|
|
146
|
+
pipe.
|
|
147
|
+
|
|
148
|
+
The simulation parameters can be found in the corresponding MOOSE input
|
|
149
|
+
file: case16.i which can be retrieved using `sim_case_input_file_path`
|
|
150
|
+
in this class. Note that this case uses a Gmsh *.geo file for geometry
|
|
151
|
+
and mesh creation.
|
|
152
|
+
|
|
153
|
+
Returns
|
|
154
|
+
-------
|
|
155
|
+
Path
|
|
156
|
+
Path to the exodus (*.e) output file for this simulation case.
|
|
157
|
+
"""
|
|
158
|
+
return Path(files("pyvale.data").joinpath("case16_out.e"))
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def mechanical_2d_path() -> Path:
|
|
162
|
+
"""Path to a MOOSE simulation output in exodus format. This case is a 2D
|
|
163
|
+
plate with a hole in the center with the bottom edge fixed and a
|
|
164
|
+
displacement applied to the top edge. This is a mechanical problem and
|
|
165
|
+
solves for the displacement vector field and the tensorial strain field.
|
|
166
|
+
|
|
167
|
+
The simulation parameters can be found in the corresponding MOOSE input
|
|
168
|
+
file: case17.i which can be retrieved using `sim_case_input_file_path`
|
|
169
|
+
in this class. Note that this case uses a Gmsh *.geo file for geometry
|
|
170
|
+
and mesh creation.
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
Path
|
|
175
|
+
Path to the exodus (*.e) output file for this simulation case.
|
|
176
|
+
"""
|
|
177
|
+
return Path(files("pyvale.data").joinpath("case17_out.e"))
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def thermomechanical_2d_path() -> Path:
|
|
181
|
+
"""Path to a MOOSE simulation output in exodus format. This case is a
|
|
182
|
+
thermo-mechanical analysis of a 2D plate with a heat flux applied on one
|
|
183
|
+
edge and a heat transfer coefficient applied on the opposing edge. The
|
|
184
|
+
mechanical deformation results from thermal expansion due to the imposed
|
|
185
|
+
temperature gradient. This model is solved for the scalar temperature
|
|
186
|
+
field, vector displacement and tensor strain field.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
Path
|
|
191
|
+
Path to the exodus (*.e) output file for this simulation case.
|
|
192
|
+
"""
|
|
193
|
+
return Path(files("pyvale.data").joinpath("case18_1_out.e"))
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def thermomechanical_3d_path() -> Path:
|
|
197
|
+
"""Path to a MOOSE simulation output in exodus format. This case is a
|
|
198
|
+
thermo-mechanical analysis of a 3D monoblock divertor armour with a heat
|
|
199
|
+
flux applied on the top surface and a heat transfer coefficient applied
|
|
200
|
+
on the inner surface of the pipe. The mechanical deformation results
|
|
201
|
+
from thermal expansion due to the imposed temperature gradient.
|
|
202
|
+
This model is solved for the scalar temperature field, vector
|
|
203
|
+
displacement and tensor strain field.
|
|
204
|
+
|
|
205
|
+
Returns
|
|
206
|
+
-------
|
|
207
|
+
Path
|
|
208
|
+
Path to the exodus (*.e) output file for this simulation case.
|
|
209
|
+
"""
|
|
210
|
+
return Path(files("pyvale.data").joinpath("case16_out.e"))
|
|
211
|
+
|
|
212
|
+
@staticmethod
|
|
213
|
+
def thermomechanical_2d_experiment_paths() -> list[Path]:
|
|
214
|
+
"""Path to a MOOSE simulation output in exodus format. This case is a
|
|
215
|
+
thermo-mechanical analysis of a 2D plate with a heat flux applied on one
|
|
216
|
+
edge and a heat transfer coefficient applied on the opposing edge. The
|
|
217
|
+
mechanical deformation results from thermal expansion due to the imposed
|
|
218
|
+
temperature gradient. This model is solved for the scalar temperature
|
|
219
|
+
field, vector temperature and tensor strain field.
|
|
220
|
+
|
|
221
|
+
Here we analyse 3 separate experiments where the thermal conductivity of
|
|
222
|
+
the material is perturbed from the nominal case by +/-10%.
|
|
223
|
+
|
|
224
|
+
The simulation parameters can be found in the corresponding MOOSE input
|
|
225
|
+
file: case18.i which can be retrieved using `sim_case_input_file_path`
|
|
226
|
+
in this class.
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
Path
|
|
231
|
+
Path to the exodus (*.e) output file for this simulation case.
|
|
232
|
+
"""
|
|
233
|
+
return [Path(files("pyvale.data").joinpath("case18_1_out.e")),
|
|
234
|
+
Path(files("pyvale.data").joinpath("case18_2_out.e")),
|
|
235
|
+
Path(files("pyvale.data").joinpath("case18_3_out.e"))]
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def render_mechanical_3d_path() -> Path:
|
|
239
|
+
"""_summary_
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
Path
|
|
244
|
+
_description_
|
|
245
|
+
"""
|
|
246
|
+
return Path(files("pyvale.data").joinpath("case26_out.e"))
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def render_simple_block_path() -> Path:
|
|
250
|
+
return Path(files("pyvale.data").joinpath("case25_out.e"))
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
8
|
+
import enum
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
import numpy as np
|
|
11
|
+
from pyvale.core.sensordata import SensorData
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EErrType(enum.Enum):
|
|
15
|
+
"""Enumeration defining the error type for separation of error types for
|
|
16
|
+
later analysis.
|
|
17
|
+
|
|
18
|
+
EErrType.SYSTEMATIC:
|
|
19
|
+
Also known as an epistemic error and is due to a lack of
|
|
20
|
+
knowledge. Common examples include spatial or temporal averaging,
|
|
21
|
+
digitisation / round off error and calibration errors.
|
|
22
|
+
|
|
23
|
+
EErrType.RANDOM:
|
|
24
|
+
Also known as aleatory error and is generally a result of sensor
|
|
25
|
+
noise.
|
|
26
|
+
"""
|
|
27
|
+
SYSTEMATIC = enum.auto()
|
|
28
|
+
RANDOM = enum.auto()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class EErrDependence(enum.Enum):
|
|
32
|
+
"""Enumeration defining error dependence.
|
|
33
|
+
|
|
34
|
+
EErrDependence.INDEPENDENT:
|
|
35
|
+
Errors are calculated based on the ground truth sensor values
|
|
36
|
+
interpolated from the input simulation.
|
|
37
|
+
|
|
38
|
+
EErrDependence.DEPENDENT:
|
|
39
|
+
Errors are calculated based on the accumulated sensor reading due
|
|
40
|
+
to all preceeding errors in the chain.
|
|
41
|
+
"""
|
|
42
|
+
INDEPENDENT = enum.auto()
|
|
43
|
+
DEPENDENT = enum.auto()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class IErrCalculator(ABC):
|
|
47
|
+
"""Interface (abstract base class) for sensor error calculation allowing for
|
|
48
|
+
chaining of errors.
|
|
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
|
+
pass
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def get_error_dep(self) -> EErrDependence:
|
|
63
|
+
"""Abstract method for getting the error dependence.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
EErrDependence
|
|
68
|
+
Enumeration definining RANDOM or SYSTEMATIC error types.
|
|
69
|
+
"""
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def set_error_dep(self, dependence: EErrDependence) -> None:
|
|
74
|
+
"""Abstract method for setting the error dependence.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
dependence : EErrDependence
|
|
79
|
+
Enumeration definining RANDOM or SYSTEMATIC error types.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def calc_errs(self,
|
|
84
|
+
err_basis: np.ndarray,
|
|
85
|
+
sens_data: SensorData,
|
|
86
|
+
) -> tuple[np.ndarray, SensorData]:
|
|
87
|
+
"""Abstract method that calculates the error array based on the input
|
|
88
|
+
err_basis array. The output error array will be the same shape as the
|
|
89
|
+
input err_basis array.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
err_basis : np.ndarray
|
|
94
|
+
Used as the base array for calculating the returned error
|
|
95
|
+
sens_data : SensorData
|
|
96
|
+
Sensor data object holding the current sensor state before applying
|
|
97
|
+
this error calculation.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
tuple[np.ndarray, SensorData]
|
|
102
|
+
Tuple containing the error array from this calculator and a
|
|
103
|
+
SensorData object with the current accumulated sensor state starting
|
|
104
|
+
from the nominal state up to and including this error calculator in
|
|
105
|
+
the error chain. Note that many errors do not modify the sensor data
|
|
106
|
+
so the sensor data class is passed through this function unchanged.
|
|
107
|
+
"""
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IDriftCalculator(ABC):
|
|
13
|
+
"""Interface (abstract base class) for applying a function to cause a sensor
|
|
14
|
+
array measurement to drift (normally over time). The initialiser for the
|
|
15
|
+
concrete implementation of this class specifies the function parameters and
|
|
16
|
+
a unified method is provided to calculate the drift based on the input.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def calc_drift(self, drift_var: np.ndarray) -> np.ndarray:
|
|
21
|
+
"""Abstract method. Used to calculate the drift function based on the
|
|
22
|
+
input drift variable (useually the time steps).
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
drift_var : np.ndarray
|
|
27
|
+
Array of values (normally time steps) that are used to calculate the
|
|
28
|
+
drift errors.
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
np.ndarray
|
|
33
|
+
Array of drift errors having the same shape as the input drift_var
|
|
34
|
+
array.
|
|
35
|
+
"""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DriftConstant(IDriftCalculator):
|
|
40
|
+
"""Class for applying a constant drift error over time.
|
|
41
|
+
|
|
42
|
+
Implements the IDriftCalculator interface.
|
|
43
|
+
"""
|
|
44
|
+
def __init__(self, offset: float) -> None:
|
|
45
|
+
"""Initialiser for the `DriftConstant` class.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
offset : float
|
|
50
|
+
Constant drift offset.
|
|
51
|
+
"""
|
|
52
|
+
self._offset = offset
|
|
53
|
+
|
|
54
|
+
def calc_drift(self, drift_var: np.ndarray) -> np.ndarray:
|
|
55
|
+
"""Calculates the drift errors based on the input drift variable array.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
drift_var : np.ndarray
|
|
60
|
+
Array of values (normally time steps) that are used to calculate the
|
|
61
|
+
drift errors.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
np.ndarray
|
|
66
|
+
Array of drift errors having the same shape as the input drift_var
|
|
67
|
+
array.
|
|
68
|
+
"""
|
|
69
|
+
return self._offset*np.ones_like(drift_var)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class DriftLinear(IDriftCalculator):
|
|
73
|
+
"""Class for applying a linear drift error over time.
|
|
74
|
+
|
|
75
|
+
Implements the IDriftCalculator interface.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, slope: float, offset: float = 0.0) -> None:
|
|
79
|
+
"""Initialiser for the `DriftLinear` class.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
slope : float
|
|
84
|
+
Slope of the drift error function.
|
|
85
|
+
offset : float, optional
|
|
86
|
+
Offset (intercept) of the drift error function, by default 0.0.
|
|
87
|
+
"""
|
|
88
|
+
self._slope = slope
|
|
89
|
+
self._offset = offset
|
|
90
|
+
|
|
91
|
+
def calc_drift(self, drift_var: np.ndarray) -> np.ndarray:
|
|
92
|
+
"""Calculates the drift errors based on the input drift variable array.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
drift_var : np.ndarray
|
|
97
|
+
Array of values (normally time steps) that are used to calculate the
|
|
98
|
+
drift errors.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
np.ndarray
|
|
103
|
+
Array of drift errors having the same shape as the input drift_var
|
|
104
|
+
array.
|
|
105
|
+
"""
|
|
106
|
+
return self._slope*drift_var + self._offset
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class DriftPolynomial(IDriftCalculator):
|
|
110
|
+
"""Class for applying a polynomial drift error over time. The coefficients
|
|
111
|
+
of the polynomial are specified with a numpy array from constant term to
|
|
112
|
+
highest power.
|
|
113
|
+
|
|
114
|
+
Implements the IDriftCalculator interface.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, coeffs: np.ndarray) -> None:
|
|
118
|
+
"""Initialiser for the `DriftPolynomial` class.
|
|
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
|