Skip to content

Modules

DeformationGradient

A class representing deformation gradient operations.

Source code in hyper_surrogate/deformation_gradient.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
class DeformationGradient:
    """A class representing deformation gradient operations."""

    def __init__(self) -> None:
        pass

    @staticmethod
    def uniaxial(stretch: np.ndarray) -> np.ndarray:
        """
        Calculate the deformation gradient tensor for uniaxial deformation.

        Args:
            stretch: A 1D array representing the stretch factor.
        Returns:
            The deformation gradient tensor as a 3D array.
        """
        stretch = np.atleast_1d(stretch)
        # Calculate the transverse stretch factor for the entire array
        stretch_t = stretch**-0.5
        # Initialize the resulting 3D array with zeros
        result = np.zeros((stretch.size, 3, 3))
        # Fill in the diagonal values for each 2D sub-array
        result[:, 0, 0] = stretch  # Set the first diagonal elements to stretch
        result[:, 1, 1] = stretch_t  # Set the second diagonal elements to stretch_t
        result[:, 2, 2] = stretch_t  # Set the third diagonal elements to stretch_t

        return result

    @staticmethod
    def shear(shear: np.ndarray) -> np.ndarray:
        """
        Calculate the deformation gradient tensor for shear deformation.

        Args:
            shear: A 1D array representing the shear factor.

        Returns:
            The deformation gradient tensor as a 3D array.
        """
        shear = np.atleast_1d(shear)
        # Initialize the resulting 3D array with the identity matrix replicated for each shear value
        result = np.repeat(np.eye(3)[np.newaxis, :, :], shear.size, axis=0)

        # Set the shear values in the appropriate position for each 2D sub-array
        result[:, 0, 1] = shear

        return result

    @staticmethod
    def biaxial(stretch1: np.ndarray, stretch2: np.ndarray) -> np.ndarray:
        """
        Calculate the deformation gradient tensor for biaxial deformation.
        latex equation:

        Args:
            stretch1: A 1D array representing the first stretch factor.
            stretch2: A 1D array representing the second stretch factor.

        Returns:
            The deformation gradient tensor as a 3D array.
        """
        # Calculate the third stretch factor for the entire arrays
        stretch1 = np.atleast_1d(stretch1)
        stretch2 = np.atleast_1d(stretch2)
        stretch3 = (stretch1 * stretch2) ** -1.0

        # Initialize the resulting 3D array with zeros
        result = np.zeros((stretch1.size, 3, 3))

        # Fill in the diagonal values for each 2D sub-array
        result[:, 0, 0] = stretch1  # Set the first diagonal elements to stretch1
        result[:, 1, 1] = stretch2  # Set the second diagonal elements to stretch2
        result[:, 2, 2] = stretch3  # Set the third diagonal elements to stretch3

        return result

    @staticmethod
    def _axis_rotation(axis: int, angle: float) -> np.ndarray:
        """
        Calculate the rotation matrix for a given axis and angle.

        Args:
            axis: An integer representing the axis of rotation (0 for x-axis, 1 for y-axis, 2 for z-axis).
            angle: A float representing the angle of rotation in radians.

        Returns:
            The rotation matrix as a 2D array.
        """
        c, s = np.cos(angle), np.sin(angle)
        dict_axis = {
            0: np.array([
                [1, 0, 0],
                [0, c, -s],
                [0, s, c],
            ]),
            1: np.array([
                [c, 0, s],
                [0, 1, 0],
                [-s, 0, c],
            ]),
            2: np.array([
                [c, -s, 0],
                [s, c, 0],
                [0, 0, 1],
            ]),
        }
        return dict_axis[axis] if axis in dict_axis else np.eye(3)

    def rotation(self, axis: np.ndarray, angle: np.ndarray) -> np.ndarray:
        """
        Calculate the rotation matrix for multiple axes and angles.

        Args:
            axis: A 1D array representing the axes of rotation (0 for x-axis, 1 for y-axis, 2 for z-axis).
            angle: A 1D array representing the angles of rotation in radians.

        Returns:
            The rotation matrix as a 3D array.
        """
        axis, angle = np.atleast_1d(axis), np.atleast_1d(angle)
        rotation = []
        for ax, ang in zip(axis, angle):
            rotation.append(self._axis_rotation(ax, ang))
        return np.array(rotation)

    def rescale(self, F: np.ndarray) -> Any:
        """
        Rescale the deformation gradient tensor.

        Args:
            F: The deformation gradient tensor as a 3D array.

        Returns:
            The rescaled deformation gradient tensor.
        """
        return F / np.linalg.det(F) ** (1.0 / 3.0)

    @staticmethod
    def to_radians(degree: float) -> float:
        """
        Convert degrees to radians.

        Args:
            degree: The angle in degrees.

        Returns:
            The angle in radians.
        """
        return degree * np.pi / 180

    @staticmethod
    def rotate(F: np.ndarray, R: np.ndarray) -> Any:
        """
        Rotate the deformation gradient tensor.

        Args:
            F: The deformation gradient tensor as a 3D array.
            R: The rotation matrix as a 3D array.

        Returns:
            The rotated deformation gradient tensor.
        """
        F = np.atleast_3d(F)
        R = np.atleast_3d(R)
        return np.einsum("nij,njk,nlk->nil", R, F, R)

biaxial(stretch1, stretch2) staticmethod

Calculate the deformation gradient tensor for biaxial deformation. latex equation:

Parameters:

Name Type Description Default
stretch1 ndarray

A 1D array representing the first stretch factor.

required
stretch2 ndarray

A 1D array representing the second stretch factor.

required

Returns:

Type Description
ndarray

The deformation gradient tensor as a 3D array.

Source code in hyper_surrogate/deformation_gradient.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
@staticmethod
def biaxial(stretch1: np.ndarray, stretch2: np.ndarray) -> np.ndarray:
    """
    Calculate the deformation gradient tensor for biaxial deformation.
    latex equation:

    Args:
        stretch1: A 1D array representing the first stretch factor.
        stretch2: A 1D array representing the second stretch factor.

    Returns:
        The deformation gradient tensor as a 3D array.
    """
    # Calculate the third stretch factor for the entire arrays
    stretch1 = np.atleast_1d(stretch1)
    stretch2 = np.atleast_1d(stretch2)
    stretch3 = (stretch1 * stretch2) ** -1.0

    # Initialize the resulting 3D array with zeros
    result = np.zeros((stretch1.size, 3, 3))

    # Fill in the diagonal values for each 2D sub-array
    result[:, 0, 0] = stretch1  # Set the first diagonal elements to stretch1
    result[:, 1, 1] = stretch2  # Set the second diagonal elements to stretch2
    result[:, 2, 2] = stretch3  # Set the third diagonal elements to stretch3

    return result

rescale(F)

Rescale the deformation gradient tensor.

Parameters:

Name Type Description Default
F ndarray

The deformation gradient tensor as a 3D array.

required

Returns:

Type Description
Any

The rescaled deformation gradient tensor.

Source code in hyper_surrogate/deformation_gradient.py
133
134
135
136
137
138
139
140
141
142
143
def rescale(self, F: np.ndarray) -> Any:
    """
    Rescale the deformation gradient tensor.

    Args:
        F: The deformation gradient tensor as a 3D array.

    Returns:
        The rescaled deformation gradient tensor.
    """
    return F / np.linalg.det(F) ** (1.0 / 3.0)

rotate(F, R) staticmethod

Rotate the deformation gradient tensor.

Parameters:

Name Type Description Default
F ndarray

The deformation gradient tensor as a 3D array.

required
R ndarray

The rotation matrix as a 3D array.

required

Returns:

Type Description
Any

The rotated deformation gradient tensor.

Source code in hyper_surrogate/deformation_gradient.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
@staticmethod
def rotate(F: np.ndarray, R: np.ndarray) -> Any:
    """
    Rotate the deformation gradient tensor.

    Args:
        F: The deformation gradient tensor as a 3D array.
        R: The rotation matrix as a 3D array.

    Returns:
        The rotated deformation gradient tensor.
    """
    F = np.atleast_3d(F)
    R = np.atleast_3d(R)
    return np.einsum("nij,njk,nlk->nil", R, F, R)

rotation(axis, angle)

Calculate the rotation matrix for multiple axes and angles.

Parameters:

Name Type Description Default
axis ndarray

A 1D array representing the axes of rotation (0 for x-axis, 1 for y-axis, 2 for z-axis).

required
angle ndarray

A 1D array representing the angles of rotation in radians.

required

Returns:

Type Description
ndarray

The rotation matrix as a 3D array.

Source code in hyper_surrogate/deformation_gradient.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def rotation(self, axis: np.ndarray, angle: np.ndarray) -> np.ndarray:
    """
    Calculate the rotation matrix for multiple axes and angles.

    Args:
        axis: A 1D array representing the axes of rotation (0 for x-axis, 1 for y-axis, 2 for z-axis).
        angle: A 1D array representing the angles of rotation in radians.

    Returns:
        The rotation matrix as a 3D array.
    """
    axis, angle = np.atleast_1d(axis), np.atleast_1d(angle)
    rotation = []
    for ax, ang in zip(axis, angle):
        rotation.append(self._axis_rotation(ax, ang))
    return np.array(rotation)

shear(shear) staticmethod

Calculate the deformation gradient tensor for shear deformation.

Parameters:

Name Type Description Default
shear ndarray

A 1D array representing the shear factor.

required

Returns:

Type Description
ndarray

The deformation gradient tensor as a 3D array.

