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,13 +5,14 @@
5
5
  # ==============================================================================
6
6
 
7
7
  """
8
- Pyvale example: Custom tensor field sensors (strain gauges) in 3D
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
- def main() -> None:
27
-
28
- # Frist we load our simulation as a `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
- # As we are creating a 3D tensor field sensor we now have a third
35
- # displacement field component here for scaling. Note that you don't need to
36
- # scale the displacements here if you only want to analyse strains.
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
- # Here is the main difference when creating a tensor field sensor array. We
43
- # create a tensor field where we need to specify the normal and deviatoric
44
- # component string keys as they appear in our `SimData` object. We have a 3D
45
- # simulation here so we have 3 normal components and 3 deviatoric (shear).
46
- field_name = "strain"
47
- norm_comps = ("strain_xx","strain_yy","strain_zz")
48
- dev_comps = ("strain_xy","strain_yz","strain_xz")
49
- strain_field = pyv.FieldTensor(sim_data,
50
- field_name=field_name,
51
- norm_comps=norm_comps,
52
- dev_comps=dev_comps,
53
- elem_dims=3)
54
-
55
- # Here we manually define our sensor positions to place a sensor on the
56
- # centre of each face of our 10mm cube. From here everything is the same as
57
- # for our 2D vector field sensor arrays.
58
- sensor_positions = np.array(((5.0,0.0,5.0), # bottom
59
- (5.0,10.0,5.0), # top
60
- (5.0,5.0,0.0), # xy face
61
- (5.0,5.0,10.0), # xy face
62
- (0.0,5.0,5.0), # yz face
63
- (10.0,5.0,5.0),)) # yz face
64
-
65
- # We set custom sensor sampling times here but we could also set this to
66
- # None to have the sensors sample at the simulation time steps.
67
- sample_times = np.linspace(0.0,np.max(sim_data.time),50)
68
-
69
- # We are going to manually specify the sensor angles for all our sensors.
70
- sens_angles = (Rotation.from_euler("zyx", [0, 0, 0], degrees=True),
71
- Rotation.from_euler("zyx", [0, 0, 0], degrees=True),
72
- Rotation.from_euler("zyx", [45, 0, 0], degrees=True),
73
- Rotation.from_euler("zyx", [45, 0, 0], degrees=True),
74
- Rotation.from_euler("zyx", [0, 0, 45], degrees=True),
75
- Rotation.from_euler("zyx", [0, 0, 45], degrees=True),)
76
-
77
-
78
- sens_data = pyv.SensorData(positions=sensor_positions,
79
- sample_times=sample_times,
80
- angles=sens_angles)
81
-
82
- # Here we create a descriptor that will be used to label visualisations of
83
- # the sensor locations and time traces for our sensors. For the strain
84
- # gauges we are modelling here we could also use the descriptor factory to
85
- # get these defaults.
86
- descriptor = pyv.SensorDescriptor(name="Strain",
87
- symbol=r"\varepsilon",
88
- units=r"-",
89
- tag="SG",
90
- components=('xx','yy','zz','xy','yz','xz'))
91
-
92
-
93
- straingauge_array = pyv.SensorArrayPoint(sens_data,
94
- strain_field,
95
- descriptor)
96
-
97
- # We can add any errors we like to our error chain. Here we add some basic
98
- # percentage errors.
99
- error_chain = []
100
- error_chain.append(pyv.ErrSysUnif(low=-0.1e-3,high=0.1e-3))
101
- error_chain.append(pyv.ErrRandNormPercent(std_percent=1.0))
102
-
103
- # Now we add a field error to perturb the positions of each sensor on its
104
- # relevant face and then add a +/- 2deg angle error.
105
-
106
- pos_uncert = 0.1 # units = mm
107
- pos_rand_xyz = (pyv.GenNormal(std=pos_uncert),
108
- pyv.GenNormal(std=pos_uncert),
109
- pyv.GenNormal(std=pos_uncert))
110
-
111
- angle_uncert = 2.0
112
- angle_rand_zyx = (pyv.GenUniform(low=-angle_uncert,high=angle_uncert), # units = deg
113
- pyv.GenUniform(low=-angle_uncert,high=angle_uncert),
114
- pyv.GenUniform(low=-angle_uncert,high=angle_uncert))
115
-
116
- # We are going to lock position perturbation so that the sensors stay on the
117
- # faces of the cube they are positioned on.
118
- pos_lock = np.full(sensor_positions.shape,False,dtype=bool)
119
- pos_lock[0:2,1] = True # Block translation in y
120
- pos_lock[2:4,2] = True # Block translation in z
121
- pos_lock[4:6,0] = True # Block translation in x
122
-
123
- # We are also going to lock angular perturbation so that each sensor is only
124
- # allowed to rotate on the plane it is on.
125
- angle_lock = np.full(sensor_positions.shape,True,dtype=bool)
126
- angle_lock[0:2,1] = False # Allow rotation about y
127
- angle_lock[2:4,0] = False # Allow rotation about z
128
- angle_lock[4:6,2] = False # Alloq rotation about x
129
-
130
- field_error_data = pyv.ErrFieldData(pos_rand_xyz=pos_rand_xyz,
131
- pos_lock_xyz=pos_lock,
132
- ang_rand_zyx=angle_rand_zyx,
133
- ang_lock_zyx=angle_lock)
134
- sys_err_field = pyv.ErrSysField(strain_field,field_error_data)
135
- error_chain.append(sys_err_field)
136
-
137
-
138
- error_int = pyv.ErrIntegrator(error_chain,
139
- sens_data,
140
- straingauge_array.get_measurement_shape())
141
- straingauge_array.set_error_integrator(error_int)
142
-
143
- # We run our virtual sensor simulation as normal. The only thing to note is
144
- # that the second dimension of our measurement array will contain our tensor
145
- # components in the order they are specified in the tuples with the normal
146
- # components first followed by the deviatoric.
147
- measurements = straingauge_array.calc_measurements()
148
-
149
- # We print some of the results for one of the sensors so we can see the
150
- # effect of the field errors.
151
- print(80*"-")
152
-
153
- sens_print: int = 0
154
- time_print: int = 5
155
- comp_print: int = 1 # strain_yy based on order in tuple
156
-
157
- print("ROTATED SENSORS WITH ANGLE ERRORS:")
158
- print(f"These are the last {time_print} virtual measurements of sensor "
159
- + f"{sens_print} for {norm_comps[comp_print]}:")
160
-
161
- pyv.print_measurements(sens_array=straingauge_array,
162
- sensors=(sens_print,sens_print+1),
163
- components=(comp_print,comp_print+1),
164
- time_steps=(measurements.shape[2]-time_print,
165
- measurements.shape[2]))
166
- print(80*"-")
167
-
168
- # We can plot a given component of our tensor field and display our sensor
169
- # locations with respect to the field.
170
- plot_field = "strain_yy"
171
- pv_plot = pyv.plot_point_sensors_on_sim(straingauge_array,plot_field)
172
- pv_plot.show(cpos="xy")
173
-
174
- # We can also plot time traces for all components of the tensor field.
175
- for cc in (norm_comps+dev_comps):
176
- pyv.plot_time_traces(straingauge_array,cc)
177
-
178
- plt.show()
179
-
180
-
181
- if __name__ == "__main__":
182
- main()
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()