pyvale 2025.5.3__cp311-cp311-musllinux_1_2_i686.whl → 2025.7.1__cp311-cp311-musllinux_1_2_i686.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 (98) hide show
  1. pyvale/__init__.py +12 -0
  2. pyvale/blendercalibrationdata.py +3 -1
  3. pyvale/blenderscene.py +7 -5
  4. pyvale/blendertools.py +27 -5
  5. pyvale/camera.py +1 -0
  6. pyvale/cameradata.py +3 -0
  7. pyvale/camerasensor.py +147 -0
  8. pyvale/camerastereo.py +4 -4
  9. pyvale/cameratools.py +23 -61
  10. pyvale/cython/rastercyth.c +1657 -1352
  11. pyvale/cython/rastercyth.cpython-311-i386-linux-musl.so +0 -0
  12. pyvale/cython/rastercyth.py +71 -26
  13. pyvale/data/DIC_Challenge_Star_Noise_Def.tiff +0 -0
  14. pyvale/data/DIC_Challenge_Star_Noise_Ref.tiff +0 -0
  15. pyvale/data/plate_hole_def0000.tiff +0 -0
  16. pyvale/data/plate_hole_def0001.tiff +0 -0
  17. pyvale/data/plate_hole_ref0000.tiff +0 -0
  18. pyvale/data/plate_rigid_def0000.tiff +0 -0
  19. pyvale/data/plate_rigid_def0001.tiff +0 -0
  20. pyvale/data/plate_rigid_ref0000.tiff +0 -0
  21. pyvale/dataset.py +96 -6
  22. pyvale/dic/cpp/dicbruteforce.cpp +370 -0
  23. pyvale/dic/cpp/dicfourier.cpp +648 -0
  24. pyvale/dic/cpp/dicinterpolator.cpp +559 -0
  25. pyvale/dic/cpp/dicmain.cpp +215 -0
  26. pyvale/dic/cpp/dicoptimizer.cpp +675 -0
  27. pyvale/dic/cpp/dicrg.cpp +137 -0
  28. pyvale/dic/cpp/dicscanmethod.cpp +677 -0
  29. pyvale/dic/cpp/dicsmooth.cpp +138 -0
  30. pyvale/dic/cpp/dicstrain.cpp +383 -0
  31. pyvale/dic/cpp/dicutil.cpp +563 -0
  32. pyvale/dic2d.py +164 -0
  33. pyvale/dic2dcpp.cpython-311-i386-linux-musl.so +0 -0
  34. pyvale/dicchecks.py +476 -0
  35. pyvale/dicdataimport.py +247 -0
  36. pyvale/dicregionofinterest.py +887 -0
  37. pyvale/dicresults.py +55 -0
  38. pyvale/dicspecklegenerator.py +238 -0
  39. pyvale/dicspecklequality.py +305 -0
  40. pyvale/dicstrain.py +387 -0
  41. pyvale/dicstrainresults.py +37 -0
  42. pyvale/errorintegrator.py +10 -8
  43. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +124 -113
  44. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +124 -132
  45. pyvale/examples/basics/ex1_3_customsens_therm3d.py +199 -195
  46. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +125 -121
  47. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +145 -141
  48. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +96 -101
  49. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +109 -105
  50. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +92 -91
  51. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +96 -90
  52. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +88 -89
  53. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +172 -171
  54. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +88 -86
  55. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +90 -90
  56. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +93 -91
  57. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +172 -160
  58. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +154 -148
  59. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +249 -231
  60. pyvale/examples/dic/ex1_region_of_interest.py +98 -0
  61. pyvale/examples/dic/ex2_plate_with_hole.py +149 -0
  62. pyvale/examples/dic/ex3_plate_with_hole_strain.py +93 -0
  63. pyvale/examples/dic/ex4_dic_blender.py +95 -0
  64. pyvale/examples/dic/ex5_dic_challenge.py +102 -0
  65. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +4 -2
  66. pyvale/examples/renderblender/ex1_1_blenderscene.py +152 -105
  67. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +151 -100
  68. pyvale/examples/renderblender/ex2_1_stereoscene.py +183 -116
  69. pyvale/examples/renderblender/ex2_2_stereodeformed.py +185 -112
  70. pyvale/examples/renderblender/ex3_1_blendercalibration.py +164 -109
  71. pyvale/examples/renderrasterisation/ex_rastenp.py +74 -35
  72. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +6 -13
  73. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +2 -2
  74. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +2 -4
  75. pyvale/imagedef2d.py +3 -2
  76. pyvale/imagetools.py +137 -0
  77. pyvale/rastercy.py +34 -4
  78. pyvale/rasternp.py +300 -276
  79. pyvale/rasteropts.py +58 -0
  80. pyvale/renderer.py +47 -0
  81. pyvale/rendermesh.py +52 -62
  82. pyvale/renderscene.py +51 -0
  83. pyvale/sensorarrayfactory.py +2 -2
  84. pyvale/sensortools.py +19 -35
  85. pyvale/simcases/case21.i +1 -1
  86. pyvale/simcases/run_1case.py +8 -0
  87. pyvale/simtools.py +2 -2
  88. pyvale/visualsimplotter.py +180 -0
  89. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/METADATA +11 -57
  90. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/RECORD +96 -56
  91. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/WHEEL +1 -1
  92. pyvale.libs/libgcc_s-887de51c.so.1 +0 -0
  93. pyvale.libs/libgomp-24921df4.so.1.0.0 +0 -0
  94. pyvale.libs/libstdc++-d6415257.so.6.0.33 +0 -0
  95. pyvale/examples/visualisation/ex1_1_plot_traces.py +0 -102
  96. pyvale/examples/visualisation/ex2_1_animate_sim.py +0 -89
  97. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/licenses/LICENSE +0 -0
  98. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/top_level.txt +0 -0
