pyvale 2025.4.0__py3-none-any.whl → 2025.5.1__py3-none-any.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.
- pyvale/__init__.py +78 -64
- pyvale/analyticmeshgen.py +102 -0
- pyvale/{core/analyticsimdatafactory.py → analyticsimdatafactory.py} +44 -16
- pyvale/analyticsimdatagenerator.py +323 -0
- pyvale/blendercalibrationdata.py +15 -0
- pyvale/blenderlightdata.py +26 -0
- pyvale/blendermaterialdata.py +15 -0
- pyvale/blenderrenderdata.py +30 -0
- pyvale/blenderscene.py +488 -0
- pyvale/blendertools.py +420 -0
- pyvale/{core/camera.py → camera.py} +15 -15
- pyvale/{core/cameradata.py → cameradata.py} +27 -22
- pyvale/{core/cameradata2d.py → cameradata2d.py} +8 -6
- pyvale/camerastereo.py +217 -0
- pyvale/{core/cameratools.py → cameratools.py} +220 -26
- pyvale/{core/cython → cython}/rastercyth.py +11 -7
- pyvale/data/__init__.py +5 -7
- pyvale/data/cal_target.tiff +0 -0
- pyvale/data/case00_HEX20_out.e +0 -0
- pyvale/data/case00_HEX27_out.e +0 -0
- pyvale/data/case00_HEX8_out.e +0 -0
- pyvale/data/case00_TET10_out.e +0 -0
- pyvale/data/case00_TET14_out.e +0 -0
- pyvale/data/case00_TET4_out.e +0 -0
- pyvale/{core/dataset.py → dataset.py} +91 -16
- pyvale/{core/errorcalculator.py → errorcalculator.py} +13 -16
- pyvale/{core/errordriftcalc.py → errordriftcalc.py} +14 -14
- pyvale/{core/errorintegrator.py → errorintegrator.py} +25 -28
- pyvale/{core/errorrand.py → errorrand.py} +39 -46
- pyvale/errorsyscalib.py +134 -0
- pyvale/{core/errorsysdep.py → errorsysdep.py} +25 -29
- pyvale/{core/errorsysfield.py → errorsysfield.py} +59 -52
- pyvale/{core/errorsysindep.py → errorsysindep.py} +85 -182
- pyvale/examples/__init__.py +5 -7
- pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
- pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
- pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
- pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
- pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
- pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
- pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
- pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
- pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
- pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
- pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
- pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
- pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
- pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
- pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
- pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
- pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_1_scalarvisualisation.py +6 -9
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_2_scalarcasebuild.py +8 -11
- pyvale/examples/{analyticdatagen → genanalyticdata}/ex2_1_analyticsensors.py +9 -12
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +8 -15
- pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
- pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
- pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
- pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
- pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastenp.py +6 -7
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_oneframe.py +5 -7
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_cypara.py +6 -13
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_pypara.py +9 -12
- pyvale/examples/{ex1_4_thermal2d.py → visualisation/ex1_1_plot_traces.py} +33 -20
- pyvale/examples/{features/ex_animation_tools_3dmonoblock.py → visualisation/ex2_1_animate_sim.py} +37 -31
- pyvale/experimentsimulator.py +175 -0
- pyvale/{core/field.py → field.py} +6 -14
- pyvale/fieldconverter.py +351 -0
- pyvale/{core/fieldsampler.py → fieldsampler.py} +9 -10
- pyvale/{core/fieldscalar.py → fieldscalar.py} +17 -18
- pyvale/{core/fieldtensor.py → fieldtensor.py} +23 -26
- pyvale/{core/fieldtransform.py → fieldtransform.py} +9 -5
- pyvale/{core/fieldvector.py → fieldvector.py} +14 -16
- pyvale/{core/generatorsrandom.py → generatorsrandom.py} +29 -52
- pyvale/{core/imagedef2d.py → imagedef2d.py} +11 -8
- pyvale/{core/integratorfactory.py → integratorfactory.py} +12 -13
- pyvale/{core/integratorquadrature.py → integratorquadrature.py} +57 -32
- pyvale/integratorrectangle.py +165 -0
- pyvale/{core/integratorspatial.py → integratorspatial.py} +9 -10
- pyvale/{core/integratortype.py → integratortype.py} +7 -8
- pyvale/output.py +17 -0
- pyvale/pyvaleexceptions.py +11 -0
- pyvale/{core/raster.py → raster.py} +8 -8
- pyvale/{core/rastercy.py → rastercy.py} +11 -10
- pyvale/{core/rasternp.py → rasternp.py} +12 -13
- pyvale/{core/rendermesh.py → rendermesh.py} +10 -19
- pyvale/{core/sensorarray.py → sensorarray.py} +7 -8
- pyvale/{core/sensorarrayfactory.py → sensorarrayfactory.py} +64 -78
- pyvale/{core/sensorarraypoint.py → sensorarraypoint.py} +39 -41
- pyvale/{core/sensordata.py → sensordata.py} +7 -8
- pyvale/sensordescriptor.py +213 -0
- pyvale/{core/sensortools.py → sensortools.py} +8 -9
- pyvale/simcases/case00_HEX20.i +5 -5
- pyvale/simcases/case00_HEX27.i +5 -5
- pyvale/simcases/case00_HEX8.i +242 -0
- pyvale/simcases/case00_TET10.i +2 -2
- pyvale/simcases/case00_TET14.i +2 -2
- pyvale/simcases/case00_TET4.i +242 -0
- pyvale/simcases/run_1case.py +1 -1
- pyvale/simtools.py +67 -0
- pyvale/visualexpplotter.py +191 -0
- pyvale/{core/visualimagedef.py → visualimagedef.py} +13 -10
- pyvale/{core/visualimages.py → visualimages.py} +10 -9
- pyvale/visualopts.py +493 -0
- pyvale/{core/visualsimanimator.py → visualsimanimator.py} +47 -19
- pyvale/visualsimsensors.py +318 -0
- pyvale/visualtools.py +136 -0
- pyvale/visualtraceplotter.py +142 -0
- {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/METADATA +17 -14
- pyvale-2025.5.1.dist-info/RECORD +172 -0
- {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/WHEEL +1 -1
- pyvale/core/__init__.py +0 -7
- pyvale/core/analyticmeshgen.py +0 -59
- pyvale/core/analyticsimdatagenerator.py +0 -160
- pyvale/core/cython/rastercyth.c +0 -32267
- pyvale/core/experimentsimulator.py +0 -99
- pyvale/core/fieldconverter.py +0 -154
- pyvale/core/integratorrectangle.py +0 -88
- pyvale/core/optimcheckfuncs.py +0 -153
- pyvale/core/sensordescriptor.py +0 -101
- pyvale/core/visualexpplotter.py +0 -151
- pyvale/core/visualopts.py +0 -180
- pyvale/core/visualsimplotter.py +0 -182
- pyvale/core/visualtools.py +0 -81
- pyvale/core/visualtraceplotter.py +0 -256
- pyvale/examples/analyticdatagen/__init__.py +0 -7
- pyvale/examples/ex1_1_thermal2d.py +0 -89
- pyvale/examples/ex1_2_thermal2d.py +0 -111
- pyvale/examples/ex1_3_thermal2d.py +0 -113
- pyvale/examples/ex1_5_thermal2d.py +0 -105
- pyvale/examples/ex2_1_thermal3d .py +0 -87
- pyvale/examples/ex2_2_thermal3d.py +0 -51
- pyvale/examples/ex2_3_thermal3d.py +0 -109
- pyvale/examples/ex3_1_displacement2d.py +0 -47
- pyvale/examples/ex3_2_displacement2d.py +0 -79
- pyvale/examples/ex3_3_displacement2d.py +0 -104
- pyvale/examples/ex3_4_displacement2d.py +0 -105
- pyvale/examples/ex4_1_strain2d.py +0 -57
- pyvale/examples/ex4_2_strain2d.py +0 -79
- pyvale/examples/ex4_3_strain2d.py +0 -100
- pyvale/examples/ex5_1_multiphysics2d.py +0 -78
- pyvale/examples/ex6_1_multiphysics2d_expsim.py +0 -118
- pyvale/examples/ex6_2_multiphysics3d_expsim.py +0 -158
- pyvale/examples/features/__init__.py +0 -7
- pyvale/examples/features/ex_area_avg.py +0 -89
- pyvale/examples/features/ex_calibration_error.py +0 -108
- pyvale/examples/features/ex_chain_field_errs.py +0 -141
- pyvale/examples/features/ex_field_errs.py +0 -78
- pyvale/examples/features/ex_sensor_single_angle_batch.py +0 -110
- pyvale-2025.4.0.dist-info/RECORD +0 -157
- {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/licenses/LICENSE +0 -0
- {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/top_level.txt +0 -0
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
from typing import Callable
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
9
7
|
import numpy as np
|
|
10
|
-
from pyvale.
|
|
8
|
+
from pyvale.errorcalculator import (IErrCalculator,
|
|
11
9
|
EErrType,
|
|
12
|
-
|
|
13
|
-
from pyvale.
|
|
14
|
-
from pyvale.
|
|
10
|
+
EErrDep)
|
|
11
|
+
from pyvale.generatorsrandom import IGenRandom
|
|
12
|
+
from pyvale.sensordata import SensorData
|
|
15
13
|
|
|
16
14
|
|
|
17
15
|
class ErrSysOffset(IErrCalculator):
|
|
@@ -22,21 +20,20 @@ class ErrSysOffset(IErrCalculator):
|
|
|
22
20
|
|
|
23
21
|
def __init__(self,
|
|
24
22
|
offset: float,
|
|
25
|
-
err_dep:
|
|
26
|
-
"""
|
|
27
|
-
|
|
23
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
|
|
24
|
+
"""
|
|
28
25
|
Parameters
|
|
29
26
|
----------
|
|
30
27
|
offset : float
|
|
31
28
|
Constant offset to apply to all simulated measurements from the
|
|
32
29
|
sensor array.
|
|
33
30
|
err_dep : EErrDependence, optional
|
|
34
|
-
Error , by default EErrDependence.INDEPENDENT
|
|
31
|
+
Error calculation dependence, by default EErrDependence.INDEPENDENT.
|
|
35
32
|
"""
|
|
36
33
|
self._offset = offset
|
|
37
34
|
self._err_dep = err_dep
|
|
38
35
|
|
|
39
|
-
def get_error_dep(self) ->
|
|
36
|
+
def get_error_dep(self) -> EErrDep:
|
|
40
37
|
"""Gets the error dependence state for this error calculator. An
|
|
41
38
|
independent error is calculated based on the input truth values as the
|
|
42
39
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -52,7 +49,7 @@ class ErrSysOffset(IErrCalculator):
|
|
|
52
49
|
"""
|
|
53
50
|
return self._err_dep
|
|
54
51
|
|
|
55
|
-
def set_error_dep(self, dependence:
|
|
52
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
56
53
|
"""Sets the error dependence state for this error calculator. An
|
|
57
54
|
independent error is calculated based on the input truth values as the
|
|
58
55
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -111,9 +108,8 @@ class ErrSysOffsetPercent(IErrCalculator):
|
|
|
111
108
|
|
|
112
109
|
def __init__(self,
|
|
113
110
|
offset_percent: float,
|
|
114
|
-
err_dep:
|
|
115
|
-
"""
|
|
116
|
-
|
|
111
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
|
|
112
|
+
"""
|
|
117
113
|
Parameters
|
|
118
114
|
----------
|
|
119
115
|
offset_percent : float
|
|
@@ -125,7 +121,7 @@ class ErrSysOffsetPercent(IErrCalculator):
|
|
|
125
121
|
self._offset_percent = offset_percent
|
|
126
122
|
self._err_dep = err_dep
|
|
127
123
|
|
|
128
|
-
def get_error_dep(self) ->
|
|
124
|
+
def get_error_dep(self) -> EErrDep:
|
|
129
125
|
"""Gets the error dependence state for this error calculator. An
|
|
130
126
|
independent error is calculated based on the input truth values as the
|
|
131
127
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -138,7 +134,7 @@ class ErrSysOffsetPercent(IErrCalculator):
|
|
|
138
134
|
"""
|
|
139
135
|
return self._err_dep
|
|
140
136
|
|
|
141
|
-
def set_error_dep(self, dependence:
|
|
137
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
142
138
|
"""Sets the error dependence state for this error calculator. An
|
|
143
139
|
independent error is calculated based on the input truth values as the
|
|
144
140
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -188,7 +184,7 @@ class ErrSysOffsetPercent(IErrCalculator):
|
|
|
188
184
|
sens_data)
|
|
189
185
|
|
|
190
186
|
|
|
191
|
-
class
|
|
187
|
+
class ErrSysUnif(IErrCalculator):
|
|
192
188
|
"""Systematic error calculator for applying an offset to each sensor that is
|
|
193
189
|
sampled from a uniform probability distribution specified by its upper and
|
|
194
190
|
lower bounds. Implements the `IErrCalculator` interface.
|
|
@@ -198,10 +194,9 @@ class ErrSysUniform(IErrCalculator):
|
|
|
198
194
|
def __init__(self,
|
|
199
195
|
low: float,
|
|
200
196
|
high: float,
|
|
201
|
-
err_dep:
|
|
197
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
202
198
|
seed: int | None = None) -> None:
|
|
203
|
-
"""
|
|
204
|
-
|
|
199
|
+
"""
|
|
205
200
|
Parameters
|
|
206
201
|
----------
|
|
207
202
|
low : float
|
|
@@ -221,7 +216,7 @@ class ErrSysUniform(IErrCalculator):
|
|
|
221
216
|
self._rng = np.random.default_rng(seed)
|
|
222
217
|
self._err_dep = err_dep
|
|
223
218
|
|
|
224
|
-
def get_error_dep(self) ->
|
|
219
|
+
def get_error_dep(self) -> EErrDep:
|
|
225
220
|
"""Gets the error dependence state for this error calculator. An
|
|
226
221
|
independent error is calculated based on the input truth values as the
|
|
227
222
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -237,7 +232,7 @@ class ErrSysUniform(IErrCalculator):
|
|
|
237
232
|
"""
|
|
238
233
|
return self._err_dep
|
|
239
234
|
|
|
240
|
-
def set_error_dep(self, dependence:
|
|
235
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
241
236
|
"""Sets the error dependence state for this error calculator. An
|
|
242
237
|
independent error is calculated based on the input truth values as the
|
|
243
238
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -297,7 +292,7 @@ class ErrSysUniform(IErrCalculator):
|
|
|
297
292
|
return (sys_errs,sens_data)
|
|
298
293
|
|
|
299
294
|
|
|
300
|
-
class
|
|
295
|
+
class ErrSysUnifPercent(IErrCalculator):
|
|
301
296
|
"""Systematic error calculator for applying a percentage offset to each
|
|
302
297
|
sensor that is sampled from a uniform probability distribution specified by
|
|
303
298
|
its upper and lower bounds.
|
|
@@ -313,10 +308,9 @@ class ErrSysUniformPercent(IErrCalculator):
|
|
|
313
308
|
def __init__(self,
|
|
314
309
|
low_percent: float,
|
|
315
310
|
high_percent: float,
|
|
316
|
-
err_dep:
|
|
311
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
317
312
|
seed: int | None = None) -> None:
|
|
318
|
-
"""
|
|
319
|
-
|
|
313
|
+
"""
|
|
320
314
|
Parameters
|
|
321
315
|
----------
|
|
322
316
|
low_percent : float
|
|
@@ -334,7 +328,7 @@ class ErrSysUniformPercent(IErrCalculator):
|
|
|
334
328
|
self._rng = np.random.default_rng(seed)
|
|
335
329
|
self._err_dep = err_dep
|
|
336
330
|
|
|
337
|
-
def get_error_dep(self) ->
|
|
331
|
+
def get_error_dep(self) -> EErrDep:
|
|
338
332
|
"""Gets the error dependence state for this error calculator. An
|
|
339
333
|
independent error is calculated based on the input truth values as the
|
|
340
334
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -347,7 +341,7 @@ class ErrSysUniformPercent(IErrCalculator):
|
|
|
347
341
|
"""
|
|
348
342
|
return self._err_dep
|
|
349
343
|
|
|
350
|
-
def set_error_dep(self, dependence:
|
|
344
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
351
345
|
"""Sets the error dependence state for this error calculator. An
|
|
352
346
|
independent error is calculated based on the input truth values as the
|
|
353
347
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -404,7 +398,7 @@ class ErrSysUniformPercent(IErrCalculator):
|
|
|
404
398
|
return (err_basis*sys_errs,sens_data)
|
|
405
399
|
|
|
406
400
|
|
|
407
|
-
class
|
|
401
|
+
class ErrSysNorm(IErrCalculator):
|
|
408
402
|
"""Systematic error calculator for applying an offset to each individual
|
|
409
403
|
sensor in the array based on sampling from a normal distribution specified
|
|
410
404
|
by its standard deviation and mean. Note that the offset is constant for
|
|
@@ -415,10 +409,9 @@ class ErrSysNormal(IErrCalculator):
|
|
|
415
409
|
def __init__(self,
|
|
416
410
|
std: float,
|
|
417
411
|
mean: float = 0.0,
|
|
418
|
-
err_dep:
|
|
412
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
419
413
|
seed: int | None = None) -> None:
|
|
420
|
-
"""
|
|
421
|
-
|
|
414
|
+
"""
|
|
422
415
|
Parameters
|
|
423
416
|
----------
|
|
424
417
|
std : float
|
|
@@ -436,7 +429,7 @@ class ErrSysNormal(IErrCalculator):
|
|
|
436
429
|
self._rng = np.random.default_rng(seed)
|
|
437
430
|
self._err_dep = err_dep
|
|
438
431
|
|
|
439
|
-
def get_error_dep(self) ->
|
|
432
|
+
def get_error_dep(self) -> EErrDep:
|
|
440
433
|
"""Gets the error dependence state for this error calculator. An
|
|
441
434
|
independent error is calculated based on the input truth values as the
|
|
442
435
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -452,7 +445,7 @@ class ErrSysNormal(IErrCalculator):
|
|
|
452
445
|
"""
|
|
453
446
|
return self._err_dep
|
|
454
447
|
|
|
455
|
-
def set_error_dep(self, dependence:
|
|
448
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
456
449
|
"""Sets the error dependence state for this error calculator. An
|
|
457
450
|
independent error is calculated based on the input truth values as the
|
|
458
451
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -528,13 +521,24 @@ class ErrSysNormPercent(IErrCalculator):
|
|
|
528
521
|
|
|
529
522
|
def __init__(self,
|
|
530
523
|
std_percent: float,
|
|
531
|
-
err_dep:
|
|
524
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT,
|
|
532
525
|
seed: int | None = None) -> None:
|
|
526
|
+
"""
|
|
527
|
+
Parameters
|
|
528
|
+
----------
|
|
529
|
+
std_percent : float
|
|
530
|
+
Standard deviation as a percentage in the range (0,100).
|
|
531
|
+
err_dep : EErrDependence, optional
|
|
532
|
+
Error calculation dependence, by default EErrDependence.INDEPENDENT.
|
|
533
|
+
seed : int | None, optional
|
|
534
|
+
Optional seed for the random generator to allow for replicable
|
|
535
|
+
behaviour, by default None.
|
|
536
|
+
"""
|
|
533
537
|
self._std = std_percent/100
|
|
534
538
|
self._rng = np.random.default_rng(seed)
|
|
535
539
|
self._err_dep = err_dep
|
|
536
540
|
|
|
537
|
-
def get_error_dep(self) ->
|
|
541
|
+
def get_error_dep(self) -> EErrDep:
|
|
538
542
|
"""Gets the error dependence state for this error calculator. An
|
|
539
543
|
independent error is calculated based on the input truth values as the
|
|
540
544
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -547,7 +551,7 @@ class ErrSysNormPercent(IErrCalculator):
|
|
|
547
551
|
"""
|
|
548
552
|
return self._err_dep
|
|
549
553
|
|
|
550
|
-
def set_error_dep(self, dependence:
|
|
554
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
551
555
|
"""Sets the error dependence state for this error calculator. An
|
|
552
556
|
independent error is calculated based on the input truth values as the
|
|
553
557
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -574,12 +578,6 @@ class ErrSysNormPercent(IErrCalculator):
|
|
|
574
578
|
err_basis: np.ndarray,
|
|
575
579
|
sens_data: SensorData,
|
|
576
580
|
) -> tuple[np.ndarray, SensorData]:
|
|
577
|
-
|
|
578
|
-
err_shape = np.array(err_basis.shape)
|
|
579
|
-
err_shape[-1] = 1
|
|
580
|
-
sys_errs = self._rng.normal(loc=0.0,
|
|
581
|
-
scale=self._std,
|
|
582
|
-
size=err_shape)
|
|
583
581
|
"""Calculates the error array based on the size of the input.
|
|
584
582
|
|
|
585
583
|
Parameters
|
|
@@ -597,6 +595,13 @@ class ErrSysNormPercent(IErrCalculator):
|
|
|
597
595
|
sensor data object as it is not modified by this class. The returned
|
|
598
596
|
error array has the same shape as the input error basis.
|
|
599
597
|
"""
|
|
598
|
+
|
|
599
|
+
err_shape = np.array(err_basis.shape)
|
|
600
|
+
err_shape[-1] = 1
|
|
601
|
+
sys_errs = self._rng.normal(loc=0.0,
|
|
602
|
+
scale=self._std,
|
|
603
|
+
size=err_shape)
|
|
604
|
+
|
|
600
605
|
tile_shape = np.array(err_basis.shape)
|
|
601
606
|
tile_shape[0:-1] = 1
|
|
602
607
|
sys_errs = np.tile(sys_errs,tuple(tile_shape))
|
|
@@ -604,7 +609,7 @@ class ErrSysNormPercent(IErrCalculator):
|
|
|
604
609
|
return (err_basis*sys_errs,sens_data)
|
|
605
610
|
|
|
606
611
|
|
|
607
|
-
class
|
|
612
|
+
class ErrSysGen(IErrCalculator):
|
|
608
613
|
"""Systematic error calculator for applying a unique offset to each sensor
|
|
609
614
|
by sample from a user specified probability distribution (an implementation
|
|
610
615
|
of the `IGeneratorRandom` interface).
|
|
@@ -614,13 +619,21 @@ class ErrSysGenerator(IErrCalculator):
|
|
|
614
619
|
__slots__ = ("_generator","_err_dep")
|
|
615
620
|
|
|
616
621
|
def __init__(self,
|
|
617
|
-
generator:
|
|
618
|
-
err_dep:
|
|
619
|
-
|
|
622
|
+
generator: IGenRandom,
|
|
623
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
|
|
624
|
+
"""
|
|
625
|
+
Parameters
|
|
626
|
+
----------
|
|
627
|
+
generator : IGenRandom
|
|
628
|
+
Random generator object used to calculate the systematic error in
|
|
629
|
+
simulation units.
|
|
630
|
+
err_dep : EErrDependence, optional
|
|
631
|
+
Error calculation dependence, by default EErrDependence.INDEPENDENT.
|
|
632
|
+
"""
|
|
620
633
|
self._generator = generator
|
|
621
634
|
self._err_dep = err_dep
|
|
622
635
|
|
|
623
|
-
def get_error_dep(self) ->
|
|
636
|
+
def get_error_dep(self) -> EErrDep:
|
|
624
637
|
"""Gets the error dependence state for this error calculator. An
|
|
625
638
|
independent error is calculated based on the input truth values as the
|
|
626
639
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -633,7 +646,7 @@ class ErrSysGenerator(IErrCalculator):
|
|
|
633
646
|
"""
|
|
634
647
|
return self._err_dep
|
|
635
648
|
|
|
636
|
-
def set_error_dep(self, dependence:
|
|
649
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
637
650
|
"""Sets the error dependence state for this error calculator. An
|
|
638
651
|
independent error is calculated based on the input truth values as the
|
|
639
652
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -705,13 +718,21 @@ class ErrSysGenPercent(IErrCalculator):
|
|
|
705
718
|
__slots__ = ("_generator","_err_dep")
|
|
706
719
|
|
|
707
720
|
def __init__(self,
|
|
708
|
-
generator:
|
|
709
|
-
err_dep:
|
|
710
|
-
|
|
721
|
+
generator: IGenRandom,
|
|
722
|
+
err_dep: EErrDep = EErrDep.INDEPENDENT) -> None:
|
|
723
|
+
"""
|
|
724
|
+
Parameters
|
|
725
|
+
----------
|
|
726
|
+
generator : IGenRandom
|
|
727
|
+
Random generator which returns a percentage error in the range
|
|
728
|
+
(0,100)
|
|
729
|
+
err_dep : EErrDep, optional
|
|
730
|
+
Error calculation dependence, by default EErrDep.INDEPENDENT
|
|
731
|
+
"""
|
|
711
732
|
self._generator = generator
|
|
712
733
|
self._err_dep = err_dep
|
|
713
734
|
|
|
714
|
-
def get_error_dep(self) ->
|
|
735
|
+
def get_error_dep(self) -> EErrDep:
|
|
715
736
|
"""Gets the error dependence state for this error calculator. An
|
|
716
737
|
independent error is calculated based on the input truth values as the
|
|
717
738
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -724,7 +745,7 @@ class ErrSysGenPercent(IErrCalculator):
|
|
|
724
745
|
"""
|
|
725
746
|
return self._err_dep
|
|
726
747
|
|
|
727
|
-
def set_error_dep(self, dependence:
|
|
748
|
+
def set_error_dep(self, dependence: EErrDep) -> None:
|
|
728
749
|
"""Sets the error dependence state for this error calculator. An
|
|
729
750
|
independent error is calculated based on the input truth values as the
|
|
730
751
|
error basis. A dependent error is calculated based on the accumulated
|
|
@@ -771,7 +792,9 @@ class ErrSysGenPercent(IErrCalculator):
|
|
|
771
792
|
err_shape = np.array(err_basis.shape)
|
|
772
793
|
err_shape[-1] = 1
|
|
773
794
|
|
|
774
|
-
sys_errs = self._generator.generate(
|
|
795
|
+
sys_errs = self._generator.generate(shape=err_shape)
|
|
796
|
+
# Convert percent to decimal
|
|
797
|
+
sys_errs = sys_errs/100.0
|
|
775
798
|
|
|
776
799
|
tile_shape = np.array(err_basis.shape)
|
|
777
800
|
tile_shape[0:-1] = 1
|
|
@@ -781,125 +804,5 @@ class ErrSysGenPercent(IErrCalculator):
|
|
|
781
804
|
return (sys_errs,sens_data)
|
|
782
805
|
|
|
783
806
|
|
|
784
|
-
class ErrSysCalibration(IErrCalculator):
|
|
785
|
-
"""Systematic error calculator for calibration errors. The user specifies an
|
|
786
|
-
assumed calibration and a ground truth calibration function. The ground
|
|
787
|
-
truth calibration function is inverted and linearly interpolated numerically
|
|
788
|
-
based on the number of divisions specified by the user.
|
|
789
|
-
|
|
790
|
-
Implements the `IErrCalculator` interface.
|
|
791
|
-
"""
|
|
792
|
-
__slots__ = ("_assumed_cali","_truth_calib","_cal_range","_n_cal_divs",
|
|
793
|
-
"_err_dep","_truth_calc_table")
|
|
794
|
-
|
|
795
|
-
def __init__(self,
|
|
796
|
-
assumed_calib: Callable[[np.ndarray],np.ndarray],
|
|
797
|
-
truth_calib: Callable[[np.ndarray],np.ndarray],
|
|
798
|
-
cal_range: tuple[float,float],
|
|
799
|
-
n_cal_divs: int = 10000,
|
|
800
|
-
err_dep: EErrDependence = EErrDependence.INDEPENDENT) -> None:
|
|
801
|
-
"""_summary_
|
|
802
|
-
|
|
803
|
-
Parameters
|
|
804
|
-
----------
|
|
805
|
-
assumed_calib : Callable[[np.ndarray],np.ndarray]
|
|
806
|
-
Assumed calibration function taking the input unitless 'signal' and
|
|
807
|
-
converting it to the same units as the physical field being sampled
|
|
808
|
-
by the sensor array.
|
|
809
|
-
truth_calib : Callable[[np.ndarray],np.ndarray]
|
|
810
|
-
Assumed calibration function taking the input unitless 'signal' and
|
|
811
|
-
converting it to the same units as the physical field being sampled
|
|
812
|
-
by the sensor array.
|
|
813
|
-
cal_range : tuple[float,float]
|
|
814
|
-
Range over which the calibration functions are valid. This is
|
|
815
|
-
normally based on a voltage range such as (0,10) volts.
|
|
816
|
-
n_cal_divs : int, optional
|
|
817
|
-
Number of divisions to discretise the the truth calibration function
|
|
818
|
-
for numerical inversion, by default 10000.
|
|
819
|
-
err_dep : EErrDependence, optional
|
|
820
|
-
Error calculation dependence, by default EErrDependence.INDEPENDENT.
|
|
821
|
-
"""
|
|
822
|
-
self._assumed_calib = assumed_calib
|
|
823
|
-
self._truth_calib = truth_calib
|
|
824
|
-
self._cal_range = cal_range
|
|
825
|
-
self._n_cal_divs = n_cal_divs
|
|
826
|
-
self._err_dep = err_dep
|
|
827
|
-
|
|
828
|
-
self._truth_cal_table = np.zeros((n_cal_divs,2))
|
|
829
|
-
self._truth_cal_table[:,0] = np.linspace(cal_range[0],
|
|
830
|
-
cal_range[1],
|
|
831
|
-
n_cal_divs)
|
|
832
|
-
self._truth_cal_table[:,1] = self._truth_calib(
|
|
833
|
-
self._truth_cal_table[:,0])
|
|
834
|
-
|
|
835
|
-
def get_error_dep(self) -> EErrDependence:
|
|
836
|
-
"""Gets the error dependence state for this error calculator. An
|
|
837
|
-
independent error is calculated based on the input truth values as the
|
|
838
|
-
error basis. A dependent error is calculated based on the accumulated
|
|
839
|
-
sensor reading from all preceeding errors in the chain.
|
|
840
|
-
|
|
841
|
-
Returns
|
|
842
|
-
-------
|
|
843
|
-
EErrDependence
|
|
844
|
-
Enumeration defining INDEPENDENT or DEPENDENT behaviour.
|
|
845
|
-
"""
|
|
846
|
-
return self._err_dep
|
|
847
|
-
|
|
848
|
-
def set_error_dep(self, dependence: EErrDependence) -> None:
|
|
849
|
-
"""Sets the error dependence state for this error calculator. An
|
|
850
|
-
independent error is calculated based on the input truth values as the
|
|
851
|
-
error basis. A dependent error is calculated based on the accumulated
|
|
852
|
-
sensor reading from all preceeding errors in the chain.
|
|
853
|
-
|
|
854
|
-
Parameters
|
|
855
|
-
----------
|
|
856
|
-
dependence : EErrDependence
|
|
857
|
-
Enumeration defining INDEPENDENT or DEPENDENT behaviour.
|
|
858
|
-
"""
|
|
859
|
-
self._err_dep = dependence
|
|
860
|
-
|
|
861
|
-
def get_error_type(self) -> EErrType:
|
|
862
|
-
"""Gets the error type.
|
|
863
|
-
|
|
864
|
-
Returns
|
|
865
|
-
-------
|
|
866
|
-
EErrType
|
|
867
|
-
Enumeration definining RANDOM or SYSTEMATIC error types.
|
|
868
|
-
"""
|
|
869
|
-
return EErrType.SYSTEMATIC
|
|
870
|
-
|
|
871
|
-
def calc_errs(self,
|
|
872
|
-
err_basis: np.ndarray,
|
|
873
|
-
sens_data: SensorData,
|
|
874
|
-
) -> tuple[np.ndarray, SensorData]:
|
|
875
|
-
"""Calculates the error array based on the size of the input.
|
|
876
|
-
|
|
877
|
-
Parameters
|
|
878
|
-
----------
|
|
879
|
-
err_basis : np.ndarray
|
|
880
|
-
Array of values with the same dimensions as the sensor measurement
|
|
881
|
-
matrix.
|
|
882
|
-
sens_data : SensorData
|
|
883
|
-
The accumulated sensor state data for all errors prior to this one.
|
|
884
|
-
|
|
885
|
-
Returns
|
|
886
|
-
-------
|
|
887
|
-
tuple[np.ndarray, SensorData]
|
|
888
|
-
Tuple containing the calculated error array and pass through of the
|
|
889
|
-
sensor data object as it is not modified by this class. The returned
|
|
890
|
-
error array has the same shape as the input error basis.
|
|
891
|
-
"""
|
|
892
|
-
# shape=(n_sens,n_comps,n_time_steps)
|
|
893
|
-
signal_from_field = np.interp(err_basis,
|
|
894
|
-
self._truth_cal_table[:,1],
|
|
895
|
-
self._truth_cal_table[:,0])
|
|
896
|
-
# shape=(n_sens,n_comps,n_time_steps)
|
|
897
|
-
field_from_assumed_calib = self._assumed_calib(signal_from_field)
|
|
898
|
-
|
|
899
|
-
sys_errs = field_from_assumed_calib - err_basis
|
|
900
|
-
|
|
901
|
-
return (sys_errs,sens_data)
|
|
902
|
-
|
|
903
|
-
|
|
904
807
|
|
|
905
808
|
|
pyvale/examples/__init__.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
================================================================================
|
|
7
|
-
"""
|
|
1
|
+
#===============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
#===============================================================================
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
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
|
|
11
|
+
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 output.
|
|
14
|
+
|
|
15
|
+
Test case: Scalar field point sensors (thermocouples) on a 2D thermal simulation
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
import matplotlib.pyplot as plt
|
|
20
|
+
import mooseherder as mh
|
|
21
|
+
import pyvale as pyv
|
|
22
|
+
|
|
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()
|