pyvale 2025.5.3__cp311-cp311-win32.whl → 2025.7.1__cp311-cp311-win32.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/__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.cp311-win32.pyd +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.cp311-win32.pyd +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 +93 -56
  91. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.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.1.dist-info}/licenses/LICENSE +0 -0
  95. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/top_level.txt +0 -0
@@ -5,11 +5,12 @@
5
5
  # ==============================================================================
6
6
 
7
7
  """
8
- Pyvale example: Basics of pyvale point sensor simualtion
9
- --------------------------------------------------------------------------------
10
- In this example we introduce the basic features of pyvale for point sensor
8
+ Basics: Pyvale point sensor simulation
9
+ ================================================================================
10
+
11
+ In this example we introduce the basic features of `pyvale` for point sensor
11
12
  simulation. We demonstrate quick sensor array construction with defaults using
12
- the pyvale sensor array factory. Finally we run a sensor simulation and display
13
+ the `pyvale` sensor array factory. Finally we run a sensor simulation and display
13
14
  the output.
14
15
 
15
16
  Test case: Scalar field point sensors (thermocouples) on a 2D thermal simulation
@@ -20,112 +21,122 @@ import matplotlib.pyplot as plt
20
21
  import mooseherder as mh
21
22
  import pyvale as pyv
22
23
 
23
-
24
- def main() -> None:
25
-
26
- # Here we load a pre-generated MOOSE finite element simulation dataset that
27
- # comes packaged with pyvale. The simulation is a 2D rectangular plate with
28
- # a bi-directional temperature gradient. You can replace this with the path
29
- # to your own MOOSE simulation with exodus output (*.e). Note that the
30
- # field_key must match the name of your variable in your MOOSE simulation.
31
- data_path = pyv.DataSet.thermal_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
- # Scale to mm to make 3D visualisation scaling easier as pyvista scales
36
- # everything to unity
37
- sim_data = pyv.scale_length_units(scale=1000.0,
38
- sim_data=sim_data,
39
- disp_comps=None)
40
-
41
- # We now use a helper function to create a grid of sensor locations but we
42
- # could have also manually built the numpy array of sensor locations which
43
- # has the shape=(num_sensors,coord[x,y,z]).
44
- n_sens = (3,2,1)
45
- x_lims = (0.0,100.0)
46
- y_lims = (0.0,50.0)
47
- z_lims = (0.0,0.0)
48
- sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
49
-
50
- # This dataclass contains the parameters to build our sensor array. We can
51
- # also customise the output frequency, the sensor area and the sensor
52
- # orientation. For now we will use the defaults which assumes an ideal point
53
- # sensor sampling at the simulation time steps.
54
- sens_data = pyv.SensorData(positions=sens_pos)
55
-
56
- # Now that we have our sensor locations we can use the sensor factory to
57
- # build a basic thermocouple array with some useful defaults. In later
58
- # examples we will see how to customise sensor parameters and errors.
59
- # This basic thermocouple array includes a 1% systematic and random error.
60
- # If you want to remove the simulated errors and just interpolate at the
61
- # sensor locations then user `.thermocouples_no_errs()`.
62
- field_key: str = "temperature"
63
- tc_array = pyv.SensorArrayFactory \
64
- .thermocouples_basic_errs(sim_data,
65
- sens_data,
66
- elem_dims=2,
67
- field_name=field_key)
68
-
69
- # We have built our sensor array so now we can call `calc_measurements()` to
70
- # generate simulated sensor traces.
71
- measurements = tc_array.calc_measurements()
72
- print(f"\nMeasurements for last sensor:\n{measurements[-1,0,:]}\n")
73
-
74
- # We can now visualise the sensor locations on the simulation mesh and the
75
- # simulated sensor traces using pyvale's visualisation tools which use
76
- # pyvista for meshes and matplotlib for sensor traces. pyvale will return
77
- # plot and axes objects to the user allowing additional customisation using
78
- # pyvista and matplotlib. This also means that we need to call `.show()`
79
- # ourselves to display the figure as pyvale does not do this for us.
80
-
81
- # If we are going to save figures we need to make sure the path exists. Here
82
- # we create a default output path based on your current working directory.
83
- output_path = Path.cwd() / "pyvale-output"
84
- if not output_path.is_dir():
85
- output_path.mkdir(parents=True, exist_ok=True)
86
-
87
- # This creates a pyvista visualisation of the sensor locations on the
88
- # simulation mesh. The plot will can be shown in interactive mode by calling
89
- # `pv_plot.show()` alternatively
90
- pv_plot = pyv.plot_point_sensors_on_sim(tc_array,field_key)
91
-
92
- # We determined manually by moving camera in interative mode and then
93
- # printing camera position to console after window close, as below.
94
- pv_plot.camera_position = [(-7.547, 59.753, 134.52),
95
- (41.916, 25.303, 9.297),
96
- (0.0810, 0.969, -0.234)]
97
-
98
-
99
- # This allows us to save a vector graphic and raster graphic showing the
100
- # sensor locations on the simulation mesh
101
- save_render = output_path / "basics_ex1_1_sensorlocs.svg"
102
- pv_plot.save_graphic(save_render) # only for .svg .eps .ps .pdf .tex
103
- pv_plot.screenshot(save_render.with_suffix(".png"))
104
-
105
- # We can also show the simulation and sensor locations in interative mode
106
- # by calling `.show()`
107
- pv_plot.show()
108
-
109
- print(80*"-")
110
- print("Camera position after interative view:")
111
- print(pv_plot.camera_position)
112
- print(80*"-"+"\n")
113
-
114
- # This plots the time traces for all of our sensors. The solid line shows
115
- # the 'truth' interpolated from the simulation and the dashed line with
116
- # markers shows the simulated sensor traces. In later examples we will see
117
- # how to configure this plot but for now we note we that we are returned a
118
- # matplotlib figure and axes object which allows for further customisation.
119
- (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
120
-
121
- # We can also save the sensor trace plot as a vector and raster graphic
122
- save_traces = output_path/"basics_ex1_1_sensortraces.png"
123
- fig.savefig(save_traces, dpi=300, bbox_inches="tight")
124
- fig.savefig(save_traces.with_suffix(".svg"), dpi=300, bbox_inches="tight")
125
-
126
- # The trace plot can also be shown in interactive mode using `plt.show()`
127
- plt.show()
128
-
129
-
130
- if __name__ == "__main__":
131
- main()
24
+ #%%
25
+ # Here we load a pre-generated MOOSE finite element simulation dataset that
26
+ # comes packaged with pyvale. The simulation is a 2D rectangular plate with
27
+ # a bi-directional temperature gradient. You can replace this with the path
28
+ # to your own MOOSE simulation with exodus output (*.e). Note that the
29
+ # field_key must match the name of your variable in your MOOSE simulation.
30
+ # We use `mooseherder` to load the exodus file into a `SimData` object.
31
+ data_path = pyv.DataSet.thermal_2d_path()
32
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
33
+
34
+ #%%
35
+ # Scale to mm to make 3D visualisation scaling easier as pyvista scales
36
+ # everything to unity
37
+ sim_data = pyv.scale_length_units(scale=1000.0,
38
+ sim_data=sim_data,
39
+ disp_comps=None)
40
+
41
+ #%%
42
+ # We now use a helper function to create a grid of sensor locations but we
43
+ # could have also manually built the numpy array of sensor locations which
44
+ # has the shape=(num_sensors,coord[x,y,z]).
45
+ n_sens = (3,2,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
+ #%%
52
+ # This dataclass contains the parameters to build our sensor array. We can
53
+ # also customise the output frequency, the sensor area and the sensor
54
+ # orientation. For now we will use the defaults which assumes an ideal point
55
+ # sensor sampling at the simulation time steps.
56
+ sens_data = pyv.SensorData(positions=sens_pos)
57
+
58
+ #%%
59
+ # Now that we have our sensor locations we can use the sensor factory to
60
+ # build a basic thermocouple array with some useful defaults. In later
61
+ # examples we will see how to customise sensor parameters and errors.
62
+ # This basic thermocouple array includes a 1% systematic and random error.
63
+ # If you want to remove the simulated errors and just interpolate at the
64
+ # sensor locations then user `.thermocouples_no_errs()`.
65
+ field_key: str = "temperature"
66
+ tc_array = pyv.SensorArrayFactory \
67
+ .thermocouples_basic_errs(sim_data,
68
+ sens_data,
69
+ elem_dims=2,
70
+ field_name=field_key)
71
+
72
+ #%%
73
+ # We have built our sensor array so now we can call `calc_measurements()` to
74
+ # generate simulated sensor traces.
75
+ measurements = tc_array.calc_measurements()
76
+ print(f"\nMeasurements for last sensor:\n{measurements[-1,0,:]}\n")
77
+
78
+ #%%
79
+ # We can now visualise the sensor locations on the simulation mesh and the
80
+ # simulated sensor traces using pyvale's visualisation tools which use
81
+ # pyvista for meshes and matplotlib for sensor traces. pyvale will return
82
+ # plot and axes objects to the user allowing additional customisation using
83
+ # pyvista and matplotlib. This also means that we need to call `.show()`
84
+ # ourselves to display the figure as pyvale does not do this for us.
85
+ #
86
+ # If we are going to save figures we need to make sure the path exists. Here
87
+ # we create a default output path based on your current working directory.
88
+ output_path = Path.cwd() / "pyvale-output"
89
+ if not output_path.is_dir():
90
+ output_path.mkdir(parents=True, exist_ok=True)
91
+
92
+ #%%
93
+ # This creates a pyvista visualisation of the sensor locations on the
94
+ # simulation mesh. The plot will can be shown in interactive mode by calling
95
+ # `pv_plot.show()`.
96
+ pv_plot = pyv.plot_point_sensors_on_sim(tc_array,field_key)
97
+
98
+ #%%
99
+ # We determined manually by moving camera in interative mode and then
100
+ # printing camera position to console after window close, as below.
101
+ pv_plot.camera_position = [(-7.547, 59.753, 134.52),
102
+ (41.916, 25.303, 9.297),
103
+ (0.0810, 0.969, -0.234)]
104
+
105
+ #%%
106
+ # This allows us to save a vector graphic and raster graphic showing the
107
+ # sensor locations on the simulation mesh
108
+ save_render = output_path / "basics_ex1_1_sensorlocs.svg"
109
+ pv_plot.save_graphic(save_render) # only for .svg .eps .ps .pdf .tex
110
+ pv_plot.screenshot(save_render.with_suffix(".png"))
111
+
112
+ #%%
113
+ # We can also show the simulation and sensor locations in interative mode
114
+ # by calling `.show()`
115
+ pv_plot.show()
116
+
117
+ print(80*"-")
118
+ print("Camera position after interactive view:")
119
+ print(pv_plot.camera_position)
120
+ print(80*"-"+"\n")
121
+
122
+ #%%
123
+ # This plots the time traces for all of our sensors. The solid line shows
124
+ # the 'truth' interpolated from the simulation and the dashed line with
125
+ # markers shows the simulated sensor traces. In later examples we will see
126
+ # how to configure this plot but for now we note we that we are returned a
127
+ # matplotlib figure and axes object which allows for further customisation.
128
+ (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
129
+
130
+ #%%
131
+ # We can also save the sensor trace plot as a vector and raster graphic
132
+ save_traces = output_path/"basics_ex1_1_sensortraces.png"
133
+ fig.savefig(save_traces, dpi=300, bbox_inches="tight")
134
+ fig.savefig(save_traces.with_suffix(".svg"), dpi=300, bbox_inches="tight")
135
+
136
+ #%%
137
+ # The trace plot can also be shown in interactive mode using `plt.show()`
138
+ plt.show()
139
+
140
+ #%%
141
+ # That is it for this example. In the next one we will look at the `pyvale`
142
+ # simulated measurement model.
@@ -5,10 +5,11 @@
5
5
  # ==============================================================================
6
6
 
7
7
  """
