pyvale 2025.7.1__cp311-cp311-win_amd64.whl → 2025.8.1__cp311-cp311-win_amd64.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 (186) hide show
  1. pyvale/__init__.py +12 -92
  2. pyvale/blender/__init__.py +23 -0
  3. pyvale/{pyvaleexceptions.py → blender/blenderexceptions.py} +0 -3
  4. pyvale/{blenderlightdata.py → blender/blenderlightdata.py} +3 -3
  5. pyvale/{blendermaterialdata.py → blender/blendermaterialdata.py} +1 -1
  6. pyvale/{blenderrenderdata.py → blender/blenderrenderdata.py} +5 -3
  7. pyvale/{blenderscene.py → blender/blenderscene.py} +33 -30
  8. pyvale/{blendertools.py → blender/blendertools.py} +14 -10
  9. pyvale/dataset/__init__.py +7 -0
  10. pyvale/dataset/dataset.py +443 -0
  11. pyvale/dic/__init__.py +20 -0
  12. pyvale/dic/cpp/dicfourier.cpp +36 -4
  13. pyvale/dic/cpp/dicinterpolator.cpp +56 -1
  14. pyvale/dic/cpp/dicmain.cpp +24 -19
  15. pyvale/dic/cpp/dicoptimizer.cpp +6 -1
  16. pyvale/dic/cpp/dicscanmethod.cpp +32 -32
  17. pyvale/dic/cpp/dicsignalhandler.cpp +16 -0
  18. pyvale/dic/cpp/dicstrain.cpp +7 -3
  19. pyvale/dic/cpp/dicutil.cpp +79 -23
  20. pyvale/{dic2d.py → dic/dic2d.py} +51 -29
  21. pyvale/dic/dic2dconv.py +6 -0
  22. pyvale/dic/dic2dcpp.cp311-win_amd64.pyd +0 -0
  23. pyvale/{dicchecks.py → dic/dicchecks.py} +28 -16
  24. pyvale/dic/dicdataimport.py +370 -0
  25. pyvale/{dicregionofinterest.py → dic/dicregionofinterest.py} +169 -12
  26. pyvale/{dicresults.py → dic/dicresults.py} +4 -1
  27. pyvale/{dicstrain.py → dic/dicstrain.py} +9 -9
  28. pyvale/examples/basics/{ex1_1_basicscalars_therm2d.py → ex1a_basicscalars_therm2d.py} +12 -9
  29. pyvale/examples/basics/{ex1_2_sensormodel_therm2d.py → ex1b_sensormodel_therm2d.py} +17 -14
  30. pyvale/examples/basics/{ex1_3_customsens_therm3d.py → ex1c_customsens_therm3d.py} +27 -24
  31. pyvale/examples/basics/{ex1_4_basicerrors_therm3d.py → ex1d_basicerrors_therm3d.py} +32 -29
  32. pyvale/examples/basics/{ex1_5_fielderrs_therm3d.py → ex1e_fielderrs_therm3d.py} +19 -15
  33. pyvale/examples/basics/{ex1_6_caliberrs_therm2d.py → ex1f_caliberrs_therm2d.py} +20 -16
  34. pyvale/examples/basics/{ex1_7_spatavg_therm2d.py → ex1g_spatavg_therm2d.py} +19 -16
  35. pyvale/examples/basics/{ex2_1_basicvectors_disp2d.py → ex2a_basicvectors_disp2d.py} +13 -10
  36. pyvale/examples/basics/{ex2_2_vectorsens_disp2d.py → ex2b_vectorsens_disp2d.py} +19 -15
  37. pyvale/examples/basics/{ex2_3_sensangle_disp2d.py → ex2c_sensangle_disp2d.py} +21 -18
  38. pyvale/examples/basics/{ex2_4_chainfielderrs_disp2d.py → ex2d_chainfielderrs_disp2d.py} +31 -29
  39. pyvale/examples/basics/{ex2_5_vectorfields3d_disp3d.py → ex2e_vectorfields3d_disp3d.py} +21 -18
  40. pyvale/examples/basics/{ex3_1_basictensors_strain2d.py → ex3a_basictensors_strain2d.py} +16 -14
  41. pyvale/examples/basics/{ex3_2_tensorsens2d_strain2d.py → ex3b_tensorsens2d_strain2d.py} +17 -14
  42. pyvale/examples/basics/{ex3_3_tensorsens3d_strain3d.py → ex3c_tensorsens3d_strain3d.py} +25 -22
  43. pyvale/examples/basics/{ex4_1_expsim2d_thermmech2d.py → ex4a_expsim2d_thermmech2d.py} +17 -14
  44. pyvale/examples/basics/{ex4_2_expsim3d_thermmech3d.py → ex4b_expsim3d_thermmech3d.py} +37 -34
  45. pyvale/examples/basics/ex5_nomesh.py +24 -0
  46. pyvale/examples/dic/ex1_2_blenderdeformed.py +174 -0
  47. pyvale/examples/dic/ex1_region_of_interest.py +6 -3
  48. pyvale/examples/dic/ex2_plate_with_hole.py +21 -18
  49. pyvale/examples/dic/ex3_plate_with_hole_strain.py +8 -6
  50. pyvale/examples/dic/ex4_dic_blender.py +17 -15
  51. pyvale/examples/dic/ex5_dic_challenge.py +19 -14
  52. pyvale/examples/genanalyticdata/ex1_1_scalarvisualisation.py +16 -10
  53. pyvale/examples/genanalyticdata/ex1_2_scalarcasebuild.py +3 -3
  54. pyvale/examples/genanalyticdata/ex2_1_analyticsensors.py +29 -23
  55. pyvale/examples/genanalyticdata/ex2_2_analyticsensors_nomesh.py +67 -0
  56. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +12 -9
  57. pyvale/examples/mooseherder/ex0_create_moose_config.py +65 -0
  58. pyvale/examples/mooseherder/ex1a_modify_moose_input.py +71 -0
  59. pyvale/examples/mooseherder/ex1b_modify_gmsh_input.py +69 -0
  60. pyvale/examples/mooseherder/ex2a_run_moose_once.py +80 -0
  61. pyvale/examples/mooseherder/ex2b_run_gmsh_once.py +64 -0
  62. pyvale/examples/mooseherder/ex2c_run_both_once.py +114 -0
  63. pyvale/examples/mooseherder/ex3_run_moose_seq_para.py +157 -0
  64. pyvale/examples/mooseherder/ex4_run_gmsh-moose_seq_para.py +176 -0
  65. pyvale/examples/mooseherder/ex5_run_moose_paramulti.py +136 -0
  66. pyvale/examples/mooseherder/ex6_read_moose_exodus.py +163 -0
  67. pyvale/examples/mooseherder/ex7a_read_moose_herd_results.py +153 -0
  68. pyvale/examples/mooseherder/ex7b_read_multi_herd_results.py +116 -0
  69. pyvale/examples/mooseherder/ex7c_read_multi_gmshmoose_results.py +127 -0
  70. pyvale/examples/mooseherder/ex7d_readconfig_multi_gmshmoose_results.py +143 -0
  71. pyvale/examples/mooseherder/ex8_read_existing_sweep_output.py +72 -0
  72. pyvale/examples/renderblender/ex1_1_blenderscene.py +24 -20
  73. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +22 -18
  74. pyvale/examples/renderblender/ex2_1_stereoscene.py +36 -29
  75. pyvale/examples/renderblender/ex2_2_stereodeformed.py +26 -20
  76. pyvale/examples/renderblender/ex3_1_blendercalibration.py +24 -17
  77. pyvale/examples/renderrasterisation/ex_rastenp.py +14 -12
  78. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +14 -15
  79. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +13 -11
  80. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +13 -11
  81. pyvale/mooseherder/__init__.py +32 -0
  82. pyvale/mooseherder/directorymanager.py +416 -0
  83. pyvale/mooseherder/exodusreader.py +763 -0
  84. pyvale/mooseherder/gmshrunner.py +163 -0
  85. pyvale/mooseherder/inputmodifier.py +236 -0
  86. pyvale/mooseherder/mooseconfig.py +226 -0
  87. pyvale/mooseherder/mooseherd.py +527 -0
  88. pyvale/mooseherder/mooserunner.py +303 -0
  89. pyvale/mooseherder/outputreader.py +22 -0
  90. pyvale/mooseherder/simdata.py +92 -0
  91. pyvale/mooseherder/simrunner.py +31 -0
  92. pyvale/mooseherder/sweepreader.py +356 -0
  93. pyvale/mooseherder/sweeptools.py +76 -0
  94. pyvale/sensorsim/__init__.py +82 -0
  95. pyvale/{camera.py → sensorsim/camera.py} +7 -7
  96. pyvale/{camerasensor.py → sensorsim/camerasensor.py} +7 -7
  97. pyvale/{camerastereo.py → sensorsim/camerastereo.py} +2 -2
  98. pyvale/{cameratools.py → sensorsim/cameratools.py} +4 -4
  99. pyvale/{cython → sensorsim/cython}/rastercyth.c +596 -596
  100. pyvale/{cython → sensorsim/cython}/rastercyth.cp311-win_amd64.pyd +0 -0
  101. pyvale/{cython → sensorsim/cython}/rastercyth.py +16 -17
  102. pyvale/{errorcalculator.py → sensorsim/errorcalculator.py} +1 -1
  103. pyvale/{errorintegrator.py → sensorsim/errorintegrator.py} +2 -2
  104. pyvale/{errorrand.py → sensorsim/errorrand.py} +4 -4
  105. pyvale/{errorsyscalib.py → sensorsim/errorsyscalib.py} +2 -2
  106. pyvale/{errorsysdep.py → sensorsim/errorsysdep.py} +2 -2
  107. pyvale/{errorsysfield.py → sensorsim/errorsysfield.py} +8 -8
  108. pyvale/{errorsysindep.py → sensorsim/errorsysindep.py} +3 -3
  109. pyvale/sensorsim/exceptions.py +8 -0
  110. pyvale/{experimentsimulator.py → sensorsim/experimentsimulator.py} +23 -3
  111. pyvale/{field.py → sensorsim/field.py} +1 -1
  112. pyvale/{fieldconverter.py → sensorsim/fieldconverter.py} +72 -19
  113. pyvale/sensorsim/fieldinterp.py +37 -0
  114. pyvale/sensorsim/fieldinterpmesh.py +124 -0
  115. pyvale/sensorsim/fieldinterppoints.py +55 -0
  116. pyvale/{fieldsampler.py → sensorsim/fieldsampler.py} +4 -4
  117. pyvale/{fieldscalar.py → sensorsim/fieldscalar.py} +28 -24
  118. pyvale/{fieldtensor.py → sensorsim/fieldtensor.py} +33 -31
  119. pyvale/{fieldvector.py → sensorsim/fieldvector.py} +33 -31
  120. pyvale/{imagedef2d.py → sensorsim/imagedef2d.py} +9 -5
  121. pyvale/{integratorfactory.py → sensorsim/integratorfactory.py} +6 -6
  122. pyvale/{integratorquadrature.py → sensorsim/integratorquadrature.py} +3 -3
  123. pyvale/{integratorrectangle.py → sensorsim/integratorrectangle.py} +3 -3
  124. pyvale/{integratorspatial.py → sensorsim/integratorspatial.py} +1 -1
  125. pyvale/{rastercy.py → sensorsim/rastercy.py} +5 -5
  126. pyvale/{rasternp.py → sensorsim/rasternp.py} +9 -9
  127. pyvale/{rasteropts.py → sensorsim/rasteropts.py} +1 -1
  128. pyvale/{renderer.py → sensorsim/renderer.py} +1 -1
  129. pyvale/{rendermesh.py → sensorsim/rendermesh.py} +5 -5
  130. pyvale/{renderscene.py → sensorsim/renderscene.py} +2 -2
  131. pyvale/{sensorarray.py → sensorsim/sensorarray.py} +1 -1
  132. pyvale/{sensorarrayfactory.py → sensorsim/sensorarrayfactory.py} +12 -12
  133. pyvale/{sensorarraypoint.py → sensorsim/sensorarraypoint.py} +10 -8
  134. pyvale/{sensordata.py → sensorsim/sensordata.py} +1 -1
  135. pyvale/{sensortools.py → sensorsim/sensortools.py} +2 -20
  136. pyvale/sensorsim/simtools.py +174 -0
  137. pyvale/{visualexpplotter.py → sensorsim/visualexpplotter.py} +3 -3
  138. pyvale/{visualimages.py → sensorsim/visualimages.py} +2 -2
  139. pyvale/{visualsimanimator.py → sensorsim/visualsimanimator.py} +4 -4
  140. pyvale/{visualsimplotter.py → sensorsim/visualsimplotter.py} +5 -5
  141. pyvale/{visualsimsensors.py → sensorsim/visualsimsensors.py} +12 -12
  142. pyvale/{visualtools.py → sensorsim/visualtools.py} +1 -1
  143. pyvale/{visualtraceplotter.py → sensorsim/visualtraceplotter.py} +2 -2
  144. pyvale/simcases/case17.geo +3 -0
  145. pyvale/simcases/case17.i +4 -4
  146. pyvale/simcases/run_1case.py +1 -9
  147. pyvale/simcases/run_all_cases.py +1 -1
  148. pyvale/simcases/run_build_case.py +1 -1
  149. pyvale/simcases/run_example_cases.py +1 -1
  150. pyvale/verif/__init__.py +12 -0
  151. pyvale/{analyticsimdatafactory.py → verif/analyticsimdatafactory.py} +2 -2
  152. pyvale/{analyticsimdatagenerator.py → verif/analyticsimdatagenerator.py} +2 -2
  153. pyvale/verif/psens.py +125 -0
  154. pyvale/verif/psensconst.py +18 -0
  155. pyvale/verif/psensmech.py +227 -0
  156. pyvale/verif/psensmultiphys.py +187 -0
  157. pyvale/verif/psensscalar.py +347 -0
  158. pyvale/verif/psenstensor.py +123 -0
  159. pyvale/verif/psensvector.py +116 -0
  160. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/METADATA +6 -7
  161. pyvale-2025.8.1.dist-info/RECORD +260 -0
  162. pyvale/dataset.py +0 -415
  163. pyvale/dic2dcpp.cp311-win_amd64.pyd +0 -0
  164. pyvale/dicdataimport.py +0 -247
  165. pyvale/simtools.py +0 -67
  166. pyvale-2025.7.1.dist-info/RECORD +0 -211
  167. /pyvale/{blendercalibrationdata.py → blender/blendercalibrationdata.py} +0 -0
  168. /pyvale/{dicspecklegenerator.py → dic/dicspecklegenerator.py} +0 -0
  169. /pyvale/{dicspecklequality.py → dic/dicspecklequality.py} +0 -0
  170. /pyvale/{dicstrainresults.py → dic/dicstrainresults.py} +0 -0
  171. /pyvale/{cameradata.py → sensorsim/cameradata.py} +0 -0
  172. /pyvale/{cameradata2d.py → sensorsim/cameradata2d.py} +0 -0
  173. /pyvale/{errordriftcalc.py → sensorsim/errordriftcalc.py} +0 -0
  174. /pyvale/{fieldtransform.py → sensorsim/fieldtransform.py} +0 -0
  175. /pyvale/{generatorsrandom.py → sensorsim/generatorsrandom.py} +0 -0
  176. /pyvale/{imagetools.py → sensorsim/imagetools.py} +0 -0
  177. /pyvale/{integratortype.py → sensorsim/integratortype.py} +0 -0
  178. /pyvale/{output.py → sensorsim/output.py} +0 -0
  179. /pyvale/{raster.py → sensorsim/raster.py} +0 -0
  180. /pyvale/{sensordescriptor.py → sensorsim/sensordescriptor.py} +0 -0
  181. /pyvale/{visualimagedef.py → sensorsim/visualimagedef.py} +0 -0
  182. /pyvale/{visualopts.py → sensorsim/visualopts.py} +0 -0
  183. /pyvale/{analyticmeshgen.py → verif/analyticmeshgen.py} +0 -0
  184. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/WHEEL +0 -0
  185. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/licenses/LICENSE +0 -0
  186. {pyvale-2025.7.1.dist-info → pyvale-2025.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,347 @@
1
+ #===============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ #===============================================================================
6
+ import copy
7
+ import numpy as np
8
+
9
+ import pyvale.mooseherder as mh
10
+ import pyvale.sensorsim as sens
11
+ import pyvale.verif.psens as psens
12
+ import pyvale.verif.psensconst as psensconst
13
+ import pyvale.dataset as dataset
14
+
15
+ """
16
+ DEVELOPER VERIFICATION MODULE
17
+ --------------------------------------------------------------------------------
18
+ This module contains developer utility functions used for verification testing
19
+ of the point sensor simulation toolbox in pyvale.
20
+
21
+ Specifically, this module contains functions used for testing point sensors
22
+ applied to scalar fields.
23
+ """
24
+
25
+ # TODO: fix position locking for 3D field errors
26
+
27
+ def simdata_2d() -> mh.SimData:
28
+ data_path = dataset.thermal_2d_path()
29
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
30
+ sim_data = sens.scale_length_units(scale=1000.0,
31
+ sim_data=sim_data,
32
+ disp_comps=None)
33
+ return sim_data
34
+
35
+
36
+ def simdata_3d() -> mh.SimData:
37
+ data_path = dataset.thermal_3d_path()
38
+ sim_data = mh.ExodusReader(data_path).read_all_sim_data()
39
+ sim_data = sens.scale_length_units(scale=1000.0,
40
+ sim_data=sim_data,
41
+ disp_comps=None)
42
+ return sim_data
43
+
44
+
45
+ def sens_pos_2d() -> dict[str,np.ndarray]:
46
+ sim_dims = sens.SimTools.get_sim_dims(simdata_2d())
47
+ sens_pos = {}
48
+
49
+ x_lims = sim_dims["x"]
50
+ y_lims = sim_dims["y"]
51
+ z_lims = (0,0)
52
+
53
+ n_sens = (4,1,1)
54
+ sens_pos["line-4"] = sens.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
55
+
56
+ n_sens = (2,2,1)
57
+ sens_pos["grid-22"] = sens.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
58
+
59
+ return sens_pos
60
+
61
+
62
+ def sens_pos_3d() -> dict[str,np.ndarray]:
63
+ sim_dims = sens.SimTools.get_sim_dims(simdata_3d())
64
+
65
+ sens_pos = {}
66
+
67
+ n_sens = (1,4,1)
68
+ x_lims = (sim_dims["x"][1],sim_dims["x"][1])
69
+ y_lims = sim_dims["y"]
70
+ z_lims = sim_dims["z"]
71
+ sens_pos["line-y-xy"] = sens.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
72
+
73
+ n_sens = (1,4,1)
74
+ x_lims = (9.4,9.4)
75
+ y_lims = sim_dims["y"]
76
+ z_lims = (sim_dims["z"][1],sim_dims["z"][1])
77
+ sens_pos["line-y-yz"] = sens.create_sensor_pos_array(n_sens,x_lims,y_lims,z_lims)
78
+
79
+ return sens_pos
80
+
81
+
82
+ def sens_pos_2d_lock(sens_pos: np.ndarray) -> dict[str,np.ndarray]:
83
+ pos_lock = {}
84
+
85
+ lock = np.full_like(sens_pos,False,dtype=bool)
86
+ lock[:,2] = True # lock z
87
+ pos_lock["line-4"] = None
88
+
89
+ lock = np.full_like(sens_pos,False,dtype=bool)
90
+ lock[:,2] = True # lock z
91
+ pos_lock["grid-22"] = None
92
+
93
+ return pos_lock
94
+
95
+
96
+ def sens_pos_3d_lock(sens_pos: np.ndarray) -> dict[str,np.ndarray]:
97
+ pos_lock = {}
98
+
99
+ lock = np.full_like(sens_pos,False,dtype=bool)
100
+ lock[:,2] = True # lock z
101
+ pos_lock["line-y-xy"] = lock
102
+
103
+ lock = np.full_like(sens_pos,False,dtype=bool)
104
+ lock[:,0] = True # lock x
105
+ pos_lock["line-y-yz"] = None
106
+
107
+ return pos_lock
108
+
109
+
110
+ def sens_data_2d_dict() -> dict[str,sens.SensorData]:
111
+ return psens.sens_data_dict(simdata_2d(),sens_pos_2d())
112
+
113
+
114
+ def sens_data_3d_dict() -> dict[str,sens.SensorData]:
115
+ return psens.sens_data_dict(simdata_3d(),sens_pos_3d())
116
+
117
+
118
+ def err_chain_sfield(field: sens.IField,
119
+ sens_pos: np.ndarray,
120
+ samp_times: np.ndarray | None,
121
+ pos_lock: np.ndarray | None,
122
+ ) -> list[sens.IErrCalculator]:
123
+
124
+ if samp_times is None:
125
+ samp_times = field.get_time_steps()
126
+
127
+ pos_offset_xyz = np.array((1.0,1.0,1.0),dtype=np.float64)
128
+ pos_offset_xyz = np.tile(pos_offset_xyz,(sens_pos.shape[0],1))
129
+
130
+ time_offset = np.full((samp_times.shape[0],),0.1)
131
+
132
+ pos_rand = sens.GenNormal(std=1.0,
133
+ mean=0.0,
134
+ seed=psensconst.GOLD_SEED) # units = mm
135
+ time_rand = sens.GenNormal(std=0.1,
136
+ mean=0.0,
137
+ seed=psensconst.GOLD_SEED) # units = s
138
+
139
+ field_err_data = sens.ErrFieldData(
140
+ pos_offset_xyz=pos_offset_xyz,
141
+ time_offset=time_offset,
142
+ pos_rand_xyz=(pos_rand,pos_rand,pos_rand),
143
+ time_rand=time_rand,
144
+ pos_lock_xyz=pos_lock,
145
+ )
146
+
147
+ err_chain = []
148
+ err_chain.append(sens.ErrSysField(field,
149
+ field_err_data))
150
+ return err_chain
151
+
152
+
153
+ def err_chain_sfield_dep(field: sens.IField,
154
+ sens_pos: np.ndarray,
155
+ samp_times: np.ndarray | None,
156
+ pos_lock: np.ndarray | None,
157
+ ) -> list[sens.IErrCalculator]:
158
+
159
+ if samp_times is None:
160
+ samp_times = field.get_time_steps()
161
+
162
+ time_offset = 2.0*np.ones_like(samp_times)
163
+ time_error_data = sens.ErrFieldData(time_offset=time_offset)
164
+
165
+ pos_offset = -1.0*np.ones_like(sens_pos)
166
+ pos_error_data = sens.ErrFieldData(pos_offset_xyz=pos_offset,
167
+ pos_lock_xyz=pos_lock)
168
+
169
+ err_chain = []
170
+ err_chain.append(sens.ErrSysField(field,
171
+ time_error_data,
172
+ sens.EErrDep.DEPENDENT))
173
+ err_chain.append(sens.ErrSysField(field,
174
+ time_error_data,
175
+ sens.EErrDep.DEPENDENT))
176
+
177
+ err_chain.append(sens.ErrSysField(field,
178
+ pos_error_data,
179
+ sens.EErrDep.DEPENDENT))
180
+ err_chain.append(sens.ErrSysField(field,
181
+ pos_error_data,
182
+ sens.EErrDep.DEPENDENT))
183
+ return err_chain
184
+
185
+
186
+ def calib_assumed(signal: np.ndarray) -> np.ndarray:
187
+ return 24.3*signal + 0.616
188
+
189
+ def calib_truth(signal: np.ndarray) -> np.ndarray:
190
+ return -0.01897 + 25.41881*signal - 0.42456*signal**2 + 0.04365*signal**3
191
+
192
+ def err_chain_calib() -> list[sens.IErrCalculator]:
193
+ signal_calib_range = np.array((0.0,6.0),dtype=np.float64)
194
+ cal_err = sens.ErrSysCalibration(calib_assumed,
195
+ calib_truth,
196
+ signal_calib_range,
197
+ n_cal_divs=10000)
198
+ return [cal_err,]
199
+
200
+
201
+ def err_chain_2d_dict(field: sens.IField,
202
+ sens_pos: np.ndarray,
203
+ samp_times: np.ndarray | None,
204
+ pos_lock: np.ndarray | None
205
+ ) -> dict[str,list[sens.IErrCalculator]]:
206
+ err_cases = {}
207
+ err_cases["none"] = None
208
+ err_cases["basic"] = psens.err_chain_basic()
209
+ err_cases["basic-gen"] = psens.err_chain_gen()
210
+ err_cases["field"] = err_chain_sfield(field,sens_pos,samp_times,pos_lock)
211
+ err_cases["field-dep"] = err_chain_sfield_dep(field,sens_pos,samp_times,pos_lock)
212
+ err_cases["calib"] = err_chain_calib()
213
+
214
+ # This has to be last so when we chain all errors together the saturation
215
+ # error is the last thing that happens
216
+ err_cases["basic-dep"] = psens.err_chain_dep()
217
+
218
+ err_cases["all"] = psens.err_chain_all(err_cases)
219
+
220
+ return err_cases
221
+
222
+
223
+ def err_chain_3d_dict(field: sens.IField,
224
+ sens_pos: np.ndarray,
225
+ samp_times: np.ndarray | None,
226
+ pos_lock: np.ndarray | None
227
+ ) -> dict[str,list[sens.IErrCalculator]]:
228
+ err_cases = {}
229
+ err_cases["none"] = None
230
+ err_cases["basic"] = psens.err_chain_basic()
231
+ err_cases["basic-gen"] = psens.err_chain_gen()
232
+ err_cases["field"] = err_chain_sfield(field,sens_pos,samp_times,pos_lock)
233
+ err_cases["field-dep"] = err_chain_sfield_dep(field,sens_pos,samp_times,pos_lock)
234
+ err_cases["calib"] = err_chain_calib()
235
+
236
+ # This has to be last so when we chain all errors together the saturation
237
+ # error is the last thing that happens
238
+ err_cases["basic-dep"] = psens.err_chain_dep()
239
+
240
+ err_cases["all"] = psens.err_chain_all(err_cases)
241
+
242
+ return err_cases
243
+
244
+
245
+ def sens_noerrs(sim_data: mh.SimData,
246
+ sens_data: sens.SensorData,
247
+ elem_dims: int) -> sens.SensorArrayPoint:
248
+ descriptor = sens.SensorDescriptorFactory.temperature_descriptor()
249
+ field = sens.FieldScalar(sim_data,
250
+ field_key="temperature",
251
+ elem_dims=elem_dims)
252
+ sens_array = sens.SensorArrayPoint(sens_data,
253
+ field,
254
+ descriptor)
255
+ return sens_array
256
+
257
+
258
+ def gen_sens_dict_2d(sim_data: mh.SimData,
259
+ sens_data_dict: dict[str, sens.SensorData],
260
+ tag: str
261
+ ) -> dict[str, sens.SensorArrayPoint]:
262
+
263
+ sens_dict = {}
264
+ for ss in sens_data_dict:
265
+ sens_array = sens_noerrs(sim_data,
266
+ sens_data_dict[ss],
267
+ elem_dims=2)
268
+
269
+ pos_lock = sens_pos_2d_lock(sens_data_dict[ss].positions)
270
+ for kk in pos_lock:
271
+ if kk in ss:
272
+ pos_lock_key = kk
273
+ break
274
+
275
+ err_chain_dict = err_chain_2d_dict(sens_array.get_field(),
276
+ sens_data_dict[ss].positions,
277
+ sens_data_dict[ss].sample_times,
278
+ pos_lock[pos_lock_key])
279
+
280
+ for ee in err_chain_dict:
281
+ key = f"{tag}_{ss}_err-{ee}"
282
+ sens_dict[key] = copy.deepcopy(sens_array)
283
+
284
+ if err_chain_dict[ee] is not None:
285
+ err_int_opts = sens.ErrIntOpts()
286
+ err_int = sens.ErrIntegrator(err_chain_dict[ee],
287
+ sens_data_dict[ss],
288
+ sens_dict[key].get_measurement_shape(),
289
+ err_int_opts=err_int_opts)
290
+ sens_dict[key].set_error_integrator(err_int)
291
+
292
+ return sens_dict
293
+
294
+ def gen_sens_dict_3d(sim_data: mh.SimData,
295
+ sens_data_dict: dict[str,sens.SensorData],
296
+ tag: str,
297
+ ) -> dict[str,sens.SensorArrayPoint]:
298
+
299
+ sens_dict = {}
300
+ for ss in sens_data_dict:
301
+ sens_array = sens_noerrs(sim_data,
302
+ sens_data_dict[ss],
303
+ elem_dims=3)
304
+
305
+ pos_lock = sens_pos_3d_lock(sens_data_dict[ss].positions)
306
+ for kk in pos_lock:
307
+ if kk in ss:
308
+ pos_lock_key = kk
309
+ break
310
+
311
+ err_chain_dict = err_chain_3d_dict(sens_array.get_field(),
312
+ sens_data_dict[ss].positions,
313
+ sens_data_dict[ss].sample_times,
314
+ pos_lock[pos_lock_key])
315
+
316
+ for ee in err_chain_dict:
317
+ key = f"{tag}_{ss}_err-{ee}"
318
+ sens_dict[key] = copy.deepcopy(sens_array)
319
+
320
+ if err_chain_dict[ee] is not None:
321
+ err_int_opts = sens.ErrIntOpts()
322
+ err_int = sens.ErrIntegrator(err_chain_dict[ee],
323
+ sens_data_dict[ss],
324
+ sens_dict[key].get_measurement_shape(),
325
+ err_int_opts=err_int_opts)
326
+ sens_dict[key].set_error_integrator(err_int)
327
+
328
+ return sens_dict
329
+
330
+
331
+
332
+ def sens_2d_dict() -> dict[str,sens.SensorArrayPoint]:
333
+ sens_data_dict = sens_data_2d_dict()
334
+ sim_data = simdata_2d()
335
+ tag = "scal2d"
336
+ return gen_sens_dict_2d(sim_data,sens_data_dict,tag)
337
+
338
+ def sens_3d_dict() -> dict[str,sens.SensorArrayPoint]:
339
+ sens_data_dict = sens_data_3d_dict()
340
+ sim_data = simdata_3d()
341
+ tag = "scal3d"
342
+ return gen_sens_dict_3d(sim_data,sens_data_dict,tag)
343
+
344
+
345
+
346
+
347
+
@@ -0,0 +1,123 @@
1
+ #===============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ #===============================================================================
6
+ import copy
7
+ import pyvale.mooseherder as mh
8
+ import pyvale.sensorsim as sens
9
+ import pyvale.verif.psensmech as psensmech
10
+
11
+ """
12
+ DEVELOPER VERIFICATION MODULE
13
+ --------------------------------------------------------------------------------
14
+ This module contains developer utility functions used for verification testing
15
+ of the point sensor simulation toolbox in pyvale.
16
+
17
+ Specifically, this module contains functions used for testing point sensors
18
+ applied to tensor fields.
19
+ """
20
+
21
+ # TODO
22
+ # - Calibration errors for tensor fields
23
+
24
+ def sens_2d_noerrs(sim_data: mh.SimData,
25
+ sens_data: sens.SensorData) -> sens.SensorArrayPoint:
26
+ descriptor = sens.SensorDescriptorFactory.strain_descriptor()
27
+ field_name = "strain"
28
+ norm_comps = ("strain_xx","strain_yy")
29
+ dev_comps = ("strain_xy",)
30
+ field = sens.FieldTensor(sim_data,
31
+ field_name=field_name,
32
+ norm_comps=norm_comps,
33
+ dev_comps=dev_comps,
34
+ elem_dims=2)
35
+ sens_array = sens.SensorArrayPoint(sens_data,
36
+ field,
37
+ descriptor)
38
+ return sens_array
39
+
40
+
41
+ def sens_3d_noerrs(sim_data: mh.SimData,
42
+ sens_data: sens.SensorData) -> sens.SensorArrayPoint:
43
+ descriptor = sens.SensorDescriptorFactory.strain_descriptor()
44
+ field_name = "strain"
45
+ norm_comps = ("strain_xx","strain_yy","strain_zz")
46
+ dev_comps = ("strain_xy","strain_yz","strain_xz")
47
+ field = sens.FieldTensor(sim_data,
48
+ field_name=field_name,
49
+ norm_comps=norm_comps,
50
+ dev_comps=dev_comps,
51
+ elem_dims=3)
52
+ sens_array = sens.SensorArrayPoint(sens_data,
53
+ field,
54
+ descriptor)
55
+ return sens_array
56
+
57
+
58
+ def sens_2d_dict() -> dict[str,sens.SensorArrayPoint]:
59
+ sim_data = psensmech.simdata_mech_2d()
60
+ sens_data_dict = psensmech.sens_data_2d_dict()
61
+
62
+ sens_dict = {}
63
+ for ss in sens_data_dict:
64
+ sens_array = sens_2d_noerrs(sim_data,sens_data_dict[ss])
65
+
66
+ pos_lock = psensmech.sens_pos_2d_lock(sens_data_dict[ss].positions)
67
+ for kk in pos_lock:
68
+ if kk in ss:
69
+ pos_lock_key = kk
70
+ break
71
+
72
+ err_chain_dict = psensmech.err_chain_2d_dict(sens_array.get_field(),
73
+ sens_data_dict[ss].positions,
74
+ sens_data_dict[ss].sample_times,
75
+ pos_lock[pos_lock_key])
76
+
77
+ for ee in err_chain_dict:
78
+ tag = f"tens2d_{ss}_err-{ee}"
79
+ sens_dict[tag] = copy.deepcopy(sens_array)
80
+
81
+ if err_chain_dict[ee] is not None:
82
+ err_int_opts = sens.ErrIntOpts()
83
+ err_int = sens.ErrIntegrator(err_chain_dict[ee],
84
+ sens_data_dict[ss],
85
+ sens_dict[tag].get_measurement_shape(),
86
+ err_int_opts=err_int_opts)
87
+ sens_dict[tag].set_error_integrator(err_int)
88
+
89
+ return sens_dict
90
+
91
+
92
+ def sens_3d_dict() -> dict[str,sens.SensorArrayPoint]:
93
+ sim_data = psensmech.simdata_mech_3d()
94
+ sens_data_dict = psensmech.sens_data_3d_dict()
95
+
96
+ sens_dict = {}
97
+ for ss in sens_data_dict:
98
+ sens_array = sens_3d_noerrs(sim_data,sens_data_dict[ss])
99
+
100
+ pos_lock = psensmech.sens_pos_3d_lock(sens_data_dict[ss].positions)
101
+ for kk in pos_lock:
102
+ if kk in ss:
103
+ pos_lock_key = kk
104
+ break
105
+
106
+ err_chain_dict = psensmech.err_chain_3d_dict(sens_array.get_field(),
107
+ sens_data_dict[ss].positions,
108
+ sens_data_dict[ss].sample_times,
109
+ pos_lock=pos_lock[pos_lock_key])
110
+
111
+ for ee in err_chain_dict:
112
+ tag = f"tens3d_{ss}_err-{ee}"
113
+ sens_dict[tag] = copy.deepcopy(sens_array)
114
+
115
+ if err_chain_dict[ee] is not None:
116
+ err_int_opts = sens.ErrIntOpts()
117
+ err_int = sens.ErrIntegrator(err_chain_dict[ee],
118
+ sens_data_dict[ss],
119
+ sens_dict[tag].get_measurement_shape(),
120
+ err_int_opts=err_int_opts)
121
+ sens_dict[tag].set_error_integrator(err_int)
122
+
123
+ return sens_dict
@@ -0,0 +1,116 @@
1
+ #===============================================================================
2
+ # pyvale: the python validation engine
3
+ # License: MIT
4
+ # Copyright (C) 2025 The Computer Aided Validation Team
5
+ #===============================================================================
6
+ import copy
7
+ import pyvale.mooseherder as mh
8
+ import pyvale.sensorsim as sens
9
+ import pyvale.verif.psensmech as psensmech
10
+
11
+
12
+ """
13
+ DEVELOPER VERIFICATION MODULE
14
+ --------------------------------------------------------------------------------
15
+ This module contains developer utility functions used for verification testing
16
+ of the point sensor simulation toolbox in pyvale.
17
+
18
+ Specifically, this module contains functions used for testing point sensors
19
+ applied to vector fields.
20
+ """
21
+
22
+ # TODO
23
+ # - Calibration errors for vector fields
24
+
25
+
26
+ def sens_2d_noerrs(sim_data: mh.SimData,
27
+ sens_data: sens.SensorData) -> sens.SensorArrayPoint:
28
+ descriptor = sens.SensorDescriptorFactory.displacement_descriptor()
29
+ field = sens.FieldVector(sim_data,
30
+ field_key="disp",
31
+ components=("disp_x","disp_y"),
32
+ elem_dims=2)
33
+ sens_array = sens.SensorArrayPoint(sens_data,
34
+ field,
35
+ descriptor)
36
+ return sens_array
37
+
38
+
39
+ def sens_3d_noerrs(sim_data: mh.SimData,
40
+ sens_data: sens.SensorData) -> sens.SensorArrayPoint:
41
+ descriptor = sens.SensorDescriptorFactory.displacement_descriptor()
42
+ field = sens.FieldVector(sim_data,
43
+ field_key="disp",
44
+ components=("disp_x","disp_y","disp_z"),
45
+ elem_dims=3)
46
+ sens_array = sens.SensorArrayPoint(sens_data,
47
+ field,
48
+ descriptor)
49
+ return sens_array
50
+
51
+ def sens_2d_dict() -> dict[str,sens.SensorArrayPoint]:
52
+ sim_data = psensmech.simdata_mech_2d()
53
+ sens_data_dict = psensmech.sens_data_2d_dict()
54
+
55
+ sens_dict = {}
56
+ for ss in sens_data_dict:
57
+ sens_array = sens_2d_noerrs(sim_data,sens_data_dict[ss])
58
+
59
+ pos_lock = psensmech.sens_pos_2d_lock(sens_data_dict[ss].positions)
60
+ for kk in pos_lock:
61
+ if kk in ss:
62
+ pos_lock_key = kk
63
+ break
64
+
65
+ err_chain_dict = psensmech.err_chain_2d_dict(sens_array.get_field(),
66
+ sens_data_dict[ss].positions,
67
+ sens_data_dict[ss].sample_times,
68
+ pos_lock[pos_lock_key])
69
+
70
+ for ee in err_chain_dict:
71
+ tag = f"vec2d_{ss}_err-{ee}"
72
+ sens_dict[tag] = copy.deepcopy(sens_array)
73
+
74
+ if err_chain_dict[ee] is not None:
75
+ err_int_opts = sens.ErrIntOpts()
76
+ err_int = sens.ErrIntegrator(err_chain_dict[ee],
77
+ sens_data_dict[ss],
78
+ sens_dict[tag].get_measurement_shape(),
79
+ err_int_opts=err_int_opts)
80
+ sens_dict[tag].set_error_integrator(err_int)
81
+
82
+ return sens_dict
83
+
84
+
85
+ def sens_3d_dict() -> dict[str,sens.SensorArrayPoint]:
86
+ sim_data = psensmech.simdata_mech_3d()
87
+ sens_data_dict = psensmech.sens_data_3d_dict()
88
+
89
+ sens_dict = {}
90
+ for ss in sens_data_dict:
91
+ sens_array = sens_3d_noerrs(sim_data,sens_data_dict[ss])
92
+
93
+ pos_lock = psensmech.sens_pos_3d_lock(sens_data_dict[ss].positions)
94
+ for kk in pos_lock:
95
+ if kk in ss:
96
+ pos_lock_key = kk
97
+ break
98
+
99
+ err_chain_dict = psensmech.err_chain_3d_dict(sens_array.get_field(),
100
+ sens_data_dict[ss].positions,
101
+ sens_data_dict[ss].sample_times,
102
+ pos_lock=pos_lock[pos_lock_key])
103
+
104
+ for ee in err_chain_dict:
105
+ tag = f"vec3d_{ss}_err-{ee}"
106
+ sens_dict[tag] = copy.deepcopy(sens_array)
107
+
108
+ if err_chain_dict[ee] is not None:
109
+ err_int_opts = sens.ErrIntOpts()
110
+ err_int = sens.ErrIntegrator(err_chain_dict[ee],
111
+ sens_data_dict[ss],
112
+ sens_dict[tag].get_measurement_shape(),
113
+ err_int_opts=err_int_opts)
114
+ sens_dict[tag].set_error_integrator(err_int)
115
+
116
+ return sens_dict
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyvale
3
- Version: 2025.7.1
3
+ Version: 2025.8.1
4
4
  Summary: An all-in-one package for sensor simulation, sensor uncertainty quantification, sensor placement optimisation and simulation calibration or validation.
5
5
  Author-email: "scepticalrabbit et al." <thescepticalrabbit@gmail.com>
6
6
  License: MIT License
@@ -29,7 +29,6 @@ Project-URL: Issue Tracker, https://github.com/Digital-Validation-Laboratory/pyv
29
29
  Requires-Python: ==3.11.*
30
30
  Description-Content-Type: text/markdown
31
31
  License-File: LICENSE
32
- Requires-Dist: mooseherder>=0.1.0
33
32
  Requires-Dist: numpy<2.0.0
34
33
  Requires-Dist: scipy>=1.14.0
35
34
  Requires-Dist: netCDF4>=1.6.5
@@ -54,22 +53,22 @@ Dynamic: license-file
54
53
  # pyvale
55
54
  ![fig_pyvale_logo](https://raw.githubusercontent.com/Computer-Aided-Validation-Laboratory/pyvale/main/images/pyvale_logo.png)
56
55
 
57
- The python validation engine (`pyvale`) is you virtual engineering laboratory: An all-in-one package for sensor simulation, sensor uncertainty quantification, sensor placement optimisation and simulation calibration/validation. Used to simulate experimental data from an input multi-physics simulation by explicitly modelling sensors with realistic uncertainties. Useful for experimental design, sensor placement optimisation, testing simulation validation metrics and virtually testing digital shadows/twins.
56
+ The python validation engine (`pyvale`) is your virtual engineering laboratory: An all-in-one package for sensor simulation, sensor uncertainty quantification, sensor placement optimisation and simulation calibration/validation. Used to simulate experimental data from an input multi-physics simulation by explicitly modelling sensors with realistic uncertainties. Useful for experimental design, sensor placement optimisation, testing simulation validation metrics and virtually testing digital shadows/twins.
58
57
 
59
- We provide dedicated tools for simulation and uncertainty quantification of imaging sensors including digital image correlation (DIC) and infra-red thermography (IRT). Check out the [documentation](https://computer-aided-validation-laboratory.github.io/pyvale/index.html) to get started with some of our examples.
58
+ We are actively developing dedicated tools for simulation and uncertainty quantification of imaging sensors including digital image correlation (DIC) and infra-red thermography (IRT). Check out the [documentation](https://computer-aided-validation-laboratory.github.io/pyvale/index.html) to get started with some of our examples.
60
59
 
61
60
  ## Quick Demo: Simulating Point Sensors
62
61
  Here we demonstrate how `pyvale` can be used to simulate thermocouples and strain gauges applied to a [MOOSE](https://mooseframework.inl.gov/index.html) thermo-mechanical simulation of a fusion divertor armour heatsink. The figures below show visualisations of the virtual thermocouple and strain gauge locations on the simualtion mesh as well as time traces for each sensor over a series of simulated experiments.
63
62
 
64
- The code to run the simulated experiments and produce the output shown here comes from [this example](https://computer-aided-validation-laboratory.github.io/pyvale/examples/point/ex4_2.html). You can find more examples and details of `pyvale` python API in the `pyvale` [documentation](https://computer-aided-validation-laboratory.github.io/pyvale/index.html).
63
+ The code to run the simulated experiments and produce the output shown here comes from [this example](https://computer-aided-validation-laboratory.github.io/pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.html). You can find more examples and details of `pyvale` python API in the `pyvale` [documentation](https://computer-aided-validation-laboratory.github.io/pyvale/index.html).
65
64
 
66
65
  |![fig_thermomech3d_tc_vis](https://raw.githubusercontent.com/Computer-Aided-Validation-Laboratory/pyvale/main/images/thermomech3d_tc_vis.png)|![fig_thermomech3d_sg_vis](https://raw.githubusercontent.com/Computer-Aided-Validation-Laboratory/pyvale/main/images/thermomech3d_sg_vis.png)|
67
66
  |--|--|
68
- |*Visualisation of the thermcouple locations.*|*Visualisation of the strain gauge locations.*|
67
+ |*Visualisation of the thermocouple locations.*|*Visualisation of the strain gauge locations.*|
69
68
 
70
69
  |![fig_thermomech3d_tc_traces](https://raw.githubusercontent.com/Computer-Aided-Validation-Laboratory/pyvale/main/images/thermomech3d_tc_traces.png)|![fig_thermomech3d_sg_traces](https://raw.githubusercontent.com/Computer-Aided-Validation-Laboratory/pyvale/main/images/thermomech3d_sg_traces.png)|
71
70
  |--|--|
72
- |*Thermocouples time traces over a series of simulated experiments.*|*Strain gauge time traces over a series of simulated experiments.*|
71
+ |*Thermocouple time traces over a series of simulated experiments.*|*Strain gauge time traces over a series of simulated experiments.*|
73
72
 
74
73
 
75
74
  ## Quick Install