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
pyvale/visualopts.py CHANGED
@@ -1,112 +1,350 @@
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
+ """
8
+ This module contains options dataclasses for controlling the appearance of
9
+ visualisations in pyvale.
10
+ """
11
+ import platform
7
12
  from pathlib import Path
8
13
  import enum
9
- from dataclasses import dataclass
14
+ from dataclasses import dataclass, field
10
15
  import numpy as np
11
16
  import matplotlib as plt
12
17
 
13
18
 
14
19
  @dataclass(slots=True)
15
20
  class PlotOptsGeneral:
16
- """ Dataclass for controlling the properties of figures and graphs such as
21
+ """Dataclass for controlling the properties of figures and graphs such as
17
22
  figure size, resolution, font sizes, marker sizes, line widths and
18
- colormaps.
23
+ colormaps. This dataclass is used to interact with matplotlib and pyvista
24
+ so units conform to these packages. The defaults set in this dataclass are
25
+ selected based on producing print quality figures for journal articles.
19
26
  """
27
+
20
28
  aspect_ratio: float = 1.62
21
- # These are in inches because of matplotlib
22
- a4_width: float = 8.25
23
- a4_height: float = 11.75
24
- a4_margin_width: float = 0.5
25
- a4_margin_height: float = 0.5
26
- a4_print_width: float = a4_width-2*a4_margin_width
27
- a4_print_height: float = a4_height-2*a4_margin_height
29
+ """Aspect ratio of the figure canvas.
30
+ """
28
31
 
29
32
  single_fig_scale: float = 0.5
30
-
31
- single_fig_size_square: tuple[float,float] = (
32
- a4_print_width*single_fig_scale,
33
- a4_print_width*single_fig_scale
34
- )
35
- single_fig_size_portrait: tuple[float,float] = (
36
- a4_print_width*single_fig_scale/aspect_ratio,
37
- a4_print_width*single_fig_scale
38
- )
39
- single_fig_size_landscape: tuple[float,float] = (
40
- a4_print_width*single_fig_scale,
41
- a4_print_width*single_fig_scale/aspect_ratio
42
- )
33
+ """Scaling for a single column figure, defaults to a half (0.5) page width.
34
+ """
43
35
 
44
36
  resolution: float = 300.0
37
+ """Figure resolution in dpi, defaults to 300dpi for print quality.
38
+ """
39
+
40
+ font_def_weight: str = "normal"
41
+ """Default weight for fonts on plots.
42
+ """
45
43
 
46
- font_name: str = 'Liberation Sans'
47
- font_def_weight: str = 'normal'
48
44
  font_def_size: float = 8.0
45
+ """Default font size for plots.
46
+ """
47
+
49
48
  font_tick_size: float = 8.0
49
+ """Default font tick label size
50
+ """
51
+
50
52
  font_head_size: float = 9.0
53
+ """Default font size for headings/titles on plots.
54
+ """
55
+
51
56
  font_ax_size: float = 8.0
57
+ """Default axis label font size.
58
+ """
59
+
52
60
  font_leg_size: float = 8.0
61
+ """Default font size for legends.
62
+ """
53
63
 
54
64
  ms: float = 3.2
65
+ """Marker size for points on plots
66
+ """
67
+
55
68
  lw: float = 0.8
69
+ """Line width for traces on plots.
70
+ """
56
71
 
57
72
  cmap_seq: str = "cividis"
73
+ """The colormap to use for monotonic fields
74
+ """
75
+
58
76
  cmap_div: str = "RdBu"
77
+ """The colormap to use for diverging fields, defaults to Red-Blue.
78
+ """
79
+
80
+ colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
81
+ """Color cycle for lines on plots.
82
+ """
83
+
84
+ # These are in inches because of matplotlib
85
+ a4_width: float = 8.25
86
+ """Width of an A4 page in inches.
87
+ """
88
+
89
+ a4_height: float = 11.75
90
+ """Height of an A4 page in inches.
91
+ """
92
+
93
+ a4_margin_width: float = 0.5
94
+ """Margin width on an A4 page in inches.
95
+ """
96
+
97
+ a4_margin_height: float = 0.5
98
+ """Margin heigh on an A4 page in inches.
99
+ """
100
+
101
+ font_name: str = field(init=False)
102
+ """Does not need to be initialised, calculated from other inputs. Name of
103
+ the font to use. Defaults to Arial on Windows/Mac and Liberation Sans on
104
+ Linux.
105
+ """
106
+
107
+ a4_print_width: float = field(init=False)
108
+ """Does not need to be initialised, calculated from other inputs. Printable
109
+ width of an A4 page in inches based on subtracting twice the margin width.
110
+ """
111
+
112
+ a4_print_height: float = field(init=False)
113
+ """Does not need to be initialised, calculated from other inputs. Printable
114
+ height of an A4 page in inches based on subtracting twice the margin width.
115
+ """
116
+
117
+ single_fig_size_square: tuple[float,float] = field(init=False)
118
+ """Does not need to be initialised, calculated from other inputs. Uses the
119
+ printable A4 width and the single figure scaling to create a square canvas
120
+ that fits in a single column of a two column journal article.
121
+ """
122
+
123
+ single_fig_size_portrait: tuple[float,float] = field(init=False)
124
+ """Does not need to be initialised, calculated from other inputs. Uses the
125
+ printable A4 width and the single figure scaling to create a potrait canvas
126
+ that fits in a single column of a two column journal article.
127
+ """
128
+
129
+ single_fig_size_landscape: tuple[float,float] = field(init=False)
130
+ """Does not need to be initialised, calculated from other inputs. Uses the
131
+ printable A4 width and the single figure scaling to create a landscape
132
+ canvas that fits in a single column of a two column journal article.
133
+ """
134
+
135
+ colors_num: int = field(init=False)
136
+ """Does not need to be initialised, calculated from other inputs. The number
137
+ of colors in the line color cycle.
138
+ """
59
139
 
60
- colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
61
- n_colors = len(plt.rcParams['axes.prop_cycle'].by_key()['color'])
62
140
 
63
141
  def __post_init__(self) -> None:
64
- plt.rc('font', size=self.font_def_size)
65
- plt.rc('axes', titlesize=self.font_def_size)
142
+
143
+ self.font_name = "Arial"
144
+ if platform.system() == "Linux":
145
+ self.font_name = "Liberation Sans"
146
+
147
+ self.a4_print_width = self.a4_width-2*self.a4_margin_width
148
+ self.a4_print_height = self.a4_height-2*self.a4_margin_height
149
+
150
+ self.single_fig_size_square = (
151
+ self.a4_print_width*self.single_fig_scale,
152
+ self.a4_print_width*self.single_fig_scale
153
+ )
154
+
155
+ self.single_fig_size_portrait = (
156
+ self.a4_print_width*self.single_fig_scale/self.aspect_ratio,
157
+ self.a4_print_width*self.single_fig_scale
158
+ )
159
+ self.single_fig_size_landscape = (
160
+ self.a4_print_width*self.single_fig_scale,
161
+ self.a4_print_width*self.single_fig_scale/self.aspect_ratio
162
+ )
163
+
164
+ self.colors_num = len(self.colors)
165
+
166
+ plt.rc("font", size=self.font_def_size)
167
+ plt.rc("axes", titlesize=self.font_def_size)
66
168
 
67
169
 
68
170
  @dataclass(slots=True)
69
171
  class TraceOptsSensor:
70
- """Dataclass for controlling the properties of sensor trace plots.
172
+ """Dataclass for controlling the appearance of sensor trace plots including
173
+ axis labels, line styles and time over which to plot the sensor traces. Note
174
+ that latex symbols can be used in label strings by using a python raw string
175
+ . For example: r"strain, $\epsilon$ [-]".
176
+ """
177
+
178
+ legend_loc: str | None = "best"
179
+ """Set the legend location based on matplotlib legend location string. If
180
+ None then no legend is added. The legend lists the sensors by tag
181
+ """
182
+
183
+ x_label: str = r"x [$mm$]"
184
+ """Label for the x axis defaults to: r"x [$mm$]".
185
+ """
186
+
187
+ y_label: str = r"y [$mm$]"
188
+ """Label for the y axis defaults to: r"y [$mm$]".
189
+ """
190
+
191
+ z_label: str = r"z [$mm$]"
192
+ """Label for the z axis defaults to: r"z [$mm$]".
71
193
  """
