pyvale 2025.4.0__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.

Files changed (153) hide show
  1. pyvale/__init__.py +78 -64
  2. pyvale/analyticmeshgen.py +102 -0
  3. pyvale/{core/analyticsimdatafactory.py → analyticsimdatafactory.py} +44 -16
  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/{core/camera.py → camera.py} +15 -15
  12. pyvale/{core/cameradata.py → cameradata.py} +27 -22
  13. pyvale/{core/cameradata2d.py → cameradata2d.py} +8 -6
  14. pyvale/camerastereo.py +217 -0
  15. pyvale/{core/cameratools.py → cameratools.py} +220 -26
  16. pyvale/{core/cython → cython}/rastercyth.py +11 -7
  17. pyvale/data/__init__.py +5 -7
  18. pyvale/data/cal_target.tiff +0 -0
  19. pyvale/data/case00_HEX20_out.e +0 -0
  20. pyvale/data/case00_HEX27_out.e +0 -0
  21. pyvale/data/case00_HEX8_out.e +0 -0
  22. pyvale/data/case00_TET10_out.e +0 -0
  23. pyvale/data/case00_TET14_out.e +0 -0
  24. pyvale/data/case00_TET4_out.e +0 -0
  25. pyvale/{core/dataset.py → dataset.py} +91 -16
  26. pyvale/{core/errorcalculator.py → errorcalculator.py} +13 -16
  27. pyvale/{core/errordriftcalc.py → errordriftcalc.py} +14 -14
  28. pyvale/{core/errorintegrator.py → errorintegrator.py} +25 -28
  29. pyvale/{core/errorrand.py → errorrand.py} +39 -46
  30. pyvale/errorsyscalib.py +134 -0
  31. pyvale/{core/errorsysdep.py → errorsysdep.py} +25 -29
  32. pyvale/{core/errorsysfield.py → errorsysfield.py} +59 -52
  33. pyvale/{core/errorsysindep.py → errorsysindep.py} +85 -182
  34. pyvale/examples/__init__.py +5 -7
  35. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
  36. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
  37. pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
  38. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
  39. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
  40. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
  41. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
  42. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
  43. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
  44. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
  45. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
  46. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
  47. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
  48. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
  49. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
  50. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
  51. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
  52. pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_1_scalarvisualisation.py +6 -9
  53. pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_2_scalarcasebuild.py +8 -11
  54. pyvale/examples/{analyticdatagen → genanalyticdata}/ex2_1_analyticsensors.py +9 -12
  55. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +8 -15
  56. pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
  57. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
  58. pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
  59. pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
  60. pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
  61. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastenp.py +6 -7
  62. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_oneframe.py +5 -7
  63. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_cypara.py +6 -13
  64. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_pypara.py +9 -12
  65. pyvale/examples/{ex1_4_thermal2d.py → visualisation/ex1_1_plot_traces.py} +33 -20
  66. pyvale/examples/{features/ex_animation_tools_3dmonoblock.py → visualisation/ex2_1_animate_sim.py} +37 -31
  67. pyvale/experimentsimulator.py +175 -0
  68. pyvale/{core/field.py → field.py} +6 -14
  69. pyvale/fieldconverter.py +351 -0
  70. pyvale/{core/fieldsampler.py → fieldsampler.py} +9 -10
  71. pyvale/{core/fieldscalar.py → fieldscalar.py} +17 -18
  72. pyvale/{core/fieldtensor.py → fieldtensor.py} +23 -26
  73. pyvale/{core/fieldtransform.py → fieldtransform.py} +9 -5
  74. pyvale/{core/fieldvector.py → fieldvector.py} +14 -16
  75. pyvale/{core/generatorsrandom.py → generatorsrandom.py} +29 -52
  76. pyvale/{core/imagedef2d.py → imagedef2d.py} +11 -8
  77. pyvale/{core/integratorfactory.py → integratorfactory.py} +12 -13
  78. pyvale/{core/integratorquadrature.py → integratorquadrature.py} +57 -32
  79. pyvale/integratorrectangle.py +165 -0
  80. pyvale/{core/integratorspatial.py → integratorspatial.py} +9 -10
  81. pyvale/{core/integratortype.py → integratortype.py} +7 -8
  82. pyvale/output.py +17 -0
  83. pyvale/pyvaleexceptions.py +11 -0
  84. pyvale/{core/raster.py → raster.py} +8 -8
  85. pyvale/{core/rastercy.py → rastercy.py} +11 -10
  86. pyvale/{core/rasternp.py → rasternp.py} +12 -13
  87. pyvale/{core/rendermesh.py → rendermesh.py} +10 -19
  88. pyvale/{core/sensorarray.py → sensorarray.py} +7 -8
  89. pyvale/{core/sensorarrayfactory.py → sensorarrayfactory.py} +64 -78
  90. pyvale/{core/sensorarraypoint.py → sensorarraypoint.py} +39 -41
  91. pyvale/{core/sensordata.py → sensordata.py} +7 -8
  92. pyvale/sensordescriptor.py +213 -0
  93. pyvale/{core/sensortools.py → sensortools.py} +8 -9
  94. pyvale/simcases/case00_HEX20.i +5 -5
  95. pyvale/simcases/case00_HEX27.i +5 -5
  96. pyvale/simcases/case00_HEX8.i +242 -0
  97. pyvale/simcases/case00_TET10.i +2 -2
  98. pyvale/simcases/case00_TET14.i +2 -2
  99. pyvale/simcases/case00_TET4.i +242 -0
  100. pyvale/simcases/run_1case.py +1 -1
  101. pyvale/simtools.py +67 -0
  102. pyvale/visualexpplotter.py +191 -0
  103. pyvale/{core/visualimagedef.py → visualimagedef.py} +13 -10
  104. pyvale/{core/visualimages.py → visualimages.py} +10 -9
  105. pyvale/visualopts.py +493 -0
  106. pyvale/{core/visualsimanimator.py → visualsimanimator.py} +47 -19
  107. pyvale/visualsimsensors.py +318 -0
  108. pyvale/visualtools.py +136 -0
  109. pyvale/visualtraceplotter.py +142 -0
  110. {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/METADATA +17 -14
  111. pyvale-2025.5.1.dist-info/RECORD +172 -0
  112. {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/WHEEL +1 -1
  113. pyvale/core/__init__.py +0 -7
  114. pyvale/core/analyticmeshgen.py +0 -59
  115. pyvale/core/analyticsimdatagenerator.py +0 -160
  116. pyvale/core/cython/rastercyth.c +0 -32267
  117. pyvale/core/experimentsimulator.py +0 -99
  118. pyvale/core/fieldconverter.py +0 -154
  119. pyvale/core/integratorrectangle.py +0 -88
  120. pyvale/core/optimcheckfuncs.py +0 -153
  121. pyvale/core/sensordescriptor.py +0 -101
  122. pyvale/core/visualexpplotter.py +0 -151
  123. pyvale/core/visualopts.py +0 -180
  124. pyvale/core/visualsimplotter.py +0 -182
  125. pyvale/core/visualtools.py +0 -81
  126. pyvale/core/visualtraceplotter.py +0 -256
  127. pyvale/examples/analyticdatagen/__init__.py +0 -7
  128. pyvale/examples/ex1_1_thermal2d.py +0 -89
  129. pyvale/examples/ex1_2_thermal2d.py +0 -111
  130. pyvale/examples/ex1_3_thermal2d.py +0 -113
  131. pyvale/examples/ex1_5_thermal2d.py +0 -105
  132. pyvale/examples/ex2_1_thermal3d .py +0 -87
  133. pyvale/examples/ex2_2_thermal3d.py +0 -51
  134. pyvale/examples/ex2_3_thermal3d.py +0 -109
  135. pyvale/examples/ex3_1_displacement2d.py +0 -47
  136. pyvale/examples/ex3_2_displacement2d.py +0 -79
  137. pyvale/examples/ex3_3_displacement2d.py +0 -104
  138. pyvale/examples/ex3_4_displacement2d.py +0 -105
  139. pyvale/examples/ex4_1_strain2d.py +0 -57
  140. pyvale/examples/ex4_2_strain2d.py +0 -79
  141. pyvale/examples/ex4_3_strain2d.py +0 -100
  142. pyvale/examples/ex5_1_multiphysics2d.py +0 -78
  143. pyvale/examples/ex6_1_multiphysics2d_expsim.py +0 -118
  144. pyvale/examples/ex6_2_multiphysics3d_expsim.py +0 -158
  145. pyvale/examples/features/__init__.py +0 -7
  146. pyvale/examples/features/ex_area_avg.py +0 -89
  147. pyvale/examples/features/ex_calibration_error.py +0 -108
  148. pyvale/examples/features/ex_chain_field_errs.py +0 -141
  149. pyvale/examples/features/ex_field_errs.py +0 -78
  150. pyvale/examples/features/ex_sensor_single_angle_batch.py +0 -110
  151. pyvale-2025.4.0.dist-info/RECORD +0 -157
  152. {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/licenses/LICENSE +0 -0
  153. {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/top_level.txt +0 -0
@@ -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
+
@@ -1,17 +1,16 @@
1
- """
2
- ================================================================================
3
- pyvale: the python validation engine
4
- License: MIT
5
- Copyright (C) 2025 The Computer Aided Validation Team
6
- ================================================================================
7
- """
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
8
7
  import enum
9
8
  from typing import Callable
10
9
  import numpy as np
11
- from pyvale.core.sensordata import SensorData
12
- from pyvale.core.errorcalculator import (IErrCalculator,
10
+ from pyvale.sensordata import SensorData
11
+ from pyvale.errorcalculator import (IErrCalculator,
13
12
  EErrType,
14
- EErrDependence)
13
+ EErrDep)
15
14
 
16
15
 
17
16
  class ERoundMethod(enum.Enum):
@@ -54,9 +53,8 @@ class ErrSysRoundOff(IErrCalculator):
54
53
  def __init__(self,
55
54
  method: ERoundMethod = ERoundMethod.ROUND,
56
55
  base: float = 1.0,
57
- err_dep: EErrDependence = EErrDependence.DEPENDENT) -> None:
58
- """Initialiser for the `ErrSysRoundOff` class.
59
-
56
+ err_dep: EErrDep = EErrDep.DEPENDENT) -> None:
57
+ """
60
58
  Parameters
61
59
  ----------
62
60
  method : ERoundMethod, optional
@@ -71,7 +69,7 @@ class ErrSysRoundOff(IErrCalculator):
71
69
  self._method = _select_round_method(method)
72
70
  self._err_dep = err_dep
73
71
 
74
- def get_error_dep(self) -> EErrDependence:
72
+ def get_error_dep(self) -> EErrDep:
75
73
  """Gets the error dependence state for this error calculator. An
76
74
  independent error is calculated based on the input truth values as the
77
75
  error basis. A dependent error is calculated based on the accumulated
@@ -84,7 +82,7 @@ class ErrSysRoundOff(IErrCalculator):
84
82
  """
85
83
  return self._err_dep
86
84
 
87
- def set_error_dep(self, dependence: EErrDependence) -> None:
85
+ def set_error_dep(self, dependence: EErrDep) -> None:
88
86
  """Sets the error dependence state for this error calculator. An
89
87
  independent error is calculated based on the input truth values as the
90
88
  error basis. A dependent error is calculated based on the accumulated
@@ -143,9 +141,8 @@ class ErrSysDigitisation(IErrCalculator):
143
141
  def __init__(self,
144
142
  bits_per_unit: float,
145
143
  method: ERoundMethod = ERoundMethod.ROUND,
146
- err_dep: EErrDependence = EErrDependence.DEPENDENT) -> None:
147
- """Initialiser for the `ErrSysDigitisation` class.
148
-
144
+ err_dep: EErrDep = EErrDep.DEPENDENT) -> None:
145
+ """
149
146
  Parameters
150
147
  ----------
151
148
  bits_per_unit : float
@@ -160,7 +157,7 @@ class ErrSysDigitisation(IErrCalculator):
160
157
  self._method = _select_round_method(method)
161
158
  self._err_dep = err_dep
162
159
 
163
- def get_error_dep(self) -> EErrDependence:
160
+ def get_error_dep(self) -> EErrDep:
164
161
  """Gets the error dependence state for this error calculator. An
165
162
  independent error is calculated based on the input truth values as the
166
163
  error basis. A dependent error is calculated based on the accumulated
@@ -173,7 +170,7 @@ class ErrSysDigitisation(IErrCalculator):
173
170
  """
174
171
  return self._err_dep
175
172
 
176
- def set_error_dep(self, dependence: EErrDependence) -> None:
173
+ def set_error_dep(self, dependence: EErrDep) -> None:
177
174
  """Sets the error dependence state for this error calculator. An
178
175
  independent error is calculated based on the input truth values as the
179
176
  error basis. A dependent error is calculated based on the accumulated
@@ -227,18 +224,17 @@ class ErrSysSaturation(IErrCalculator):
227
224
  """Systematic error calculator for saturation error base on user specified
228
225
  minimum and maximum measurement values. Implements the `IErrCalculator`
229
226
  interface.
227
+
228
+ NOTE: For this error to function as expected and clamp the measurement
229
+ within the specified range it must be placed last in the error chain and
230
+ the behaviour must be set to: EErrDependence.DEPENDENT.
230
231
  """
231
232
  __slots__ = ("_min","_max","_err_dep")
232
233
 
233
234
  def __init__(self,
234
235
  meas_min: float,
235
236
  meas_max: float) -> None:
236
- """Initialiser for the `ErrSysSaturation` class.
237
-
238
- NOTE: For this error to function as expected and clamp the measurement
239
- within the specified range it must be placed last in the error chain and
240
- the behaviour must be set to: EErrDependence.DEPENDENT.
241
-
237
+ """
242
238
  Parameters
243
239
  ----------
244
240
  meas_min : float
@@ -258,9 +254,9 @@ class ErrSysSaturation(IErrCalculator):
258
254
 
259
255
  self._min = meas_min
260
256
  self._max = meas_max
261
- self._err_dep = EErrDependence.DEPENDENT
257
+ self._err_dep = EErrDep.DEPENDENT
262
258
 
263
- def get_error_dep(self) -> EErrDependence:
259
+ def get_error_dep(self) -> EErrDep:
264
260
  """Gets the error dependence state for this error calculator. An
265
261
  independent error is calculated based on the input truth values as the
266
262
  error basis. A dependent error is calculated based on the accumulated
@@ -273,7 +269,7 @@ class ErrSysSaturation(IErrCalculator):
273
269
  """
274
270
  return self._err_dep
275
271
 
276
- def set_error_dep(self, dependence: EErrDependence) -> None:
272
+ def set_error_dep(self, dependence: EErrDep) -> None:
277
273
  """Sets the error dependence state for this error calculator. An
278
274
  independent error is calculated based on the input truth values as the
279
275
  error basis. A dependent error is calculated based on the accumulated
@@ -1,24 +1,30 @@
1
- """
2
- ================================================================================
3
- pyvale: the python validation engine
4
- License: MIT
5
- Copyright (C) 2025 The Computer Aided Validation Team
6
- ================================================================================
7
- """
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
8
7
  import copy
9
8
  from dataclasses import dataclass
10
9
  import numpy as np
11
10
  from scipy.spatial.transform import Rotation
12
11
 
13
- from pyvale.core.field import IField
14
- from pyvale.core.fieldsampler import sample_field_with_sensor_data
15
- from pyvale.core.sensordata import SensorData
16
- from pyvale.core.integratortype import EIntSpatialType
17
- from pyvale.core.errorcalculator import (IErrCalculator,
12
+ from pyvale.field import IField
13
+ from pyvale.fieldsampler import sample_field_with_sensor_data
14
+ from pyvale.sensordata import SensorData
15
+ from pyvale.integratortype import EIntSpatialType
16
+ from pyvale.errorcalculator import (IErrCalculator,
18
17
  EErrType,
19
- EErrDependence)
20
- from pyvale.core.errordriftcalc import IDriftCalculator
21
- from pyvale.core.generatorsrandom import IGeneratorRandom
18
+ EErrDep)
19
+ from pyvale.errordriftcalc import IDriftCalculator
20
+ from pyvale.generatorsrandom import IGenRandom
21
+
22
+ # TODO:
23
+ # - Implement different perturbed sampling times for each sensor or allow all
24
+ # to lock to the same time step as it works now.
25
+ # - Need to check that we perform field rotations correctly for sensor angles.
26
+ # - This needs to be updated to take rotation objects for offsets and to build
27
+ # and compose rotations
22
28
 
23
29
 
24
30
  @dataclass(slots=True)
@@ -46,25 +52,26 @@ class ErrFieldData:
46
52
  num_time_steps,). If None then no time offset is applied.
47
53
  """
48
54
 
49
- pos_rand_xyz: tuple[IGeneratorRandom | None,
50
- IGeneratorRandom | None,
51
- IGeneratorRandom | None] = (None,None,None)
52
- """Tuple of random generators (implementations of `IGeneratorRandom`
55
+ pos_rand_xyz: tuple[IGenRandom | None,
56
+ IGenRandom | None,
57
+ IGenRandom | None] = (None,None,None)
58
+ """Tuple of random generators (implementations of `IGenRandom`
53
59
  interface) for perturbing the sensor positions. The generators perturb the
54
60
  X, Y and Z coordinates in order. If None then that axis is not randomly
55
- perturbed from the nominal sensor position.
61
+ perturbed from the nominal sensor position. Note that the random generators
62
+ should return position perturbations consistent with the simulation units.
56
63
  """
57
64
 
58
- ang_rand_zyx: tuple[IGeneratorRandom | None,
59
- IGeneratorRandom | None,
60
- IGeneratorRandom | None] = (None,None,None)
61
- """Tuple of random generators (implementations of `IGeneratorRandom`
65
+ ang_rand_zyx: tuple[IGenRandom | None,
66
+ IGenRandom | None,
67
+ IGenRandom | None] = (None,None,None)
68
+ """Tuple of random generators (implementations of `IGenRandom`
62
69
  interface) for perturbing the sensor angles. The generators perturb
63
70
  rotations about the the Z, Y and X axis in order. If None then that axis is
64
71
  not randomly perturbed from the nominal sensor position.
65
72
  """
66
73
 
67
- time_rand: IGeneratorRandom | None = None
74
+ time_rand: IGenRandom | None = None
68
75
  """Random generator for perturbing sensor array sampling times for the
69
76
  purpose of calculating field based errors. If None then sensor sampling
70
77
  times will not be perturbed from the nominal times.
@@ -82,11 +89,13 @@ class ErrFieldData:
82
89
  specified above. shape=(3,)
83
90
  """
84
91
 
85
- # DEV FEATURE: locks the coordinate even if offsets and random generators are specified
92
+ # DEV FEATURE: locks the coordinate even if offsets and random generators
93
+ # are specified. These allow individual sensors to be locked when we only
94
+ # specify a random generator for each axis not each sensor.
86
95
  pos_lock_xyz: np.ndarray | None = None
87
96
  ang_lock_zyx: np.ndarray | None = None
88
97
 
89
- #TODO: implement drift for other dimensions, pos/angle
98
+ # TODO: implement drift for other dimensions, pos/angle
90
99
  time_drift: IDriftCalculator | None = None
91
100
  """Temporal drift calculation
92
101
  """
@@ -108,9 +117,8 @@ class ErrSysField(IErrCalculator):
108
117
  def __init__(self,
109
118
  field: IField,
110
119
  field_err_data: ErrFieldData,
111
- err_dep: EErrDependence = EErrDependence.INDEPENDENT) -> None:
112
- """Initialiser for the `ErrSysField` class.
113
-
120
+ err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
121
+ """
114
122
  Parameters
115
123
  ----------
116
124
  field : IField
@@ -121,15 +129,15 @@ class ErrSysField(IErrCalculator):
121
129
  Dataclass specifying which sensor array parameters will be perturbed
122
130
  and how they will be perturbed. See the `ErrFieldData` class for
123
131
  more detail
124
- err_dep : EErrDependence, optional
125
- Error calculation dependence, by default EErrDependence.INDEPENDENT.
132
+ err_dep : EErrDep, optional
133
+ Error calculation dependence, by default EErrDep.DEPENDENT.
126
134
  """
127
135
  self._field = field
128
136
  self._field_err_data = field_err_data
129
137
  self._err_dep = err_dep
130
138
  self._sensor_data_perturbed = SensorData()
131
139
 
132
- def get_error_dep(self) -> EErrDependence:
140
+ def get_error_dep(self) -> EErrDep:
133
141
  """Gets the error dependence state for this error calculator. An
134
142
  independent error is calculated based on the input truth values as the
135
143
  error basis. A dependent error is calculated based on the accumulated
@@ -137,12 +145,12 @@ class ErrSysField(IErrCalculator):
137
145
 
138
146
  Returns
139
147
  -------
140
- EErrDependence
148
+ EErrDep
141
149
  Enumeration defining INDEPENDENT or DEPENDENT behaviour.
142
150
  """
143
151
  return self._err_dep
144
152
 
145
- def set_error_dep(self, dependence: EErrDependence) -> None:
153
+ def set_error_dep(self, dependence: EErrDep) -> None:
146
154
  """Sets the error dependence state for this error calculator. An
147
155
  independent error is calculated based on the input truth values as the
148
156
  error basis. A dependent error is calculated based on the accumulated
@@ -150,7 +158,7 @@ class ErrSysField(IErrCalculator):
150
158
 
151
159
  Parameters
152
160
  ----------
153
- dependence : EErrDependence
161
+ dependence : EErrDep
154
162
  Enumeration defining INDEPENDENT or DEPENDENT behaviour.
155
163
  """
156
164
  self._err_dep = dependence
@@ -232,9 +240,9 @@ class ErrSysField(IErrCalculator):
232
240
 
233
241
  def _perturb_sensor_positions(sens_pos_nominal: np.ndarray,
234
242
  pos_offset_xyz: np.ndarray | None,
235
- pos_rand_xyz: tuple[IGeneratorRandom | None,
236
- IGeneratorRandom | None,
237
- IGeneratorRandom | None] | None,
243
+ pos_rand_xyz: tuple[IGenRandom | None,
244
+ IGenRandom | None,
245
+ IGenRandom | None] | None,
238
246
  pos_loc_xyz: np.ndarray | None,
239
247
  ) -> np.ndarray:
240
248
  """Helper function for perturbing the sensor positions from their nominal
@@ -250,9 +258,9 @@ def _perturb_sensor_positions(sens_pos_nominal: np.ndarray,
250
258
  Offsets to apply to the sensor positions as an array with shape=
251
259
  (num_sensors,3) wherethe columns represent the position in the X, Y and
252
260
  Z axes. If None then no offset is applied.
253
- pos_rand_xyz : tuple[IGeneratorRandom | None,
254
- IGeneratorRandom | None,
255
- IGeneratorRandom | None] | None
261
+ pos_rand_xyz : tuple[IGenRandom | None,
262
+ IGenRandom | None,
263
+ IGenRandom | None] | None
256
264
  Random generators for sensor position perturbations along the the X, Y
257
265
  and Z axes. If None then no perturbation is applied.
258
266
  pos_loc_xyz : np.ndarray | None
@@ -285,7 +293,7 @@ def _perturb_sensor_positions(sens_pos_nominal: np.ndarray,
285
293
  def _perturb_sample_times(sim_time: np.ndarray,
286
294
  time_nominal: np.ndarray | None,
287
295
  time_offset: np.ndarray | None,
288
- time_rand: IGeneratorRandom | None,
296
+ time_rand: IGenRandom | None,
289
297
  time_drift: IDriftCalculator | None
290
298
  ) -> np.ndarray | None:
291
299
  """Helper function for calculating perturbed sensor sampling times for the
@@ -301,7 +309,7 @@ def _perturb_sample_times(sim_time: np.ndarray,
301
309
  time_offset : np.ndarray | None
302
310
  Array of time offsets to apply to all sensors. If None then no offsets
303
311
  are applied.
304
- time_rand : IGeneratorRandom | None
312
+ time_rand : IGenRandom | None
305
313
  Random generator for perturbing the sampling times of all sensors. If
306
314
  None then no random perturbation of sampling times occurs.
307
315
  time_drift : IDriftCalculator | None
@@ -337,9 +345,9 @@ def _perturb_sample_times(sim_time: np.ndarray,
337
345
  def _perturb_sensor_angles(n_sensors: int,
338
346
  angles_nominal: tuple[Rotation,...] | None,
339
347
  angle_offsets_zyx: np.ndarray | None,
340
- rand_ang_zyx: tuple[IGeneratorRandom | None,
341
- IGeneratorRandom | None,
342
- IGeneratorRandom | None] | None,
348
+ rand_ang_zyx: tuple[IGenRandom | None,
349
+ IGenRandom | None,
350
+ IGenRandom | None] | None,
343
351
  angle_loc_zyx: np.ndarray | None,
344
352
  ) -> tuple[Rotation,...] | None:
345
353
  """Helper function for perturbing sensor angles for the purpose of
@@ -357,9 +365,9 @@ def _perturb_sensor_angles(n_sensors: int,
357
365
  Angle offsets to apply to the sensor array as an array with shape=(
358
366
  num_sensors,3) where the columns are the rotations about Z, Y and X in
359
367
  degrees. If None then no offsets are applied.
360
- rand_ang_zyx : tuple[IGeneratorRandom | None,
361
- IGeneratorRandom | None,
362
- IGeneratorRandom | None] | None
368
+ rand_ang_zyx : tuple[IGenRandom | None,
369
+ IGenRandom | None,
370
+ IGenRandom | None] | None
363
371
  Random generators for perturbing sensor angles about the Z, Y and X axis
364
372
  respectively. If None then no random perturbation to the sensor angle
365
373
  occurs.
@@ -403,5 +411,4 @@ def _perturb_sensor_angles(n_sensors: int,
403
411
  sensor_rot = Rotation.from_euler("zyx",sensor_rot_angs, degrees=True)
404
412
  angles_perturbed[ii] = sensor_rot*rot_nom
405
413
 
406
- return tuple(angles_perturbed)
407
-
414
+ return tuple(angles_perturbed)