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.

Files changed (153) hide show
  1. pyvale/__init__.py +78 -64
  2. pyvale/analyticmeshgen.py +102 -0
  3. pyvale/{core/analyticsimdatafactory.py → analyticsimdatafactory.py} +44 -16
  4. pyvale/analyticsimdatagenerator.py +323 -0
  5. pyvale/blendercalibrationdata.py +15 -0
  6. pyvale/blenderlightdata.py +26 -0
  7. pyvale/blendermaterialdata.py +15 -0
  8. pyvale/blenderrenderdata.py +30 -0
  9. pyvale/blenderscene.py +488 -0
  10. pyvale/blendertools.py +420 -0
  11. pyvale/{core/camera.py → camera.py} +15 -15
  12. pyvale/{core/cameradata.py → cameradata.py} +27 -22
  13. pyvale/{core/cameradata2d.py → cameradata2d.py} +8 -6
  14. pyvale/camerastereo.py +217 -0
  15. pyvale/{core/cameratools.py → cameratools.py} +220 -26
  16. pyvale/{core/cython → cython}/rastercyth.py +11 -7
  17. pyvale/data/__init__.py +5 -7
  18. pyvale/data/cal_target.tiff +0 -0
  19. pyvale/data/case00_HEX20_out.e +0 -0
  20. pyvale/data/case00_HEX27_out.e +0 -0
  21. pyvale/data/case00_HEX8_out.e +0 -0
  22. pyvale/data/case00_TET10_out.e +0 -0
  23. pyvale/data/case00_TET14_out.e +0 -0
  24. pyvale/data/case00_TET4_out.e +0 -0
  25. pyvale/{core/dataset.py → dataset.py} +91 -16
  26. pyvale/{core/errorcalculator.py → errorcalculator.py} +13 -16
  27. pyvale/{core/errordriftcalc.py → errordriftcalc.py} +14 -14
  28. pyvale/{core/errorintegrator.py → errorintegrator.py} +25 -28
  29. pyvale/{core/errorrand.py → errorrand.py} +39 -46
  30. pyvale/errorsyscalib.py +134 -0
  31. pyvale/{core/errorsysdep.py → errorsysdep.py} +25 -29
  32. pyvale/{core/errorsysfield.py → errorsysfield.py} +59 -52
  33. pyvale/{core/errorsysindep.py → errorsysindep.py} +85 -182
  34. pyvale/examples/__init__.py +5 -7
  35. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +131 -0
  36. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +158 -0
  37. pyvale/examples/basics/ex1_3_customsens_therm3d.py +216 -0
  38. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +153 -0
  39. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +168 -0
  40. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +133 -0
  41. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +123 -0
  42. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +112 -0
  43. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +111 -0
  44. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +139 -0
  45. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +196 -0
  46. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +109 -0
  47. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +114 -0
  48. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +111 -0
  49. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +182 -0
  50. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +171 -0
  51. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +252 -0
  52. pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_1_scalarvisualisation.py +6 -9
  53. pyvale/examples/{analyticdatagen → genanalyticdata}/ex1_2_scalarcasebuild.py +8 -11
  54. pyvale/examples/{analyticdatagen → genanalyticdata}/ex2_1_analyticsensors.py +9 -12
  55. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +8 -15
  56. pyvale/examples/renderblender/ex1_1_blenderscene.py +121 -0
  57. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +119 -0
  58. pyvale/examples/renderblender/ex2_1_stereoscene.py +128 -0
  59. pyvale/examples/renderblender/ex2_2_stereodeformed.py +131 -0
  60. pyvale/examples/renderblender/ex3_1_blendercalibration.py +120 -0
  61. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastenp.py +6 -7
  62. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_oneframe.py +5 -7
  63. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_cypara.py +6 -13
  64. pyvale/examples/{rasterisation → renderrasterisation}/ex_rastercyth_static_pypara.py +9 -12
  65. pyvale/examples/{ex1_4_thermal2d.py → visualisation/ex1_1_plot_traces.py} +33 -20
  66. pyvale/examples/{features/ex_animation_tools_3dmonoblock.py → visualisation/ex2_1_animate_sim.py} +37 -31
  67. pyvale/experimentsimulator.py +175 -0
  68. pyvale/{core/field.py → field.py} +6 -14
  69. pyvale/fieldconverter.py +351 -0
  70. pyvale/{core/fieldsampler.py → fieldsampler.py} +9 -10
  71. pyvale/{core/fieldscalar.py → fieldscalar.py} +17 -18
  72. pyvale/{core/fieldtensor.py → fieldtensor.py} +23 -26
  73. pyvale/{core/fieldtransform.py → fieldtransform.py} +9 -5
  74. pyvale/{core/fieldvector.py → fieldvector.py} +14 -16
  75. pyvale/{core/generatorsrandom.py → generatorsrandom.py} +29 -52
  76. pyvale/{core/imagedef2d.py → imagedef2d.py} +11 -8
  77. pyvale/{core/integratorfactory.py → integratorfactory.py} +12 -13
  78. pyvale/{core/integratorquadrature.py → integratorquadrature.py} +57 -32
  79. pyvale/integratorrectangle.py +165 -0
  80. pyvale/{core/integratorspatial.py → integratorspatial.py} +9 -10
  81. pyvale/{core/integratortype.py → integratortype.py} +7 -8
  82. pyvale/output.py +17 -0
  83. pyvale/pyvaleexceptions.py +11 -0
  84. pyvale/{core/raster.py → raster.py} +8 -8
  85. pyvale/{core/rastercy.py → rastercy.py} +11 -10
  86. pyvale/{core/rasternp.py → rasternp.py} +12 -13
  87. pyvale/{core/rendermesh.py → rendermesh.py} +10 -19
  88. pyvale/{core/sensorarray.py → sensorarray.py} +7 -8
  89. pyvale/{core/sensorarrayfactory.py → sensorarrayfactory.py} +64 -78
  90. pyvale/{core/sensorarraypoint.py → sensorarraypoint.py} +39 -41
  91. pyvale/{core/sensordata.py → sensordata.py} +7 -8
  92. pyvale/sensordescriptor.py +213 -0
  93. pyvale/{core/sensortools.py → sensortools.py} +8 -9
  94. pyvale/simcases/case00_HEX20.i +5 -5
  95. pyvale/simcases/case00_HEX27.i +5 -5
  96. pyvale/simcases/case00_HEX8.i +242 -0
  97. pyvale/simcases/case00_TET10.i +2 -2
  98. pyvale/simcases/case00_TET14.i +2 -2
  99. pyvale/simcases/case00_TET4.i +242 -0
  100. pyvale/simcases/run_1case.py +1 -1
  101. pyvale/simtools.py +67 -0
  102. pyvale/visualexpplotter.py +191 -0
  103. pyvale/{core/visualimagedef.py → visualimagedef.py} +13 -10
  104. pyvale/{core/visualimages.py → visualimages.py} +10 -9
  105. pyvale/visualopts.py +493 -0
  106. pyvale/{core/visualsimanimator.py → visualsimanimator.py} +47 -19
  107. pyvale/visualsimsensors.py +318 -0
  108. pyvale/visualtools.py +136 -0
  109. pyvale/visualtraceplotter.py +142 -0
  110. {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/METADATA +17 -14
  111. pyvale-2025.5.1.dist-info/RECORD +172 -0
  112. {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/WHEEL +1 -1
  113. pyvale/core/__init__.py +0 -7
  114. pyvale/core/analyticmeshgen.py +0 -59
  115. pyvale/core/analyticsimdatagenerator.py +0 -160
  116. pyvale/core/cython/rastercyth.c +0 -32267
  117. pyvale/core/experimentsimulator.py +0 -99
  118. pyvale/core/fieldconverter.py +0 -154
  119. pyvale/core/integratorrectangle.py +0 -88
  120. pyvale/core/optimcheckfuncs.py +0 -153
  121. pyvale/core/sensordescriptor.py +0 -101
  122. pyvale/core/visualexpplotter.py +0 -151
  123. pyvale/core/visualopts.py +0 -180
  124. pyvale/core/visualsimplotter.py +0 -182
  125. pyvale/core/visualtools.py +0 -81
  126. pyvale/core/visualtraceplotter.py +0 -256
  127. pyvale/examples/analyticdatagen/__init__.py +0 -7
  128. pyvale/examples/ex1_1_thermal2d.py +0 -89
  129. pyvale/examples/ex1_2_thermal2d.py +0 -111
  130. pyvale/examples/ex1_3_thermal2d.py +0 -113
  131. pyvale/examples/ex1_5_thermal2d.py +0 -105
  132. pyvale/examples/ex2_1_thermal3d .py +0 -87
  133. pyvale/examples/ex2_2_thermal3d.py +0 -51
  134. pyvale/examples/ex2_3_thermal3d.py +0 -109
  135. pyvale/examples/ex3_1_displacement2d.py +0 -47
  136. pyvale/examples/ex3_2_displacement2d.py +0 -79
  137. pyvale/examples/ex3_3_displacement2d.py +0 -104
  138. pyvale/examples/ex3_4_displacement2d.py +0 -105
  139. pyvale/examples/ex4_1_strain2d.py +0 -57
  140. pyvale/examples/ex4_2_strain2d.py +0 -79
  141. pyvale/examples/ex4_3_strain2d.py +0 -100
  142. pyvale/examples/ex5_1_multiphysics2d.py +0 -78
  143. pyvale/examples/ex6_1_multiphysics2d_expsim.py +0 -118
  144. pyvale/examples/ex6_2_multiphysics3d_expsim.py +0 -158
  145. pyvale/examples/features/__init__.py +0 -7
  146. pyvale/examples/features/ex_area_avg.py +0 -89
  147. pyvale/examples/features/ex_calibration_error.py +0 -108
  148. pyvale/examples/features/ex_chain_field_errs.py +0 -141
  149. pyvale/examples/features/ex_field_errs.py +0 -78
  150. pyvale/examples/features/ex_sensor_single_angle_batch.py +0 -110
  151. pyvale-2025.4.0.dist-info/RECORD +0 -157
  152. {pyvale-2025.4.0.dist-info → pyvale-2025.5.1.dist-info}/licenses/LICENSE +0 -0
  153. {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
-
@@ -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
-
@@ -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
-
@@ -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