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,220 @@
1
+ """
2
+ ================================================================================
3
+ pyvale: the python validation engine
4
+ License: MIT
5
+ Copyright (C) 2025 The Computer Aided Validation Team
6
+ ================================================================================
7
+ """
8
+
9
+ import time
10
+ import numpy as np
11
+ from scipy.spatial.transform import Rotation
12
+ import matplotlib.pyplot as plt
13
+ import mooseherder as mh
14
+ import pyvale as pyv
15
+ import imagebenchmarks as ib
16
+
17
+ def main() -> None:
18
+ """pyvale example: rasterisation field renderer
19
+ ----------------------------------------------------------------------------
20
+ - TODO
21
+ """
22
+ print()
23
+ print(80*"=")
24
+ print("RASTER CYTHON FILE (should be *.so on Linux):")
25
+ print(pyv.rastercyth.__file__)
26
+ print(80*"=")
27
+ print()
28
+
29
+ return
30
+
31
+ benchmark = True
32
+ if not benchmark:
33
+
34
+ # This a path to an exodus *.e output file from MOOSE, this can be
35
+ # replaced with a path to your own simulation file
36
+ #sim_path = Path.home()/"pyvale"/"src"/"pyvale"/"simcases"/"case26_out.e"
37
+
38
+ sim_path = pyv.DataSet.render_simple_block_path()
39
+ sim_path = pyv.DataSet.render_mechanical_3d_path()
40
+ sim_data = mh.ExodusReader(sim_path).read_all_sim_data()
41
+
42
+ disp_comps = ("disp_x","disp_y","disp_z")
43
+
44
+ # Scale m -> mm
45
+ sim_data = pyv.scale_length_units(sim_data,disp_comps,1000.0)
46
+
47
+ print()
48
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_x']))=}")
49
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_y']))=}")
50
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_z']))=}")
51
+ print()
52
+
53
+ # Extracts the surface mesh from a full 3d simulation for rendering
54
+ render_mesh = pyv.create_render_mesh(sim_data,
55
+ ("disp_y","disp_x","disp_z"),
56
+ sim_spat_dim=3,
57
+ field_disp_keys=disp_comps)
58
+
59
+
60
+
61
+
62
+
63
+ pixel_num = np.array((960,1280),dtype=np.int32)
64
+ pixel_size = np.array((5.3e-3,5.3e-3),dtype=np.float64)
65
+ focal_leng: float = 50.0
66
+ cam_rot = Rotation.from_euler("ZYX",(0.0,-30.0,-10.0),degrees=True)
67
+ fov_scale_factor: float = 1.1
68
+
69
+ (roi_pos_world,
70
+ cam_pos_world) = pyv.CameraTools.pos_fill_frame_from_rotation(
71
+ coords_world=render_mesh.coords,
72
+ pixel_num=pixel_num,
73
+ pixel_size=pixel_size,
74
+ focal_leng=focal_leng,
75
+ cam_rot=cam_rot,
76
+ frame_fill=fov_scale_factor,
77
+ )
78
+
79
+ cam_data = pyv.CameraData(
80
+ pixels_num=pixel_num,
81
+ pixels_size=pixel_size,
82
+ pos_world=cam_pos_world,
83
+ rot_world=cam_rot,
84
+ roi_cent_world=roi_pos_world,
85
+ focal_length=focal_leng,
86
+ sub_samp=2,
87
+ back_face_removal=True,
88
+ )
89
+
90
+ else:
91
+ case_ind = 0
92
+ (case_ident,render_mesh,cam_data) = ib.load_benchmark_by_index(case_ind)
93
+ print(80*"=")
94
+ print("BENCHMARK IDENTIFIER:")
95
+ print(f"{case_ident}")
96
+ print(80*"=")
97
+
98
+
99
+ print()
100
+ print(80*"-")
101
+ print("MESH DATA:")
102
+ print(80*"-")
103
+ print("connectivity.shape=(num_elems,num_nodes_per_elem)")
104
+ print(f"{render_mesh.connectivity.shape=}")
105
+ print()
106
+ print("coords.shape=(num_nodes,coord[x,y,z])")
107
+ print(f"{render_mesh.coords.shape=}")
108
+ print()
109
+ print("fields.shape=(num_coords,num_time_steps,num_components)")
110
+ print(f"{render_mesh.fields_render.shape=}")
111
+ if render_mesh.fields_disp is not None:
112
+ print(f"{render_mesh.fields_disp.shape=}")
113
+ print(80*"-")
114
+ print()
115
+
116
+ print(80*"-")
117
+ print("CAMERA DATA:")
118
+ print(80*"-")
119
+ print(f"{cam_data.image_dist=}")
120
+ print(f"{cam_data.roi_cent_world=}")
121
+ print(f"{cam_data.pos_world=}")
122
+ print()
123
+ print("World to camera matrix:")
124
+ print(cam_data.world_to_cam_mat)
125
+ print(80*"-")
126
+ print()
127
+
128
+
129
+ print(80*"-")
130
+ total_frames = render_mesh.fields_render.shape[1]*render_mesh.fields_render.shape[2]
131
+ print(f"Time steps to render: {render_mesh.fields_render.shape[1]}")
132
+ print(f"Fields to render: {render_mesh.fields_render.shape[2]}")
133
+ print(f"Total frames to render: {total_frames}")
134
+ print(80*"-")
135
+
136
+ print(80*"=")
137
+ print("RASTER ELEMENT LOOP START")
138
+ print(80*"=")
139
+
140
+ num_loops = 1
141
+
142
+ loop_times = np.zeros((num_loops,),dtype=np.float64)
143
+
144
+ frame = -1
145
+ fields_render = render_mesh.fields_render[:,frame,:]
146
+ total_frames = fields_render.shape[1]
147
+
148
+ print()
149
+ print("Running raster loop.")
150
+ for nn in range(num_loops):
151
+ print(f"Running loop {nn}")
152
+ loop_start = time.perf_counter()
153
+
154
+ (image_buffer,
155
+ depth_buffer,
156
+ elems_in_image) = pyv.rastercyth.raster_frame(
157
+ render_mesh.coords,
158
+ render_mesh.connectivity,
159
+ fields_render,
160
+ cam_data)
161
+
162
+ loop_times[nn] = time.perf_counter() - loop_start
163
+
164
+ print()
165
+ print(80*"=")
166
+ print("PERFORMANCE TIMERS")
167
+ print(f"Elements in image = {elems_in_image}")
168
+ print(f"Image buffer shape = {image_buffer.shape}")
169
+ print(f"Avg. total render time = {np.mean(loop_times):.4f} seconds")
170
+ print(f"Avg. render time per frame = {(np.mean(loop_times)/total_frames):.4f} seconds")
171
+ print(80*"=")
172
+
173
+
174
+ #===========================================================================
175
+ # PLOTTING
176
+ plot_on = True
177
+ plot_field = 0
178
+
179
+ # depth_to_plot = np.copy(np.asarray(depth_buffer[:,:,plot_frame]))
180
+ # depth_to_plot[depth_buffer[:,:,plot_frame] > 10*cam_data.image_dist] = np.nan
181
+ # image_to_plot = np.copy(np.asarray(image_buffer[:,:,plot_frame,plot_field]))
182
+ # image_to_plot[depth_buffer[:,:,plot_frame] > 10*cam_data.image_dist] = np.nan
183
+
184
+ if plot_on:
185
+ plot_opts = pyv.PlotOptsGeneral()
186
+
187
+
188
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
189
+ layout='constrained')
190
+ fig.set_dpi(plot_opts.resolution)
191
+ cset = plt.imshow(depth_buffer[:,:],
192
+ cmap=plt.get_cmap(plot_opts.cmap_seq))
193
+ #origin='lower')
194
+ ax.set_aspect('equal','box')
195
+ fig.colorbar(cset)
196
+ ax.set_title(f"Depth buffer",fontsize=plot_opts.font_head_size)
197
+ ax.set_xlabel(r"x ($px$)",
198
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
199
+ ax.set_ylabel(r"y ($px$)",
200
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
201
+
202
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
203
+ layout='constrained')
204
+ fig.set_dpi(plot_opts.resolution)
205
+ cset = plt.imshow(image_buffer[:,:,plot_field],
206
+ cmap=plt.get_cmap(plot_opts.cmap_seq))
207
+ #origin='lower')
208
+ ax.set_aspect('equal','box')
209
+ fig.colorbar(cset)
210
+ ax.set_title(f"Field Image",fontsize=plot_opts.font_head_size)
211
+ ax.set_xlabel(r"x ($px$)",
212
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
213
+ ax.set_ylabel(r"y ($px$)",
214
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
215
+
216
+ plt.show()
217
+
218
+
219
+ if __name__ == "__main__":
220
+ main()
@@ -0,0 +1,194 @@
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 time
10
+ import numpy as np
11
+ from scipy.spatial.transform import Rotation
12
+ import matplotlib.pyplot as plt
13
+ import mooseherder as mh
14
+ import pyvale as pyv
15
+
16
+ def main() -> None:
17
+ """pyvale example: rasterisation field renderer
18
+ ----------------------------------------------------------------------------
19
+ - TODO
20
+ """
21
+ print()
22
+ print(80*"=")
23
+ print("RASTER CYTHON FILE (should be *.so on Linux):")
24
+ print(pyv.rastercyth.__file__)
25
+ print(80*"=")
26
+ print()
27
+
28
+ # This a path to an exodus *.e output file from MOOSE, this can be
29
+ # replaced with a path to your own simulation file
30
+ sim_path = pyv.DataSet.render_mechanical_3d_path()
31
+ #sim_path = pyv.DataSet.render_simple_block_path()
32
+ #sim_path = Path.home()/"pyvale"/"src"/"pyvale"/"simcases"/"case26_out.e"
33
+ sim_data = mh.ExodusReader(sim_path).read_all_sim_data()
34
+
35
+ disp_comps = ("disp_x","disp_y","disp_z")
36
+
37
+ # Scale m -> mm
38
+ sim_data = pyv.scale_length_units(sim_data,disp_comps,1000.0)
39
+
40
+ print()
41
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_x']))=}")
42
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_y']))=}")
43
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_z']))=}")
44
+ print()
45
+
46
+ # Extracts the surface mesh from a full 3d simulation for rendering
47
+ render_mesh = pyv.create_render_mesh(sim_data,
48
+ ("disp_y","disp_x"),
49
+ sim_spat_dim=3,
50
+ field_disp_keys=disp_comps)
51
+
52
+ print()
53
+ print(80*"-")
54
+ print("MESH DATA:")
55
+ print(80*"-")
56
+ print("connectivity.shape=(num_elems,num_nodes_per_elem)")
57
+ print(f"{render_mesh.connectivity.shape=}")
58
+ print()
59
+ print("coords.shape=(num_nodes,coord[x,y,z])")
60
+ print(f"{render_mesh.coords.shape=}")
61
+ print()
62
+ print("fields.shape=(num_coords,num_time_steps,num_components)")
63
+ print(f"{render_mesh.fields_render.shape=}")
64
+ if render_mesh.fields_disp is not None:
65
+ print(f"{render_mesh.fields_disp.shape=}")
66
+ print(80*"-")
67
+ print()
68
+
69
+ pixel_num = np.array((960,1280),dtype=np.int32)
70
+ pixel_size = np.array((5.3e-3,5.3e-3),dtype=np.float64)
71
+ focal_leng: float = 50.0
72
+ cam_rot = Rotation.from_euler("zyx",(0.0,-30.0,-10.0),degrees=True)
73
+ fov_scale_factor: float = 1.1
74
+
75
+ (roi_pos_world,
76
+ cam_pos_world) = pyv.CameraTools.pos_fill_frame_from_rotation(
77
+ coords_world=render_mesh.coords,
78
+ pixel_num=pixel_num,
79
+ pixel_size=pixel_size,
80
+ focal_leng=focal_leng,
81
+ cam_rot=cam_rot,
82
+ frame_fill=fov_scale_factor,
83
+ )
84
+
85
+ cam_data = pyv.CameraData(
86
+ pixels_num=pixel_num,
87
+ pixels_size=pixel_size,
88
+ pos_world=cam_pos_world,
89
+ rot_world=cam_rot,
90
+ roi_cent_world=roi_pos_world,
91
+ focal_length=focal_leng,
92
+ sub_samp=2,
93
+ back_face_removal=True,
94
+ )
95
+
96
+ print(80*"-")
97
+ print("CAMERA DATA:")
98
+ print(80*"-")
99
+ print(f"{cam_data.image_dist=}")
100
+ print(f"{cam_data.roi_cent_world=}")
101
+ print(f"{cam_data.pos_world=}")
102
+ print()
103
+ print("World to camera matrix:")
104
+ print(cam_data.world_to_cam_mat)
105
+ print(80*"-")
106
+ print()
107
+
108
+ print(80*"-")
109
+ total_frames = render_mesh.fields_render.shape[1]*render_mesh.fields_render.shape[2]
110
+ print(f"Time steps to render: {render_mesh.fields_render.shape[1]}")
111
+ print(f"Fields to render: {render_mesh.fields_render.shape[2]}")
112
+ print(f"Total frames to render: {total_frames}")
113
+ print(80*"-")
114
+
115
+ print(80*"=")
116
+ print("RASTER ELEMENT LOOP START")
117
+ print(80*"=")
118
+
119
+ num_loops = 1
120
+ loop_times = np.zeros((num_loops,),dtype=np.float64)
121
+
122
+ print()
123
+ print("Running raster loop.")
124
+ for nn in range(num_loops):
125
+ print(f"Running loop {nn}")
126
+ loop_start = time.perf_counter()
127
+
128
+ (image_buffer,
129
+ depth_buffer,
130
+ elems_in_image) = pyv.rastercyth.raster_static_mesh(
131
+ render_mesh,
132
+ cam_data,
133
+ 0)
134
+
135
+ loop_times[nn] = time.perf_counter() - loop_start
136
+
137
+ print()
138
+ print(80*"=")
139
+ print("PERFORMANCE TIMERS")
140
+ print(f"Elements in image = {elems_in_image}")
141
+ print(f"Image buffer shape = {image_buffer.shape}")
142
+ print(f"Avg. total render time = {np.mean(loop_times):.4f} seconds")
143
+ print(f"Avg. render time per frame = {(np.mean(loop_times)/total_frames):.4f} seconds")
144
+ print(80*"=")
145
+
146
+
147
+ #===========================================================================
148
+ # PLOTTING
149
+ plot_on = True
150
+ plot_frames = (1,)#range(3)
151
+ plot_field = 0
152
+
153
+ # depth_to_plot = np.copy(np.asarray(depth_buffer[:,:,plot_frame]))
154
+ # depth_to_plot[depth_buffer[:,:,plot_frame] > 10*cam_data.image_dist] = np.nan
155
+ # image_to_plot = np.copy(np.asarray(image_buffer[:,:,plot_frame,plot_field]))
156
+ # image_to_plot[depth_buffer[:,:,plot_frame] > 10*cam_data.image_dist] = np.nan
157
+
158
+ if plot_on:
159
+ plot_opts = pyv.PlotOptsGeneral()
160
+
161
+ for ff in plot_frames:
162
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
163
+ layout='constrained')
164
+ fig.set_dpi(plot_opts.resolution)
165
+ cset = plt.imshow(depth_buffer[:,:,ff],
166
+ cmap=plt.get_cmap(plot_opts.cmap_seq))
167
+ #origin='lower')
168
+ ax.set_aspect('equal','box')
169
+ fig.colorbar(cset)
170
+ ax.set_title(f"Depth buffer: {ff}",fontsize=plot_opts.font_head_size)
171
+ ax.set_xlabel(r"x ($px$)",
172
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
173
+ ax.set_ylabel(r"y ($px$)",
174
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
175
+
176
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
177
+ layout='constrained')
178
+ fig.set_dpi(plot_opts.resolution)
179
+ cset = plt.imshow(image_buffer[:,:,ff,plot_field],
180
+ cmap=plt.get_cmap(plot_opts.cmap_seq))
181
+ #origin='lower')
182
+ ax.set_aspect('equal','box')
183
+ fig.colorbar(cset)
184
+ ax.set_title(f"Field Image: {ff}",fontsize=plot_opts.font_head_size)
185
+ ax.set_xlabel(r"x ($px$)",
186
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
187
+ ax.set_ylabel(r"y ($px$)",
188
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
189
+
190
+ plt.show()
191
+
192
+
193
+ if __name__ == "__main__":
194
+ main()
@@ -0,0 +1,193 @@
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 time
10
+ import numpy as np
11
+ from scipy.spatial.transform import Rotation
12
+ import matplotlib.pyplot as plt
13
+ import mooseherder as mh
14
+ import pyvale as pyv
15
+
16
+ def main() -> None:
17
+ """pyvale example: rasterisation field renderer
18
+ ----------------------------------------------------------------------------
19
+ - TODO
20
+ """
21
+ print()
22
+ print(80*"=")
23
+ print("RASTER CYTHON FILE (should be *.so on Linux):")
24
+ print(pyv.rastercyth.__file__)
25
+ print(80*"=")
26
+ print()
27
+
28
+ # This a path to an exodus *.e output file from MOOSE, this can be
29
+ # replaced with a path to your own simulation file
30
+ sim_path = pyv.DataSet.render_mechanical_3d_path()
31
+ #sim_path = pyv.DataSet.render_simple_block_path()
32
+ #sim_path = Path.home()/"pyvale"/"src"/"pyvale"/"simcases"/"case26_out.e"
33
+ sim_data = mh.ExodusReader(sim_path).read_all_sim_data()
34
+
35
+ disp_comps = ("disp_x","disp_y","disp_z")
36
+
37
+ # Scale m -> mm
38
+ sim_data = pyv.scale_length_units(sim_data,disp_comps,1000.0)
39
+
40
+ print()
41
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_x']))=}")
42
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_y']))=}")
43
+ print(f"{np.max(np.abs(sim_data.node_vars['disp_z']))=}")
44
+ print()
45
+
46
+ # Extracts the surface mesh from a full 3d simulation for rendering
47
+ render_mesh = pyv.create_render_mesh(sim_data,
48
+ ("disp_y","disp_x"),
49
+ sim_spat_dim=3,
50
+ field_disp_keys=disp_comps)
51
+
52
+ print()
53
+ print(80*"-")
54
+ print("MESH DATA:")
55
+ print(80*"-")
56
+ print("connectivity.shape=(num_elems,num_nodes_per_elem)")
57
+ print(f"{render_mesh.connectivity.shape=}")
58
+ print()
59
+ print("coords.shape=(num_nodes,coord[x,y,z])")
60
+ print(f"{render_mesh.coords.shape=}")
61
+ print()
62
+ print("fields.shape=(num_coords,num_time_steps,num_components)")
63
+ print(f"{render_mesh.fields_render.shape=}")
64
+ if render_mesh.fields_disp is not None:
65
+ print(f"{render_mesh.fields_disp.shape=}")
66
+ print(80*"-")
67
+ print()
68
+
69
+ pixel_num = np.array((960,1280),dtype=np.int32)
70
+ pixel_size = np.array((5.3e-3,5.3e-3),dtype=np.float64)
71
+ focal_leng: float = 50.0
72
+ cam_rot = Rotation.from_euler("zyx",(0.0,-30.0,-10.0),degrees=True)
73
+ fov_scale_factor: float = 1.1
74
+
75
+ (roi_pos_world,
76
+ cam_pos_world) = pyv.CameraTools.pos_fill_frame_from_rotation(
77
+ coords_world=render_mesh.coords,
78
+ pixel_num=pixel_num,
79
+ pixel_size=pixel_size,
80
+ focal_leng=focal_leng,
81
+ cam_rot=cam_rot,
82
+ frame_fill=fov_scale_factor,
83
+ )
84
+
85
+ cam_data = pyv.CameraData(
86
+ pixels_num=pixel_num,
87
+ pixels_size=pixel_size,
88
+ pos_world=cam_pos_world,
89
+ rot_world=cam_rot,
90
+ roi_cent_world=roi_pos_world,
91
+ focal_length=focal_leng,
92
+ sub_samp=2,
93
+ back_face_removal=True,
94
+ )
95
+
96
+ print(80*"-")
97
+ print("CAMERA DATA:")
98
+ print(80*"-")
99
+ print(f"{cam_data.image_dist=}")
100
+ print(f"{cam_data.roi_cent_world=}")
101
+ print(f"{cam_data.pos_world=}")
102
+ print()
103
+ print("World to camera matrix:")
104
+ print(cam_data.world_to_cam_mat)
105
+ print(80*"-")
106
+ print()
107
+
108
+ print(80*"-")
109
+ total_frames = render_mesh.fields_render.shape[1]*render_mesh.fields_render.shape[2]
110
+ print(f"Time steps to render: {render_mesh.fields_render.shape[1]}")
111
+ print(f"Fields to render: {render_mesh.fields_render.shape[2]}")
112
+ print(f"Total frames to render: {total_frames}")
113
+ print(80*"-")
114
+
115
+ print(80*"=")
116
+ print("RASTER ELEMENT LOOP START")
117
+ print(80*"=")
118
+
119
+ num_loops = 1
120
+ loop_times = np.zeros((num_loops,),dtype=np.float64)
121
+
122
+ print()
123
+ print("Running raster loop.")
124
+ for nn in range(num_loops):
125
+ print(f"Running loop {nn}")
126
+ loop_start = time.perf_counter()
127
+
128
+ (image_buffer,
129
+ depth_buffer,
130
+ elems_in_images) = pyv.RasterCY.raster_static_mesh(cam_data,
131
+ render_mesh,
132
+ 16)
133
+
134
+ loop_times[nn] = time.perf_counter() - loop_start
135
+
136
+ print()
137
+ print(80*"=")
138
+ print("PERFORMANCE TIMERS")
139
+ print(f"Elements in image = {elems_in_images}")
140
+ print(f"Image buffer shape = {image_buffer.shape}")
141
+ print(f"Avg. total render time = {np.mean(loop_times):.4f} seconds")
142
+ print(f"Avg. render time per frame = {(np.mean(loop_times)/total_frames):.4f} seconds")
143
+ print(80*"=")
144
+
145
+
146
+ #===========================================================================
147
+ # PLOTTING
148
+ plot_on = False
149
+ plot_frames = (-1,)#range(3)
150
+ plot_field = 0
151
+
152
+ # depth_to_plot = np.copy(np.asarray(depth_buffer[:,:,plot_frame]))
153
+ # depth_to_plot[depth_buffer[:,:,plot_frame] > 10*cam_data.image_dist] = np.nan
154
+ # image_to_plot = np.copy(np.asarray(image_buffer[:,:,plot_frame,plot_field]))
155
+ # image_to_plot[depth_buffer[:,:,plot_frame] > 10*cam_data.image_dist] = np.nan
156
+
157
+ if plot_on:
158
+ plot_opts = pyv.PlotOptsGeneral()
159
+
160
+ for ff in plot_frames:
161
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
162
+ layout='constrained')
163
+ fig.set_dpi(plot_opts.resolution)
164
+ cset = plt.imshow(depth_buffer[:,:,ff],
165
+ cmap=plt.get_cmap(plot_opts.cmap_seq))
166
+ #origin='lower')
167
+ ax.set_aspect('equal','box')
168
+ fig.colorbar(cset)
169
+ ax.set_title(f"Depth buffer: {ff}",fontsize=plot_opts.font_head_size)
170
+ ax.set_xlabel(r"x ($px$)",
171
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
172
+ ax.set_ylabel(r"y ($px$)",
173
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
174
+
175
+ (fig, ax) = plt.subplots(figsize=plot_opts.single_fig_size_square,
176
+ layout='constrained')
177
+ fig.set_dpi(plot_opts.resolution)
178
+ cset = plt.imshow(image_buffer[:,:,ff,plot_field],
179
+ cmap=plt.get_cmap(plot_opts.cmap_seq))
180
+ #origin='lower')
181
+ ax.set_aspect('equal','box')
182
+ fig.colorbar(cset)
183
+ ax.set_title(f"Field Image: {ff}",fontsize=plot_opts.font_head_size)
184
+ ax.set_xlabel(r"x ($px$)",
185
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
186
+ ax.set_ylabel(r"y ($px$)",
187
+ fontsize=plot_opts.font_ax_size, fontname=plot_opts.font_name)
188
+
189
+ plt.show()
190
+
191
+
192
+ if __name__ == "__main__":
193
+ main()