Source code in hyper_surrogate/deformation_gradient.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@staticmethod
def shear(shear: np.ndarray) -> np.ndarray:
    """
    Calculate the deformation gradient tensor for shear deformation.

    Args:
        shear: A 1D array representing the shear factor.

    Returns:
        The deformation gradient tensor as a 3D array.
    """
    shear = np.atleast_1d(shear)
    # Initialize the resulting 3D array with the identity matrix replicated for each shear value
    result = np.repeat(np.eye(3)[np.newaxis, :, :], shear.size, axis=0)

    # Set the shear values in the appropriate position for each 2D sub-array
    result[:, 0, 1] = shear

    return result

to_radians(degree) staticmethod

Convert degrees to radians.

Parameters:

Name Type Description Default
degree float

The angle in degrees.

required

Returns:

Type Description
float

The angle in radians.

Source code in hyper_surrogate/deformation_gradient.py
145
146
147
148
149
150
151
152
153
154
155
156
@staticmethod
def to_radians(degree: float) -> float:
    """
    Convert degrees to radians.

    Args:
        degree: The angle in degrees.

    Returns:
        The angle in radians.
    """
    return degree * np.pi / 180

uniaxial(stretch) staticmethod

Calculate the deformation gradient tensor for uniaxial deformation.

Parameters:

Name Type Description Default
stretch ndarray

A 1D array representing the stretch factor.

required

Returns: The deformation gradient tensor as a 3D array.

Source code in hyper_surrogate/deformation_gradient.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@staticmethod
def uniaxial(stretch: np.ndarray) -> np.ndarray:
    """
    Calculate the deformation gradient tensor for uniaxial deformation.

    Args:
        stretch: A 1D array representing the stretch factor.
    Returns:
        The deformation gradient tensor as a 3D array.
    """
    stretch = np.atleast_1d(stretch)
    # Calculate the transverse stretch factor for the entire array
    stretch_t = stretch**-0.5
    # Initialize the resulting 3D array with zeros
    result = np.zeros((stretch.size, 3, 3))
    # Fill in the diagonal values for each 2D sub-array
    result[:, 0, 0] = stretch  # Set the first diagonal elements to stretch
    result[:, 1, 1] = stretch_t  # Set the second diagonal elements to stretch_t
    result[:, 2, 2] = stretch_t  # Set the third diagonal elements to stretch_t

    return result

DeformationGradientGenerator

Bases: DeformationGradient

Generates deformation gradients for hyper-surrogate modeling.

Parameters:

Name Type Description Default
seed int | None

Seed value for the random number generator. Default is None.

None
size int | None

Size of the generator. Default is None.

None
generator Generator | None

Random number generator. Default is None.

None

Attributes:

Name Type Description
seed int | None

Seed value for the random number generator.

size int | None

Size of the generator.

generator Generator

Random number generator.

Methods:

Name Description
axis

int = 3) -> Any: Generates a random axis.

angle

float = 5) -> Any: Generates a random angle.

generate_rotation

int = 3, min_interval: float = 5) -> np.ndarray: Generates a random rotation matrix.

generate

float = 0.4, stretch_max: float = 3.0, shear_min: float = -1, shear_max: float = 1) -> Any: Generates a deformation gradient.

Source code in hyper_surrogate/deformation_gradient.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
class DeformationGradientGenerator(DeformationGradient):
    """
    Generates deformation gradients for hyper-surrogate modeling.

    Args:
        seed (int | None): Seed value for the random number generator. Default is None.
        size (int | None): Size of the generator. Default is None.
        generator (Generator | None): Random number generator. Default is None.

    Attributes:
        seed (int | None): Seed value for the random number generator.
        size (int | None): Size of the generator.
        generator (Generator): Random number generator.

    Methods:
        axis(n_axis: int = 3) -> Any:
            Generates a random axis.

        angle(min_interval: float = 5) -> Any:
            Generates a random angle.

        generate_rotation(n_axis: int = 3, min_interval: float = 5) -> np.ndarray:
            Generates a random rotation matrix.

        generate(stretch_min: float = 0.4, stretch_max: float = 3.0, shear_min: float = -1, shear_max: float = 1) -> Any:
            Generates a deformation gradient.

    """

    def __init__(
        self,
        seed: int | None = None,
        size: int | None = None,
        generator: Generator | None = None,
    ) -> None:
        self.seed = seed
        self.size = size
        self.generator = generator if generator else Generator(seed=seed, size=size)

    def axis(self, n_axis: int = 3) -> Any:
        """
        Generates a random axis.

        Args:
            n_axis (int): Number of axes to choose from. Default is 3.

        Returns:
            Any: Randomly generated axis.

        """
        return self.generator.integer_in_interval(low=0, high=n_axis)

    def angle(self, min_interval: float = 5) -> Any:
        """
        Generates a random angle.

        Args:
            min_interval (float): Minimum interval for the angle. Default is 5.

        Returns:
            Any: Randomly generated angle.

        """
        min_interval = self.to_radians(min_interval)
        return self.generator.float_in_interval(a=0, b=np.pi, interval=min_interval)

    def generate_rotation(self, n_axis: int = 3, min_interval: float = 5) -> np.ndarray:
        """
        Generates a random rotation matrix.

        Args:
            n_axis (int): Number of axes to choose from. Default is 3.
            min_interval (float): Minimum interval for the angle. Default is 5.

        Returns:
            np.ndarray: Randomly generated rotation matrix.

        """
        axis = self.axis(n_axis=n_axis)
        angle = self.angle(min_interval=min_interval)
        return self.rotation(axis, angle)

    def generate(
        self,
        stretch_min: float = 0.4,
        stretch_max: float = 3.0,
        shear_min: float = -1,
        shear_max: float = 1,
    ) -> Any:
        """
        Generates a random deformation gradient.

        Args:
            stretch_min (float): Minimum value for stretch. Default is 0.4.
            stretch_max (float): Maximum value for stretch. Default is 3.0.
            shear_min (float): Minimum value for shear. Default is -1.
            shear_max (float): Maximum value for shear. Default is 1.

        Returns:
            Any: Generated random deformation gradient.

        """
        u, s, b1, b2 = (
            self.generator.uniform(stretch_min, stretch_max),
            self.generator.uniform(shear_min, shear_max),
            self.generator.uniform(stretch_min, stretch_max),
            self.generator.uniform(stretch_min, stretch_max),
        )
        fu, fs, fb = (
            self.uniaxial(u),
            self.shear(s),
            self.biaxial(b1, b2),
        )
        r1, r2, r3 = (
            self.generate_rotation(),
            self.generate_rotation(),
            self.generate_rotation(),
        )

        # rotate deformation gradients
        fu = self.rotate(fu, r1)
        fs = self.rotate(fs, r2)
        fb = self.rotate(fb, r3)

        # Compute deformation gradient
        return np.matmul(np.matmul(fb, fu), fs)

angle(min_interval=5)

Generates a random angle.

Parameters:

Name Type Description Default
min_interval float

Minimum interval for the angle. Default is 5.

5

Returns:

Name Type Description
Any Any

Randomly generated angle.

Source code in hyper_surrogate/deformation_gradient.py
227
228
229
230
231
232
233
234
235
236
237
238
239
def angle(self, min_interval: float = 5) -> Any:
    """
    Generates a random angle.

    Args:
        min_interval (float): Minimum interval for the angle. Default is 5.

    Returns:
        Any: Randomly generated angle.

    """
    min_interval = self.to_radians(min_interval)
    return self.generator.float_in_interval(a=0, b=np.pi, interval=min_interval)

axis(n_axis=3)

Generates a random axis.

Parameters:

Name Type Description Default
n_axis int

Number of axes to choose from. Default is 3.

3

Returns:

Name Type Description
Any Any

Randomly generated axis.

Source code in hyper_surrogate/deformation_gradient.py
214
215
216
217
218
219
220
221
222
223
224
225
def axis(self, n_axis: int = 3) -> Any:
    """
    Generates a random axis.

    Args:
        n_axis (int): Number of axes to choose from. Default is 3.

    Returns:
        Any: Randomly generated axis.

    """
    return self.generator.integer_in_interval(low=0, high=n_axis)

generate(stretch_min=0.4, stretch_max=3.0, shear_min=-1, shear_max=1)

Generates a random deformation gradient.

Parameters:

Name Type Description Default
stretch_min float

Minimum value for stretch. Default is 0.4.

0.4
stretch_max float

Maximum value for stretch. Default is 3.0.

3.0
shear_min float

Minimum value for shear. Default is -1.

-1
shear_max float

Maximum value for shear. Default is 1.

1

Returns:

Name Type Description
Any Any

Generated random deformation gradient.

Source code in hyper_surrogate/deformation_gradient.py
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
def generate(
    self,
    stretch_min: float = 0.4,
    stretch_max: float = 3.0,
    shear_min: float = -1,
    shear_max: float = 1,
) -> Any:
    """
    Generates a random deformation gradient.

    Args:
        stretch_min (float): Minimum value for stretch. Default is 0.4.
        stretch_max (float): Maximum value for stretch. Default is 3.0.
        shear_min (float): Minimum value for shear. Default is -1.
        shear_max (float): Maximum value for shear. Default is 1.

    Returns:
        Any: Generated random deformation gradient.

    """
    u, s, b1, b2 = (
        self.generator.uniform(stretch_min, stretch_max),
        self.generator.uniform(shear_min, shear_max),
        self.generator.uniform(stretch_min, stretch_max),
        self.generator.uniform(stretch_min, stretch_max),
    )
    fu, fs, fb = (
        self.uniaxial(u),
        self.shear(s),
        self.biaxial(b1, b2),
    )
    r1, r2, r3 = (
        self.generate_rotation(),
        self.generate_rotation(),
        self.generate_rotation(),
    )

    # rotate deformation gradients
    fu = self.rotate(fu, r1)
    fs = self.rotate(fs, r2)
    fb = self.rotate(fb, r3)

    # Compute deformation gradient
    return np.matmul(np.matmul(fb, fu), fs)

