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.

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.1.dist-info}/METADATA +17 -14
  96. pyvale-2025.5.1.dist-info/RECORD +172 -0
  97. {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.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.1.dist-info}/licenses/LICENSE +0 -0
  126. {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/top_level.txt +0 -0
@@ -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 numpy as np
8
8
 
@@ -14,12 +14,14 @@ from pyvale.fieldtensor import FieldTensor
14
14
  from pyvale.sensordescriptor import SensorDescriptorFactory
15
15
  from pyvale.sensorarraypoint import SensorArrayPoint, SensorData
16
16
  from pyvale.errorintegrator import ErrIntegrator
17
- from pyvale.errorsysindep import ErrSysUniformPercent
17
+ from pyvale.errorsysindep import ErrSysUnifPercent
18
18
  from pyvale.errorrand import ErrRandNormPercent
19
19
  from pyvale.errorsysdep import (ErrSysDigitisation,
20
20
  ErrSysSaturation)
21
21
 
22
- #TODO: Docstrings
22
+ # TODO:
23
+ # - docstrings
24
+ # - more sensor models
23
25
 
24
26
  class SensorArrayFactory:
25
27
  """Namespace for static methods used to build common types of sensor arrays
@@ -29,35 +31,13 @@ class SensorArrayFactory:
29
31
  @staticmethod
30
32
  def thermocouples_no_errs(sim_data: mh.SimData,
31
33
  sensor_data: SensorData,
34
+ elem_dims: int,
32
35
  field_name: str = "temperature",
33
- spat_dims: int = 3,
34
36
  ) -> SensorArrayPoint:
35
- """Builds and returns a point sensor array with common parameters used
36
- for thermocouples applied to a temperature field without any simulated
37
- measurement errors. Allows the user to build and attach their own error
38
- chain or use this for fast interpolation to sensor locations without
39
- errors.
40
-
41
- Parameters
42
- ----------
43
- sim_data : mh.SimData
44
- Simulation data containing a mesh and a temperature field for the
45
- thermocouple array to sample.
46
- sensor_data : SensorData
47
- _description_
48
- field_name : str, optional
49
- _description_, by default "temperature"
50
- spat_dims : int, optional
51
- , by default 3
52
-
53
- Returns
54
- -------
55
- SensorArrayPoint
56
- _description_
57
- """
37
+
58
38
  descriptor = SensorDescriptorFactory.temperature_descriptor()
59
39
 
60
- t_field = FieldScalar(sim_data,field_name,spat_dims)
40
+ t_field = FieldScalar(sim_data,field_name,elem_dims)
61
41
 
62
42
  sens_array = SensorArrayPoint(sensor_data,
63
43
  t_field,
@@ -68,19 +48,20 @@ class SensorArrayFactory:
68
48
  @staticmethod
69
49
  def thermocouples_basic_errs(sim_data: mh.SimData,
70
50
  sensor_data: SensorData,
51
+ elem_dims: int,
71
52
  field_name: str = "temperature",
72
- spat_dims: int = 3,
73
53
  errs_pc: float = 1.0
74
54
  ) -> SensorArrayPoint:
75
55
 
76
56
  sens_array = SensorArrayFactory.thermocouples_no_errs(sim_data,
77
57
  sensor_data,
78
- field_name,
79
- spat_dims)
58
+ elem_dims,
59
+ field_name)
80
60
 
81
61
  err_int = basic_err_integrator(sens_array.get_measurement_shape(),
82
62
  sensor_data,
83
- errs_pc)
63
+ sys_err_pc=errs_pc,
64
+ rand_err_pc=errs_pc)
84
65
 
85
66
  # Normal thermcouple amp = 5mV / K
86
67
  err_int._err_chain.append(ErrSysDigitisation(bits_per_unit=2**16/1000))
@@ -89,19 +70,22 @@ class SensorArrayFactory:
89
70
  sens_array.set_error_integrator(err_int)
90
71
  return sens_array
91
72
 
73
+
74
+
92
75
  @staticmethod
93
76
  def disp_sensors_no_errs(sim_data: mh.SimData,
94
- sensor_data: SensorData,
95
- field_name: str = "displacement",
96
- spat_dims: int = 3,
97
- ) -> SensorArrayPoint:
77
+ sensor_data: SensorData,
78
+ elem_dims: int,
79
+ field_name: str,
80
+ field_comps: tuple[str,...],
81
+ ) -> SensorArrayPoint:
98
82
 
99
83
  descriptor = SensorDescriptorFactory.displacement_descriptor()
100
84
 
101
85
  disp_field = FieldVector(sim_data,
102
86
  field_name,
103
- ('disp_x','disp_y'),
104
- spat_dims)
87
+ field_comps,
88
+ elem_dims)
105
89
 
106
90
  sens_array = SensorArrayPoint(sensor_data,
107
91
  disp_field,
@@ -112,18 +96,21 @@ class SensorArrayFactory:
112
96
  @staticmethod
113
97
  def disp_sensors_basic_errs(sim_data: mh.SimData,
114
98
  sensor_data: SensorData,
115
- field_name: str = "displacement",
116
- spat_dims: int = 3,
117
- errs_pc: float = 1
99
+ elem_dims: int,
100
+ field_name: str,
101
+ field_comps: tuple[str,...],
102
+ errs_pc: float = 1.0,
118
103
  ) -> SensorArrayPoint:
119
104
 
120
105
  sens_array = SensorArrayFactory.disp_sensors_no_errs(sim_data,
121
- sensor_data,
122
- field_name,
123
- spat_dims)
106
+ sensor_data,
107
+ elem_dims,
108
+ field_name,
109
+ field_comps)
124
110
  err_int = basic_err_integrator(sens_array.get_measurement_shape(),
125
111
  sensor_data,
126
- errs_pc)
112
+ sys_err_pc=errs_pc,
113
+ rand_err_pc=errs_pc)
127
114
  sens_array.set_error_integrator(err_int)
128
115
 
129
116
  return sens_array
@@ -131,23 +118,18 @@ class SensorArrayFactory:
131
118
  @staticmethod
132
119
  def strain_gauges_no_errs(sim_data: mh.SimData,
133
120
  sensor_data: SensorData,
134
- field_name: str = "strain",
135
- spat_dims: int = 3
121
+ elem_dims: int,
122
+ field_name: str,
123
+ norm_comps: tuple[str,...],
124
+ dev_comps: tuple[str,...]
136
125
  ) -> SensorArrayPoint:
137
- descriptor = SensorDescriptorFactory.strain_descriptor(spat_dims)
138
-
139
- if spat_dims == 2:
140
- norm_components = ('strain_xx','strain_yy')
141
- dev_components = ('strain_xy',)
142
- else:
143
- norm_components = ('strain_xx','strain_yy','strain_zz')
144
- dev_components = ('strain_xy','strain_yz','strain_xz')
126
+ descriptor = SensorDescriptorFactory.strain_descriptor(elem_dims)
145
127
 
146
128
  strain_field = FieldTensor(sim_data,
147
- field_name,
148
- norm_components,
149
- dev_components,
150
- spat_dims)
129
+ field_name,
130
+ norm_comps,
131
+ dev_comps,
132
+ elem_dims)
151
133
 
152
134
  sens_array = SensorArrayPoint(sensor_data,
153
135
  strain_field,
@@ -159,19 +141,24 @@ class SensorArrayFactory:
159
141
  @staticmethod
160
142
  def strain_gauges_basic_errs(sim_data: mh.SimData,
161
143
  sensor_data: SensorData,
162
- field_name: str = "strain",
163
- spat_dims: int = 3,
144
+ elem_dims: int,
145
+ field_name: str,
146
+ norm_comps: tuple[str,...],
147
+ dev_comps: tuple[str,...],
164
148
  errs_pc: float = 1.0
165
149
  ) -> SensorArrayPoint:
166
150
 
167
151
  sens_array = SensorArrayFactory.strain_gauges_no_errs(sim_data,
168
152
  sensor_data,
153
+ elem_dims,
169
154
  field_name,
170
- spat_dims)
155
+ norm_comps,
156
+ dev_comps)
171
157
 
172
158
  err_int = basic_err_integrator(sens_array.get_measurement_shape(),
173
159
  sensor_data,
174
- errs_pc)
160
+ sys_err_pc=errs_pc,
161
+ rand_err_pc=errs_pc)
175
162
  sens_array.set_error_integrator(err_int)
176
163
 
177
164
  return sens_array
@@ -203,7 +190,7 @@ def basic_err_integrator(meas_shape: np.ndarray,
203
190
  a normal percentage random error.
204
191
  """
205
192
  err_chain = []
206
- err_chain.append(ErrSysUniformPercent(-sys_err_pc,sys_err_pc))
193
+ err_chain.append(ErrSysUnifPercent(-sys_err_pc,sys_err_pc))
207
194
  err_chain.append(ErrRandNormPercent(rand_err_pc))
208
195
  err_int = ErrIntegrator(err_chain,sensor_data,meas_shape)
209
196
  return err_int
@@ -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 numpy as np
8
8
  from pyvale.field import IField
@@ -52,16 +52,15 @@ class SensorArrayPoint(ISensorArray):
52
52
  simulated physical fields quickly using finite element shape functions.
53
53
  """
54
54
 
55
- __slots__ = ("field","descriptor","sensor_data","_truth","_measurements",
56
- "error_integrator")
55
+ __slots__ = ("_field","_descriptor","_sensor_data","_truth","_measurements",
56
+ "_error_integrator")
57
57
 
58
58
  def __init__(self,
59
59
  sensor_data: SensorData,
60
60
  field: IField,
61
61
  descriptor: SensorDescriptor | None = None,
62
62
  ) -> None:
63
- """Initialiser for the `SensorArrayPoint` class.
64
-
63
+ """
65
64
  Parameters
66
65
  ----------
67
66
  sensor_data : SensorData
@@ -75,13 +74,13 @@ class SensorArrayPoint(ISensorArray):
75
74
  Contains descriptive information about the sensor array for display
76
75
  and visualisations, by default None.
77
76
  """
78
- self.sensor_data = sensor_data
79
- self.field = field
80
- self.error_integrator = None
77
+ self._sensor_data = sensor_data
78
+ self._field = field
79
+ self._error_integrator = None
81
80
 
82
- self.descriptor = SensorDescriptor()
81
+ self._descriptor = SensorDescriptor()
83
82
  if descriptor is not None:
84
- self.descriptor = descriptor
83
+ self._descriptor = descriptor
85
84
 
86
85
  self._truth = None
87
86
  self._measurements = None
@@ -96,10 +95,10 @@ class SensorArrayPoint(ISensorArray):
96
95
  np.ndarray
97
96
  Sample times with shape: (num_time_steps,)
98
97
  """
99
- if self.sensor_data.sample_times is None:
100
- return self.field.get_time_steps()
98
+ if self._sensor_data.sample_times is None:
99
+ return self._field.get_time_steps()
101
100
 
102
- return self.sensor_data.sample_times
101
+ return self._sensor_data.sample_times
103
102
 
104
103
  def get_measurement_shape(self) -> tuple[int,int,int]:
105
104
  """Gets the shape of the sensor measurement array. shape=(num_sensors,
@@ -112,8 +111,8 @@ class SensorArrayPoint(ISensorArray):
112
111
  num_field_components,num_time_steps)
113
112
  """
114
113
 
115
- return (self.sensor_data.positions.shape[0],
116
- len(self.field.get_all_components()),
114
+ return (self._sensor_data.positions.shape[0],
115
+ len(self._field.get_all_components()),
117
116
  self.get_sample_times().shape[0])
118
117
 
119
118
  def get_field(self) -> IField:
@@ -125,7 +124,7 @@ class SensorArrayPoint(ISensorArray):
125
124
  IField
126
125
  Reference to an `IField` interface.
127
126
  """
128
- return self.field
127
+ return self._field
129
128
 
130
129
 
131
130
  def calc_truth_values(self) -> np.ndarray:
@@ -139,8 +138,8 @@ class SensorArrayPoint(ISensorArray):
139
138
  Array of ground truth sensor values. shape=(num_sensors,
140
139
  num_field_components,num_time_steps).
141
140
  """
142
- self._truth = sample_field_with_sensor_data(self.field,
143
- self.sensor_data)
141
+ self._truth = sample_field_with_sensor_data(self._field,
142
+ self._sensor_data)
144
143
 
145
144
  return self._truth
146
145
 
@@ -170,7 +169,7 @@ class SensorArrayPoint(ISensorArray):
170
169
  err_int : ErrIntegrator
171
170
  Error integration object with a chain of user defined sensor errors.
172
171
  """
173
- self.error_integrator = err_int
172
+ self._error_integrator = err_int
174
173
 
175
174
  def get_sensor_data_perturbed(self) -> SensorData | None:
176
175
  """Gets the final sensor array parameters after all errors in the error
@@ -183,10 +182,10 @@ class SensorArrayPoint(ISensorArray):
183
182
  The accumulated sensor array parameters as a SensorData object.
184
183
  Returns None if no error integrator has been specified.
185
184
  """
186
- if self.error_integrator is None:
185
+ if self._error_integrator is None:
187
186
  return None
188
187
 
189
- return self.error_integrator.get_sens_data_accumulated()
188
+ return self._error_integrator.get_sens_data_accumulated()
190
189
 
191
190
  def get_errors_systematic(self) -> np.ndarray | None:
192
191
  """Gets the systematic error array from the previously calculated sensor
@@ -199,10 +198,10 @@ class SensorArrayPoint(ISensorArray):
199
198
  ,num_field_components,num_time_steps). Returns None if no error
200
199
  integrator has been set.
201
200
  """
202
- if self.error_integrator is None:
201
+ if self._error_integrator is None:
203
202
  return None
204
203
 
205
- return self.error_integrator.get_errs_systematic()
204
+ return self._error_integrator.get_errs_systematic()
206
205
 
207
206
  def get_errors_random(self) -> np.ndarray | None:
208
207
  """Gets the random error array from the previously calculated sensor
@@ -215,10 +214,10 @@ class SensorArrayPoint(ISensorArray):
215
214
  ,num_field_components,num_time_steps). Returns None if no error
216
215
  integrator has been set.
217
216
  """
218
- if self.error_integrator is None:
217
+ if self._error_integrator is None:
219
218
  return None
220
219
 
221
- return self.error_integrator.get_errs_random()
220
+ return self._error_integrator.get_errs_random()
222
221
 
223
222
  def get_errors_total(self) -> np.ndarray | None:
224
223
  """Gets the total error array from the previously calculated sensor
@@ -231,10 +230,10 @@ class SensorArrayPoint(ISensorArray):
231
230
  ,num_field_components,num_time_steps). Returns None if no error
232
231
  integrator has been set.
233
232
  """
234
- if self.error_integrator is None:
233
+ if self._error_integrator is None:
235
234
  return None
236
235
 
237
- return self.error_integrator.get_errs_total()
236
+ return self._error_integrator.get_errs_total()
238
237
 
239
238
  def calc_measurements(self) -> np.ndarray:
240
239
  """Calculates a set of sensor measurements using the specified sensor
@@ -253,11 +252,11 @@ class SensorArrayPoint(ISensorArray):
253
252
  systematic errors if an error integrator is specified. shape=(
254
253
  num_sensors,num_field_components,num_time_steps).
255
254
  """
256
- if self.error_integrator is None:
255
+ if self._error_integrator is None:
257
256
  self._measurements = self.get_truth()
258
257
  else:
259
258
  self._measurements = self.get_truth() + \
260
- self.error_integrator.calc_errors_from_chain(self.get_truth())
259
+ self._error_integrator.calc_errors_from_chain(self.get_truth())
261
260
 
262
261
  return self._measurements
263
262
 
pyvale/sensordata.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 dataclasses import dataclass
8
8
  import numpy as np
@@ -1,25 +1,75 @@
1
1
 
2
- # ================================================================================
2
+ # ==============================================================================
3
3
  # pyvale: the python validation engine
4
4
  # License: MIT
5
5
  # Copyright (C) 2025 The Computer Aided Validation Team
6
- # ================================================================================
6
+ # ==============================================================================
7
+
8
+ """
9
+ This module is used to create sensor descriptors which are strings used to label
10
+ plots and visualisations for virtual sensor simulations.
11
+ """
7
12
 
8
13
  from dataclasses import dataclass
9
14
  import numpy as np
10
15
 
11
- #TODO: Docstrings
12
16
 
13
17
  @dataclass(slots=True)
14
18
  class SensorDescriptor:
15
- name: str = 'Measured Value'
19
+ """Dataclass for storing string descriptors for sensor array vis2ualisation.
20
+ Used for labelling matplotlib and pyvista plots with the sensor name,
21
+ physical units and other descriptors.
22
+ """
23
+
24
+ name: str = "Measured Value"
25
+ """String describing the field that the sensor measures e.g. temperature
26
+ , strain etc. Defaults to 'Measured Value'.
27
+ """
28
+
16
29
  units: str = r"-"
30
+ """String describing the sensor measurement units. Defaults to '-'. Latex
31
+ symbols can be used with a raw string.
32
+ """
33
+
17
34
  time_units: str = r"s"
35
+ """String describing time units. Defaults to 's'.
36
+ """
37
+
18
38
  symbol: str = r"m"
19
- tag: str = 'S'
39
+ """Symbol for describing the field the sensor measures. For example 'T' for
40
+ temperature of r'\epsilon' for strain. Latex symbols can be used with a raw
41
+ string.
42
+ """
43
+
44
+ tag: str = "S"
45
+ """String shorthand tag used to label sensors on pyvista plots. Defaults to
46
+ 'S'.
47
+ """
48
+
20
49
  components: tuple[str,...] | None = None
50
+ """Tuple of strings describing the field components. Defaults to None which
51
+ is used for scalar fields. For vector fields use ('x','y','z') for 3D and
52
+ for tensor fields use ('xx','yy','zz','xy','yz','xz').
53
+ """
54
+
21
55
 
22
56
  def create_label(self, comp_ind: int | None = None) -> str:
57
+ """Creates an axis label for a matplotlib plot based on the sensor
58
+ descriptor string. The axis label takes the form: 'name, symbol [units]'
59
+ This version creates a label with line breaks which is useful for
60
+ vertical colourbars.
61
+
62
+ Parameters
63
+ ----------
64
+ comp_ind : int | None, optional
65
+ Index of the field component to create a label for, by default None.
66
+ If None the first field component is used.
67
+
68
+ Returns
69
+ -------
70
+ str
71
+ Axis label for field component in the form: 'name, symbol [units]'.
72
+ """
23
73
  label = ""
24
74
  if self.name != "":
25
75
  label = label + rf"{self.name} "
@@ -37,6 +87,22 @@ class SensorDescriptor:
37
87
  return label
38
88
 
39
89
  def create_label_flat(self, comp_ind: int | None = None) -> str:
90
+ """Creates an axis label for a matplotlib plot based on the sensor
91
+ descriptor string. The axis label takes the form: 'name, symbol [units]'
92
+ This version creates a label with no line breaks which is useful for
93
+ axis labels on plots.
94
+
95
+ Parameters
96
+ ----------
97
+ comp_ind : int | None, optional
98
+ Index of the field component to create a label for, by default None.
99
+ If None the first field component is used.
100
+
101
+ Returns
102
+ -------
103
+ str
104
+ Axis label for field component in the form: 'name, symbol [units]'.
105
+ """
40
106
  label = ""
41
107
  if self.name != "":
42
108
  label = label + rf"{self.name} "
@@ -54,47 +120,94 @@ class SensorDescriptor:
54
120
  return label
55
121
 
56
122
  def create_sensor_tags(self,n_sensors: int) -> list[str]:
123
+ """Creates a list of numbered sensor tags for labelling sensor locations
124
+ or for graph legends. Tags are shorthand names for sensors such as TC
125
+ for thermocouples or SG for strain gauges.
126
+
127
+ Parameters
128
+ ----------
129
+ n_sensors : int
130
+ The number of sensors to create tags for.
131
+
132
+ Returns
133
+ -------
134
+ list[str]
135
+ A list of sensor tags
136
+ """
57
137
  z_width = int(np.log10(n_sensors))+1
58
138
 
59
139
  sensor_names = list()
60
140
  for ss in range(n_sensors):
61
- num_str = f'{ss+1}'.zfill(z_width)
62
- sensor_names.append(f'{self.tag}{num_str}')
141
+ num_str = f"{ss+1}".zfill(z_width)
142
+ sensor_names.append(f"{self.tag}{num_str}")
63
143
 
64
144
  return sensor_names
65
145
 
66
146
 
67
147
  class SensorDescriptorFactory:
148
+ """A factory for building common sensor descriptors for scalar, vector and
149
+ tensor fields. Builds descriptors for thermcouples, displacement sensors
150
+ and strain sensors.
151
+ """
152
+
68
153
  @staticmethod
69
154
  def temperature_descriptor() -> SensorDescriptor:
70
- descriptor = SensorDescriptor()
71
- descriptor.name = 'Temp.'
72
- descriptor.symbol = 'T'
73
- descriptor.units = r'^{\circ}C'
74
- descriptor.tag = 'TC'
155
+ """Creates a generic temperature sensor descriptor. Assumes the sensor
156
+ is measuring a temperature in degrees C.
157
+
158
+ Returns
159
+ -------
160
+ SensorDescriptor
161
+ The default temperature sensor descriptor.
162
+ """
163
+ descriptor = SensorDescriptor(name="Temp.",
164
+ symbol="T",
165
+ units=r"^{\circ}C",
166
+ tag="TC")
75
167
  return descriptor
76
168
 
77
169
  @staticmethod
78
170
  def displacement_descriptor() -> SensorDescriptor:
79
- descriptor = SensorDescriptor()
80
- descriptor.name = 'Disp.'
81
- descriptor.symbol = 'u'
82
- descriptor.units = r'm'
83
- descriptor.tag = 'DS'
84
- descriptor.components = ('x','y','z')
171
+ """Creates a generic displacement sensor descriptor. Assumes units of mm
172
+ and vector components of x,y,z.
173
+
174
+ Returns
175
+ -------
176
+ SensorDescriptor
177
+ The default displacement sensor descriptor.
178
+ """
179
+ descriptor = SensorDescriptor(name="Disp.",
180
+ symbol="u",
181
+ units=r"mm",
182
+ tag="DS",
183
+ components=("x","y","z"))
85
184
  return descriptor
86
185
 
87
186
  @staticmethod
88
187
  def strain_descriptor(spat_dims: int = 3) -> SensorDescriptor:
89
- descriptor = SensorDescriptor()
90
- descriptor.name = 'Strain'
91
- descriptor.symbol = r'\varepsilon'
92
- descriptor.units = r'-'
93
- descriptor.tag = 'SG'
188
+ """Creates a generic strain sensor descriptor. Assumes strain is
189
+ unitless and that the components are xx,yy,xy for 2D and xx,yy,zz,xy,yz,
190
+ xz for 3D.
191
+
192
+ Parameters
193
+ ----------
194
+ spat_dims : int, optional
195
+ Number of spatial dimensions used for setting the components of the
196
+ tensor strain field, by default 3.
197
+
198
+ Returns
199
+ -------
200
+ SensorDescriptor
201
+ The default strain sensor descriptor.
202
+ """
203
+ descriptor = SensorDescriptor(name="Strain",
204
+ symbol=r"\varepsilon",
205
+ units=r"-",
206
+ tag="SG")
94
207
 
95
208
  if spat_dims == 2:
96
- descriptor.components = ('xx','yy','xy')
209
+ descriptor.components = ("xx","yy","xy")
97
210
  else:
98
- descriptor.components = ('xx','yy','zz','xy','yz','xz')
211
+ descriptor.components = ("xx","yy","zz","xy","yz","xz")
99
212
 
100
213
  return descriptor
pyvale/sensortools.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 numpy as np
8
8
  import mooseherder as mh
@@ -115,7 +115,7 @@ def print_measurements(sens_array: ISensorArray,
115
115
  print_toterrs = tot_errs[sensors[0]:sensors[1],
116
116
  components[0]:components[1],
117
117
  time_steps[0]:time_steps[1]]
118
- print(f"total errors = \n {print_syserrs}")
118
+ print(f"total errors = \n {print_toterrs}")
119
119
 
120
120
  print()
121
121