pyvale 2025.5.3__cp311-cp311-musllinux_1_2_aarch64.whl → 2025.7.1__cp311-cp311-musllinux_1_2_aarch64.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/__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-aarch64-linux-musl.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-aarch64-linux-musl.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 +96 -56
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/WHEEL +1 -1
- pyvale.libs/libgcc_s-69c45f16.so.1 +0 -0
- pyvale.libs/libgomp-b626072d.so.1.0.0 +0 -0
- pyvale.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- 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
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Basics: Custom tensor field sensors (strain gauges) in 3D
|
|
9
|
+
================================================================================
|
|
10
|
+
|
|
10
11
|
In this example we build a custom tensor field sensor array (i.e. a strain gauge
|
|
11
12
|
array) in 3D. We will also demonstrate how to specify sensor angles and field
|
|
12
13
|
errors based on sensor angles.
|
|
13
14
|
|
|
14
|
-
Note that this tutorial assumes you are familiar with the use of pyvale for
|
|
15
|
+
Note that this tutorial assumes you are familiar with the use of `pyvale` for
|
|
15
16
|
scalar fields as described in the first set of examples.
|
|
16
17
|
|
|
17
18
|
Test case: point strain sensors on a 2D plate with hole loaded in tension
|
|
@@ -23,160 +24,171 @@ from scipy.spatial.transform import Rotation
|
|
|
23
24
|
import mooseherder as mh
|
|
24
25
|
import pyvale as pyv
|
|
25
26
|
|
|
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
|
-
|
|
27
|
+
#%%
|
|
28
|
+
# First we load our simulation asa `SimData` object. In this case we are
|
|
29
|
+
# loading a 10mm cube loaded in tension in the y direction with the addition
|
|
30
|
+
# of a thermal gradient in the y direction.
|
|
31
|
+
data_path = pyv.DataSet.element_case_path(pyv.EElemTest.HEX20)
|
|
32
|
+
sim_data = mh.ExodusReader(data_path).read_all_sim_data()
|
|
33
|
+
|
|
34
|
+
#%%
|
|
35
|
+
# As we are creating a 3D tensor field sensor we now have a third
|
|
36
|
+
# displacement field component here for scaling. Note that you don't need to
|
|
37
|
+
# scale the displacements here if you only want to analyse strains.
|
|
38
|
+
disp_comps = ("disp_x","disp_y","disp_z")
|
|
39
|
+
sim_data = pyv.scale_length_units(scale=1000.0,
|
|
40
|
+
sim_data=sim_data,
|
|
41
|
+
disp_comps=disp_comps)
|
|
42
|
+
|
|
43
|
+
#%%
|
|
44
|
+
# Here is the main difference when creating a tensor field sensor array. We
|
|
45
|
+
# create a tensor field where we need to specify the normal and deviatoric
|
|
46
|
+
# component string keys as they appear in our `SimData` object. We have a 3D
|
|
47
|
+
# simulation here so we have 3 normal components and 3 deviatoric (shear).
|
|
48
|
+
field_name = "strain"
|
|
49
|
+
norm_comps = ("strain_xx","strain_yy","strain_zz")
|
|
50
|
+
dev_comps = ("strain_xy","strain_yz","strain_xz")
|
|
51
|
+
strain_field = pyv.FieldTensor(sim_data,
|
|
52
|
+
field_name=field_name,
|
|
53
|
+
norm_comps=norm_comps,
|
|
54
|
+
dev_comps=dev_comps,
|
|
55
|
+
elem_dims=3)
|
|
56
|
+
|
|
57
|
+
#%%
|
|
58
|
+
# Here we manually define our sensor positions to place a sensor on the
|
|
59
|
+
# centre of each face of our 10mm cube. From here everything is the same as
|
|
60
|
+
# for our 2D vector field sensor arrays.
|
|
61
|
+
sensor_positions = np.array(((5.0,0.0,5.0), # bottom
|
|
62
|
+
(5.0,10.0,5.0), # top
|
|
63
|
+
(5.0,5.0,0.0), # xy face
|
|
64
|
+
(5.0,5.0,10.0), # xy face
|
|
65
|
+
(0.0,5.0,5.0), # yz face
|
|
66
|
+
(10.0,5.0,5.0),)) # yz face
|
|
67
|
+
|
|
68
|
+
#%%
|
|
69
|
+
# We set custom sensor sampling times here but we could also set this to
|
|
70
|
+
# None to have the sensors sample at the simulation time steps.
|
|
71
|
+
sample_times = np.linspace(0.0,np.max(sim_data.time),50)
|
|
72
|
+
|
|
73
|
+
#%%
|
|
74
|
+
# We are going to manually specify the sensor angles for all our sensors.
|
|
75
|
+
sens_angles = (Rotation.from_euler("zyx", [0, 0, 0], degrees=True),
|
|
76
|
+
Rotation.from_euler("zyx", [0, 0, 0], degrees=True),
|
|
77
|
+
Rotation.from_euler("zyx", [45, 0, 0], degrees=True),
|
|
78
|
+
Rotation.from_euler("zyx", [45, 0, 0], degrees=True),
|
|
79
|
+
Rotation.from_euler("zyx", [0, 0, 45], degrees=True),
|
|
80
|
+
Rotation.from_euler("zyx", [0, 0, 45], degrees=True),)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
sens_data = pyv.SensorData(positions=sensor_positions,
|
|
84
|
+
sample_times=sample_times,
|
|
85
|
+
angles=sens_angles)
|
|
86
|
+
|
|
87
|
+
#%%
|
|
88
|
+
# Here we create a descriptor that will be used to label visualisations of
|
|
89
|
+
# the sensor locations and time traces for our sensors. For the strain
|
|
90
|
+
# gauges we are modelling here we could also use the descriptor factory to
|
|
91
|
+
# get these defaults.
|
|
92
|
+
descriptor = pyv.SensorDescriptor(name="Strain",
|
|
93
|
+
symbol=r"\varepsilon",
|
|
94
|
+
units=r"-",
|
|
95
|
+
tag="SG",
|
|
96
|
+
components=('xx','yy','zz','xy','yz','xz'))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
straingauge_array = pyv.SensorArrayPoint(sens_data,
|
|
100
|
+
strain_field,
|
|
101
|
+
descriptor)
|
|
102
|
+
|
|
103
|
+
#%%
|
|
104
|
+
# We can add any errors we like to our error chain. Here we add some basic
|
|
105
|
+
# percentage errors.
|
|
106
|
+
error_chain = []
|
|
107
|
+
error_chain.append(pyv.ErrSysUnif(low=-0.1e-3,high=0.1e-3))
|
|
108
|
+
error_chain.append(pyv.ErrRandNormPercent(std_percent=1.0))
|
|
109
|
+
|
|
110
|
+
#%%
|
|
111
|
+
# Now we add a field error to perturb the positions of each sensor on its
|
|
112
|
+
# relevant face and then add a +/- 2deg angle error.
|
|
113
|
+
|
|
114
|
+
pos_uncert = 0.1 # units = mm
|
|
115
|
+
pos_rand_xyz = (pyv.GenNormal(std=pos_uncert),
|
|
116
|
+
pyv.GenNormal(std=pos_uncert),
|
|
117
|
+
pyv.GenNormal(std=pos_uncert))
|
|
118
|
+
|
|
119
|
+
angle_uncert = 2.0
|
|
120
|
+
angle_rand_zyx = (pyv.GenUniform(low=-angle_uncert,high=angle_uncert), # units = deg
|
|
121
|
+
pyv.GenUniform(low=-angle_uncert,high=angle_uncert),
|
|
122
|
+
pyv.GenUniform(low=-angle_uncert,high=angle_uncert))
|
|
123
|
+
|
|
124
|
+
#%%
|
|
125
|
+
# We are going to lock position perturbation so that the sensors stay on the
|
|
126
|
+
# faces of the cube they are positioned on.
|
|
127
|
+
pos_lock = np.full(sensor_positions.shape,False,dtype=bool)
|
|
128
|
+
pos_lock[0:2,1] = True # Block translation in y
|
|
129
|
+
pos_lock[2:4,2] = True # Block translation in z
|
|
130
|
+
pos_lock[4:6,0] = True # Block translation in x
|
|
131
|
+
|
|
132
|
+
#%%
|
|
133
|
+
# We are also going to lock angular perturbation so that each sensor is only
|
|
134
|
+
# allowed to rotate on the plane it is on.
|
|
135
|
+
angle_lock = np.full(sensor_positions.shape,True,dtype=bool)
|
|
136
|
+
angle_lock[0:2,1] = False # Allow rotation about y
|
|
137
|
+
angle_lock[2:4,0] = False # Allow rotation about z
|
|
138
|
+
angle_lock[4:6,2] = False # Allow rotation about x
|
|
139
|
+
|
|
140
|
+
field_error_data = pyv.ErrFieldData(pos_rand_xyz=pos_rand_xyz,
|
|
141
|
+
pos_lock_xyz=pos_lock,
|
|
142
|
+
ang_rand_zyx=angle_rand_zyx,
|
|
143
|
+
ang_lock_zyx=angle_lock)
|
|
144
|
+
sys_err_field = pyv.ErrSysField(strain_field,field_error_data)
|
|
145
|
+
error_chain.append(sys_err_field)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
error_int = pyv.ErrIntegrator(error_chain,
|
|
149
|
+
sens_data,
|
|
150
|
+
straingauge_array.get_measurement_shape())
|
|
151
|
+
straingauge_array.set_error_integrator(error_int)
|
|
152
|
+
|
|
153
|
+
#%%
|
|
154
|
+
# We run our virtual sensor simulation as normal. The only thing to note is
|
|
155
|
+
# that the second dimension of our measurement array will contain our tensor
|
|
156
|
+
# components in the order they are specified in the tuples with the normal
|
|
157
|
+
# components first followed by the deviatoric.
|
|
158
|
+
measurements = straingauge_array.calc_measurements()
|
|
159
|
+
|
|
160
|
+
#%%
|
|
161
|
+
# We print some of the results for one of the sensors so we can see the
|
|
162
|
+
# effect of the field errors.
|
|
163
|
+
print(80*"-")
|
|
164
|
+
|
|
165
|
+
sens_print: int = 0
|
|
166
|
+
time_print: int = 5
|
|
167
|
+
comp_print: int = 1 # strain_yy based on order in tuple
|
|
168
|
+
|
|
169
|
+
sens_print = 0
|
|
170
|
+
comp_print = 1 # strain_yy based on order in tuple
|
|
171
|
+
time_last = 5
|
|
172
|
+
time_print = slice(measurements.shape[2]-time_last,measurements.shape[2])
|
|
173
|
+
|
|
174
|
+
print("ROTATED SENSORS WITH ANGLE ERRORS:")
|
|
175
|
+
print(f"These are the last {time_last} virtual measurements of sensor "
|
|
176
|
+
+ f"{sens_print} for {norm_comps[comp_print]}:")
|
|
177
|
+
|
|
178
|
+
pyv.print_measurements(straingauge_array,sens_print,comp_print,time_print)
|
|
179
|
+
|
|
180
|
+
print(80*"-")
|
|
181
|
+
|
|
182
|
+
#%%
|
|
183
|
+
# We can plot a given component of our tensor field and display our sensor
|
|
184
|
+
# locations with respect to the field.
|
|
185
|
+
plot_field = "strain_yy"
|
|
186
|
+
pv_plot = pyv.plot_point_sensors_on_sim(straingauge_array,plot_field)
|
|
187
|
+
pv_plot.show(cpos="xy")
|
|
188
|
+
|
|
189
|
+
#%%
|
|
190
|
+
# We can also plot time traces for all components of the tensor field.
|
|
191
|
+
for cc in (norm_comps+dev_comps):
|
|
192
|
+
pyv.plot_time_traces(straingauge_array,cc)
|
|
193
|
+
|
|
194
|
+
plt.show()
|