generate_rotation(n_axis=3, min_interval=5)

Generates a random rotation matrix.

Parameters:

Name Type Description Default
n_axis int

Number of axes to choose from. Default is 3.

3
min_interval float

Minimum interval for the angle. Default is 5.

5

Returns:

Type Description
ndarray

np.ndarray: Randomly generated rotation matrix.

Source code in hyper_surrogate/deformation_gradient.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
def generate_rotation(self, n_axis: int = 3, min_interval: float = 5) -> np.ndarray:
    """
    Generates a random rotation matrix.

    Args:
        n_axis (int): Number of axes to choose from. Default is 3.
        min_interval (float): Minimum interval for the angle. Default is 5.

    Returns:
        np.ndarray: Randomly generated rotation matrix.

    """
    axis = self.axis(n_axis=n_axis)
    angle = self.angle(min_interval=min_interval)
    return self.rotation(axis, angle)

Generator

A class that provides various random number generation methods.

Source code in hyper_surrogate/generator.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class Generator:
    """A class that provides various random number generation methods."""

    def __init__(self, seed: int | None = None, size: int | None = None) -> None:
        """
        Initialize the Generator object.

        Args:
            seed (int | None): The seed value for random number generation. If None, a random seed will be used.
            size (int | None): The size of the generated random numbers. If None, a single random number will be generated.

        Returns:
            None
        """
        self.seed = seed
        self.size = size
        np.random.seed(self.seed)

    def uniform(self, low: float, high: float) -> np.ndarray:
        """
        Generate random numbers from a uniform distribution.

        Args:
            low (float): The lower bound of the distribution.
            high (float): The upper bound of the distribution.

        Returns:
            np.ndarray: An array of random numbers from the uniform distribution.
        """
        return np.random.uniform(low, high, size=self.size)

    def integer_in_interval(self, low: int = 0, high: int = 3) -> np.ndarray[Any, Any]:
        """
        Generate random integers in the specified interval.

        Args:
            low (int): The lower bound of the interval (inclusive).
            high (int): The upper bound of the interval (exclusive).

        Returns:
            np.ndarray: An array of random integers in the specified interval.
        """
        return np.random.randint(low, high, size=self.size)

    def float_in_interval(self, a: float = 0, b: float = 180, interval: float = 5) -> np.ndarray[Any, Any]:
        """
        Generate random numbers in the specified interval with a given interval.

        Args:
            a (float): The lower bound of the interval.
            b (float): The upper bound of the interval.
            interval (float): The interval between the generated numbers.

        Returns:
            np.ndarray: An array of random numbers in the specified interval.
        """
        if interval <= 0 or interval >= 180 or interval == 0:
            return np.array([0])
        return np.random.choice(np.arange(a, b + interval, interval), size=self.size)

    def normal(self, loc: float, scale: float) -> np.ndarray:
        """
        Generate random numbers from a normal distribution.

        Args:
            loc (float): The mean of the distribution.
            scale (float): The standard deviation of the distribution.

        Returns:
            np.ndarray: An array of random numbers from the normal distribution.
        """
        return np.random.normal(loc, scale, size=self.size)

    def lognormal(self, mean: float, sigma: float) -> np.ndarray:
        """
        Generate random numbers from a log-normal distribution.

        Args:
            mean (float): The mean of the underlying normal distribution.
            sigma (float): The standard deviation of the underlying normal distribution.

        Returns:
            np.ndarray: An array of random numbers from the log-normal distribution.
        """
        return np.random.lognormal(mean, sigma, size=self.size)

    def beta(self, a: float, b: float) -> np.ndarray:
        """
        Generate random numbers from a beta distribution.

        Args:
            a (float): The shape parameter (alpha) of the distribution.
            b (float): The shape parameter (beta) of the distribution.

        Returns:
            np.ndarray: An array of random numbers from the beta distribution.
        """
        return np.random.beta(a, b, size=self.size)

    def gamma(self, shape: float, scale: float) -> np.ndarray:
        """
        Generate random numbers from a gamma distribution.

        Args:
            shape (float): The shape parameter (k) of the distribution.
            scale (float): The scale parameter (theta) of the distribution.

        Returns:
            np.ndarray: An array of random numbers from the gamma distribution.
        """
        return np.random.gamma(shape, scale, size=self.size)

    def weibull(self, a: float) -> np.ndarray:
        """
        Generate random numbers from a Weibull distribution.

        Args:
            a (float): The shape parameter (k) of the distribution.

        Returns:
            np.ndarray: An array of random numbers from the Weibull distribution.
        """
        return np.random.weibull(a, size=self.size)

__init__(seed=None, size=None)

Initialize the Generator object.

Parameters:

Name Type Description Default
seed int | None

The seed value for random number generation. If None, a random seed will be used.

None
size int | None

The size of the generated random numbers. If None, a single random number will be generated.

None

Returns:

Type Description
None

None

Source code in hyper_surrogate/generator.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def __init__(self, seed: int | None = None, size: int | None = None) -> None:
    """
    Initialize the Generator object.

    Args:
        seed (int | None): The seed value for random number generation. If None, a random seed will be used.
        size (int | None): The size of the generated random numbers. If None, a single random number will be generated.

    Returns:
        None
    """
    self.seed = seed
    self.size = size
    np.random.seed(self.seed)

beta(a, b)

Generate random numbers from a beta distribution.

Parameters:

Name Type Description Default
a float

The shape parameter (alpha) of the distribution.

required
b float

The shape parameter (beta) of the distribution.

required

Returns:

Type Description
ndarray

np.ndarray: An array of random numbers from the beta distribution.

Source code in hyper_surrogate/generator.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def beta(self, a: float, b: float) -> np.ndarray:
    """
    Generate random numbers from a beta distribution.

    Args:
        a (float): The shape parameter (alpha) of the distribution.
        b (float): The shape parameter (beta) of the distribution.

    Returns:
        np.ndarray: An array of random numbers from the beta distribution.
    """
    return np.random.beta(a, b, size=self.size)

float_in_interval(a=0, b=180, interval=5)

Generate random numbers in the specified interval with a given interval.

Parameters:

Name Type Description Default
a float

The lower bound of the interval.

0
b float

The upper bound of the interval.

180
interval float

The interval between the generated numbers.

5

Returns:

Type Description
ndarray[Any, Any]

np.ndarray: An array of random numbers in the specified interval.

Source code in hyper_surrogate/generator.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def float_in_interval(self, a: float = 0, b: float = 180, interval: float = 5) -> np.ndarray[Any, Any]:
    """
    Generate random numbers in the specified interval with a given interval.

    Args:
        a (float): The lower bound of the interval.
        b (float): The upper bound of the interval.
        interval (float): The interval between the generated numbers.

    Returns:
        np.ndarray: An array of random numbers in the specified interval.
    """
    if interval <= 0 or interval >= 180 or interval == 0:
        return np.array([0])
    return np.random.choice(np.arange(a, b + interval, interval), size=self.size)

gamma(shape, scale)

Generate random numbers from a gamma distribution.

Parameters:

Name Type Description Default
shape float

The shape parameter (k) of the distribution.

required
scale float

The scale parameter (theta) of the distribution.

required

Returns:

Type Description
ndarray

np.ndarray: An array of random numbers from the gamma distribution.

Source code in hyper_surrogate/generator.py
105
106
107
108
109
110
111
112
113
114
115
116
def gamma(self, shape: float, scale: float) -> np.ndarray:
    """
    Generate random numbers from a gamma distribution.

    Args:
        shape (float): The shape parameter (k) of the distribution.
        scale (float): The scale parameter (theta) of the distribution.

    Returns:
        np.ndarray: An array of random numbers from the gamma distribution.
    """
    return np.random.gamma(shape, scale, size=self.size)

integer_in_interval(low=0, high=3)

Generate random integers in the specified interval.

Parameters:

Name Type Description Default
low int

The lower bound of the interval (inclusive).

0
high int

The upper bound of the interval (exclusive).

3

Returns:

Type Description
ndarray[Any, Any]

np.ndarray: An array of random integers in the specified interval.

Source code in hyper_surrogate/generator.py
37
38
39
40
41
42
43
44
45
46
47
48
def integer_in_interval(self, low: int = 0, high: int = 3) -> np.ndarray[Any, Any]:
    """
    Generate random integers in the specified interval.

    Args:
        low (int): The lower bound of the interval (inclusive).
        high (int): The upper bound of the interval (exclusive).

    Returns:
        np.ndarray: An array of random integers in the specified interval.
    """
    return np.random.randint(low, high, size=self.size)

lognormal(mean, sigma)

Generate random numbers from a log-normal distribution.

Parameters:

Name Type Description Default
mean float

The mean of the underlying normal distribution.

required
sigma float

The standard deviation of the underlying normal distribution.

required

Returns:

Type Description
ndarray

np.ndarray: An array of random numbers from the log-normal distribution.

Source code in hyper_surrogate/generator.py
79
80
81
82
83
84
85
86
87
88
89
90
def lognormal(self, mean: float, sigma: float) -> np.ndarray:
    """
    Generate random numbers from a log-normal distribution.

    Args:
        mean (float): The mean of the underlying normal distribution.
        sigma (float): The standard deviation of the underlying normal distribution.

    Returns:
        np.ndarray: An array of random numbers from the log-normal distribution.
    """
    return np.random.lognormal(mean, sigma, size=self.size)

