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
@@ -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 numpy as np
9
- from pyvale.core.field import IField
10
- from pyvale.core.sensorarray import ISensorArray
11
- from pyvale.core.errorintegrator import ErrIntegrator
12
- from pyvale.core.sensordescriptor import SensorDescriptor
13
- from pyvale.core.sensordata import SensorData
14
- from pyvale.core.fieldsampler import sample_field_with_sensor_data
8
+ from pyvale.field import IField
9
+ from pyvale.sensorarray import ISensorArray
10
+ from pyvale.errorintegrator import ErrIntegrator
11
+ from pyvale.sensordescriptor import SensorDescriptor
12
+ from pyvale.sensordata import SensorData
13
+ from pyvale.fieldsampler import sample_field_with_sensor_data
15
14
 
16
15
 
17
16
  class SensorArrayPoint(ISensorArray):
@@ -53,16 +52,15 @@ class SensorArrayPoint(ISensorArray):
53
52
  simulated physical fields quickly using finite element shape functions.
54
53
  """
55
54
 
56
- __slots__ = ("field","descriptor","sensor_data","_truth","_measurements",
57
- "error_integrator")
55
+ __slots__ = ("_field","_descriptor","_sensor_data","_truth","_measurements",
56
+ "_error_integrator")
58
57
 
59
58
  def __init__(self,
60
59
  sensor_data: SensorData,
61
60
  field: IField,
62
61
  descriptor: SensorDescriptor | None = None,
63
62
  ) -> None:
64
- """Initialiser for the `SensorArrayPoint` class.
65
-
63
+ """
66
64
  Parameters
67
65
  ----------
68
66
  sensor_data : SensorData
@@ -76,13 +74,13 @@ class SensorArrayPoint(ISensorArray):
76
74
  Contains descriptive information about the sensor array for display
77
75
  and visualisations, by default None.
78
76
  """
79
- self.sensor_data = sensor_data
80
- self.field = field
81
- self.error_integrator = None
77
+ self._sensor_data = sensor_data
78
+ self._field = field
79
+ self._error_integrator = None
82
80
 
83
- self.descriptor = SensorDescriptor()
81
+ self._descriptor = SensorDescriptor()
84
82
  if descriptor is not None:
85
- self.descriptor = descriptor
83
+ self._descriptor = descriptor
86
84
 
87
85
  self._truth = None
88
86
  self._measurements = None
@@ -97,10 +95,10 @@ class SensorArrayPoint(ISensorArray):
97
95
  np.ndarray
98
96
  Sample times with shape: (num_time_steps,)
99
97
  """
100
- if self.sensor_data.sample_times is None:
101
- return self.field.get_time_steps()
98
+ if self._sensor_data.sample_times is None:
99
+ return self._field.get_time_steps()
102
100
 
103
- return self.sensor_data.sample_times
101
+ return self._sensor_data.sample_times
104
102
 
105
103
  def get_measurement_shape(self) -> tuple[int,int,int]:
106
104
  """Gets the shape of the sensor measurement array. shape=(num_sensors,
@@ -113,8 +111,8 @@ class SensorArrayPoint(ISensorArray):
113
111
  num_field_components,num_time_steps)
114
112
  """
115
113
 
116
- return (self.sensor_data.positions.shape[0],
117
- len(self.field.get_all_components()),
114
+ return (self._sensor_data.positions.shape[0],
115
+ len(self._field.get_all_components()),
118
116
  self.get_sample_times().shape[0])
119
117
 
120
118
  def get_field(self) -> IField:
@@ -126,7 +124,7 @@ class SensorArrayPoint(ISensorArray):
126
124
  IField
127
125
  Reference to an `IField` interface.
128
126
  """
129
- return self.field
127
+ return self._field
130
128
 
131
129
 
132
130
  def calc_truth_values(self) -> np.ndarray:
@@ -140,8 +138,8 @@ class SensorArrayPoint(ISensorArray):
140
138
  Array of ground truth sensor values. shape=(num_sensors,
141
139
  num_field_components,num_time_steps).
142
140
  """
143
- self._truth = sample_field_with_sensor_data(self.field,
144
- self.sensor_data)
141
+ self._truth = sample_field_with_sensor_data(self._field,
142
+ self._sensor_data)
145
143
 
146
144
  return self._truth
147
145
 
@@ -171,7 +169,7 @@ class SensorArrayPoint(ISensorArray):
171
169
  err_int : ErrIntegrator
172
170
  Error integration object with a chain of user defined sensor errors.
173
171
  """
174
- self.error_integrator = err_int
172
+ self._error_integrator = err_int
175
173
 
176
174
  def get_sensor_data_perturbed(self) -> SensorData | None:
177
175
  """Gets the final sensor array parameters after all errors in the error
@@ -184,10 +182,10 @@ class SensorArrayPoint(ISensorArray):
184
182
  The accumulated sensor array parameters as a SensorData object.
185
183
  Returns None if no error integrator has been specified.
186
184
  """
187
- if self.error_integrator is None:
185
+ if self._error_integrator is None:
188
186
  return None
189
187
 
190
- return self.error_integrator.get_sens_data_accumulated()
188
+ return self._error_integrator.get_sens_data_accumulated()
191
189
 
192
190
  def get_errors_systematic(self) -> np.ndarray | None:
193
191
  """Gets the systematic error array from the previously calculated sensor
@@ -200,10 +198,10 @@ class SensorArrayPoint(ISensorArray):
200
198
  ,num_field_components,num_time_steps). Returns None if no error
201
199
  integrator has been set.
202
200
  """
203
- if self.error_integrator is None:
201
+ if self._error_integrator is None:
204
202
  return None
205
203
 
206
- return self.error_integrator.get_errs_systematic()
204
+ return self._error_integrator.get_errs_systematic()
207
205
 
208
206
  def get_errors_random(self) -> np.ndarray | None:
209
207
  """Gets the random error array from the previously calculated sensor
@@ -216,10 +214,10 @@ class SensorArrayPoint(ISensorArray):
216
214
  ,num_field_components,num_time_steps). Returns None if no error
217
215
  integrator has been set.
218
216
  """
219
- if self.error_integrator is None:
217
+ if self._error_integrator is None:
220
218
  return None
221
219
 
222
- return self.error_integrator.get_errs_random()
220
+ return self._error_integrator.get_errs_random()
223
221
 
224
222
  def get_errors_total(self) -> np.ndarray | None:
225
223
  """Gets the total error array from the previously calculated sensor
@@ -232,10 +230,10 @@ class SensorArrayPoint(ISensorArray):
232
230
  ,num_field_components,num_time_steps). Returns None if no error
233
231
  integrator has been set.
234
232
  """
235
- if self.error_integrator is None:
233
+ if self._error_integrator is None:
236
234
  return None
237
235
 
238
- return self.error_integrator.get_errs_total()
236
+ return self._error_integrator.get_errs_total()
239
237
 
240
238
  def calc_measurements(self) -> np.ndarray:
241
239
  """Calculates a set of sensor measurements using the specified sensor
@@ -254,11 +252,11 @@ class SensorArrayPoint(ISensorArray):
254
252
  systematic errors if an error integrator is specified. shape=(
255
253
  num_sensors,num_field_components,num_time_steps).
256
254
  """
257
- if self.error_integrator is None:
255
+ if self._error_integrator is None:
258
256
  self._measurements = self.get_truth()
259
257
  else:
260
258
  self._measurements = self.get_truth() + \
261
- self.error_integrator.calc_errors_from_chain(self.get_truth())
259
+ self._error_integrator.calc_errors_from_chain(self.get_truth())
262
260
 
263
261
  return self._measurements
264
262
 
@@ -1,14 +1,13 @@
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
  from dataclasses import dataclass
9
8
  import numpy as np
10
9
  from scipy.spatial.transform import Rotation
11
- from pyvale.core.integratortype import EIntSpatialType
10
+ from pyvale.integratortype import EIntSpatialType
12
11
 
13
12
 
14
13
  @dataclass(slots=True)
@@ -0,0 +1,213 @@
1
+
2
+ # ==============================================================================
3
+ # pyvale: the python validation engine
4
+ # License: MIT
5
+ # Copyright (C) 2025 The Computer Aided Validation Team
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
+ """
12
+
13
+ from dataclasses import dataclass
14
+ import numpy as np
15
+
16
+
17
+ @dataclass(slots=True)
18
+ class SensorDescriptor:
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
+
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
+
34
+ time_units: str = r"s"
35
+ """String describing time units. Defaults to 's'.
36
+ """
37
+
38
+ symbol: str = r"m"
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
+
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
+
55
+
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
+ """
73
+ label = ""
74
+ if self.name != "":
75
+ label = label + rf"{self.name} "
76
+
77
+
78
+ symbol = rf"${self.symbol}$ "
79
+ if comp_ind is not None and self.components is not None:
80
+ symbol = rf"${self.symbol}_{{{self.components[comp_ind]}}}$ "
81
+ if symbol != "":
82
+ label = label + symbol
83
+
84
+ if self.units != "":
85
+ label = label + "\n" + rf"[${self.units}$]"
86
+
87
+ return label
88
+
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
+ """
106
+ label = ""
107
+ if self.name != "":
108
+ label = label + rf"{self.name} "
109
+
110
+
111
+ symbol = rf"${self.symbol}$ "
112
+ if comp_ind is not None and self.components is not None:
113
+ symbol = rf"${self.symbol}_{{{self.components[comp_ind]}}}$ "
114
+ if symbol != "":
115
+ label = label + symbol
116
+
117
+ if self.units != "":
118
+ label = label + " " + rf"[${self.units}$]"
119
+
120
+ return label
121
+
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
+ """
137
+ z_width = int(np.log10(n_sensors))+1
138
+
139
+ sensor_names = list()
140
+ for ss in range(n_sensors):
141
+ num_str = f"{ss+1}".zfill(z_width)
142
+ sensor_names.append(f"{self.tag}{num_str}")
143
+
144
+ return sensor_names
145
+
146
+
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
+
153
+ @staticmethod
154
+ def temperature_descriptor() -> SensorDescriptor:
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")
167
+ return descriptor
168
+
169
+ @staticmethod
170
+ def displacement_descriptor() -> SensorDescriptor:
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"))
184
+ return descriptor
185
+
186
+ @staticmethod
187
+ def strain_descriptor(spat_dims: int = 3) -> SensorDescriptor:
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")
207
+
208
+ if spat_dims == 2:
209
+ descriptor.components = ("xx","yy","xy")
210
+ else:
211
+ descriptor.components = ("xx","yy","zz","xy","yz","xz")
212
+
213
+ return descriptor
@@ -1,13 +1,12 @@
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 numpy as np
9
8
  import mooseherder as mh
