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,108 @@
1
+ '''
2
+ ================================================================================
3
+ DEV: calibration check
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 numpy as np
12
+ import mooseherder as mh
13
+ import pyvale
14
+
15
+
16
+ def assumed_calib(signal: np.ndarray) -> np.ndarray:
17
+ return 24.3*signal + 0.616
18
+
19
+
20
+ def truth_calib(signal: np.ndarray) -> np.ndarray:
21
+ return -0.01897 + 25.41881*signal - 0.42456*signal**2 + 0.04365*signal**3
22
+
23
+
24
+ def main() -> None:
25
+ """pyvale example: sensor calibration error
26
+ Based on K-type thermocouple calibration
27
+ """
28
+ n_divs = 10000
29
+ signal_calib_range = np.array((0,6))
30
+ v = np.linspace(signal_calib_range[0],signal_calib_range[1],n_divs)
31
+
32
+ temp_truth = -0.01897 + 25.41881*v - 0.42456*v**2 + 0.04365*v**3
33
+ temp_assumed = 24.3*v + 0.616
34
+ calib_error = temp_assumed - temp_truth
35
+ print()
36
+ print(80*"-")
37
+ print(f"Calibration error over signal:"+\
38
+ f" {signal_calib_range[0]} to {signal_calib_range[1]} mV")
39
+ print(calib_error)
40
+ print(f"Max calib error: {np.max(calib_error)}")
41
+ print(f"Min calib error: {np.min(calib_error)}")
42
+ print()
43
+
44
+ data_path = pyvale.DataSet.thermal_2d_path()
45
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
46
+ # Scale to mm to make 3D visualisation scaling easier
47
+ sim_data.coords = sim_data.coords*1000.0
48
+
49
+ descriptor = pyvale.SensorDescriptorFactory.temperature_descriptor()
50
+
51
+ field_key = 'temperature'
52
+ t_field = pyvale.FieldScalar(sim_data,
53
+ field_key=field_key,
54
+ spat_dims=2)
55
+
56
+ n_sens = (4,1,1)
57
+ x_lims = (0.0,100.0)
58
+ y_lims = (0.0,50.0)
59
+ z_lims = (0.0,0.0)
60
+ sens_pos = pyvale.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
61
+
62
+ use_sim_time = False
63
+ if use_sim_time:
64
+ sample_times = None
65
+ else:
66
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
67
+
68
+ sens_data = pyvale.SensorData(positions=sens_pos,
69
+ sample_times=sample_times)
70
+
71
+ tc_array = pyvale.SensorArrayPoint(sens_data,
72
+ t_field,
73
+ descriptor)
74
+
75
+ cal_err = pyvale.ErrSysCalibration(assumed_calib,
76
+ truth_calib,
77
+ signal_calib_range,
78
+ n_cal_divs=10000)
79
+ sys_err_int = pyvale.ErrIntegrator([cal_err],
80
+ sens_data,
81
+ tc_array.get_measurement_shape())
82
+ tc_array.set_error_integrator(sys_err_int)
83
+
84
+ measurements = tc_array.get_measurements()
85
+
86
+ print('\n'+80*'-')
87
+ print('For a sensor: measurement = truth + sysematic error + random error')
88
+ print(f'measurements.shape = {measurements.shape} = '+
89
+ '(n_sensors,n_field_components,n_timesteps)\n')
90
+ print("The truth, systematic error and random error arrays have the same "+
91
+ "shape.")
92
+
93
+ print(80*'-')
94
+ print('Looking at the last 5 time steps (measurements) of sensor 0:')
95
+ pyvale.print_measurements(tc_array,
96
+ (0,1),
97
+ (0,1),
98
+ (measurements.shape[2]-5,measurements.shape[2]))
99
+ print(80*'-')
100
+
101
+ pyvale.plot_time_traces(tc_array,field_key)
102
+ plt.show()
103
+
104
+
105
+
106
+
107
+ if __name__ == "__main__":
108
+ main()
@@ -0,0 +1,141 @@
1
+ """
2
+ ================================================================================
3
+ example: displacement sensors on a 2d plate
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 mooseherder as mh
13
+ import pyvale
14
+
15
+ def main() -> None:
16
+ data_path = pyvale.DataSet.mechanical_2d_path()
17
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
18
+ # Scale to mm to make 3D visualisation scaling easier
19
+ sim_data.coords = sim_data.coords*1000.0 # type: ignore
20
+
21
+ descriptor = pyvale.SensorDescriptorFactory.displacement_descriptor()
22
+
23
+ spat_dims = 2
24
+ field_key = "disp"
25
+ components = ("disp_x","disp_y")
26
+ disp_field = pyvale.FieldVector(sim_data,field_key,components,spat_dims)
27
+
28
+ n_sens = (2,3,1)
29
+ x_lims = (0.0,100.0)
30
+ y_lims = (0.0,150.0)
31
+ z_lims = (0.0,0.0)
32
+ sensor_positions = pyvale.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
33
+
34
+ use_sim_time = True
35
+ if use_sim_time:
36
+ sample_times = None
37
+ else:
38
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
39
+
40
+ sensor_data = pyvale.SensorData(positions=sensor_positions,
41
+ sample_times=sample_times)
42
+
43
+ disp_sens_array = pyvale.SensorArrayPoint(sensor_data,
44
+ disp_field,
45
+ descriptor)
46
+
47
+ pos_offset = -1.0*np.ones_like(sensor_positions)
48
+ pos_offset[:,2] = 0 # in 2d we only have offset in x and y so zero z
49
+ pos_error_data = pyvale.ErrFieldData(pos_offset_xyz=pos_offset)
50
+
51
+ angle_offset = np.zeros_like(sensor_positions)
52
+ angle_offset[:,0] = 1.0 # only rotate about z in 2D
53
+ angle_error_data = pyvale.ErrFieldData(ang_offset_zyx=angle_offset)
54
+
55
+ time_offset = 2.0*np.ones_like(disp_sens_array.get_sample_times())
56
+ time_error_data = pyvale.ErrFieldData(time_offset=time_offset)
57
+
58
+ field_errs = []
59
+ field_errs.append(pyvale.ErrSysField(disp_field,
60
+ time_error_data))
61
+ field_errs.append(pyvale.ErrSysField(disp_field,
62
+ time_error_data))
63
+
64
+ field_errs.append(pyvale.ErrSysField(disp_field,
65
+ pos_error_data))
66
+ field_errs.append(pyvale.ErrSysField(disp_field,
67
+ pos_error_data))
68
+
69
+ field_errs.append(pyvale.ErrSysField(disp_field,
70
+ angle_error_data))
71
+ field_errs.append(pyvale.ErrSysField(disp_field,
72
+ angle_error_data))
73
+
74
+ err_int_opts = pyvale.ErrIntOpts(force_dependence=True,
75
+ store_all_errs=True)
76
+ error_int = pyvale.ErrIntegrator(field_errs,
77
+ sensor_data,
78
+ disp_sens_array.get_measurement_shape(),
79
+ err_int_opts)
80
+ disp_sens_array.set_error_integrator(error_int)
81
+
82
+ measurements = disp_sens_array.calc_measurements()
83
+
84
+ sens_data_by_chain = error_int.get_sens_data_by_chain()
85
+ if sens_data_by_chain is not None:
86
+ for ii,ss in enumerate(sens_data_by_chain):
87
+ print(80*"-")
88
+ if ss is not None:
89
+ print(f"SensorData @ [{ii}]")
90
+ print("TIME")
91
+ print(ss.sample_times)
92
+ print()
93
+ print("POSITIONS")
94
+ print(ss.positions)
95
+ print()
96
+ print("ANGLES")
97
+ for aa in ss.angles:
98
+ print(aa.as_euler("zyx",degrees=True))
99
+ print()
100
+ print(80*"-")
101
+
102
+ print()
103
+ print(80*"=")
104
+ sens_data_accumulated = error_int.get_sens_data_accumulated()
105
+ print("TIME")
106
+ print(sens_data_accumulated.sample_times)
107
+ print()
108
+ print("POSITIONS")
109
+ print(sens_data_accumulated.positions)
110
+ print()
111
+ print("ANGLES")
112
+ for aa in sens_data_accumulated.angles:
113
+ print(aa.as_euler("zyx",degrees=True))
114
+ print()
115
+ print(80*"=")
116
+
117
+ print(80*"-")
118
+ sens_num = 4
119
+ print("The last 5 time steps (measurements) of sensor {sens_num}:")
120
+ pyvale.print_measurements(disp_sens_array,
121
+ (sens_num-1,sens_num),
122
+ (0,1),
123
+ (measurements.shape[2]-5,measurements.shape[2]))
124
+ print(80*"-")
125
+
126
+ plot_field = "disp_x"
127
+
128
+ if plot_field == "disp_x":
129
+ pv_plot = pyvale.plot_point_sensors_on_sim(disp_sens_array,"disp_x")
130
+ pv_plot.show()
131
+ elif plot_field == "disp_y":
132
+ pv_plot = pyvale.plot_point_sensors_on_sim(disp_sens_array,"disp_y")
133
+ pv_plot.show()
134
+
135
+ pyvale.plot_time_traces(disp_sens_array,"disp_x")
136
+ pyvale.plot_time_traces(disp_sens_array,"disp_y")
137
+ plt.show()
138
+
139
+
140
+ if __name__ == "__main__":
141
+ main()
@@ -0,0 +1,78 @@
1
+ '''
2
+ ================================================================================
3
+ Example: thermocouples on a 2d plate
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 mooseherder as mh
13
+ import pyvale
14
+
15
+
16
+ def main() -> None:
17
+ """pyvale example: point sensors on a 2D thermal simulation
18
+ ----------------------------------------------------------------------------
19
+ """
20
+ data_path = pyvale.DataSet.thermal_2d_path()
21
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
22
+ field_key = list(sim_data.node_vars.keys())[0] # type: ignore
23
+ # Scale to mm to make 3D visualisation scaling easier
24
+ sim_data.coords = sim_data.coords*1000.0 # type: ignore
25
+
26
+ n_sens = (4,1,1)
27
+ x_lims = (0.0,100.0)
28
+ y_lims = (0.0,50.0)
29
+ z_lims = (0.0,0.0)
30
+ sens_pos = pyvale.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
31
+
32
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50) # | None
33
+
34
+ sensor_data = pyvale.SensorData(positions=sens_pos,
35
+ sample_times=sample_times)
36
+
37
+ tc_array = pyvale.SensorArrayFactory \
38
+ .thermocouples_no_errs(sim_data,
39
+ sensor_data,
40
+ field_key,
41
+ spat_dims=2)
42
+
43
+ #---------------------------------------------------------------------------
44
+ # Standard independent systematic errors
45
+ pos_rand = pyvale.GeneratorNormal(std=1.0) # mm
46
+ pos_lock = np.full_like(sensor_data.positions,False,dtype=bool)
47
+ pos_lock[:,2] = True
48
+ field_err_data = pyvale.ErrFieldData(
49
+ pos_rand_xyz=(pos_rand,pos_rand,pos_rand),
50
+ pos_lock_xyz=pos_lock
51
+ )
52
+
53
+ err_chain = []
54
+ err_chain.append(pyvale.ErrSysField(tc_array.get_field(),
55
+ field_err_data))
56
+ err_int = pyvale.ErrIntegrator(err_chain,
57
+ sensor_data,
58
+ tc_array.get_measurement_shape())
59
+ tc_array.set_error_integrator(err_int)
60
+
61
+
62
+ #---------------------------------------------------------------------------
63
+ measurements = tc_array.calc_measurements()
64
+ print(80*'-')
65
+ sens_num = 4
66
+ print('The last 5 time steps (measurements) of sensor {sens_num}:')
67
+ pyvale.print_measurements(tc_array,
68
+ (sens_num-1,sens_num),
69
+ (0,1),
70
+ (measurements.shape[2]-5,measurements.shape[2]))
71
+ print(80*'-')
72
+
73
+ pyvale.plot_time_traces(tc_array,field_key)
74
+ plt.show()
75
+
76
+
77
+ if __name__ == '__main__':
78
+ main()
@@ -0,0 +1,110 @@
1
+ """
2
+ ================================================================================
3
+ Example: displacement sensors on a 2d plate
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
+ from scipy.spatial.transform import Rotation
13
+ import mooseherder as mh
14
+ import pyvale
15
+
16
+ def main() -> None:
17
+ """pyvale example: single rotation batch processing
18
+ ----------------------------------------------------------------------------
19
+ - Tests that when only one sensor rotation is provided that
20
+ all sensors are assumed to have the same rotation and batch processed.
21
+ """
22
+ data_path = pyvale.DataSet.mechanical_2d_path()
23
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
24
+ # Scale to mm to make 3D visualisation scaling easier
25
+ sim_data.coords = sim_data.coords*1000.0 # type: ignore
26
+
27
+ descriptor = pyvale.SensorDescriptorFactory.displacement_descriptor()
28
+
29
+ spat_dims = 2
30
+ field_key = "disp"
31
+ components = ("disp_x","disp_y")
32
+ disp_field = pyvale.FieldVector(sim_data,field_key,components,spat_dims)
33
+
34
+ #---------------------------------------------------------------------------
35
+ n_sens = (2,3,1)
36
+ x_lims = (0.0,100.0)
37
+ y_lims = (0.0,150.0)
38
+ z_lims = (0.0,0.0)
39
+ sensor_positions = pyvale.create_sensor_pos_array(n_sens,
40
+ x_lims,
41
+ y_lims,
42
+ z_lims)
43
+
44
+ use_sim_time = False
45
+ if use_sim_time:
46
+ sample_times = None
47
+ else:
48
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
49
+
50
+ # Provide only a single rotation for the 6 sensors
51
+ sensor_angles = (Rotation.from_euler("zyx", [180, 0, 0], degrees=True),)
52
+
53
+ sensor_data_norot = pyvale.SensorData(positions=sensor_positions,
54
+ sample_times=sample_times)
55
+
56
+
57
+ sensor_data_rot = pyvale.SensorData(positions=sensor_positions,
58
+ sample_times=sample_times,
59
+ angles=sensor_angles)
60
+
61
+ #---------------------------------------------------------------------------
62
+ disp_sensors_norot = pyvale.SensorArrayPoint(sensor_data_norot,
63
+ disp_field,
64
+ descriptor)
65
+
66
+
67
+ disp_sensors_rot = pyvale.SensorArrayPoint(sensor_data_rot,
68
+ disp_field,
69
+ descriptor)
70
+
71
+
72
+
73
+ measurements_norot = disp_sensors_norot.calc_measurements()
74
+ measurements_rot = disp_sensors_rot.calc_measurements()
75
+
76
+ #---------------------------------------------------------------------------
77
+ sens_to_print = 1
78
+ print(80*"-")
79
+ print(f"The last 5 time steps (measurements) of non-rotated sensor {sens_to_print}:")
80
+ pyvale.print_measurements(disp_sensors_norot,
81
+ (sens_to_print-1,sens_to_print),
82
+ (0,1),
83
+ (measurements_norot.shape[2]-5,measurements_norot.shape[2]))
84
+ print(80*"-")
85
+ print(f"The last 5 time steps (measurements) of rotated sensor {sens_to_print}:")
86
+ pyvale.print_measurements(disp_sensors_rot,
87
+ (sens_to_print-1,sens_to_print),
88
+ (0,1),
89
+ (measurements_rot.shape[2]-5,measurements_rot.shape[2]))
90
+ print(80*"-")
91
+
92
+ #---------------------------------------------------------------------------
93
+ plot_field = "disp_x"
94
+
95
+ if plot_field == "disp_x":
96
+ pv_plot = pyvale.plot_point_sensors_on_sim(disp_sensors_rot,"disp_x")
97
+ pv_plot.show(cpos="xy")
98
+ elif plot_field == "disp_y":
99
+ pv_plot = pyvale.plot_point_sensors_on_sim(disp_sensors_rot,"disp_y")
100
+ pv_plot.show(cpos="xy")
101
+
102
+ (fig,ax) = pyvale.plot_time_traces(disp_sensors_norot,plot_field)
103
+ ax.set_title("No rotation")
104
+ (fig,ax) = pyvale.plot_time_traces(disp_sensors_rot,plot_field)
105
+ ax.set_title("Rotated")
106
+ plt.show()
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()
@@ -0,0 +1,86 @@
1
+ """
2
+ ================================================================================
3
+ pyvale: the python computer aided validation engine
4
+
5
+ License: MIT
6
+ Copyright (C) 2025 The Computer Aided Validation Team
7
+ ================================================================================
8
+ """
9
+
10
+ from pathlib import Path
11
+ import numpy as np
12
+ import mooseherder as mh
13
+ import pyvale as pyv
14
+
15
+
16
+ def main() -> None:
17
+ #---------------------------------------------------------------------------
18
+ # LOAD FILES
19
+ sim_path = pyv.DataSet.mechanical_2d_path()
20
+ sim_data = mh.ExodusReader(sim_path).read_all_sim_data()
21
+
22
+ image_path = pyv.DataSet.dic_pattern_5mpx_path()
23
+ image_speckle = pyv.CameraTools.load_image(image_path)
24
+
25
+ save_path = Path.cwd()/"exampleoutput"
26
+
27
+ coords = sim_data.coords
28
+ connectivity = (sim_data.connect["connect1"]-1).T # Beware 0 indexing here
29
+ disp_x = sim_data.node_vars["disp_x"][:,:]
30
+ disp_y = sim_data.node_vars["disp_y"][:,:]
31
+
32
+ print()
33
+ print(80*"-")
34
+ print(f"{coords.shape=}")
35
+ print(f"{connectivity.shape=}")
36
+ print(f"{disp_x.shape=}")
37
+ print(f"{disp_y.shape=}")
38
+ print(80*"-")
39
+
40
+ #---------------------------------------------------------------------------
41
+ # INPUT DATA
42
+ cam_data = pyv.CameraData2D(pixels_count=np.array((1040,1540)),
43
+ leng_per_px=0.1e-3,
44
+ bits=8,
45
+ roi_cent_world=np.mean(coords,axis=0),
46
+ subsample=3)
47
+ id_opts = pyv.ImageDefOpts(save_path=save_path,
48
+ crop_on=True,
49
+ add_static_ref=True)
50
+
51
+
52
+ #---------------------------------------------------------------------------
53
+ # PRE-PROCESS IMAGES
54
+ (upsampled_image,
55
+ image_mask,
56
+ image_input,
57
+ disp_x,
58
+ disp_y) = pyv.ImageDef2D.preprocess(cam_data,
59
+ image_speckle,
60
+ coords,
61
+ connectivity,
62
+ disp_x,
63
+ disp_y,
64
+ id_opts,
65
+ print_on=True)
66
+
67
+ ff = -1
68
+ disp = np.array((disp_x[:,ff],disp_y[:,ff])).T
69
+ print(f"{disp.shape=}")
70
+
71
+ #---------------------------------------------------------------------------
72
+ # DEFORM IMAGES AND SAVE
73
+ pyv.ImageDef2D.deform_images_to_disk(cam_data,
74
+ upsampled_image,
75
+ coords,
76
+ connectivity,
77
+ disp_x,
78
+ disp_y,
79
+ image_mask,
80
+ id_opts,
81
+ print_on=True)
82
+
83
+
84
+ if __name__ == "__main__":
85
+ main()
86
+
@@ -0,0 +1,154 @@
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
+ # TODO
17
+ # - Fix the image averaging function to use cython
18
+ # - Saving of the rendered images for post processing or analysis
19
+ # - Collapse image display functions into visual to simplify code
20
+ #
21
+ # CAMERA:
22
+ # - Need option to work camera rotation based on a given position
23
+ # - The z axis is easy as we can just do roi-cam_pos but what about x and y
24
+ #
25
+ # SCENE OBJECT:
26
+ # - Allow multiple objects in the scene with their own transformations
27
+ # - Allow multiple cameras in the scene
28
+
29
+
30
+ def main() -> None:
31
+ """pyvale example: rasterisation field renderer
32
+ ----------------------------------------------------------------------------
33
+ - TODO
34
+ """
35
+ # This a path to an exodus *.e output file from MOOSE, this can be
36
+ # replaced with a path to your own simulation file
37
+ #sim_path = Path.home()/"pyvale"/"src"/"pyvale"/"simcases"/"case21_out.e"
38
+ sim_path = pyv.DataSet.render_mechanical_3d_path()
39
+
40
+ disp_comps = ("disp_x","disp_y","disp_z")
41
+
42
+ sim_data = mh.ExodusReader(sim_path).read_all_sim_data()
43
+
44
+ # Scale m -> mm
45
+ sim_data = pyv.scale_length_units(sim_data,disp_comps,1000.0)
46
+
47
+ # Extracts the surface mesh from a full 3d simulation for rendering
48
+ render_mesh = pyv.create_render_mesh(sim_data,
49
+ ("disp_y","disp_x"),
50
+ sim_spat_dim=3,
51
+ field_disp_keys=disp_comps)
52
+
53
+ print()
54
+ print(80*"-")
55
+ print("MESH DATA:")
56
+ print(80*"-")
57
+ print("connectivity.shape=(num_elems,num_nodes_per_elem)")
58
+ print(f"{render_mesh.connectivity.shape=}")
59
+ print()
60
+ print("coords.shape=(num_nodes,coord[x,y,z])")
61
+ print(f"{render_mesh.coords.shape=}")
62
+ print()
63
+ print("fields.shape=(num_coords,num_time_steps,num_components)")
64
+ print(f"{render_mesh.fields_render.shape=}")
65
+ if render_mesh.fields_disp is not None:
66
+ print(f"{render_mesh.fields_disp.shape=}")
67
+ print(80*"-")
68
+ print()
69
+
70
+
71
+ pixel_num = np.array((960,1280))
72
+ pixel_size = np.array((5.3e-3,5.3e-3))
73
+ focal_leng: float = 50
74
+ cam_rot = Rotation.from_euler("zyx",(0.0,-30.0,-10.0),degrees=True)
75
+ fov_scale_factor: float = 1.1
76
+
77
+ (roi_pos_world,
78
+ cam_pos_world) = pyv.CameraTools.pos_fill_frame_from_rotation(
79
+ coords_world=render_mesh.coords,
80
+ pixel_num=pixel_num,
81
+ pixel_size=pixel_size,
82
+ focal_leng=focal_leng,
83
+ cam_rot=cam_rot,
84
+ frame_fill=fov_scale_factor,
85
+ )
86
+
87
+ cam_data = pyv.CameraData(
88
+ pixels_num=pixel_num,
89
+ pixels_size=pixel_size,
90
+ pos_world=cam_pos_world,
91
+ rot_world=cam_rot,
92
+ roi_cent_world=roi_pos_world,
93
+ focal_length=focal_leng,
94
+ sub_samp=2,
95
+ back_face_removal=True,
96
+ )
97
+
98
+ print(80*"-")
99
+ print("CAMERA DATA:")
100
+ print(80*"-")
101
+ print(f"{roi_pos_world=}")
102
+ print(f"{cam_pos_world=}")
103
+ print()
104
+ print("World to camera matrix:")
105
+ print(cam_data.world_to_cam_mat)
106
+ print(80*"-")
107
+ print()
108
+
109
+ print(80*"-")
110
+ total_frames = render_mesh.fields_render.shape[1]*render_mesh.fields_render.shape[2]
111
+ print(f"Time steps to render: {render_mesh.fields_render.shape[1]}")
112
+ print(f"Fields to render: {render_mesh.fields_render.shape[2]}")
113
+ print(f"Total frames to render: {total_frames}")
114
+ print(80*"-")
115
+
116
+ print()
117
+ print(80*"=")
118
+ print("RASTER LOOP START")
119
+
120
+ #save_path = Path.cwd()/"example_output"
121
+ save_path = None
122
+ static_mesh = False
123
+
124
+ time_start_loop = time.perf_counter()
125
+ if static_mesh:
126
+ images = pyv.RasterNP.raster_static_mesh(
127
+ cam_data,render_mesh,save_path,threads_num=8
128
+ )
129
+ else:
130
+ time_start_loop = time.perf_counter()
131
+ images = pyv.RasterNP.raster_deformed_mesh(
132
+ cam_data,render_mesh,save_path,parallel=8
133
+ )
134
+
135
+ time_end_loop = time.perf_counter()
136
+ render_time = time_end_loop - time_start_loop
137
+
138
+
139
+ print("RASTER LOOP END")
140
+ print(80*"=")
141
+ print("PERFORMANCE")
142
+ print(f"Total frames = {total_frames}")
143
+ print(f"Total render time = {render_time:.4f} seconds")
144
+ print(f"Time per frame = {(render_time/total_frames):.4f} seconds")
145
+ print(80*"=")
146
+
147
+ plot_on = True
148
+ if plot_on:
149
+ (fig,ax) = pyv.plot_field_image(images[:,:,-1,0],
150
+ title_str="Disp. y, [mm]")
151
+ plt.show()
152
+
153
+ if __name__ == "__main__":
154
+ main()