normal(loc, scale)

Generate random numbers from a normal distribution.

Parameters:

Name Type Description Default
loc float

The mean of the distribution.

required
scale float

The standard deviation of the distribution.

required

Returns:

Type Description
ndarray

np.ndarray: An array of random numbers from the normal distribution.

Source code in hyper_surrogate/generator.py
66
67
68
69
70
71
72
73
74
75
76
77
def normal(self, loc: float, scale: float) -> np.ndarray:
    """
    Generate random numbers from a normal distribution.

    Args:
        loc (float): The mean of the distribution.
        scale (float): The standard deviation of the distribution.

    Returns:
        np.ndarray: An array of random numbers from the normal distribution.
    """
    return np.random.normal(loc, scale, size=self.size)

uniform(low, high)

Generate random numbers from a uniform distribution.

Parameters:

Name Type Description Default
low float

The lower bound of the distribution.

required
high float

The upper bound of the distribution.

required

Returns:

Type Description
ndarray

np.ndarray: An array of random numbers from the uniform distribution.

Source code in hyper_surrogate/generator.py
24
25
26
27
28
29
30
31
32
33
34
35
def uniform(self, low: float, high: float) -> np.ndarray:
    """
    Generate random numbers from a uniform distribution.

    Args:
        low (float): The lower bound of the distribution.
        high (float): The upper bound of the distribution.

    Returns:
        np.ndarray: An array of random numbers from the uniform distribution.
    """
    return np.random.uniform(low, high, size=self.size)

weibull(a)

Generate random numbers from a Weibull distribution.

Parameters:

Name Type Description Default
a float

The shape parameter (k) of the distribution.

required

Returns:

Type Description
ndarray

np.ndarray: An array of random numbers from the Weibull distribution.

Source code in hyper_surrogate/generator.py
118
119
120
121
122
123
124
125
126
127
128
def weibull(self, a: float) -> np.ndarray:
    """
    Generate random numbers from a Weibull distribution.

    Args:
        a (float): The shape parameter (k) of the distribution.

    Returns:
        np.ndarray: An array of random numbers from the Weibull distribution.
    """
    return np.random.weibull(a, size=self.size)

Kinematics

A class that provides various kinematic methods.

Source code in hyper_surrogate/kinematics.py
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
class Kinematics:
    """A class that provides various kinematic methods."""

    def __init__(self) -> None:
        """
        Initialize the Kinematics object.

        Returns:
            None
        """
        pass

    def jacobian(self, f: np.ndarray) -> Any:
        """
        Compute the Jacobian of the deformation gradient.

        Args:
            f: 4D tensor of shape (N, 3, 3, 3).

        Returns:
            np.ndarray: The Jacobian of the deformation gradient.
        """
        return np.linalg.det(f)

    @staticmethod
    def invariant1(F: np.ndarray) -> Any:
        """
        Calculate the first invariant of each tensor in the batch.

        Args:
            F: 4D tensor of shape (N, 3, 3, 3).

        Returns:
            The first invariant of each tensor in the batch.
        """
        # einsum
        return np.einsum("nii->n", F)

    @staticmethod
    def invariant2(F: np.ndarray) -> Any:
        """
        Calculate the second invariant of the deformation gradient tensor.

        Args:
            F: 4D tensor of shape (N, 3, 3, 3).

        Returns:
            The second invariant.
        """
        # use einsum to calculate the second invariant: 0.5 * (np.trace(F) ** 2 - np.trace(np.matmul(F, F)))
        return 0.5 * (np.einsum("nii->n", F) ** 2 - np.einsum("nij,nji->n", F, F))

    @staticmethod
    def invariant3(F: np.ndarray) -> Any:
        """
        Calculate the third invariant of the deformation gradient tensor.

        Args:
            F: The deformation gradient tensor as a 3D array.

        Returns:
            The third invariant.
        """
        return np.linalg.det(F)

    @staticmethod
    def right_cauchy_green(f: np.ndarray) -> Any:
        """
        Compute the right Cauchy-Green deformation tensor for a batch of deformation gradients
        using a more efficient vectorized approach.
        $$C = F^T F$$

        Args:
            f (np.ndarray): The deformation gradient tensor with shape (N, 3, 3),
                            where N is the number of deformation gradients.

        Returns:
            np.ndarray: The batch of right Cauchy-Green deformation tensors, shape (N, 3, 3).
        """
        # Use np.einsum to perform batch matrix multiplication: f's transpose @ f
        # The einsum subscript 'nij,nkj->nik' denotes batched matrix multiplication
        # where 'n' iterates over each matrix in the batch,
        # 'ji' are the indices of the transposed matrix,
        # and 'jk' are the indices for the second matrix.
        # Note: The difference from the left Cauchy-Green tensor is in the order of multiplication.
        return np.einsum("nji,njk->nik", f, f)

    @staticmethod
    def left_cauchy_green(f: np.ndarray) -> Any:
        """
        Compute the left Cauchy-Green deformation tensor for a batch of deformation gradients
        using a more efficient vectorized approach.

        Args:
            f (np.ndarray): The deformation gradient tensor with shape (N, 3, 3),
                            where N is the number of deformation gradients.

        Returns:
            np.ndarray: The batch of left Cauchy-Green deformation tensors, shape (N, 3, 3).
        """
        # Use np.einsum to perform batch matrix multiplication: f @ f's transpose
        # The einsum subscript 'nij,njk->nik' denotes batched matrix multiplication
        # where 'n' iterates over each matrix in the batch,
        # 'ij' are the indices of the first matrix,
        # and 'jk' are the indices for the second matrix (transposed to 'kj' for multiplication).
        return np.einsum("nij,nkj->nik", f, f)

    @staticmethod
    def rotation_tensor(f: np.ndarray) -> Any:
        """
        Compute the rotation tensors.

        Args:
            f (np.ndarray): The deformation gradients. batched with shape (N, 3, 3).

        Returns:
            np.ndarray: The rotation tensors. batched with shape (N, 3, 3).
        """
        return np.einsum("nij,njk->nik", f, np.linalg.inv(f))

    @staticmethod
    def pushforward(f: np.ndarray, tensor2D: np.ndarray) -> Any:
        """
        Forward tensor configuration.
        F*tensor2D*F^T. This is the forward transformation of a 2D tensor.

        Args:
            f (np.ndarray): deformation gradient # (N, 3, 3)
            tensor2D (np.ndarray): The 2D tensor to be mapped # (N, 3, 3)

        Returns:
            np.ndarray: The transformed tensor.
        """
        return np.einsum("nik,njl,nkl->nij", f, f, tensor2D)

    def principal_stretches(self, f: np.ndarray) -> np.ndarray:
        """
        Compute the principal stretches.

        Args:
            f (np.ndarray): The deformation gradient.

        Returns:
            np.ndarray: The principal stretches.
        """
        return np.sqrt(np.linalg.eigvals(self.right_cauchy_green(f)))

    def principal_directions(self, f: np.ndarray) -> np.ndarray:
        """
        Compute the principal directions.

        Args:
            f (np.ndarray): The deformation gradient.

        Returns:
            np.ndarray: The principal directions.
        """
        return np.linalg.eig(self.right_cauchy_green(f))[1]

__init__()

Initialize the Kinematics object.

Returns:

Type Description
None

None

Source code in hyper_surrogate/kinematics.py
 9
10
11
12
13
14
15
16
def __init__(self) -> None:
    """
    Initialize the Kinematics object.

    Returns:
        None
    """
    pass

invariant1(F) staticmethod

Calculate the first invariant of each tensor in the batch.

Parameters:

Name Type Description Default
F ndarray

4D tensor of shape (N, 3, 3, 3).

required

Returns:

Type Description
Any

The first invariant of each tensor in the batch.

Source code in hyper_surrogate/kinematics.py
30
31
32
33
34
35
36
37
38
39
40
41
42
@staticmethod
def invariant1(F: np.ndarray) -> Any:
    """
    Calculate the first invariant of each tensor in the batch.

    Args:
        F: 4D tensor of shape (N, 3, 3, 3).

    Returns:
        The first invariant of each tensor in the batch.
    """
    # einsum
    return np.einsum("nii->n", F)

invariant2(F) staticmethod

Calculate the second invariant of the deformation gradient tensor.

Parameters:

Name Type Description Default
F ndarray

4D tensor of shape (N, 3, 3, 3).

required

Returns:

Type Description
Any

The second invariant.

Source code in hyper_surrogate/kinematics.py
44
45
46
47
48
49
50
51
52
53
54
55
56
@staticmethod
def invariant2(F: np.ndarray) -> Any:
    """
    Calculate the second invariant of the deformation gradient tensor.

    Args:
        F: 4D tensor of shape (N, 3, 3, 3).

    Returns:
        The second invariant.
    """
    # use einsum to calculate the second invariant: 0.5 * (np.trace(F) ** 2 - np.trace(np.matmul(F, F)))
    return 0.5 * (np.einsum("nii->n", F) ** 2 - np.einsum("nij,nji->n", F, F))

invariant3(F) staticmethod

Calculate the third invariant of the deformation gradient tensor.

Parameters:

Name Type Description Default
F ndarray

The deformation gradient tensor as a 3D array.

required

Returns:

Type Description
Any

The third invariant.

Source code in hyper_surrogate/kinematics.py
58
59
60
61
62
63
64
65
66
67
68
69
@staticmethod
def invariant3(F: np.ndarray) -> Any:
    """
    Calculate the third invariant of the deformation gradient tensor.

    Args:
        F: The deformation gradient tensor as a 3D array.

    Returns:
        The third invariant.
    """
    return np.linalg.det(F)

