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