72
- legend: bool = True
73
194
 
74
- x_label: str = r"x [$m$]"
75
- y_label: str = r"y [$m$]"
76
- z_label: str = r"z [$m$]"
77
195
  time_label: str = r"Time, $t$ [$s$]"
196
+ """Label for the time axis for traces pots which is assumed to be the
197
+ horizontal axis.
198
+ """
78
199
 
79
200
  truth_line: str | None = "-"
80
- sim_line: str | None = "-"
81
- meas_line: str = "--o"
201
+ """Matplotlib line style string for the ground truth virtual sensor values.
202
+ If None then the truth line is not plotted for all virtual sensors.
203
+ """
204
+
205
+ sim_line: str | None = None
206
+ """Matplotlib line style for the simulation output at the virtual sensor
207
+ locations. If None then the line is not plotted for all virtual sensors.
208
+ """
209
+
210
+ meas_line: str = "--+"
211
+ """Matplotlib line style for the virtual sensor measurement traces.
212
+ """
82
213
 
83
214
  sensors_to_plot: np.ndarray | None = None
215
+ """Array (1D) of indices for the sensors to plot. If None then all sensors
216
+ are plotted. Defaults to None.
217
+ """
218
+
84
219
  time_min_max: tuple[float,float] | None = None
220
+ """Time range over which to plot the sensor traces. If None then the full
221
+ time range is plotted. Defaults to None.
222
+ """
223
+
224
+
225
+ class EExpVisCentre(enum.Enum):
226
+ """Enumeration for plotting the center of the distribution of a series of
227
+ virtual sensor experiment traces.
228
+ """
229
+
230
+ MEAN = enum.auto()
231
+ """Mean over all virtual experiments for plotting the center of the virtual
232
+ experiment traces.
233
+ """
234
+
235
+ MEDIAN = enum.auto()
236
+ """Median over all virtual experiments for plotting the center of the
237
+ virtual experiment traces.
238
+ """
239
+
240
+
241
+ class EExpVisBounds(enum.Enum):
242
+ """Enumeration for plotting the uncertainty bounds of a series of virtual
243
+ sensor experiment traces. The uncertainty bounds are shown by filling
244
+ between the given upper and lower bounds. See the experient trace opts
245
+ dataclass which also allows for a scaling factor to be set to allow for
246
+ plotting a given multiple of the standard deviation.
247
+ """
248
+
249
+ MINMAX = enum.auto()
250
+ """Minimum and maximum over all virtual experiments for each sampling point.
251
+ """
252
+
253
+ QUARTILE = enum.auto()
254
+ """Lower 25% and upper 75% quartiles over all virtual experiments for each
255
+ sampling point.
256
+ """
257
+
258
+ MAD = enum.auto()
259
+ """Median absolute deviation over all virtual experiments for each sampling
260
+ point.
261
+ """
262
+
263
+ STD = enum.auto()
264
+ """Standard deviation over all virtual sensor experiments for each sampling
265
+ point.
266
+ """
267
+
85
268
 