jacobian(f)

Compute the Jacobian of the deformation gradient.

Parameters:

Name Type Description Default
f ndarray

4D tensor of shape (N, 3, 3, 3).

required

Returns:

Type Description
Any

np.ndarray: The Jacobian of the deformation gradient.

Source code in hyper_surrogate/kinematics.py
18
19
20
21
22
23
24
25
26
27
28
def jacobian(self, f: np.ndarray) -> Any:
    """
    Compute the Jacobian of the deformation gradient.

    Args:
        f: 4D tensor of shape (N, 3, 3, 3).

    Returns:
        np.ndarray: The Jacobian of the deformation gradient.
    """
    return np.linalg.det(f)

left_cauchy_green(f) staticmethod

Compute the left Cauchy-Green deformation tensor for a batch of deformation gradients using a more efficient vectorized approach.

Parameters:

Name Type Description Default
f ndarray

The deformation gradient tensor with shape (N, 3, 3), where N is the number of deformation gradients.

required

Returns:

Type Description
Any

np.ndarray: The batch of left Cauchy-Green deformation tensors, shape (N, 3, 3).

Source code in hyper_surrogate/kinematics.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@staticmethod
def left_cauchy_green(f: np.ndarray) -> Any:
    """
    Compute the left Cauchy-Green deformation tensor for a batch of deformation gradients
    using a more efficient vectorized approach.

    Args:
        f (np.ndarray): The deformation gradient tensor with shape (N, 3, 3),
                        where N is the number of deformation gradients.

    Returns:
        np.ndarray: The batch of left Cauchy-Green deformation tensors, shape (N, 3, 3).
    """
    # Use np.einsum to perform batch matrix multiplication: f @ f's transpose
    # The einsum subscript 'nij,njk->nik' denotes batched matrix multiplication
    # where 'n' iterates over each matrix in the batch,
    # 'ij' are the indices of the first matrix,
    # and 'jk' are the indices for the second matrix (transposed to 'kj' for multiplication).
    return np.einsum("nij,nkj->nik", f, f)

principal_directions(f)

Compute the principal directions.

Parameters:

Name Type Description Default
f ndarray

The deformation gradient.

required

Returns:

Type Description
ndarray

np.ndarray: The principal directions.

Source code in hyper_surrogate/kinematics.py
153
154
155
156
157
158
159
160
161
162
163
def principal_directions(self, f: np.ndarray) -> np.ndarray:
    """
    Compute the principal directions.

    Args:
        f (np.ndarray): The deformation gradient.

    Returns:
        np.ndarray: The principal directions.
    """
    return np.linalg.eig(self.right_cauchy_green(f))[1]

principal_stretches(f)

Compute the principal stretches.

Parameters:

Name Type Description Default
f ndarray

The deformation gradient.

required

Returns:

Type Description
ndarray

np.ndarray: The principal stretches.

Source code in hyper_surrogate/kinematics.py
141
142
143
144
145
146
147
148
149
150
151
def principal_stretches(self, f: np.ndarray) -> np.ndarray:
    """
    Compute the principal stretches.

    Args:
        f (np.ndarray): The deformation gradient.

    Returns:
        np.ndarray: The principal stretches.
    """
    return np.sqrt(np.linalg.eigvals(self.right_cauchy_green(f)))

pushforward(f, tensor2D) staticmethod

Forward tensor configuration. Ftensor2DF^T. This is the forward transformation of a 2D tensor.

Parameters:

Name Type Description Default
f ndarray

deformation gradient # (N, 3, 3)

required
tensor2D ndarray

The 2D tensor to be mapped # (N, 3, 3)

required

Returns:

Type Description
Any

np.ndarray: The transformed tensor.

Source code in hyper_surrogate/kinematics.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
@staticmethod
def pushforward(f: np.ndarray, tensor2D: np.ndarray) -> Any:
    """
    Forward tensor configuration.
    F*tensor2D*F^T. This is the forward transformation of a 2D tensor.

    Args:
        f (np.ndarray): deformation gradient # (N, 3, 3)
        tensor2D (np.ndarray): The 2D tensor to be mapped # (N, 3, 3)

    Returns:
        np.ndarray: The transformed tensor.
    """
    return np.einsum("nik,njl,nkl->nij", f, f, tensor2D)

right_cauchy_green(f) staticmethod

Compute the right Cauchy-Green deformation tensor for a batch of deformation gradients using a more efficient vectorized approach. \(\(C = F^T F\)\)

Parameters:

Name Type Description Default
f ndarray

The deformation gradient tensor with shape (N, 3, 3), where N is the number of deformation gradients.

required

Returns:

Type Description
Any

np.ndarray: The batch of right Cauchy-Green deformation tensors, shape (N, 3, 3).

Source code in hyper_surrogate/kinematics.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@staticmethod
def right_cauchy_green(f: np.ndarray) -> Any:
    """
    Compute the right Cauchy-Green deformation tensor for a batch of deformation gradients
    using a more efficient vectorized approach.
    $$C = F^T F$$

    Args:
        f (np.ndarray): The deformation gradient tensor with shape (N, 3, 3),
                        where N is the number of deformation gradients.

    Returns:
        np.ndarray: The batch of right Cauchy-Green deformation tensors, shape (N, 3, 3).
    """
    # Use np.einsum to perform batch matrix multiplication: f's transpose @ f
    # The einsum subscript 'nij,nkj->nik' denotes batched matrix multiplication
    # where 'n' iterates over each matrix in the batch,
    # 'ji' are the indices of the transposed matrix,
    # and 'jk' are the indices for the second matrix.
    # Note: The difference from the left Cauchy-Green tensor is in the order of multiplication.
    return np.einsum("nji,njk->nik", f, f)

rotation_tensor(f) staticmethod

Compute the rotation tensors.

Parameters:

Name Type Description Default
f ndarray

The deformation gradients. batched with shape (N, 3, 3).

required

Returns:

Type Description
Any

np.ndarray: The rotation tensors. batched with shape (N, 3, 3).

Source code in hyper_surrogate/kinematics.py
113
114
115
116
117
118
119
120
121
122
123
124
@staticmethod
def rotation_tensor(f: np.ndarray) -> Any:
    """
    Compute the rotation tensors.

    Args:
        f (np.ndarray): The deformation gradients. batched with shape (N, 3, 3).

    Returns:
        np.ndarray: The rotation tensors. batched with shape (N, 3, 3).
    """
    return np.einsum("nij,njk->nik", f, np.linalg.inv(f))

Material

Bases: SymbolicHandler

Material class for defining the constitutive model of the material. The class is inherited from the SymbolicHandler class and provides the necessary methods to define the constitutive model in symbolic form.

args: parameters: Iterable[Any] - The material parameters as a list of strings

properties: sef: The strain energy function in symbolic form

methods: pk2() -> Any: Returns the second Piola-Kirchhoff stress tensor cmat() -> Any: Returns the material stiffness tensor

Source code in hyper_surrogate/materials.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class Material(SymbolicHandler):
    """
    Material class for defining the constitutive model of the material.
    The class is inherited from the SymbolicHandler class and provides
    the necessary methods to define the constitutive model in symbolic form.

    args:
    parameters: Iterable[Any] - The material parameters as a list of strings

    properties:
    sef: The strain energy function in symbolic form

    methods:
    pk2() -> Any: Returns the second Piola-Kirchhoff stress tensor
    cmat() -> Any: Returns the material stiffness tensor
    """

    def __init__(self, parameters: Iterable[Any]) -> None:
        super().__init__()
        self.parameters = parameters

    @property
    def sef(self) -> Any:
        """Strain energy function in symbolic form."""
        # Dummy placeholder
        return sym.Symbol("sef")

    @property
    def pk2_symb(self) -> Any:
        """Second Piola-Kirchhoff stress tensor in symbolic form."""
        return self.pk2_tensor(self.sef)

    @property
    def cmat_symb(self) -> Any:
        """Material stiffness tensor in symbolic form."""
        return self.cmat_tensor(self.pk2_symb)

    def sigma_symb(self, f: sym.Matrix) -> Any:
        """Cauchy stress tensor in symbolic form."""
        return self.pushforward_2nd_order(self.pk2_symb, f)

    def smat_symb(self, f: sym.Matrix) -> Any:
        """Material stiffness tensor in spatial form."""
        return self.pushforward_4th_order(self.cmat_symb, f)

    def cauchy(self, f: sym.Matrix) -> Any:
        return self.reduce_2nd_order(self.sigma_symb(f))

    def tangent(self, f: sym.Matrix) -> Any:
        # TODO:        # implement jaumman_rate_mat
        return self.reduce_4th_order(self.smat_symb(f))

    def pk2(self) -> Any:
        """Second Piola-Kirchhoff stress tensor generator of numerical form."""
        return self.lambdify(self.pk2_symb, *self.parameters)

    def cmat(self) -> Any:
        """Material stiffness tensor generator of numerical form."""
        return self.lambdify(self.cmat_symb, *self.parameters)

    def sigma(self, f: sym.Matrix) -> Any:
        """Cauchy stress tensor generator of numerical form."""
        return self.lambdify(self.sigma_symb(f), *self.parameters)

    def smat(self, f: sym.Matrix) -> Any:
        """Material stiffness tensor generator of numerical form."""
        return self.lambdify(self.smat_symb(f), *self.parameters)

cmat_symb: Any property

Material stiffness tensor in symbolic form.

pk2_symb: Any property

