pyvale 2025.5.3__cp311-cp311-macosx_14_0_arm64.whl → 2025.7.0__cp311-cp311-macosx_14_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyvale might be problematic. Click here for more details.

Files changed (95) hide show
  1. pyvale/.dylibs/libomp.dylib +0 -0
  2. pyvale/.dylibs/libunwind.1.0.dylib +0 -0
  3. pyvale/__init__.py +12 -0
  4. pyvale/blendercalibrationdata.py +3 -1
  5. pyvale/blenderscene.py +7 -5
  6. pyvale/blendertools.py +27 -5
  7. pyvale/camera.py +1 -0
  8. pyvale/cameradata.py +3 -0
  9. pyvale/camerasensor.py +147 -0
  10. pyvale/camerastereo.py +4 -4
  11. pyvale/cameratools.py +23 -61
  12. pyvale/cython/rastercyth.c +1657 -1352
  13. pyvale/cython/rastercyth.cpython-311-darwin.so +0 -0
  14. pyvale/cython/rastercyth.py +71 -26
  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-darwin.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.0.dist-info}/METADATA +11 -57
  90. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/RECORD +93 -57
  91. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/WHEEL +1 -1
  92. pyvale/examples/visualisation/ex1_1_plot_traces.py +0 -102
  93. pyvale/examples/visualisation/ex2_1_animate_sim.py +0 -89
  94. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/licenses/LICENSE +0 -0
  95. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/top_level.txt +0 -0
@@ -5,8 +5,9 @@
5
5
  # ==============================================================================
6
6
 
