pyvale 2025.5.3__cp311-cp311-macosx_14_0_arm64.whl → 2025.7.1__cp311-cp311-macosx_14_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyvale might be problematic. Click here for more details.
- pyvale/.dylibs/libomp.dylib +0 -0
- pyvale/.dylibs/libunwind.1.0.dylib +0 -0
- pyvale/__init__.py +12 -0
- pyvale/blendercalibrationdata.py +3 -1
- pyvale/blenderscene.py +7 -5
- pyvale/blendertools.py +27 -5
- pyvale/camera.py +1 -0
- pyvale/cameradata.py +3 -0
- pyvale/camerasensor.py +147 -0
- pyvale/camerastereo.py +4 -4
- pyvale/cameratools.py +23 -61
- pyvale/cython/rastercyth.c +1657 -1352
- pyvale/cython/rastercyth.cpython-311-darwin.so +0 -0
- pyvale/cython/rastercyth.py +71 -26
- pyvale/data/DIC_Challenge_Star_Noise_Def.tiff +0 -0
- pyvale/data/DIC_Challenge_Star_Noise_Ref.tiff +0 -0
- pyvale/data/plate_hole_def0000.tiff +0 -0
- pyvale/data/plate_hole_def0001.tiff +0 -0
- pyvale/data/plate_hole_ref0000.tiff +0 -0
- pyvale/data/plate_rigid_def0000.tiff +0 -0
- pyvale/data/plate_rigid_def0001.tiff +0 -0
- pyvale/data/plate_rigid_ref0000.tiff +0 -0
- pyvale/dataset.py +96 -6
- pyvale/dic/cpp/dicbruteforce.cpp +370 -0
- pyvale/dic/cpp/dicfourier.cpp +648 -0
- pyvale/dic/cpp/dicinterpolator.cpp +559 -0
- pyvale/dic/cpp/dicmain.cpp +215 -0
- pyvale/dic/cpp/dicoptimizer.cpp +675 -0
- pyvale/dic/cpp/dicrg.cpp +137 -0
- pyvale/dic/cpp/dicscanmethod.cpp +677 -0
- pyvale/dic/cpp/dicsmooth.cpp +138 -0
- pyvale/dic/cpp/dicstrain.cpp +383 -0
- pyvale/dic/cpp/dicutil.cpp +563 -0
- pyvale/dic2d.py +164 -0
- pyvale/dic2dcpp.cpython-311-darwin.so +0 -0
- pyvale/dicchecks.py +476 -0
- pyvale/dicdataimport.py +247 -0
- pyvale/dicregionofinterest.py +887 -0
- pyvale/dicresults.py +55 -0
- pyvale/dicspecklegenerator.py +238 -0
- pyvale/dicspecklequality.py +305 -0
- pyvale/dicstrain.py +387 -0
- pyvale/dicstrainresults.py +37 -0
- pyvale/errorintegrator.py +10 -8
- pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +124 -113
- pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +124 -132
- pyvale/examples/basics/ex1_3_customsens_therm3d.py +199 -195
- pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +125 -121
- pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +145 -141
- pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +96 -101
- pyvale/examples/basics/ex1_7_spatavg_therm2d.py +109 -105
- pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +92 -91
- pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +96 -90
- pyvale/examples/basics/ex2_3_sensangle_disp2d.py +88 -89
- pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +172 -171
- pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +88 -86
- pyvale/examples/basics/ex3_1_basictensors_strain2d.py +90 -90
- pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +93 -91
- pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +172 -160
- pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +154 -148
- pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +249 -231
- pyvale/examples/dic/ex1_region_of_interest.py +98 -0
- pyvale/examples/dic/ex2_plate_with_hole.py +149 -0
- pyvale/examples/dic/ex3_plate_with_hole_strain.py +93 -0
- pyvale/examples/dic/ex4_dic_blender.py +95 -0
- pyvale/examples/dic/ex5_dic_challenge.py +102 -0
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +4 -2
- pyvale/examples/renderblender/ex1_1_blenderscene.py +152 -105
- pyvale/examples/renderblender/ex1_2_blenderdeformed.py +151 -100
- pyvale/examples/renderblender/ex2_1_stereoscene.py +183 -116
- pyvale/examples/renderblender/ex2_2_stereodeformed.py +185 -112
- pyvale/examples/renderblender/ex3_1_blendercalibration.py +164 -109
- pyvale/examples/renderrasterisation/ex_rastenp.py +74 -35
- pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +6 -13
- pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +2 -2
- pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +2 -4
- pyvale/imagedef2d.py +3 -2
- pyvale/imagetools.py +137 -0
- pyvale/rastercy.py +34 -4
- pyvale/rasternp.py +300 -276
- pyvale/rasteropts.py +58 -0
- pyvale/renderer.py +47 -0
- pyvale/rendermesh.py +52 -62
- pyvale/renderscene.py +51 -0
- pyvale/sensorarrayfactory.py +2 -2
- pyvale/sensortools.py +19 -35
- pyvale/simcases/case21.i +1 -1
- pyvale/simcases/run_1case.py +8 -0
- pyvale/simtools.py +2 -2
- pyvale/visualsimplotter.py +180 -0
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/METADATA +11 -57
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/RECORD +95 -57
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/WHEEL +1 -1
- pyvale/examples/visualisation/ex1_1_plot_traces.py +0 -102
- pyvale/examples/visualisation/ex2_1_animate_sim.py +0 -89
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/licenses/LICENSE +0 -0
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/top_level.txt +0 -0
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
5
|
# ==============================================================================
|
|
6
6
|
|
|
7
|
-
"""
|
|
8
|
-
|
|
7
|
+
"""Basics: Multi-physics experiment simulation in 3D
|
|
8
|
+
================================================================================
|
|
9
|
+
|
|
9
10
|
In the previous example we performed a series of simulated experiments on a set
|
|
10
11
|
of 2D multi-physics simulations. Here we use a 3D thermo-mechanical analysis of
|
|
11
12
|
a divertor armour heatsink to show how we can run simulated experiments in 3D.
|
|
12
13
|
|
|
13
|
-
Note that this tutorial assumes you are familiar with the use of pyvale for
|
|
14
|
+
Note that this tutorial assumes you are familiar with the use of `pyvale` for
|
|
14
15
|
scalar and tensor fields as described in the previous examples.
|
|
15
16
|
|
|
16
17
|
Test case: thermo-mechanical analysis of a divertor heatsink in 3D
|
|
@@ -22,231 +23,248 @@ import matplotlib.pyplot as plt
|
|
|
22
23
|
import mooseherder as mh
|
|
23
24
|
import pyvale as pyv
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
26
|
+
#%%
|
|
27
|
+
# First we get the path to simulation output file and then read the
|
|
28
|
+
# simulation into a `SimData` object. In this case our simulation is a
|
|
29
|
+
# thermomechanical model of a divertor heatsink.
|
|
30
|
+
sim_path = pyv.DataSet.thermomechanical_3d_path()
|
|
31
|
+
sim_data = mh.ExodusReader(sim_path).read_all_sim_data()
|
|
32
|
+
elem_dims: int = 3
|
|
33
|
+
|
|
34
|
+
#%%
|
|
35
|
+
# We scale our length and displacement units to mm to help with
|
|
36
|
+
# visualisation.
|
|
37
|
+
disp_comps = ("disp_x","disp_y","disp_z")
|
|
38
|
+
sim_data = pyv.scale_length_units(scale=1000.0,
|
|
39
|
+
sim_data=sim_data,
|
|
40
|
+
disp_comps=disp_comps)
|
|
41
|
+
|
|
42
|
+
#%%
|
|
43
|
+
# If we are going to save figures showing where our sensors are and their
|
|
44
|
+
# simulated traces we need to create a directory. Set the flag below to
|
|
45
|
+
# save the figures when you run the script
|
|
46
|
+
save_figs = False
|
|
47
|
+
save_tag = "thermomech3d"
|
|
48
|
+
fig_save_path = Path.cwd()/"images"
|
|
49
|
+
if not fig_save_path.is_dir():
|
|
50
|
+
fig_save_path.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
|
|
52
|
+
#%%
|
|
53
|
+
# We specify manual sensor sampling times but we could also set this to None
|
|
54
|
+
# for the sensors to sample at the simulation time steps.
|
|
55
|
+
sample_times = np.linspace(0.0,np.max(sim_data.time),50)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
x_lims = (12.5,12.5)
|
|
59
|
+
y_lims = (0.0,33.0)
|
|
60
|
+
z_lims = (0.0,12.0)
|
|
61
|
+
n_sens = (1,4,1)
|
|
62
|
+
tc_sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
|
|
63
|
+
|
|
64
|
+
tc_sens_data = pyv.SensorData(positions=tc_sens_pos,
|
|
65
|
+
sample_times=sample_times)
|
|
66
|
+
#%%
|
|
67
|
+
# We use the sensor array factory to create our thermocouple array with no
|
|
68
|
+
# errors.
|
|
69
|
+
tc_field_name = "temperature"
|
|
70
|
+
tc_array = pyv.SensorArrayFactory \
|
|
71
|
+
.thermocouples_no_errs(sim_data,
|
|
72
|
+
tc_sens_data,
|
|
73
|
+
elem_dims=elem_dims,
|
|
74
|
+
field_name=tc_field_name)
|
|
75
|
+
#%%
|
|
76
|
+
# Now we build our error chain starting with some basic errors on the order
|
|
77
|
+
# of 1 degree.
|
|
78
|
+
tc_err_chain = []
|
|
79
|
+
tc_err_chain.append(pyv.ErrSysUnif(low=1.0,high=1.0))
|
|
80
|
+
tc_err_chain.append(pyv.ErrRandNorm(std=1.0))
|
|
81
|
+
|
|
82
|
+
#%%
|
|
83
|
+
# Now we add positioning error for our thermocouples.
|
|
84
|
+
tc_pos_uncert = 0.1 # units = mm
|
|
85
|
+
tc_pos_rand = (pyv.GenNormal(std=tc_pos_uncert),
|
|
86
|
+
pyv.GenNormal(std=tc_pos_uncert),
|
|
87
|
+
pyv.GenNormal(std=tc_pos_uncert))
|
|
88
|
+
|
|
89
|
+
#%%
|
|
90
|
+
# We block translation in x so the thermocouples stay attached.
|
|
91
|
+
tc_pos_lock = np.full(tc_sens_pos.shape,False,dtype=bool)
|
|
92
|
+
tc_pos_lock[:,0] = True
|
|
93
|
+
|
|
94
|
+
tc_field_err_data = pyv.ErrFieldData(pos_rand_xyz=tc_pos_rand,
|
|
95
|
+
pos_lock_xyz=tc_pos_lock)
|
|
96
|
+
tc_err_chain.append(pyv.ErrSysField(tc_array.get_field(),
|
|
97
|
+
|
|
98
|
+
tc_field_err_data))
|
|
99
|
+
#%%
|
|
100
|
+
# We have finished our error chain so we can build our error integrator and
|
|
101
|
+
# attach it to our thermocouple array.
|
|
102
|
+
tc_error_int = pyv.ErrIntegrator(tc_err_chain,
|
|
103
|
+
tc_sens_data,
|
|
104
|
+
tc_array.get_measurement_shape())
|
|
105
|
+
tc_array.set_error_integrator(tc_error_int)
|
|
106
|
+
|
|
107
|
+
#%%
|
|
108
|
+
# We visualise our thermcouple locations on our mesh to make sure they are
|
|
109
|
+
# in the correct positions.
|
|
110
|
+
pv_plot = pyv.plot_point_sensors_on_sim(tc_array,"temperature")
|
|
111
|
+
pv_plot.camera_position = [(59.354, 43.428, 69.946),
|
|
112
|
+
(-2.858, 13.189, 4.523),
|
|
113
|
+
(-0.215, 0.948, -0.233)]
|
|
114
|
+
if save_figs:
|
|
115
|
+
pv_plot.save_graphic(fig_save_path/(save_tag+"_tc_vis.svg"))
|
|
116
|
+
pv_plot.screenshot(fig_save_path/(save_tag+"_tc_vis.png"))
|
|
117
|
+
|
|
118
|
+
pv_plot.show()
|
|
119
|
+
|
|
120
|
+
#%%
|
|
121
|
+
# Now we have finished with our thermocouple array we can move on to our
|
|
122
|
+
# strain gauge array.
|
|
123
|
+
#
|
|
124
|
+
# We use the same sampling time but we are going to place the strain gauges
|
|
125
|
+
# down the side of the monoblock where the pipe passes through.
|
|
126
|
+
x_lims = (9.4,9.4)
|
|
127
|
+
y_lims = (0.0,33.0)
|
|
128
|
+
z_lims = (12.0,12.0)
|
|
129
|
+
n_sens = (1,4,1)
|
|
130
|
+
sg_sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
|
|
131
|
+
|
|
132
|
+
sg_sens_data = pyv.SensorData(positions=sg_sens_pos,
|
|
133
|
+
sample_times=sample_times)
|
|
134
|
+
|
|
135
|
+
#%%
|
|
136
|
+
# We use the sensor array factory to give us a strain gauge array with no
|
|
137
|
+
# errors.
|
|
138
|
+
sg_field_name = "strain"
|
|
139
|
+
sg_norm_comps = ("strain_xx","strain_yy","strain_zz")
|
|
140
|
+
sg_dev_comps = ("strain_xy","strain_yz","strain_xz")
|
|
141
|
+
sg_array = pyv.SensorArrayFactory \
|
|
142
|
+
.strain_gauges_no_errs(sim_data,
|
|
143
|
+
sg_sens_data,
|
|
144
|
+
elem_dims=elem_dims,
|
|
145
|
+
field_name=sg_field_name,
|
|
146
|
+
norm_comps=sg_norm_comps,
|
|
147
|
+
dev_comps=sg_dev_comps)
|
|
148
|
+
|
|
149
|
+
#%%
|
|
150
|
+
# Now we build our error chain starting with some basic errors on the order
|
|
151
|
+
# of 1 percent.
|
|
152
|
+
sg_err_chain = []
|
|
153
|
+
sg_err_chain.append(pyv.ErrSysUnifPercent(low_percent=1.0,high_percent=1.0))
|
|
154
|
+
sg_err_chain.append(pyv.ErrRandNormPercent(std_percent=1.0))
|
|
155
|
+
|
|
156
|
+
#%%
|
|
157
|
+
# We are going to add +/-2 degree rotation uncertainty to our strain gauges.
|
|
158
|
+
angle_uncert = 2.0
|
|
159
|
+
angle_rand_zyx = (pyv.GenUniform(low=-angle_uncert,high=angle_uncert), # units = deg
|
|
160
|
+
pyv.GenUniform(low=-angle_uncert,high=angle_uncert),
|
|
161
|
+
pyv.GenUniform(low=-angle_uncert,high=angle_uncert))
|
|
162
|
+
|
|
163
|
+
#%%
|
|
164
|
+
# We only allow rotation on the face the strain gauges are on
|
|
165
|
+
angle_lock = np.full(sg_sens_pos.shape,True,dtype=bool)
|
|
166
|
+
angle_lock[:,0] = False # Allow rotation about z
|
|
167
|
+
|
|
168
|
+
sg_field_err_data = pyv.ErrFieldData(ang_rand_zyx=angle_rand_zyx,
|
|
169
|
+
ang_lock_zyx=angle_lock)
|
|
170
|
+
sg_err_chain.append(pyv.ErrSysField(sg_array.get_field(),
|
|
171
|
+
sg_field_err_data))
|
|
172
|
+
|
|
173
|
+
#%%
|
|
174
|
+
# We have finished our error chain so we can build our error integrator and
|
|
175
|
+
# attach it to our thermocouple array.
|
|
176
|
+
sg_error_int = pyv.ErrIntegrator(sg_err_chain,
|
|
177
|
+
sg_sens_data,
|
|
178
|
+
sg_array.get_measurement_shape())
|
|
179
|
+
sg_array.set_error_integrator(sg_error_int)
|
|
180
|
+
|
|
181
|
+
#%%
|
|
182
|
+
# Now we visualise the strain gauge locations to make sure they are where
|
|
183
|
+
# we expect them to be.
|
|
184
|
+
pv_plot = pyv.plot_point_sensors_on_sim(sg_array,"strain_yy")
|
|
185
|
+
pv_plot.camera_position = [(59.354, 43.428, 69.946),
|
|
186
|
+
(-2.858, 13.189, 4.523),
|
|
187
|
+
(-0.215, 0.948, -0.233)]
|
|
188
|
+
if save_figs:
|
|
189
|
+
pv_plot.save_graphic(fig_save_path/(save_tag+"_sg_vis.svg"))
|
|
190
|
+
pv_plot.screenshot(fig_save_path/(save_tag+"_sg_vis.png"))
|
|
191
|
+
|
|
192
|
+
pv_plot.show()
|
|
193
|
+
|
|
194
|
+
#%%
|
|
195
|
+
# We have both our sensor arrays so we will create and run our experiment.
|
|
196
|
+
# Here we only have a single input simulation in our list and we only run
|
|
197
|
+
# 100 simulated experiments as we are going to plot all simulated data
|
|
198
|
+
# points on our traces. Note that if you are running more than 100
|
|
199
|
+
# experiments here you will need to set the trace plots below to not show
|
|
200
|
+
# all points on the graph.
|
|
201
|
+
sim_list = [sim_data,]
|
|
202
|
+
sensor_arrays = [tc_array,sg_array]
|
|
203
|
+
exp_sim = pyv.ExperimentSimulator(sim_list,
|
|
204
|
+
sensor_arrays,
|
|
205
|
+
num_exp_per_sim=100)
|
|
206
|
+
|
|
207
|
+
#%%
|
|
208
|
+
# We run our experiments and calculate summary statistics as in the previous
|
|
209
|
+
# example
|
|
210
|
+
exp_data = exp_sim.run_experiments()
|
|
211
|
+
exp_stats = exp_sim.calc_stats()
|
|
212
|
+
|
|
213
|
+
#%%
|
|
214
|
+
# We print the lengths of our exp_data and exp_stats lists along with the
|
|
215
|
+
# shape of the numpy arrays they contain so we can index into them easily.
|
|
216
|
+
print(80*"=")
|
|
217
|
+
print("exp_data and exp_stats are lists where the index is the sensor array")
|
|
218
|
+
print("position in the list as field components are not consistent dims:")
|
|
219
|
+
print(f"{len(exp_data)=}")
|
|
220
|
+
print(f"{len(exp_stats)=}")
|
|
221
|
+
print()
|
|
222
|
+
print(80*"-")
|
|
223
|
+
print("Thermal sensor array @ exp_data[0]")
|
|
224
|
+
print(80*"-")
|
|
225
|
+
print("shape=(n_sims,n_exps,n_sensors,n_field_comps,n_time_steps)")
|
|
226
|
+
print(f"{exp_data[0].shape=}")
|
|
227
|
+
print()
|
|
228
|
+
print("Stats are calculated over all experiments (axis=1)")
|
|
229
|
+
print("shape=(n_sims,n_sensors,n_field_comps,n_time_steps)")
|
|
230
|
+
print(f"{exp_stats[0].max.shape=}")
|
|
231
|
+
print()
|
|
232
|
+
print(80*"-")
|
|
233
|
+
print("Mechanical sensor array @ exp_data[1]")
|
|
234
|
+
print(80*"-")
|
|
235
|
+
print("shape=(n_sims,n_exps,n_sensors,n_field_comps,n_time_steps)")
|
|
236
|
+
print(f"{exp_data[1].shape=}")
|
|
237
|
+
print()
|
|
238
|
+
print("shape=(n_sims,n_sensors,n_field_comps,n_time_steps)")
|
|
239
|
+
print(f"{exp_stats[1].max.shape=}")
|
|
240
|
+
print(80*"=")
|
|
241
|
+
|
|
242
|
+
#%%
|
|
243
|
+
# Finally, we are going to plot the simulated sensor traces but we are going
|
|
244
|
+
# to control some of the plotting options using the options data class here.
|
|
245
|
+
# We set the plot to show all simulated experiment data points and to plot
|
|
246
|
+
# the median as the centre line and to fill between the min and max values.
|
|
247
|
+
# Note that the default here is to plot the mean and fill between 3 times
|
|
248
|
+
# the standard deviation.
|
|
249
|
+
trace_opts = pyv.TraceOptsExperiment(plot_all_exp_points=True,
|
|
250
|
+
centre=pyv.EExpVisCentre.MEDIAN,
|
|
251
|
+
fill_between=pyv.EExpVisBounds.MINMAX)
|
|
252
|
+
|
|
253
|
+
(fig,ax) = pyv.plot_exp_traces(exp_sim,
|
|
254
|
+
component="temperature",
|
|
255
|
+
sens_array_num=0,
|
|
256
|
+
sim_num=0,
|
|
257
|
+
trace_opts=trace_opts)
|
|
258
|
+
if save_figs:
|
|
259
|
+
fig.savefig(fig_save_path/(save_tag+"_tc_traces.png"),
|
|
260
|
+
dpi=300, format='png', bbox_inches='tight')
|
|
261
|
+
|
|
262
|
+
(fig,ax) = pyv.plot_exp_traces(exp_sim,
|
|
263
|
+
component="strain_yy",
|
|
264
|
+
sens_array_num=1,
|
|
265
|
+
sim_num=0,
|
|
266
|
+
trace_opts=trace_opts)
|
|
267
|
+
if save_figs:
|
|
268
|
+
fig.savefig(fig_save_path/(save_tag+"_sg_traces.png"),
|
|
269
|
+
dpi=300, format='png', bbox_inches='tight')
|
|
270
|
+
plt.show()
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#================================================================================
|
|
2
|
+
#Example: thermocouples on a 2d plate
|
|
3
|
+
#
|
|
4
|
+
#pyvale: the python validation engine
|
|
5
|
+
#License: MIT
|
|
6
|
+
#Copyright (C) 2024 The Computer Aided Validation Team
|
|
7
|
+
#================================================================================
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
Selecting a Region of Interest (ROI)
|
|
11
|
+
---------------------------------------------
|
|
12
|
+
|
|
13
|
+
This example looks at the current core functionality of the Region of Interest
|
|
14
|
+
(ROI) Selection Firsly we'll need to import `pyvale` itself.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
import pyvale
|
|
19
|
+
|
|
20
|
+
# %%
|
|
21
|
+
# We'll begin by selecting our Region of Interest (ROI) using the interactive selection tool.
|
|
22
|
+
# First, we create an instance of the ROI class. We pass a reference image to it, which is
|
|
23
|
+
# displayed as the underlay during ROI selection.
|
|
24
|
+
ref_img = pyvale.DataSet.dic_plate_with_hole_ref()
|
|
25
|
+
roi = pyvale.DICRegionOfInterest(ref_image=ref_img)
|
|
26
|
+
roi.interactive_selection(subset_size=31)
|
|
27
|
+
|
|
28
|
+
# create a directory for the the different outputs
|
|
29
|
+
output_path = Path.cwd() / "pyvale-output"
|
|
30
|
+
if not output_path.is_dir():
|
|
31
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
# %%
|
|
34
|
+
# .. image:: ../../../../_static/roi_tool.gif
|
|
35
|
+
# :alt: ROI selection GUI (animated)
|
|
36
|
+
# :width: 600px
|
|
37
|
+
# :align: center
|
|
38
|
+
|
|
39
|
+
# %%
|
|
40
|
+
# After closing the interactive tool, a mask and a set of seed coordinates will be generated.
|
|
41
|
+
# These can be used directly in the DIC engine. If you plan to reuse the ROI, it’s a good idea
|
|
42
|
+
# to save it. For very large images, set `binary=True` to reduce file size and speed up saving.
|
|
43
|
+
roi_file = output_path / "roi.dat"
|
|
44
|
+
roi.save_array(filename=roi_file,binary=False)
|
|
45
|
+
|
|
46
|
+
# %%
|
|
47
|
+
# To reuse the saved ROI mask in the future, load it using:
|
|
48
|
+
roi.read_array(filename=roi_file,binary=False)
|
|
49
|
+
|
|
50
|
+
# %%
|
|
51
|
+
# If you are loading a previously saved ROI, you may want to visualize it
|
|
52
|
+
# overlaid on the reference image to verify it before proceeding with correlation.
|
|
53
|
+
roi.show_image()
|
|
54
|
+
|
|
55
|
+
# %%
|
|
56
|
+
# There are also programmatic ways to define an ROI.
|
|
57
|
+
# For example, to exclude a boundary region and keep only the central part:
|
|
58
|
+
roi.reset_mask()
|
|
59
|
+
roi.rect_boundary(left=50,right=50,bottom=50,top=50)
|
|
60
|
+
boundary_img = output_path / "rect_boundary.tiff"
|
|
61
|
+
roi.save_image(boundary_img)
|
|
62
|
+
|
|
63
|
+
# %%
|
|
64
|
+
# This excludes 50 pixels along each edge of the image from the ROI.
|
|
65
|
+
# Alternatively, to define a specific rectangular region:
|
|
66
|
+
roi.reset_mask()
|
|
67
|
+
roi.rect_region(x=200,y=200,size_x=200,size_y=200)
|
|
68
|
+
region_img = output_path / "rect_region.tiff"
|
|
69
|
+
roi.save_image(region_img)
|
|
70
|
+
|
|
71
|
+
# %%
|
|
72
|
+
# .. list-table::
|
|
73
|
+
# :widths: 50 50
|
|
74
|
+
# :align: center
|
|
75
|
+
# :header-rows: 0
|
|
76
|
+
#
|
|
77
|
+
# * - .. figure:: ../../../../_static/rect_boundary.png
|
|
78
|
+
# :width: 300px
|
|
79
|
+
# :align: center
|
|
80
|
+
#
|
|
81
|
+
# ``roi.rect_boundary(left=200, right=200, bottom=200, top=200)``
|
|
82
|
+
#
|
|
83
|
+
# - .. figure:: ../../../../_static/rect_region.png
|
|
84
|
+
# :width: 300px
|
|
85
|
+
# :align: center
|
|
86
|
+
#
|
|
87
|
+
# ``roi.rect_region(x=200, y=200, size_x=200, size_y=200)``
|
|
88
|
+
|
|
89
|
+
# %%
|
|
90
|
+
# The `rect_region` example above creates an ROI starting at pixel coordinates (200, 200)
|
|
91
|
+
# with a size of 200×200 pixels.
|
|
92
|
+
#
|
|
93
|
+
# You can also manually modify the ROI mask. A good starting point is:
|
|
94
|
+
# `roi.rect_boundary(0, 0, 0, 0)` — this sets the ROI to include the full image.
|
|
95
|
+
# From there, you can manipulate `roi.mask` as you would any other 2D NumPy array.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|