pyvale 2025.4.1__py3-none-any.whl → 2025.5.1__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 +18 -3
- pyvale/analyticmeshgen.py +1 -0
- pyvale/analyticsimdatafactory.py +18 -13
- pyvale/analyticsimdatagenerator.py +105 -72
- pyvale/blendercalibrationdata.py +15 -0
- pyvale/blenderlightdata.py +26 -0
- pyvale/blendermaterialdata.py +15 -0
- pyvale/blenderrenderdata.py +30 -0
- pyvale/blenderscene.py +488 -0
- pyvale/blendertools.py +420 -0
- pyvale/camera.py +6 -5
- pyvale/cameradata.py +25 -7
- pyvale/cameradata2d.py +6 -4
- pyvale/camerastereo.py +217 -0
- pyvale/cameratools.py +206 -11
- pyvale/cython/rastercyth.py +6 -2
- pyvale/data/cal_target.tiff +0 -0
- pyvale/dataset.py +73 -14
- pyvale/errorcalculator.py +8 -10
- pyvale/errordriftcalc.py +10 -9
- pyvale/errorintegrator.py +19 -21
- pyvale/errorrand.py +33 -39
- pyvale/errorsyscalib.py +134 -0
- pyvale/errorsysdep.py +19 -22
- pyvale/errorsysfield.py +49 -41
- pyvale/errorsysindep.py +79 -175
- pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
- pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
- pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
- pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
- pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
- pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
- pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
- pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
- pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
- pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
- pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
- pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
- pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
- pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
- pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
- pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
- pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_1_scalarvisualisation.py +6 -9
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_2_scalarcasebuild.py +8 -11
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex2_1_analyticsensors.py +9 -12
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +8 -15
- pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
- pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
- pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
- pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
- pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastenp.py +3 -2
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_oneframe.py +2 -2
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_cypara.py +3 -8
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_pypara.py +6 -7
- pyvale/examples/{ex1_4_thermal2d.py → visualisation/ex1_1_plot_traces.py} +32 -16
- pyvale/examples/{features/ex_animation_tools_3dmonoblock.py → visualisation/ex2_1_animate_sim.py} +37 -31
- pyvale/experimentsimulator.py +107 -30
- pyvale/field.py +2 -9
- pyvale/fieldconverter.py +98 -22
- pyvale/fieldsampler.py +2 -2
- pyvale/fieldscalar.py +10 -10
- pyvale/fieldtensor.py +15 -17
- pyvale/fieldtransform.py +7 -2
- pyvale/fieldvector.py +6 -7
- pyvale/generatorsrandom.py +25 -47
- pyvale/imagedef2d.py +6 -2
- pyvale/integratorfactory.py +2 -2
- pyvale/integratorquadrature.py +50 -24
- pyvale/integratorrectangle.py +85 -7
- pyvale/integratorspatial.py +4 -4
- pyvale/integratortype.py +3 -3
- pyvale/output.py +17 -0
- pyvale/pyvaleexceptions.py +11 -0
- pyvale/raster.py +6 -5
- pyvale/rastercy.py +6 -4
- pyvale/rasternp.py +6 -4
- pyvale/rendermesh.py +6 -2
- pyvale/sensorarray.py +2 -2
- pyvale/sensorarrayfactory.py +52 -65
- pyvale/sensorarraypoint.py +29 -30
- pyvale/sensordata.py +2 -2
- pyvale/sensordescriptor.py +138 -25
- pyvale/sensortools.py +3 -3
- pyvale/simtools.py +67 -0
- pyvale/visualexpplotter.py +99 -57
- pyvale/visualimagedef.py +11 -7
- pyvale/visualimages.py +6 -4
- pyvale/visualopts.py +372 -58
- pyvale/visualsimanimator.py +42 -13
- pyvale/visualsimsensors.py +318 -0
- pyvale/visualtools.py +69 -13
- pyvale/visualtraceplotter.py +52 -165
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/METADATA +17 -14
- pyvale-2025.5.1.dist-info/RECORD +172 -0
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/WHEEL +1 -1
- pyvale/examples/analyticdatagen/__init__.py +0 -5
- pyvale/examples/ex1_1_thermal2d.py +0 -86
- pyvale/examples/ex1_2_thermal2d.py +0 -108
- pyvale/examples/ex1_3_thermal2d.py +0 -110
- pyvale/examples/ex1_5_thermal2d.py +0 -102
- pyvale/examples/ex2_1_thermal3d .py +0 -84
- pyvale/examples/ex2_2_thermal3d.py +0 -51
- pyvale/examples/ex2_3_thermal3d.py +0 -106
- pyvale/examples/ex3_1_displacement2d.py +0 -44
- pyvale/examples/ex3_2_displacement2d.py +0 -76
- pyvale/examples/ex3_3_displacement2d.py +0 -101
- pyvale/examples/ex3_4_displacement2d.py +0 -102
- pyvale/examples/ex4_1_strain2d.py +0 -54
- pyvale/examples/ex4_2_strain2d.py +0 -76
- pyvale/examples/ex4_3_strain2d.py +0 -97
- pyvale/examples/ex5_1_multiphysics2d.py +0 -75
- pyvale/examples/ex6_1_multiphysics2d_expsim.py +0 -115
- pyvale/examples/ex6_2_multiphysics3d_expsim.py +0 -160
- pyvale/examples/features/__init__.py +0 -5
- pyvale/examples/features/ex_area_avg.py +0 -89
- pyvale/examples/features/ex_calibration_error.py +0 -108
- pyvale/examples/features/ex_chain_field_errs.py +0 -141
- pyvale/examples/features/ex_field_errs.py +0 -78
- pyvale/examples/features/ex_sensor_single_angle_batch.py +0 -110
- pyvale/optimcheckfuncs.py +0 -153
- pyvale/visualsimplotter.py +0 -182
- pyvale-2025.4.1.dist-info/RECORD +0 -163
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/licenses/LICENSE +0 -0
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/top_level.txt +0 -0
pyvale/errorcalculator.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ==============================================================================
|
|
2
2
|
# pyvale: the python validation engine
|
|
3
3
|
# License: MIT
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
-
#
|
|
5
|
+
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
import enum
|
|
8
8
|
from abc import ABC, abstractmethod
|
|
@@ -27,14 +27,14 @@ class EErrType(enum.Enum):
|
|
|
27
27
|
RANDOM = enum.auto()
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
class
|
|
30
|
+
class EErrDep(enum.Enum):
|
|
31
31
|
"""Enumeration defining error dependence.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
EErrDep.INDEPENDENT:
|
|
34
34
|
Errors are calculated based on the ground truth sensor values
|
|
35
35
|
interpolated from the input simulation.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
EErrDep.DEPENDENT:
|
|
38
38
|
Errors are calculated based on the accumulated sensor reading due
|
|
39
39
|
to all preceeding errors in the chain.
|
|
40
40
|
"""
|
|
@@ -46,6 +46,7 @@ class IErrCalculator(ABC):
|
|
|
46
46
|
"""Interface (abstract base class) for sensor error calculation allowing for
|
|
47
47
|
chaining of errors.
|
|
48
48
|
"""
|
|
49
|
+
|
|
49
50
|
@abstractmethod
|
|
50
51
|
def get_error_type(self) -> EErrType:
|
|
51
52
|
"""Abstract method for getting the error type.
|
|
@@ -55,10 +56,9 @@ class IErrCalculator(ABC):
|
|
|
55
56
|
EErrType
|
|
56
57
|
Enumeration definining RANDOM or SYSTEMATIC error types.
|
|
57
58
|
"""
|
|
58
|
-
pass
|
|
59
59
|
|
|
60
60
|
@abstractmethod
|
|
61
|
-
def get_error_dep(self) ->
|
|
61
|
+
def get_error_dep(self) -> EErrDep:
|
|
62
62
|
"""Abstract method for getting the error dependence.
|
|
63
63
|
|
|
64
64
|
Returns
|
|
@@ -66,10 +66,9 @@ class IErrCalculator(ABC):
|
|
|
66
66
|
EErrDependence
|
|
67
67
|
Enumeration definining RANDOM or SYSTEMATIC error types.
|
|
68
68
|
"""
|
|
69
|
-
pass
|
|
70
69
|
|
|
71
70
|
@abstractmethod
|
|
72
|
-
def set_error_dep(self, dependence:
|
|
71
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
73
72
|
"""Abstract method for setting the error dependence.
|
|
74
73
|
|
|
75
74
|
Parameters
|
|
@@ -104,7 +103,6 @@ class IErrCalculator(ABC):
|
|
|
104
103
|
the error chain. Note that many errors do not modify the sensor data
|
|
105
104
|
so the sensor data class is passed through this function unchanged.
|
|
106
105
|
"""
|
|
107
|
-
pass
|
|
108
106
|
|
|
109
107
|
|
|
110
108
|
|
pyvale/errordriftcalc.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ==============================================================================
|
|
2
2
|
# pyvale: the python validation engine
|
|
3
3
|
# License: MIT
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
-
#
|
|
5
|
+
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
8
|
import numpy as np
|
|
@@ -32,7 +32,6 @@ class IDriftCalculator(ABC):
|
|
|
32
32
|
Array of drift errors having the same shape as the input drift_var
|
|
33
33
|
array.
|
|
34
34
|
"""
|
|
35
|
-
pass
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
class DriftConstant(IDriftCalculator):
|
|
@@ -40,9 +39,10 @@ class DriftConstant(IDriftCalculator):
|
|
|
40
39
|
|
|
41
40
|
Implements the IDriftCalculator interface.
|
|
42
41
|
"""
|
|
43
|
-
|
|
44
|
-
"""Initialiser for the `DriftConstant` class.
|
|
42
|
+
__slots__ = ("_offset",)
|
|
45
43
|
|
|
44
|
+
def __init__(self, offset: float) -> None:
|
|
45
|
+
"""
|
|
46
46
|
Parameters
|
|
47
47
|
----------
|
|
48
48
|
offset : float
|
|
@@ -73,10 +73,10 @@ class DriftLinear(IDriftCalculator):
|
|
|
73
73
|
|
|
74
74
|
Implements the IDriftCalculator interface.
|
|
75
75
|
"""
|
|
76
|
+
__slots__ = ("_slope","_offset")
|
|
76
77
|
|
|
77
78
|
def __init__(self, slope: float, offset: float = 0.0) -> None:
|
|
78
|
-
"""
|
|
79
|
-
|
|
79
|
+
"""
|
|
80
80
|
Parameters
|
|
81
81
|
----------
|
|
82
82
|
slope : float
|
|
@@ -113,9 +113,10 @@ class DriftPolynomial(IDriftCalculator):
|
|
|
113
113
|
Implements the IDriftCalculator interface.
|
|
114
114
|
"""
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
"""Initialiser for the `DriftPolynomial` class.
|
|
116
|
+
__slots__ = ("_coeffs",)
|
|
118
117
|
|
|
118
|
+
def __init__(self, coeffs: np.ndarray) -> None:
|
|
119
|
+
"""
|
|
119
120
|
Parameters
|
|
120
121
|
----------
|
|
121
122
|
coeffs : np.ndarray
|
pyvale/errorintegrator.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ==============================================================================
|
|
2
2
|
# pyvale: the python validation engine
|
|
3
3
|
# License: MIT
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
-
#
|
|
5
|
+
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
import copy
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
import numpy as np
|
|
10
10
|
from pyvale.errorcalculator import (IErrCalculator,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
EErrType,
|
|
12
|
+
EErrDep)
|
|
13
13
|
from pyvale.sensordata import SensorData
|
|
14
14
|
|
|
15
15
|
|
|
@@ -19,12 +19,9 @@ class ErrIntOpts:
|
|
|
19
19
|
errors are calculated and stored in memory for later use.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
force_dependence:
|
|
23
|
-
"""Forces all errors to be calculated
|
|
24
|
-
|
|
25
|
-
errors are calculated based on the ground truth whereas dependent errors are
|
|
26
|
-
calculated based on the accumulated sensor measurement at that stage in the
|
|
27
|
-
error chain.
|
|
22
|
+
force_dependence: EErrDep | None = None
|
|
23
|
+
"""Forces all errors to be calculated with the specified dependence. If set
|
|
24
|
+
to None then all errors will use their default/preset dependence.
|
|
28
25
|
|
|
29
26
|
Note that some errors are inherently independent so will not change. For
|
|
30
27
|
example: `ErrRandNormal` is purely independent whereas `ErrRandNormPercent`
|
|
@@ -71,8 +68,7 @@ class ErrIntegrator:
|
|
|
71
68
|
sensor_data_initial: SensorData,
|
|
72
69
|
meas_shape: tuple[int,int,int],
|
|
73
70
|
err_int_opts: ErrIntOpts | None = None) -> None:
|
|
74
|
-
"""
|
|
75
|
-
|
|
71
|
+
"""
|
|
76
72
|
Parameters
|
|
77
73
|
----------
|
|
78
74
|
err_chain : list[IErrCalculator]
|
|
@@ -117,7 +113,7 @@ class ErrIntegrator:
|
|
|
117
113
|
"""Sets the error chain that will be looped over to calculate the sensor
|
|
118
114
|
measurement errors. If the error integration options are forcing error
|
|
119
115
|
dependence then all errors in the chain will have their dependence set
|
|
120
|
-
to
|
|
116
|
+
to the specified value.
|
|
121
117
|
|
|
122
118
|
Parameters
|
|
123
119
|
----------
|
|
@@ -126,9 +122,9 @@ class ErrIntegrator:
|
|
|
126
122
|
"""
|
|
127
123
|
self._err_chain = err_chain
|
|
128
124
|
|
|
129
|
-
if self._err_int_opts.force_dependence:
|
|
125
|
+
if self._err_int_opts.force_dependence is not None:
|
|
130
126
|
for ee in self._err_chain:
|
|
131
|
-
ee.set_error_dep(
|
|
127
|
+
ee.set_error_dep(self._err_int_opts.force_dependence)
|
|
132
128
|
|
|
133
129
|
|
|
134
130
|
def calc_errors_from_chain(self, truth: np.ndarray) -> np.ndarray:
|
|
@@ -187,14 +183,15 @@ class ErrIntegrator:
|
|
|
187
183
|
|
|
188
184
|
for ii,ee in enumerate(self._err_chain):
|
|
189
185
|
|
|
190
|
-
if ee.get_error_dep() ==
|
|
186
|
+
if ee.get_error_dep() == EErrDep.DEPENDENT:
|
|
191
187
|
(error_array,sens_data) = ee.calc_errs(truth+accumulated_error,
|
|
192
188
|
self._sens_data_accumulated)
|
|
193
|
-
|
|
189
|
+
|
|
194
190
|
else:
|
|
195
191
|
(error_array,sens_data) = ee.calc_errs(truth,
|
|
196
192
|
self._sens_data_initial)
|
|
197
193
|
|
|
194
|
+
self._sens_data_accumulated = sens_data
|
|
198
195
|
self._sens_data_by_chain.append(sens_data)
|
|
199
196
|
|
|
200
197
|
if ee.get_error_type() == EErrType.SYSTEMATIC:
|
|
@@ -233,10 +230,11 @@ class ErrIntegrator:
|
|
|
233
230
|
|
|
234
231
|
for ee in self._err_chain:
|
|
235
232
|
|
|
236
|
-
if ee.get_error_dep() ==
|
|
237
|
-
(error_array,sens_data) = ee.calc_errs(
|
|
238
|
-
|
|
239
|
-
|
|
233
|
+
if ee.get_error_dep() == EErrDep.DEPENDENT:
|
|
234
|
+
(error_array,sens_data) = ee.calc_errs(
|
|
235
|
+
truth+accumulated_error,
|
|
236
|
+
self._sens_data_accumulated
|
|
237
|
+
)
|
|
240
238
|
else:
|
|
241
239
|
(error_array,sens_data) = ee.calc_errs(truth,
|
|
242
240
|
self._sens_data_initial)
|
pyvale/errorrand.py
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ==============================================================================
|
|
2
2
|
# pyvale: the python validation engine
|
|
3
3
|
# License: MIT
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
-
#
|
|
5
|
+
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
from pyvale.sensordata import SensorData
|
|
9
9
|
from pyvale.errorcalculator import (IErrCalculator,
|
|
10
10
|
EErrType,
|
|
11
|
-
|
|
12
|
-
from pyvale.generatorsrandom import
|
|
11
|
+
EErrDep)
|
|
12
|
+
from pyvale.generatorsrandom import IGenRandom
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class ErrRandUnif(IErrCalculator):
|
|
16
16
|
"""Random error calculator based on uniform sampling of an interval
|
|
17
17
|
specified by its upper and lower bound.
|
|
18
18
|
|
|
@@ -23,10 +23,9 @@ class ErrRandUniform(IErrCalculator):
|
|
|
23
23
|
def __init__(self,
|
|
24
24
|
low: float,
|
|
25
25
|
high: float,
|
|
26
|
-
err_dep:
|
|
26
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
27
27
|
seed: int | None = None) -> None:
|
|
28
|
-
"""
|
|
29
|
-
|
|
28
|
+
"""
|
|
30
29
|
Parameters
|
|
31
30
|
----------
|
|
32
31
|
low : float
|
|
@@ -54,7 +53,7 @@ class ErrRandUniform(IErrCalculator):
|
|
|
54
53
|
self.rng = np.random.default_rng(seed)
|
|
55
54
|
self.err_dep = err_dep
|
|
56
55
|
|
|
57
|
-
def get_error_dep(self) ->
|
|
56
|
+
def get_error_dep(self) -> EErrDep:
|
|
58
57
|
"""Gets the error dependence state for this error calculator. An
|
|
59
58
|
independent error is calculated based on the input truth values as the
|
|
60
59
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -69,7 +68,7 @@ class ErrRandUniform(IErrCalculator):
|
|
|
69
68
|
"""
|
|
70
69
|
return self.err_dep
|
|
71
70
|
|
|
72
|
-
def set_error_dep(self, dependence:
|
|
71
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
73
72
|
"""Sets the error dependence state for this error calculator. An
|
|
74
73
|
independent error is calculated based on the input truth values as the
|
|
75
74
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -138,10 +137,9 @@ class ErrRandUnifPercent(IErrCalculator):
|
|
|
138
137
|
def __init__(self,
|
|
139
138
|
low_percent: float,
|
|
140
139
|
high_percent: float,
|
|
141
|
-
err_dep:
|
|
140
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
142
141
|
seed: int | None = None) -> None:
|
|
143
|
-
"""
|
|
144
|
-
|
|
142
|
+
"""
|
|
145
143
|
Parameters
|
|
146
144
|
----------
|
|
147
145
|
low_percent : float
|
|
@@ -170,7 +168,7 @@ class ErrRandUnifPercent(IErrCalculator):
|
|
|
170
168
|
self.rng = np.random.default_rng(seed)
|
|
171
169
|
self.err_dep = err_dep
|
|
172
170
|
|
|
173
|
-
def get_error_dep(self) ->
|
|
171
|
+
def get_error_dep(self) -> EErrDep:
|
|
174
172
|
"""Gets the error dependence state for this error calculator. An
|
|
175
173
|
independent error is calculated based on the input truth values as the
|
|
176
174
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -183,7 +181,7 @@ class ErrRandUnifPercent(IErrCalculator):
|
|
|
183
181
|
"""
|
|
184
182
|
return self.err_dep
|
|
185
183
|
|
|
186
|
-
def set_error_dep(self, dependence:
|
|
184
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
187
185
|
"""Sets the error dependence state for this error calculator. An
|
|
188
186
|
independent error is calculated based on the input truth values as the
|
|
189
187
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -234,7 +232,7 @@ class ErrRandUnifPercent(IErrCalculator):
|
|
|
234
232
|
return (err_basis*norm_rand,sens_data)
|
|
235
233
|
|
|
236
234
|
|
|
237
|
-
class
|
|
235
|
+
class ErrRandNorm(IErrCalculator):
|
|
238
236
|
"""Random error calculator based on sampling of a normal (Gaussian)
|
|
239
237
|
distribution specified using the standard deviation with an assumed zero
|
|
240
238
|
mean. A non-zero mean is a systematic error and should be specified using
|
|
@@ -246,10 +244,9 @@ class ErrRandNormal(IErrCalculator):
|
|
|
246
244
|
|
|
247
245
|
def __init__(self,
|
|
248
246
|
std: float,
|
|
249
|
-
err_dep:
|
|
247
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
250
248
|
seed: int | None = None) -> None:
|
|
251
|
-
"""
|
|
252
|
-
|
|
249
|
+
"""
|
|
253
250
|
Parameters
|
|
254
251
|
----------
|
|
255
252
|
std : float
|
|
@@ -264,7 +261,7 @@ class ErrRandNormal(IErrCalculator):
|
|
|
264
261
|
self.rng = np.random.default_rng(seed)
|
|
265
262
|
self.err_dep = err_dep
|
|
266
263
|
|
|
267
|
-
def get_error_dep(self) ->
|
|
264
|
+
def get_error_dep(self) -> EErrDep:
|
|
268
265
|
"""Gets the error dependence state for this error calculator. An
|
|
269
266
|
independent error is calculated based on the input truth values as the
|
|
270
267
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -279,7 +276,7 @@ class ErrRandNormal(IErrCalculator):
|
|
|
279
276
|
"""
|
|
280
277
|
return self.err_dep
|
|
281
278
|
|
|
282
|
-
def set_error_dep(self, dependence:
|
|
279
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
283
280
|
"""Sets the error dependence state for this error calculator. An
|
|
284
281
|
independent error is calculated based on the input truth values as the
|
|
285
282
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -349,10 +346,9 @@ class ErrRandNormPercent(IErrCalculator):
|
|
|
349
346
|
|
|
350
347
|
def __init__(self,
|
|
351
348
|
std_percent: float,
|
|
352
|
-
err_dep:
|
|
349
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
353
350
|
seed: int | None = None) -> None:
|
|
354
|
-
"""
|
|
355
|
-
|
|
351
|
+
"""
|
|
356
352
|
Parameters
|
|
357
353
|
----------
|
|
358
354
|
std_percent : float
|
|
@@ -368,7 +364,7 @@ class ErrRandNormPercent(IErrCalculator):
|
|
|
368
364
|
self._rng = np.random.default_rng(seed)
|
|
369
365
|
self._err_dep = err_dep
|
|
370
366
|
|
|
371
|
-
def get_error_dep(self) ->
|
|
367
|
+
def get_error_dep(self) -> EErrDep:
|
|
372
368
|
"""Gets the error dependence state for this error calculator. An
|
|
373
369
|
independent error is calculated based on the input truth values as the
|
|
374
370
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -381,7 +377,7 @@ class ErrRandNormPercent(IErrCalculator):
|
|
|
381
377
|
"""
|
|
382
378
|
return self._err_dep
|
|
383
379
|
|
|
384
|
-
def set_error_dep(self, dependence:
|
|
380
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
385
381
|
"""Sets the error dependence state for this error calculator. An
|
|
386
382
|
independent error is calculated based on the input truth values as the
|
|
387
383
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -441,10 +437,9 @@ class ErrRandGenerator(IErrCalculator):
|
|
|
441
437
|
__slots__ = ("_generator","_err_dep")
|
|
442
438
|
|
|
443
439
|
def __init__(self,
|
|
444
|
-
generator:
|
|
445
|
-
err_dep:
|
|
446
|
-
"""
|
|
447
|
-
|
|
440
|
+
generator: IGenRandom,
|
|
441
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
|
|
442
|
+
"""
|
|
448
443
|
Parameters
|
|
449
444
|
----------
|
|
450
445
|
generator : IGeneratorRandom
|
|
@@ -455,7 +450,7 @@ class ErrRandGenerator(IErrCalculator):
|
|
|
455
450
|
self._generator = generator
|
|
456
451
|
self._err_dep = err_dep
|
|
457
452
|
|
|
458
|
-
def get_error_dep(self) ->
|
|
453
|
+
def get_error_dep(self) -> EErrDep:
|
|
459
454
|
"""Gets the error dependence state for this error calculator. An
|
|
460
455
|
independent error is calculated based on the input truth values as the
|
|
461
456
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -470,7 +465,7 @@ class ErrRandGenerator(IErrCalculator):
|
|
|
470
465
|
"""
|
|
471
466
|
return self._err_dep
|
|
472
467
|
|
|
473
|
-
def set_error_dep(self, dependence:
|
|
468
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
474
469
|
"""Sets the error dependence state for this error calculator. An
|
|
475
470
|
independent error is calculated based on the input truth values as the
|
|
476
471
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -536,10 +531,9 @@ class ErrRandGenPercent(IErrCalculator):
|
|
|
536
531
|
__slots__ = ("_generator","_err_dep")
|
|
537
532
|
|
|
538
533
|
def __init__(self,
|
|
539
|
-
generator:
|
|
540
|
-
err_dep:
|
|
541
|
-
"""
|
|
542
|
-
|
|
534
|
+
generator: IGenRandom,
|
|
535
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
|
|
536
|
+
"""
|
|
543
537
|
Parameters
|
|
544
538
|
----------
|
|
545
539
|
generator : IGeneratorRandom
|
|
@@ -550,7 +544,7 @@ class ErrRandGenPercent(IErrCalculator):
|
|
|
550
544
|
self._generator = generator
|
|
551
545
|
self._err_dep = err_dep
|
|
552
546
|
|
|
553
|
-
def get_error_dep(self) ->
|
|
547
|
+
def get_error_dep(self) -> EErrDep:
|
|
554
548
|
"""Gets the error dependence state for this error calculator. An
|
|
555
549
|
independent error is calculated based on the input truth values as the
|
|
556
550
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -563,7 +557,7 @@ class ErrRandGenPercent(IErrCalculator):
|
|
|
563
557
|
"""
|
|
564
558
|
return self._err_dep
|
|
565
559
|
|
|
566
|
-
def set_error_dep(self, dependence:
|
|
560
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
567
561
|
"""Sets the error dependence state for this error calculator. An
|
|
568
562
|
independent error is calculated based on the input truth values as the
|
|
569
563
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -608,6 +602,6 @@ class ErrRandGenPercent(IErrCalculator):
|
|
|
608
602
|
error array has the same shape as the input error basis.
|
|
609
603
|
"""
|
|
610
604
|
rand_errs = err_basis \
|
|
611
|
-
* self._generator.generate(shape=err_basis.shape)/100
|
|
605
|
+
* self._generator.generate(shape=err_basis.shape)/100.0
|
|
612
606
|
|
|
613
607
|
return (rand_errs,sens_data)
|
pyvale/errorsyscalib.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
from typing import Callable
|
|
8
|
+
import numpy as np
|
|
9
|
+
from pyvale.errorcalculator import (IErrCalculator,
|
|
10
|
+
EErrType,
|
|
11
|
+
EErrDep)
|
|
12
|
+
from pyvale.sensordata import SensorData
|
|
13
|
+
|
|
14
|
+
# TODO: add option to use Newton's method for function inversion instead of a
|
|
15
|
+
# cal table.
|
|
16
|
+
|
|
17
|
+
class ErrSysCalibration(IErrCalculator):
|
|
18
|
+
"""Systematic error calculator for calibration errors. The user specifies an
|
|
19
|
+
assumed calibration and a ground truth calibration function. The ground
|
|
20
|
+
truth calibration function is inverted and linearly interpolated numerically
|
|
21
|
+
based on the number of divisions specified by the user.
|
|
22
|
+
|
|
23
|
+
Implements the `IErrCalculator` interface.
|
|
24
|
+
"""
|
|
25
|
+
__slots__ = ("_assumed_cali","_truth_calib","_cal_range","_n_cal_divs",
|
|
26
|
+
"_err_dep","_truth_calc_table")
|
|
27
|
+
|
|
28
|
+
def __init__(self,
|
|
29
|
+
assumed_calib: Callable[[np.ndarray],np.ndarray],
|
|
30
|
+
truth_calib: Callable[[np.ndarray],np.ndarray],
|
|
31
|
+
cal_range: tuple[float,float],
|
|
32
|
+
n_cal_divs: int = 10000,
|
|
33
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
assumed_calib : Callable[[np.ndarray],np.ndarray]
|
|
38
|
+
Assumed calibration function taking the input unitless 'signal' and
|
|
39
|
+
converting it to the same units as the physical field being sampled
|
|
40
|
+
by the sensor array.
|
|
41
|
+
truth_calib : Callable[[np.ndarray],np.ndarray]
|
|
42
|
+
Assumed calibration function taking the input unitless 'signal' and
|
|
43
|
+
converting it to the same units as the physical field being sampled
|
|
44
|
+
by the sensor array.
|
|
45
|
+
cal_range : tuple[float,float]
|
|
46
|
+
Range over which the calibration functions are valid. This is
|
|
47
|
+
normally based on a voltage range such as (0,10) volts.
|
|
48
|
+
n_cal_divs : int, optional
|
|
49
|
+
Number of divisions to discretise the the truth calibration function
|
|
50
|
+
for numerical inversion, by default 10000.
|
|
51
|
+
err_dep : EErrDependence, optional
|
|
52
|
+
Error calculation dependence, by default EErrDependence.INDEPENDENT.
|
|
53
|
+
"""
|
|
54
|
+
self._assumed_calib = assumed_calib
|
|
55
|
+
self._truth_calib = truth_calib
|
|
56
|
+
self._cal_range = cal_range
|
|
57
|
+
self._n_cal_divs = n_cal_divs
|
|
58
|
+
self._err_dep = err_dep
|
|
59
|
+
|
|
60
|
+
self._truth_cal_table = np.zeros((n_cal_divs,2))
|
|
61
|
+
self._truth_cal_table[:,0] = np.linspace(cal_range[0],
|
|
62
|
+
cal_range[1],
|
|
63
|
+
n_cal_divs)
|
|
64
|
+
self._truth_cal_table[:,1] = self._truth_calib(
|
|
65
|
+
self._truth_cal_table[:,0])
|
|
66
|
+
|
|
67
|
+
def get_error_dep(self) -> EErrDep:
|
|
68
|
+
"""Gets the error dependence state for this error calculator. An
|
|
69
|
+
independent error is calculated based on the input truth values as the
|
|
70
|
+
error basis. A dependent error is calculated based on the accumulated
|
|
71
|
+
sensor reading from all preceeding errors in the chain.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
EErrDependence
|
|
76
|
+
Enumeration defining INDEPENDENT or DEPENDENT behaviour.
|
|
77
|
+
"""
|
|
78
|
+
return self._err_dep
|
|
79
|
+
|
|
80
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
81
|
+
"""Sets the error dependence state for this error calculator. An
|
|
82
|
+
independent error is calculated based on the input truth values as the
|
|
83
|
+
error basis. A dependent error is calculated based on the accumulated
|
|
84
|
+
sensor reading from all preceeding errors in the chain.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
dependence : EErrDependence
|
|
89
|
+
Enumeration defining INDEPENDENT or DEPENDENT behaviour.
|
|
90
|
+
"""
|
|
91
|
+
self._err_dep = dependence
|
|
92
|
+
|
|
93
|
+
def get_error_type(self) -> EErrType:
|
|
94
|
+
"""Gets the error type.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
EErrType
|
|
99
|
+
Enumeration definining RANDOM or SYSTEMATIC error types.
|
|
100
|
+
"""
|
|
101
|
+
return EErrType.SYSTEMATIC
|
|
102
|
+
|
|
103
|
+
def calc_errs(self,
|
|
104
|
+
err_basis: np.ndarray,
|
|
105
|
+
sens_data: SensorData,
|
|
106
|
+
) -> tuple[np.ndarray, SensorData]:
|
|
107
|
+
"""Calculates the error array based on the size of the input.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
err_basis : np.ndarray
|
|
112
|
+
Array of values with the same dimensions as the sensor measurement
|
|
113
|
+
matrix.
|
|
114
|
+
sens_data : SensorData
|
|
115
|
+
The accumulated sensor state data for all errors prior to this one.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
tuple[np.ndarray, SensorData]
|
|
120
|
+
Tuple containing the calculated error array and pass through of the
|
|
121
|
+
sensor data object as it is not modified by this class. The returned
|
|
122
|
+
error array has the same shape as the input error basis.
|
|
123
|
+
"""
|
|
124
|
+
# shape=(n_sens,n_comps,n_time_steps)
|
|
125
|
+
signal_from_field = np.interp(err_basis,
|
|
126
|
+
self._truth_cal_table[:,1],
|
|
127
|
+
self._truth_cal_table[:,0])
|
|
128
|
+
# shape=(n_sens,n_comps,n_time_steps)
|
|
129
|
+
field_from_assumed_calib = self._assumed_calib(signal_from_field)
|
|
130
|
+
|
|
131
|
+
sys_errs = field_from_assumed_calib - err_basis
|
|
132
|
+
|
|
133
|
+
return (sys_errs,sens_data)
|
|
134
|
+
|