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
@@ -0,0 +1,318 @@
1
+ # ==============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ # ==============================================================================
6
+
7
+ """
8
+ This module contains functions for visualising virtual sensors on a simulation
9
+ mesh with simulated fields using pyvista.
10
+ """
11
+
12
+ import vtk #NOTE: has to be here to fix latex bug in pyvista/vtk
13
+ # See: https://github.com/pyvista/pyvista/discussions/2928
14
+ #NOTE: causes output to console to be suppressed unfortunately
15
+ #NOTE: May 2025, the console suppression output is fixed but the vtk import is
16
+ #still required tro make latex work.
17
+ import pyvista as pv
18
+
19
+ import mooseherder as mh
20
+
21
+ from pyvale.sensorarraypoint import SensorArrayPoint
22
+ from pyvale.fieldconverter import simdata_to_pyvista
23
+ from pyvale.visualopts import (VisOptsSimSensors,VisOptsImageSave)
24
+ from pyvale.visualtools import (create_pv_plotter,
25
+ get_colour_lims,
26
+ save_pv_image)
27
+
28
+
29
+ # TODO: this needs to be updated to allow the user to plot at sensor times not
30
+ # just simulation times. This will require interpolation of the underlying
31
+ # simulation fields.
32
+ def add_sim_field(pv_plot: pv.Plotter,
33
+ sensor_array: SensorArrayPoint,
34
+ component: str,
35
+ time_step: int,
36
+ vis_opts: VisOptsSimSensors,
37
+ ) -> tuple[pv.Plotter,pv.UnstructuredGrid]:
38
+ """Adds a simulation field to a pyvista plot object which is visualised on
39
+ the mesh using a colormap.
40
+
41
+ Parameters
42
+ ----------
43
+ pv_plot : pv.Plotter
44
+ Handle to the pyvista plot object to add the simulation field to.
45
+ sensor_array : SensorArrayPoint
46
+ Sensor array associated with the field to be plotted.
47
+ component : str
48
+ String key for the field component to be shown.
49
+ time_step : int
50
+ Time step to plot based on the time steps in the underlying simulation
51
+ data object.
52
+ vis_opts : VisOptsSimSensors
53
+ Dataclass containing options for controlling the appearance of the
54
+ virtual sensors.
55
+
56
+ Returns
57
+ -------
58
+ tuple[pv.Plotter,pv.UnstructuredGrid]
59
+ Tuple containing a handle to the pyvista plotter which has had the field
60
+ visualisation added and the pyvistas unstructured grid that was used to
61
+ plot the field.
62
+ """
63
+ sim_vis = sensor_array._field.get_visualiser()
64
+ sim_data = sensor_array._field.get_sim_data()
65
+ sim_vis[component] = sim_data.node_vars[component][:,time_step]
66
+ comp_ind = sensor_array._field.get_component_index(component)
67
+
68
+ scalar_bar_args = {"title":sensor_array._descriptor.create_label(comp_ind),
69
+ "vertical":vis_opts.colour_bar_vertical,
70
+ "title_font_size":vis_opts.colour_bar_font_size,
71
+ "label_font_size":vis_opts.colour_bar_font_size}
72
+
73
+ pv_plot.add_mesh(sim_vis,
74
+ scalars=component,
75
+ label="sim-data",
76
+ show_edges=vis_opts.show_edges,
77
+ show_scalar_bar=vis_opts.colour_bar_show,
78
+ scalar_bar_args=scalar_bar_args,
79
+ lighting=False,
80
+ clim=vis_opts.colour_bar_lims)
81
+
82
+ if vis_opts.time_label_pos is not None:
83
+ pv_plot.add_text(f"Time: {sim_data.time[time_step]} " + \
84
+ f"{sensor_array._descriptor.time_units}",
85
+ position=vis_opts.time_label_pos,
86
+ font_size=vis_opts.time_label_font_size,
87
+ name='time-label')
88
+
89
+ return (pv_plot,sim_vis)
90
+
91
+
92
+ # TODO: this should be able to take a list of ISensorArray and plot all of them
93
+ # on the same mesh.
94
+ def add_sensor_points_nom(pv_plot: pv.Plotter,
95
+ sensor_array: SensorArrayPoint,
96
+ vis_opts: VisOptsSimSensors,
97
+ ) -> pv.Plotter:
98
+ """Adds points and tagged labels showing the virtual sensor locations on
99
+ the simulation mesh in the given pyvista plot object.
100
+
101
+ Parameters
102
+ ----------
103
+ pv_plot : pv.Plotter
104
+ Pyvista plotter used to display the virtual sensor locations.
105
+ sensor_array : SensorArrayPoint
106
+ Sensor array for which the virtual sensor location will be shown.
107
+ vis_opts : VisOptsSimSensors
108
+ Dataclass containing options for controlling the appearance of the
109
+ virtual sensors.
110
+
111
+ Returns
112
+ -------
113
+ pv.Plotter
114
+ Pyvista plotter which has had the virtual sensor locations added.
115
+ """
116
+ vis_sens_nominal = pv.PolyData(sensor_array._sensor_data.positions)
117
+ vis_sens_nominal["labels"] = sensor_array._descriptor.create_sensor_tags(
118
+ sensor_array.get_measurement_shape()[0])
119
+
120
+ # Add points to show sensor locations
121
+ pv_plot.add_point_labels(vis_sens_nominal,"labels",
122
+ font_size=vis_opts.sens_label_font_size,
123
+ shape_color=vis_opts.sens_label_colour,
124
+ point_color=vis_opts.sens_colour_nom,
125
+ render_points_as_spheres=True,
126
+ point_size=vis_opts.sens_point_size,
127
+ always_visible=True)
128
+
129
+ return pv_plot
130
+
131
+
132
+ def add_sensor_points_pert(pv_plot: pv.Plotter,
133
+ sensor_array: SensorArrayPoint,
134
+ vis_opts: VisOptsSimSensors,
135
+ ) -> pv.Plotter:
136
+ """Adds points showing the perturbed virtual sensor locations on
137
+ the simulation mesh in the given pyvista plot object. Note that this will
138
+ only work if field errors are added perturbing the sensor locations.
139
+
140
+ Parameters
141
+ ----------
142
+ pv_plot : pv.Plotter
143
+ Pyvista plotter used to display the virtual sensor locations.
144
+ sensor_array : SensorArrayPoint
145
+ Sensor array for which the virtual sensor location will be shown.
146
+ vis_opts : VisOptsSimSensors
147
+ Dataclass containing options for controlling the appearance of the
148
+ virtual sensors.
149
+
150
+ Returns
151
+ -------
152
+ pv.Plotter
153
+ Pyvista plotter which has had the virtual sensor locations added.
154
+ """
155
+ sens_data_perturbed = sensor_array.get_sensor_data_perturbed()
156
+
157
+ if sens_data_perturbed is not None and vis_opts.show_perturbed_pos:
158
+ vis_sens_perturbed = pv.PolyData(sens_data_perturbed.positions)
159
+ vis_sens_perturbed["labels"] = ["",]*sensor_array.get_measurement_shape()[0]
160
+
161
+ pv_plot.add_point_labels(vis_sens_perturbed,"labels",
162
+ font_size=vis_opts.sens_label_font_size,
163
+ shape_color=vis_opts.sens_label_colour,
164
+ point_color=vis_opts.sens_colour_pert,
165
+ render_points_as_spheres=True,
166
+ point_size=vis_opts.sens_point_size,
167
+ always_visible=True)
168
+
169
+ return pv_plot
170
+
171
+
172
+ def plot_sim_mesh(sim_data: mh.SimData,
173
+ elem_dims: int,
174
+ vis_opts: VisOptsSimSensors | None = None,
175
+ ) -> pv.Plotter:
176
+ """Plots the simulation mesh without any fields. Useful for visualising
177
+ mesh geometry.
178
+
179
+ Parameters
180
+ ----------
181
+ sim_data : mh.SimData
182
+ Sim data object containing the mesh to plot.
183
+ elem_dims : int
184
+ Number of dimensions for the elements to be plotted.
185
+ vis_opts : VisOptsSimSensors | None, optional
186
+ Dataclass containing options for controlling the appearance of the
187
+ virtual sensors, by default None. If None then a default options
188
+ dataclass is created.
189
+
190
+ Returns
191
+ -------
192
+ pv.Plotter
193
+ Handle to the pyvista plotter that is showing the mesh.
194
+ """
195
+ if vis_opts is None:
196
+ vis_opts = VisOptsSimSensors()
197
+
198
+ (_,sim_vis) = simdata_to_pyvista(sim_data=sim_data,
199
+ components=None,
200
+ elem_dims=elem_dims)
201
+
202
+ pv_plot = create_pv_plotter(vis_opts)
203
+ pv_plot.add_mesh(sim_vis,
204
+ label="sim-data",
205
+ show_edges=vis_opts.show_edges,
206
+ lighting=False)
207
+ return pv_plot
208
+
209
+
210
+ def plot_sim_data(sim_data: mh.SimData,
211
+ component: str,
212
+ elem_dims: int,
213
+ time_step: int = -1,
214
+ vis_opts: VisOptsSimSensors | None = None
215
+ ) -> pv.Plotter:
216
+ """Plots the simulation mesh showing the specified phyiscal field at the
217
+ time step specified.
218
+
219
+ Parameters
220
+ ----------
221
+ sim_data : mh.SimData
222
+ simulation data object containing the mesh and field data to show.
223
+ component : str
224
+ String key for accessing the nodal field to visualise in the sim data
225
+ object.
226
+ elem_dims : int
227
+ Number of dimensions for the elements to be plotted.
228
+ time_step : int, optional
229
+ Simulation time step number to plot, by default -1 (the last time step).
230
+ vis_opts : VisOptsSimSensors | None, optional
231
+ Dataclass containing options for controlling the appearance of the
232
+ virtual sensors, by default None. If None then a default options
233
+ dataclass is created.
234
+
235
+ Returns
236
+ -------
237
+ pv.Plotter
238
+ Handle to the pyvista plotter showing the simulation mesh and field.
239
+ """
240
+ if vis_opts is None:
241
+ vis_opts = VisOptsSimSensors()
242
+
243
+ (_,sim_vis) = simdata_to_pyvista(sim_data,
244
+ (component,),
245
+ elem_dims)
246
+
247
+ sim_vis[component] = sim_data.node_vars[component][:,time_step]
248
+
249
+ pv_plot = create_pv_plotter(vis_opts)
250
+ pv_plot.add_mesh(sim_vis,
251
+ scalars=component,
252
+ label="sim-data",
253
+ show_edges=vis_opts.show_edges,
254
+ show_scalar_bar=vis_opts.colour_bar_show,
255
+ lighting=False,
256
+ clim=vis_opts.colour_bar_lims)
257
+
258
+ return pv_plot
259
+
260
+
261
+ def plot_point_sensors_on_sim(sensor_array: SensorArrayPoint,
262
+ component: str,
263
+ time_step: int = -1,
264
+ vis_opts: VisOptsSimSensors | None = None,
265
+ image_save_opts: VisOptsImageSave | None = None,
266
+ ) -> pv.Plotter:
267
+ """Creates a visualisation of the virtual sensor locations on the simulation
268
+ mesh showing the underlying field the sensors are sampling at the specified
269
+ time step.
270
+
271
+ Parameters
272
+ ----------
273
+ sensor_array : SensorArrayPoint
274
+ Sensor array containing the sensors to plot and the field to display.
275
+ component : str
276
+ String key for accessing the nodal field to visualise in the sim data
277
+ object.
278
+ time_step : int, optional
279
+ Simulation time step number to plot, by default -1 (the last time step).
280
+ vis_opts : VisOptsSimSensors | None, optional
281
+ Dataclass containing options for controlling the appearance of the
282
+ virtual sensors, by default None. If None then a default options
283
+ dataclass is created.
284
+ image_save_opts : VisOptsImageSave | None, optional
285
+ Dataclass containing options for saving image of the virtual sensor
286
+ visualisation, by default None. If None a default options dataclass is
287
+ created.
288
+
289
+ Returns
290
+ -------
291
+ pv.Plotter
292
+ Handle to the pyvista plotter showing the sensor locations.
293
+ """
294
+ if vis_opts is None:
295
+ vis_opts = VisOptsSimSensors()
296
+
297
+ sim_data = sensor_array._field.get_sim_data()
298
+ vis_opts.colour_bar_lims = get_colour_lims(
299
+ sim_data.node_vars[component][:,time_step],
300
+ vis_opts.colour_bar_lims)
301
+
302
+ pv_plot = create_pv_plotter(vis_opts)
303
+
304
+ pv_plot = add_sensor_points_pert(pv_plot,sensor_array,vis_opts)
305
+ pv_plot = add_sensor_points_nom(pv_plot,sensor_array,vis_opts)
306
+ (pv_plot,_) = add_sim_field(pv_plot,
307
+ sensor_array,
308
+ component,
309
+ time_step,
310
+ vis_opts)
311
+
312
+ pv_plot.camera_position = vis_opts.camera_position
313
+
314
+ if image_save_opts is not None:
315
+ save_pv_image(pv_plot,image_save_opts)
316
+
317
+ return pv_plot
318
+
pyvale/visualtools.py CHANGED
@@ -1,14 +1,19 @@
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 utility functions used for creating pyvale visualisations.
9
+ """
6
10
 
7
11
  from pathlib import Path
8
12
  import numpy as np
9
- #import vtk #NOTE: has to be here to fix latex bug in pyvista/vtk
13
+ import vtk #NOTE: has to be here to fix latex bug in pyvista/vtk
10
14
  # See: https://github.com/pyvista/pyvista/discussions/2928
11
- #NOTE: causes output to console to be suppressed unfortunately
15
+ # NOTE: causes output to console to be suppressed unfortunately
16
+ # NOTE: May2025 still needs include but does not suppress console output
12
17
  import pyvista as pv
13
18
  from pyvale.visualopts import (VisOptsSimSensors,
14
19
  VisOptsImageSave,
@@ -16,9 +21,19 @@ from pyvale.visualopts import (VisOptsSimSensors,
16
21
  VisOptsAnimation,
17
22
  EAnimationType)
18
23
 
19
- #TODO: Docstrings
20
-
21
24
  def create_pv_plotter(vis_opts: VisOptsSimSensors) -> pv.Plotter:
25
+ """Creates a pyvista plotter based on the input options.
26
+
27
+ Parameters
28
+ ----------
29
+ vis_opts : VisOptsSimSensors
30
+ Dataclass containing the visualisation options for creating the plotter.
31
+
32
+ Returns
33
+ -------
34
+ pv.Plotter
35
+ Blank pyvista plotter object with the given settings.
36
+ """
22
37
  pv_plot = pv.Plotter(window_size=vis_opts.window_size_px)
23
38
  pv_plot.set_background(vis_opts.background_colour)
24
39
  pv.global_theme.font.color = vis_opts.font_colour
@@ -27,19 +42,48 @@ def create_pv_plotter(vis_opts: VisOptsSimSensors) -> pv.Plotter:
27
42
 
28
43
 
29
44
  def get_colour_lims(component_data: np.ndarray,
30
- colour_bar_lims: tuple[float,float] | None
31
- ) -> tuple[float,float]:
32
-
45
+ colour_bar_lims: tuple[float,float] | None
46
+ ) -> tuple[float,float]:
47
+ """Gets the colourbar limits based on the input component data array.
48
+
49
+ Parameters
50
+ ----------
51
+ component_data : np.ndarray
52
+ Array of data for the field component of interest. Can be any shape as
53
+ the array is flattened for the limit calculations
54
+ colour_bar_lims : tuple[float,float] | None
55
+ Forces the colourbar limits to be the values give in the tuple. If None
56
+ then the colorbar limits are calculated based on the input data array.
57
+
58
+ Returns
59
+ -------
60
+ tuple[float,float]
61
+ Colourbar limits in the form: (min,max).
62
+ """
33
63
  if colour_bar_lims is None:
34
64
  min_comp = np.min(component_data.flatten())
35
65
  max_comp = np.max(component_data.flatten())
36
66
  colour_bar_lims = (min_comp,max_comp)
37
67
 
68
+ assert colour_bar_lims[1] > colour_bar_lims[0], ("Colourbar minimum must be"
69
+ + " smaller than the colourbar maximum.")
70
+
38
71
  return colour_bar_lims
39
72
 
40
73
 
41
- def save_image(pv_plot: pv.Plotter,
74
+ def save_pv_image(pv_plot: pv.Plotter,
42
75
  image_save_opts: VisOptsImageSave) -> None:
76
+ """Saves an image of a pyvista visualisation to disk based on the input
77
+ options.
78
+
79
+ Parameters
80
+ ----------
81
+ pv_plot : pv.Plotter
82
+ Pyvista plotter object to save the image from.
83
+ image_save_opts : VisOptsImageSave
84
+ Dataclass containing the options to save the image.
85
+ """
86
+
43
87
  if image_save_opts.path is None:
44
88
  image_save_opts.path = Path.cwd() / "pyvale-image"
45
89
 
@@ -55,6 +99,21 @@ def save_image(pv_plot: pv.Plotter,
55
99
 
56
100
  def set_animation_writer(pv_plot: pv.Plotter,
57
101
  anim_opts: VisOptsAnimation) -> pv.Plotter:
102
+ """Sets the animation writer and output path for a virtual sensor simulation
103
+ visualisation.
104
+
105
+ Parameters
106
+ ----------
107
+ pv_plot : pv.Plotter
108
+ Pyvistas plot object which will be used to create the animation.
109
+ anim_opts : VisOptsAnimation
110
+ Dataclass containing the options for creating the animation.
111
+
112
+ Returns
113
+ -------
114
+ pv.Plotter
115
+ Pyvista plotter with the given animation writer opened.
116
+ """
58
117
  if anim_opts.save_animation is None:
59
118
  return pv_plot
60
119
 
@@ -69,9 +128,6 @@ def set_animation_writer(pv_plot: pv.Plotter,
69
128
 
70
129
  elif anim_opts.save_animation == EAnimationType.MP4:
71
130
  anim_opts.save_path = anim_opts.save_path.with_suffix(".mp4")
72
- print(80*"=")
73
- print(f"{anim_opts.save_path=}")
74
- print(80*"=")
75
131
  pv_plot.open_movie(anim_opts.save_path,
76
132
  anim_opts.frames_per_second)
77
133