pyvale 2025.5.3__cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.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 +89 -0
- pyvale/analyticmeshgen.py +102 -0
- pyvale/analyticsimdatafactory.py +91 -0
- 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/camera.py +146 -0
- pyvale/cameradata.py +69 -0
- pyvale/cameradata2d.py +84 -0
- pyvale/camerastereo.py +217 -0
- pyvale/cameratools.py +522 -0
- pyvale/cython/rastercyth.c +32211 -0
- pyvale/cython/rastercyth.cpython-311-aarch64-linux-gnu.so +0 -0
- pyvale/cython/rastercyth.py +640 -0
- pyvale/data/__init__.py +5 -0
- 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/data/case13_out.e +0 -0
- pyvale/data/case16_out.e +0 -0
- pyvale/data/case17_out.e +0 -0
- pyvale/data/case18_1_out.e +0 -0
- pyvale/data/case18_2_out.e +0 -0
- pyvale/data/case18_3_out.e +0 -0
- pyvale/data/case25_out.e +0 -0
- pyvale/data/case26_out.e +0 -0
- pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
- pyvale/dataset.py +325 -0
- pyvale/errorcalculator.py +109 -0
- pyvale/errordriftcalc.py +146 -0
- pyvale/errorintegrator.py +336 -0
- pyvale/errorrand.py +607 -0
- pyvale/errorsyscalib.py +134 -0
- pyvale/errorsysdep.py +327 -0
- pyvale/errorsysfield.py +414 -0
- pyvale/errorsysindep.py +808 -0
- pyvale/examples/__init__.py +5 -0
- 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/genanalyticdata/ex1_1_scalarvisualisation.py +35 -0
- pyvale/examples/genanalyticdata/ex1_2_scalarcasebuild.py +43 -0
- pyvale/examples/genanalyticdata/ex2_1_analyticsensors.py +80 -0
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +79 -0
- 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/renderrasterisation/ex_rastenp.py +153 -0
- pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +218 -0
- pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +187 -0
- pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +190 -0
- pyvale/examples/visualisation/ex1_1_plot_traces.py +102 -0
- pyvale/examples/visualisation/ex2_1_animate_sim.py +89 -0
- pyvale/experimentsimulator.py +175 -0
- pyvale/field.py +128 -0
- pyvale/fieldconverter.py +351 -0
- pyvale/fieldsampler.py +111 -0
- pyvale/fieldscalar.py +166 -0
- pyvale/fieldtensor.py +218 -0
- pyvale/fieldtransform.py +388 -0
- pyvale/fieldvector.py +213 -0
- pyvale/generatorsrandom.py +505 -0
- pyvale/imagedef2d.py +569 -0
- pyvale/integratorfactory.py +240 -0
- pyvale/integratorquadrature.py +217 -0
- pyvale/integratorrectangle.py +165 -0
- pyvale/integratorspatial.py +89 -0
- pyvale/integratortype.py +43 -0
- pyvale/output.py +17 -0
- pyvale/pyvaleexceptions.py +11 -0
- pyvale/raster.py +31 -0
- pyvale/rastercy.py +77 -0
- pyvale/rasternp.py +603 -0
- pyvale/rendermesh.py +147 -0
- pyvale/sensorarray.py +178 -0
- pyvale/sensorarrayfactory.py +196 -0
- pyvale/sensorarraypoint.py +278 -0
- pyvale/sensordata.py +71 -0
- pyvale/sensordescriptor.py +213 -0
- pyvale/sensortools.py +142 -0
- pyvale/simcases/case00_HEX20.i +242 -0
- pyvale/simcases/case00_HEX27.i +242 -0
- pyvale/simcases/case00_HEX8.i +242 -0
- pyvale/simcases/case00_TET10.i +242 -0
- pyvale/simcases/case00_TET14.i +242 -0
- pyvale/simcases/case00_TET4.i +242 -0
- pyvale/simcases/case01.i +101 -0
- pyvale/simcases/case02.i +156 -0
- pyvale/simcases/case03.i +136 -0
- pyvale/simcases/case04.i +181 -0
- pyvale/simcases/case05.i +234 -0
- pyvale/simcases/case06.i +305 -0
- pyvale/simcases/case07.geo +135 -0
- pyvale/simcases/case07.i +87 -0
- pyvale/simcases/case08.geo +144 -0
- pyvale/simcases/case08.i +153 -0
- pyvale/simcases/case09.geo +204 -0
- pyvale/simcases/case09.i +87 -0
- pyvale/simcases/case10.geo +204 -0
- pyvale/simcases/case10.i +257 -0
- pyvale/simcases/case11.geo +337 -0
- pyvale/simcases/case11.i +147 -0
- pyvale/simcases/case12.geo +388 -0
- pyvale/simcases/case12.i +329 -0
- pyvale/simcases/case13.i +140 -0
- pyvale/simcases/case14.i +159 -0
- pyvale/simcases/case15.geo +337 -0
- pyvale/simcases/case15.i +150 -0
- pyvale/simcases/case16.geo +391 -0
- pyvale/simcases/case16.i +357 -0
- pyvale/simcases/case17.geo +135 -0
- pyvale/simcases/case17.i +144 -0
- pyvale/simcases/case18.i +254 -0
- pyvale/simcases/case18_1.i +254 -0
- pyvale/simcases/case18_2.i +254 -0
- pyvale/simcases/case18_3.i +254 -0
- pyvale/simcases/case19.geo +252 -0
- pyvale/simcases/case19.i +99 -0
- pyvale/simcases/case20.geo +252 -0
- pyvale/simcases/case20.i +250 -0
- pyvale/simcases/case21.geo +74 -0
- pyvale/simcases/case21.i +155 -0
- pyvale/simcases/case22.geo +82 -0
- pyvale/simcases/case22.i +140 -0
- pyvale/simcases/case23.geo +164 -0
- pyvale/simcases/case23.i +140 -0
- pyvale/simcases/case24.geo +79 -0
- pyvale/simcases/case24.i +123 -0
- pyvale/simcases/case25.geo +82 -0
- pyvale/simcases/case25.i +140 -0
- pyvale/simcases/case26.geo +166 -0
- pyvale/simcases/case26.i +140 -0
- pyvale/simcases/run_1case.py +61 -0
- pyvale/simcases/run_all_cases.py +69 -0
- pyvale/simcases/run_build_case.py +64 -0
- pyvale/simcases/run_example_cases.py +69 -0
- pyvale/simtools.py +67 -0
- pyvale/visualexpplotter.py +191 -0
- pyvale/visualimagedef.py +74 -0
- pyvale/visualimages.py +76 -0
- pyvale/visualopts.py +493 -0
- pyvale/visualsimanimator.py +111 -0
- pyvale/visualsimsensors.py +318 -0
- pyvale/visualtools.py +136 -0
- pyvale/visualtraceplotter.py +142 -0
- pyvale-2025.5.3.dist-info/METADATA +144 -0
- pyvale-2025.5.3.dist-info/RECORD +175 -0
- pyvale-2025.5.3.dist-info/WHEEL +6 -0
- pyvale-2025.5.3.dist-info/licenses/LICENSE +21 -0
- pyvale-2025.5.3.dist-info/top_level.txt +1 -0
- pyvale.libs/libgomp-d22c30c5.so.1.0.0 +0 -0
pyvale/__init__.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
`pyvale`: the python validation engine. Used to simulate experimental data from
|
|
9
|
+
an input multi-physics simulation by explicitly modelling sensors with realistic
|
|
10
|
+
uncertainties. Useful for experimental design, sensor placement optimisation,
|
|
11
|
+
testing simulation validation metrics and testing digital shadows/twins.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# NOTE: this simplifies and decouples how the user calls pyvale from the
|
|
15
|
+
# underlying project structure: the user should be able to use 'pyvale.'
|
|
16
|
+
# and access everything in one layer without multiple import dots
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
from pyvale.dataset import *
|
|
20
|
+
|
|
21
|
+
from pyvale.field import *
|
|
22
|
+
from pyvale.fieldscalar import *
|
|
23
|
+
from pyvale.fieldvector import *
|
|
24
|
+
from pyvale.fieldtensor import *
|
|
25
|
+
from pyvale.fieldconverter import *
|
|
26
|
+
from pyvale.fieldtransform import *
|
|
27
|
+
|
|
28
|
+
from pyvale.integratorspatial import *
|
|
29
|
+
from pyvale.integratorquadrature import *
|
|
30
|
+
from pyvale.integratorrectangle import *
|
|
31
|
+
from pyvale.integratorfactory import *
|
|
32
|
+
|
|
33
|
+
from pyvale.sensordescriptor import *
|
|
34
|
+
from pyvale.sensortools import *
|
|
35
|
+
from pyvale.sensorarray import *
|
|
36
|
+
from pyvale.sensorarrayfactory import *
|
|
37
|
+
from pyvale.sensorarraypoint import *
|
|
38
|
+
from pyvale.sensordata import *
|
|
39
|
+
|
|
40
|
+
from pyvale.camera import *
|
|
41
|
+
from pyvale.cameradata import *
|
|
42
|
+
from pyvale.cameradata2d import *
|
|
43
|
+
from pyvale.cameratools import *
|
|
44
|
+
from pyvale.camerastereo import *
|
|
45
|
+
|
|
46
|
+
import pyvale.cython.rastercyth as rastercyth
|
|
47
|
+
from pyvale.rastercy import *
|
|
48
|
+
|
|
49
|
+
from pyvale.rendermesh import *
|
|
50
|
+
from pyvale.rasternp import *
|
|
51
|
+
|
|
52
|
+
from pyvale.imagedef2d import *
|
|
53
|
+
|
|
54
|
+
from pyvale.errorintegrator import *
|
|
55
|
+
from pyvale.errorrand import *
|
|
56
|
+
from pyvale.errorsysindep import *
|
|
57
|
+
from pyvale.errorsysdep import *
|
|
58
|
+
from pyvale.errorsysfield import *
|
|
59
|
+
from pyvale.errorsyscalib import *
|
|
60
|
+
from pyvale.errordriftcalc import *
|
|
61
|
+
|
|
62
|
+
from pyvale.generatorsrandom import *
|
|
63
|
+
|
|
64
|
+
from pyvale.visualopts import *
|
|
65
|
+
from pyvale.visualtools import *
|
|
66
|
+
from pyvale.visualsimsensors import *
|
|
67
|
+
from pyvale.visualsimanimator import *
|
|
68
|
+
from pyvale.visualexpplotter import *
|
|
69
|
+
from pyvale.visualtraceplotter import *
|
|
70
|
+
from pyvale.visualimages import *
|
|
71
|
+
from pyvale.visualimagedef import *
|
|
72
|
+
|
|
73
|
+
from pyvale.analyticmeshgen import *
|
|
74
|
+
from pyvale.analyticsimdatagenerator import *
|
|
75
|
+
from pyvale.analyticsimdatafactory import *
|
|
76
|
+
|
|
77
|
+
from pyvale.experimentsimulator import *
|
|
78
|
+
|
|
79
|
+
from pyvale.blendercalibrationdata import *
|
|
80
|
+
from pyvale.blenderlightdata import *
|
|
81
|
+
from pyvale.blendermaterialdata import *
|
|
82
|
+
from pyvale.blenderrenderdata import *
|
|
83
|
+
from pyvale.blenderscene import *
|
|
84
|
+
from pyvale.blendertools import *
|
|
85
|
+
from pyvale.simtools import *
|
|
86
|
+
|
|
87
|
+
from pyvale.output import *
|
|
88
|
+
from pyvale.pyvaleexceptions import *
|
|
89
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#===============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
#===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Analytic mesh creation tools for testing pyvale sensor simulation and
|
|
9
|
+
uncertainty quantification functionality with a known analytic function for the
|
|
10
|
+
scalar/vector/tensor field of interest.
|
|
11
|
+
"""
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def rectangle_mesh_2d(leng_x: float,
|
|
16
|
+
leng_y: float,
|
|
17
|
+
n_elem_x: int,
|
|
18
|
+
n_elem_y: int) -> tuple[np.ndarray,np.ndarray]:
|
|
19
|
+
"""Creates the nodal coordinates and element connectivity table for a simple
|
|
20
|
+
2D quad mesh for a rectangular plate.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
leng_x : float
|
|
25
|
+
Length of the plate in the x direction.
|
|
26
|
+
leng_y : float
|
|
27
|
+
Length of the plate in the y direction.
|
|
28
|
+
n_elem_x : int
|
|
29
|
+
Number of elements along the x axis
|
|
30
|
+
n_elem_y : int
|
|
31
|
+
Number of elements along the y axis
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
tuple[np.ndarray,np.ndarray]
|
|
36
|
+
The coordinates and connectivity table as numpy arrays. The coordinates
|
|
37
|
+
have shape=(n_nodes,coord[x,y,z]). The connectivity tables has shape=
|
|
38
|
+
(nodes_per_elem,num_elems).
|
|
39
|
+
"""
|
|
40
|
+
n_elems = n_elem_x*n_elem_y
|
|
41
|
+
n_node_x = n_elem_x+1
|
|
42
|
+
n_node_y = n_elem_y+1
|
|
43
|
+
nodes_per_elem = 4
|
|
44
|
+
|
|
45
|
+
coord_x = np.linspace(0,leng_x,n_node_x)
|
|
46
|
+
coord_y = np.linspace(0,leng_y,n_node_y)
|
|
47
|
+
(coord_grid_x,coord_grid_y) = np.meshgrid(coord_x,coord_y)
|
|
48
|
+
|
|
49
|
+
coord_x = np.atleast_2d(coord_grid_x.flatten()).T
|
|
50
|
+
coord_y = np.atleast_2d(coord_grid_y.flatten()).T
|
|
51
|
+
coord_z = np.zeros_like(coord_x)
|
|
52
|
+
coords = np.hstack((coord_x,coord_y,coord_z))
|
|
53
|
+
|
|
54
|
+
connect = np.zeros((n_elems,nodes_per_elem)).astype(np.int64)
|
|
55
|
+
row = 1
|
|
56
|
+
nn = 0
|
|
57
|
+
for ee in range(n_elems):
|
|
58
|
+
nn += 1
|
|
59
|
+
if nn >= row*n_node_x:
|
|
60
|
+
row += 1
|
|
61
|
+
nn += 1
|
|
62
|
+
|
|
63
|
+
connect[ee,:] = np.array([nn,nn+1,nn+n_node_x+1,nn+n_node_x])
|
|
64
|
+
connect = connect.T
|
|
65
|
+
|
|
66
|
+
return (coords,connect)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def fill_dims_2d(coord_x: np.ndarray,
|
|
70
|
+
coord_y: np.ndarray,
|
|
71
|
+
time: np.ndarray) -> tuple[np.ndarray,np.ndarray,np.ndarray]:
|
|
72
|
+
"""Helper function to generate 2D filled arrays tih consistent dimensions
|
|
73
|
+
for array based maths operations. Takes 1D input vectors for the x, y and
|
|
74
|
+
time dimensions and returns 2D arrays with shape=(num_coords,num_timesteps).
|
|
75
|
+
Useful for evaluating analytical functions in space and time.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
coord_x : np.ndarray
|
|
80
|
+
1D flattened coordinate list for the x axis.
|
|
81
|
+
coord_y : np.ndarray
|
|
82
|
+
1D flattened coordinate list for the y axis.
|
|
83
|
+
time : np.ndarray
|
|
84
|
+
1D array of time steps.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
tuple[np.ndarray,np.ndarray,np.ndarray]
|
|
90
|
+
Filled 2D arrays with shape=(num_coords,num_timesteps) for the x, y and
|
|
91
|
+
time parameters respectively.
|
|
92
|
+
"""
|
|
93
|
+
full_x = np.repeat(np.atleast_2d(coord_x).T,
|
|
94
|
+
time.shape[0],
|
|
95
|
+
axis=1)
|
|
96
|
+
full_y = np.repeat(np.atleast_2d(coord_y).T,
|
|
97
|
+
time.shape[0],
|
|
98
|
+
axis=1)
|
|
99
|
+
full_time = np.repeat(np.atleast_2d(time),
|
|
100
|
+
coord_x.shape[0],
|
|
101
|
+
axis=0)
|
|
102
|
+
return (full_x,full_y,full_time)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Helper functions and mini factory for building standard test meshes with
|
|
9
|
+
analytic functions for the physical fields.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import sympy
|
|
14
|
+
import mooseherder as mh
|
|
15
|
+
from pyvale.analyticsimdatagenerator import (AnalyticData2D,
|
|
16
|
+
AnalyticSimDataGen)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def standard_case_2d() -> AnalyticData2D:
|
|
20
|
+
"""Created the standard 2D analytic test case which is a plate with
|
|
21
|
+
dimensions 10x7.5 (x,y), number of elements 40x30 (x,y), and time steps of
|
|
22
|
+
0 to 10 in increments of 1.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
AnalyticCaseData2D
|
|
27
|
+
_description_
|
|
28
|
+
"""
|
|
29
|
+
case_data = AnalyticData2D()
|
|
30
|
+
case_data.length_x = 10.0
|
|
31
|
+
case_data.length_y = 7.5
|
|
32
|
+
n_elem_mult = 10
|
|
33
|
+
case_data.num_elem_x = 4*n_elem_mult
|
|
34
|
+
case_data.num_elem_y = 3*n_elem_mult
|
|
35
|
+
case_data.time_steps = np.linspace(0.0,1.0,11)
|
|
36
|
+
return case_data
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AnalyticCaseFactory:
|
|
40
|
+
"""Namespace for function used to build pre-defined 2D meshes and fields
|
|
41
|
+
based on analytic functions for testing the sensor simulation functionality
|
|
42
|
+
of pyvale.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def scalar_linear_2d() -> tuple[mh.SimData,AnalyticSimDataGen]:
|
|
47
|
+
"""_summary_
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
tuple[mh.SimData,AnalyticSimDataGenerator]
|
|
52
|
+
_description_
|
|
53
|
+
"""
|
|
54
|
+
case_data = standard_case_2d()
|
|
55
|
+
(sym_y,sym_x,sym_t) = sympy.symbols("y,x,t")
|
|
56
|
+
case_data.funcs_x = (20.0/case_data.length_x * sym_x,)
|
|
57
|
+
case_data.funcs_y = (10.0/case_data.length_y * sym_y,)
|
|
58
|
+
case_data.funcs_t = (sym_t,)
|
|
59
|
+
case_data.offsets_space = (20.0,)
|
|
60
|
+
case_data.offsets_time = (0.0,)
|
|
61
|
+
|
|
62
|
+
data_gen = AnalyticSimDataGen(case_data)
|
|
63
|
+
|
|
64
|
+
sim_data = data_gen.generate_sim_data()
|
|
65
|
+
|
|
66
|
+
return (sim_data,data_gen)
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def scalar_quadratic_2d() -> tuple[mh.SimData,AnalyticSimDataGen]:
|
|
70
|
+
"""_summary_
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
tuple[mh.SimData,AnalyticSimDataGenerator]
|
|
75
|
+
_description_
|
|
76
|
+
"""
|
|
77
|
+
case_data = standard_case_2d()
|
|
78
|
+
(sym_y,sym_x,sym_t) = sympy.symbols("y,x,t")
|
|
79
|
+
case_data.funcs_x = (sym_x*(sym_x - case_data.length_x),)
|
|
80
|
+
case_data.funcs_y = (sym_y*(sym_y - case_data.length_y),)
|
|
81
|
+
case_data.funcs_t = (sym_t,)
|
|
82
|
+
|
|
83
|
+
data_gen = AnalyticSimDataGen(case_data)
|
|
84
|
+
|
|
85
|
+
sim_data = data_gen.generate_sim_data()
|
|
86
|
+
|
|
87
|
+
return (sim_data,data_gen)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#===============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
#===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Generic tools for creating SimData objects based on analytic functions for the
|
|
9
|
+
underlying physical fields. Useful for testing pyvale.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
import numpy as np
|
|
14
|
+
import sympy
|
|
15
|
+
import mooseherder as mh
|
|
16
|
+
from pyvale.analyticmeshgen import rectangle_mesh_2d, fill_dims_2d
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(slots=True)
|
|
20
|
+
class AnalyticData2D:
|
|
21
|
+
"""Dataclass for describing a 2D analytic test case for pyvale sensor
|
|
22
|
+
simulation. Includes information about the geometry, the mesh and the
|
|
23
|
+
analytic functions used to generate the field data.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
length_x: float = 10.0
|
|
27
|
+
"""Length of the test case geometry in the X direction in length units.
|
|
28
|
+
Defaults to 10.0.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
length_y: float = 7.5
|
|
32
|
+
"""Length of the test case geometry in the Y direction in length units.
|
|
33
|
+
Defaults to 7.5.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
num_elem_x: int = 4
|
|
37
|
+
"""Number of elements in the mesh in the X direction. Defaults to 4.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
num_elem_y: int = 3
|
|
41
|
+
"""Number of elements in the mesh in the Y direction. Defaults to 3.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
time_steps: np.ndarray | None = None
|
|
45
|
+
"""1D array of time steps for the analytic test case. Defaults to None which
|
|
46
|
+
is for a test case that only has spatially varying functions.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
field_keys: tuple[str,...] = ('scalar',)
|
|
50
|
+
"""Keys used to describe the field of interest. For a scalar field there is
|
|
51
|
+
only a single key. For a vector field 2 keys are required in 2D (xx,yy). For
|
|
52
|
+
a tensor field 3 keys are required for 2D (xx,yy,xy). Defaults to a single
|
|
53
|
+
key for a scalar field: ("scalar",).
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
funcs_x: tuple[sympy.Expr,...] | None = None
|
|
57
|
+
"""Analytic functions describing the field variation as a function of the x
|
|
58
|
+
coordinate. This tuple should have the same number of functions as the
|
|
59
|
+
number of field keys. Analytic functions in x, y and t are multiplied
|
|
60
|
+
together so setting a function to a constant of 1 will have no effect.
|
|
61
|
+
"""
|
|
62
|
+
funcs_y: tuple[sympy.Expr,...] | None = None
|
|
63
|
+
"""Analytic functions describing the field variation as a function of the y
|
|
64
|
+
coordinate. This tuple should have the same number of functions as the
|
|
65
|
+
number of field keys. Analytic functions in x, y and t are multiplied
|
|
66
|
+
together so setting a function to a constant of 1 will have no effect.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
funcs_t: tuple[sympy.Expr,...] | None = None
|
|
70
|
+
"""Analytic functions describing the field variation as a function of time
|
|
71
|
+
This tuple should have the same number of functions as the number of field
|
|
72
|
+
keys. Analytic functions in x, y and t are multiplied together so setting a
|
|
73
|
+
function to a constant of 1 will have no effect.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
symbols: tuple[sympy.Symbol,...] = (sympy.Symbol("y"),
|
|
77
|
+
sympy.Symbol("x"),
|
|
78
|
+
sympy.Symbol("t"))
|
|
79
|
+
"""Sympy symbols describing the relevant dimensions of the problem. For 2D
|
|
80
|
+
spatial dimensions default to x and y and time is denoted t. Note that these
|
|
81
|
+
are the symbols used to describe the analytic field functions.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
offsets_space: tuple[float,...] = (0.0,)
|
|
85
|
+
"""Constants which are added to the physical field functions in each spatial
|
|
86
|
+
dimensions.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
offsets_time: tuple[float,...] = (0.0,)
|
|
90
|
+
"""Constant which is added to the physical field function in time.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
nodes_per_elem: int = 4
|
|
94
|
+
"""Number of nodes per element. Currently only rectangular meshes and with
|
|
95
|
+
4 nodes per element are supported. Defaults to 4.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AnalyticSimDataGen:
|
|
100
|
+
"""Class for generating analytic field data as a `SimData` object to test
|
|
101
|
+
the sensor simulation functionality of pyvale. Provides tools to evaluate
|
|
102
|
+
the analytic field functions at a given spatial coordinate/time to check
|
|
103
|
+
against pyvale interpolation functions. Currently only support 2D cases.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
__slots__ = ("case_data","coords","connect","field_sym_funcs",
|
|
107
|
+
"field_lam_funcs","field_eval")
|
|
108
|
+
|
|
109
|
+
def __init__(self, case_data: AnalyticData2D
|
|
110
|
+
) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
case_data : AnalyticCaseData2D
|
|
115
|
+
Data class containing the parameters required to create the analytic
|
|
116
|
+
mesh and the underlying physical field functions.
|
|
117
|
+
"""
|
|
118
|
+
self.case_data = case_data
|
|
119
|
+
(self.coords,self.connect) = rectangle_mesh_2d(case_data.length_x,
|
|
120
|
+
case_data.length_y,
|
|
121
|
+
case_data.num_elem_x,
|
|
122
|
+
case_data.num_elem_y)
|
|
123
|
+
|
|
124
|
+
self.field_sym_funcs = dict()
|
|
125
|
+
self.field_lam_funcs = dict()
|
|
126
|
+
for ii,kk in enumerate(case_data.field_keys):
|
|
127
|
+
self.field_sym_funcs[kk] = ((case_data.funcs_x[ii] *
|
|
128
|
+
case_data.funcs_y[ii] +
|
|
129
|
+
case_data.offsets_space[ii]) *
|
|
130
|
+
(case_data.funcs_t[ii] +
|
|
131
|
+
case_data.offsets_time[ii]))
|
|
132
|
+
|
|
133
|
+
self.field_lam_funcs[kk] = sympy.lambdify(case_data.symbols,
|
|
134
|
+
self.field_sym_funcs[kk],
|
|
135
|
+
'numpy')
|
|
136
|
+
self.field_eval = dict()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def evaluate_field_truth(self,
|
|
140
|
+
field_key: str,
|
|
141
|
+
coords: np.ndarray,
|
|
142
|
+
time_steps: np.ndarray | None = None) -> np.ndarray:
|
|
143
|
+
"""Calculates the 'truth' from the analytical functions describing the
|
|
144
|
+
physical fields at the specified coordinates and time steps.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
field_key : str
|
|
149
|
+
Key for the underlying physical field.
|
|
150
|
+
coords : np.ndarray
|
|
151
|
+
Coordinates at which to evaluate the analytic physical field. shape
|
|
152
|
+
=(n_coords,coord[x,y,z])
|
|
153
|
+
time_steps : np.ndarray | None, optional
|
|
154
|
+
Time steps at which to evaluate the physical field, by default None.
|
|
155
|
+
If this is none the evaluation time steps are assumed to match the
|
|
156
|
+
nominal time steps.
|
|
157
|
+
|
|
158
|
+
Returns
|
|
159
|
+
-------
|
|
160
|
+
np.ndarray
|
|
161
|
+
Array of analytic field evaluations with shape = (n_coords,
|
|
162
|
+
n_time_steps)
|
|
163
|
+
"""
|
|
164
|
+
if time_steps is None:
|
|
165
|
+
time_steps = self.case_data.time_steps
|
|
166
|
+
|
|
167
|
+
(x_eval,y_eval,t_eval) = fill_dims_2d(coords[:,0],
|
|
168
|
+
coords[:,1],
|
|
169
|
+
time_steps)
|
|
170
|
+
|
|
171
|
+
field_vals = self.field_lam_funcs[field_key](y_eval,
|
|
172
|
+
x_eval,
|
|
173
|
+
t_eval)
|
|
174
|
+
return field_vals
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def evaluate_all_fields_truth(self,
|
|
178
|
+
coords: np.ndarray,
|
|
179
|
+
time_steps: np.ndarray | None = None
|
|
180
|
+
) -> dict[str,np.ndarray]:
|
|
181
|
+
"""Evaluates all analytic physical fields at the specified coordinates
|
|
182
|
+
and time steps.
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
coords : np.ndarray
|
|
187
|
+
Coordinates at which to evaluate the analytic physical field. shape
|
|
188
|
+
=(n_coords,coord[x,y,z])
|
|
189
|
+
time_steps : np.ndarray | None, optional
|
|
190
|
+
Time steps at which to evaluate the physical field, by default None.
|
|
191
|
+
If this is none the evaluation time steps are assumed to match the
|
|
192
|
+
nominal time steps.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
dict[str,np.ndarray]
|
|
197
|
+
Dictionary keyed by the field name giving a numpy array with shape =
|
|
198
|
+
(n_coords,n_timesteps)
|
|
199
|
+
"""
|
|
200
|
+
if time_steps is None:
|
|
201
|
+
time_steps = self.case_data.time_steps
|
|
202
|
+
|
|
203
|
+
(x_eval,y_eval,t_eval) = fill_dims_2d(coords[:,0],
|
|
204
|
+
coords[:,1],
|
|
205
|
+
time_steps)
|
|
206
|
+
|
|
207
|
+
eval_comps = dict()
|
|
208
|
+
for kk in self.case_data.field_keys:
|
|
209
|
+
eval_comps[kk] = self.field_lam_funcs[kk](y_eval,
|
|
210
|
+
x_eval,
|
|
211
|
+
t_eval)
|
|
212
|
+
return eval_comps
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def evaluate_field_at_nodes(self, field_key: str) -> np.ndarray:
|
|
216
|
+
"""Evaluates the underlying physical field at the node locations and
|
|
217
|
+
nominal time steps.
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
----------
|
|
221
|
+
field_key : str
|
|
222
|
+
String key for the field to be evaluated.
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
np.ndarray
|
|
227
|
+
Array of field evaluations with shape=(n_nodes,n_timesteps)
|
|
228
|
+
"""
|
|
229
|
+
(x_eval,y_eval,t_eval) = fill_dims_2d(self.coords[:,0],
|
|
230
|
+
self.coords[:,1],
|
|
231
|
+
self.case_data.time_steps)
|
|
232
|
+
|
|
233
|
+
self.field_eval[field_key] = self.field_lam_funcs[field_key](y_eval,
|
|
234
|
+
x_eval,
|
|
235
|
+
t_eval)
|
|
236
|
+
return self.field_eval[field_key]
|
|
237
|
+
|
|
238
|
+
def evaluate_all_fields_at_nodes(self) -> dict[str,np.ndarray]:
|
|
239
|
+
"""Evaluates all physical fields at the node locations and nominal time
|
|
240
|
+
steps.
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
dict[str,np.ndarray]
|
|
245
|
+
Dictionary keyed by the field name giving a numpy array with shape =
|
|
246
|
+
(n_coords,n_timesteps)
|
|
247
|
+
"""
|
|
248
|
+
(x_eval,y_eval,t_eval) = fill_dims_2d(self.coords[:,0],
|
|
249
|
+
self.coords[:,1],
|
|
250
|
+
self.case_data.time_steps)
|
|
251
|
+
eval_comps = dict()
|
|
252
|
+
for kk in self.case_data.field_keys:
|
|
253
|
+
eval_comps[kk] = self.field_lam_funcs[kk](y_eval,
|
|
254
|
+
x_eval,
|
|
255
|
+
t_eval)
|
|
256
|
+
self.field_eval = eval_comps
|
|
257
|
+
return self.field_eval
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def generate_sim_data(self) -> mh.SimData:
|
|
261
|
+
"""Creates a SimData object using the analytic case geometry, mesh
|
|
262
|
+
parameters and the underlying physical fields.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
mh.SimData
|
|
267
|
+
SimData object built from the analytic case data.
|
|
268
|
+
"""
|
|
269
|
+
sim_data = mh.SimData()
|
|
270
|
+
sim_data.num_spat_dims = 2
|
|
271
|
+
sim_data.time = self.case_data.time_steps
|
|
272
|
+
sim_data.coords = self.coords
|
|
273
|
+
sim_data.connect = {'connect1': self.connect}
|
|
274
|
+
|
|
275
|
+
if not self.field_eval:
|
|
276
|
+
self.evaluate_all_fields_at_nodes()
|
|
277
|
+
sim_data.node_vars = self.field_eval
|
|
278
|
+
|
|
279
|
+
return sim_data
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_visualisation_grid(self,
|
|
283
|
+
field_key: str | None = None,
|
|
284
|
+
time_step: int = -1
|
|
285
|
+
) -> tuple[np.ndarray,np.ndarray,np.ndarray]:
|
|
286
|
+
"""Creates a visualisation grid for plotting heatmaps of the specified
|
|
287
|
+
analytic field using matplotlib.
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
field_key : str | None, optional
|
|
292
|
+
String key for the field to be visualised, by default None. If None
|
|
293
|
+
then the first field key is used.
|
|
294
|
+
time_step : int, optional
|
|
295
|
+
Time step at which to extract the field to be plotted, by default -1
|
|
296
|
+
|
|
297
|
+
Returns
|
|
298
|
+
-------
|
|
299
|
+
tuple[np.ndarray,np.ndarray,np.ndarray]
|
|
300
|
+
Tuple containing the 2D grid of x coordinates, grid of y coordinates
|
|
301
|
+
and a grid of field evaluations.
|
|
302
|
+
"""
|
|
303
|
+
if field_key is None:
|
|
304
|
+
field_key = self.case_data.field_keys[0]
|
|
305
|
+
|
|
306
|
+
grid_shape = (self.case_data.num_elem_y+1,
|
|
307
|
+
self.case_data.num_elem_x+1)
|
|
308
|
+
|
|
309
|
+
grid_x = np.atleast_2d(self.coords[:,0]).T.reshape(grid_shape)
|
|
310
|
+
grid_y = np.atleast_2d(self.coords[:,1]).T.reshape(grid_shape)
|
|
311
|
+
|
|
312
|
+
if not self.field_eval:
|
|
313
|
+
self.evaluate_all_fields_at_nodes()
|
|
314
|
+
|
|
315
|
+
scalar_grid = np.reshape(self.field_eval[field_key][:,time_step],grid_shape)
|
|
316
|
+
|
|
317
|
+
return (grid_x,grid_y,scalar_grid)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
#TODO: doctsrings
|
|
9
|
+
|
|
10
|
+
@dataclass(slots=True)
|
|
11
|
+
class CalibrationData:
|
|
12
|
+
angle_lims: tuple = (-10, 10)
|
|
13
|
+
angle_step: int = 5
|
|
14
|
+
plunge_lims: tuple = (-5, 5)
|
|
15
|
+
plunge_step: int = 5
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import Enum
|
|
8
|
+
import numpy as np
|
|
9
|
+
from scipy.spatial.transform import Rotation
|
|
10
|
+
|
|
11
|
+
#TODO: docstrings
|
|
12
|
+
|
|
13
|
+
class BlenderLightType(Enum):
|
|
14
|
+
POINT = 'POINT'
|
|
15
|
+
SUN = 'SUN'
|
|
16
|
+
SPOT = 'SPOT'
|
|
17
|
+
AREA = 'AREA'
|
|
18
|
+
|
|
19
|
+
@dataclass(slots=True)
|
|
20
|
+
class BlenderLightData():
|
|
21
|
+
pos_world: np.ndarray
|
|
22
|
+
rot_world: Rotation
|
|
23
|
+
energy: int # NOTE: In Watts
|
|
24
|
+
type: BlenderLightType = BlenderLightType.POINT
|
|
25
|
+
shadow_soft_size: float = 1.5
|
|
26
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
#TODO: docstrings
|
|
9
|
+
|
|
10
|
+
@dataclass(slots=True)
|
|
11
|
+
class BlenderMaterialData():
|
|
12
|
+
# TODO: Add other material properties here
|
|
13
|
+
roughness: float = 1.0
|
|
14
|
+
metallic: float = 0.0
|
|
15
|
+
interpolant: int = 'Cubic'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# pyvale: the python validation engine
|
|
3
|
+
# License: MIT
|
|
4
|
+
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from pyvale.cameradata import CameraData
|
|
11
|
+
from pyvale.output import Outputs
|
|
12
|
+
|
|
13
|
+
#TODO: docstrings
|
|
14
|
+
|
|
15
|
+
class RenderEngine(Enum):
|
|
16
|
+
"""Different render engines on Blender
|
|
17
|
+
"""
|
|
18
|
+
CYCLES = "CYCLES"
|
|
19
|
+
EEVEE = "BLENDER_EEVEE_NEXT"
|
|
20
|
+
WORKBENCH = "BLENDER_WORKBENCH"
|
|
21
|
+
|
|
22
|
+
@dataclass(slots=True)
|
|
23
|
+
class RenderData:
|
|
24
|
+
cam_data: CameraData | tuple[CameraData, CameraData]
|
|
25
|
+
base_dir: Path = Outputs.base_dir
|
|
26
|
+
samples: int = 2
|
|
27
|
+
engine: RenderEngine = RenderEngine.CYCLES
|
|
28
|
+
max_bounces: int = 12
|
|
29
|
+
bit_size: int = 8
|
|
30
|
+
threads:int = 4
|