diff --git a/tests/test_mechanics.py b/tests/test_mechanics.py new file mode 100644 index 0000000000000000000000000000000000000000..611eaba7fd242d8f79c29da4e260650753e63b30 --- /dev/null +++ b/tests/test_mechanics.py @@ -0,0 +1,129 @@ +"""Unit tests for solid mechanics.""" + +import unittest + +import numpy as np + +from ogstools.propertylib import tensor_math +from ogstools.propertylib.tensor_math import sym_tensor_to_mat + + +class MechanicsTest(unittest.TestCase): + """Test case for physical properties.""" + + rng = np.random.default_rng() + N_SAMPLES = 100000 + + def equality( + self, vals1: np.ndarray, vals2: np.ndarray, rtol=1e-7, atol=1e-9 + ): + """Assert the equality of two arrays.""" + np.testing.assert_allclose( + vals1, vals2, verbose=True, rtol=rtol, atol=atol + ) + + def test_frobenius_norm(self): + """Test Frobenius norm.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + sig_mat = sym_tensor_to_mat(sig) + frob2 = np.sqrt( + tensor_math.trace( + np.diagonal( + np.transpose(sig_mat, (0, 2, 1)) @ sig_mat, 0, 2 + ) + ) + ) + self.equality(tensor_math.frobenius_norm(sig), frob2) + + def test_I1(self): + """Test first invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + self.equality( + tensor_math.I1(sig), + tensor_math.trace(tensor_math.eigenvalues(sig)), + ) + + def test_I2(self): + """Test second invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + eig_vals = tensor_math.eigenvalues(sig) + self.equality( + tensor_math.I2(sig), + np.sum(eig_vals * np.roll(eig_vals, 1, axis=-1), axis=-1), + ) + + def test_I3(self): + """Test third invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + eig_vals = tensor_math.eigenvalues(sig) + self.equality(tensor_math.I3(sig), np.prod(eig_vals, axis=-1)) + + def test_J1(self): + """Test first deviator invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + j1 = tensor_math.J1(sig) + self.equality(j1, np.zeros(j1.shape)) + + def test_J2(self): + """Test second deviator invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + self.equality( + tensor_math.J2(sig), + 0.5 + * ( + tensor_math.trace(sig**2) + - (1.0 / 3.0) * tensor_math.trace(sig) ** 2 + ), + ) + + def test_J3(self): + """Test third deviator invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + # not exactly sure why, but for stresses where the below condition + # is very close to zero, the error of this test shoots up. + # Probably some floating point precision issue. + mask = ( + np.abs( + tensor_math.trace(sig - tensor_math.mean(sig)[..., None]) + ) + > 1e-9 + ) + sig = sig[mask] + self.equality( + tensor_math.J3(sig), + (1.0 / 3.0) + * ( + tensor_math.trace(sig**3) + - tensor_math.trace(sig**2) * tensor_math.trace(sig) + + (2.0 / 9.0) * tensor_math.trace(sig) ** 3.0 + ), + ) + + def test_von_mises(self): + """Test von Mises invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + ev = tensor_math.eigenvalues(sig) + self.equality( + tensor_math.von_mises(sig), + np.sqrt(0.5 * np.sum(np.square(ev - np.roll(ev, 1, -1)), -1)), + ) + + def test_octahedral_shear_stress(self): + """Test octahedral shear stress invariant.""" + for symten_len in [4, 6]: + sig = self.rng.random((self.N_SAMPLES, symten_len)) * 1e6 + self.equality( + tensor_math.octahedral_shear(sig), + (1.0 / 3.0) + * np.sqrt( + 2 * tensor_math.I1(sig) ** 2 - 6 * tensor_math.I2(sig) + ), + )