86
269
 
87
270
  @dataclass(slots=True)
88
271
  class TraceOptsExperiment:
89
- """Dataclass for contorlling the properties of sensor trace plots from
272
+ """Dataclass for controlling the properties of sensor trace plots from
90
273
  batches of simulated experiments.
91
274
  """
92
- legend: bool = True
93
275
 
94
- x_label: str = r"x [$m$]"
95
- y_label: str = r"y [$m$]"
96
- z_label: str = r"z [$m$]"
276
+ legend_loc: str | None = "best"
277
+ """Set the legend location based on matplotlib legend location string. If
278
+ None then no legend is added. The legend lists the sensors by tag
279
+ """
280
+
281
+ x_label: str = r"x [$mm$]"
282
+ """Label for the x axis defaults to: r"x [$mm$]".
283
+ """
284
+
285
+ y_label: str = r"y [$mm$]"
286
+ """Label for the y axis defaults to: r"y [$mm$]".
287
+ """
288
+
289
+ z_label: str = r"z [$mm$]"
290
+ """Label for the z axis defaults to: r"z [$mm$]".
291
+ """
292
+
97
293
  time_label: str = r"Time, $t$ [$s$]"
294
+ """Label for the time axis for traces pots which is assumed to be the
295
+ horizontal axis.
296
+ """
297
+
298
+ truth_line: str | None = "-"
299
+ """Matplotlib line style string for the ground truth virtual sensor values.
300
+ If None then the truth line is not plotted for all virtual sensors.
301
+ """
98
302
 
99
- truth_line: str | None = None
100
303
  sim_line: str | None = None
101
- exp_mean_line: str = "-"
304
+ """Matplotlib line style for the simulation output at the virtual sensor
305
+ locations. If None then the line is not plotted for all virtual sensors.
306
+ """
307
+
308
+ exp_centre_line: str = "-"
309
+ """Matplotlib line style string for the experiment centre line.
310
+ """
311
+
102
312
  exp_marker_line: str = "+"
313
+ """Maplotlib line style string use for plotting all experiments.
314
+ """
103
315
 
104
316
  sensors_to_plot: np.ndarray | None = None
317
+ """Array (1D) of indices for the sensors to plot. If None then all sensors
318
+ are plotted. Defaults to None.
319
+ """
320
+
105
321
  time_min_max: tuple[float,float] | None = None
322
+ """Time range over which to plot the sensor traces. If None then the full
323
+ time range is plotted. Defaults to None.
324
+ """
325
+
326
+ centre: EExpVisCentre = EExpVisCentre.MEAN
327
+ """Specifies the summary statistic to use for the center line of the sensor
328
+ trace distribution. Defaults to EExpVisCentre.MEAN.
329
+ """
330
+
331
+ fill_between: EExpVisBounds | None = EExpVisBounds.MINMAX
332
+ """Specifies the summary statistic to use for plotting the uncertainty
333
+ bounds for the virtual sensor traces. Defaults to EExpVisBounds.MINMAX.
334
+ Note that this statistic will be multipled by the fill_scale parameter.
335
+ """
336
+
337
+ fill_scale: float = 1.0
338
+ """Scaling factor multiplied by the uncertainty bound summary statistic for
339
+ showing filled uncertainty bounds on sensor traces plots. Defaults to 1.0.
340
+ A common setting would be 2.0 or 3.0 while setting fill_between =
341
+ EExpVisBounds.STD (standard deviation).
342
+ """
106
343
 
107
- centre: str = "mean"
108
344
  plot_all_exp_points: bool = False
109
- fill_between: str | None = "3std"
345
+ """Allows all experiment points to be plotted. Note that for more than 100
346
+ experiments for a given sensor array this will be slow. Defaults to False.
347
+ """
110
348
 
111
349
 
112
350
  @dataclass(slots=True)
@@ -116,56 +354,130 @@ class VisOptsSimSensors:
116
354
  """
