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,100 @@
1
+ '''
2
+ ================================================================================
3
+ Example: strain 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 as R
13
+ import mooseherder as mh
14
+ import pyvale as pyv
15
+
16
+ def main() -> None:
17
+ """pyvale example: strain sensors on a 2D plate with a hole
18
+ ----------------------------------------------------------------------------
19
+ - Demonstrates rotation of tensor fields
20
+ """
21
+ data_path = pyv.DataSet.mechanical_2d_path()
22
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
23
+ # Scale to mm to make 3D visualisation scaling easier
24
+ sim_data.coords = sim_data.coords*1000.0 # type: ignore
25
+
26
+ descriptor = pyv.SensorDescriptorFactory.strain_descriptor()
27
+
28
+ spat_dims = 2
29
+ field_key = 'strain'
30
+ norm_components = ('strain_xx','strain_yy')
31
+ dev_components = ('strain_xy',)
32
+ strain_field = pyv.FieldTensor(sim_data,
33
+ field_key,
34
+ norm_components,
35
+ dev_components,
36
+ spat_dims)
37
+
38
+ n_sens = (2,3,1)
39
+ x_lims = (0.0,100.0)
40
+ y_lims = (0.0,150.0)
41
+ z_lims = (0.0,0.0)
42
+ sensor_positions = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,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
+ sens_data_norot = pyv.SensorData(positions=sensor_positions,
51
+ sample_times=sample_times)
52
+
53
+ sg_array_norot = pyv.SensorArrayPoint(sens_data_norot,
54
+ strain_field,
55
+ descriptor)
56
+
57
+ meas_norot = sg_array_norot.get_measurements()
58
+
59
+ sens_angles = sensor_positions.shape[0] * \
60
+ (R.from_euler("zyx", [45, 0, 0], degrees=True),)
61
+
62
+ sens_data_rot = pyv.SensorData(positions=sensor_positions,
63
+ sample_times=sample_times,
64
+ angles=sens_angles)
65
+
66
+ sg_array_rot = pyv.SensorArrayPoint(sens_data_rot,
67
+ strain_field,
68
+ descriptor)
69
+
70
+ angle_offset = np.zeros_like(sensor_positions)
71
+ angle_offset[:,0] = 1.0 # only rotate about z in 2D
72
+ angle_error_data = pyv.ErrFieldData(ang_offset_zyx=angle_offset)
73
+
74
+ sys_err_rot = pyv.ErrSysField(strain_field,angle_error_data)
75
+ err_int = pyv.ErrIntegrator([sys_err_rot],
76
+ sens_data_rot,
77
+ sg_array_rot.get_measurement_shape())
78
+ sg_array_rot.set_error_integrator(err_int)
79
+
80
+
81
+ meas_rot = sg_array_rot.get_measurements()
82
+
83
+
84
+ print(80*'-')
85
+ sens_num = 4
86
+ print('The last 5 time steps (measurements) of sensor {sens_num}:')
87
+ pyv.print_measurements(sg_array_rot,
88
+ (sens_num-1,sens_num),
89
+ (1,2),
90
+ (meas_rot.shape[2]-5,meas_rot.shape[2]))
91
+ print(80*'-')
92
+
93
+ plot_comp = 'strain_yy'
94
+ pyv.plot_time_traces(sg_array_norot,plot_comp)
95
+ pyv.plot_time_traces(sg_array_rot,plot_comp)
96
+ plt.show()
97
+
98
+
99
+ if __name__ == "__main__":
100
+ main()
@@ -0,0 +1,78 @@
1
+ '''
2
+ ================================================================================
3
+ Example: thermo-mechanical multiphysics on 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 as pyv
14
+
15
+
16
+ def main() -> None:
17
+ """pyvale example: thermo-mechanical multi-physics sensors on a 2D plate
18
+ ----------------------------------------------------------------------------
19
+ """
20
+ #===========================================================================
21
+ # Load Simulations as mooseherder.SimData objects
22
+ data_path = pyv.DataSet.thermomechanical_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
+ #===========================================================================
28
+ # Specify sensor locations and sample times
29
+ n_sens = (4,1,1)
30
+ x_lims = (0.0,100.0)
31
+ y_lims = (0.0,50.0)
32
+ z_lims = (0.0,0.0)
33
+ sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
34
+
35
+ use_sim_time = True
36
+ if use_sim_time:
37
+ sample_times = None
38
+ else:
39
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
40
+
41
+ sens_data = pyv.SensorData(positions=sens_pos,
42
+ sample_times=sample_times)
43
+
44
+ #===========================================================================
45
+ # Create pyvale sensor arrays for thermal and mechanical data
46
+ tc_field = 'temperature'
47
+ tc_array = pyv.SensorArrayFactory \
48
+ .thermocouples_basic_errs(sim_data,
49
+ sens_data,
50
+ tc_field,
51
+ spat_dims=2)
52
+
53
+ sg_field = 'strain'
54
+ sg_array = pyv.SensorArrayFactory \
55
+ .strain_gauges_basic_errs(sim_data,
56
+ sens_data,
57
+ sg_field,
58
+ spat_dims=2)
59
+
60
+ #===========================================================================
61
+ # Visualise Traces
62
+ print(80*'-')
63
+ sens_num = 4
64
+ print('THERMAL: The last 5 time steps (measurements) of sensor {sens_num}:')
65
+ pyv.print_measurements(tc_array,
66
+ (sens_num-1,sens_num),
67
+ (0,1),
68
+ (tc_array.get_measurement_shape()[2]-5,
69
+ tc_array.get_measurement_shape()[2]))
70
+ print(80*'-')
71
+
72
+ pyv.plot_time_traces(tc_array,"temperature")
73
+ pyv.plot_time_traces(sg_array,"strain_xx")
74
+ plt.show()
75
+
76
+
77
+ if __name__ == "__main__":
78
+ main()
@@ -0,0 +1,118 @@
1
+ '''
2
+ ================================================================================
3
+ Example: thermo-mechanical multiphysics 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 as pyv
14
+
15
+ def main() -> None:
16
+ """pyvale example: thermo-mechanical multi-physics sensors on a 2D plate
17
+ ----------------------------------------------------------------------------
18
+ - Demonstrates the experiment module for running many monte-carlo style
19
+ experiments and statistically analysing the results.
20
+ """
21
+ # Load Simulations as mooseherder.SimData objects
22
+ #base_path = Path("src/pyvale/data")
23
+ data_paths = pyv.DataSet.thermomechanical_2d_experiment_paths()
24
+
25
+ sim_list = []
26
+ for pp in data_paths:
27
+ sim_data = mh.ExodusReader(pp).read_all_sim_data()
28
+ # Scale to mm to make 3D visualisation scaling easier
29
+ sim_data.coords = sim_data.coords*1000.0 # type: ignore
30
+ sim_list.append(sim_data)
31
+
32
+ #===========================================================================
33
+ # Create pyvale sensor arrays for thermal and mechanical data
34
+ sim_data = sim_list[0]
35
+
36
+ n_sens = (4,1,1)
37
+ x_lims = (0.0,100.0)
38
+ y_lims = (0.0,50.0)
39
+ z_lims = (0.0,0.0)
40
+ sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
41
+
42
+ use_sim_time = True
43
+ if use_sim_time:
44
+ sample_times = None
45
+ else:
46
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
47
+
48
+ sens_data = pyv.SensorData(positions=sens_pos,
49
+ sample_times=sample_times)
50
+
51
+ tc_field = 'temperature'
52
+ tc_array = pyv.SensorArrayFactory \
53
+ .thermocouples_basic_errs(sim_data,
54
+ sens_data,
55
+ tc_field,
56
+ spat_dims=2,
57
+ errs_pc=1.0)
58
+
59
+ sg_field = 'strain'
60
+ sg_array = pyv.SensorArrayFactory \
61
+ .strain_gauges_basic_errs(sim_data,
62
+ sens_data,
63
+ sg_field,
64
+ spat_dims=2,
65
+ errs_pc=1.0)
66
+
67
+ sensor_arrays = [tc_array,sg_array]
68
+
69
+ #===========================================================================
70
+ # Create and run the simulated experiment
71
+ exp_sim = pyv.ExperimentSimulator(sim_list,
72
+ sensor_arrays,
73
+ num_exp_per_sim=1000)
74
+
75
+ exp_data = exp_sim.run_experiments()
76
+ exp_stats = exp_sim.calc_stats()
77
+
78
+ #===========================================================================
79
+ print(80*"=")
80
+ print("exp_data and exp_stats are lists where the index is the sensor array")
81
+ print("position in the list as field components are not consistent dims.\n")
82
+
83
+ print(80*"-")
84
+ print("Thermal sensor array @ exp_data[0]")
85
+ print(80*"-")
86
+ print("shape=(n_sims,n_exps,n_sensors,n_field_comps,n_time_steps)")
87
+ print(f"{exp_data[0].shape=}")
88
+ print()
89
+ print("Stats are calculated over all experiments (axis=1)")
90
+ print("shape=(n_sims,n_sensors,n_field_comps,n_time_steps)")
91
+ print(f"{exp_stats[0].max.shape=}")
92
+ print()
93
+ print(80*"-")
94
+ print("Mechanical sensor array @ exp_data[1]")
95
+ print(80*"-")
96
+ print("shape=(n_sims,n_exps,n_sensors,n_field_comps,n_time_steps)")
97
+ print(f"{exp_data[1].shape=}")
98
+ print()
99
+ print("shape=(n_sims,n_sensors,n_field_comps,n_time_steps)")
100
+ print(f"{exp_stats[1].max.shape=}")
101
+ print(80*"=")
102
+
103
+ #===========================================================================
104
+ # VISUALISE RESULTS
105
+ (fig,ax) = pyv.plot_exp_traces(exp_sim,
106
+ component="temperature",
107
+ sens_array_num=0,
108
+ sim_num=0)
109
+
110
+ (fig,ax) = pyv.plot_exp_traces(exp_sim,
111
+ component="strain_yy",
112
+ sens_array_num=1,
113
+ sim_num=2)
114
+ plt.show()
115
+
116
+
117
+ if __name__ == "__main__":
118
+ main()
@@ -0,0 +1,158 @@
1
+ '''
2
+ ================================================================================
3
+ Example: thermo-mechanical multiphysics on a divertor armour heatsink
4
+
5
+ pyvale: the python validation engine
6
+ License: MIT
7
+ Copyright (C) 2025 The Computer Aided Validation Team
8
+ ================================================================================
9
+ '''
10
+ from pathlib import Path
11
+ import numpy as np
12
+ import matplotlib.pyplot as plt
13
+ import mooseherder as mh
14
+ import pyvale as pyv
15
+
16
+ def main() -> None:
17
+ """pyvale example: 3D thermo-mechanical with thermocouples & strain gauges
18
+ ----------------------------------------------------------------------------
19
+ """
20
+ # Load Simulations as mooseherder.SimData objects
21
+ sim_path = pyv.DataSet.thermomechanical_3d_path()
22
+ sim_data = mh.ExodusReader(sim_path).read_all_sim_data()
23
+
24
+ # Scale to mm to make 3D visualisation scaling easier
25
+ sim_data.coords = sim_data.coords*1000.0
26
+ pyv.print_dimensions(sim_data)
27
+
28
+ sim_list = [sim_data]
29
+
30
+ use_sim_time = True
31
+ if use_sim_time:
32
+ sample_times = None
33
+ else:
34
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
35
+
36
+ save_tag = "thermomech3d"
37
+ fig_save_path = Path.cwd()/"images"
38
+ if not fig_save_path.is_dir():
39
+ fig_save_path.mkdir(parents=True, exist_ok=True)
40
+
41
+ #---------------------------------------------------------------------------
42
+ # Create the thermocouple array
43
+ x_lims = (12.5,12.5)
44
+ y_lims = (0.0,33.0)
45
+ z_lims = (0.0,12.0)
46
+ n_sens = (1,4,1)
47
+ tc_sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
48
+
49
+ tc_sens_data = pyv.SensorData(positions=tc_sens_pos,
50
+ sample_times=sample_times)
51
+
52
+ tc_field = "temperature"
53
+ tc_array = pyv.SensorArrayFactory \
54
+ .thermocouples_basic_errs(sim_data,
55
+ tc_sens_data,
56
+ tc_field,
57
+ spat_dims=3,
58
+ errs_pc=1.0)
59
+
60
+ # Visualise the thermocouple locations:
61
+ pv_plot = pyv.plot_point_sensors_on_sim(tc_array,"temperature")
62
+ pv_plot.camera_position = [(59.354, 43.428, 69.946),
63
+ (-2.858, 13.189, 4.523),
64
+ (-0.215, 0.948, -0.233)]
65
+ pv_plot.save_graphic(fig_save_path/(save_tag+"_tc_vis.svg"))
66
+ pv_plot.show()
67
+
68
+ #---------------------------------------------------------------------------
69
+ # Create the strain gauge array
70
+ x_lims = (9.4,9.4)
71
+ y_lims = (0.0,33.0)
72
+ z_lims = (12.0,12.0)
73
+ n_sens = (1,4,1)
74
+ sg_sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
75
+
76
+ sg_sens_data = pyv.SensorData(positions=sg_sens_pos,
77
+ sample_times=sample_times)
78
+
79
+
80
+ sg_field = "strain"
81
+ sg_array = pyv.SensorArrayFactory \
82
+ .strain_gauges_basic_errs(sim_data,
83
+ sg_sens_data,
84
+ sg_field,
85
+ spat_dims=3,
86
+ errs_pc=1.0)
87
+
88
+ # Visualise the strain gauge locations:
89
+ pv_plot = pyv.plot_point_sensors_on_sim(sg_array,"strain_yy")
90
+ pv_plot.camera_position = [(59.354, 43.428, 69.946),
91
+ (-2.858, 13.189, 4.523),
92
+ (-0.215, 0.948, -0.233)]
93
+ pv_plot.save_graphic(fig_save_path/(save_tag+"_sg_vis.svg"))
94
+ pv_plot.show()
95
+
96
+ #---------------------------------------------------------------------------
97
+ # Create and run the simulated experiment
98
+ sensor_arrays = [tc_array,sg_array]
99
+
100
+ exp_sim = pyv.ExperimentSimulator(sim_list,
101
+ sensor_arrays,
102
+ num_exp_per_sim=100)
103
+
104
+ exp_data = exp_sim.run_experiments()
105
+ exp_stats = exp_sim.calc_stats()
106
+
107
+ #---------------------------------------------------------------------------
108
+ print(80*"=")
109
+ print("exp_data and exp_stats are lists where the index is the sensor array")
110
+ print("position in the list as field components are not consistent dims:")
111
+ print(f"{len(exp_data)=}")
112
+ print(f"{len(exp_stats)=}")
113
+ print()
114
+
115
+ print(80*"-")
116
+ print("Thermal sensor array @ exp_data[0]")
117
+ print(80*"-")
118
+ print("shape=(n_sims,n_exps,n_sensors,n_field_comps,n_time_steps)")
119
+ print(f"{exp_data[0].shape=}")
120
+ print()
121
+ print("Stats are calculated over all experiments (axis=1)")
122
+ print("shape=(n_sims,n_sensors,n_field_comps,n_time_steps)")
123
+ print(f"{exp_stats[0].max.shape=}")
124
+ print()
125
+ print(80*"-")
126
+ print("Mechanical sensor array @ exp_data[1]")
127
+ print(80*"-")
128
+ print("shape=(n_sims,n_exps,n_sensors,n_field_comps,n_time_steps)")
129
+ print(f"{exp_data[1].shape=}")
130
+ print()
131
+ print("shape=(n_sims,n_sensors,n_field_comps,n_time_steps)")
132
+ print(f"{exp_stats[1].max.shape=}")
133
+ print(80*"=")
134
+
135
+ #---------------------------------------------------------------------------
136
+ # Visualise all sensor traces over all experiments
137
+ trace_opts = pyv.TraceOptsExperiment(plot_all_exp_points=True)
138
+
139
+ (fig,ax) = pyv.plot_exp_traces(exp_sim,
140
+ component="temperature",
141
+ sens_array_num=0,
142
+ sim_num=0,
143
+ trace_opts=trace_opts)
144
+ fig.savefig(fig_save_path/(save_tag+"_tc_traces.png"),
145
+ dpi=300, format='png', bbox_inches='tight')
146
+
147
+ (fig,ax) = pyv.plot_exp_traces(exp_sim,
148
+ component="strain_yy",
149
+ sens_array_num=1,
150
+ sim_num=0,
151
+ trace_opts=trace_opts)
152
+ fig.savefig(fig_save_path/(save_tag+"_sg_traces.png"),
153
+ dpi=300, format='png', bbox_inches='tight')
154
+ plt.show()
155
+
156
+
157
+ if __name__ == "__main__":
158
+ main()
@@ -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,83 @@
1
+ '''
2
+ ================================================================================
3
+ Example: 3d thermocouples on a monoblock
4
+
5
+ pyvale: the python validation engine
6
+ License: MIT
7
+ Copyright (C) 2025 The Computer Aided Validation Team
8
+ ================================================================================
9
+ '''
10
+ from pathlib import Path
11
+ import numpy as np
12
+ import mooseherder as mh
13
+ import pyvale
14
+
15
+
16
+ def main() -> None:
17
+ """pyvale example: visualisation tools 3D
18
+ """
19
+ # Use mooseherder to read the exodus and get a SimData object
20
+ data_path = pyvale.DataSet.thermal_3d_path()
21
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
22
+ field_name = 'temperature'
23
+ # Scale to mm to make 3D visualisation scaling easier
24
+ sim_data.coords = sim_data.coords*1000.0 # type: ignore
25
+
26
+ pyvale.print_dimensions(sim_data)
27
+
28
+ n_sens = (1,4,1)
29
+ x_lims = (12.5,12.5)
30
+ y_lims = (0,33.0)
31
+ z_lims = (0.0,12.0)
32
+ sens_pos = pyvale.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
33
+
34
+ sens_data = pyvale.SensorData(positions=sens_pos)
35
+
36
+ tc_array = pyvale.SensorArrayFactory() \
37
+ .thermocouples_basic_errs(sim_data,
38
+ sens_data,
39
+ field_name,
40
+ spat_dims=3)
41
+
42
+ measurements = tc_array.get_measurements()
43
+ print(f'\nMeasurements for sensor at top of block:\n{measurements[-1,0,:]}\n')
44
+
45
+ vis_opts = pyvale.VisOptsSimSensors()
46
+ vis_opts.window_size_px = (1200,800)
47
+ vis_opts.camera_position = np.array([(59.354, 43.428, 69.946),
48
+ (-2.858, 13.189, 4.523),
49
+ (-0.215, 0.948, -0.233)])
50
+
51
+ vis_mode = "vector"
52
+ save_dir = Path.cwd() / "exampleoutput"
53
+ if not save_dir.is_dir():
54
+ save_dir.mkdir()
55
+
56
+ if vis_mode == "animate":
57
+ anim_opts = pyvale.VisOptsAnimation()
58
+
59
+ anim_opts.save_path = save_dir / "test_animation"
60
+ anim_opts.save_animation = pyvale.EAnimationType.MP4
61
+
62
+ pv_anim = pyvale.animate_sim_with_sensors(tc_array,
63
+ field_name,
64
+ time_steps=None,
65
+ vis_opts=vis_opts,
66
+ anim_opts=anim_opts)
67
+
68
+ else:
69
+ image_save_opts = pyvale.VisOptsImageSave()
70
+
71
+ image_save_opts.path = save_dir / "test_vector_graphics"
72
+ image_save_opts.image_type = pyvale.EImageType.SVG
73
+
74
+ pv_plot = pyvale.plot_point_sensors_on_sim(tc_array,
75
+ field_name,
76
+ time_step=-1,
77
+ vis_opts=vis_opts,
78
+ image_save_opts=image_save_opts)
79
+ pv_plot.show()
80
+
81
+
82
+ if __name__ == '__main__':
83
+ main()
@@ -0,0 +1,89 @@
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: thermocouples on a 2d plate
18
+ ----------------------------------------------------------------------------
19
+ - Demonstrates area averaging for truth and for systematic errors
20
+ """
21
+ data_path = pyvale.DataSet.thermal_2d_path()
22
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
23
+ # Scale to mm to make 3D visualisation scaling easier
24
+ sim_data.coords = sim_data.coords*1000.0 # type: ignore
25
+
26
+ descriptor = pyvale.SensorDescriptorFactory.temperature_descriptor()
27
+
28
+ field_key = "temperature"
29
+ t_field = pyvale.FieldScalar(sim_data,
30
+ field_key=field_key,
31
+ spat_dims=2)
32
+
33
+ n_sens = (4,1,1)
34
+ x_lims = (0.0,100.0)
35
+ y_lims = (0.0,50.0)
36
+ z_lims = (0.0,0.0)
37
+ sens_pos = pyvale.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
38
+
39
+ use_sim_time = True
40
+ if use_sim_time:
41
+ sample_times = None
42
+ else:
43
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
44
+
45
+ sensor_dims = np.array([10.0,10.0,0])
46
+ sensor_data = pyvale.SensorData(positions=sens_pos,
47
+ sample_times=sample_times,
48
+ spatial_averager=pyvale.EIntSpatialType.QUAD4PT,
49
+ spatial_dims=sensor_dims)
50
+
51
+ tc_array = pyvale.SensorArrayPoint(sensor_data,
52
+ t_field,
53
+ descriptor)
54
+
55
+ area_avg_err_data = pyvale.ErrFieldData(
56
+ spatial_averager=pyvale.EIntSpatialType.RECT1PT,
57
+ spatial_dims=sensor_dims
58
+ )
59
+ err_chain = []
60
+ err_chain.append(pyvale.ErrSysField(t_field,
61
+ area_avg_err_data))
62
+ error_int = pyvale.ErrIntegrator(err_chain,
63
+ sensor_data,
64
+ tc_array.get_measurement_shape())
65
+ tc_array.set_error_integrator(error_int)
66
+
67
+ measurements = tc_array.get_measurements()
68
+
69
+ print("\n"+80*"-")
70
+ print("For a sensor: measurement = truth + sysematic error + random error")
71
+ print(f"measurements.shape = {measurements.shape} = "+
72
+ "(n_sensors,n_field_components,n_timesteps)\n")
73
+ print("The truth, systematic error and random error arrays have the same "+
74
+ "shape.")
75
+
76
+ print(80*"-")
77
+ print("Looking at the last 5 time steps (measurements) of sensor 0:")
78
+ pyvale.print_measurements(tc_array,
79
+ (0,1),
80
+ (0,1),
81
+ (0,10))
82
+ print(80*"-")
83
+
84
+ pyvale.plot_time_traces(tc_array,field_key)
85
+ plt.show()
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()