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.
- pyvale/__init__.py +18 -3
- pyvale/analyticmeshgen.py +1 -0
- pyvale/analyticsimdatafactory.py +18 -13
- pyvale/analyticsimdatagenerator.py +105 -72
- pyvale/blendercalibrationdata.py +15 -0
- pyvale/blenderlightdata.py +26 -0
- pyvale/blendermaterialdata.py +15 -0
- pyvale/blenderrenderdata.py +30 -0
- pyvale/blenderscene.py +488 -0
- pyvale/blendertools.py +420 -0
- pyvale/camera.py +6 -5
- pyvale/cameradata.py +25 -7
- pyvale/cameradata2d.py +6 -4
- pyvale/camerastereo.py +217 -0
- pyvale/cameratools.py +206 -11
- pyvale/cython/rastercyth.py +6 -2
- pyvale/data/cal_target.tiff +0 -0
- pyvale/dataset.py +73 -14
- pyvale/errorcalculator.py +8 -10
- pyvale/errordriftcalc.py +10 -9
- pyvale/errorintegrator.py +19 -21
- pyvale/errorrand.py +33 -39
- pyvale/errorsyscalib.py +134 -0
- pyvale/errorsysdep.py +19 -22
- pyvale/errorsysfield.py +49 -41
- pyvale/errorsysindep.py +79 -175
- pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
- pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
- pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
- pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
- pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
- pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
- pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
- pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
- pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
- pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
- pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
- pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
- pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
- pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
- pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
- pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
- pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_1_scalarvisualisation.py +6 -9
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_2_scalarcasebuild.py +8 -11
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex2_1_analyticsensors.py +9 -12
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +8 -15
- pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
- pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
- pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
- pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
- pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastenp.py +3 -2
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_oneframe.py +2 -2
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_cypara.py +3 -8
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_pypara.py +6 -7
- pyvale/examples/{ex1_4_thermal2d.py → visualisation/ex1_1_plot_traces.py} +32 -16
- pyvale/examples/{features/ex_animation_tools_3dmonoblock.py → visualisation/ex2_1_animate_sim.py} +37 -31
- pyvale/experimentsimulator.py +107 -30
- pyvale/field.py +2 -9
- pyvale/fieldconverter.py +98 -22
- pyvale/fieldsampler.py +2 -2
- pyvale/fieldscalar.py +10 -10
- pyvale/fieldtensor.py +15 -17
- pyvale/fieldtransform.py +7 -2
- pyvale/fieldvector.py +6 -7
- pyvale/generatorsrandom.py +25 -47
- pyvale/imagedef2d.py +6 -2
- pyvale/integratorfactory.py +2 -2
- pyvale/integratorquadrature.py +50 -24
- pyvale/integratorrectangle.py +85 -7
- pyvale/integratorspatial.py +4 -4
- pyvale/integratortype.py +3 -3
- pyvale/output.py +17 -0
- pyvale/pyvaleexceptions.py +11 -0
- pyvale/raster.py +6 -5
- pyvale/rastercy.py +6 -4
- pyvale/rasternp.py +6 -4
- pyvale/rendermesh.py +6 -2
- pyvale/sensorarray.py +2 -2
- pyvale/sensorarrayfactory.py +52 -65
- pyvale/sensorarraypoint.py +29 -30
- pyvale/sensordata.py +2 -2
- pyvale/sensordescriptor.py +138 -25
- pyvale/sensortools.py +3 -3
- pyvale/simtools.py +67 -0
- pyvale/visualexpplotter.py +99 -57
- pyvale/visualimagedef.py +11 -7
- pyvale/visualimages.py +6 -4
- pyvale/visualopts.py +372 -58
- pyvale/visualsimanimator.py +42 -13
- pyvale/visualsimsensors.py +318 -0
- pyvale/visualtools.py +69 -13
- pyvale/visualtraceplotter.py +52 -165
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/METADATA +17 -14
- pyvale-2025.5.1.dist-info/RECORD +172 -0
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/WHEEL +1 -1
- pyvale/examples/analyticdatagen/__init__.py +0 -5
- pyvale/examples/ex1_1_thermal2d.py +0 -86
- pyvale/examples/ex1_2_thermal2d.py +0 -108
- pyvale/examples/ex1_3_thermal2d.py +0 -110
- pyvale/examples/ex1_5_thermal2d.py +0 -102
- pyvale/examples/ex2_1_thermal3d .py +0 -84
- pyvale/examples/ex2_2_thermal3d.py +0 -51
- pyvale/examples/ex2_3_thermal3d.py +0 -106
- pyvale/examples/ex3_1_displacement2d.py +0 -44
- pyvale/examples/ex3_2_displacement2d.py +0 -76
- pyvale/examples/ex3_3_displacement2d.py +0 -101
- pyvale/examples/ex3_4_displacement2d.py +0 -102
- pyvale/examples/ex4_1_strain2d.py +0 -54
- pyvale/examples/ex4_2_strain2d.py +0 -76
- pyvale/examples/ex4_3_strain2d.py +0 -97
- pyvale/examples/ex5_1_multiphysics2d.py +0 -75
- pyvale/examples/ex6_1_multiphysics2d_expsim.py +0 -115
- pyvale/examples/ex6_2_multiphysics3d_expsim.py +0 -160
- pyvale/examples/features/__init__.py +0 -5
- pyvale/examples/features/ex_area_avg.py +0 -89
- pyvale/examples/features/ex_calibration_error.py +0 -108
- pyvale/examples/features/ex_chain_field_errs.py +0 -141
- pyvale/examples/features/ex_field_errs.py +0 -78
- pyvale/examples/features/ex_sensor_single_angle_batch.py +0 -110
- pyvale/optimcheckfuncs.py +0 -153
- pyvale/visualsimplotter.py +0 -182
- pyvale-2025.4.1.dist-info/RECORD +0 -163
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
"""
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
|
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
|
-
|
|
81
|
-
|
|
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
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
+
|
pyvale/visualsimanimator.py
CHANGED
|
@@ -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
|
-
|
|
16
|
-
|
|
17
|
-
from pyvale.
|
|
18
|
-
|
|
19
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
68
|
-
position=vis_opts.
|
|
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
|
|