8
- Pyvale example: Sensor model & `get_measurements()` vs `calc_measurements()`
9
- --------------------------------------------------------------------------------
10
- In this example we explain the pyvale virtual sensor measurement model. For a
11
- virtual sensor in pyvale a measurement is defined as measurement = truth +
8
+ Basics: Sensor model & `get_measurements()` vs `calc_measurements()`
9
+ ================================================================================
10
+
11
+ In this example we explain the `pyvale` virtual sensor measurement model. For a
12
+ virtual sensor in `pyvale` a measurement is defined as measurement = truth +
12
13
  systematic error + random error. Sources of systematic errors include: spatial/
13
14
  temporal averaging, uncertainty in position / sampling time / orientation,
14
15
  digitisation, saturation, and calibration. Sources of random error are generally
@@ -19,10 +20,10 @@ However, systematic errors cannot easily be accounted for without a forward
19
20
  model of the source of the error. Characterising the contribution of systematic
20
21
  errors to the total measurement error is a key application of `pyvale`.
21
22
 
22
- between the `get_measurements()` and `calc_measurements()` methods for a sensor
23
- array. Calling `get_measurements()` retrieves the results for the current
24
- simulated experiment whereas calling `calc_measurements()` will generate a new
25
- simulated experiment by sampling / calculating the systematic and random errors.
23
+ A sensor array has two key methods: `get_measurements()` and `calc_measurements`
24
+ . Calling `get_measurements()` retrieves the results for the current simulated
25
+ experiment whereas calling `calc_measurements()` will generate a new simulated
26
+ experiment by sampling / calculating the systematic and random errors.
26
27
 
