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.

Files changed (157) hide show
  1. pyvale/__init__.py +75 -0
  2. pyvale/core/__init__.py +7 -0
  3. pyvale/core/analyticmeshgen.py +59 -0
  4. pyvale/core/analyticsimdatafactory.py +63 -0
  5. pyvale/core/analyticsimdatagenerator.py +160 -0
  6. pyvale/core/camera.py +146 -0
  7. pyvale/core/cameradata.py +64 -0
  8. pyvale/core/cameradata2d.py +82 -0
  9. pyvale/core/cameratools.py +328 -0
  10. pyvale/core/cython/rastercyth.c +32267 -0
  11. pyvale/core/cython/rastercyth.py +636 -0
  12. pyvale/core/dataset.py +250 -0
  13. pyvale/core/errorcalculator.py +112 -0
  14. pyvale/core/errordriftcalc.py +146 -0
  15. pyvale/core/errorintegrator.py +339 -0
  16. pyvale/core/errorrand.py +614 -0
  17. pyvale/core/errorsysdep.py +331 -0
  18. pyvale/core/errorsysfield.py +407 -0
  19. pyvale/core/errorsysindep.py +905 -0
  20. pyvale/core/experimentsimulator.py +99 -0
  21. pyvale/core/field.py +136 -0
  22. pyvale/core/fieldconverter.py +154 -0
  23. pyvale/core/fieldsampler.py +112 -0
  24. pyvale/core/fieldscalar.py +167 -0
  25. pyvale/core/fieldtensor.py +221 -0
  26. pyvale/core/fieldtransform.py +384 -0
  27. pyvale/core/fieldvector.py +215 -0
  28. pyvale/core/generatorsrandom.py +528 -0
  29. pyvale/core/imagedef2d.py +566 -0
  30. pyvale/core/integratorfactory.py +241 -0
  31. pyvale/core/integratorquadrature.py +192 -0
  32. pyvale/core/integratorrectangle.py +88 -0
  33. pyvale/core/integratorspatial.py +90 -0
  34. pyvale/core/integratortype.py +44 -0
  35. pyvale/core/optimcheckfuncs.py +153 -0
  36. pyvale/core/raster.py +31 -0
  37. pyvale/core/rastercy.py +76 -0
  38. pyvale/core/rasternp.py +604 -0
  39. pyvale/core/rendermesh.py +156 -0
  40. pyvale/core/sensorarray.py +179 -0
  41. pyvale/core/sensorarrayfactory.py +210 -0
  42. pyvale/core/sensorarraypoint.py +280 -0
  43. pyvale/core/sensordata.py +72 -0
  44. pyvale/core/sensordescriptor.py +101 -0
  45. pyvale/core/sensortools.py +143 -0
  46. pyvale/core/visualexpplotter.py +151 -0
  47. pyvale/core/visualimagedef.py +71 -0
  48. pyvale/core/visualimages.py +75 -0
  49. pyvale/core/visualopts.py +180 -0
  50. pyvale/core/visualsimanimator.py +83 -0
  51. pyvale/core/visualsimplotter.py +182 -0
  52. pyvale/core/visualtools.py +81 -0
  53. pyvale/core/visualtraceplotter.py +256 -0
  54. pyvale/data/__init__.py +7 -0
  55. pyvale/data/case13_out.e +0 -0
  56. pyvale/data/case16_out.e +0 -0
  57. pyvale/data/case17_out.e +0 -0
  58. pyvale/data/case18_1_out.e +0 -0
  59. pyvale/data/case18_2_out.e +0 -0
  60. pyvale/data/case18_3_out.e +0 -0
  61. pyvale/data/case25_out.e +0 -0
  62. pyvale/data/case26_out.e +0 -0
  63. pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
  64. pyvale/examples/__init__.py +7 -0
  65. pyvale/examples/analyticdatagen/__init__.py +7 -0
  66. pyvale/examples/analyticdatagen/ex1_1_scalarvisualisation.py +38 -0
  67. pyvale/examples/analyticdatagen/ex1_2_scalarcasebuild.py +46 -0
  68. pyvale/examples/analyticdatagen/ex2_1_analyticsensors.py +83 -0
  69. pyvale/examples/ex1_1_thermal2d.py +89 -0
  70. pyvale/examples/ex1_2_thermal2d.py +111 -0
  71. pyvale/examples/ex1_3_thermal2d.py +113 -0
  72. pyvale/examples/ex1_4_thermal2d.py +89 -0
  73. pyvale/examples/ex1_5_thermal2d.py +105 -0
  74. pyvale/examples/ex2_1_thermal3d .py +87 -0
  75. pyvale/examples/ex2_2_thermal3d.py +51 -0
  76. pyvale/examples/ex2_3_thermal3d.py +109 -0
  77. pyvale/examples/ex3_1_displacement2d.py +47 -0
  78. pyvale/examples/ex3_2_displacement2d.py +79 -0
  79. pyvale/examples/ex3_3_displacement2d.py +104 -0
  80. pyvale/examples/ex3_4_displacement2d.py +105 -0
  81. pyvale/examples/ex4_1_strain2d.py +57 -0
  82. pyvale/examples/ex4_2_strain2d.py +79 -0
  83. pyvale/examples/ex4_3_strain2d.py +100 -0
  84. pyvale/examples/ex5_1_multiphysics2d.py +78 -0
  85. pyvale/examples/ex6_1_multiphysics2d_expsim.py +118 -0
  86. pyvale/examples/ex6_2_multiphysics3d_expsim.py +158 -0
  87. pyvale/examples/features/__init__.py +7 -0
  88. pyvale/examples/features/ex_animation_tools_3dmonoblock.py +83 -0
  89. pyvale/examples/features/ex_area_avg.py +89 -0
  90. pyvale/examples/features/ex_calibration_error.py +108 -0
  91. pyvale/examples/features/ex_chain_field_errs.py +141 -0
  92. pyvale/examples/features/ex_field_errs.py +78 -0
  93. pyvale/examples/features/ex_sensor_single_angle_batch.py +110 -0
  94. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +86 -0
  95. pyvale/examples/rasterisation/ex_rastenp.py +154 -0
  96. pyvale/examples/rasterisation/ex_rastercyth_oneframe.py +220 -0
  97. pyvale/examples/rasterisation/ex_rastercyth_static_cypara.py +194 -0
  98. pyvale/examples/rasterisation/ex_rastercyth_static_pypara.py +193 -0
  99. pyvale/simcases/case00_HEX20.i +242 -0
  100. pyvale/simcases/case00_HEX27.i +242 -0
  101. pyvale/simcases/case00_TET10.i +242 -0
  102. pyvale/simcases/case00_TET14.i +242 -0
  103. pyvale/simcases/case01.i +101 -0
  104. pyvale/simcases/case02.i +156 -0
  105. pyvale/simcases/case03.i +136 -0
  106. pyvale/simcases/case04.i +181 -0
  107. pyvale/simcases/case05.i +234 -0
  108. pyvale/simcases/case06.i +305 -0
  109. pyvale/simcases/case07.geo +135 -0
  110. pyvale/simcases/case07.i +87 -0
  111. pyvale/simcases/case08.geo +144 -0
  112. pyvale/simcases/case08.i +153 -0
  113. pyvale/simcases/case09.geo +204 -0
  114. pyvale/simcases/case09.i +87 -0
  115. pyvale/simcases/case10.geo +204 -0
  116. pyvale/simcases/case10.i +257 -0
  117. pyvale/simcases/case11.geo +337 -0
  118. pyvale/simcases/case11.i +147 -0
  119. pyvale/simcases/case12.geo +388 -0
  120. pyvale/simcases/case12.i +329 -0
  121. pyvale/simcases/case13.i +140 -0
  122. pyvale/simcases/case14.i +159 -0
  123. pyvale/simcases/case15.geo +337 -0
  124. pyvale/simcases/case15.i +150 -0
  125. pyvale/simcases/case16.geo +391 -0
  126. pyvale/simcases/case16.i +357 -0
  127. pyvale/simcases/case17.geo +135 -0
  128. pyvale/simcases/case17.i +144 -0
  129. pyvale/simcases/case18.i +254 -0
  130. pyvale/simcases/case18_1.i +254 -0
  131. pyvale/simcases/case18_2.i +254 -0
  132. pyvale/simcases/case18_3.i +254 -0
  133. pyvale/simcases/case19.geo +252 -0
  134. pyvale/simcases/case19.i +99 -0
  135. pyvale/simcases/case20.geo +252 -0
  136. pyvale/simcases/case20.i +250 -0
  137. pyvale/simcases/case21.geo +74 -0
  138. pyvale/simcases/case21.i +155 -0
  139. pyvale/simcases/case22.geo +82 -0
  140. pyvale/simcases/case22.i +140 -0
  141. pyvale/simcases/case23.geo +164 -0
  142. pyvale/simcases/case23.i +140 -0
  143. pyvale/simcases/case24.geo +79 -0
  144. pyvale/simcases/case24.i +123 -0
  145. pyvale/simcases/case25.geo +82 -0
  146. pyvale/simcases/case25.i +140 -0
  147. pyvale/simcases/case26.geo +166 -0
  148. pyvale/simcases/case26.i +140 -0
  149. pyvale/simcases/run_1case.py +61 -0
  150. pyvale/simcases/run_all_cases.py +69 -0
  151. pyvale/simcases/run_build_case.py +64 -0
  152. pyvale/simcases/run_example_cases.py +69 -0
  153. pyvale-2025.4.0.dist-info/METADATA +140 -0
  154. pyvale-2025.4.0.dist-info/RECORD +157 -0
  155. pyvale-2025.4.0.dist-info/WHEEL +5 -0
  156. pyvale-2025.4.0.dist-info/licenses/LICENSE +21 -0
  157. 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)
@@ -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
+ """
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
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()