Second Piola-Kirchhoff stress tensor in symbolic form.

sef: Any property

Strain energy function in symbolic form.

cmat()

Material stiffness tensor generator of numerical form.

Source code in hyper_surrogate/materials.py
64
65
66
def cmat(self) -> Any:
    """Material stiffness tensor generator of numerical form."""
    return self.lambdify(self.cmat_symb, *self.parameters)

pk2()

Second Piola-Kirchhoff stress tensor generator of numerical form.

Source code in hyper_surrogate/materials.py
60
61
62
def pk2(self) -> Any:
    """Second Piola-Kirchhoff stress tensor generator of numerical form."""
    return self.lambdify(self.pk2_symb, *self.parameters)

sigma(f)

Cauchy stress tensor generator of numerical form.

Source code in hyper_surrogate/materials.py
68
69
70
def sigma(self, f: sym.Matrix) -> Any:
    """Cauchy stress tensor generator of numerical form."""
    return self.lambdify(self.sigma_symb(f), *self.parameters)

sigma_symb(f)

Cauchy stress tensor in symbolic form.

Source code in hyper_surrogate/materials.py
45
46
47
def sigma_symb(self, f: sym.Matrix) -> Any:
    """Cauchy stress tensor in symbolic form."""
    return self.pushforward_2nd_order(self.pk2_symb, f)

smat(f)

Material stiffness tensor generator of numerical form.

Source code in hyper_surrogate/materials.py
72
73
74
def smat(self, f: sym.Matrix) -> Any:
    """Material stiffness tensor generator of numerical form."""
    return self.lambdify(self.smat_symb(f), *self.parameters)

smat_symb(f)

Material stiffness tensor in spatial form.

Source code in hyper_surrogate/materials.py
49
50
51
def smat_symb(self, f: sym.Matrix) -> Any:
    """Material stiffness tensor in spatial form."""
    return self.pushforward_4th_order(self.cmat_symb, f)

MooneyRivlin

Bases: Material

Mooney-Rivlin material model for hyperelastic materials. The class inherits from the Material class and provides the necessary methods to define the Mooney-Rivlin model in symbolic form.

properties: sef: The strain energy function in symbolic form

Source code in hyper_surrogate/materials.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
class MooneyRivlin(Material):
    """
    Mooney-Rivlin material model for hyperelastic materials.
    The class inherits from the Material class and provides the necessary
    methods to define the Mooney-Rivlin model in symbolic form.

    properties:
    sef: The strain energy function in symbolic form
    """

    def __init__(self) -> None:
        params = ["C10", "C01"]
        super().__init__(params)

    @property
    def sef(self) -> Any:
        return (self.invariant1 - 3) * sym.Symbol("C10") + (self.invariant2 - 3) * sym.Symbol("C01")

NeoHooke

Bases: Material

Neo-Hookean material model for hyperelastic materials. The class inherits from the Material class and provides the necessary methods to define the Neo-Hookean model in symbolic form.

properties: sef: The strain energy function in symbolic form

Source code in hyper_surrogate/materials.py
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
class NeoHooke(Material):
    """
    Neo-Hookean material model for hyperelastic materials.
    The class inherits from the Material class and provides the necessary
    methods to define the Neo-Hookean model in symbolic form.

    properties:
    sef: The strain energy function in symbolic form
    """

    def __init__(self) -> None:
        params = ["C10"]
        super().__init__(params)

    @property
    def sef(self) -> Any:
        return (self.invariant1 - 3) * sym.Symbol("C10")

SymbolicHandler

A class that handles symbolic computations using SymPy.

Attributes:

Name Type Description
c_tensor Matrix

A 3x3 matrix of symbols.

Source code in hyper_surrogate/symbolic.py
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
class SymbolicHandler:
    """
    A class that handles symbolic computations using SymPy.

    Attributes:
        c_tensor (sym.Matrix): A 3x3 matrix of symbols.
    """

    def __init__(self) -> None:
        self.c_tensor = self._c_tensor()

    def c_symbols(self) -> Any:
        """
        Return the c_tensor flattened symbols.

        Returns:
            list: A list of c_tensor symbols.
        """
        return [self.c_tensor[i, j] for i in range(3) for j in range(3)]

    def _c_tensor(self) -> sym.ImmutableMatrix:
        """
        Create a 3x3 matrix of symbols.

        Returns:
            sym.Matrix: A 3x3 matrix of symbols.
        """
        return sym.ImmutableMatrix(3, 3, lambda i, j: sym.Symbol(f"C_{i + 1}{j + 1}"))

    # multuply c_tensor by itself
    def _c_tensor_squared(self) -> Any:
        """
        Compute the square of the c_tensor.

        Returns:
            sym.Matrix: The square of the c_tensor.
        """
        # matrix product
        return self.c_tensor.multiply(self.c_tensor)

    @property
    def invariant1(self) -> Any:
        """
        Compute the first invariant of the c_tensor.

        Returns:
            sym.Expr: The first invariant of the c_tensor.
        """
        I3 = self.invariant3  # Determinant
        trace = self.c_tensor.trace()  # Trace
        return trace * (I3 ** (-sym.Rational(1, 3)))

    @property
    def invariant2(self) -> Any:
        """
        Compute the second invariant of the c_tensor.

        Returns:
            sym.Expr: The second invariant of the c_tensor.
        """
        c_squared = self._c_tensor_squared()
        return sym.Rational(1, 2) * (self.c_tensor.multiply(self.c_tensor).trace() - c_squared.trace())

    @property
    def invariant3(self) -> Any:
        """
        Compute the third invariant of the c_tensor.

        Returns:
            sym.Expr: The third invariant of the c_tensor.
        """
        return self.c_tensor.det()

    def pk2_tensor(self, sef: sym.Expr) -> sym.Matrix:
        """
        Compute the pk2 tensor.

        Args:
            sef (sym.Expr): The strain energy function.

        Returns:
            sym.Matrix: The pk2 tensor.
        """
        return sym.Matrix([[sym.diff(sef, self.c_tensor[i, j]) for j in range(3)] for i in range(3)])

    def cmat_tensor(self, pk2: sym.Matrix) -> sym.ImmutableDenseNDimArray:
        """
        Compute the cmat tensor.

        Args:
            pk2 (sym.Matrix): The pk2 tensor.

        Returns:
            sym.MutableDenseNDimArray: The cmat tensor.
        """
        return sym.ImmutableDenseNDimArray([
            [[[sym.diff(2 * pk2[i, j], self.c_tensor[k, ll]) for ll in range(3)] for k in range(3)] for j in range(3)]
            for i in range(3)
        ])

    def substitute(
        self,
        symbolic_tensor: sym.MutableDenseMatrix,
        numerical_c_tensor: np.ndarray,
        *args: dict,
    ) -> Any:
        """
        Automatically substitute numerical values from a given 3x3 numerical matrix into c_tensor.

        Args:
            symbolic_tensor (sym.Matrix): A symbolic tensor to substitute numerical values into.
            numerical_c_tensor (np.ndarray): A 3x3 numerical matrix to substitute into c_tensor.
            args (dict): Additional substitution dictionaries.

        Returns:
            sym.Matrix: The symbolic_tensor with numerical values substituted.

        Raises:
            ValueError: If numerical_tensor is not a 3x3 matrix.
        """
        if not isinstance(numerical_c_tensor, np.ndarray) or numerical_c_tensor.shape != (3, 3):
            raise ValueError("c_tensor.shape")

        # Start with substitutions for c_tensor elements
        substitutions = {self.c_tensor[i, j]: numerical_c_tensor[i, j] for i in range(3) for j in range(3)}
        # Merge additional substitution dictionaries from *args
        substitutions.update(*args)
        return symbolic_tensor.subs(substitutions)

    def substitute_iterator(
        self,
        symbolic_tensor: sym.MutableDenseMatrix,
        numerical_c_tensors: np.ndarray,
        *args: dict,
    ) -> Any:
        """
        Automatically substitute numerical values from a given 3x3 numerical matrix into c_tensor.

        Args:
            symbolic_tensor (sym.Matrix): A symbolic tensor to substitute numerical values into.
            numerical_c_tensors (np.ndarray): N 3x3 numerical matrices to substitute into c_tensor.
            args (dict): Additional substitution dictionaries.

        Returns:
            sym.Matrix: The symbolic_tensor with numerical values substituted.

        Raises:
            ValueError: If numerical_tensor is not a 3x3 matrix.
        """
        for numerical_c_tensor in numerical_c_tensors:
            yield self.substitute(symbolic_tensor, numerical_c_tensor, *args)

    def lambdify(self, symbolic_tensor: sym.Matrix, *args: Iterable[Any]) -> Any:
        """
        Create a lambdified function from a symbolic tensor that can be used for numerical evaluation.

        Args:
            symbolic_tensor (sym.Expr or sym.Matrix): The symbolic tensor to be lambdified.
            args (dict): Additional substitution lists of symbols.
        Returns:
            function: A function that can be used to numerically evaluate the tensor with specific values.
        """

        return sym.lambdify((self.c_symbols(), *args), symbolic_tensor, modules="numpy")

    def _evaluate(self, lambdified_tensor: Any, *args: Any) -> Any:
        """
        Evaluate a lambdified tensor with specific values.

        Args:
            lambdified_tensor (function): A lambdified tensor function.
            args (dict): Additional substitution lists of symbols.

        Returns:
            Any: The evaluated tensor.
        """
        return lambdified_tensor(*args)

    def evaluate_iterator(self, lambdified_tensor: Any, numerical_c_tensors: np.ndarray, *args: Any) -> Any:
        """
        Evaluate a lambdified tensor with specific values.

        Args:
            lambdified_tensor (function): A lambdified tensor function.
            args (dict): Additional substitution lists of symbols.

        Returns:
            Any: The evaluated tensor.
        """
        for numerical_c_tensor in numerical_c_tensors:
            yield self._evaluate(lambdified_tensor, numerical_c_tensor.flatten(), *args)

    @staticmethod
    def reduce_2nd_order(tensor: sym.Matrix) -> Any:
        """
        Convert a 3x3 matrix to 6x1 matrix using Voigt notation

        Args:
            tensor (sp.Matrix): A 3x3 symmetric matrix.

        Returns:
            sp.Matrix: A 6x1 matrix.
        """
        # Validate the input tensor dimensions
        if tensor.shape != (3, 3):
            raise ValueError("Wrong.shape.")
        # Voigt notation conversion: xx, yy, zz, xy, xz, yz
        return sym.Matrix([
            tensor[0, 0],  # xx
            tensor[1, 1],  # yy
            tensor[2, 2],  # zz
            tensor[0, 1],  # xy
            tensor[0, 2],  # xz
            tensor[1, 2],  # yz
        ])

    @staticmethod
    def reduce_4th_order(tensor: sym.MutableDenseNDimArray) -> Any:
        """
        Convert a 3x3x3x3 matrix to 6x6 matrix using Voigt notation

        Args:
            tensor (sym.MutableDenseNDimArray): A 3x3x3x3 matrix.

        Returns:
            sym.Matrix: A 6x6 matrix.
        """
        # Validate the input tensor dimensions
        if tensor.shape != (3, 3, 3, 3):
            raise ValueError("Wrong.shape.")

        # Voigt notation index mapping
        ii1 = [0, 1, 2, 0, 0, 1]
        ii2 = [0, 1, 2, 1, 2, 2]

        # Initialize a 6x6 matrix
        voigt_matrix = sym.Matrix.zeros(6, 6)

        # Fill the Voigt matrix by averaging symmetric components of the 4th-order tensor
        for i in range(6):
            for j in range(6):
                # Access the relevant tensor components using ii1 and ii2 mappings
                pp1 = tensor[ii1[i], ii2[i], ii1[j], ii2[j]]
                pp2 = tensor[ii1[i], ii2[i], ii2[j], ii1[j]]
                # Average the two permutations to ensure symmetry
                voigt_matrix[i, j] = 0.5 * (pp1 + pp2)
        return voigt_matrix

    @staticmethod
    def pushforward_2nd_order(tensor2: sym.Matrix, f: sym.Matrix) -> Any:
        """
        Push forward a 2nd order tensor in material configuration.

        args:
        tensor2: Any - The 2nd order tensor
        f: Any - The deformation gradient tensor

        returns:
        Any - The pushforwarded 2nd order tensor
        """
        return sym.simplify(f * tensor2 * f.T)

    @staticmethod
    def pushforward_4th_order(tensor4: sym.MutableDenseNDimArray, f: sym.Matrix) -> sym.MutableDenseNDimArray:
        """
        Push forward a 4th order tensor in material configuration.

        Args:
            tensor4 (sym.MutableDenseNDimArray): The 4th order tensor.
            f (sym.Matrix): The deformation gradient tensor (2nd order tensor).

        Returns:
            sym.MutableDenseNDimArray: The pushforwarded 4th order tensor.
        """
        # Calculate the pushforwarded tensor using comprehensions and broadcasting
        return sym.MutableDenseNDimArray([
            [
                [
                    [
                        sum(
                            f[i, ii] * f[j, jj] * f[k, kk] * f[l0, ll] * tensor4[ii, jj, kk, ll]
                            for ii in range(3)
                            for jj in range(3)
                            for kk in range(3)
                            for ll in range(3)
                        )
                        for l0 in range(3)
                    ]
                    for k in range(3)
                ]
                for j in range(3)
            ]
            for i in range(3)
        ])

    @staticmethod
    def jaumann_rate_mat(sigma: sym.Matrix) -> sym.MutableDenseNDimArray:
        """
        Compute the Jaumann rate contribution for the spatial elasticity tensor.

        Args:
            sigma (sym.Matrix): The Cauchy stress tensor (2nd order tensor).

        Returns:
            sym.MutableDenseNDimArray: The Jaumann rate contribution (4th order tensor).
        """
        # Ensure sigma is a 3x3 matrix
        if sigma.shape != (3, 3):
            raise ValueError("Wrongshape")

        # Use list comprehension to build the 4th-order tensor directly
        return sym.MutableDenseNDimArray([
            [
                [
                    [
                        (1 / 2)
                        * (
                            int(i == k) * sigma[j, ll]
                            + sigma[i, k] * int(j == ll)
                            + int(i == ll) * sigma[j, k]
                            + sigma[i, ll] * int(j == k)
                        )
                        for ll in range(3)
                    ]
                    for k in range(3)
                ]
                for j in range(3)
            ]
            for i in range(3)
        ])