7
7
  """
8
- Pyvale example: Building a point sensor array from scratch with custom errors
9
- --------------------------------------------------------------------------------
8
+ Basics: Building a point sensor array from scratch with custom errors
9
+ ================================================================================
10
+
10
11
  Here we build a custom point sensor array from scratch that is similar to the
11
12
  pre-built thermocouple array from example 1.1. For this example we switch to a
12
13
  3D thermal simulation of a fusion heatsink component.
@@ -20,197 +21,200 @@ import matplotlib.pyplot as plt
20
21
  import mooseherder as mh
21
22
  import pyvale as pyv
22
23
 
24
+ #%%
25
+ # To build our custom point sensor array we need to at minimum provide a
26
+ # `IField` (i.e. `FieldScaler`, `FieldVector`, `FieldTensor`) and a
27
+ # `SensorData` object. For labelling visualisations (e.g. axis labels and
28
+ # unit labels) we can also provide a `SensorDescriptor` object.
29
+ # Once we have built our `SensorArrayPoint` object from these we can then
30
+ # attach custom chains of different types of random and systematic errors
31
+ # to be evaluated when we run our measurement simulation. This example is
32
+ # based on the same thermal example we have used in the last two examples so
33
+ # we start by loading our simulation data:
34
+
35
+ data_path = pyv.DataSet.thermal_3d_path()
36
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
37
+ sim_data = pyv.scale_length_units(scale=1000.0,
38
+ sim_data=sim_data,
39
+ disp_comps=None)
40
+
41
+ #%%
42
+ # We are going to build a custom temperature sensor so we need a scalar
43
+ # field object to perform interpolation to the sensor locations at the
44
+ # desired sampling times.
45
+ field_key: str = "temperature"
46
+ t_field = pyv.FieldScalar(sim_data,
47
+ field_key=field_key,
48
+ elem_dims=3)
49
+
50
+
51
+ #%%
52
+ # Next we need to create our `SensorData` object which will set the position
53
+ # and sampling times of our sensors. We use the same helper function we used
54
+ # previously to create a uniformly spaced grid of sensors in space
55
+ n_sens = (1,4,1)
56
+ x_lims = (12.5,12.5)
57
+ y_lims = (0.0,33.0)
58
+ z_lims = (0.0,12.0)
59
+ sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
60
+
61
+ #%%
62
+ # We are also going to specify the times at which we would like to simulate
63
+ # measurements. Setting this to `None` will default the measurements times
64
+ # to match the simulation time steps.
65
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50)
66
+
67
+ sensor_data = pyv.SensorData(positions=sens_pos,
68
+ sample_times=sample_times)
69
+
70
+ #%%
71
+ # Finally, we can create a `SensorDescriptor` which will be used to label
72
+ # the visualisation and sensor trace plots we have seen in previous
73
+ # examples.
74
+ use_auto_descriptor: str = "blank"
75
+ if use_auto_descriptor == "manual":
76
+ descriptor = pyv.SensorDescriptor(name="Temperature",
77
+ symbol="T",
78
+ units = r"^{\circ}C",
79
+ tag = "TC")
80
+ elif use_auto_descriptor == "factory":
81
+ descriptor = pyv.SensorDescriptorFactory.temperature_descriptor()
82
+ else:
83
+ descriptor = pyv.SensorDescriptor()
84
+
85
+ #%%
86
+ # We can now build our custom point sensor array. This sensor array has no
87
+ # errors so if we call `get_measurements()` or `calc_measurements()` we will
88
+ # be able to extract the simulation truth values at the sensor locations.
89
+ tc_array = pyv.SensorArrayPoint(sensor_data,
90
+ t_field,
91
+ descriptor)
92
+
93
+ #%%
94
+ # This is a new 3D simulation we are analysing so we should visualise the
95
+ # sensor locations before we run our measurement simulation. We use the same
96
+ # code as we did in example 1.1 to display the sensor locations.
97
+ #
98
+ # We are also going to save some figures to disk as well as displaying them
99
+ # interactively so we create a directory for this:
100
+ output_path = Path.cwd() / "pyvale-output"
101
+ if not output_path.is_dir():
102
+ output_path.mkdir(parents=True, exist_ok=True)
103
+
104
+ pv_plot = pyv.plot_point_sensors_on_sim(tc_array,field_key)
105
+
106
+ pv_plot.camera_position = [(59.354, 43.428, 69.946),
107
+ (-2.858, 13.189, 4.523),
108
+ (-0.215, 0.948, -0.233)]
109
+
110
+ save_render = output_path / "customsensors_ex1_3_sensorlocs.svg"
111
+ pv_plot.save_graphic(save_render) # only for .svg .eps .ps .pdf .tex
112
+ pv_plot.screenshot(save_render.with_suffix(".png"))
113
+
114
+ pv_plot.show()
115
+
116
+ #%%
117
+ # If we want to simulate sources of uncertainty for our sensor array we need
118
+ # to add an `ErrIntegrator` to our sensor array using the method
119
+ # `set_error_integrator()`. We provide our `ErrIntegrator` a list of error
120
+ # objects which will be evaluated in the order specified in the list.
121
+ #
122
+ # In pyvale errors have a type specified as: random / systematic
123
+ # (`EErrorType`) and a dependence `EErrDependence` as: independent /
124
+ # dependent. When analysing errors all random all systematic errors are
125
+ # grouped and summed together.
126
+ #
127
+ # The error dependence determines if an error is
128
+ # calculated based on the truth (independent) or the accumulated measurement
129
+ # based on all previous errors in the chain (dependent). Some errors are
130
+ # purely independent such as random noise with a normal distribution with a
131
+ # set standard devitation. An example of an error that is dependent would be
132
+ # saturation which must be place last in the error chain and will clamp the
133
+ # final sensor value to be within the specified bounds.
134
+ #
135
+ # pyvale provides a library of different random `ErrRand*` and systematic
136
+ # `ErrSys*` errors which can be found listed in the docs. In the next
137
+ # example we will explore the error library but for now we will specify some
138
+ # common error types. Try experimenting with the code below to turn the
139
+ # different error types off and on to see how it changes the virtual sensor
140
+ # measurements.
141
+ #
142
+ # This systematic error is just a constant offset of -5 to all simulated
143
+ # measurements. Note that error values should be specified in the same
144
+ # units as the simulation.
145
+ #
146
+ # This systematic error samples from a uniform probability distribution.
147
+ errors_on = {"sys": True,
148
+ "rand": True}
149
+
150
+ error_chain = []
151
+ if errors_on["sys"]:
152
+ error_chain.append(pyv.ErrSysOffset(offset=-10.0))
153
+ error_chain.append(pyv.ErrSysUnif(low=-10.0,
154
+ high=10.0))
155
+ #%%
156
+ # This random error is generated by sampling from a normal distribution
157
+ # with the given standard deviation in simulation units.
158
+ # This random error is generated as a percentage sampled from uniform
159
+ # probability distribution
160
+
161
+ if errors_on["rand"]:
162
+ error_chain.append(pyv.ErrRandNorm(std=5.0))
163
+ error_chain.append(pyv.ErrRandUnifPercent(low_percent=-5.0,
164
+ high_percent=5.0))
165
+
166
+ #%%
167
+ # By default pyvale does not store all individual error source
168
+ # calculations (i.e. only the total random and total systematic error are
169
+ # stored) to save memory but this can be changed using `ErrIntOpts`. This
170
+ # can also be used to force all errors to behave as if they are DEPENDENT or
171
+ # INDEPENDENT.
172
+
173
+ if len(error_chain) > 0:
174
+ err_int_opts = pyv.ErrIntOpts()
175
+ error_integrator = pyv.ErrIntegrator(error_chain,
176
+ sensor_data,
177
+ tc_array.get_measurement_shape(),
178
+ err_int_opts=err_int_opts)
179
+ tc_array.set_error_integrator(error_integrator)
180
+
181
+ #%%
182
+ # Now that we have added our error chain we can run a simulation to sample
183
+ # from all our error sources.
184
+ measurements = tc_array.calc_measurements()
185
+
186
+ #%%
187
+ # We display the simulation results by printing to the console and by
188
+ # plotting the sensor times traces. Try experimenting with the errors above
189
+ # to see how the results change.
190
+ print("\n"+80*"-")
191
+ print("For a virtual sensor: measurement = truth + sysematic error + random error")
192
+ print(f"measurements.shape = {measurements.shape} = "+
193
+ "(n_sensors,n_field_components,n_timesteps)\n")
194
+ print("The truth, systematic error and random error arrays have the same "+
195
+ "shape.")
196
+
197
+ print(80*"-")
198
+
199
+ sens_print = 0
200
+ comp_print = 0
201
+ time_last = 5
202
+ time_print = slice(measurements.shape[2]-time_last,measurements.shape[2])
203
+
204
+ print(f"These are the last {time_last} virtual measurements of sensor "
205
+ + f"{sens_print}:")
206
+
207
+ pyv.print_measurements(tc_array,sens_print,comp_print,time_print)
208
+
209
+ print(80*"-")
210
+
211
+ (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
212
+
213
+ save_traces = output_path/"customsensors_ex1_3_sensortraces.png"
214
+ fig.savefig(save_traces, dpi=300, bbox_inches="tight")
215
+ fig.savefig(save_traces.with_suffix(".svg"), dpi=300, bbox_inches="tight")
216
+
217
+ plt.show()
218
+
219
+
23
220
 
24
- def main() -> None:
25
- # To build our custom point sensor array we need to at minimum provide a
26
- # `IField` (i.e. `FieldScaler`, `FieldVector`, `FieldTensor`) and a
27
- # `SensorData` object. For labelling visualisations (e.g. axis labels and
28
- # unit labels) we can also provide a `SensorDescriptor` object.
29
- # Once we have built our `SensorArrayPoint` object from these we can then
30
- # attach custom chains of different types of random and systematic errors
31
- # to be evaluated when we run our measurement simulation. This example is
32
- # based on the same thermal example we have used in the last two examples so
33
- # we start by loading our simulation data:
34
-
35
- data_path = pyv.DataSet.thermal_3d_path()
36
- sim_data = mh.ExodusReader(data_path).read_all_sim_data()
37
- # Scale to mm to make 3D visualisation scaling easier as pyvista scales
38
- # everything to unity
39
- sim_data = pyv.scale_length_units(scale=1000.0,
40
- sim_data=sim_data,
41
- disp_comps=None)
42
-
43
- # We are going to build a custom temperature sensor so we need a scalar
44
- # field object to perform interpolation to the sensor locations at the
45
- # desired sampling times.
46
- field_key: str = "temperature"
47
- t_field = pyv.FieldScalar(sim_data,
48
- field_key=field_key,
49
- elem_dims=3)
50
-
51
-
52
- # Next we need to create our `SensorData` object which will set the position
53
- # and sampling times of our sensors. We use the same helper function we used
54
- # previously to create a uniformly spaced grid of sensors in space
55
- n_sens = (1,4,1)
56
- x_lims = (12.5,12.5)
57
- y_lims = (0.0,33.0)
58
- z_lims = (0.0,12.0)
59
- sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
60
-
61
- # We are also going to specify the times at which we would like to simulate
62
- # measurements. Setting this to `None` will default the measurements times
63
- # to match the simulation time steps.
64
- sample_times = np.linspace(0.0,np.max(sim_data.time),50)
65
-
66
- sensor_data = pyv.SensorData(positions=sens_pos,
67
- sample_times=sample_times)
68
-
69
- # Finally, we can create a `SensorDescriptor` which will be used to label
70
- # the visualisation and sensor trace plots we have seen in previous
71
- # examples.
72
- use_auto_descriptor: str = "blank"
73
- if use_auto_descriptor == "manual":
74
- descriptor = pyv.SensorDescriptor(name="Temperature",
75
- symbol="T",
76
- units = r"^{\circ}C",
77
- tag = "TC")
78
- elif use_auto_descriptor == "factory":
79
- descriptor = pyv.SensorDescriptorFactory.temperature_descriptor()
80
- else:
81
- descriptor = pyv.SensorDescriptor()
82
-
83
-
84
- # We can now build our custom point sensor array. This sensor array has no
85
- # errors so if we call `get_measurements()` or `calc_measurements()` we will
86
- # be able to extract the simulation truth values at the sensor locations.
87
- tc_array = pyv.SensorArrayPoint(sensor_data,
88
- t_field,
89
- descriptor)
90
-
91
- # This is a new 3D simulation we are analysing so we should visualise the
92
- # sensor locations before we run our measurement simulation. We use the same
93
- # code as we did in example 1.1 to display the sensor locations.
94
-
95
- # We are also going to save some figures to disk as well as displaying them
96
- # interactively so we create a directory for this:
97
- output_path = Path.cwd() / "pyvale-output"
98
- if not output_path.is_dir():
99
- output_path.mkdir(parents=True, exist_ok=True)
100
-
101
- pv_plot = pyv.plot_point_sensors_on_sim(tc_array,field_key)
102
-
103
- pv_plot.camera_position = [(59.354, 43.428, 69.946),
104
- (-2.858, 13.189, 4.523),
105
- (-0.215, 0.948, -0.233)]
106
-
107
- save_render = output_path / "customsensors_ex1_3_sensorlocs.svg"
108
- pv_plot.save_graphic(save_render) # only for .svg .eps .ps .pdf .tex
109
- pv_plot.screenshot(save_render.with_suffix(".png"))
110
-
111
- pv_plot.show()
112
-
113
- # If we want to simulate sources of uncertainty for our sensor array we need
114
- # to add an `ErrIntegrator` to our sensor array using the method
115
- # `set_error_integrator()`. We provide our `ErrIntegrator` a list of error
116
- # objects which will be evaluated in the order specified in the list.
117
- #
118
- # In pyvale errors have a type specified as: random / systematic
119
- # (`EErrorType`) and a dependence `EErrDependence` as: independent /
120
- # dependent. When analysing errors all random all systematic errors are
121
- # grouped and summed together.
122
- #
123
- # The error dependence determines if an error is
124
- # calculated based on the truth (independent) or the accumulated measurement
125
- # based on all previous errors in the chain (dependent). Some errors are
126
- # purely independent such as random noise with a normal distribution with a
127
- # set standard devitation. An example of an error that is dependent would be
128
- # saturation which must be place last in the error chain and will clamp the
129
- # final sensor value to be within the specified bounds.
130
- #
131
- # pyvale provides a library of different random `ErrRand*` and systematic
132
- # `ErrSys*` errors which can be found listed in the docs. In the next
133
- # example we will explore the error library but for now we will specify some
134
- # common error types. Try experimenting with the code below to turn the
135
- # different error types off and on to see how it changes the virtual sensor
136
- # measurements.
137
- errors_on = {"sys": True,
138
- "rand": True}
139
-
140
- error_chain = []
141
- if errors_on["sys"]:
142
- # This systematic error is just a constant offset of -5 to all simulated
143
- # measurements. Note that error values should be specified in the same
144
- # units as the simulation.
145
- error_chain.append(pyv.ErrSysOffset(offset=-10.0))
146
-
147
- # This systematic error samples from a uniform probability distribution.
148
- error_chain.append(pyv.ErrSysUnif(low=-10.0,
149
- high=10.0))
150
-
151
- if errors_on["rand"]:
152
- # This random error is generated by sampling from a normal distribution
153
- # with the given standard deviation in simulation units.
154
- error_chain.append(pyv.ErrRandNorm(std=5.0))
155
-
156
- # This random error is generated as a percentage sampled from uniform
157
- # probability distribution
158
- error_chain.append(pyv.ErrRandUnifPercent(low_percent=-5.0,
159
- high_percent=5.0))
160
-
161
-
162
- # By default pyvale does not store all individual error source
163
- # calculations (i.e. only the total random and total systematic error are
164
- # stored) to save memory but this can be changed using `ErrIntOpts`. This
165
- # can also be used to force all errors to behave if they
166
-
167
- if len(error_chain) > 0:
168
- err_int_opts = pyv.ErrIntOpts()
169
- error_integrator = pyv.ErrIntegrator(error_chain,
170
- sensor_data,
171
- tc_array.get_measurement_shape(),
172
- err_int_opts=err_int_opts)
173
- tc_array.set_error_integrator(error_integrator)
174
-
175
-
176
- # Now that we have added our error chain we can run a simulation to sample
177
- # from all our error sources.
178
- measurements = tc_array.calc_measurements()
179
-
180
- # We display the simulation results by printing to the console and by
181
- # plotting the sensor times traces. Try experimenting with the errors above
182
- # to see how the results change.
183
- print("\n"+80*"-")
184
- print("For a virtual sensor: measurement = truth + sysematic error + random error")
185
- print(f"measurements.shape = {measurements.shape} = "+
186
- "(n_sensors,n_field_components,n_timesteps)\n")
187
- print("The truth, systematic error and random error arrays have the same "+
188
- "shape.")
189
-
190
- print(80*"-")
191
-
192
- sens_print: int = 0
193
- time_print: int = 5
194
- comp_print: int = 0
195
-
196
- print(f"These are the last {time_print} virtual measurements of sensor "
197
- + f"{sens_print}:")
198
-
199
- pyv.print_measurements(sens_array=tc_array,
200
- sensors=(sens_print,sens_print+1),
201
- components=(comp_print,comp_print+1),
202
- time_steps=(measurements.shape[2]-time_print,
203
- measurements.shape[2]))
204
- print(80*"-")
205
-
206
- (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
207
-
208
- save_traces = output_path/"customsensors_ex1_3_sensortraces.png"
209
- fig.savefig(save_traces, dpi=300, bbox_inches="tight")
210
- fig.savefig(save_traces.with_suffix(".svg"), dpi=300, bbox_inches="tight")
211
-
212
- plt.show()
213
-
214
-
215
- if __name__ == "__main__":
216
- main()
@@ -5,8 +5,9 @@
5
5
  # ==============================================================================
6
6
 
7
7
  """