27
28
  Test case: Scalar field point sensors (thermocouples) on a 2D thermal simulation
28
29
  """
@@ -32,127 +33,118 @@ import mooseherder as mh
32
33
  import pyvale as pyv
33
34
 
34
35
 
35
- def main() -> None:
36
- # The first part of this example is the similar to basics example 1.1, so
37
- # feel free to skip to after the first call to `calc_measurements()`.
38
-
39
- # Here we load a pre-generated MOOSE finite element simulation dataset that
40
- # comes packaged with pyvale. The simulation is a 2D rectangular plate with
41
- # a bi-directional temperature gradient.
42
- data_path = pyv.DataSet.thermal_2d_path()
43
- sim_data = mh.ExodusReader(data_path).read_all_sim_data()
44
- field_key: str = "temperature"
45
- # Scale to mm to make 3D visualisation scaling easier as pyvista scales
46
- # everything to unity
47
- sim_data = pyv.scale_length_units(scale=1000.0,
48
- sim_data=sim_data,
49
- disp_comps=None)
50
-
51
- # We now use a helper function to create a grid of sensor locations but we
52
- # could have also manually built the numpy array of sensor locations which
53
- # has the shape=(num_sensors,coord[x,y,z]).
54
- n_sens = (4,1,1)
55
- x_lims = (0.0,100.0)
56
- y_lims = (0.0,50.0)
57
- z_lims = (0.0,0.0)
58
- sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
59
-
60
- # This dataclass contains the parameters to build our sensor array. We can
61
- # also customise the output frequency, the sensor area and the sensor
62
- # orientation. For now we will use the defaults which assumes an ideal point
63
- # sensor sampling at the simulation time steps.
64
- sens_data = pyv.SensorData(positions=sens_pos)
65
-
66
- # Now that we have our sensor locations we can use the sensor factory to
67
- # build a basic thermocouple array with some useful defaults. In later
68
- # examples we will see how to customise sensor parameters and errors.
69
- # This basic thermocouple array includes a 5% systematic and random error -
70
- # We are specifically using exaggerated errors here for visualisation.
71
- tc_array = pyv.SensorArrayFactory \
72
- .thermocouples_basic_errs(sim_data,
73
- sens_data,
74
- elem_dims=2,
75
- field_name=field_key,
76
- errs_pc=5.0)
77
-
78
-
79
- # We have built our sensor array so now we can call `calc_measurements()` to
80
- # generate simulated sensor traces.
81
- measurements = tc_array.calc_measurements()
82
-
83
- # From here we are going to experiment with repeated calls to
84
- # `calc_measurements()` and `get_measurements()` for our sensor array. We
85
- # will print the results to the console as well as plotting time traces of
86
- # the simulated sensor output. All further explanations are in the print
87
- # statements below.
88
-
89
- print("\n"+80*"-")
90
- print("For a sensor array: "
91
- + "measurement = truth + sysematic error + random error")
92
- print(f"\nmeasurements.shape = {measurements.shape} = "
93
- + "(n_sensors,n_field_components,n_timesteps)\n")
94
- print("Here we have a scalar temperature field so only 1 field component.")
95
- print("The truth, systematic error and random error arrays all have the "+
96
- "same shape.")
97
-
98
- sens_print: int = 0
99
- time_print: int = 5
100
- comp_print: int = 0
101
-
102
- print(80*"-")
103
- print(f"Looking at the last {time_print} virtual measurements" +
104
- f" of sensor {sens_print}:")
105
-
106
- pyv.print_measurements(sens_array=tc_array,
107
- sensors=(sens_print,sens_print+1),
108
- components=(comp_print,comp_print+1),
109
- time_steps=(measurements.shape[2]-time_print,
110
- measurements.shape[2]))
111
- print(80*"-")
112
- print("If we call the `calc_measurements()` method then the errors are "
113
- + "re-calculated.")
114
- measurements = tc_array.calc_measurements()
115
-
116
- pyv.print_measurements(sens_array=tc_array,
117
- sensors=(sens_print,sens_print+1),
118
- components=(comp_print,comp_print+1),
119
- time_steps=(measurements.shape[2]-time_print,
120
- measurements.shape[2]))
121
-
122
- (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
123
- ax.set_title("Exp 1: called calc_measurements()")
124
-
125
- print(80*"-")
126
- print("If we call the `get_measurements()` method then the errors are the "
127
- + "same:")
128
- measurements = tc_array.get_measurements()
129
-
130
- pyv.print_measurements(sens_array=tc_array,
131
- sensors=(sens_print,sens_print+1),
132
- components=(comp_print,comp_print+1),
133
- time_steps=(measurements.shape[2]-time_print,
134
- measurements.shape[2]))
135
-
136
- (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
137
- ax.set_title("Exp 2: called get_measurements()")
138
-
139
- print(80*"-")
140
- print("If we call the `calc_measurements()` method again we generate / "
141
- "sample new errors:")
142
- measurements = tc_array.calc_measurements()
143
-
144
- pyv.print_measurements(sens_array=tc_array,
145
- sensors=(sens_print,sens_print+1),
146
- components=(comp_print,comp_print+1),
147
- time_steps=(measurements.shape[2]-time_print,
148
- measurements.shape[2]))
149
-
150
- (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
151
- ax.set_title("Exp 3: called calc_measurements()")
152
-
153
- print(80*"-")
154
-
155
- plt.show()
156
-
157
- if __name__ == "__main__":
158
- main()
36
+ #%%
37
+ # The first part of this example is the similar to basics example 1.1, so
38
+ # feel free to skip to after the first call to `calc_measurements()`.
39
+ #
40
+ # Here we load a pre-generated MOOSE finite element simulation dataset that
41
+ # comes packaged with pyvale. The simulation is a 2D rectangular plate with
42
+ # a bi-directional temperature gradient.
43
+ data_path = pyv.DataSet.thermal_2d_path()
44
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
45
+ field_key: str = "temperature"
46
+
47
+ #%%
48
+ # Scale to mm to make 3D visualisation scaling easier as pyvista scales
49
+ # everything to unity
50
+ sim_data = pyv.scale_length_units(scale=1000.0,
51
+ sim_data=sim_data,
52
+ disp_comps=None)
53
+
54
+ #%%
55
+ # We now use a helper function to create a grid of sensor locations but we
56
+ # could have also manually built the numpy array of sensor locations which
57
+ # has the shape=(num_sensors,coord[x,y,z]).
58
+ n_sens = (4,1,1)
59
+ x_lims = (0.0,100.0)
60
+ y_lims = (0.0,50.0)
61
+ z_lims = (0.0,0.0)
62
+ sens_pos = pyv.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
63
+
64
+ #%%
65
+ # This dataclass contains the parameters to build our sensor array. We can
66
+ # also customise the output frequency, the sensor area and the sensor
67
+ # orientation. For now we will use the defaults which assumes an ideal point
68
+ # sensor sampling at the simulation time steps.
69
+ sens_data = pyv.SensorData(positions=sens_pos)
70
+
71
+ #%%
72
+ # Now that we have our sensor locations we can use the sensor factory to
73
+ # build a basic thermocouple array with some useful defaults. In later
74
+ # examples we will see how to customise sensor parameters and errors.
75
+ # This basic thermocouple array includes a 5% systematic and random error -
76
+ # We are specifically using exaggerated errors here for visualisation.
77
+ tc_array = pyv.SensorArrayFactory \
78
+ .thermocouples_basic_errs(sim_data,
79
+ sens_data,
80
+ elem_dims=2,
81
+ field_name=field_key,
82
+ errs_pc=5.0)
83
+
84
+ #%%
85
+ # We have built our sensor array so now we can call `calc_measurements()` to
86
+ # generate simulated sensor traces.
87
+ measurements = tc_array.calc_measurements()
88
+
89
+ #%%
90
+ # From here we are going to experiment with repeated calls to
91
+ # `calc_measurements()` and `get_measurements()` for our sensor array. We
92
+ # will print the results to the console as well as plotting time traces of
93
+ # the simulated sensor output. All further explanations are in the print
94
+ # statements below.
95
+
96
+ print("\n"+80*"-")
97
+ print("For a sensor array: "
98
+ + "measurement = truth + sysematic error + random error")
99
+ print(f"\nmeasurements.shape = {measurements.shape} = "
100
+ + "(n_sensors,n_field_components,n_timesteps)\n")
101
+ print("Here we have a scalar temperature field so only 1 field component.")
102
+ print("The truth, systematic error and random error arrays all have the "+
103
+ "same shape.")
104
+
105
+ sens_print = 0
106
+ comp_print = 0
107
+ time_last = 5
108
+ time_print = slice(measurements.shape[2]-time_last,measurements.shape[2])
109
+
110
+ print(80*"-")
111
+ print(f"Looking at the last {time_last} virtual measurements for sensor"
112
+ +f" {sens_print}:")
113
+
114
+ pyv.print_measurements(tc_array,sens_print,comp_print,time_print)
115
+
116
+ print(80*"-")
117
+ print("If we call the `calc_measurements()` method then the errors are "
118
+ + "re-calculated.")
119
+ measurements = tc_array.calc_measurements()
120
+
121
+ pyv.print_measurements(tc_array,sens_print,comp_print,time_print)
122
+
123
+ (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
124
+ ax.set_title("Exp 1: called calc_measurements()")
125
+
126
+ print(80*"-")
127
+ print("If we call the `get_measurements()` method then the errors are the "
128
+ + "same:")
129
+ measurements = tc_array.get_measurements()
130
+
131
+ pyv.print_measurements(tc_array,sens_print,comp_print,time_print)
132
+
133
+ (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
134
+ ax.set_title("Exp 2: called get_measurements()")
135
+
136
+ print(80*"-")
137
+ print("If we call the `calc_measurements()` method again we generate / "
138
+ "sample new errors:")
139
+ measurements = tc_array.calc_measurements()
140
+
141
+ pyv.print_measurements(tc_array,sens_print,comp_print,time_print)
142
+
143
+ (fig,ax) = pyv.plot_time_traces(tc_array,field_key)
144
+ ax.set_title("Exp 3: called calc_measurements()")
145
+
146
+ print(80*"-")
147
+
148
+ plt.show()
149
+
150
+