invariant1: Any property

Compute the first invariant of the c_tensor.

Returns:

Type Description
Any

sym.Expr: The first invariant of the c_tensor.

invariant2: Any property

Compute the second invariant of the c_tensor.

Returns:

Type Description
Any

sym.Expr: The second invariant of the c_tensor.

invariant3: Any property

Compute the third invariant of the c_tensor.

Returns:

Type Description
Any

sym.Expr: The third invariant of the c_tensor.

c_symbols()

Return the c_tensor flattened symbols.

Returns:

Name Type Description
list Any

A list of c_tensor symbols.

Source code in hyper_surrogate/symbolic.py
18
19
20
21
22
23
24
25
def c_symbols(self) -> Any:
    """
    Return the c_tensor flattened symbols.

    Returns:
        list: A list of c_tensor symbols.
    """
    return [self.c_tensor[i, j] for i in range(3) for j in range(3)]

cmat_tensor(pk2)

Compute the cmat tensor.

Parameters:

Name Type Description Default
pk2 Matrix

The pk2 tensor.

required

Returns:

Type Description
ImmutableDenseNDimArray

sym.MutableDenseNDimArray: The cmat tensor.

Source code in hyper_surrogate/symbolic.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def cmat_tensor(self, pk2: sym.Matrix) -> sym.ImmutableDenseNDimArray:
    """
    Compute the cmat tensor.

    Args:
        pk2 (sym.Matrix): The pk2 tensor.

    Returns:
        sym.MutableDenseNDimArray: The cmat tensor.
    """
    return sym.ImmutableDenseNDimArray([
        [[[sym.diff(2 * pk2[i, j], self.c_tensor[k, ll]) for ll in range(3)] for k in range(3)] for j in range(3)]
        for i in range(3)
    ])

evaluate_iterator(lambdified_tensor, numerical_c_tensors, *args)

Evaluate a lambdified tensor with specific values.

Parameters:

Name Type Description Default
lambdified_tensor function

A lambdified tensor function.

required
args dict

Additional substitution lists of symbols.

()

Returns:

Name Type Description
Any Any

The evaluated tensor.

Source code in hyper_surrogate/symbolic.py
185
186
187
188
189
190
191
192
193
194
195
196
197
def evaluate_iterator(self, lambdified_tensor: Any, numerical_c_tensors: np.ndarray, *args: Any) -> Any:
    """
    Evaluate a lambdified tensor with specific values.

    Args:
        lambdified_tensor (function): A lambdified tensor function.
        args (dict): Additional substitution lists of symbols.

    Returns:
        Any: The evaluated tensor.
    """
    for numerical_c_tensor in numerical_c_tensors:
        yield self._evaluate(lambdified_tensor, numerical_c_tensor.flatten(), *args)

jaumann_rate_mat(sigma) staticmethod

Compute the Jaumann rate contribution for the spatial elasticity tensor.

Parameters:

Name Type Description Default
sigma Matrix

The Cauchy stress tensor (2nd order tensor).

required

Returns:

Type Description
MutableDenseNDimArray

sym.MutableDenseNDimArray: The Jaumann rate contribution (4th order tensor).

Source code in hyper_surrogate/symbolic.py
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
@staticmethod
def jaumann_rate_mat(sigma: sym.Matrix) -> sym.MutableDenseNDimArray:
    """
    Compute the Jaumann rate contribution for the spatial elasticity tensor.

    Args:
        sigma (sym.Matrix): The Cauchy stress tensor (2nd order tensor).

    Returns:
        sym.MutableDenseNDimArray: The Jaumann rate contribution (4th order tensor).
    """
    # Ensure sigma is a 3x3 matrix
    if sigma.shape != (3, 3):
        raise ValueError("Wrongshape")

    # Use list comprehension to build the 4th-order tensor directly
    return sym.MutableDenseNDimArray([
        [
            [
                [
                    (1 / 2)
                    * (
                        int(i == k) * sigma[j, ll]
                        + sigma[i, k] * int(j == ll)
                        + int(i == ll) * sigma[j, k]
                        + sigma[i, ll] * int(j == k)
                    )
                    for ll in range(3)
                ]
                for k in range(3)
            ]
            for j in range(3)
        ]
        for i in range(3)
    ])

lambdify(symbolic_tensor, *args)

Create a lambdified function from a symbolic tensor that can be used for numerical evaluation.

Parameters:

Name Type Description Default
symbolic_tensor Expr or Matrix

The symbolic tensor to be lambdified.

required
args dict

Additional substitution lists of symbols.

()

Returns: function: A function that can be used to numerically evaluate the tensor with specific values.