117
355
  # pyvista ops
118
356
  window_size_px: tuple[int,int] = (1280,800)
357
+ """Window size for pyvista canvas in pixels: (horizontal_px,vertical_px).
358
+ """
359
+
119
360
  camera_position: np.ndarray | str = "xy"
361
+ """Camera position for the pyvista view either as a string of axis labels
362
+ or as a 3x3 rotation matrix. Defaults to viewing the x-y plane with "xy".
363
+ """
364
+
120
365
  show_edges: bool = True
366
+ """Flag to show the element edges in visualisations. Defaults to True.
367
+ """
368
+
121
369
  interactive: bool = True
370
+ """Flag to allow interactive viewing of the plot. Defaults to True.
371
+ """
122
372
 
123
373
  font_colour: str = "black"
124
- background_colour: str = "white" # "white"
374
+ """Font colour string. Useful for creating "dark mode" style plots with
375
+ "white" font and a "black background". Defaults to "light mode" with "black"
376
+ font.
377
+ """
125
378
 
126
- time_label_font_size: float = 12
127
- time_label_position: str = "upper_left"
128
- time_label_show: bool = True
379
+ background_colour: str = "white"
380
+ """Background colour string. Useful for creating "dark mode" style plots
381
+ with "white" font and a "black background". Defaults to "light mode" with
382
+ "black" font.
383
+ """
384
+
385
+ time_label_pos: str | None = "upper_left"
386
+ """Position of the simulation time step label. If None then the simulation
387
+ time step label is not shown. Defaults to "upper_left".
388
+ """
389
+
390
+ time_label_font_size: int = 12
391
+ """Font size for the simulation time step label on the canvas. Defaults to
392
+ 12.
393
+ """
129
394
 
