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,99 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
================================================================================
|
|
3
|
-
pyvale: the python validation engine
|
|
4
|
-
License: MIT
|
|
5
|
-
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
-
================================================================================
|
|
7
|
-
"""
|
|
8
|
-
from dataclasses import dataclass
|
|
9
|
-
import numpy as np
|
|
10
|
-
from pyvale.core.sensorarray import ISensorArray
|
|
11
|
-
import mooseherder as mh
|
|
12
|
-
|
|
13
|
-
# NOTE: This module is a feature under developement.
|
|
14
|
-
|
|
15
|
-
@dataclass(slots=True)
|
|
16
|
-
class ExperimentStats:
|
|
17
|
-
"""Dataclass holding summary statistics for a series of simulated
|
|
18
|
-
experiments.
|
|
19
|
-
"""
|
|
20
|
-
mean: np.ndarray | None = None
|
|
21
|
-
std: np.ndarray | None = None
|
|
22
|
-
cov: np.ndarray | None = None
|
|
23
|
-
max: np.ndarray | None = None
|
|
24
|
-
min: np.ndarray | None = None
|
|
25
|
-
med: np.ndarray | None = None
|
|
26
|
-
q25: np.ndarray | None = None
|
|
27
|
-
q75: np.ndarray | None = None
|
|
28
|
-
mad: np.ndarray | None = None
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class ExperimentSimulator:
|
|
32
|
-
"""An experiment simulator for running monte-carlo analysis by applying a
|
|
33
|
-
list of sensor arrays to a list of simulations over a given number of user
|
|
34
|
-
defined experiments. Calculates summary statistics for each sensor array
|
|
35
|
-
applied to each simulation.
|
|
36
|
-
"""
|
|
37
|
-
__slots__ = ("sim_list","sensor_arrays","num_exp_per_sim","_exp_data",
|
|
38
|
-
"_exp_stats")
|
|
39
|
-
|
|
40
|
-
def __init__(self,
|
|
41
|
-
sim_list: list[mh.SimData],
|
|
42
|
-
sensor_arrays: list[ISensorArray],
|
|
43
|
-
num_exp_per_sim: int
|
|
44
|
-
) -> None:
|
|
45
|
-
|
|
46
|
-
self.sim_list = sim_list
|
|
47
|
-
self.sensor_arrays = sensor_arrays
|
|
48
|
-
self.num_exp_per_sim = num_exp_per_sim
|
|
49
|
-
self._exp_data = [None]*len(self.sensor_arrays)
|
|
50
|
-
self._exp_stats = [None]*len(self.sensor_arrays)
|
|
51
|
-
|
|
52
|
-
def get_data(self) -> list[np.ndarray | None]:
|
|
53
|
-
return self._exp_data
|
|
54
|
-
|
|
55
|
-
def get_stats(self) -> list[np.ndarray | None]:
|
|
56
|
-
return self._exp_stats
|
|
57
|
-
|
|
58
|
-
def run_experiments(self) -> list[np.ndarray]:
|
|
59
|
-
n_sims = len(self.sim_list)
|
|
60
|
-
# shape=list[n_arrays](n_sims,n_exps,n_sens,n_comps,n_time_steps)
|
|
61
|
-
self._exp_data = [None]*len(self.sensor_arrays)
|
|
62
|
-
|
|
63
|
-
for ii,aa in enumerate(self.sensor_arrays):
|
|
64
|
-
meas_array = np.zeros((n_sims,self.num_exp_per_sim)+
|
|
65
|
-
aa.get_measurement_shape())
|
|
66
|
-
|
|
67
|
-
for jj,ss in enumerate(self.sim_list):
|
|
68
|
-
aa.get_field().set_sim_data(ss)
|
|
69
|
-
|
|
70
|
-
for ee in range(self.num_exp_per_sim):
|
|
71
|
-
meas_array[jj,ee,:,:,:] = aa.calc_measurements()
|
|
72
|
-
|
|
73
|
-
self._exp_data[ii] = meas_array
|
|
74
|
-
|
|
75
|
-
return self._exp_data
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def calc_stats(self) -> list[ExperimentStats]:
|
|
79
|
-
# shape=list[n_arrays](n_sims,n_exps,n_sens,n_comps,n_time_steps)
|
|
80
|
-
self._exp_stats = [None]*len(self.sensor_arrays)
|
|
81
|
-
for ii,_ in enumerate(self.sensor_arrays):
|
|
82
|
-
array_stats = ExperimentStats()
|
|
83
|
-
array_stats.max = np.max(self._exp_data[ii],axis=1)
|
|
84
|
-
array_stats.min = np.min(self._exp_data[ii],axis=1)
|
|
85
|
-
array_stats.mean = np.mean(self._exp_data[ii],axis=1)
|
|
86
|
-
array_stats.std = np.std(self._exp_data[ii],axis=1)
|
|
87
|
-
array_stats.med = np.median(self._exp_data[ii],axis=1)
|
|
88
|
-
array_stats.q25 = np.quantile(self._exp_data[ii],0.25,axis=1)
|
|
89
|
-
array_stats.q75 = np.quantile(self._exp_data[ii],0.75,axis=1)
|
|
90
|
-
array_stats.mad = np.median(np.abs(self._exp_data[ii] -
|
|
91
|
-
np.median(self._exp_data[ii],axis=1,keepdims=True)),axis=1)
|
|
92
|
-
self._exp_stats[ii] = array_stats
|
|
93
|
-
|
|
94
|
-
return self._exp_stats
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
pyvale/core/fieldconverter.py
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
================================================================================
|
|
3
|
-
pyvale: the python validation engine
|
|
4
|
-
License: MIT
|
|
5
|
-
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
-
================================================================================
|
|
7
|
-
"""
|
|
8
|
-
import warnings
|
|
9
|
-
import numpy as np
|
|
10
|
-
import pyvista as pv
|
|
11
|
-
from pyvista import CellType
|
|
12
|
-
import mooseherder as mh
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def simdata_to_pyvista(sim_data: mh.SimData,
|
|
16
|
-
components: tuple[str,...] | None,
|
|
17
|
-
spat_dim: int
|
|
18
|
-
) -> tuple[pv.UnstructuredGrid,pv.UnstructuredGrid]:
|
|
19
|
-
"""Converts the mesh and field data in a `SimData` object into a pyvista
|
|
20
|
-
UnstructuredGrid for sampling (interpolating) the data and visualisation.
|
|
21
|
-
|
|
22
|
-
Parameters
|
|
23
|
-
----------
|
|
24
|
-
sim_data : mh.SimData
|
|
25
|
-
Object containing a mesh and associated field data from a simulation.
|
|
26
|
-
components : tuple[str,...] | None
|
|
27
|
-
String keys for the components of the field to extract from the
|
|
28
|
-
simulation data.
|
|
29
|
-
spat_dim : int
|
|
30
|
-
Number of spatial dimensions (2 or 3) used to determine the element
|
|
31
|
-
types in the mesh from the number of nodes per element.
|
|
32
|
-
|
|
33
|
-
Returns
|
|
34
|
-
-------
|
|
35
|
-
tuple[pv.UnstructuredGrid,pv.UnstructuredGrid]
|
|
36
|
-
The first UnstructuredGrid has the field components attached as dataset
|
|
37
|
-
arrays. The second has no field data attached for visualisation.
|
|
38
|
-
"""
|
|
39
|
-
flat_connect = np.array([],dtype=np.int64)
|
|
40
|
-
cell_types = np.array([],dtype=np.int64)
|
|
41
|
-
|
|
42
|
-
for cc in sim_data.connect:
|
|
43
|
-
# NOTE: need the -1 here to make element numbers 0 indexed!
|
|
44
|
-
this_connect = np.copy(sim_data.connect[cc])-1
|
|
45
|
-
(nodes_per_elem,n_elems) = this_connect.shape
|
|
46
|
-
|
|
47
|
-
this_cell_type = _get_pyvista_cell_type(nodes_per_elem,spat_dim)
|
|
48
|
-
|
|
49
|
-
# VTK and exodus have different winding for 3D higher order quads
|
|
50
|
-
this_connect = _exodus_to_pyvista_connect(this_cell_type,this_connect)
|
|
51
|
-
|
|
52
|
-
this_connect = this_connect.T.flatten()
|
|
53
|
-
idxs = np.arange(0,n_elems*nodes_per_elem,nodes_per_elem,dtype=np.int64)
|
|
54
|
-
|
|
55
|
-
this_connect = np.insert(this_connect,idxs,nodes_per_elem)
|
|
56
|
-
|
|
57
|
-
cell_types = np.hstack((cell_types,np.full(n_elems,this_cell_type)))
|
|
58
|
-
flat_connect = np.hstack((flat_connect,this_connect),dtype=np.int64)
|
|
59
|
-
|
|
60
|
-
cells = flat_connect
|
|
61
|
-
|
|
62
|
-
points = sim_data.coords
|
|
63
|
-
pv_grid = pv.UnstructuredGrid(cells, cell_types, points)
|
|
64
|
-
pv_grid_vis = pv.UnstructuredGrid(cells, cell_types, points)
|
|
65
|
-
|
|
66
|
-
if components is not None and sim_data.node_vars is not None:
|
|
67
|
-
for cc in components:
|
|
68
|
-
pv_grid[cc] = sim_data.node_vars[cc]
|
|
69
|
-
|
|
70
|
-
return (pv_grid,pv_grid_vis)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def _get_pyvista_cell_type(nodes_per_elem: int, spat_dim: int) -> CellType:
|
|
74
|
-
"""Helper function to identify the pyvista element type in the mesh.
|
|
75
|
-
|
|
76
|
-
Parameters
|
|
77
|
-
----------
|
|
78
|
-
nodes_per_elem : int
|
|
79
|
-
Number of nodes per element.
|
|
80
|
-
spat_dim : int
|
|
81
|
-
Number of spatial dimensions in the mesh (2 or 3).
|
|
82
|
-
|
|
83
|
-
Returns
|
|
84
|
-
-------
|
|
85
|
-
CellType
|
|
86
|
-
Enumeration describing the element type in pyvista.
|
|
87
|
-
"""
|
|
88
|
-
cell_type = 0
|
|
89
|
-
|
|
90
|
-
if spat_dim == 2:
|
|
91
|
-
if nodes_per_elem == 4:
|
|
92
|
-
cell_type = CellType.QUAD
|
|
93
|
-
elif nodes_per_elem == 3:
|
|
94
|
-
cell_type = CellType.TRIANGLE
|
|
95
|
-
elif nodes_per_elem == 6:
|
|
96
|
-
cell_type = CellType.QUADRATIC_TRIANGLE
|
|
97
|
-
elif nodes_per_elem == 7:
|
|
98
|
-
cell_type = CellType.BIQUADRATIC_TRIANGLE
|
|
99
|
-
elif nodes_per_elem == 8:
|
|
100
|
-
cell_type = CellType.QUADRATIC_QUAD
|
|
101
|
-
elif nodes_per_elem == 9:
|
|
102
|
-
cell_type = CellType.BIQUADRATIC_QUAD
|
|
103
|
-
else:
|
|
104
|
-
warnings.warn(f"Cell type 2D with {nodes_per_elem} "
|
|
105
|
-
+ "nodes not recognised. Defaulting to 4 node QUAD")
|
|
106
|
-
cell_type = CellType.QUAD
|
|
107
|
-
else:
|
|
108
|
-
if nodes_per_elem == 8:
|
|
109
|
-
cell_type = CellType.HEXAHEDRON
|
|
110
|
-
elif nodes_per_elem == 4:
|
|
111
|
-
cell_type = CellType.TETRA
|
|
112
|
-
elif nodes_per_elem == 10:
|
|
113
|
-
cell_type = CellType.QUADRATIC_TETRA
|
|
114
|
-
elif nodes_per_elem == 20:
|
|
115
|
-
cell_type = CellType.QUADRATIC_HEXAHEDRON
|
|
116
|
-
elif nodes_per_elem == 27:
|
|
117
|
-
cell_type = CellType.TRIQUADRATIC_HEXAHEDRON
|
|
118
|
-
else:
|
|
119
|
-
warnings.warn(f"Cell type 3D with {nodes_per_elem} "
|
|
120
|
-
+ "nodes not recognised. Defaulting to 8 node HEX")
|
|
121
|
-
cell_type = CellType.HEXAHEDRON
|
|
122
|
-
|
|
123
|
-
return cell_type
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def _exodus_to_pyvista_connect(cell_type: CellType, connect: np.ndarray) -> np.ndarray:
|
|
127
|
-
copy_connect = np.copy(connect)
|
|
128
|
-
|
|
129
|
-
# NOTE: it looks like VTK does not support TET14
|
|
130
|
-
# VTK and exodus have different winding for 3D higher order quads
|
|
131
|
-
if cell_type == CellType.QUADRATIC_HEXAHEDRON:
|
|
132
|
-
connect[12:16,:] = copy_connect[16:20,:]
|
|
133
|
-
connect[16:20,:] = copy_connect[12:16,:]
|
|
134
|
-
elif cell_type == CellType.TRIQUADRATIC_HEXAHEDRON:
|
|
135
|
-
connect[12:16,:] = copy_connect[16:20,:]
|
|
136
|
-
connect[16:20,:] = copy_connect[12:16,:]
|
|
137
|
-
connect[20:24,:] = copy_connect[23:27,:]
|
|
138
|
-
connect[24,:] = copy_connect[21,:]
|
|
139
|
-
connect[25,:] = copy_connect[22,:]
|
|
140
|
-
connect[26,:] = copy_connect[20,:]
|
|
141
|
-
|
|
142
|
-
return connect
|
|
143
|
-
|
|
144
|
-
def scale_length_units(sim_data: mh.SimData,
|
|
145
|
-
disp_comps: tuple[str,...],
|
|
146
|
-
scale: float) -> mh.SimData:
|
|
147
|
-
|
|
148
|
-
sim_data.coords = sim_data.coords*scale
|
|
149
|
-
for cc in disp_comps:
|
|
150
|
-
sim_data.node_vars[cc] = sim_data.node_vars[cc]*scale
|
|
151
|
-
|
|
152
|
-
return sim_data
|
|
153
|
-
|
|
154
|
-
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
================================================================================
|
|
3
|
-
pyvale: the python validation engine
|
|
4
|
-
License: MIT
|
|
5
|
-
Copyright (C) 2025 The Computer Aided Validation Team
|
|
6
|
-
================================================================================
|
|
7
|
-
"""
|
|
8
|
-
import numpy as np
|
|
9
|
-
from pyvale.core.field import IField
|
|
10
|
-
from pyvale.core.integratorspatial import (IIntegratorSpatial,
|
|
11
|
-
create_int_pt_array)
|
|
12
|
-
from pyvale.core.sensordata import SensorData
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
#TODO: Docstrings
|
|
16
|
-
|
|
17
|
-
#NOTE: code below is very similar to quadrature integrator should be able to
|
|
18
|
-
# refactor into injected classes/functions
|
|
19
|
-
|
|
20
|
-
class Rectangle2D(IIntegratorSpatial):
|
|
21
|
-
__slots__ = ("_field","sens_data","_area","_area_int","_n_int_pts",
|
|
22
|
-
"_int_pt_offsets","_int_pts","_averages")
|
|
23
|
-
|
|
24
|
-
def __init__(self,
|
|
25
|
-
field: IField,
|
|
26
|
-
sens_data: SensorData,
|
|
27
|
-
int_pt_offsets: np.ndarray) -> None:
|
|
28
|
-
|
|
29
|
-
self._field = field
|
|
30
|
-
self._sens_data = sens_data
|
|
31
|
-
|
|
32
|
-
self._area = (self._sens_data.spatial_dims[0]
|
|
33
|
-
* self._sens_data.spatial_dims[1])
|
|
34
|
-
self._area_int = self._area/int_pt_offsets.shape[0]
|
|
35
|
-
|
|
36
|
-
self._n_int_pts = int_pt_offsets.shape[0]
|
|
37
|
-
self._int_pt_offsets = int_pt_offsets
|
|
38
|
-
self._int_pts = create_int_pt_array(self._sens_data,
|
|
39
|
-
self._int_pt_offsets)
|
|
40
|
-
|
|
41
|
-
self._averages = None
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def calc_integrals(self, sens_data: SensorData | None = None) -> np.ndarray:
|
|
45
|
-
self._averages = self.calc_averages(sens_data)
|
|
46
|
-
return self._area*self.get_averages()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def get_integrals(self) -> np.ndarray:
|
|
50
|
-
return self._area*self.get_averages()
|
|
51
|
-
|
|
52
|
-
def calc_averages(self, sens_data: SensorData | None = None) -> np.ndarray:
|
|
53
|
-
|
|
54
|
-
if sens_data is not None:
|
|
55
|
-
self._sens_data = sens_data
|
|
56
|
-
|
|
57
|
-
# shape=(n_sens*n_int_pts,n_dims)
|
|
58
|
-
self._int_pts = create_int_pt_array(self._sens_data,
|
|
59
|
-
self._int_pt_offsets)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# shape=(n_int_pts*n_sens,n_comps,n_timesteps)
|
|
63
|
-
int_vals = self._field.sample_field(self._int_pts,
|
|
64
|
-
self._sens_data.sample_times,
|
|
65
|
-
self._sens_data.angles)
|
|
66
|
-
|
|
67
|
-
meas_shape = (self._sens_data.positions.shape[0],
|
|
68
|
-
int_vals.shape[1],
|
|
69
|
-
int_vals.shape[2])
|
|
70
|
-
|
|
71
|
-
# shape=(n_gauss_pts,n_sens,n_comps,n_timesteps)
|
|
72
|
-
int_vals = int_vals.reshape((self._n_int_pts,)+meas_shape,
|
|
73
|
-
order='F')
|
|
74
|
-
|
|
75
|
-
# shape=(n_sensors,n_comps,n_timsteps)
|
|
76
|
-
self._averages = 1/self._area * np.sum(self._area_int*int_vals,axis=0)
|
|
77
|
-
|
|
78
|
-
return self._averages
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def get_averages(self) -> np.ndarray:
|
|
82
|
-
if self._averages is None:
|
|
83
|
-
self._averages = self.calc_averages()
|
|
84
|
-
|
|
85
|
-
return self._averages
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
pyvale/core/optimcheckfuncs.py
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
OPTIMISATION TEST FUNCTIONS - N DIMS
|
|
3
|
-
https://gist.github.com/denis-bz/da697d8bc74fae4598bf
|
|
4
|
-
https://www.sfu.ca/~ssurjano/optimization.html
|
|
5
|
-
https://en.wikipedia.org/wiki/Test_functions_for_optimization
|
|
6
|
-
"""
|
|
7
|
-
from typing import Callable,Any
|
|
8
|
-
import numpy as np
|
|
9
|
-
import matplotlib.pyplot as plt
|
|
10
|
-
from pyvale.core.visualopts import PlotOptsGeneral
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def ackley(x: np.ndarray,
|
|
14
|
-
a: float = 20.0,
|
|
15
|
-
b: float = 0.2,
|
|
16
|
-
c: float = 2*np.pi) -> np.ndarray:
|
|
17
|
-
"""ACKLEY
|
|
18
|
-
Dimension: N
|
|
19
|
-
Local Minima: many
|
|
20
|
-
Global Minimum: f(x) = 0 @ (0,0,....,0)
|
|
21
|
-
Eval: [-32.768,32.768] or smaller
|
|
22
|
-
"""
|
|
23
|
-
n = x.shape[1]
|
|
24
|
-
sum1 = np.sum(x**2,axis=1)
|
|
25
|
-
sum2 = np.sum(np.cos(c*x),axis=1)
|
|
26
|
-
f = -a*np.exp(-b*np.sqrt(sum1/n)) - np.exp(sum2/n) + a + np.exp(1)
|
|
27
|
-
return f
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def dixonprice(x: np.ndarray) -> np.ndarray:
|
|
31
|
-
"""DIXON-PRICE
|
|
32
|
-
Dimension: N
|
|
33
|
-
Local Minima: Large valley
|
|
34
|
-
Global Minimum: f(x) = 0 @ x_i = 2^-((2^i-2)/2^i) for i = 1,...,d
|
|
35
|
-
Eval: [-10.0,10.0] or smaller
|
|
36
|
-
"""
|
|
37
|
-
n = x.shape[1]
|
|
38
|
-
j = np.arange( 2, n+1 )
|
|
39
|
-
x2 = 2 * x**2
|
|
40
|
-
f = np.sum( j * (x2[:,1:] - x[:,:-1]) **2, axis=1) + (x[:,0] - 1) **2
|
|
41
|
-
return f
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def griewank(x: np.ndarray, div: float = 4000.0) -> np.ndarray:
|
|
45
|
-
"""GRIEWANK
|
|
46
|
-
Dimension: N
|
|
47
|
-
Local Minima: many
|
|
48
|
-
Global Minimum: f(x) = 0 @ (0,0,....,0)
|
|
49
|
-
Eval: [-600,600] or smaller
|
|
50
|
-
"""
|
|
51
|
-
n = x.shape[1]
|
|
52
|
-
j = np.arange( 1., n+1 )
|
|
53
|
-
sum1 = np.sum( x**2, axis=1 )
|
|
54
|
-
p = np.prod( np.cos( x / np.sqrt(j) ), axis=1)
|
|
55
|
-
f = sum1/div - p + 1
|
|
56
|
-
return f
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def rastrigin(x: np.ndarray, a: float = 10) -> np.ndarray:
|
|
60
|
-
"""RASTRIGIN
|
|
61
|
-
Dimension: N
|
|
62
|
-
Local Minima: many
|
|
63
|
-
Global Minimum: f(x) = 0 @ (0,0,....,0)
|
|
64
|
-
Eval: [-5.12,5.12] or smaller
|
|
65
|
-
"""
|
|
66
|
-
n = x.shape[1]
|
|
67
|
-
sum1 = np.sum(x**2 - a*np.cos(2*np.pi*x),axis=1)
|
|
68
|
-
f = a*n + sum1
|
|
69
|
-
return f
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def rosenbrock(x: np.ndarray, a: float = 100) -> float:
|
|
73
|
-
"""ROSENBROCK
|
|
74
|
-
Dimension: N
|
|
75
|
-
Local Minima: Large valley
|
|
76
|
-
Global minimum: at (x,y) = (1,1) where f(x,y)=0
|
|
77
|
-
Eval: [-5.0,10.0] or smaller
|
|
78
|
-
"""
|
|
79
|
-
x0 = x[:,:-1] # x_(i) ... to n-1
|
|
80
|
-
x1 = x[:,1:] # x_(i+1) ... to n-1
|
|
81
|
-
f = a*np.sum((x1-x0**2)**2,axis=1) + np.sum((1-x0)**2, axis=1)
|
|
82
|
-
return f
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def sphere(x: np.ndarray) -> np.ndarray:
|
|
86
|
-
"""SPHERE
|
|
87
|
-
Dimension: N
|
|
88
|
-
Local Minima: none
|
|
89
|
-
Global minimum: f(x) = 0 @ (0,0,....,0)
|
|
90
|
-
Eval: [-inf,inf] or smaller
|
|
91
|
-
"""
|
|
92
|
-
f = np.sum(x**2,axis=1)
|
|
93
|
-
return f
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def get_mesh_x_2d(xlim1: tuple[float,float],
|
|
97
|
-
xlim2: tuple[float,float],
|
|
98
|
-
n: int = 100):
|
|
99
|
-
xv1 = np.linspace(xlim1[0],xlim1[1],n)
|
|
100
|
-
xv2 = np.linspace(xlim2[0],xlim2[1],n)
|
|
101
|
-
(xm1,xm2) = np.meshgrid(xv1,xv2)
|
|
102
|
-
return (xm1,xm2)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def get_flat_x_2d(xlim1: tuple[float,float],
|
|
106
|
-
xlim2: tuple[float,float],
|
|
107
|
-
n: int = 100) -> np.ndarray:
|
|
108
|
-
|
|
109
|
-
(xm1,xm2) = get_mesh_x_2d(xlim1,xlim2,n)
|
|
110
|
-
xf1 = xm1.flatten()
|
|
111
|
-
xf2 = xm2.flatten()
|
|
112
|
-
return np.column_stack((xf1,xf2))
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def f_mesh_2d(fun: Callable,
|
|
116
|
-
xlim1: tuple[float,float],
|
|
117
|
-
xlim2: tuple[float,float],
|
|
118
|
-
n: int = 100) -> np.ndarray:
|
|
119
|
-
|
|
120
|
-
(xm1,_) = get_mesh_x_2d(xlim1,xlim2,n)
|
|
121
|
-
xf = get_flat_x_2d(xlim1,xlim2,n)
|
|
122
|
-
f_flat = fun(xf)
|
|
123
|
-
f_mesh = f_flat.reshape(xm1.shape)
|
|
124
|
-
|
|
125
|
-
return f_mesh
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def plot_fun_2d(tStr: str,
|
|
129
|
-
fun: Callable,
|
|
130
|
-
xlim1: tuple[float,float],
|
|
131
|
-
xlim2: tuple[float,float],
|
|
132
|
-
n: int =100) -> tuple[Any,Any]:
|
|
133
|
-
|
|
134
|
-
(xm1,xm2) = get_mesh_x_2d(xlim1,xlim2,n)
|
|
135
|
-
f_mesh = f_mesh_2d(fun,xlim1,xlim2,n)
|
|
136
|
-
# Plot the function
|
|
137
|
-
pp = PlotOptsGeneral()
|
|
138
|
-
fig, ax = plt.subplots(figsize=pp.single_fig_size_square,
|
|
139
|
-
layout="constrained")
|
|
140
|
-
fig.set_dpi(pp.resolution)
|
|
141
|
-
|
|
142
|
-
plt.contourf(xm1, xm2, f_mesh, 20, cmap=pp.cmap_seq)
|
|
143
|
-
|
|
144
|
-
plt.colorbar()
|
|
145
|
-
plt.title(tStr,fontsize=pp.font_head_size,fontname=pp.font_name)
|
|
146
|
-
plt.xlabel("x1",fontsize=pp.font_ax_size,fontname=pp.font_name)
|
|
147
|
-
plt.ylabel("x2",fontsize=pp.font_ax_size,fontname=pp.font_name)
|
|
148
|
-
|
|
149
|
-
#plt.show()
|
|
150
|
-
#plt.savefig(save_path+save_name, dpi=pp.resolution, format="png", bbox_inches="tight")
|
|
151
|
-
return fig,ax
|
|
152
|
-
|
|
153
|
-
|
pyvale/core/sensordescriptor.py
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
"""
|
|
3
|
-
================================================================================
|
|
4
|
-
pyvale: the python validation engine
|
|
5
|
-
License: MIT
|
|
6
|
-
Copyright (C) 2025 The Computer Aided Validation Team
|
|
7
|
-
================================================================================
|
|
8
|
-
"""
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
import numpy as np
|
|
11
|
-
|
|
12
|
-
#TODO: Docstrings
|
|
13
|
-
|
|
14
|
-
@dataclass(slots=True)
|
|
15
|
-
class SensorDescriptor:
|
|
16
|
-
name: str = 'Measured Value'
|
|
17
|
-
units: str = r"-"
|
|
18
|
-
time_units: str = r"s"
|
|
19
|
-
symbol: str = r"m"
|
|
20
|
-
tag: str = 'S'
|
|
21
|
-
components: tuple[str,...] | None = None
|
|
22
|
-
|
|
23
|
-
def create_label(self, comp_ind: int | None = None) -> str:
|
|
24
|
-
label = ""
|
|
25
|
-
if self.name != "":
|
|
26
|
-
label = label + rf"{self.name} "
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
symbol = rf"${self.symbol}$ "
|
|
30
|
-
if comp_ind is not None and self.components is not None:
|
|
31
|
-
symbol = rf"${self.symbol}_{{{self.components[comp_ind]}}}$ "
|
|
32
|
-
if symbol != "":
|
|
33
|
-
label = label + symbol
|
|
34
|
-
|
|
35
|
-
if self.units != "":
|
|
36
|
-
label = label + "\n" + rf"[${self.units}$]"
|
|
37
|
-
|
|
38
|
-
return label
|
|
39
|
-
|
|
40
|
-
def create_label_flat(self, comp_ind: int | None = None) -> str:
|
|
41
|
-
label = ""
|
|
42
|
-
if self.name != "":
|
|
43
|
-
label = label + rf"{self.name} "
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
symbol = rf"${self.symbol}$ "
|
|
47
|
-
if comp_ind is not None and self.components is not None:
|
|
48
|
-
symbol = rf"${self.symbol}_{{{self.components[comp_ind]}}}$ "
|
|
49
|
-
if symbol != "":
|
|
50
|
-
label = label + symbol
|
|
51
|
-
|
|
52
|
-
if self.units != "":
|
|
53
|
-
label = label + " " + rf"[${self.units}$]"
|
|
54
|
-
|
|
55
|
-
return label
|
|
56
|
-
|
|
57
|
-
def create_sensor_tags(self,n_sensors: int) -> list[str]:
|
|
58
|
-
z_width = int(np.log10(n_sensors))+1
|
|
59
|
-
|
|
60
|
-
sensor_names = list()
|
|
61
|
-
for ss in range(n_sensors):
|
|
62
|
-
num_str = f'{ss+1}'.zfill(z_width)
|
|
63
|
-
sensor_names.append(f'{self.tag}{num_str}')
|
|
64
|
-
|
|
65
|
-
return sensor_names
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class SensorDescriptorFactory:
|
|
69
|
-
@staticmethod
|
|
70
|
-
def temperature_descriptor() -> SensorDescriptor:
|
|
71
|
-
descriptor = SensorDescriptor()
|
|
72
|
-
descriptor.name = 'Temp.'
|
|
73
|
-
descriptor.symbol = 'T'
|
|
74
|
-
descriptor.units = r'^{\circ}C'
|
|
75
|
-
descriptor.tag = 'TC'
|
|
76
|
-
return descriptor
|
|
77
|
-
|
|
78
|
-
@staticmethod
|
|
79
|
-
def displacement_descriptor() -> SensorDescriptor:
|
|
80
|
-
descriptor = SensorDescriptor()
|
|
81
|
-
descriptor.name = 'Disp.'
|
|
82
|
-
descriptor.symbol = 'u'
|
|
83
|
-
descriptor.units = r'm'
|
|
84
|
-
descriptor.tag = 'DS'
|
|
85
|
-
descriptor.components = ('x','y','z')
|
|
86
|
-
return descriptor
|
|
87
|
-
|
|
88
|
-
@staticmethod
|
|
89
|
-
def strain_descriptor(spat_dims: int = 3) -> SensorDescriptor:
|
|
90
|
-
descriptor = SensorDescriptor()
|
|
91
|
-
descriptor.name = 'Strain'
|
|
92
|
-
descriptor.symbol = r'\varepsilon'
|
|
93
|
-
descriptor.units = r'-'
|
|
94
|
-
descriptor.tag = 'SG'
|
|
95
|
-
|
|
96
|
-
if spat_dims == 2:
|
|
97
|
-
descriptor.components = ('xx','yy','xy')
|
|
98
|
-
else:
|
|
99
|
-
descriptor.components = ('xx','yy','zz','xy','yz','xz')
|
|
100
|
-
|
|
101
|
-
return descriptor
|