Source code in hyper_surrogate/symbolic.py
159
160
161
162
163
164
165
166
167
168
169
170
def lambdify(self, symbolic_tensor: sym.Matrix, *args: Iterable[Any]) -> Any:
    """
    Create a lambdified function from a symbolic tensor that can be used for numerical evaluation.

    Args:
        symbolic_tensor (sym.Expr or sym.Matrix): The symbolic tensor to be lambdified.
        args (dict): Additional substitution lists of symbols.
    Returns:
        function: A function that can be used to numerically evaluate the tensor with specific values.
    """

    return sym.lambdify((self.c_symbols(), *args), symbolic_tensor, modules="numpy")

pk2_tensor(sef)

Compute the pk2 tensor.

Parameters:

Name Type Description Default
sef Expr

The strain energy function.

required

Returns:

Type Description
Matrix

sym.Matrix: The pk2 tensor.

Source code in hyper_surrogate/symbolic.py
80
81
82
83
84
85
86
87
88
89
90
def pk2_tensor(self, sef: sym.Expr) -> sym.Matrix:
    """
    Compute the pk2 tensor.

    Args:
        sef (sym.Expr): The strain energy function.

    Returns:
        sym.Matrix: The pk2 tensor.
    """
    return sym.Matrix([[sym.diff(sef, self.c_tensor[i, j]) for j in range(3)] for i in range(3)])

pushforward_2nd_order(tensor2, f) staticmethod

Push forward a 2nd order tensor in material configuration.

args: tensor2: Any - The 2nd order tensor f: Any - The deformation gradient tensor

returns: Any - The pushforwarded 2nd order tensor

Source code in hyper_surrogate/symbolic.py
255
256
257
258
259
260
261
262
263
264
265
266
267
@staticmethod
def pushforward_2nd_order(tensor2: sym.Matrix, f: sym.Matrix) -> Any:
    """
    Push forward a 2nd order tensor in material configuration.

    args:
    tensor2: Any - The 2nd order tensor
    f: Any - The deformation gradient tensor

    returns:
    Any - The pushforwarded 2nd order tensor
    """
    return sym.simplify(f * tensor2 * f.T)

pushforward_4th_order(tensor4, f) staticmethod

Push forward a 4th order tensor in material configuration.

Parameters:

Name Type Description Default
tensor4 MutableDenseNDimArray

The 4th order tensor.

required
f Matrix

The deformation gradient tensor (2nd order tensor).

required

Returns:

Type Description
MutableDenseNDimArray

sym.MutableDenseNDimArray: The pushforwarded 4th order tensor.

Source code in hyper_surrogate/symbolic.py
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
@staticmethod
def pushforward_4th_order(tensor4: sym.MutableDenseNDimArray, f: sym.Matrix) -> sym.MutableDenseNDimArray:
    """
    Push forward a 4th order tensor in material configuration.

    Args:
        tensor4 (sym.MutableDenseNDimArray): The 4th order tensor.
        f (sym.Matrix): The deformation gradient tensor (2nd order tensor).

    Returns:
        sym.MutableDenseNDimArray: The pushforwarded 4th order tensor.
    """
    # Calculate the pushforwarded tensor using comprehensions and broadcasting
    return sym.MutableDenseNDimArray([
        [
            [
                [
                    sum(
                        f[i, ii] * f[j, jj] * f[k, kk] * f[l0, ll] * tensor4[ii, jj, kk, ll]
                        for ii in range(3)
                        for jj in range(3)
                        for kk in range(3)
                        for ll in range(3)
                    )
                    for l0 in range(3)
                ]
                for k in range(3)
            ]
            for j in range(3)
        ]
        for i in range(3)
    ])

reduce_2nd_order(tensor) staticmethod

Convert a 3x3 matrix to 6x1 matrix using Voigt notation

Parameters:

Name Type Description Default
tensor Matrix

A 3x3 symmetric matrix.

required

Returns:

Type Description
Any

sp.Matrix: A 6x1 matrix.

Source code in hyper_surrogate/symbolic.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
@staticmethod
def reduce_2nd_order(tensor: sym.Matrix) -> Any:
    """
    Convert a 3x3 matrix to 6x1 matrix using Voigt notation

    Args:
        tensor (sp.Matrix): A 3x3 symmetric matrix.

    Returns:
        sp.Matrix: A 6x1 matrix.
    """
    # Validate the input tensor dimensions
    if tensor.shape != (3, 3):
        raise ValueError("Wrong.shape.")
    # Voigt notation conversion: xx, yy, zz, xy, xz, yz
    return sym.Matrix([
        tensor[0, 0],  # xx
        tensor[1, 1],  # yy
        tensor[2, 2],  # zz
        tensor[0, 1],  # xy
        tensor[0, 2],  # xz
        tensor[1, 2],  # yz
    ])

reduce_4th_order(tensor) staticmethod

Convert a 3x3x3x3 matrix to 6x6 matrix using Voigt notation

Parameters:

Name Type Description Default
tensor MutableDenseNDimArray

A 3x3x3x3 matrix.

required

Returns:

Type Description
Any

sym.Matrix: A 6x6 matrix.

Source code in hyper_surrogate/symbolic.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
@staticmethod
def reduce_4th_order(tensor: sym.MutableDenseNDimArray) -> Any:
    """
    Convert a 3x3x3x3 matrix to 6x6 matrix using Voigt notation

    Args:
        tensor (sym.MutableDenseNDimArray): A 3x3x3x3 matrix.

    Returns:
        sym.Matrix: A 6x6 matrix.
    """
    # Validate the input tensor dimensions
    if tensor.shape != (3, 3, 3, 3):
        raise ValueError("Wrong.shape.")

    # Voigt notation index mapping
    ii1 = [0, 1, 2, 0, 0, 1]
    ii2 = [0, 1, 2, 1, 2, 2]

    # Initialize a 6x6 matrix
    voigt_matrix = sym.Matrix.zeros(6, 6)

    # Fill the Voigt matrix by averaging symmetric components of the 4th-order tensor
    for i in range(6):
        for j in range(6):
            # Access the relevant tensor components using ii1 and ii2 mappings
            pp1 = tensor[ii1[i], ii2[i], ii1[j], ii2[j]]
            pp2 = tensor[ii1[i], ii2[i], ii2[j], ii1[j]]
            # Average the two permutations to ensure symmetry
            voigt_matrix[i, j] = 0.5 * (pp1 + pp2)
    return voigt_matrix

substitute(symbolic_tensor, numerical_c_tensor, *args)

Automatically substitute numerical values from a given 3x3 numerical matrix into c_tensor.

Parameters:

Name Type Description Default
symbolic_tensor Matrix

A symbolic tensor to substitute numerical values into.

required
numerical_c_tensor ndarray

A 3x3 numerical matrix to substitute into c_tensor.

required
args dict

Additional substitution dictionaries.

()

Returns:

Type Description
Any

sym.Matrix: The symbolic_tensor with numerical values substituted.

Raises:

Type Description
ValueError

If numerical_tensor is not a 3x3 matrix.

Source code in hyper_surrogate/symbolic.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def substitute(
    self,
    symbolic_tensor: sym.MutableDenseMatrix,
    numerical_c_tensor: np.ndarray,
    *args: dict,
) -> Any:
    """
    Automatically substitute numerical values from a given 3x3 numerical matrix into c_tensor.

    Args:
        symbolic_tensor (sym.Matrix): A symbolic tensor to substitute numerical values into.
        numerical_c_tensor (np.ndarray): A 3x3 numerical matrix to substitute into c_tensor.
        args (dict): Additional substitution dictionaries.

    Returns:
        sym.Matrix: The symbolic_tensor with numerical values substituted.

    Raises:
        ValueError: If numerical_tensor is not a 3x3 matrix.
    """
    if not isinstance(numerical_c_tensor, np.ndarray) or numerical_c_tensor.shape != (3, 3):
        raise ValueError("c_tensor.shape")

    # Start with substitutions for c_tensor elements
    substitutions = {self.c_tensor[i, j]: numerical_c_tensor[i, j] for i in range(3) for j in range(3)}
    # Merge additional substitution dictionaries from *args
    substitutions.update(*args)
    return symbolic_tensor.subs(substitutions)

substitute_iterator(symbolic_tensor, numerical_c_tensors, *args)

Automatically substitute numerical values from a given 3x3 numerical matrix into c_tensor.

Parameters:

Name Type Description Default
symbolic_tensor Matrix

A symbolic tensor to substitute numerical values into.

required
numerical_c_tensors ndarray

N 3x3 numerical matrices to substitute into c_tensor.

required
args dict

Additional substitution dictionaries.

()

Returns:

Type Description
Any

sym.Matrix: The symbolic_tensor with numerical values substituted.

Raises:

Type Description
ValueError

If numerical_tensor is not a 3x3 matrix.

Source code in hyper_surrogate/symbolic.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def substitute_iterator(
    self,
    symbolic_tensor: sym.MutableDenseMatrix,
    numerical_c_tensors: np.ndarray,
    *args: dict,
) -> Any:
    """
    Automatically substitute numerical values from a given 3x3 numerical matrix into c_tensor.

    Args:
        symbolic_tensor (sym.Matrix): A symbolic tensor to substitute numerical values into.
        numerical_c_tensors (np.ndarray): N 3x3 numerical matrices to substitute into c_tensor.
        args (dict): Additional substitution dictionaries.

    Returns:
        sym.Matrix: The symbolic_tensor with numerical values substituted.

    Raises:
        ValueError: If numerical_tensor is not a 3x3 matrix.
    """
    for numerical_c_tensor in numerical_c_tensors:
        yield self.substitute(symbolic_tensor, numerical_c_tensor, *args)