pyvale 2025.5.3__cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.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 +89 -0
- pyvale/analyticmeshgen.py +102 -0
- pyvale/analyticsimdatafactory.py +91 -0
- pyvale/analyticsimdatagenerator.py +323 -0
- 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 +146 -0
- pyvale/cameradata.py +69 -0
- pyvale/cameradata2d.py +84 -0
- pyvale/camerastereo.py +217 -0
- pyvale/cameratools.py +522 -0
- pyvale/cython/rastercyth.c +32211 -0
- pyvale/cython/rastercyth.cpython-311-x86_64-linux-gnu.so +0 -0
- pyvale/cython/rastercyth.py +640 -0
- pyvale/data/__init__.py +5 -0
- pyvale/data/cal_target.tiff +0 -0
- pyvale/data/case00_HEX20_out.e +0 -0
- pyvale/data/case00_HEX27_out.e +0 -0
- pyvale/data/case00_HEX8_out.e +0 -0
- pyvale/data/case00_TET10_out.e +0 -0
- pyvale/data/case00_TET14_out.e +0 -0
- pyvale/data/case00_TET4_out.e +0 -0
- pyvale/data/case13_out.e +0 -0
- pyvale/data/case16_out.e +0 -0
- pyvale/data/case17_out.e +0 -0
- pyvale/data/case18_1_out.e +0 -0
- pyvale/data/case18_2_out.e +0 -0
- pyvale/data/case18_3_out.e +0 -0
- pyvale/data/case25_out.e +0 -0
- pyvale/data/case26_out.e +0 -0
- pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
- pyvale/dataset.py +325 -0
- pyvale/errorcalculator.py +109 -0
- pyvale/errordriftcalc.py +146 -0
- pyvale/errorintegrator.py +336 -0
- pyvale/errorrand.py +607 -0
- pyvale/errorsyscalib.py +134 -0
- pyvale/errorsysdep.py +327 -0
- pyvale/errorsysfield.py +414 -0
- pyvale/errorsysindep.py +808 -0
- pyvale/examples/__init__.py +5 -0
- 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/genanalyticdata/ex1_1_scalarvisualisation.py +35 -0
- pyvale/examples/genanalyticdata/ex1_2_scalarcasebuild.py +43 -0
- pyvale/examples/genanalyticdata/ex2_1_analyticsensors.py +80 -0
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +79 -0
- 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/renderrasterisation/ex_rastenp.py +153 -0
- pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +218 -0
- pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +187 -0
- pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +190 -0
- pyvale/examples/visualisation/ex1_1_plot_traces.py +102 -0
- pyvale/examples/visualisation/ex2_1_animate_sim.py +89 -0
- pyvale/experimentsimulator.py +175 -0
- pyvale/field.py +128 -0
- pyvale/fieldconverter.py +351 -0
- pyvale/fieldsampler.py +111 -0
- pyvale/fieldscalar.py +166 -0
- pyvale/fieldtensor.py +218 -0
- pyvale/fieldtransform.py +388 -0
- pyvale/fieldvector.py +213 -0
- pyvale/generatorsrandom.py +505 -0
- pyvale/imagedef2d.py +569 -0
- pyvale/integratorfactory.py +240 -0
- pyvale/integratorquadrature.py +217 -0
- pyvale/integratorrectangle.py +165 -0
- pyvale/integratorspatial.py +89 -0
- pyvale/integratortype.py +43 -0
- pyvale/output.py +17 -0
- pyvale/pyvaleexceptions.py +11 -0
- pyvale/raster.py +31 -0
- pyvale/rastercy.py +77 -0
- pyvale/rasternp.py +603 -0
- pyvale/rendermesh.py +147 -0
- pyvale/sensorarray.py +178 -0
- pyvale/sensorarrayfactory.py +196 -0
- pyvale/sensorarraypoint.py +278 -0
- pyvale/sensordata.py +71 -0
- pyvale/sensordescriptor.py +213 -0
- pyvale/sensortools.py +142 -0
- pyvale/simcases/case00_HEX20.i +242 -0
- pyvale/simcases/case00_HEX27.i +242 -0
- pyvale/simcases/case00_HEX8.i +242 -0
- pyvale/simcases/case00_TET10.i +242 -0
- pyvale/simcases/case00_TET14.i +242 -0
- pyvale/simcases/case00_TET4.i +242 -0
- pyvale/simcases/case01.i +101 -0
- pyvale/simcases/case02.i +156 -0
- pyvale/simcases/case03.i +136 -0
- pyvale/simcases/case04.i +181 -0
- pyvale/simcases/case05.i +234 -0
- pyvale/simcases/case06.i +305 -0
- pyvale/simcases/case07.geo +135 -0
- pyvale/simcases/case07.i +87 -0
- pyvale/simcases/case08.geo +144 -0
- pyvale/simcases/case08.i +153 -0
- pyvale/simcases/case09.geo +204 -0
- pyvale/simcases/case09.i +87 -0
- pyvale/simcases/case10.geo +204 -0
- pyvale/simcases/case10.i +257 -0
- pyvale/simcases/case11.geo +337 -0
- pyvale/simcases/case11.i +147 -0
- pyvale/simcases/case12.geo +388 -0
- pyvale/simcases/case12.i +329 -0
- pyvale/simcases/case13.i +140 -0
- pyvale/simcases/case14.i +159 -0
- pyvale/simcases/case15.geo +337 -0
- pyvale/simcases/case15.i +150 -0
- pyvale/simcases/case16.geo +391 -0
- pyvale/simcases/case16.i +357 -0
- pyvale/simcases/case17.geo +135 -0
- pyvale/simcases/case17.i +144 -0
- pyvale/simcases/case18.i +254 -0
- pyvale/simcases/case18_1.i +254 -0
- pyvale/simcases/case18_2.i +254 -0
- pyvale/simcases/case18_3.i +254 -0
- pyvale/simcases/case19.geo +252 -0
- pyvale/simcases/case19.i +99 -0
- pyvale/simcases/case20.geo +252 -0
- pyvale/simcases/case20.i +250 -0
- pyvale/simcases/case21.geo +74 -0
- pyvale/simcases/case21.i +155 -0
- pyvale/simcases/case22.geo +82 -0
- pyvale/simcases/case22.i +140 -0
- pyvale/simcases/case23.geo +164 -0
- pyvale/simcases/case23.i +140 -0
- pyvale/simcases/case24.geo +79 -0
- pyvale/simcases/case24.i +123 -0
- pyvale/simcases/case25.geo +82 -0
- pyvale/simcases/case25.i +140 -0
- pyvale/simcases/case26.geo +166 -0
- pyvale/simcases/case26.i +140 -0
- pyvale/simcases/run_1case.py +61 -0
- pyvale/simcases/run_all_cases.py +69 -0
- pyvale/simcases/run_build_case.py +64 -0
- pyvale/simcases/run_example_cases.py +69 -0
- pyvale/simtools.py +67 -0
- pyvale/visualexpplotter.py +191 -0
- pyvale/visualimagedef.py +74 -0
- pyvale/visualimages.py +76 -0
- pyvale/visualopts.py +493 -0
- pyvale/visualsimanimator.py +111 -0
- pyvale/visualsimsensors.py +318 -0
- pyvale/visualtools.py +136 -0
- pyvale/visualtraceplotter.py +142 -0
- pyvale-2025.5.3.dist-info/METADATA +144 -0
- pyvale-2025.5.3.dist-info/RECORD +175 -0
- pyvale-2025.5.3.dist-info/WHEEL +6 -0
- pyvale-2025.5.3.dist-info/licenses/LICENSE +21 -0
- pyvale-2025.5.3.dist-info/top_level.txt +1 -0
- pyvale.libs/libgomp-a34b3233.so.1.0.0 +0 -0
pyvale/simtools.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
import numpy as np
|
|
7
|
+
from pyvale.rendermesh import RenderMeshData
|
|
8
|
+
|
|
9
|
+
class SimTools:
|
|
10
|
+
"""Namespace for tools required for analysing simulation results.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def centre_mesh_nodes(nodes: np.ndarray, spat_dim: int) -> np.ndarray:
|
|
15
|
+
"""A method to centre the nodes of a mesh around the origin.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
nodes : np.ndarray
|
|
20
|
+
An array containing the node locations of the mesh.
|
|
21
|
+
spat_dim : int
|
|
22
|
+
The spatial dimension of the mesh.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
np.ndarray
|
|
27
|
+
An array containing the mesh node locations, but centred around
|
|
28
|
+
the origin.
|
|
29
|
+
"""
|
|
30
|
+
max = np.max(nodes, axis=0)
|
|
31
|
+
min = np.min(nodes, axis=0)
|
|
32
|
+
middle = max - ((max - min) / 2)
|
|
33
|
+
if spat_dim == 3:
|
|
34
|
+
middle[2] = 0
|
|
35
|
+
centred = np.subtract(nodes, middle)
|
|
36
|
+
return centred
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def get_deformed_nodes(timestep: int,
|
|
40
|
+
render_mesh: RenderMeshData) -> np.ndarray | None:
|
|
41
|
+
"""A method to obtain the deformed locations of all the nodes at a given
|
|
42
|
+
timestep.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
timestep : int
|
|
47
|
+
The timestep at which to find the deformed nodes.
|
|
48
|
+
render_mesh: RenderMeshData
|
|
49
|
+
A dataclass containing the skinned mesh and simulation results.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
np.ndarray | None
|
|
54
|
+
An array containing the deformed values of all the components at
|
|
55
|
+
each node location. Returns None if there are no deformation values.
|
|
56
|
+
"""
|
|
57
|
+
if render_mesh.fields_disp is None:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
added_disp = render_mesh.fields_disp[:, timestep]
|
|
61
|
+
if added_disp.shape[1] == 2:
|
|
62
|
+
added_disp = np.hstack((added_disp,np.zeros([added_disp.shape[0],1])))
|
|
63
|
+
coords = np.delete(render_mesh.coords, 3, axis=1)
|
|
64
|
+
deformed_nodes = coords + added_disp
|
|
65
|
+
return deformed_nodes
|
|
66
|
+
|
|
67
|
+
|
|
@@ -0,0 +1,191 @@
|
|
|
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 function for plotting virtuals sensor trace summary
|
|
9
|
+
statistics and uncertainty bounds over simulated experiments.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
import numpy as np
|
|
14
|
+
import matplotlib.pyplot as plt
|
|
15
|
+
from pyvale.pyvaleexceptions import VisError
|
|
16
|
+
from pyvale.visualopts import (PlotOptsGeneral,
|
|
17
|
+
TraceOptsExperiment,
|
|
18
|
+
EExpVisBounds,
|
|
19
|
+
EExpVisCentre)
|
|
20
|
+
from pyvale.experimentsimulator import ExperimentSimulator
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def plot_exp_traces(exp_sim: ExperimentSimulator,
|
|
24
|
+
component: str,
|
|
25
|
+
sens_array_num: int,
|
|
26
|
+
sim_num: int,
|
|
27
|
+
trace_opts: TraceOptsExperiment | None = None,
|
|
28
|
+
plot_opts: PlotOptsGeneral | None = None) -> tuple[Any,Any]:
|
|
29
|
+
"""Plots time traces for summary statistics of virtual sensor traces over
|
|
30
|
+
a series of virtual experiments.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
exp_sim : ExperimentSimulator
|
|
35
|
+
Experiment simulation object containing the set of virtual experiment to
|
|
36
|
+
be plotted.
|
|
37
|
+
component : str
|
|
38
|
+
String key for the field component to plot.
|
|
39
|
+
sens_array_num : int
|
|
40
|
+
List index for the sensor array to plot.
|
|
41
|
+
sim_num : int
|
|
42
|
+
Index for the simulation to plot.
|
|
43
|
+
trace_opts : TraceOptsExperiment | None, optional
|
|
44
|
+
Dataclass containing specific options for controlling the plot
|
|
45
|
+
appearance, by default None. If None the default options are used.
|
|
46
|
+
plot_opts : PlotOptsGeneral | None, optional
|
|
47
|
+
Dataclass containing general options for formatting plots and
|
|
48
|
+
visualisations, by default None. If None the default options are used.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
tuple[Any,Any]
|
|
53
|
+
A tuple containing a handle to the matplotlib figure and axis objects:
|
|
54
|
+
(fig,ax).
|
|
55
|
+
|
|
56
|
+
Raises
|
|
57
|
+
------
|
|
58
|
+
VisError
|
|
59
|
+
There are no virtual experiments or virtuale experiment stats to plot in
|
|
60
|
+
the ExperimentSimulator object. Call 'run_experiments' and 'calc_stats'.
|
|
61
|
+
"""
|
|
62
|
+
if trace_opts is None:
|
|
63
|
+
trace_opts = TraceOptsExperiment()
|
|
64
|
+
|
|
65
|
+
if plot_opts is None:
|
|
66
|
+
plot_opts = PlotOptsGeneral()
|
|
67
|
+
|
|
68
|
+
descriptor = exp_sim._sensor_arrays[sens_array_num]._descriptor
|
|
69
|
+
comp_ind = exp_sim._sensor_arrays[sens_array_num].get_field().get_component_index(component)
|
|
70
|
+
samp_time = exp_sim._sensor_arrays[sens_array_num].get_sample_times()
|
|
71
|
+
num_sens = exp_sim._sensor_arrays[sens_array_num].get_measurement_shape()[0]
|
|
72
|
+
|
|
73
|
+
exp_data = exp_sim._exp_data
|
|
74
|
+
exp_stats = exp_sim._exp_stats
|
|
75
|
+
|
|
76
|
+
if exp_data is None or exp_stats is None:
|
|
77
|
+
raise VisError("Before visualising virtual experiment traces the " \
|
|
78
|
+
"virtual experiments must be run. exp_data or exp_stats is None.")
|
|
79
|
+
|
|
80
|
+
if trace_opts.sensors_to_plot is None:
|
|
81
|
+
sensors_to_plot = range(num_sens)
|
|
82
|
+
else:
|
|
83
|
+
sensors_to_plot = trace_opts.sensors_to_plot
|
|
84
|
+
|
|
85
|
+
#---------------------------------------------------------------------------
|
|
86
|
+
# Figure canvas setup
|
|
87
|
+
fig, ax = plt.subplots(figsize=plot_opts.single_fig_size_landscape,
|
|
88
|
+
layout='constrained')
|
|
89
|
+
fig.set_dpi(plot_opts.resolution)
|
|
90
|
+
|
|
91
|
+
#---------------------------------------------------------------------------
|
|
92
|
+
# Plot all simulated experimental points
|
|
93
|
+
if trace_opts.plot_all_exp_points:
|
|
94
|
+
for ss in sensors_to_plot:
|
|
95
|
+
for ee in range(exp_sim._num_exp_per_sim):
|
|
96
|
+
ax.plot(samp_time,
|
|
97
|
+
exp_data[sens_array_num][sim_num,ee,ss,comp_ind,:],
|
|
98
|
+
"+",
|
|
99
|
+
lw=plot_opts.lw,
|
|
100
|
+
ms=plot_opts.ms,
|
|
101
|
+
color=plot_opts.colors[ss % plot_opts.colors_num])
|
|
102
|
+
|
|
103
|
+
sensor_tags = descriptor.create_sensor_tags(num_sens)
|
|
104
|
+
lines = []
|
|
105
|
+
for ss in sensors_to_plot:
|
|
106
|
+
if trace_opts.centre == EExpVisCentre.MEDIAN:
|
|
107
|
+
trace_centre = exp_stats[sens_array_num].med[sim_num,ss,comp_ind,:]
|
|
108
|
+
else:
|
|
109
|
+
trace_centre = exp_stats[sens_array_num].mean[sim_num,ss,comp_ind,:]
|
|
110
|
+
|
|
111
|
+
line, = ax.plot(samp_time,
|
|
112
|
+
trace_centre,
|
|
113
|
+
trace_opts.exp_centre_line,
|
|
114
|
+
label=sensor_tags[ss],
|
|
115
|
+
lw=plot_opts.lw,
|
|
116
|
+
ms=plot_opts.ms,
|
|
117
|
+
color=plot_opts.colors[ss % plot_opts.colors_num])
|
|
118
|
+
lines.append(line)
|
|
119
|
+
|
|
120
|
+
if trace_opts.fill_between is not None:
|
|
121
|
+
upper = np.zeros_like(exp_stats[sens_array_num].min)
|
|
122
|
+
lower = np.zeros_like(exp_stats[sens_array_num].min)
|
|
123
|
+
|
|
124
|
+
if trace_opts.fill_between == EExpVisBounds.MINMAX:
|
|
125
|
+
upper = trace_opts.fill_scale*exp_stats[sens_array_num].min
|
|
126
|
+
lower = trace_opts.fill_scale*exp_stats[sens_array_num].max
|
|
127
|
+
elif trace_opts.fill_between == EExpVisBounds.QUARTILE:
|
|
128
|
+
upper = trace_opts.fill_scale*exp_stats[sens_array_num].q25
|
|
129
|
+
lower = trace_opts.fill_scale*exp_stats[sens_array_num].q75
|
|
130
|
+
elif trace_opts.fill_between == EExpVisBounds.STD:
|
|
131
|
+
upper = trace_centre + \
|
|
132
|
+
trace_opts.fill_scale*exp_stats[sens_array_num].std
|
|
133
|
+
lower = trace_centre - \
|
|
134
|
+
trace_opts.fill_scale*exp_stats[sens_array_num].std
|
|
135
|
+
elif trace_opts.fill_between == EExpVisBounds.MAD:
|
|
136
|
+
upper = trace_centre + \
|
|
137
|
+
trace_opts.fill_scale*exp_stats[sens_array_num].mad
|
|
138
|
+
lower = trace_centre - \
|
|
139
|
+
trace_opts.fill_scale*exp_stats[sens_array_num].mad
|
|
140
|
+
|
|
141
|
+
ax.fill_between(samp_time,
|
|
142
|
+
upper[sim_num,ss,comp_ind,:],
|
|
143
|
+
lower[sim_num,ss,comp_ind,:],
|
|
144
|
+
color=plot_opts.colors[ss % plot_opts.colors_num],
|
|
145
|
+
alpha=0.2)
|
|
146
|
+
|
|
147
|
+
#---------------------------------------------------------------------------
|
|
148
|
+
# Plot simulation and truth line
|
|
149
|
+
if trace_opts.sim_line is not None:
|
|
150
|
+
sim_time = exp_sim._sensor_arrays[sens_array_num].get_field().get_time_steps()
|
|
151
|
+
sim_vals = exp_sim._sensor_arrays[sens_array_num].get_field().sample_field(
|
|
152
|
+
exp_sim._sensor_arrays[sens_array_num]._positions)
|
|
153
|
+
|
|
154
|
+
for ss in sensors_to_plot:
|
|
155
|
+
ax.plot(sim_time,
|
|
156
|
+
sim_vals[ss,comp_ind,:],
|
|
157
|
+
trace_opts.sim_line,
|
|
158
|
+
lw=plot_opts.lw,
|
|
159
|
+
ms=plot_opts.ms)
|
|
160
|
+
|
|
161
|
+
if trace_opts.truth_line is not None:
|
|
162
|
+
truth = exp_sim._sensor_arrays[sens_array_num].get_truth()
|
|
163
|
+
for ss in sensors_to_plot:
|
|
164
|
+
ax.plot(samp_time,
|
|
165
|
+
truth[ss,comp_ind,:],
|
|
166
|
+
trace_opts.truth_line,
|
|
167
|
+
lw=plot_opts.lw,
|
|
168
|
+
ms=plot_opts.ms,
|
|
169
|
+
color=plot_opts.colors[ss % plot_opts.colors_num])
|
|
170
|
+
|
|
171
|
+
#---------------------------------------------------------------------------
|
|
172
|
+
# Axis / legend labels and options
|
|
173
|
+
ax.set_xlabel(trace_opts.time_label,
|
|
174
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
175
|
+
ax.set_ylabel(descriptor.create_label(comp_ind),
|
|
176
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
177
|
+
|
|
178
|
+
if trace_opts.time_min_max is None:
|
|
179
|
+
ax.set_xlim((np.min(samp_time),np.max(samp_time))) # type: ignore
|
|
180
|
+
else:
|
|
181
|
+
ax.set_xlim(trace_opts.time_min_max)
|
|
182
|
+
|
|
183
|
+
if trace_opts.legend_loc is not None:
|
|
184
|
+
ax.legend(handles=lines,
|
|
185
|
+
prop={"size":plot_opts.font_leg_size},
|
|
186
|
+
loc=trace_opts.legend_loc)
|
|
187
|
+
|
|
188
|
+
plt.grid(True)
|
|
189
|
+
plt.draw()
|
|
190
|
+
|
|
191
|
+
return (fig,ax)
|
pyvale/visualimagedef.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
NOTE: this module is a feature under developement.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
|
|
14
|
+
class ImageDefDiags:
|
|
15
|
+
@staticmethod
|
|
16
|
+
def plot_speckle_image(image: np.ndarray,
|
|
17
|
+
title: str = "",
|
|
18
|
+
cmap: str = "gray") -> None:
|
|
19
|
+
fig, ax = plt.subplots()
|
|
20
|
+
cset = plt.imshow(image,cmap=plt.get_cmap(cmap),origin='lower')
|
|
21
|
+
ax.set_aspect('equal','box')
|
|
22
|
+
ax.set_title(title,fontsize=12)
|
|
23
|
+
fig.colorbar(cset)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def plot_vector_image(image: np.ndarray,
|
|
27
|
+
title: str = "",
|
|
28
|
+
cmap: str = "plasma") -> None:
|
|
29
|
+
fig, ax = plt.subplots()
|
|
30
|
+
cset = plt.imshow(image,cmap=plt.get_cmap(cmap),origin='lower')
|
|
31
|
+
ax.set_aspect('equal','box')
|
|
32
|
+
ax.set_title(title,fontsize=12)
|
|
33
|
+
fig.colorbar(cset)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def plot_image_xy(image: np.ndarray,
|
|
38
|
+
extent: tuple[float,float,float,float],
|
|
39
|
+
title: str = "",
|
|
40
|
+
cmap: str = "plasma",
|
|
41
|
+
ax_units: str = "mm") -> None:
|
|
42
|
+
|
|
43
|
+
fig, ax = plt.subplots()
|
|
44
|
+
cset = plt.imshow(image,
|
|
45
|
+
aspect='auto',interpolation='none',
|
|
46
|
+
origin='lower',cmap=plt.get_cmap(cmap),
|
|
47
|
+
extent=extent)
|
|
48
|
+
ax.set_aspect('equal','box')
|
|
49
|
+
ax.set_title(title,fontsize=12)
|
|
50
|
+
ax.set_xlabel(f'x [{ax_units}]',fontsize=12)
|
|
51
|
+
ax.set_ylabel(f'y [{ax_units}]',fontsize=12)
|
|
52
|
+
fig.colorbar(cset)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def plot_all_diags(def_image: np.ndarray,
|
|
56
|
+
def_mask: np.ndarray | None,
|
|
57
|
+
def_image_subpx: np.ndarray,
|
|
58
|
+
subpx_disp_x: np.ndarray,
|
|
59
|
+
subpx_disp_y: np.ndarray,
|
|
60
|
+
subpx_grid_xm: np.ndarray,
|
|
61
|
+
subpx_grid_ym: np.ndarray) -> None:
|
|
62
|
+
image_map = "gray"
|
|
63
|
+
vector_map = "plasma"
|
|
64
|
+
|
|
65
|
+
if def_mask is not None:
|
|
66
|
+
ImageDefDiags.plot_speckle_image('Def. Mask',def_mask,image_map)
|
|
67
|
+
|
|
68
|
+
ImageDefDiags.plot_speckle_image('Subpx Def. Image',def_image_subpx,image_map)
|
|
69
|
+
ImageDefDiags.plot_speckle_image('Def. Image',def_image,image_map)
|
|
70
|
+
|
|
71
|
+
ext = tuple(np.array([subpx_grid_xm.min(),subpx_grid_xm.max(),
|
|
72
|
+
subpx_grid_ym.min(),subpx_grid_ym.max()])*10**3)
|
|
73
|
+
ImageDefDiags.plot_image_xy('Sub Pixel Disp X',subpx_disp_x,ext,vector_map)
|
|
74
|
+
ImageDefDiags.plot_image_xy('Sub Pixel Disp Y',subpx_disp_y,ext,vector_map)
|
pyvale/visualimages.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
NOTE: this module is a feature under developement.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
import numpy as np
|
|
13
|
+
import matplotlib.pyplot as plt
|
|
14
|
+
from pyvale.camera import CameraBasic2D
|
|
15
|
+
from pyvale.visualopts import PlotOptsGeneral
|
|
16
|
+
|
|
17
|
+
# TODO: this only works for a 2D camera, maybe this should be deprecated
|
|
18
|
+
def plot_measurement_image(camera: CameraBasic2D,
|
|
19
|
+
component: str,
|
|
20
|
+
time_step: int = -1,
|
|
21
|
+
plot_opts: PlotOptsGeneral | None = None
|
|
22
|
+
) -> tuple[Any,Any]:
|
|
23
|
+
|
|
24
|
+
if plot_opts is None:
|
|
25
|
+
plot_opts = PlotOptsGeneral()
|
|
26
|
+
|
|
27
|
+
comp_ind = camera.get_field().get_component_index(component)
|
|
28
|
+
meas_image = camera.get_measurement_images()[:,:,comp_ind,time_step]
|
|
29
|
+
descriptor = camera.get_descriptor()
|
|
30
|
+
|
|
31
|
+
(fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
|
|
32
|
+
layout='constrained')
|
|
33
|
+
fig.set_dpi(plot_opts.resolution)
|
|
34
|
+
|
|
35
|
+
cset = plt.imshow(meas_image,
|
|
36
|
+
cmap=plt.get_cmap(plot_opts.cmap_seq),
|
|
37
|
+
origin='lower')
|
|
38
|
+
ax.set_aspect('equal','box')
|
|
39
|
+
|
|
40
|
+
fig.colorbar(cset,
|
|
41
|
+
label=descriptor.create_label_flat(comp_ind))
|
|
42
|
+
|
|
43
|
+
title = f"Time: {camera.get_sample_times()[time_step]}s"
|
|
44
|
+
ax.set_title(title,fontsize=plot_opts.font_head_size)
|
|
45
|
+
ax.set_xlabel(r"x ($px$)",
|
|
46
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
47
|
+
ax.set_ylabel(r"y ($px$)",
|
|
48
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
49
|
+
|
|
50
|
+
return (fig,ax)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def plot_field_image(image: np.ndarray,
|
|
54
|
+
title_str: str | None = None,
|
|
55
|
+
plot_opts: PlotOptsGeneral | None = None
|
|
56
|
+
) -> tuple[Any,Any]:
|
|
57
|
+
|
|
58
|
+
if plot_opts is None:
|
|
59
|
+
plot_opts = PlotOptsGeneral()
|
|
60
|
+
|
|
61
|
+
(fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
|
|
62
|
+
layout='constrained')
|
|
63
|
+
fig.set_dpi(plot_opts.resolution)
|
|
64
|
+
cset = plt.imshow(image,
|
|
65
|
+
cmap=plt.get_cmap(plot_opts.cmap_seq))
|
|
66
|
+
#origin='lower')
|
|
67
|
+
ax.set_aspect('equal','box')
|
|
68
|
+
fig.colorbar(cset)
|
|
69
|
+
if title_str is not None:
|
|
70
|
+
ax.set_title(title_str,fontsize=plot_opts.font_head_size)
|
|
71
|
+
ax.set_xlabel(r"x ($px$)",
|
|
72
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
73
|
+
ax.set_ylabel(r"y ($px$)",
|
|
74
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
75
|
+
|
|
76
|
+
return (fig,ax)
|