8
- Pyvale example: Overview of the basic error library
9
- --------------------------------------------------------------------------------
8
+ Basics: Overview of the basic error library
9
+ ================================================================================
10
+
10
11
  Building on what we learned in examples 1.1-1.3 we now have a look at the basic
11
12
  error library for pyvale. The sensor error models in pyvale are grouped into the
12
13
  types of random (`ErrRand*`) and systematic (`ErrSys*`). In this example we will
@@ -31,123 +32,126 @@ import mooseherder as mh
31
32
  import pyvale as pyv
32
33
 
33
34
 
34
- def main() -> None:
35
- # First we use everything we learned from the first three examples to build
36
- # a thermocouple sensor array for the same 3D thermal simulation we have
37
- # analysed in the previous example.
38
- data_path = pyv.DataSet.thermal_3d_path()
39
- sim_data = mh.ExodusReader(data_path).read_all_sim_data()
40
- sim_data = pyv.scale_length_units(scale=1000.0,
41
- sim_data=sim_data,
42
- disp_comps=None)
43
- n_sens = (1,4,1)
44
- x_lims = (12.5,12.5)
45
- y_lims = (0.0,33.0)
46
- z_lims = (0.0,12.0)
47
- sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
48
-
49
- sample_times = np.linspace(0.0,np.max(sim_data.time),50) # | None
50
-
51
- sensor_data = pyv.SensorData(positions=sens_pos,
52
- sample_times=sample_times)
53
-
54
- field_key: str = "temperature"
55
- tc_array = pyv.SensorArrayFactory \
56
- .thermocouples_no_errs(sim_data,
57
- sensor_data,
58
- elem_dims=3,
59
- field_name=field_key)
60
-
61
- # Now we have our thermocouple array applied to our simulation without any
62
- # errors we can build a custom chain of basic errors. Here we will start by
63
- # adding a series of systematic errors that are independent:
64
- err_chain = []
65
-
66
- # For probability sampling systematic errors the distribution is sampled to
67
- # provide an offset which is assumed to be constant over all sensor sampling
68
- # times. This is different to random errors which are sampled to provide a
69
- # different error for each sensor and time step.
70
-
71
- # These systematic errors provide a constant offset to all measurements in
72
- # simulation units or as a percentage.
73
- err_chain.append(pyv.ErrSysOffset(offset=-10.0))
74
- err_chain.append(pyv.ErrSysOffsetPercent(offset_percent=-1.0))
75
-
76
- # These systematic errors are sampled from a uniform or normal probability
77
- # distribution either in simulation units or as a percentage.
78
- err_chain.append(pyv.ErrSysUnif(low=-1.0,
79
- high=1.0))
80
- err_chain.append(pyv.ErrSysUnifPercent(low_percent=-1.0,
81
- high_percent=1.0))
82
- err_chain.append(pyv.ErrSysNorm(std=1.0))
83
- err_chain.append(pyv.ErrSysNormPercent(std_percent=1.0))
84
-
85
- # pyvale includes a series of random number generator objects that wrap the
86
- # random number generators from numpy. These are named `Gen*` and can be
87
- # used with an `ErrSysGen` or an `ErrSysGenPercent` object to create custom
88
- # probability distribution sampling errors:
89
- sys_gen = pyv.GenTriangular(left=-1.0,
35
+ #%%
36
+ # First we use everything we learned from the first three examples to build
37
+ # a thermocouple sensor array for the same 3D thermal simulation we have
38
+ # analysed in the previous example.
39
+ data_path = pyv.DataSet.thermal_3d_path()
40
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
41
+ sim_data = pyv.scale_length_units(scale=1000.0,
42
+ sim_data=sim_data,
43
+ disp_comps=None)
44
+ n_sens = (1,4,1)
45
+ x_lims = (12.5,12.5)
46
+ y_lims = (0.0,33.0)
47
+ z_lims = (0.0,12.0)
48
+ sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
49
+
50
+ sample_times = np.linspace(0.0,np.max(sim_data.time),50) # | None
51
+
52
+ sensor_data = pyv.SensorData(positions=sens_pos,
53
+ sample_times=sample_times)
54
+
55
+ field_key: str = "temperature"
56
+ tc_array = pyv.SensorArrayFactory \
57
+ .thermocouples_no_errs(sim_data,
58
+ sensor_data,
59
+ elem_dims=3,
60
+ field_name=field_key)
61
+
62
+ #%%
63
+ # Now we have our thermocouple array applied to our simulation without any
64
+ # errors we can build a custom chain of basic errors. Here we will start by
65
+ # adding a series of systematic errors that are independent:
66
+ err_chain = []
67
+
68
+ #%%
69
+ # For probability sampling systematic errors the distribution is sampled to
70
+ # provide an offset which is assumed to be constant over all sensor sampling
71
+ # times. This is different to random errors which are sampled to provide a
72
+ # different error for each sensor and time step.
73
+ #
74
+ # These systematic errors provide a constant offset to all measurements in
75
+ # simulation units or as a percentage.
76
+ err_chain.append(pyv.ErrSysOffset(offset=-10.0))
77
+ err_chain.append(pyv.ErrSysOffsetPercent(offset_percent=-1.0))
78
+
79
+ #%%
80
+ # These systematic errors are sampled from a uniform or normal probability
81
+ # distribution either in simulation units or as a percentage.
82
+ err_chain.append(pyv.ErrSysUnif(low=-1.0,
83
+ high=1.0))
84
+ err_chain.append(pyv.ErrSysUnifPercent(low_percent=-1.0,
85
+ high_percent=1.0))
86
+ err_chain.append(pyv.ErrSysNorm(std=1.0))
87
+ err_chain.append(pyv.ErrSysNormPercent(std_percent=1.0))
88
+
89
+ #%%
90
+ # pyvale includes a series of random number generator objects that wrap the
91
+ # random number generators from numpy. These are named `Gen*` and can be
92
+ # used with an `ErrSysGen` or an `ErrSysGenPercent` object to create custom
93
+ # probability distribution sampling errors:
94
+ sys_gen = pyv.GenTriangular(left=-1.0,
95
+ mode=0.0,
96
+ right=1.0)
97
+ err_chain.append(pyv.ErrSysGen(sys_gen))
98
+
99
+ #%%
100
+ # We can also build the equivalent of `ErrSysUnifPercent` above using a
101
+ # `Gen` object inserted into an `ErrSysGenPercent` object:
102
+ unif_gen = pyv.GenUniform(low=-1.0,
103
+ high=1.0)
104
+ err_chain.append(pyv.ErrSysGenPercent(unif_gen))
105
+
106
+ #%%
107
+ # We can also add a series of random errors in a similar manner to the
108
+ # systematic errors above noting that these will generate a new error for
109
+ # each sensor and each time step whereas the systematic error sampling
110
+ # provides a constant shift over all sampling times for each sensor.
111
+ err_chain.append(pyv.ErrRandNorm(std = 2.0))
112
+ err_chain.append(pyv.ErrRandNormPercent(std_percent=2.0))
113
+ err_chain.append(pyv.ErrRandUnif(low=-2.0,high=2.0))
114
+ err_chain.append(pyv.ErrRandUnifPercent(low_percent=-2.0,
115
+ high_percent=2.0))
116
+ rand_gen = pyv.GenTriangular(left=-5.0,
90
117
  mode=0.0,
91
- right=1.0)
92
- err_chain.append(pyv.ErrSysGen(sys_gen))
93
-
94
- # We can also build the equivalent of `ErrSysUnifPercent` above using a
95
- # `Gen` object inserted into an `ErrSysGenPercent` object:
96
- unif_gen = pyv.GenUniform(low=-1.0,
97
- high=1.0)
98
- err_chain.append(pyv.ErrSysGenPercent(unif_gen))
99
-
100
-
101
- # We can also add a series of random errors in a similar manner to the
102
- # systematic errors above noting that these will generate a new error for
103
- # each sensor and each time step whereas the systematic error sampling
104
- # provides a constant shift over all sampling times for each sensor.
105
- err_chain.append(pyv.ErrRandNorm(std = 2.0))
106
- err_chain.append(pyv.ErrRandNormPercent(std_percent=2.0))
107
- err_chain.append(pyv.ErrRandUnif(low=-2.0,high=2.0))
108
- err_chain.append(pyv.ErrRandUnifPercent(low_percent=-2.0,
109
- high_percent=2.0))
110
- rand_gen = pyv.GenTriangular(left=-5.0,
111
- mode=0.0,
112
- right=5.0)
113
- err_chain.append(pyv.ErrRandGenerator(rand_gen))
114
-
115
- # Finally we add some dependent systematic errors including rounding errors,
116
- # digitisation and saturation. Note that the saturation error must be placed
117
- # last in the error chain. Try changing some of these values to see how the
118
- # sensor traces change - particularly the saturation error.
119
- err_chain.append(pyv.ErrSysRoundOff(pyv.ERoundMethod.ROUND,0.1))
120
- err_chain.append(pyv.ErrSysDigitisation(bits_per_unit=2**16/100))
121
- err_chain.append(pyv.ErrSysSaturation(meas_min=0.0,meas_max=400.0))
122
-
123
- err_int = pyv.ErrIntegrator(err_chain,
124
- sensor_data,
125
- tc_array.get_measurement_shape())
126
- tc_array.set_error_integrator(err_int)
127
-
128
- # Now we can run the sensor simulation and display the results to see the
129
- # different error sources as we have done in previous examples.
130
- measurements = tc_array.calc_measurements()
131
-
132
- print(80*"-")
133
-
134
- sens_print: int = 0
135
- time_print: int = 5
136
- comp_print: int = 0
137
-
138
- print(f"These are the last {time_print} virtual measurements of sensor "
139
- + f"{sens_print}:")
140
-
141
- pyv.print_measurements(sens_array=tc_array,
142
- sensors=(sens_print,sens_print+1),
143
- components=(comp_print,comp_print+1),
144
- time_steps=(measurements.shape[2]-time_print,
145
- measurements.shape[2]))
146
- print(80*"-")
147
-
148
- pyv.plot_time_traces(tc_array,field_key)
149
- plt.show()
150
-
151
-
152
- if __name__ == '__main__':
153
- main()
118
+ right=5.0)
119
+ err_chain.append(pyv.ErrRandGenerator(rand_gen))
120
+
121
+ #%%
122
+ # Finally we add some dependent systematic errors including rounding errors,
123
+ # digitisation and saturation. Note that the saturation error must be placed
124
+ # last in the error chain. Try changing some of these values to see how the
125
+ # sensor traces change - particularly the saturation error.
126
+ err_chain.append(pyv.ErrSysRoundOff(pyv.ERoundMethod.ROUND,0.1))
127
+ err_chain.append(pyv.ErrSysDigitisation(bits_per_unit=2**16/100))
128
+ err_chain.append(pyv.ErrSysSaturation(meas_min=0.0,meas_max=400.0))
129
+
130
+ err_int = pyv.ErrIntegrator(err_chain,
131
+ sensor_data,
132
+ tc_array.get_measurement_shape())
133
+ tc_array.set_error_integrator(err_int)
134
+
135
+ #%%
136
+ # Now we can run the sensor simulation and display the results to see the
137
+ # different error sources as we have done in previous examples.
138
+ measurements = tc_array.calc_measurements()
139
+
140
+ print(80*"-")
141
+
142
+ sens_print = 0
143
+ comp_print = 0
144
+ time_last = 5
145
+ time_print = slice(measurements.shape[2]-time_last,measurements.shape[2])
146
+
147
+
148
+ print(f"These are the last {time_last} virtual measurements of sensor "
149
+ + f"{sens_print}:")
150
+
151
+ pyv.print_measurements(tc_array,sens_print,comp_print,time_print)
152
+
153
+ print(80*"-")
154
+
155
+ pyv.plot_time_traces(tc_array,field_key)
156
+ plt.show()
157
+