130
- colour_bar_font_size: float = 18
131
395
  colour_bar_show: bool = True
396
+ """Flag to show the colourbar for the simulation field. Defaults to True.
397
+ """
398
+
399
+ colour_bar_font_size: int = 18
400
+ """Font size for the colourbar. Defaults to 18.
401
+ """
402
+
132
403
  colour_bar_lims: tuple[float,float] | None = None
404
+ """Max and min limits for the colour bar. If None the default limits are
405
+ used.
406
+ """
407
+
133
408
  colour_bar_vertical: bool = True
409
+ """Flag to set the colourbar to vertical instead of horizontal. Defaults to
410
+ True.
411
+ """
134
412
 
135
413
  # pyvale ops
136
414
  show_perturbed_pos: bool = True
415
+ """Flag to show the perturbed sensor positions if field errors are used.
416
+ Defaults to True.
417
+ """
418
+
137
419
  sens_colour_nom: str = "red"
420
+ """Colour for the markers showing the nominal sensor locations.
421
+ """
422
+
138
423
  sens_colour_pert: str = "blue"
139
- sens_point_size: float = 20
140
- sens_label_font_size: float = 30
424
+ """Colour for the markers showing the perturbed sensor locations.
425
+ """
426
+
427
+ sens_point_size: float = 20.0
428
+ """Size for the markers used to show the sensor locations on the mesh.
429
+ """
430
+
431
+ sens_label_font_size: int = 30
432
+ """Font size for the sensor marker labels.
433
+ """
434
+
141
435
  sens_label_colour: str = "grey"
436
+ """Colour for the sensor labels. Note that this needs to provide reasonable
437
+ contrast with the selected font colour so "grey" is the default.
438
+ """
142
439
 
143
440
 
144
441
  class EImageType(enum.Enum):
442
+ """NOTE: This is a feature under developement.
443
+
444
+ Enumeration for specifying the format for saving images.
445
+ """
145
446
  PNG = enum.auto()
146
447
  SVG = enum.auto()
147
448
 
449
+
148
450
  @dataclass(slots=True)
149
451
  class VisOptsImageSave:
150
- """Dataclass for image save options.
452
+ """NOTE: This is a feature under developement.
453
+
454
+ Dataclass for image saving options.
151
455
  """
456
+
152
457
  path: Path | None = None
153
458
  image_type: EImageType = EImageType.PNG
154
459
  transparent_background: bool = False
155
460
 
156
461
 
462
+
157
463
  class EAnimationType(enum.Enum):
464
+ """NOTE: This is a feature under developement.
465
+
466
+ Enumeration for specifying the save file type for animations.
467
+ """
158
468
  MP4 = enum.auto()
159
469
  GIF = enum.auto()
160
470
 
471
+
161
472
  @dataclass(slots=True)
162
473
  class VisOptsAnimation:
163
- """Dataclass for animation save options.
474
+ """NOTE: This is a feature under developement.
475
+
476
+ Dataclass for animation save options.
164
477
  """
478
+
165
479
  frames_per_second: float = 10.0
166
480
  off_screen: bool = False
167
-
168
- # save options
169
481
  save_animation: EAnimationType | None = None
170
482
  save_path: Path | None = None
171
483
 
@@ -177,3 +489,5 @@ class VisOptsAnimation:
177
489
 
178
490
 
