pyvale 2025.4.0__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 +75 -0
- pyvale/core/__init__.py +7 -0
- pyvale/core/analyticmeshgen.py +59 -0
- pyvale/core/analyticsimdatafactory.py +63 -0
- pyvale/core/analyticsimdatagenerator.py +160 -0
- pyvale/core/camera.py +146 -0
- pyvale/core/cameradata.py +64 -0
- pyvale/core/cameradata2d.py +82 -0
- pyvale/core/cameratools.py +328 -0
- pyvale/core/cython/rastercyth.c +32267 -0
- pyvale/core/cython/rastercyth.py +636 -0
- pyvale/core/dataset.py +250 -0
- pyvale/core/errorcalculator.py +112 -0
- pyvale/core/errordriftcalc.py +146 -0
- pyvale/core/errorintegrator.py +339 -0
- pyvale/core/errorrand.py +614 -0
- pyvale/core/errorsysdep.py +331 -0
- pyvale/core/errorsysfield.py +407 -0
- pyvale/core/errorsysindep.py +905 -0
- pyvale/core/experimentsimulator.py +99 -0
- pyvale/core/field.py +136 -0
- pyvale/core/fieldconverter.py +154 -0
- pyvale/core/fieldsampler.py +112 -0
- pyvale/core/fieldscalar.py +167 -0
- pyvale/core/fieldtensor.py +221 -0
- pyvale/core/fieldtransform.py +384 -0
- pyvale/core/fieldvector.py +215 -0
- pyvale/core/generatorsrandom.py +528 -0
- pyvale/core/imagedef2d.py +566 -0
- pyvale/core/integratorfactory.py +241 -0
- pyvale/core/integratorquadrature.py +192 -0
- pyvale/core/integratorrectangle.py +88 -0
- pyvale/core/integratorspatial.py +90 -0
- pyvale/core/integratortype.py +44 -0
- pyvale/core/optimcheckfuncs.py +153 -0
- pyvale/core/raster.py +31 -0
- pyvale/core/rastercy.py +76 -0
- pyvale/core/rasternp.py +604 -0
- pyvale/core/rendermesh.py +156 -0
- pyvale/core/sensorarray.py +179 -0
- pyvale/core/sensorarrayfactory.py +210 -0
- pyvale/core/sensorarraypoint.py +280 -0
- pyvale/core/sensordata.py +72 -0
- pyvale/core/sensordescriptor.py +101 -0
- pyvale/core/sensortools.py +143 -0
- pyvale/core/visualexpplotter.py +151 -0
- pyvale/core/visualimagedef.py +71 -0
- pyvale/core/visualimages.py +75 -0
- pyvale/core/visualopts.py +180 -0
- pyvale/core/visualsimanimator.py +83 -0
- pyvale/core/visualsimplotter.py +182 -0
- pyvale/core/visualtools.py +81 -0
- pyvale/core/visualtraceplotter.py +256 -0
- pyvale/data/__init__.py +7 -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/examples/__init__.py +7 -0
- pyvale/examples/analyticdatagen/__init__.py +7 -0
- pyvale/examples/analyticdatagen/ex1_1_scalarvisualisation.py +38 -0
- pyvale/examples/analyticdatagen/ex1_2_scalarcasebuild.py +46 -0
- pyvale/examples/analyticdatagen/ex2_1_analyticsensors.py +83 -0
- pyvale/examples/ex1_1_thermal2d.py +89 -0
- pyvale/examples/ex1_2_thermal2d.py +111 -0
- pyvale/examples/ex1_3_thermal2d.py +113 -0
- pyvale/examples/ex1_4_thermal2d.py +89 -0
- pyvale/examples/ex1_5_thermal2d.py +105 -0
- pyvale/examples/ex2_1_thermal3d .py +87 -0
- pyvale/examples/ex2_2_thermal3d.py +51 -0
- pyvale/examples/ex2_3_thermal3d.py +109 -0
- pyvale/examples/ex3_1_displacement2d.py +47 -0
- pyvale/examples/ex3_2_displacement2d.py +79 -0
- pyvale/examples/ex3_3_displacement2d.py +104 -0
- pyvale/examples/ex3_4_displacement2d.py +105 -0
- pyvale/examples/ex4_1_strain2d.py +57 -0
- pyvale/examples/ex4_2_strain2d.py +79 -0
- pyvale/examples/ex4_3_strain2d.py +100 -0
- pyvale/examples/ex5_1_multiphysics2d.py +78 -0
- pyvale/examples/ex6_1_multiphysics2d_expsim.py +118 -0
- pyvale/examples/ex6_2_multiphysics3d_expsim.py +158 -0
- pyvale/examples/features/__init__.py +7 -0
- pyvale/examples/features/ex_animation_tools_3dmonoblock.py +83 -0
- pyvale/examples/features/ex_area_avg.py +89 -0
- pyvale/examples/features/ex_calibration_error.py +108 -0
- pyvale/examples/features/ex_chain_field_errs.py +141 -0
- pyvale/examples/features/ex_field_errs.py +78 -0
- pyvale/examples/features/ex_sensor_single_angle_batch.py +110 -0
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +86 -0
- pyvale/examples/rasterisation/ex_rastenp.py +154 -0
- pyvale/examples/rasterisation/ex_rastercyth_oneframe.py +220 -0
- pyvale/examples/rasterisation/ex_rastercyth_static_cypara.py +194 -0
- pyvale/examples/rasterisation/ex_rastercyth_static_pypara.py +193 -0
- pyvale/simcases/case00_HEX20.i +242 -0
- pyvale/simcases/case00_HEX27.i +242 -0
- pyvale/simcases/case00_TET10.i +242 -0
- pyvale/simcases/case00_TET14.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-2025.4.0.dist-info/METADATA +140 -0
- pyvale-2025.4.0.dist-info/RECORD +157 -0
- pyvale-2025.4.0.dist-info/WHEEL +5 -0
- pyvale-2025.4.0.dist-info/licenses/LICENSE +21 -0
- pyvale-2025.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
8
|
+
# import vtk #NOTE: has to be here to fix latex bug in pyvista/vtk
|
|
9
|
+
# See: https://github.com/pyvista/pyvista/discussions/2928
|
|
10
|
+
#NOTE: causes output to console to be suppressed unfortunately
|
|
11
|
+
import pyvista as pv
|
|
12
|
+
|
|
13
|
+
import mooseherder as mh
|
|
14
|
+
|
|
15
|
+
from pyvale.core.sensorarraypoint import SensorArrayPoint
|
|
16
|
+
from pyvale.core.fieldconverter import simdata_to_pyvista
|
|
17
|
+
from pyvale.core.visualopts import (VisOptsSimSensors,VisOptsImageSave)
|
|
18
|
+
from pyvale.core.visualtools import (create_pv_plotter,
|
|
19
|
+
get_colour_lims,
|
|
20
|
+
save_image)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
#TODO: Docstrings
|
|
24
|
+
|
|
25
|
+
def add_sim_field(pv_plot: pv.Plotter,
|
|
26
|
+
sensor_array: SensorArrayPoint,
|
|
27
|
+
component: str,
|
|
28
|
+
time_step: int,
|
|
29
|
+
vis_opts: VisOptsSimSensors,
|
|
30
|
+
) -> tuple[pv.Plotter,pv.UnstructuredGrid]:
|
|
31
|
+
|
|
32
|
+
sim_vis = sensor_array.field.get_visualiser()
|
|
33
|
+
sim_data = sensor_array.field.get_sim_data()
|
|
34
|
+
sim_vis[component] = sim_data.node_vars[component][:,time_step]
|
|
35
|
+
comp_ind = sensor_array.field.get_component_index(component)
|
|
36
|
+
|
|
37
|
+
scalar_bar_args = {"title":sensor_array.descriptor.create_label(comp_ind),
|
|
38
|
+
"vertical":vis_opts.colour_bar_vertical,
|
|
39
|
+
"title_font_size":vis_opts.colour_bar_font_size,
|
|
40
|
+
"label_font_size":vis_opts.colour_bar_font_size}
|
|
41
|
+
|
|
42
|
+
pv_plot.add_mesh(sim_vis,
|
|
43
|
+
scalars=component,
|
|
44
|
+
label="sim-data",
|
|
45
|
+
show_edges=vis_opts.show_edges,
|
|
46
|
+
show_scalar_bar=vis_opts.colour_bar_show,
|
|
47
|
+
scalar_bar_args=scalar_bar_args,
|
|
48
|
+
lighting=False,
|
|
49
|
+
clim=vis_opts.colour_bar_lims)
|
|
50
|
+
|
|
51
|
+
if vis_opts.time_label_show:
|
|
52
|
+
pv_plot.add_text(f"Time: {sim_data.time[time_step]} " + \
|
|
53
|
+
f"{sensor_array.descriptor.time_units}",
|
|
54
|
+
position=vis_opts.time_label_position,
|
|
55
|
+
font_size=vis_opts.time_label_font_size,
|
|
56
|
+
name='time-label')
|
|
57
|
+
|
|
58
|
+
return (pv_plot,sim_vis)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def add_sensor_points_nom(pv_plot: pv.Plotter,
|
|
62
|
+
sensor_array: SensorArrayPoint,
|
|
63
|
+
vis_opts: VisOptsSimSensors,
|
|
64
|
+
) -> pv.Plotter:
|
|
65
|
+
|
|
66
|
+
vis_sens_nominal = pv.PolyData(sensor_array.sensor_data.positions)
|
|
67
|
+
vis_sens_nominal["labels"] = sensor_array.descriptor.create_sensor_tags(
|
|
68
|
+
sensor_array.get_measurement_shape()[0])
|
|
69
|
+
|
|
70
|
+
# Add points to show sensor locations
|
|
71
|
+
pv_plot.add_point_labels(vis_sens_nominal,"labels",
|
|
72
|
+
font_size=vis_opts.sens_label_font_size,
|
|
73
|
+
shape_color=vis_opts.sens_label_colour,
|
|
74
|
+
point_color=vis_opts.sens_colour_nom,
|
|
75
|
+
render_points_as_spheres=True,
|
|
76
|
+
point_size=vis_opts.sens_point_size,
|
|
77
|
+
always_visible=True)
|
|
78
|
+
|
|
79
|
+
return pv_plot
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def add_sensor_points_pert(pv_plot: pv.Plotter,
|
|
83
|
+
sensor_array: SensorArrayPoint,
|
|
84
|
+
vis_opts: VisOptsSimSensors,
|
|
85
|
+
) -> pv.Plotter:
|
|
86
|
+
|
|
87
|
+
sens_data_perturbed = sensor_array.get_sensor_data_perturbed()
|
|
88
|
+
|
|
89
|
+
if sens_data_perturbed is not None and vis_opts.show_perturbed_pos:
|
|
90
|
+
vis_sens_perturbed = pv.PolyData(sens_data_perturbed.positions)
|
|
91
|
+
vis_sens_perturbed["labels"] = ["",]*sensor_array.get_measurement_shape()[0]
|
|
92
|
+
|
|
93
|
+
pv_plot.add_point_labels(vis_sens_perturbed,"labels",
|
|
94
|
+
font_size=vis_opts.sens_label_font_size,
|
|
95
|
+
shape_color=vis_opts.sens_label_colour,
|
|
96
|
+
point_color=vis_opts.sens_colour_pert,
|
|
97
|
+
render_points_as_spheres=True,
|
|
98
|
+
point_size=vis_opts.sens_point_size,
|
|
99
|
+
always_visible=True)
|
|
100
|
+
|
|
101
|
+
return pv_plot
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def plot_sim_mesh(sim_data: mh.SimData,
|
|
105
|
+
vis_opts: VisOptsSimSensors | None = None,
|
|
106
|
+
) -> pv.Plotter:
|
|
107
|
+
|
|
108
|
+
if vis_opts is None:
|
|
109
|
+
vis_opts = VisOptsSimSensors()
|
|
110
|
+
|
|
111
|
+
pv_simdata = simdata_to_pyvista(sim_data,
|
|
112
|
+
None,
|
|
113
|
+
sim_data.num_spat_dims)
|
|
114
|
+
|
|
115
|
+
pv_plot = create_pv_plotter(vis_opts)
|
|
116
|
+
|
|
117
|
+
pv_plot.add_mesh(pv_simdata,
|
|
118
|
+
label='sim-data',
|
|
119
|
+
show_edges=True,
|
|
120
|
+
show_scalar_bar=False)
|
|
121
|
+
|
|
122
|
+
return pv_plot
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def plot_sim_data(sim_data: mh.SimData,
|
|
126
|
+
component: str,
|
|
127
|
+
time_step: int = -1,
|
|
128
|
+
vis_opts: VisOptsSimSensors | None = None
|
|
129
|
+
) -> pv.Plotter:
|
|
130
|
+
|
|
131
|
+
if vis_opts is None:
|
|
132
|
+
vis_opts = VisOptsSimSensors()
|
|
133
|
+
|
|
134
|
+
pv_simdata = simdata_to_pyvista(sim_data,
|
|
135
|
+
(component,),
|
|
136
|
+
sim_data.num_spat_dims)
|
|
137
|
+
|
|
138
|
+
pv_plot = create_pv_plotter(vis_opts)
|
|
139
|
+
|
|
140
|
+
pv_plot.add_mesh(pv_simdata,
|
|
141
|
+
scalars=pv_simdata[component][:,time_step],
|
|
142
|
+
label="sim-data",
|
|
143
|
+
show_edges=True,
|
|
144
|
+
show_scalar_bar=True,
|
|
145
|
+
scalar_bar_args={"title":component},)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
return pv_plot
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def plot_point_sensors_on_sim(sensor_array: SensorArrayPoint,
|
|
152
|
+
component: str,
|
|
153
|
+
time_step: int = -1,
|
|
154
|
+
vis_opts: VisOptsSimSensors | None = None,
|
|
155
|
+
image_save_opts: VisOptsImageSave | None = None,
|
|
156
|
+
) -> pv.Plotter:
|
|
157
|
+
|
|
158
|
+
if vis_opts is None:
|
|
159
|
+
vis_opts = VisOptsSimSensors()
|
|
160
|
+
|
|
161
|
+
sim_data = sensor_array.field.get_sim_data()
|
|
162
|
+
vis_opts.colour_bar_lims = get_colour_lims(
|
|
163
|
+
sim_data.node_vars[component][:,time_step],
|
|
164
|
+
vis_opts.colour_bar_lims)
|
|
165
|
+
|
|
166
|
+
pv_plot = create_pv_plotter(vis_opts)
|
|
167
|
+
|
|
168
|
+
pv_plot = add_sensor_points_pert(pv_plot,sensor_array,vis_opts)
|
|
169
|
+
pv_plot = add_sensor_points_nom(pv_plot,sensor_array,vis_opts)
|
|
170
|
+
(pv_plot,_) = add_sim_field(pv_plot,
|
|
171
|
+
sensor_array,
|
|
172
|
+
component,
|
|
173
|
+
time_step,
|
|
174
|
+
vis_opts)
|
|
175
|
+
|
|
176
|
+
pv_plot.camera_position = vis_opts.camera_position
|
|
177
|
+
|
|
178
|
+
if image_save_opts is not None:
|
|
179
|
+
save_image(pv_plot,image_save_opts)
|
|
180
|
+
|
|
181
|
+
return pv_plot
|
|
182
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import numpy as np
|
|
10
|
+
#import vtk #NOTE: has to be here to fix latex bug in pyvista/vtk
|
|
11
|
+
# See: https://github.com/pyvista/pyvista/discussions/2928
|
|
12
|
+
#NOTE: causes output to console to be suppressed unfortunately
|
|
13
|
+
import pyvista as pv
|
|
14
|
+
from pyvale.core.visualopts import (VisOptsSimSensors,
|
|
15
|
+
VisOptsImageSave,
|
|
16
|
+
EImageType,
|
|
17
|
+
VisOptsAnimation,
|
|
18
|
+
EAnimationType)
|
|
19
|
+
|
|
20
|
+
#TODO: Docstrings
|
|
21
|
+
|
|
22
|
+
def create_pv_plotter(vis_opts: VisOptsSimSensors) -> pv.Plotter:
|
|
23
|
+
pv_plot = pv.Plotter(window_size=vis_opts.window_size_px)
|
|
24
|
+
pv_plot.set_background(vis_opts.background_colour)
|
|
25
|
+
pv.global_theme.font.color = vis_opts.font_colour
|
|
26
|
+
pv_plot.add_axes_at_origin(labels_off=True)
|
|
27
|
+
return pv_plot
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_colour_lims(component_data: np.ndarray,
|
|
31
|
+
colour_bar_lims: tuple[float,float] | None
|
|
32
|
+
) -> tuple[float,float]:
|
|
33
|
+
|
|
34
|
+
if colour_bar_lims is None:
|
|
35
|
+
min_comp = np.min(component_data.flatten())
|
|
36
|
+
max_comp = np.max(component_data.flatten())
|
|
37
|
+
colour_bar_lims = (min_comp,max_comp)
|
|
38
|
+
|
|
39
|
+
return colour_bar_lims
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def save_image(pv_plot: pv.Plotter,
|
|
43
|
+
image_save_opts: VisOptsImageSave) -> None:
|
|
44
|
+
if image_save_opts.path is None:
|
|
45
|
+
image_save_opts.path = Path.cwd() / "pyvale-image"
|
|
46
|
+
|
|
47
|
+
if image_save_opts.image_type == EImageType.PNG:
|
|
48
|
+
image_save_opts.path = image_save_opts.path.with_suffix(".png")
|
|
49
|
+
pv_plot.screenshot(image_save_opts.path,
|
|
50
|
+
image_save_opts.transparent_background)
|
|
51
|
+
|
|
52
|
+
elif image_save_opts.image_type == EImageType.SVG:
|
|
53
|
+
image_save_opts.path = image_save_opts.path.with_suffix(".svg")
|
|
54
|
+
pv_plot.save_graphic(image_save_opts.path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def set_animation_writer(pv_plot: pv.Plotter,
|
|
58
|
+
anim_opts: VisOptsAnimation) -> pv.Plotter:
|
|
59
|
+
if anim_opts.save_animation is None:
|
|
60
|
+
return pv_plot
|
|
61
|
+
|
|
62
|
+
if anim_opts.save_path is None:
|
|
63
|
+
anim_opts.save_path = Path.cwd() / "pyvale-animation"
|
|
64
|
+
|
|
65
|
+
if anim_opts.save_animation == EAnimationType.GIF:
|
|
66
|
+
anim_opts.save_path = anim_opts.save_path.with_suffix(".gif")
|
|
67
|
+
pv_plot.open_gif(anim_opts.save_path,
|
|
68
|
+
loop=0,
|
|
69
|
+
fps=anim_opts.frames_per_second)
|
|
70
|
+
|
|
71
|
+
elif anim_opts.save_animation == EAnimationType.MP4:
|
|
72
|
+
anim_opts.save_path = anim_opts.save_path.with_suffix(".mp4")
|
|
73
|
+
print(80*"=")
|
|
74
|
+
print(f"{anim_opts.save_path=}")
|
|
75
|
+
print(80*"=")
|
|
76
|
+
pv_plot.open_movie(anim_opts.save_path,
|
|
77
|
+
anim_opts.frames_per_second)
|
|
78
|
+
|
|
79
|
+
return pv_plot
|
|
80
|
+
|
|
81
|
+
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
from pyvale.core.sensorarraypoint import SensorArrayPoint
|
|
13
|
+
from pyvale.core.visualopts import (PlotOptsGeneral,
|
|
14
|
+
TraceOptsSensor,
|
|
15
|
+
TraceOptsExperiment)
|
|
16
|
+
from pyvale.core.experimentsimulator import ExperimentSimulator
|
|
17
|
+
|
|
18
|
+
#TODO: Docstrings
|
|
19
|
+
|
|
20
|
+
def plot_time_traces(sensor_array: SensorArrayPoint,
|
|
21
|
+
component: str,
|
|
22
|
+
trace_opts: TraceOptsSensor | None = None,
|
|
23
|
+
plot_opts: PlotOptsGeneral | None = None
|
|
24
|
+
) -> tuple[Any,Any]:
|
|
25
|
+
|
|
26
|
+
#---------------------------------------------------------------------------
|
|
27
|
+
field = sensor_array.field
|
|
28
|
+
comp_ind = sensor_array.field.get_component_index(component)
|
|
29
|
+
samp_time = sensor_array.get_sample_times()
|
|
30
|
+
measurements = sensor_array.get_measurements()
|
|
31
|
+
n_sensors = sensor_array.sensor_data.positions.shape[0]
|
|
32
|
+
descriptor = sensor_array.descriptor
|
|
33
|
+
sensors_perturbed = sensor_array.get_sensor_data_perturbed()
|
|
34
|
+
|
|
35
|
+
#---------------------------------------------------------------------------
|
|
36
|
+
if plot_opts is None:
|
|
37
|
+
plot_opts = PlotOptsGeneral()
|
|
38
|
+
|
|
39
|
+
if trace_opts is None:
|
|
40
|
+
trace_opts = TraceOptsSensor()
|
|
41
|
+
|
|
42
|
+
if trace_opts.sensors_to_plot is None:
|
|
43
|
+
sensors_to_plot = range(n_sensors)
|
|
44
|
+
else:
|
|
45
|
+
sensors_to_plot = trace_opts.sensors_to_plot
|
|
46
|
+
|
|
47
|
+
#---------------------------------------------------------------------------
|
|
48
|
+
# Figure canvas setup
|
|
49
|
+
fig, ax = plt.subplots(figsize=plot_opts.single_fig_size_landscape,
|
|
50
|
+
layout='constrained')
|
|
51
|
+
fig.set_dpi(plot_opts.resolution)
|
|
52
|
+
|
|
53
|
+
#---------------------------------------------------------------------------
|
|
54
|
+
# Plot simulation and truth lines
|
|
55
|
+
if trace_opts.sim_line is not None:
|
|
56
|
+
sim_time = field.get_time_steps()
|
|
57
|
+
sim_vals = field.sample_field(sensor_array.sensor_data.positions,
|
|
58
|
+
None,
|
|
59
|
+
sensor_array.sensor_data.angles)
|
|
60
|
+
|
|
61
|
+
for ii,ss in enumerate(sensors_to_plot):
|
|
62
|
+
ax.plot(sim_time,
|
|
63
|
+
sim_vals[ss,comp_ind,:],
|
|
64
|
+
trace_opts.sim_line,
|
|
65
|
+
lw=plot_opts.lw,
|
|
66
|
+
ms=plot_opts.ms,
|
|
67
|
+
color=plot_opts.colors[ii % plot_opts.n_colors])
|
|
68
|
+
|
|
69
|
+
if trace_opts.truth_line is not None:
|
|
70
|
+
truth = sensor_array.get_truth()
|
|
71
|
+
for ii,ss in enumerate(sensors_to_plot):
|
|
72
|
+
ax.plot(samp_time,
|
|
73
|
+
truth[ss,comp_ind,:],
|
|
74
|
+
trace_opts.truth_line,
|
|
75
|
+
lw=plot_opts.lw,
|
|
76
|
+
ms=plot_opts.ms,
|
|
77
|
+
color=plot_opts.colors[ii % plot_opts.n_colors])
|
|
78
|
+
|
|
79
|
+
sensor_tags = descriptor.create_sensor_tags(n_sensors)
|
|
80
|
+
for ii,ss in enumerate(sensors_to_plot):
|
|
81
|
+
sensor_time = samp_time
|
|
82
|
+
if sensors_perturbed is not None:
|
|
83
|
+
if sensors_perturbed.sample_times is not None:
|
|
84
|
+
sensor_time = sensors_perturbed.sample_times
|
|
85
|
+
|
|
86
|
+
ax.plot(sensor_time,
|
|
87
|
+
measurements[ss,comp_ind,:],
|
|
88
|
+
trace_opts.meas_line,
|
|
89
|
+
label=sensor_tags[ss],
|
|
90
|
+
lw=plot_opts.lw,
|
|
91
|
+
ms=plot_opts.ms,
|
|
92
|
+
color=plot_opts.colors[ii % plot_opts.n_colors])
|
|
93
|
+
|
|
94
|
+
#---------------------------------------------------------------------------
|
|
95
|
+
# Axis / legend labels and options
|
|
96
|
+
ax.set_xlabel(trace_opts.time_label,
|
|
97
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
98
|
+
ax.set_ylabel(descriptor.create_label(comp_ind),
|
|
99
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
100
|
+
|
|
101
|
+
if trace_opts.time_min_max is None:
|
|
102
|
+
min_time = np.min((np.min(samp_time),np.min(sensor_time)))
|
|
103
|
+
max_time = np.max((np.max(samp_time),np.max(sensor_time)))
|
|
104
|
+
ax.set_xlim((min_time,max_time)) # type: ignore
|
|
105
|
+
else:
|
|
106
|
+
ax.set_xlim(trace_opts.time_min_max)
|
|
107
|
+
|
|
108
|
+
if trace_opts.legend:
|
|
109
|
+
ax.legend(prop={"size":plot_opts.font_leg_size},loc='best')
|
|
110
|
+
|
|
111
|
+
plt.grid(True)
|
|
112
|
+
plt.draw()
|
|
113
|
+
|
|
114
|
+
return (fig,ax)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def plot_exp_traces(exp_sim: ExperimentSimulator,
|
|
118
|
+
component: str,
|
|
119
|
+
sens_array_num: int,
|
|
120
|
+
sim_num: int,
|
|
121
|
+
trace_opts: TraceOptsExperiment | None = None,
|
|
122
|
+
plot_opts: PlotOptsGeneral | None = None) -> tuple[Any,Any]:
|
|
123
|
+
#---------------------------------------------------------------------------
|
|
124
|
+
n_sensors = exp_sim.sensor_arrays[sens_array_num].sensor_data.positions.shape[0]
|
|
125
|
+
descriptor = exp_sim.sensor_arrays[sens_array_num].descriptor
|
|
126
|
+
comp_ind = exp_sim.sensor_arrays[sens_array_num].field.get_component_index(component)
|
|
127
|
+
samp_time = exp_sim.sensor_arrays[sens_array_num].get_sample_times()
|
|
128
|
+
num_sens = exp_sim.sensor_arrays[sens_array_num].get_measurement_shape()[0]
|
|
129
|
+
|
|
130
|
+
#---------------------------------------------------------------------------
|
|
131
|
+
if trace_opts is None:
|
|
132
|
+
trace_opts = TraceOptsExperiment()
|
|
133
|
+
|
|
134
|
+
if plot_opts is None:
|
|
135
|
+
plot_opts = PlotOptsGeneral()
|
|
136
|
+
|
|
137
|
+
#---------------------------------------------------------------------------
|
|
138
|
+
exp_data = exp_sim.get_data()
|
|
139
|
+
exp_stats = exp_sim.get_stats()
|
|
140
|
+
|
|
141
|
+
if trace_opts.sensors_to_plot is None:
|
|
142
|
+
sensors_to_plot = range(num_sens)
|
|
143
|
+
else:
|
|
144
|
+
sensors_to_plot = trace_opts.sensors_to_plot
|
|
145
|
+
|
|
146
|
+
#---------------------------------------------------------------------------
|
|
147
|
+
# Figure canvas setup
|
|
148
|
+
fig, ax = plt.subplots(figsize=plot_opts.single_fig_size_landscape,
|
|
149
|
+
layout='constrained')
|
|
150
|
+
fig.set_dpi(plot_opts.resolution)
|
|
151
|
+
|
|
152
|
+
#---------------------------------------------------------------------------
|
|
153
|
+
# Plot all simulated experimental points
|
|
154
|
+
if trace_opts.plot_all_exp_points:
|
|
155
|
+
for ii,ss in enumerate(sensors_to_plot):
|
|
156
|
+
for ee in range(exp_sim.num_exp_per_sim):
|
|
157
|
+
ax.plot(samp_time,
|
|
158
|
+
exp_data[sens_array_num][sim_num,ee,ss,comp_ind,:],
|
|
159
|
+
"+",
|
|
160
|
+
lw=plot_opts.lw,
|
|
161
|
+
ms=plot_opts.ms,
|
|
162
|
+
color=plot_opts.colors[ii % plot_opts.n_colors])
|
|
163
|
+
|
|
164
|
+
#---------------------------------------------------------------------------
|
|
165
|
+
sensor_tags = descriptor.create_sensor_tags(n_sensors)
|
|
166
|
+
for ii,ss in enumerate(sensors_to_plot):
|
|
167
|
+
if trace_opts.centre == "median":
|
|
168
|
+
ax.plot(samp_time,
|
|
169
|
+
exp_stats[sens_array_num].median[sim_num,ss,comp_ind,:],
|
|
170
|
+
trace_opts.exp_mean_line,
|
|
171
|
+
label=sensor_tags[ss],
|
|
172
|
+
lw=plot_opts.lw,
|
|
173
|
+
ms=plot_opts.ms,
|
|
174
|
+
color=plot_opts.colors[ii % plot_opts.n_colors])
|
|
175
|
+
else:
|
|
176
|
+
ax.plot(samp_time,
|
|
177
|
+
exp_stats[sens_array_num].mean[sim_num,ss,comp_ind,:],
|
|
178
|
+
trace_opts.exp_mean_line,
|
|
179
|
+
label=sensor_tags[ss],
|
|
180
|
+
lw=plot_opts.lw,
|
|
181
|
+
ms=plot_opts.ms,
|
|
182
|
+
color=plot_opts.colors[ii % plot_opts.n_colors])
|
|
183
|
+
|
|
184
|
+
if trace_opts is not None:
|
|
185
|
+
upper = np.zeros_like(exp_stats[sens_array_num].min)
|
|
186
|
+
lower = np.zeros_like(exp_stats[sens_array_num].min)
|
|
187
|
+
|
|
188
|
+
if trace_opts.fill_between == 'max':
|
|
189
|
+
upper = exp_stats[sens_array_num].min
|
|
190
|
+
lower = exp_stats[sens_array_num].max
|
|
191
|
+
|
|
192
|
+
elif trace_opts.fill_between == 'quartile':
|
|
193
|
+
upper = exp_stats[sens_array_num].q25
|
|
194
|
+
lower = exp_stats[sens_array_num].q75
|
|
195
|
+
|
|
196
|
+
elif trace_opts.fill_between == '2std':
|
|
197
|
+
upper = exp_stats[sens_array_num].mean + \
|
|
198
|
+
2*exp_stats[sens_array_num].std
|
|
199
|
+
lower = exp_stats[sens_array_num].mean - \
|
|
200
|
+
2*exp_stats[sens_array_num].std
|
|
201
|
+
|
|
202
|
+
elif trace_opts.fill_between == '3std':
|
|
203
|
+
upper = exp_stats[sens_array_num].mean + \
|
|
204
|
+
3*exp_stats[sens_array_num].std
|
|
205
|
+
lower = exp_stats[sens_array_num].mean - \
|
|
206
|
+
3*exp_stats[sens_array_num].std
|
|
207
|
+
|
|
208
|
+
ax.fill_between(samp_time,
|
|
209
|
+
upper[sim_num,ss,comp_ind,:],
|
|
210
|
+
lower[sim_num,ss,comp_ind,:],
|
|
211
|
+
color=plot_opts.colors[ii % plot_opts.n_colors],
|
|
212
|
+
alpha=0.2)
|
|
213
|
+
|
|
214
|
+
#---------------------------------------------------------------------------
|
|
215
|
+
# Plot simulation and truth line
|
|
216
|
+
if trace_opts.sim_line is not None:
|
|
217
|
+
sim_time = exp_sim.sensor_arrays[sens_array_num].field.get_time_steps()
|
|
218
|
+
sim_vals = exp_sim.sensor_arrays[sens_array_num].field.sample_field(
|
|
219
|
+
exp_sim.sensor_arrays[sens_array_num].positions)
|
|
220
|
+
|
|
221
|
+
for ss in sensors_to_plot:
|
|
222
|
+
ax.plot(sim_time,
|
|
223
|
+
sim_vals[ss,comp_ind,:],
|
|
224
|
+
trace_opts.sim_line,
|
|
225
|
+
lw=plot_opts.lw,
|
|
226
|
+
ms=plot_opts.ms)
|
|
227
|
+
|
|
228
|
+
if trace_opts.truth_line is not None:
|
|
229
|
+
truth = exp_sim.sensor_arrays[sens_array_num].get_truth()
|
|
230
|
+
for ss in sensors_to_plot:
|
|
231
|
+
ax.plot(samp_time,
|
|
232
|
+
truth[ss,comp_ind,:],
|
|
233
|
+
trace_opts.truth_line,
|
|
234
|
+
lw=plot_opts.lw,
|
|
235
|
+
ms=plot_opts.ms,
|
|
236
|
+
color=plot_opts.colors[ii % plot_opts.n_colors])
|
|
237
|
+
|
|
238
|
+
#---------------------------------------------------------------------------
|
|
239
|
+
# Axis / legend labels and options
|
|
240
|
+
ax.set_xlabel(trace_opts.time_label,
|
|
241
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
242
|
+
ax.set_ylabel(descriptor.create_label(comp_ind),
|
|
243
|
+
fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
|
|
244
|
+
|
|
245
|
+
if trace_opts.time_min_max is None:
|
|
246
|
+
ax.set_xlim((np.min(samp_time),np.max(samp_time))) # type: ignore
|
|
247
|
+
else:
|
|
248
|
+
ax.set_xlim(trace_opts.time_min_max)
|
|
249
|
+
|
|
250
|
+
if trace_opts.legend:
|
|
251
|
+
ax.legend(prop={"size":plot_opts.font_leg_size},loc='best')
|
|
252
|
+
|
|
253
|
+
plt.grid(True)
|
|
254
|
+
plt.draw()
|
|
255
|
+
|
|
256
|
+
return (fig,ax)
|
pyvale/data/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
pyvale/data/case13_out.e
ADDED
|
Binary file
|
pyvale/data/case16_out.e
ADDED
|
Binary file
|
pyvale/data/case17_out.e
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
pyvale/data/case25_out.e
ADDED
|
Binary file
|
pyvale/data/case26_out.e
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================================================================================
|
|
3
|
+
pyvale: the python validation engine
|
|
4
|
+
License: MIT
|
|
5
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
+
================================================================================
|
|
7
|
+
"""
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'''
|
|
2
|
+
================================================================================
|
|
3
|
+
Analytic test case data - linear
|
|
4
|
+
|
|
5
|
+
pyvale: the python validation engine
|
|
6
|
+
License: MIT
|
|
7
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
8
|
+
================================================================================
|
|
9
|
+
'''
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
import pyvale
|
|
12
|
+
|
|
13
|
+
def main() -> None:
|
|
14
|
+
|
|
15
|
+
(sim_data,data_gen) = pyvale.AnalyticCaseFactory.scalar_linear_2d()
|
|
16
|
+
|
|
17
|
+
(grid_x,grid_y,grid_field) = data_gen.get_visualisation_grid()
|
|
18
|
+
|
|
19
|
+
fig, ax = plt.subplots()
|
|
20
|
+
cs = ax.contourf(grid_x,grid_y,grid_field)
|
|
21
|
+
cbar = fig.colorbar(cs)
|
|
22
|
+
plt.axis('scaled')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
(sim_data,data_gen) = pyvale.AnalyticCaseFactory.scalar_quadratic_2d()
|
|
26
|
+
|
|
27
|
+
(grid_x,grid_y,grid_field) = data_gen.get_visualisation_grid()
|
|
28
|
+
|
|
29
|
+
fig, ax = plt.subplots()
|
|
30
|
+
cs = ax.contourf(grid_x,grid_y,grid_field)
|
|
31
|
+
cbar = fig.colorbar(cs)
|
|
32
|
+
plt.axis('scaled')
|
|
33
|
+
|
|
34
|
+
plt.show()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == '__main__':
|
|
38
|
+
main()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'''
|
|
2
|
+
================================================================================
|
|
3
|
+
Analytic test case data - linear
|
|
4
|
+
|
|
5
|
+
pyvale: the python validation engine
|
|
6
|
+
License: MIT
|
|
7
|
+
Copyright (C) 2025 The Computer Aided Validation Team
|
|
8
|
+
================================================================================
|
|
9
|
+
'''
|
|
10
|
+
import numpy as np
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
import sympy
|
|
13
|
+
import pyvale
|
|
14
|
+
|
|
15
|
+
def main() -> None:
|
|
16
|
+
|
|
17
|
+
case_data = pyvale.AnalyticCaseData2D()
|
|
18
|
+
case_data.length_x = 10.0
|
|
19
|
+
case_data.length_y = 7.5
|
|
20
|
+
n_elem_mult = 10
|
|
21
|
+
case_data.num_elem_x = 4*n_elem_mult
|
|
22
|
+
case_data.num_elem_y = 3*n_elem_mult
|
|
23
|
+
case_data.time_steps = np.linspace(0.0,1.0,11)
|
|
24
|
+
|
|
25
|
+
(sym_y,sym_x,sym_t) = sympy.symbols("y,x,t")
|
|
26
|
+
case_data.funcs_x = (25.0 * sympy.sin(2*sympy.pi*sym_x/case_data.length_x),)
|
|
27
|
+
case_data.funcs_y = (10.0/case_data.length_y * sym_y,)
|
|
28
|
+
case_data.funcs_t = (sym_t,)
|
|
29
|
+
case_data.offsets_space = (20.0,)
|
|
30
|
+
case_data.offsets_time = (0.0,)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
data_gen = pyvale.AnalyticSimDataGenerator(case_data)
|
|
34
|
+
sim_data = data_gen.generate_sim_data()
|
|
35
|
+
|
|
36
|
+
(grid_x,grid_y,grid_field) = data_gen.get_visualisation_grid()
|
|
37
|
+
|
|
38
|
+
fig, ax = plt.subplots()
|
|
39
|
+
cs = ax.contourf(grid_x,grid_y,grid_field)
|
|
40
|
+
cbar = fig.colorbar(cs)
|
|
41
|
+
plt.axis('scaled')
|
|
42
|
+
plt.show()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == '__main__':
|
|
46
|
+
main()
|