10
- from pyvale.core.sensorarray import ISensorArray
9
+ from pyvale.sensorarray import ISensorArray
11
10
 
12
11
 
13
12
  def create_sensor_pos_array(num_sensors: tuple[int,int,int],
@@ -116,7 +115,7 @@ def print_measurements(sens_array: ISensorArray,
116
115
  print_toterrs = tot_errs[sensors[0]:sensors[1],
117
116
  components[0]:components[1],
118
117
  time_steps[0]:time_steps[1]]
119
- print(f"total errors = \n {print_syserrs}")
118
+ print(f"total errors = \n {print_toterrs}")
120
119
 
121
120
  print()
122
121
 
@@ -5,7 +5,7 @@
5
5
  #-------------------------------------------------------------------------
6
6
  #_* MOOSEHERDER VARIABLES - START
7
7
 
8
- endTime = 50
8
+ endTime = 20
9
9
  timeStep = 1
10
10
 
11
11
  # Geometric Properties
@@ -14,9 +14,9 @@ lengY = 10e-3 # m
14
14
  lengZ = 10e-3 # m
15
15
 
16
16
  # Mesh Properties
17
- nElemX = 1
18
- nElemY = 1
19
- nElemZ = 1
17
+ nElemX = 2
18
+ nElemY = 2
19
+ nElemZ = 2
20
20
  eType = HEX20 # TET10, TET11, HEX20, HEX27
21
21
 
22
22
  # Thermal BCs
@@ -89,7 +89,7 @@ ThermExp = 17.8e-6 # 1/degC
89
89
  add_variables = true
90
90
  material_output_family = MONOMIAL # MONOMIAL, LAGRANGE
91
91
  material_output_order = FIRST # CONSTANT, FIRST, SECOND,
92
- generate_output = 'stress_xx stress_yy stress_zz stress_xy stress_yz stress_xz strain_xx strain_yy strain_zz strain_xy strain_yz strain_xz'
92
+ generate_output = 'strain_xx strain_yy strain_zz strain_xy strain_yz strain_xz'
93
93
  []
94
94
  []
95
95
 
@@ -5,7 +5,7 @@
5
5
  #-------------------------------------------------------------------------
6
6
  #_* MOOSEHERDER VARIABLES - START
7
7
 
8
- endTime = 50
8
+ endTime = 20
9
9
  timeStep = 1
10
10
 
11
11
  # Geometric Properties
@@ -14,9 +14,9 @@ lengY = 10e-3 # m
14
14
  lengZ = 10e-3 # m
15
15
 
16
16
  # Mesh Properties
17
- nElemX = 1
18
- nElemY = 1
19
- nElemZ = 1
17
+ nElemX = 2
18
+ nElemY = 2
19
+ nElemZ = 2
20
20
  eType = HEX27 # TET10, TET11, HEX20, HEX27
21
21
 
22
22
  # Thermal BCs
@@ -89,7 +89,7 @@ ThermExp = 17.8e-6 # 1/degC
89
89
  add_variables = true
90
90
  material_output_family = MONOMIAL # MONOMIAL, LAGRANGE
91
91
  material_output_order = FIRST # CONSTANT, FIRST, SECOND,
92
- generate_output = 'stress_xx stress_yy stress_zz stress_xy stress_yz stress_xz strain_xx strain_yy strain_zz strain_xy strain_yz strain_xz'
92
+ generate_output = 'strain_xx strain_yy strain_zz strain_xy strain_yz strain_xz'
93
93
  []
94
94
  []
95
95