pyvale 2025.5.3__cp311-cp311-musllinux_1_2_aarch64.whl → 2025.7.0__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/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.0.dist-info}/METADATA +11 -57
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/RECORD +94 -56
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.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.0.dist-info}/licenses/LICENSE +0 -0
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/top_level.txt +0 -0
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Basics: Sensor spatial averaging and averaging errors
|
|
9
|
+
================================================================================
|
|
10
|
+
|
|
11
|
+
In this example we show how `pyvale` can simulate sensor spatial averaging for
|
|
11
12
|
ground truth calculations as well as for calculating systematic errors.
|
|
12
13
|
|
|
13
14
|
Test case: Scalar field point sensors (thermocouples) on a 2D thermal simulation
|
|
@@ -18,106 +19,109 @@ import matplotlib.pyplot as plt
|
|
|
18
19
|
import mooseherder as mh
|
|
19
20
|
import pyvale as pyv
|
|
20
21
|
|
|
22
|
+
#%%
|
|
23
|
+
# First we are going to build a custom sensor array so we can control how
|
|
24
|
+
# the ground truth is extracted for a sensor using area averaging. Note that
|
|
25
|
+
# the default is an ideal point sensor with no spatial averaging. Later we
|
|
26
|
+
# will add area averaging as a systematic error. Note that it is possible to
|
|
27
|
+
# have an ideal point sensor with no area averaging for the truth and then
|
|
28
|
+
# add an area averaging error. It is also possible to have a truth that is
|
|
29
|
+
# area averaged without and area averaging error. The first part of this is
|
|
30
|
+
# the same as the 3D thermal example we have used previously then we control
|
|
31
|
+
# the area averaging using the sensor data object.
|
|
32
|
+
data_path = pyv.DataSet.thermal_2d_path()
|
|
33
|
+
sim_data = mh.ExodusReader(data_path).read_all_sim_data()
|
|
34
|
+
sim_data = pyv.scale_length_units(scale=1000.0,
|
|
35
|
+
sim_data=sim_data,
|
|
36
|
+
disp_comps=None)
|
|
37
|
+
|
|
38
|
+
descriptor = pyv.SensorDescriptorFactory.temperature_descriptor()
|
|
39
|
+
|
|
40
|
+
field_key = "temperature"
|
|
41
|
+
t_field = pyv.FieldScalar(sim_data,
|
|
42
|
+
field_key=field_key,
|
|
43
|
+
elem_dims=2)
|
|
44
|
+
|
|
45
|
+
n_sens = (4,1,1)
|
|
46
|
+
x_lims = (0.0,100.0)
|
|
47
|
+
y_lims = (0.0,50.0)
|
|
48
|
+
z_lims = (0.0,0.0)
|
|
49
|
+
sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
|
|
50
|
+
|
|
51
|
+
sample_times = np.linspace(0.0,np.max(sim_data.time),50) # | None
|
|
52
|
+
|
|
53
|
+
#%%
|
|
54
|
+
# This is where we control the setup of the area averaging. We need to
|
|
55
|
+
# specify the sensor dimensions and the type of numerical spatial
|
|
56
|
+
# integration to use. Here we specify a square sensor in x and y with 4
|
|
57
|
+
# point Gaussian quadrature integration. It is worth noting that increasing
|
|
58
|
+
# the number of integration points will increase computational cost as each
|
|
59
|
+
# additional integration point requires an additional interpolation of the
|
|
60
|
+
# physical field.
|
|
61
|
+
sensor_dims = np.array([20.0,20.0,0]) # units = mm
|
|
62
|
+
sensor_data = pyv.SensorData(positions=sens_pos,
|
|
63
|
+
sample_times=sample_times,
|
|
64
|
+
spatial_averager=pyv.EIntSpatialType.QUAD4PT,
|
|
65
|
+
spatial_dims=sensor_dims)
|
|
66
|
+
|
|
67
|
+
#%%
|
|
68
|
+
# We have added spatial averaging to our sensor data so we can now create
|
|
69
|
+
# our sensor array as we have done in previous examples.
|
|
70
|
+
tc_array = pyv.SensorArrayPoint(sensor_data,
|
|
71
|
+
t_field,
|
|
72
|
+
descriptor)
|
|
73
|
+
|
|
74
|
+
#%%
|
|
75
|
+
# We are also going to create a field error that includes area averaging as
|
|
76
|
+
# an error. We do this by adding the option to our field error data class
|
|
77
|
+
# specifying rectangular integration with 1 point.
|
|
78
|
+
area_avg_err_data = pyv.ErrFieldData(
|
|
79
|
+
spatial_averager=pyv.EIntSpatialType.RECT1PT,
|
|
80
|
+
spatial_dims=np.array((5.0,5.0)),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
#%%
|
|
84
|
+
# We add the field error to our error chain as normal. We could combine it
|
|
85
|
+
# with any of our other error models but we will isolate it for now so we
|
|
86
|
+
# can see what it does.
|
|
87
|
+
err_chain = []
|
|
88
|
+
err_chain.append(pyv.ErrSysField(t_field,
|
|
89
|
+
area_avg_err_data))
|
|
90
|
+
error_int = pyv.ErrIntegrator(err_chain,
|
|
91
|
+
sensor_data,
|
|
92
|
+
tc_array.get_measurement_shape())
|
|
93
|
+
tc_array.set_error_integrator(error_int)
|
|
94
|
+
|
|
95
|
+
#%%
|
|
96
|
+
# Now we run our sensor simulation to see how spatial averaging changes our
|
|
97
|
+
measurements = tc_array.calc_measurements()
|
|
98
|
+
|
|
99
|
+
print(80*"-")
|
|
100
|
+
|
|
101
|
+
sens_print = 0
|
|
102
|
+
comp_print = 0
|
|
103
|
+
time_last = 5
|
|
104
|
+
time_print = slice(measurements.shape[2]-time_last,measurements.shape[2])
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
print(f"These are the last {time_last} virtual measurements of sensor "
|
|
108
|
+
+ f"{sens_print}:")
|
|
109
|
+
|
|
110
|
+
pyv.print_measurements(tc_array,sens_print,comp_print,time_print)
|
|
111
|
+
|
|
112
|
+
print(80*"-")
|
|
113
|
+
|
|
114
|
+
pyv.plot_time_traces(tc_array,field_key)
|
|
115
|
+
plt.show()
|
|
116
|
+
|
|
117
|
+
#%%
|
|
118
|
+
# From here you now have everything you need to build your own sensor
|
|
119
|
+
# simulations for scalar field sensors using pyvale. In the next examples
|
|
120
|
+
# we will look at sensors applied to vector (e.g. displacement) and tensor
|
|
121
|
+
# fields (e.g. strain). If you don't need to sample vector or tensor fields
|
|
122
|
+
# then skip ahead to the examples on experiment simulation where you will
|
|
123
|
+
# learn how to perform Monte-Carlo sensor uncertainty quantification
|
|
124
|
+
# simulations and to analyse the results with pyvale.
|
|
125
|
+
|
|
126
|
+
|
|
21
127
|
|
|
22
|
-
def main() -> None:
|
|
23
|
-
# First we are going to build a custom sensor array so we can control how
|
|
24
|
-
# the ground truth is extracted for a sensor using area averaging. Note that
|
|
25
|
-
# the default is an ideal point sensor with no spatial averaging. Later we
|
|
26
|
-
# will add area averaging as a systematic error. Note that it is possible to
|
|
27
|
-
# have an ideal point sensor with no area averaging for the truth and then
|
|
28
|
-
# add an area averaging error. It is also possible to have a truth that is
|
|
29
|
-
# area averaged without and area averaging error. The first part of this is
|
|
30
|
-
# the same as the 3D thermal example we have used previously then we control
|
|
31
|
-
# the area averaging using the sensor data object.
|
|
32
|
-
data_path = pyv.DataSet.thermal_2d_path()
|
|
33
|
-
sim_data = mh.ExodusReader(data_path).read_all_sim_data()
|
|
34
|
-
sim_data = pyv.scale_length_units(scale=1000.0,
|
|
35
|
-
sim_data=sim_data,
|
|
36
|
-
disp_comps=None)
|
|
37
|
-
|
|
38
|
-
descriptor = pyv.SensorDescriptorFactory.temperature_descriptor()
|
|
39
|
-
|
|
40
|
-
field_key = "temperature"
|
|
41
|
-
t_field = pyv.FieldScalar(sim_data,
|
|
42
|
-
field_key=field_key,
|
|
43
|
-
elem_dims=2)
|
|
44
|
-
|
|
45
|
-
n_sens = (4,1,1)
|
|
46
|
-
x_lims = (0.0,100.0)
|
|
47
|
-
y_lims = (0.0,50.0)
|
|
48
|
-
z_lims = (0.0,0.0)
|
|
49
|
-
sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
|
|
50
|
-
|
|
51
|
-
sample_times = np.linspace(0.0,np.max(sim_data.time),50) # | None
|
|
52
|
-
|
|
53
|
-
# This is where we control the setup of the area averaging. We need to
|
|
54
|
-
# specify the sensor dimensions and the type of numerical spatial
|
|
55
|
-
# integration to use. Here we specify a square sensor in x and y with 4
|
|
56
|
-
# point Gaussian quadrature integration. It is worth noting that increasing
|
|
57
|
-
# the number of integration points will increase computational cost as each
|
|
58
|
-
# additional integration point requires an additional interpolation of the
|
|
59
|
-
# physical field.
|
|
60
|
-
sensor_dims = np.array([20.0,20.0,0]) # units = mm
|
|
61
|
-
sensor_data = pyv.SensorData(positions=sens_pos,
|
|
62
|
-
sample_times=sample_times,
|
|
63
|
-
spatial_averager=pyv.EIntSpatialType.QUAD4PT,
|
|
64
|
-
spatial_dims=sensor_dims)
|
|
65
|
-
|
|
66
|
-
# We have added spatial averaging to our sensor data so we can now create
|
|
67
|
-
# our sensor array as we have done in previous examples.
|
|
68
|
-
tc_array = pyv.SensorArrayPoint(sensor_data,
|
|
69
|
-
t_field,
|
|
70
|
-
descriptor)
|
|
71
|
-
|
|
72
|
-
# We are also going to create a field error that includes area averaging as
|
|
73
|
-
# an error. We do this by adding the option to our field error data class
|
|
74
|
-
# specifying rectangular integration with 1 point.
|
|
75
|
-
area_avg_err_data = pyv.ErrFieldData(
|
|
76
|
-
spatial_averager=pyv.EIntSpatialType.RECT1PT,
|
|
77
|
-
spatial_dims=np.array((5.0,5.0)),
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
# We add the field error to our error chain as normal. We could combine it
|
|
81
|
-
# with any of our other error models but we will isolate it for now so we
|
|
82
|
-
# can see what it does.
|
|
83
|
-
err_chain = []
|
|
84
|
-
err_chain.append(pyv.ErrSysField(t_field,
|
|
85
|
-
area_avg_err_data))
|
|
86
|
-
error_int = pyv.ErrIntegrator(err_chain,
|
|
87
|
-
sensor_data,
|
|
88
|
-
tc_array.get_measurement_shape())
|
|
89
|
-
tc_array.set_error_integrator(error_int)
|
|
90
|
-
|
|
91
|
-
# Now we run our sensor simulation to see how spatial averaging changes our
|
|
92
|
-
measurements = tc_array.calc_measurements()
|
|
93
|
-
|
|
94
|
-
print(80*"-")
|
|
95
|
-
|
|
96
|
-
sens_print: int = 0
|
|
97
|
-
time_print: int = 5
|
|
98
|
-
comp_print: int = 0
|
|
99
|
-
|
|
100
|
-
print(f"These are the last {time_print} virtual measurements of sensor "
|
|
101
|
-
+ f"{sens_print}:")
|
|
102
|
-
|
|
103
|
-
pyv.print_measurements(sens_array=tc_array,
|
|
104
|
-
sensors=(sens_print,sens_print+1),
|
|
105
|
-
components=(comp_print,comp_print+1),
|
|
106
|
-
time_steps=(measurements.shape[2]-time_print,
|
|
107
|
-
measurements.shape[2]))
|
|
108
|
-
print(80*"-")
|
|
109
|
-
|
|
110
|
-
pyv.plot_time_traces(tc_array,field_key)
|
|
111
|
-
plt.show()
|
|
112
|
-
|
|
113
|
-
# From here you now have everything you need to build your own sensor
|
|
114
|
-
# simulations for scalar field sensors using pyvale. In the next examples
|
|
115
|
-
# we will look at sensors applied to vector (e.g. displacement) and tensor
|
|
116
|
-
# fields (e.g. strain). If you don't need to sample vector or tensor fields
|
|
117
|
-
# then skip ahead to the examples on experiment simulation where you will
|
|
118
|
-
# learn how to perform Monte-Carlo sensor uncertainty quantification
|
|
119
|
-
# simulations and to analyse the results with pyvale.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if __name__ == "__main__":
|
|
123
|
-
main()
|
|
@@ -5,108 +5,109 @@
|
|
|
5
5
|
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Basics: Vector field (displacement) sensors
|
|
9
|
+
================================================================================
|
|
10
|
+
|
|
10
11
|
In this example we use the sensor array factory to build a set of displacement
|
|
11
12
|
sensors that can sample the displacement vector field from a solid mechanics
|
|
12
13
|
simulation. In the next example we will examine how we can build custom vector
|
|
13
14
|
field sensors as we did for scalar field in the first set of examples.
|
|
14
15
|
|
|
15
|
-
Note that this tutorial assumes you are familiar with the use of pyvale for
|
|
16
|
+
Note that this tutorial assumes you are familiar with the use of `pyvale` for
|
|
16
17
|
scalar fields as described in the first set of examples.
|
|
17
18
|
|
|
18
19
|
Test case: point displacement sensors on a 2D plate with hole loaded in tension
|
|
19
20
|
"""
|
|
20
21
|
|
|
21
|
-
|
|
22
22
|
import matplotlib.pyplot as plt
|
|
23
23
|
import mooseherder as mh
|
|
24
24
|
import pyvale as pyv
|
|
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
|
-
|
|
26
|
+
#%%
|
|
27
|
+
# Here we load a pre-packaged dataset from pyvale that is the output of a
|
|
28
|
+
# MOOSE simulation in exodus format. The simulation is a linear elastic
|
|
29
|
+
# rectangular plate with a central hole that is loaded in tension (we will
|
|
30
|
+
# see a visualisation of the mesh and results later). We use `mooseherder` to
|
|
31
|
+
# load the exodus file into a `SimData` object.
|
|
32
|
+
data_path = pyv.DataSet.mechanical_2d_path()
|
|
33
|
+
sim_data = mh.ExodusReader(data_path).read_all_sim_data()
|
|
34
|
+
|
|
35
|
+
#%%
|
|
36
|
+
# We scale our SI simulation to mm including the displacement fields which
|
|
37
|
+
# are also in length units. The string keys we have provided here must match
|
|
38
|
+
# the variable names you have in your SimData object.
|
|
39
|
+
field_name = "disp"
|
|
40
|
+
field_comps = ("disp_x","disp_y")
|
|
41
|
+
sim_data = pyv.scale_length_units(scale=1000.0,
|
|
42
|
+
sim_data=sim_data,
|
|
43
|
+
disp_comps=field_comps)
|
|
44
|
+
|
|
45
|
+
#%%
|
|
46
|
+
# Creating a displacement field point sensor array is similar to what we
|
|
47
|
+
# have already done for scalar fields we just need to specify the string
|
|
48
|
+
# keys for the displacement fields in the sim data object we have loaded.
|
|
49
|
+
# For 2D vector fields we expect to have 2 components which are typically:
|
|
50
|
+
# ("disp_x","disp_y"). For 3D vector fields we have 3 field components which
|
|
51
|
+
# are typically: ("disp_x","disp_y","disp_z").
|
|
52
|
+
n_sens = (2,3,1)
|
|
53
|
+
x_lims = (0.0,100.0)
|
|
54
|
+
y_lims = (0.0,150.0)
|
|
55
|
+
z_lims = (0.0,0.0)
|
|
56
|
+
sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
|
|
57
|
+
|
|
58
|
+
sens_data = pyv.SensorData(positions=sens_pos)
|
|
59
|
+
|
|
60
|
+
disp_sens_array = pyv.SensorArrayFactory \
|
|
61
|
+
.disp_sensors_basic_errs(sim_data,
|
|
62
|
+
sens_data,
|
|
63
|
+
elem_dims=2,
|
|
64
|
+
field_name=field_name,
|
|
65
|
+
field_comps=field_comps,
|
|
66
|
+
errs_pc=2.0)
|
|
67
|
+
|
|
68
|
+
#%%
|
|
69
|
+
# We run our sensor simulation as normal but we note that the second
|
|
70
|
+
# dimension of our measurement array will have the two vector components in
|
|
71
|
+
# the order we specified them in the field keys.
|
|
72
|
+
measurements = disp_sens_array.calc_measurements()
|
|
73
|
+
|
|
74
|
+
#%%
|
|
75
|
+
# Here we print the shape of the measurement array so we can see that the
|
|
76
|
+
# second dimension contains both our vector components. We also print some
|
|
77
|
+
# of the sensor measurements for the first vector component.
|
|
78
|
+
print("\n"+80*"-")
|
|
79
|
+
print("For a virtual sensor: measurement = truth + sysematic error + random error")
|
|
80
|
+
print(f"measurements.shape = {measurements.shape} = "+
|
|
81
|
+
"(n_sensors,n_field_components,n_timesteps)\n")
|
|
82
|
+
print("The truth, systematic error and random error arrays have the same "+
|
|
83
|
+
"shape.")
|
|
84
|
+
|
|
85
|
+
print(80*"-")
|
|
86
|
+
|
|
87
|
+
sens_print = 0
|
|
88
|
+
comp_print = 0
|
|
89
|
+
time_last = 5
|
|
90
|
+
time_print = slice(measurements.shape[2]-time_last,measurements.shape[2])
|
|
91
|
+
|
|
92
|
+
print(f"These are the last {time_last} virtual measurements of sensor "
|
|
93
|
+
+ f"{sens_print}:")
|
|
94
|
+
|
|
95
|
+
pyv.print_measurements(disp_sens_array,sens_print,comp_print,time_print)
|
|
96
|
+
|
|
97
|
+
print(80*"-")
|
|
98
|
+
|
|
99
|
+
#%%
|
|
100
|
+
# Now that we have multiple field components we can plot each of them on the
|
|
101
|
+
# simulation mesh and visulise the sensor locations with respect to these
|
|
102
|
+
# fields.
|
|
103
|
+
for ff in field_comps:
|
|
104
|
+
pv_plot = pyv.plot_point_sensors_on_sim(disp_sens_array,ff)
|
|
105
|
+
pv_plot.show(cpos="xy")
|
|
106
|
+
|
|
107
|
+
#%%
|
|
108
|
+
# We can also plot the traces for each component of the displacement field.
|
|
109
|
+
for ff in field_comps:
|
|
110
|
+
pyv.plot_time_traces(disp_sens_array,ff)
|
|
111
|
+
|
|
112
|
+
plt.show()
|
|
113
|
+
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Basics: Custom vector field sensors
|
|
9
|
+
================================================================================
|
|
10
|
+
|
|
10
11
|
In this example we build a custom vector field sensor array which mimics the
|
|
11
12
|
sensor array we built with the factory in the previous example.
|
|
12
13
|
|
|
@@ -21,91 +22,96 @@ import matplotlib.pyplot as plt
|
|
|
21
22
|
import mooseherder as mh
|
|
22
23
|
import pyvale as pyv
|
|
23
24
|
|
|
24
|
-
|
|
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
|
-
|
|
25
|
+
|
|
26
|
+
#%%
|
|
27
|
+
# First we load the same 2D solid mechanics simulation we had previously as
|
|
28
|
+
# a `SimData` object and then we scale everything to millimeters.
|
|
29
|
+
data_path = pyv.DataSet.mechanical_2d_path()
|
|
30
|
+
sim_data = mh.ExodusReader(data_path).read_all_sim_data()
|
|
31
|
+
field_name = "disp"
|
|
32
|
+
field_comps = ("disp_x","disp_y")
|
|
33
|
+
sim_data = pyv.scale_length_units(scale=1000.0,
|
|
34
|
+
sim_data=sim_data,
|
|
35
|
+
disp_comps=field_comps)
|
|
36
|
+
|
|
37
|
+
#%%
|
|
38
|
+
# This is the key different between building a vector field sensor vs a
|
|
39
|
+
# scalar field sensor. Here we create a vector field object which we will
|
|
40
|
+
# pass to our sensor array. In later examples we will see that the process
|
|
41
|
+
# is the same for tensor fields (e.g. strain) where we create a tensor field
|
|
42
|
+
# object and pass this to our sensor array. One thing to note is that the
|
|
43
|
+
# number of field components will be different here for a 2D vs 3D
|
|
44
|
+
# simulation. Also, it is worth noting that the element dimensions
|
|
45
|
+
# parameter does not need to match the number of field components. For
|
|
46
|
+
# example: it is possible to have a surface mesh (elem_dims=2) where we
|
|
47
|
+
# have all 3 components of the displacement field.
|
|
48
|
+
disp_field = pyv.FieldVector(sim_data,field_name,field_comps,elem_dims=2)
|
|
49
|
+
|
|
50
|
+
#%%
|
|
51
|
+
# As we saw previously for scalar fields we define our sensor data object
|
|
52
|
+
# which determines how many point sensors we have and their sampling times.
|
|
53
|
+
# For vector field sensors we can also define the sensor orientation here
|
|
54
|
+
# which we will demonstrate in the next example.
|
|
55
|
+
n_sens = (2,3,1)
|
|
56
|
+
x_lims = (0.0,100.0)
|
|
57
|
+
y_lims = (0.0,150.0)
|
|
58
|
+
z_lims = (0.0,0.0)
|
|
59
|
+
sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
|
|
60
|
+
|
|
61
|
+
#%%
|
|
62
|
+
# We set custom sampling times here but we could also set this to None so
|
|
63
|
+
# that the sensors sample at the simulation time steps.
|
|
64
|
+
sample_times = np.linspace(0.0,np.max(sim_data.time),50)
|
|
65
|
+
|
|
66
|
+
sens_data = pyv.SensorData(positions=sens_pos,
|
|
67
|
+
sample_times=sample_times)
|
|
68
|
+
|
|
69
|
+
#%%
|
|
70
|
+
# We can optionally define a custom sensor descriptor for our vector field
|
|
71
|
+
# sensor which will be used for labelling sensor placement visualisation or
|
|
72
|
+
# for time traces. It is also possible to use the sensor descriptor factory
|
|
73
|
+
# to get the same sensor descriptor object with these defaults.
|
|
74
|
+
descriptor = pyv.SensorDescriptor(name="Disp.",
|
|
75
|
+
symbol=r"u",
|
|
76
|
+
units=r"mm",
|
|
77
|
+
tag="DS",
|
|
78
|
+
components=("x","y","z"))
|
|
79
|
+
|
|
80
|
+
#%%
|
|
81
|
+
# The point sensor array class is generic and will take any field class
|
|
82
|
+
# that implements the field interface. So here we just pass in the vector
|
|
83
|
+
# field to create our vector field sensor array.
|
|
84
|
+
disp_sens_array = pyv.SensorArrayPoint(sens_data,
|
|
85
|
+
disp_field,
|
|
86
|
+
descriptor)
|
|
87
|
+
|
|
88
|
+
#%%
|
|
89
|
+
# We can add errors to our error simulation chain in exactly the same way as
|
|
90
|
+
# we did for scalar fields. We will add some simple errors for now but in
|
|
91
|
+
# the next example we will look at some field errors to do with sensor
|
|
92
|
+
# orientation that
|
|
93
|
+
error_chain = []
|
|
94
|
+
error_chain.append(pyv.ErrSysUnif(low=-0.01,high=0.01)) # units = mm
|
|
95
|
+
error_chain.append(pyv.ErrRandNorm(std=0.01)) # units = mm
|
|
96
|
+
error_int = pyv.ErrIntegrator(error_chain,
|
|
97
|
+
sens_data,
|
|
98
|
+
disp_sens_array.get_measurement_shape())
|
|
99
|
+
disp_sens_array.set_error_integrator(error_int)
|
|
100
|
+
|
|
101
|
+
disp_sens_array.calc_measurements()
|
|
102
|
+
|
|
103
|
+
#%%
|
|
104
|
+
# Now that we have multiple field components we can plot each of them on the
|
|
105
|
+
# simulation mesh and visulise the sensor locations with respect to these
|
|
106
|
+
# fields.
|
|
107
|
+
for ff in field_comps:
|
|
108
|
+
pv_plot = pyv.plot_point_sensors_on_sim(disp_sens_array,ff)
|
|
109
|
+
pv_plot.show(cpos="xy")
|
|
110
|
+
|
|
111
|
+
#%%
|
|
112
|
+
# We can also plot the traces for each component of the displacement field.
|
|
113
|
+
for ff in field_comps:
|
|
114
|
+
pyv.plot_time_traces(disp_sens_array,ff)
|
|
115
|
+
|
|
116
|
+
plt.show()
|
|
117
|
+
|