pyvale 2025.4.0__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 (157) hide show
  1. pyvale/__init__.py +75 -0
  2. pyvale/core/__init__.py +7 -0
  3. pyvale/core/analyticmeshgen.py +59 -0
  4. pyvale/core/analyticsimdatafactory.py +63 -0
  5. pyvale/core/analyticsimdatagenerator.py +160 -0
  6. pyvale/core/camera.py +146 -0
  7. pyvale/core/cameradata.py +64 -0
  8. pyvale/core/cameradata2d.py +82 -0
  9. pyvale/core/cameratools.py +328 -0
  10. pyvale/core/cython/rastercyth.c +32267 -0
  11. pyvale/core/cython/rastercyth.py +636 -0
  12. pyvale/core/dataset.py +250 -0
  13. pyvale/core/errorcalculator.py +112 -0
  14. pyvale/core/errordriftcalc.py +146 -0
  15. pyvale/core/errorintegrator.py +339 -0
  16. pyvale/core/errorrand.py +614 -0
  17. pyvale/core/errorsysdep.py +331 -0
  18. pyvale/core/errorsysfield.py +407 -0
  19. pyvale/core/errorsysindep.py +905 -0
  20. pyvale/core/experimentsimulator.py +99 -0
  21. pyvale/core/field.py +136 -0
  22. pyvale/core/fieldconverter.py +154 -0
  23. pyvale/core/fieldsampler.py +112 -0
  24. pyvale/core/fieldscalar.py +167 -0
  25. pyvale/core/fieldtensor.py +221 -0
  26. pyvale/core/fieldtransform.py +384 -0
  27. pyvale/core/fieldvector.py +215 -0
  28. pyvale/core/generatorsrandom.py +528 -0
  29. pyvale/core/imagedef2d.py +566 -0
  30. pyvale/core/integratorfactory.py +241 -0
  31. pyvale/core/integratorquadrature.py +192 -0
  32. pyvale/core/integratorrectangle.py +88 -0
  33. pyvale/core/integratorspatial.py +90 -0
  34. pyvale/core/integratortype.py +44 -0
  35. pyvale/core/optimcheckfuncs.py +153 -0
  36. pyvale/core/raster.py +31 -0
  37. pyvale/core/rastercy.py +76 -0
  38. pyvale/core/rasternp.py +604 -0
  39. pyvale/core/rendermesh.py +156 -0
  40. pyvale/core/sensorarray.py +179 -0
  41. pyvale/core/sensorarrayfactory.py +210 -0
  42. pyvale/core/sensorarraypoint.py +280 -0
  43. pyvale/core/sensordata.py +72 -0
  44. pyvale/core/sensordescriptor.py +101 -0
  45. pyvale/core/sensortools.py +143 -0
  46. pyvale/core/visualexpplotter.py +151 -0
  47. pyvale/core/visualimagedef.py +71 -0
  48. pyvale/core/visualimages.py +75 -0
  49. pyvale/core/visualopts.py +180 -0
  50. pyvale/core/visualsimanimator.py +83 -0
  51. pyvale/core/visualsimplotter.py +182 -0
  52. pyvale/core/visualtools.py +81 -0
  53. pyvale/core/visualtraceplotter.py +256 -0
  54. pyvale/data/__init__.py +7 -0
  55. pyvale/data/case13_out.e +0 -0
  56. pyvale/data/case16_out.e +0 -0
  57. pyvale/data/case17_out.e +0 -0
  58. pyvale/data/case18_1_out.e +0 -0
  59. pyvale/data/case18_2_out.e +0 -0
  60. pyvale/data/case18_3_out.e +0 -0
  61. pyvale/data/case25_out.e +0 -0
  62. pyvale/data/case26_out.e +0 -0
  63. pyvale/data/optspeckle_2464x2056px_spec5px_8bit_gblur1px.tiff +0 -0
  64. pyvale/examples/__init__.py +7 -0
  65. pyvale/examples/analyticdatagen/__init__.py +7 -0
  66. pyvale/examples/analyticdatagen/ex1_1_scalarvisualisation.py +38 -0
  67. pyvale/examples/analyticdatagen/ex1_2_scalarcasebuild.py +46 -0
  68. pyvale/examples/analyticdatagen/ex2_1_analyticsensors.py +83 -0
  69. pyvale/examples/ex1_1_thermal2d.py +89 -0
  70. pyvale/examples/ex1_2_thermal2d.py +111 -0
  71. pyvale/examples/ex1_3_thermal2d.py +113 -0
  72. pyvale/examples/ex1_4_thermal2d.py +89 -0
  73. pyvale/examples/ex1_5_thermal2d.py +105 -0
  74. pyvale/examples/ex2_1_thermal3d .py +87 -0
  75. pyvale/examples/ex2_2_thermal3d.py +51 -0
  76. pyvale/examples/ex2_3_thermal3d.py +109 -0
  77. pyvale/examples/ex3_1_displacement2d.py +47 -0
  78. pyvale/examples/ex3_2_displacement2d.py +79 -0
  79. pyvale/examples/ex3_3_displacement2d.py +104 -0
  80. pyvale/examples/ex3_4_displacement2d.py +105 -0
  81. pyvale/examples/ex4_1_strain2d.py +57 -0
  82. pyvale/examples/ex4_2_strain2d.py +79 -0
  83. pyvale/examples/ex4_3_strain2d.py +100 -0
  84. pyvale/examples/ex5_1_multiphysics2d.py +78 -0
  85. pyvale/examples/ex6_1_multiphysics2d_expsim.py +118 -0
  86. pyvale/examples/ex6_2_multiphysics3d_expsim.py +158 -0
  87. pyvale/examples/features/__init__.py +7 -0
  88. pyvale/examples/features/ex_animation_tools_3dmonoblock.py +83 -0
  89. pyvale/examples/features/ex_area_avg.py +89 -0
  90. pyvale/examples/features/ex_calibration_error.py +108 -0
  91. pyvale/examples/features/ex_chain_field_errs.py +141 -0
  92. pyvale/examples/features/ex_field_errs.py +78 -0
  93. pyvale/examples/features/ex_sensor_single_angle_batch.py +110 -0
  94. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +86 -0
  95. pyvale/examples/rasterisation/ex_rastenp.py +154 -0
  96. pyvale/examples/rasterisation/ex_rastercyth_oneframe.py +220 -0
  97. pyvale/examples/rasterisation/ex_rastercyth_static_cypara.py +194 -0
  98. pyvale/examples/rasterisation/ex_rastercyth_static_pypara.py +193 -0
  99. pyvale/simcases/case00_HEX20.i +242 -0
  100. pyvale/simcases/case00_HEX27.i +242 -0
  101. pyvale/simcases/case00_TET10.i +242 -0
  102. pyvale/simcases/case00_TET14.i +242 -0
  103. pyvale/simcases/case01.i +101 -0
  104. pyvale/simcases/case02.i +156 -0
  105. pyvale/simcases/case03.i +136 -0
  106. pyvale/simcases/case04.i +181 -0
  107. pyvale/simcases/case05.i +234 -0
  108. pyvale/simcases/case06.i +305 -0
  109. pyvale/simcases/case07.geo +135 -0
  110. pyvale/simcases/case07.i +87 -0
  111. pyvale/simcases/case08.geo +144 -0
  112. pyvale/simcases/case08.i +153 -0
  113. pyvale/simcases/case09.geo +204 -0
  114. pyvale/simcases/case09.i +87 -0
  115. pyvale/simcases/case10.geo +204 -0
  116. pyvale/simcases/case10.i +257 -0
  117. pyvale/simcases/case11.geo +337 -0
  118. pyvale/simcases/case11.i +147 -0
  119. pyvale/simcases/case12.geo +388 -0
  120. pyvale/simcases/case12.i +329 -0
  121. pyvale/simcases/case13.i +140 -0
  122. pyvale/simcases/case14.i +159 -0
  123. pyvale/simcases/case15.geo +337 -0
  124. pyvale/simcases/case15.i +150 -0
  125. pyvale/simcases/case16.geo +391 -0
  126. pyvale/simcases/case16.i +357 -0
  127. pyvale/simcases/case17.geo +135 -0
  128. pyvale/simcases/case17.i +144 -0
  129. pyvale/simcases/case18.i +254 -0
  130. pyvale/simcases/case18_1.i +254 -0
  131. pyvale/simcases/case18_2.i +254 -0
  132. pyvale/simcases/case18_3.i +254 -0
  133. pyvale/simcases/case19.geo +252 -0
  134. pyvale/simcases/case19.i +99 -0
  135. pyvale/simcases/case20.geo +252 -0
  136. pyvale/simcases/case20.i +250 -0
  137. pyvale/simcases/case21.geo +74 -0
  138. pyvale/simcases/case21.i +155 -0
  139. pyvale/simcases/case22.geo +82 -0
  140. pyvale/simcases/case22.i +140 -0
  141. pyvale/simcases/case23.geo +164 -0
  142. pyvale/simcases/case23.i +140 -0
  143. pyvale/simcases/case24.geo +79 -0
  144. pyvale/simcases/case24.i +123 -0
  145. pyvale/simcases/case25.geo +82 -0
  146. pyvale/simcases/case25.i +140 -0
  147. pyvale/simcases/case26.geo +166 -0
  148. pyvale/simcases/case26.i +140 -0
  149. pyvale/simcases/run_1case.py +61 -0
  150. pyvale/simcases/run_all_cases.py +69 -0
  151. pyvale/simcases/run_build_case.py +64 -0
  152. pyvale/simcases/run_example_cases.py +69 -0
  153. pyvale-2025.4.0.dist-info/METADATA +140 -0
  154. pyvale-2025.4.0.dist-info/RECORD +157 -0
  155. pyvale-2025.4.0.dist-info/WHEEL +5 -0
  156. pyvale-2025.4.0.dist-info/licenses/LICENSE +21 -0
  157. pyvale-2025.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,156 @@