179
491
 
492
+
493
+
@@ -1,8 +1,13 @@
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
+
7
+ """
8
+ This module contains functions for animating simulation fields including
9
+ visualisation of virtual sensor locations on the simulation mesh using pyvista.
10
+ """
6
11
 
7
12
  import numpy as np
8
13
  #import vtk #NOTE: has to be here to fix latex bug in pyvista/vtk
@@ -12,13 +17,12 @@ import pyvista as pv
12
17
  from pyvale.sensorarraypoint import SensorArrayPoint
13
18
  from pyvale.visualopts import VisOptsSimSensors, VisOptsAnimation
14
19
  from pyvale.visualtools import (create_pv_plotter,
15
- get_colour_lims,
16
- set_animation_writer)
17
- from pyvale.visualsimplotter import (add_sensor_points_nom,
18
- add_sensor_points_pert,
19
- add_sim_field)
20
+ get_colour_lims,
21
+ set_animation_writer)
22
+ from pyvale.visualsimsensors import (add_sensor_points_nom,
23
+ add_sensor_points_pert,
24
+ add_sim_field)
20
25
 
21
- #TODO: Docstrings
22
26
 
23
27
  def animate_sim_with_sensors(sensor_array: SensorArrayPoint,
24
28
  component: str,
@@ -26,7 +30,32 @@ def animate_sim_with_sensors(sensor_array: SensorArrayPoint,
26
30
  vis_opts: VisOptsSimSensors | None = None,
27
31
  anim_opts: VisOptsAnimation | None = None,
28
32
  ) -> pv.Plotter:
29
-
33
+ """Creates an animation of the simulation fields using pyvista showing the
34
+ virtual sensor locations during the animation.
35
+
36
+ Parameters
37
+ ----------
38
+ sensor_array : SensorArrayPoint
39
+ Sensor array that will be displayed on the simulation while the
40
+ simulation results are animated.
41
+ component : str
42
+ String key for the field component to animate.
43
+ time_steps : np.ndarray | None, optional
44
+ Time steps over which to creatre the animation, by default None. If None
45
+ then the animation is performed over all time steps.
46
+ vis_opts : VisOptsSimSensors | None, optional
47
+ Dataclass containing options for controlling the appearance of the
48
+ virtual sensors, by default None. If None a default options dataclass is
49
+ created.
50
+ anim_opts : VisOptsAnimation | None, optional
51
+ Dataclass containing options for controlling the animation output, by
52
+ default None. If None then a default options dataclass is created.
53
+
54
+ Returns
55
+ -------
56
+ pv.Plotter
57
+ Handle to the pyvista plotter object used to create the animation.
58
+ """
30
59
  if vis_opts is None:
31
60
  vis_opts = VisOptsSimSensors()
32
61
 
@@ -36,7 +65,7 @@ def animate_sim_with_sensors(sensor_array: SensorArrayPoint,
36
65
  if time_steps is None:
37
66
  time_steps = np.arange(0,sensor_array.get_sample_times().shape[0])
38
67
 
39
- sim_data = sensor_array.field.get_sim_data()
68
+ sim_data = sensor_array._field.get_sim_data()
40
69
  vis_opts.colour_bar_lims = get_colour_lims(
41
70
  sim_data.node_vars[component][:,time_steps],
42
71
  vis_opts.colour_bar_lims)
@@ -62,10 +91,10 @@ def animate_sim_with_sensors(sensor_array: SensorArrayPoint,
62
91
  # Updates the field plotted on the mesh
63
92
  sim_vis[component] = sim_data.node_vars[component][:,tt]
64
93
 
65
- if vis_opts.time_label_show:
94
+ if vis_opts.time_label_pos is not None:
66
95
  pv_plot.add_text(f"Time: {sim_data.time[tt]} " + \
67
- f"{sensor_array.descriptor.time_units}",
68
- position=vis_opts.time_label_position,
96
+ f"{sensor_array._descriptor.time_units}",
97
+ position=vis_opts.time_label_pos,
69
98
  font_size=vis_opts.time_label_font_size,
70
99
  name='time-label')
71
100