pyvale 2025.4.1__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 +18 -3
- pyvale/analyticmeshgen.py +1 -0
- pyvale/analyticsimdatafactory.py +18 -13
- pyvale/analyticsimdatagenerator.py +105 -72
- 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 +6 -5
- pyvale/cameradata.py +25 -7
- pyvale/cameradata2d.py +6 -4
- pyvale/camerastereo.py +217 -0
- pyvale/cameratools.py +206 -11
- pyvale/cython/rastercyth.py +6 -2
- pyvale/data/cal_target.tiff +0 -0
- pyvale/dataset.py +73 -14
- pyvale/errorcalculator.py +8 -10
- pyvale/errordriftcalc.py +10 -9
- pyvale/errorintegrator.py +19 -21
- pyvale/errorrand.py +33 -39
- pyvale/errorsyscalib.py +134 -0
- pyvale/errorsysdep.py +19 -22
- pyvale/errorsysfield.py +49 -41
- pyvale/errorsysindep.py +79 -175
- 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 +3 -2
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_oneframe.py +2 -2
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_cypara.py +3 -8
- pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_pypara.py +6 -7
- pyvale/examples/{ex1_4_thermal2d.py → visualisation/ex1_1_plot_traces.py} +32 -16
- pyvale/examples/{features/ex_animation_tools_3dmonoblock.py → visualisation/ex2_1_animate_sim.py} +37 -31
- pyvale/experimentsimulator.py +107 -30
- pyvale/field.py +2 -9
- pyvale/fieldconverter.py +98 -22
- pyvale/fieldsampler.py +2 -2
- pyvale/fieldscalar.py +10 -10
- pyvale/fieldtensor.py +15 -17
- pyvale/fieldtransform.py +7 -2
- pyvale/fieldvector.py +6 -7
- pyvale/generatorsrandom.py +25 -47
- pyvale/imagedef2d.py +6 -2
- pyvale/integratorfactory.py +2 -2
- pyvale/integratorquadrature.py +50 -24
- pyvale/integratorrectangle.py +85 -7
- pyvale/integratorspatial.py +4 -4
- pyvale/integratortype.py +3 -3
- pyvale/output.py +17 -0
- pyvale/pyvaleexceptions.py +11 -0
- pyvale/raster.py +6 -5
- pyvale/rastercy.py +6 -4
- pyvale/rasternp.py +6 -4
- pyvale/rendermesh.py +6 -2
- pyvale/sensorarray.py +2 -2
- pyvale/sensorarrayfactory.py +52 -65
- pyvale/sensorarraypoint.py +29 -30
- pyvale/sensordata.py +2 -2
- pyvale/sensordescriptor.py +138 -25
- pyvale/sensortools.py +3 -3
- pyvale/simtools.py +67 -0
- pyvale/visualexpplotter.py +99 -57
- pyvale/visualimagedef.py +11 -7
- pyvale/visualimages.py +6 -4
- pyvale/visualopts.py +372 -58
- pyvale/visualsimanimator.py +42 -13
- pyvale/visualsimsensors.py +318 -0
- pyvale/visualtools.py +69 -13
- pyvale/visualtraceplotter.py +52 -165
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/METADATA +17 -14
- pyvale-2025.5.1.dist-info/RECORD +172 -0
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/WHEEL +1 -1
- pyvale/examples/analyticdatagen/__init__.py +0 -5
- pyvale/examples/ex1_1_thermal2d.py +0 -86
- pyvale/examples/ex1_2_thermal2d.py +0 -108
- pyvale/examples/ex1_3_thermal2d.py +0 -110
- pyvale/examples/ex1_5_thermal2d.py +0 -102
- pyvale/examples/ex2_1_thermal3d .py +0 -84
- pyvale/examples/ex2_2_thermal3d.py +0 -51
- pyvale/examples/ex2_3_thermal3d.py +0 -106
- pyvale/examples/ex3_1_displacement2d.py +0 -44
- pyvale/examples/ex3_2_displacement2d.py +0 -76
- pyvale/examples/ex3_3_displacement2d.py +0 -101
- pyvale/examples/ex3_4_displacement2d.py +0 -102
- pyvale/examples/ex4_1_strain2d.py +0 -54
- pyvale/examples/ex4_2_strain2d.py +0 -76
- pyvale/examples/ex4_3_strain2d.py +0 -97
- pyvale/examples/ex5_1_multiphysics2d.py +0 -75
- pyvale/examples/ex6_1_multiphysics2d_expsim.py +0 -115
- pyvale/examples/ex6_2_multiphysics3d_expsim.py +0 -160
- pyvale/examples/features/__init__.py +0 -5
- 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/optimcheckfuncs.py +0 -153
- pyvale/visualsimplotter.py +0 -182
- pyvale-2025.4.1.dist-info/RECORD +0 -163
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/licenses/LICENSE +0 -0
- {pyvale-2025.4.1.dist-info → pyvale-2025.5.1.dist-info}/top_level.txt +0 -0
pyvale/__init__.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ==============================================================================
|
|
2
2
|
# pyvale: the python validation engine
|
|
3
3
|
# License: MIT
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
-
#
|
|
5
|
+
# ==============================================================================
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
8
|
`pyvale`: the python validation engine. Used to simulate experimental data from
|
|
@@ -15,6 +15,7 @@ testing simulation validation metrics and testing digital shadows/twins.
|
|
|
15
15
|
# underlying project structure: the user should be able to use 'pyvale.'
|
|
16
16
|
# and access everything in one layer without multiple import dots
|
|
17
17
|
|
|
18
|
+
|
|
18
19
|
from pyvale.dataset import *
|
|
19
20
|
|
|
20
21
|
from pyvale.field import *
|
|
@@ -40,6 +41,7 @@ from pyvale.camera import *
|
|
|
40
41
|
from pyvale.cameradata import *
|
|
41
42
|
from pyvale.cameradata2d import *
|
|
42
43
|
from pyvale.cameratools import *
|
|
44
|
+
from pyvale.camerastereo import *
|
|
43
45
|
|
|
44
46
|
import pyvale.cython.rastercyth as rastercyth
|
|
45
47
|
from pyvale.rastercy import *
|
|
@@ -54,13 +56,14 @@ from pyvale.errorrand import *
|
|
|
54
56
|
from pyvale.errorsysindep import *
|
|
55
57
|
from pyvale.errorsysdep import *
|
|
56
58
|
from pyvale.errorsysfield import *
|
|
59
|
+
from pyvale.errorsyscalib import *
|
|
57
60
|
from pyvale.errordriftcalc import *
|
|
58
61
|
|
|
59
62
|
from pyvale.generatorsrandom import *
|
|
60
63
|
|
|
61
64
|
from pyvale.visualopts import *
|
|
62
65
|
from pyvale.visualtools import *
|
|
63
|
-
from pyvale.
|
|
66
|
+
from pyvale.visualsimsensors import *
|
|
64
67
|
from pyvale.visualsimanimator import *
|
|
65
68
|
from pyvale.visualexpplotter import *
|
|
66
69
|
from pyvale.visualtraceplotter import *
|
|
@@ -72,3 +75,15 @@ from pyvale.analyticsimdatagenerator import *
|
|
|
72
75
|
from pyvale.analyticsimdatafactory import *
|
|
73
76
|
|
|
74
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
|
+
|
pyvale/analyticmeshgen.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# License: MIT
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
5
|
#===============================================================================
|
|
6
|
+
|
|
6
7
|
"""
|
|
7
8
|
Analytic mesh creation tools for testing pyvale sensor simulation and
|
|
8
9
|
uncertainty quantification functionality with a known analytic function for the
|
pyvale/analyticsimdatafactory.py
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ==============================================================================
|
|
2
2
|
# pyvale: the python validation engine
|
|
3
3
|
# License: MIT
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
-
#
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Helper functions and mini factory for building standard test meshes with
|
|
9
|
+
analytic functions for the physical fields.
|
|
10
|
+
"""
|
|
6
11
|
|
|
7
12
|
import numpy as np
|
|
8
13
|
import sympy
|
|
9
14
|
import mooseherder as mh
|
|
10
|
-
from pyvale.analyticsimdatagenerator import (
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
from pyvale.analyticsimdatagenerator import (AnalyticData2D,
|
|
16
|
+
AnalyticSimDataGen)
|
|
13
17
|
|
|
14
18
|
|
|
15
|
-
def standard_case_2d() ->
|
|
19
|
+
def standard_case_2d() -> AnalyticData2D:
|
|
16
20
|
"""Created the standard 2D analytic test case which is a plate with
|
|
17
21
|
dimensions 10x7.5 (x,y), number of elements 40x30 (x,y), and time steps of
|
|
18
22
|
0 to 10 in increments of 1.
|
|
@@ -22,7 +26,7 @@ def standard_case_2d() -> AnalyticCaseData2D:
|
|
|
22
26
|
AnalyticCaseData2D
|
|
23
27
|
_description_
|
|
24
28
|
"""
|
|
25
|
-
case_data =
|
|
29
|
+
case_data = AnalyticData2D()
|
|
26
30
|
case_data.length_x = 10.0
|
|
27
31
|
case_data.length_y = 7.5
|
|
28
32
|
n_elem_mult = 10
|
|
@@ -33,12 +37,13 @@ def standard_case_2d() -> AnalyticCaseData2D:
|
|
|
33
37
|
|
|
34
38
|
|
|
35
39
|
class AnalyticCaseFactory:
|
|
36
|
-
"""
|
|
37
|
-
testing the sensor simulation functionality
|
|
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.
|
|
38
43
|
"""
|
|
39
44
|
|
|
40
45
|
@staticmethod
|
|
41
|
-
def scalar_linear_2d() -> tuple[mh.SimData,
|
|
46
|
+
def scalar_linear_2d() -> tuple[mh.SimData,AnalyticSimDataGen]:
|
|
42
47
|
"""_summary_
|
|
43
48
|
|
|
44
49
|
Returns
|
|
@@ -54,14 +59,14 @@ class AnalyticCaseFactory:
|
|
|
54
59
|
case_data.offsets_space = (20.0,)
|
|
55
60
|
case_data.offsets_time = (0.0,)
|
|
56
61
|
|
|
57
|
-
data_gen =
|
|
62
|
+
data_gen = AnalyticSimDataGen(case_data)
|
|
58
63
|
|
|
59
64
|
sim_data = data_gen.generate_sim_data()
|
|
60
65
|
|
|
61
66
|
return (sim_data,data_gen)
|
|
62
67
|
|
|
63
68
|
@staticmethod
|
|
64
|
-
def scalar_quadratic_2d() -> tuple[mh.SimData,
|
|
69
|
+
def scalar_quadratic_2d() -> tuple[mh.SimData,AnalyticSimDataGen]:
|
|
65
70
|
"""_summary_
|
|
66
71
|
|
|
67
72
|
Returns
|
|
@@ -75,7 +80,7 @@ class AnalyticCaseFactory:
|
|
|
75
80
|
case_data.funcs_y = (sym_y*(sym_y - case_data.length_y),)
|
|
76
81
|
case_data.funcs_t = (sym_t,)
|
|
77
82
|
|
|
78
|
-
data_gen =
|
|
83
|
+
data_gen = AnalyticSimDataGen(case_data)
|
|
79
84
|
|
|
80
85
|
sim_data = data_gen.generate_sim_data()
|
|
81
86
|
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
# Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
5
|
#===============================================================================
|
|
6
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
|
+
|
|
7
12
|
from dataclasses import dataclass
|
|
8
13
|
import numpy as np
|
|
9
14
|
import sympy
|
|
@@ -12,7 +17,7 @@ from pyvale.analyticmeshgen import rectangle_mesh_2d, fill_dims_2d
|
|
|
12
17
|
|
|
13
18
|
|
|
14
19
|
@dataclass(slots=True)
|
|
15
|
-
class
|
|
20
|
+
class AnalyticData2D:
|
|
16
21
|
"""Dataclass for describing a 2D analytic test case for pyvale sensor
|
|
17
22
|
simulation. Includes information about the geometry, the mesh and the
|
|
18
23
|
analytic functions used to generate the field data.
|
|
@@ -72,89 +77,98 @@ class AnalyticCaseData2D:
|
|
|
72
77
|
sympy.Symbol("x"),
|
|
73
78
|
sympy.Symbol("t"))
|
|
74
79
|
"""Sympy symbols describing the relevant dimensions of the problem. For 2D
|
|
75
|
-
spatial dimensions default to x and y and time is denoted t.
|
|
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.
|
|
76
82
|
"""
|
|
77
83
|
|
|
78
84
|
offsets_space: tuple[float,...] = (0.0,)
|
|
79
|
-
"""
|
|
85
|
+
"""Constants which are added to the physical field functions in each spatial
|
|
86
|
+
dimensions.
|
|
80
87
|
"""
|
|
81
88
|
|
|
82
89
|
offsets_time: tuple[float,...] = (0.0,)
|
|
83
|
-
"""
|
|
90
|
+
"""Constant which is added to the physical field function in time.
|
|
84
91
|
"""
|
|
85
92
|
|
|
86
93
|
nodes_per_elem: int = 4
|
|
87
|
-
"""
|
|
94
|
+
"""Number of nodes per element. Currently only rectangular meshes and with
|
|
95
|
+
4 nodes per element are supported. Defaults to 4.
|
|
88
96
|
"""
|
|
89
97
|
|
|
90
98
|
|
|
91
|
-
class
|
|
99
|
+
class AnalyticSimDataGen:
|
|
92
100
|
"""Class for generating analytic field data as a `SimData` object to test
|
|
93
101
|
the sensor simulation functionality of pyvale. Provides tools to evaluate
|
|
94
|
-
the analytic field functions at a given spatial coordinate
|
|
95
|
-
against pyvale interpolation functions.
|
|
102
|
+
the analytic field functions at a given spatial coordinate/time to check
|
|
103
|
+
against pyvale interpolation functions. Currently only support 2D cases.
|
|
96
104
|
"""
|
|
97
105
|
|
|
98
|
-
__slots__ = ("
|
|
106
|
+
__slots__ = ("case_data","coords","connect","field_sym_funcs",
|
|
107
|
+
"field_lam_funcs","field_eval")
|
|
99
108
|
|
|
100
|
-
def __init__(self, case_data:
|
|
109
|
+
def __init__(self, case_data: AnalyticData2D
|
|
101
110
|
) -> None:
|
|
102
|
-
"""
|
|
103
|
-
|
|
111
|
+
"""
|
|
104
112
|
Parameters
|
|
105
113
|
----------
|
|
106
114
|
case_data : AnalyticCaseData2D
|
|
107
|
-
|
|
115
|
+
Data class containing the parameters required to create the analytic
|
|
116
|
+
mesh and the underlying physical field functions.
|
|
108
117
|
"""
|
|
109
|
-
self.
|
|
110
|
-
(self.
|
|
118
|
+
self.case_data = case_data
|
|
119
|
+
(self.coords,self.connect) = rectangle_mesh_2d(case_data.length_x,
|
|
111
120
|
case_data.length_y,
|
|
112
121
|
case_data.num_elem_x,
|
|
113
122
|
case_data.num_elem_y)
|
|
114
123
|
|
|
115
|
-
self.
|
|
116
|
-
self.
|
|
124
|
+
self.field_sym_funcs = dict()
|
|
125
|
+
self.field_lam_funcs = dict()
|
|
117
126
|
for ii,kk in enumerate(case_data.field_keys):
|
|
118
|
-
self.
|
|
127
|
+
self.field_sym_funcs[kk] = ((case_data.funcs_x[ii] *
|
|
119
128
|
case_data.funcs_y[ii] +
|
|
120
129
|
case_data.offsets_space[ii]) *
|
|
121
130
|
(case_data.funcs_t[ii] +
|
|
122
131
|
case_data.offsets_time[ii]))
|
|
123
132
|
|
|
124
|
-
self.
|
|
125
|
-
self.
|
|
133
|
+
self.field_lam_funcs[kk] = sympy.lambdify(case_data.symbols,
|
|
134
|
+
self.field_sym_funcs[kk],
|
|
126
135
|
'numpy')
|
|
127
|
-
self.
|
|
136
|
+
self.field_eval = dict()
|
|
128
137
|
|
|
129
138
|
|
|
130
139
|
def evaluate_field_truth(self,
|
|
131
140
|
field_key: str,
|
|
132
141
|
coords: np.ndarray,
|
|
133
142
|
time_steps: np.ndarray | None = None) -> np.ndarray:
|
|
134
|
-
"""
|
|
143
|
+
"""Calculates the 'truth' from the analytical functions describing the
|
|
144
|
+
physical fields at the specified coordinates and time steps.
|
|
135
145
|
|
|
136
146
|
Parameters
|
|
137
147
|
----------
|
|
138
148
|
field_key : str
|
|
139
|
-
|
|
149
|
+
Key for the underlying physical field.
|
|
140
150
|
coords : np.ndarray
|
|
141
|
-
|
|
151
|
+
Coordinates at which to evaluate the analytic physical field. shape
|
|
152
|
+
=(n_coords,coord[x,y,z])
|
|
142
153
|
time_steps : np.ndarray | None, optional
|
|
143
|
-
|
|
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.
|
|
144
157
|
|
|
145
158
|
Returns
|
|
146
159
|
-------
|
|
147
160
|
np.ndarray
|
|
148
|
-
|
|
161
|
+
Array of analytic field evaluations with shape = (n_coords,
|
|
162
|
+
n_time_steps)
|
|
149
163
|
"""
|
|
150
164
|
if time_steps is None:
|
|
151
|
-
time_steps = self.
|
|
165
|
+
time_steps = self.case_data.time_steps
|
|
152
166
|
|
|
153
167
|
(x_eval,y_eval,t_eval) = fill_dims_2d(coords[:,0],
|
|
154
168
|
coords[:,1],
|
|
155
169
|
time_steps)
|
|
156
170
|
|
|
157
|
-
field_vals = self.
|
|
171
|
+
field_vals = self.field_lam_funcs[field_key](y_eval,
|
|
158
172
|
x_eval,
|
|
159
173
|
t_eval)
|
|
160
174
|
return field_vals
|
|
@@ -162,89 +176,105 @@ class AnalyticSimDataGenerator:
|
|
|
162
176
|
|
|
163
177
|
def evaluate_all_fields_truth(self,
|
|
164
178
|
coords: np.ndarray,
|
|
165
|
-
time_steps: np.ndarray | None = None
|
|
166
|
-
|
|
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.
|
|
167
183
|
|
|
168
184
|
Parameters
|
|
169
185
|
----------
|
|
170
186
|
coords : np.ndarray
|
|
171
|
-
|
|
187
|
+
Coordinates at which to evaluate the analytic physical field. shape
|
|
188
|
+
=(n_coords,coord[x,y,z])
|
|
172
189
|
time_steps : np.ndarray | None, optional
|
|
173
|
-
|
|
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.
|
|
174
193
|
|
|
175
194
|
Returns
|
|
176
195
|
-------
|
|
177
|
-
np.ndarray
|
|
178
|
-
|
|
196
|
+
dict[str,np.ndarray]
|
|
197
|
+
Dictionary keyed by the field name giving a numpy array with shape =
|
|
198
|
+
(n_coords,n_timesteps)
|
|
179
199
|
"""
|
|
180
200
|
if time_steps is None:
|
|
181
|
-
time_steps = self.
|
|
201
|
+
time_steps = self.case_data.time_steps
|
|
182
202
|
|
|
183
203
|
(x_eval,y_eval,t_eval) = fill_dims_2d(coords[:,0],
|
|
184
204
|
coords[:,1],
|
|
185
205
|
time_steps)
|
|
186
206
|
|
|
187
207
|
eval_comps = dict()
|
|
188
|
-
for kk in self.
|
|
189
|
-
eval_comps[kk] = self.
|
|
208
|
+
for kk in self.case_data.field_keys:
|
|
209
|
+
eval_comps[kk] = self.field_lam_funcs[kk](y_eval,
|
|
190
210
|
x_eval,
|
|
191
211
|
t_eval)
|
|
192
212
|
return eval_comps
|
|
193
213
|
|
|
194
214
|
|
|
195
215
|
def evaluate_field_at_nodes(self, field_key: str) -> np.ndarray:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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.
|
|
200
223
|
|
|
201
224
|
Returns
|
|
202
225
|
-------
|
|
203
|
-
|
|
204
|
-
|
|
226
|
+
np.ndarray
|
|
227
|
+
Array of field evaluations with shape=(n_nodes,n_timesteps)
|
|
205
228
|
"""
|
|
206
|
-
|
|
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,
|
|
207
234
|
x_eval,
|
|
208
235
|
t_eval)
|
|
209
|
-
return self.
|
|
236
|
+
return self.field_eval[field_key]
|
|
210
237
|
|
|
211
238
|
def evaluate_all_fields_at_nodes(self) -> dict[str,np.ndarray]:
|
|
212
|
-
"""
|
|
239
|
+
"""Evaluates all physical fields at the node locations and nominal time
|
|
240
|
+
steps.
|
|
213
241
|
|
|
214
242
|
Returns
|
|
215
243
|
-------
|
|
216
244
|
dict[str,np.ndarray]
|
|
217
|
-
|
|
245
|
+
Dictionary keyed by the field name giving a numpy array with shape =
|
|
246
|
+
(n_coords,n_timesteps)
|
|
218
247
|
"""
|
|
219
|
-
(x_eval,y_eval,t_eval) = fill_dims_2d(self.
|
|
220
|
-
self.
|
|
221
|
-
self.
|
|
248
|
+
(x_eval,y_eval,t_eval) = fill_dims_2d(self.coords[:,0],
|
|
249
|
+
self.coords[:,1],
|
|
250
|
+
self.case_data.time_steps)
|
|
222
251
|
eval_comps = dict()
|
|
223
|
-
for kk in self.
|
|
224
|
-
eval_comps[kk] = self.
|
|
252
|
+
for kk in self.case_data.field_keys:
|
|
253
|
+
eval_comps[kk] = self.field_lam_funcs[kk](y_eval,
|
|
225
254
|
x_eval,
|
|
226
255
|
t_eval)
|
|
227
|
-
self.
|
|
228
|
-
return self.
|
|
256
|
+
self.field_eval = eval_comps
|
|
257
|
+
return self.field_eval
|
|
229
258
|
|
|
230
259
|
|
|
231
260
|
def generate_sim_data(self) -> mh.SimData:
|
|
232
|
-
"""
|
|
261
|
+
"""Creates a SimData object using the analytic case geometry, mesh
|
|
262
|
+
parameters and the underlying physical fields.
|
|
233
263
|
|
|
234
264
|
Returns
|
|
235
265
|
-------
|
|
236
266
|
mh.SimData
|
|
237
|
-
|
|
267
|
+
SimData object built from the analytic case data.
|
|
238
268
|
"""
|
|
239
269
|
sim_data = mh.SimData()
|
|
240
270
|
sim_data.num_spat_dims = 2
|
|
241
|
-
sim_data.time = self.
|
|
242
|
-
sim_data.coords = self.
|
|
243
|
-
sim_data.connect = {'connect1': self.
|
|
271
|
+
sim_data.time = self.case_data.time_steps
|
|
272
|
+
sim_data.coords = self.coords
|
|
273
|
+
sim_data.connect = {'connect1': self.connect}
|
|
244
274
|
|
|
245
|
-
if not self.
|
|
275
|
+
if not self.field_eval:
|
|
246
276
|
self.evaluate_all_fields_at_nodes()
|
|
247
|
-
sim_data.node_vars = self.
|
|
277
|
+
sim_data.node_vars = self.field_eval
|
|
248
278
|
|
|
249
279
|
return sim_data
|
|
250
280
|
|
|
@@ -253,33 +283,36 @@ class AnalyticSimDataGenerator:
|
|
|
253
283
|
field_key: str | None = None,
|
|
254
284
|
time_step: int = -1
|
|
255
285
|
) -> tuple[np.ndarray,np.ndarray,np.ndarray]:
|
|
256
|
-
"""
|
|
286
|
+
"""Creates a visualisation grid for plotting heatmaps of the specified
|
|
287
|
+
analytic field using matplotlib.
|
|
257
288
|
|
|
258
289
|
Parameters
|
|
259
290
|
----------
|
|
260
291
|
field_key : str | None, optional
|
|
261
|
-
|
|
292
|
+
String key for the field to be visualised, by default None. If None
|
|
293
|
+
then the first field key is used.
|
|
262
294
|
time_step : int, optional
|
|
263
|
-
|
|
295
|
+
Time step at which to extract the field to be plotted, by default -1
|
|
264
296
|
|
|
265
297
|
Returns
|
|
266
298
|
-------
|
|
267
299
|
tuple[np.ndarray,np.ndarray,np.ndarray]
|
|
268
|
-
|
|
300
|
+
Tuple containing the 2D grid of x coordinates, grid of y coordinates
|
|
301
|
+
and a grid of field evaluations.
|
|
269
302
|
"""
|
|
270
303
|
if field_key is None:
|
|
271
|
-
field_key = self.
|
|
304
|
+
field_key = self.case_data.field_keys[0]
|
|
272
305
|
|
|
273
|
-
grid_shape = (self.
|
|
274
|
-
self.
|
|
306
|
+
grid_shape = (self.case_data.num_elem_y+1,
|
|
307
|
+
self.case_data.num_elem_x+1)
|
|
275
308
|
|
|
276
|
-
grid_x = np.atleast_2d(self.
|
|
277
|
-
grid_y = np.atleast_2d(self.
|
|
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)
|
|
278
311
|
|
|
279
|
-
if not self.
|
|
312
|
+
if not self.field_eval:
|
|
280
313
|
self.evaluate_all_fields_at_nodes()
|
|
281
314
|
|
|
282
|
-
scalar_grid = np.reshape(self.
|
|
315
|
+
scalar_grid = np.reshape(self.field_eval[field_key][:,time_step],grid_shape)
|
|
283
316
|
|
|
284
317
|
return (grid_x,grid_y,scalar_grid)
|
|
285
318
|
|
|
@@ -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
|