1
+ """
2
+ ================================================================================
3
+ pyvale: the python validation engine
4
+ License: MIT
5
+ Copyright (C) 2025 The Computer Aided Validation Team
6
+ ================================================================================
7
+ """
8
+ from enum import Enum
9
+ from dataclasses import dataclass, field
10
+ import numpy as np
11
+ import mooseherder as mh
12
+ from pyvale.core.fieldconverter import simdata_to_pyvista
13
+
14
+
15
+ # class ImageFormat(Enum):
16
+ # NPY = 0
17
+ # TIFF = 1
18
+
19
+ # @dataclass(slots=True)
20
+ # class RenderOpts:
21
+ # image_tag: str = "image"
22
+ # image_formats: tuple[ImageFormat,...]
23
+ # bits_per_unit: int = 1
24
+ # parallel: int | None = None
25
+
26
+
27
+ @dataclass(slots=True)
28
+ class RenderMeshData:
29
+ coords: np.ndarray
30
+ connectivity: np.ndarray
31
+ fields_render: np.ndarray
32
+
33
+ # If this is None then the mesh is not deformable
34
+ fields_disp: np.ndarray | None = None
35
+
36
+ node_count: int = field(init=False)
37
+ elem_count: int = field(init=False)
38
+ nodes_per_elem: int = field(init=False)
39
+
40
+ coord_cent: np.ndarray = field(init=False)
41
+ coord_bound_min: np.ndarray = field(init=False)
42
+ coord_bound_max: np.ndarray = field(init=False)
43
+
44
+ def __post_init__(self) -> None:
45
+ # C format: num_nodes/num_elems first as it is the largest dimension
46
+ self.node_count = self.coords.shape[0]
47
+ self.elem_count = self.connectivity.shape[0]
48
+ self.nodes_per_elem = self.connectivity.shape[1]
49
+
50
+ self.coord_bound_min = np.min(self.coords,axis=0)
51
+ self.coord_bound_max = np.max(self.coords,axis=0)
52
+ self.coord_cent = (self.coord_bound_max + self.coord_bound_min)/2.0
53
+
54
+
55
+ def create_render_mesh(sim_data: mh.SimData,
56
+ field_render_keys: tuple[str,...],
57
+ sim_spat_dim: int,
58
+ field_disp_keys: tuple[str,...] | None = None,
59
+ ) -> RenderMeshData:
60
+
61
+ extract_keys = field_render_keys
62
+ if field_disp_keys is not None:
63
+ extract_keys = field_render_keys+field_disp_keys
64
+
65
+ (pv_grid,_) = simdata_to_pyvista(sim_data,
66
+ extract_keys,
67
+ spat_dim=sim_spat_dim)
68
+
69
+ pv_surf = pv_grid.extract_surface()
70
+ faces = np.array(pv_surf.faces)
71
+
72
+ first_elem_nodes_per_face = faces[0]
73
+ nodes_per_face_vec = faces[0::(first_elem_nodes_per_face+1)]
74
+
75
+ # TODO: CHECKS
76
+ # - Number of displacement keys match the spat_dim parameter
77
+ assert np.all(nodes_per_face_vec == first_elem_nodes_per_face), \
78
+ "Not all elements in the simdata object have the same number of nodes per element"
79
+
80
+ nodes_per_face = first_elem_nodes_per_face
81
+ num_faces = int(faces.shape[0] / (nodes_per_face+1))
82
+
83
+ # Reshape the faces table and slice off the first column which is just the
84
+ # number of nodes per element and should be the same for all elements
85
+ connectivity = np.reshape(faces,(num_faces,nodes_per_face+1))
86
+ # shape=(num_elems,nodes_per_elem), C format
87
+ connectivity = np.ascontiguousarray(connectivity[:,1:],dtype=np.uintp)
88
+
89
+ # shape=(num_nodes,3), C format
90
+ coords_world = np.array(pv_surf.points)
91
+
92
+ # Add w coord=1, shape=(num_nodes,3+1)
93
+ coords_world= np.hstack((coords_world,np.ones([coords_world.shape[0],1])))
94
+
95
+ # shape=(num_nodes,num_time_steps,num_components)
96
+ field_render_shape = np.array(pv_surf[field_render_keys[0]]).shape
97
+ fields_render_by_node = np.zeros(field_render_shape+(len(field_render_keys),),
98
+ dtype=np.float64)
99
+ for ii,cc in enumerate(field_render_keys):
100
+ fields_render_by_node[:,:,ii] = np.ascontiguousarray(
101
+ np.array(pv_surf[cc]))
102
+
103
+
104
+ field_disp_by_node = None
105
+ if field_disp_keys is not None:
106
+ field_disp_shape = np.array(pv_surf[field_disp_keys[0]]).shape
107
+ # shape=(num_nodes,num_time_steps,num_components)
108
+ field_disp_by_node = np.zeros(field_disp_shape+(len(field_disp_keys),),
109
+ dtype=np.float64)
110
+ for ii,cc in enumerate(field_disp_keys):
111
+ field_disp_by_node[:,:,ii] = np.ascontiguousarray(
112
+ np.array(pv_surf[cc]))
113
+
114
+
115
+
116
+ return RenderMeshData(coords=coords_world,
117
+ connectivity=connectivity,
118
+ fields_render=fields_render_by_node,
119
+ fields_disp=field_disp_by_node)
120
+
121
+
122
+ def slice_mesh_data_by_elem(coords_world: np.ndarray,
123
+ connectivity: np.ndarray,
124
+ field_by_node: np.ndarray,
125
+ ) -> tuple[np.ndarray,np.ndarray]:
126
+ """_summary_
127
+
128
+ Parameters
129
+ ----------
130
+ coords_world : np.ndarray
131
+ _description_
132
+ connectivity : np.ndarray
133
+ _description_
134
+ field_by_node : np.ndarray
135
+ _description_
136
+
137
+ Returns
138
+ -------
139
+ tuple[np.ndarray,np.ndarray]
140
+ _description_
141
+ """
142
+ # shape=(coord[X,Y,Z,W],node_per_elem,elem_num)
143
+ elem_world_coords = np.copy(coords_world[connectivity,:])
144
+
145
+ # shape=(elem_num,nodes_per_elem,coord[X,Y,Z,W]), C memory format
146
+ # elem_world_coords = np.ascontiguousarray(np.swapaxes(elem_world_coords,0,2))
147
+ elem_world_coords = np.ascontiguousarray(elem_world_coords)
148
+
149
+ # shape=(nodes_per_elem,elem_num,time_steps)
150
+ field_by_elem = np.copy(field_by_node[connectivity,:])
151
+
152
+ # shape=(elem_num,nodes_per_elem,time_steps), C memory format
153
+ # field_by_elem = np.ascontiguousarray(np.swapaxes(field_by_elem,0,1))
154
+ field_by_elem = np.ascontiguousarray(field_by_elem)
155
+
156
+ return (elem_world_coords,field_by_elem)
@@ -0,0 +1,179 @@
1
+ """
2
+ ================================================================================
3
+ pyvale: the python validation engine
4
+ License: MIT
5
+ Copyright (C) 2025 The Computer Aided Validation Team
6
+ ================================================================================
7
+ """
8
+ from abc import ABC, abstractmethod
9
+ import numpy as np
10
+ from pyvale.core.field import IField
11
+
12
+
13
+ class ISensorArray(ABC):
14
+ """Interface (abstract base class) for an array of sensors of the same
15
+ type sampling a given physical field.
16
+
17
+ This class implements the `pyvale` sensor measurement simulation model. Here
18
+ a measurement is taken as: measurement = truth + random errors + systematic
19
+ errors. The truth value for each sensor is interpolated from the physical
20
+ field (an implementation of the `IField` interface, nominally a
21
+ `FieldScalar`, `FieldVector` or `FieldTensor` object).
22
+
23
+ The random and systematic errors are calculated by a user specified error
24
+ integrator (`ErrIntegrator` class). This class contains a chain of different
25
+ types of user selected errors (implementations of the `IErrCalculator`
26
+ interface). Further information can be found in the `ErrIntegrator` class
27
+ and in implementations of the `IErrCalculator` interface.
28
+
29
+ In `pyvale`, function and methods with `calc` in their name will cause
30
+ probability distributions to be resampled and any additional calculations,
31
+ such as interpolation, to be performed. Functions and methods with `get` in
32
+ the name will directly return the previously calculated values without
33
+ resampling probability distributions.
34
+
35
+ Calling the class method `calc_measurements()` will create and return an
36
+ array of simulated sensor measurements with the following shape=(num_sensors
37
+ ,num_field_component,num_time_steps). When calling `calc_measurements()` all
38
+ sensor errors that are based on probability distributions are resampled and
39
+ any required interpolations are performed (e.g. a random perturbation of the
40
+ sensor positions requiring interpolation at the perturbed sensor location).
41
+
42
+ Calling the class method `get_measurements()` just returns the previously
43
+ calculated set of sensor measurements without resampling of probability.
44
+ Distributions.
45
+
46
+ Without an error integrator this class can be used for interpolating
47
+ simulated physical fields quickly using finite element shape functions.
48
+ """
49
+
50
+ @abstractmethod
51
+ def get_measurement_shape(self) -> tuple[int,int,int]:
52
+ """Abstract method. Gets the shape of the measurement array:
53
+ shape=(num_sensors,num_field_components,num_time_steps).
54
+
55
+ The number of sensors is specified by the user with a SensorData object.
56
+ The number of field components is dependent on the field being sampled
57
+ (i.e. 1 for a scalar field and 3 for a vector field in 3D). The number
58
+ of time steps is specified by the user in the SensorData object or
59
+ defaults to the time steps taken from the simulation.
60
+
61
+ Returns
62
+ -------
63
+ tuple[int,int,int]
64
+ Shape of the measurement array as (num_sensors,
65
+ num_field_components,num_time_steps)
66
+ """
67
+ pass
68
+
69
+ @abstractmethod
70
+ def get_field(self) -> IField:
71
+ """Abstract method. Gets the field object that this array of sensors is
72
+ sampling to simulate measurements.
73
+
74
+ Returns
75
+ -------
76
+ IField
77
+ A field object interface.
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ def get_truth(self) -> np.ndarray:
83
+ """Abstract method. Gets the ground truth sensor values that were
84
+ calculated previously. If the ground truth values have not been
85
+ calculated then `calc_truth_values()` is called first.
86
+
87
+ Returns
88
+ -------
89
+ np.ndarray
90
+ Array of ground truth sensor values. shape=(num_sensors,
91
+ num_field_components,num_time_steps).
92
+ """
93
+ pass
94
+
95
+ @abstractmethod
96
+ def get_errors_systematic(self) -> np.ndarray | None:
97
+ """Abstract method. Gets the systematic error array from the previously
98
+ calculated sensor measurements. Returns None is no error integrator has
99
+ been specified.
100
+
101
+ Returns
102
+ -------
103
+ np.ndarray | None
104
+ Array of systematic errors for this sensor array. shape=(num_sensors
105
+ ,num_field_components,num_time_steps). Returns None if no error
106
+ integrator has been set.
107
+ """
108
+ pass
109
+
110
+ @abstractmethod
111
+ def get_errors_random(self) -> np.ndarray | None:
112
+ """Abstract method. Gets the random error array from the previously
113
+ calculated sensor measurements. Returns None is no error integrator has
114
+ been specified.
115
+
116
+ Returns
117
+ -------
118
+ np.ndarray | None
119
+ Array of random errors for this sensor array. shape=(num_sensors
120
+ ,num_field_components,num_time_steps). Returns None if no error
121
+ integrator has been set.
122
+ """
123
+ pass
124
+
125
+ @abstractmethod
126
+ def get_errors_total(self) -> np.ndarray | None:
127
+ """Abstract method. Gets the total error array from the previously
128
+ calculated sensor measurements. Returns None is no error integrator has
129
+ been specified.
130
+
131
+ Returns
132
+ -------
133
+ np.ndarray | None
134
+ Array of total errors for this sensor array. shape=(num_sensors
135
+ ,num_field_components,num_time_steps). Returns None if no error
136
+ integrator has been set.
137
+ """
138
+ pass
139
+
140
+ @abstractmethod
141
+ def calc_measurements(self) -> np.ndarray:
142
+ """Abstract method. Calculates measurements as: measurement = truth +
143
+ systematic errors + random errors. The truth is calculated once and is
144
+ interpolated from the input simulation field. The errors are calculated
145
+ based on the user specified error chain.
146
+
147
+ NOTE: this is a 'calc' method and will sample all probability
148
+ distributions in the error chain returning a new simulated experiment
149
+ for this sensor array.
150
+
151
+ Returns
152
+ -------
153
+ np.ndarray
154
+ The calculated measurements for this sensor array with shape:
155
+ (num_sensors,num_field_components,num_time_steps)
156
+ """
157
+ pass
158
+
159
+ @abstractmethod
160
+ def get_measurements(self) -> np.ndarray:
161
+ """Abstract method. Returns the current set of simulated measurements if
162
+ theses have been calculated. If these have not been calculated then
163
+ 'calc_measurements()' is called and a set of measurements in then
164
+ returned.
165
+
166
+ NOTE: this is a 'get' method and does not sample from probability
167
+ distributions in the error chain and directly returns the current set of
168
+ measurements if they exist.
169
+
170
+ Returns
171
+ -------
172
+ np.ndarray
173
+ The calculated measurements for this sensor array with shape:
174
+ (num_sensors,num_field_components,num_time_steps)
175
+ """
176
+ pass
177
+
178
+
179
+
@@ -0,0 +1,210 @@
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
+
10
+ import mooseherder as mh
11
+
12
+ from pyvale.core.fieldscalar import FieldScalar
13
+ from pyvale.core.fieldvector import FieldVector
14
+ from pyvale.core.fieldtensor import FieldTensor
15
+ from pyvale.core.sensordescriptor import SensorDescriptorFactory
16
+ from pyvale.core.sensorarraypoint import SensorArrayPoint, SensorData
17
+ from pyvale.core.errorintegrator import ErrIntegrator
18
+ from pyvale.core.errorsysindep import ErrSysUniformPercent
19
+ from pyvale.core.errorrand import ErrRandNormPercent
20
+ from pyvale.core.errorsysdep import (ErrSysDigitisation,
21
+ ErrSysSaturation)
22
+
23
+ #TODO: Docstrings
24
+
25
+ class SensorArrayFactory:
26
+ """Namespace for static methods used to build common types of sensor arrays
27
+ simplifying sensor array creation for users.
28
+ """
29
+
30
+ @staticmethod
31
+ def thermocouples_no_errs(sim_data: mh.SimData,
32
+ sensor_data: SensorData,
33
+ field_name: str = "temperature",
34
+ spat_dims: int = 3,
35
+ ) -> SensorArrayPoint:
36
+ """Builds and returns a point sensor array with common parameters used
37
+ for thermocouples applied to a temperature field without any simulated
38
+ measurement errors. Allows the user to build and attach their own error
39
+ chain or use this for fast interpolation to sensor locations without
40
+ errors.
41
+
42
+ Parameters
43
+ ----------
44
+ sim_data : mh.SimData
45
+ Simulation data containing a mesh and a temperature field for the
46
+ thermocouple array to sample.
47
+ sensor_data : SensorData
48
+ _description_
49
+ field_name : str, optional
50
+ _description_, by default "temperature"
51
+ spat_dims : int, optional
52
+ , by default 3
53
+
54
+ Returns
55
+ -------
56
+ SensorArrayPoint
57
+ _description_
58
+ """
59
+ descriptor = SensorDescriptorFactory.temperature_descriptor()
60
+
61
+ t_field = FieldScalar(sim_data,field_name,spat_dims)
62
+
63
+ sens_array = SensorArrayPoint(sensor_data,
64
+ t_field,
65
+ descriptor)
66
+
67
+ return sens_array
68
+
69
+ @staticmethod
70
+ def thermocouples_basic_errs(sim_data: mh.SimData,
71
+ sensor_data: SensorData,
72
+ field_name: str = "temperature",
73
+ spat_dims: int = 3,
74
+ errs_pc: float = 1.0
75
+ ) -> SensorArrayPoint:
76
+
77
+ sens_array = SensorArrayFactory.thermocouples_no_errs(sim_data,
78
+ sensor_data,
79
+ field_name,
80
+ spat_dims)
81
+
82
+ err_int = basic_err_integrator(sens_array.get_measurement_shape(),
83
+ sensor_data,
84
+ errs_pc)
85
+
86
+ # Normal thermcouple amp = 5mV / K
87
+ err_int._err_chain.append(ErrSysDigitisation(bits_per_unit=2**16/1000))
88
+ err_int._err_chain.append(ErrSysSaturation(meas_min=0.0,meas_max=1000.0))
89
+
90
+ sens_array.set_error_integrator(err_int)
91
+ return sens_array
92
+
93
+ @staticmethod
94
+ def disp_sensors_no_errs(sim_data: mh.SimData,
95
+ sensor_data: SensorData,
96
+ field_name: str = "displacement",
97
+ spat_dims: int = 3,
98
+ ) -> SensorArrayPoint:
99
+
100
+ descriptor = SensorDescriptorFactory.displacement_descriptor()
101
+
102
+ disp_field = FieldVector(sim_data,
103
+ field_name,
104
+ ('disp_x','disp_y'),
105
+ spat_dims)
106
+
107
+ sens_array = SensorArrayPoint(sensor_data,
108
+ disp_field,
109
+ descriptor)
110
+ return sens_array
111
+
112
+
113
+ @staticmethod
114
+ def disp_sensors_basic_errs(sim_data: mh.SimData,
115
+ sensor_data: SensorData,
116
+ field_name: str = "displacement",
117
+ spat_dims: int = 3,
118
+ errs_pc: float = 1
119
+ ) -> SensorArrayPoint:
120
+
121
+ sens_array = SensorArrayFactory.disp_sensors_no_errs(sim_data,
122
+ sensor_data,
123
+ field_name,
124
+ spat_dims)
125
+ err_int = basic_err_integrator(sens_array.get_measurement_shape(),
126
+ sensor_data,
127
+ errs_pc)
128
+ sens_array.set_error_integrator(err_int)
129
+
130
+ return sens_array
131
+
132
+ @staticmethod
133
+ def strain_gauges_no_errs(sim_data: mh.SimData,
134
+ sensor_data: SensorData,
135
+ field_name: str = "strain",
136
+ spat_dims: int = 3
137
+ ) -> SensorArrayPoint:
138
+ descriptor = SensorDescriptorFactory.strain_descriptor(spat_dims)
139
+
140
+ if spat_dims == 2:
141
+ norm_components = ('strain_xx','strain_yy')
142
+ dev_components = ('strain_xy',)
143
+ else:
144
+ norm_components = ('strain_xx','strain_yy','strain_zz')
145
+ dev_components = ('strain_xy','strain_yz','strain_xz')
146
+
147
+ strain_field = FieldTensor(sim_data,
148
+ field_name,
149
+ norm_components,
150
+ dev_components,
151
+ spat_dims)
152
+
153
+ sens_array = SensorArrayPoint(sensor_data,
154
+ strain_field,
155
+ descriptor)
156
+
157
+ return sens_array
158
+
159
+
160
+ @staticmethod
161
+ def strain_gauges_basic_errs(sim_data: mh.SimData,
162
+ sensor_data: SensorData,
163
+ field_name: str = "strain",
164
+ spat_dims: int = 3,
165
+ errs_pc: float = 1.0
166
+ ) -> SensorArrayPoint:
167
+
168
+ sens_array = SensorArrayFactory.strain_gauges_no_errs(sim_data,
169
+ sensor_data,
170
+ field_name,
171
+ spat_dims)
172
+
173
+ err_int = basic_err_integrator(sens_array.get_measurement_shape(),
174
+ sensor_data,
175
+ errs_pc)
176
+ sens_array.set_error_integrator(err_int)
177
+
178
+ return sens_array
179
+
180
+
181
+ def basic_err_integrator(meas_shape: np.ndarray,
182
+ sensor_data: SensorData,
183
+ sys_err_pc: float = 1.0,
184
+ rand_err_pc: float = 1.0) -> ErrIntegrator:
185
+ """Builds a basic error integrator with uniform percentage systematic error
186
+ calculator and a percentage normal random error calculator.
187
+
188
+ Parameters
189
+ ----------
190
+ meas_shape : np.ndarray
191
+ Shape of the measurement array which is (num_sensors,
192
+ num_field_components,num_time_steps)
193
+ sensor_data : SensorData
194
+ Sensor array parameters for feeding through the error chain.
195
+ sys_err_pc : float, optional
196
+ Percentage systematic error, by default 1.0.
197
+ rand_err_pc : float, optional
198
+ Percentage random error, by default 1.0.
199
+
200
+ Returns
201
+ -------
202
+ ErrIntegrator
203
+ A basic error integrator with a uniform percentage systematic error and
204
+ a normal percentage random error.
205
+ """
206
+ err_chain = []
207
+ err_chain.append(ErrSysUniformPercent(-sys_err_pc,sys_err_pc))
208
+ err_chain.append(ErrRandNormPercent(rand_err_pc))
209
+ err_int = ErrIntegrator(err_chain,sensor_data,meas_shape)
210
+ return err_int