@@ -5,9 +5,10 @@
5
5
  # ==============================================================================
6
6
 
7
7
  """
8
- Pyvale example: Sensor spatial averaging and averaging errors
9
- --------------------------------------------------------------------------------
10
- In this example we show how pyvale can simulate sensor spatial averaging for
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
- Pyvale example: Basic vector field (displacement) sensors
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
- def main() -> None:
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).
31
- data_path = pyv.DataSet.mechanical_2d_path()
32
- # We use `mooseherder` to load the exodus file into a `SimData` object.
33
- sim_data = mh.ExodusReader(data_path).read_all_sim_data()
34
-
35
- # We scale our SI simulation to mm including the displacement fields which
36
- # are also in length units. The string keys we have provided here must match
37
- # the variable names you have in your SimData object.
38
- field_name = "disp"
39
- field_comps = ("disp_x","disp_y")
40
- sim_data = pyv.scale_length_units(scale=1000.0,
41
- sim_data=sim_data,
42
- disp_comps=field_comps)
43
-
44
- # Creating a displacement field point sensor array is similar to what we
45
- # have already done for scalar fields we just need to specify the string
46
- # keys for the displacement fields in the sim data object we have loaded.
47
- # For 2D vector fields we expect to have 2 components which are typically:
48
- # ("disp_x","disp_y"). For 3D vector fields we have 3 field components which
49
- # are typically: ("disp_x","disp_y","disp_z").
50
- n_sens = (2,3,1)
51
- x_lims = (0.0,100.0)
52
- y_lims = (0.0,150.0)
53
- z_lims = (0.0,0.0)
54
- sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
55
-
56
- sens_data = pyv.SensorData(positions=sens_pos)
57
-
58
- disp_sens_array = pyv.SensorArrayFactory \
59
- .disp_sensors_basic_errs(sim_data,
60
- sens_data,
61
- elem_dims=2,
62
- field_name=field_name,
63
- field_comps=field_comps,
64
- errs_pc=2.0)
65
-
66
- # We run our sensor simulation as normal but we note that the second
67
- # dimension of our measurement array will have the two vector components in
68
- # the order we specified them in the field keys.
69
- measurements = disp_sens_array.calc_measurements()
70
-
71
- # Here we print the shape of the measurement array so we can see that the
72
- # second dimension contains both our vector components. We also print some
73
- # of the sensor measurements for the first vector component.
74
- print("\n"+80*"-")
75
- print("For a virtual sensor: measurement = truth + sysematic error + random error")
76
- print(f"measurements.shape = {measurements.shape} = "+
77
- "(n_sensors,n_field_components,n_timesteps)\n")
78
- print("The truth, systematic error and random error arrays have the same "+
79
- "shape.")
80
-
81
- print(80*"-")
82
-
83
- sens_print: int = 0
84
- time_print: int = 5
85
- comp_print: int = 0
86
-
87
- print(f"These are the last {time_print} virtual measurements of sensor "
88
- + f"{sens_print}:")
89
-
90
- pyv.print_measurements(sens_array=disp_sens_array,
91
- sensors=(sens_print,sens_print+1),
92
- components=(comp_print,comp_print+1),
93
- time_steps=(measurements.shape[2]-time_print,
94
- measurements.shape[2]))
95
- print(80*"-")
96
-
97
- # Now that we have multiple field components we can plot each of them on the
98
- # simulation mesh and visulise the sensor locations with respect to these
99
- # fields.
100
- for ff in field_comps:
101
- pv_plot = pyv.plot_point_sensors_on_sim(disp_sens_array,ff)
102
- pv_plot.show(cpos="xy")
103
-
104
- # We can also plot the traces for each component of the displacement field.
105
- for ff in field_comps:
106
- pyv.plot_time_traces(disp_sens_array,ff)
107
-
108
- plt.show()
109
-
110
-
111
- if __name__ == "__main__":
112
- main()
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
- Pyvale example: Custom vector field sensors
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
- def main() -> None:
25
-
26
- # First we load the same 2D solid mechanics simulation we had previously as
27
- # a `SimData` object and then we scale everything to millimeters.
28
- data_path = pyv.DataSet.mechanical_2d_path()
29
- sim_data = mh.ExodusReader(data_path).read_all_sim_data()
30
- field_name = "disp"
31
- field_comps = ("disp_x","disp_y")
32
- sim_data = pyv.scale_length_units(scale=1000.0,
33
- sim_data=sim_data,
34
- disp_comps=field_comps)
35
-
36
- # This is the key different between building a vector field sensor vs a
37
- # scalar field sensor. Here we create a vector field object which we will
38
- # pass to our sensor array. In later examples we will see that the process
39
- # is the same for tensor fields (e.g. strain) where we create a tensor field
40
- # object and pass this to our sensor array. One thing to note is that the
41
- # number of field components will be different here for a 2D vs 3D
42
- # simulation. Also, it is worth noting that the element dimensions
43
- # parameter does not need to match the number of field components. For
44
- # example: it is possible to have a surface mesh (elem_dims=2) where we
45
- # have all 3 components of the displacement field.
46
- disp_field = pyv.FieldVector(sim_data,field_name,field_comps,elem_dims=2)
47
-
48
- # As we saw previously for scalar fields we define our sensor data object
49
- # which determines how many point sensors we have and their sampling times.
50
- # For vector field sensors we can also define the sensor orientation here
51
- # which we will demonstrate in the next example.
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
- # We set custom sampling times here but we could also set this to None so
59
- # that the sensors sample at the simulation time steps.
60
- sample_times = np.linspace(0.0,np.max(sim_data.time),50)
61
-
62
- sens_data = pyv.SensorData(positions=sens_pos,
63
- sample_times=sample_times)
64
-
65
- # We can optionally define a custom sensor descriptor for our vector field
66
- # sensor which will be used for labelling sensor placement visualisation or
67
- # for time traces. It is also possible to use the sensor descriptor factory
68
- # to get the same sensor descriptor object with these defaults.
69
- descriptor = pyv.SensorDescriptor(name="Disp.",
70
- symbol=r"u",
71
- units=r"mm",
72
- tag="DS",
73
- components=("x","y","z"))
74
-
75
- # The point sensor array class is generic and will take any field class
76
- # that implements the field interface. So here we just pass in the vector
77
- # field to create our vector field sensor array.
78
- disp_sens_array = pyv.SensorArrayPoint(sens_data,
79
- disp_field,
80
- descriptor)
81
-
82
- # We can add errors to our error simulation chain in exactly the same way as
83
- # we did for scalar fields. We will add some simple errors for now but in
84
- # the next example we will look at some field errors to do with sensor
85
- # orientation that
86
- error_chain = []
87
- error_chain.append(pyv.ErrSysUnif(low=-0.01,high=0.01)) # units = mm
88
- error_chain.append(pyv.ErrRandNorm(std=0.01)) # units = mm
89
- error_int = pyv.ErrIntegrator(error_chain,
90
- sens_data,
91
- disp_sens_array.get_measurement_shape())
92
- disp_sens_array.set_error_integrator(error_int)
93
-
94
- disp_sens_array.calc_measurements()
95
-
96
- # Now that we have multiple field components we can plot each of them on the
97
- # simulation mesh and visulise the sensor locations with respect to these
98
- # fields.
99
- for ff in field_comps:
100
- pv_plot = pyv.plot_point_sensors_on_sim(disp_sens_array,ff)
101
- pv_plot.show(cpos="xy")
102
-
103
- # We can also plot the traces for each component of the displacement field.
104
- for ff in field_comps:
105
- pyv.plot_time_traces(disp_sens_array,ff)
106
-
107
- plt.show()
108
-
109
-
110
- if __name__ == "__main__":
111
- main()
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
+