Exemple #1
0
 def test_convert_to_ieee(self):
     for xtal in self.ieee_data.keys():
         orig = TensorBase(self.ieee_data[xtal]['original_tensor'])
         ieee = TensorBase(self.ieee_data[xtal]['ieee_tensor'])
         struct = Structure.from_dict(self.ieee_data[xtal]['structure'])
         diff = np.max(abs(ieee - orig.convert_to_ieee(struct)))
         err_msg = "{} IEEE conversion failed with max diff {}".format(
             xtal, diff) 
         self.assertArrayAlmostEqual(ieee, orig.convert_to_ieee(struct),
                                     err_msg = err_msg, decimal=3)
 def test_zeroed(self):
     self.assertArrayEqual(
         self.low_val.zeroed(),
         TensorBase([[0, 1 + 1e-5, 0], [1 + 1e-6, 0, 0], [0, 0, 1 + 1e-5]]))
     self.assertArrayEqual(
         self.low_val.zeroed(tol=1e-6),
         TensorBase([[1e-6, 1 + 1e-5, 1e-6], [1 + 1e-6, 1e-6, 1e-6],
                     [0, 0, 1 + 1e-5]]))
     self.assertArrayEqual(
         TensorBase([[1e-6, -30, 1], [1e-7, 1, 0], [1e-8, 0, 1]]).zeroed(),
         TensorBase([[0, -30, 1], [0, 1, 0], [0, 0, 1]]))
Exemple #3
0
 def test_convert_to_ieee(self):
     for entry in self.ieee_data:
         xtal = entry['xtal']
         orig = TensorBase(entry['original_tensor'])
         ieee = TensorBase(entry['ieee_tensor'])
         struct = Structure.from_dict(entry['structure'])
         diff = np.max(abs(ieee - orig.convert_to_ieee(struct)))
         err_msg = "{} IEEE conversion failed with max diff {}".format(
             xtal, diff) 
         self.assertArrayAlmostEqual(ieee, orig.convert_to_ieee(struct),
                                     err_msg = err_msg, decimal=3)
Exemple #4
0
 def test_from_voigt(self):
     with self.assertRaises(ValueError):
         TensorBase.from_voigt([[59.33, 28.08, 28.08, 0],
                                [28.08, 59.31, 28.07, 0],
                                [28.08, 28.07, 59.32, 0, 0],
                                [0, 0, 0, 26.35, 0], [0, 0, 0, 0, 26.35]])
     # Rank 4
     TensorBase.from_voigt([[59.33, 28.08, 28.08, 0, 0, 0],
                            [28.08, 59.31, 28.07, 0, 0, 0],
                            [28.08, 28.07, 59.32, 0, 0, 0],
                            [0, 0, 0, 26.35, 0, 0], [0, 0, 0, 0, 26.35, 0],
                            [0, 0, 0, 0, 0, 26.35]])
    def test_transform(self):
        # Rank 3
        tensor = TensorBase(np.arange(0, 27).reshape(3, 3, 3))
        symm_op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False,
                                                         [0, 0, 1])
        new_tensor = tensor.transform(symm_op)

        self.assertArrayAlmostEqual(
            new_tensor, [[[-0.871, -2.884, -1.928], [-2.152, -6.665, -4.196],
                          [-1.026, -2.830, -1.572]],
                         [[0.044, 1.531, 1.804], [4.263, 21.008, 17.928],
                          [5.170, 23.026, 18.722]],
                         [[1.679, 7.268, 5.821], [9.268, 38.321, 29.919],
                          [8.285, 33.651, 26.000]]], 3)
Exemple #6
0
 def test_from_voigt(self):
     with self.assertRaises(ValueError):
         TensorBase.from_voigt([[59.33, 28.08, 28.08, 0],
                                [28.08, 59.31, 28.07, 0],
                                [28.08, 28.07, 59.32, 0, 0],
                                [0, 0, 0, 26.35, 0],
                                [0, 0, 0, 0, 26.35]])
     # Rank 4
     TensorBase.from_voigt([[59.33, 28.08, 28.08, 0, 0, 0],
                            [28.08, 59.31, 28.07, 0, 0, 0],
                            [28.08, 28.07, 59.32, 0, 0, 0],
                            [0, 0, 0, 26.35, 0, 0],
                            [0, 0, 0, 0, 26.35, 0],
                            [0, 0, 0, 0, 0, 26.35]])
Exemple #7
0
    def __new__(cls, input_array, tol=1e-3):
        """
        Create an ElasticTensor object.  The constructor throws an error if
        the shape of the input_matrix argument is not 3x3x3x3, i. e. in true
        tensor notation.  Issues a warning if the input_matrix argument does
        not satisfy standard symmetries.  Note that the constructor uses
        __new__ rather than __init__ according to the standard method of
        subclassing numpy ndarrays.

        Args:
            input_array (3x3x3x3 array-like): the 3x3x3x3 array-like
                representing the elastic tensor

            tol (float): tolerance for initial symmetry test of tensor
        """

        obj = TensorBase(input_array).view(cls)
        if obj.shape != (3, 3, 3, 3):
            raise ValueError("Default elastic tensor constructor requires "
                             "input to be the true 3x3x3x3 representation. "
                             "To construct from an elastic tensor from "
                             "6x6 Voigt array, use ElasticTensor.from_voigt")

        if not ((obj - np.transpose(obj, (1, 0, 2, 3)) < tol).all() and
                (obj - np.transpose(obj, (0, 1, 3, 2)) < tol).all() and
                (obj - np.transpose(obj, (1, 0, 3, 2)) < tol).all() and
                (obj - np.transpose(obj, (3, 2, 0, 1)) < tol).all()):
            warnings.warn("Input elasticity tensor does "
                          "not satisfy standard symmetries")

        return obj
Exemple #8
0
    def test_transform(self):
        # Rank 3
        tensor = TensorBase(np.arange(0, 27).reshape(3, 3, 3))
        symm_op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30,
                                                         False, [0, 0, 1])
        new_tensor = tensor.transform(symm_op)

        self.assertArrayAlmostEqual(new_tensor,
                                    [[[-0.871, -2.884, -1.928],
                                      [-2.152, -6.665, -4.196],
                                      [-1.026, -2.830, -1.572]],
                                     [[0.044, 1.531, 1.804],
                                      [4.263, 21.008, 17.928],
                                      [5.170, 23.026, 18.722]],
                                     [[1.679, 7.268, 5.821],
                                      [9.268, 38.321, 29.919],
                                      [8.285, 33.651, 26.000]]], 3)
Exemple #9
0
    def __new__(cls, input_array, tol=1e-3):
        """
        Create an PiezoTensor object.  The constructor throws an error if
        the shape of the input_matrix argument is not 3x3x3, i. e. in true
        tensor notation. Note that the constructor uses __new__ rather than
        __init__ according to the standard method of subclassing numpy
        ndarrays.

        Args:
            input_matrix (3x3x3 array-like): the 3x6 array-like
                representing the piezo tensor
        """
        obj = TensorBase(input_array).view(cls)
        if obj.shape != (3, 3, 3):
            raise ValueError("Default piezo tensor constructor requires "
                             "input argument to be the true 3x3x3 "
                             "array.  To construct from a 3x6 array, use "
                             "PiezoTensor.from_voigt")
        if not (obj - np.transpose(obj, (0, 2, 1)) < tol).all():
            warnings.warn("Input piezo tensor does "
                          "not satisfy standard symmetries")
        return obj
    def setUp(self):
        self.vec = TensorBase([1., 0., 0.])
        self.rand_rank2 = TensorBase(np.random.randn(3, 3))
        self.rand_rank3 = TensorBase(np.random.randn(3, 3, 3))
        self.rand_rank4 = TensorBase(np.random.randn(3, 3, 3, 3))
        a = 3.14 * 42.5 / 180
        self.non_symm = SquareTensor([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6],
                                      [0.2, 0.5, 0.5]])
        self.rotation = SquareTensor([[math.cos(a), 0,
                                       math.sin(a)], [0, 1, 0],
                                      [-math.sin(a), 0,
                                       math.cos(a)]])
        self.low_val = TensorBase([[1e-6, 1 + 1e-5, 1e-6],
                                   [1 + 1e-6, 1e-6, 1e-6],
                                   [1e-7, 1e-7, 1 + 1e-5]])
        self.symm_rank2 = TensorBase([[1, 2, 3], [2, 4, 5], [3, 5, 6]])
        self.symm_rank3 = TensorBase([[[1, 2, 3], [2, 4, 5], [3, 5, 6]],
                                      [[2, 4, 5], [4, 7, 8], [5, 8, 9]],
                                      [[3, 5, 6], [5, 8, 9], [6, 9, 10]]])
        self.symm_rank4 = TensorBase([[[[1.2, 0.4, -0.92], [0.4, 0.05, 0.11],
                                        [-0.92, 0.11, -0.02]],
                                       [[0.4, 0.05, 0.11], [0.05, -0.47, 0.09],
                                        [0.11, 0.09, -0.]],
                                       [[-0.92, 0.11, -0.02], [0.11, 0.09, 0.],
                                        [-0.02, 0., -0.3]]],
                                      [[[0.4, 0.05, 0.11], [0.05, -0.47, 0.09],
                                        [0.11, 0.09, 0.]],
                                       [[0.05, -0.47,
                                         0.09], [-0.47, 0.17, 0.62],
                                        [0.09, 0.62, 0.3]],
                                       [[0.11, 0.09, 0.], [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]]],
                                      [[[-0.92, 0.11, -0.02], [0.11, 0.09, 0.],
                                        [-0.02, 0, -0.3]],
                                       [[0.11, 0.09, 0.], [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]],
                                       [[-0.02, 0., -0.3], [0., 0.3, -0.18],
                                        [-0.3, -0.18, -0.51]]]])

        # Structural symmetries tested using BaNiO3 piezo/elastic tensors
        self.fit_r3 = TensorBase([[[0., 0., 0.03839], [0., 0., 0.],
                                   [0.03839, 0., 0.]],
                                  [[0., 0., 0.], [0., 0., 0.03839],
                                   [0., 0.03839, 0.]],
                                  [[6.89822, 0., 0.], [0., 6.89822, 0.],
                                   [0., 0., 27.4628]]])
        self.fit_r4 = TensorBase([[[[157.9, 0., 0.], [0., 63.1, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 47.4, 0.], [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.3], [0., 0., 0.], [4.3, 0.,
                                                                  0.]]],
                                  [[[0., 47.4, 0.], [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[63.1, 0., 0.], [0., 157.9, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 0., 0.], [0., 0., 4.3], [0., 4.3,
                                                                  0.]]],
                                  [[[0., 0., 4.3], [0., 0., 0.], [4.3, 0.,
                                                                  0.]],
                                   [[0., 0., 0.], [0., 0., 4.3], [0., 4.3,
                                                                  0.]],
                                   [[29.4, 0., 0.], [0., 29.4, 0.],
                                    [0., 0., 207.6]]]])

        self.unfit4 = TensorBase([[[[161.26, 0., 0.], [0., 62.76, 0.],
                                    [0., 0., 30.18]],
                                   [[0., 47.08, 0.], [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.23], [0., 0., 0.],
                                    [4.23, 0., 0.]]],
                                  [[[0., 47.08, 0.], [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[62.76, 0., 0.], [0., 155.28, -0.06],
                                    [0., -0.06, 28.53]],
                                   [[0., 0., 0.], [0., -0.06, 4.44],
                                    [0., 4.44, 0.]]],
                                  [[[0., 0., 4.23], [0., 0., 0.],
                                    [4.23, 0., 0.]],
                                   [[0., 0., 0.], [0., -0.06, 4.44],
                                    [0., 4.44, 0.]],
                                   [[30.18, 0., 0.], [0., 28.53, 0.],
                                    [0., 0., 207.57]]]])

        self.structure = self.get_structure('BaNiO3')
        ieee_file_path = os.path.join(test_dir, "ieee_conversion_data.json")
        with open(ieee_file_path) as f:
            self.ieee_data = json.load(f)
class TensorBaseTest(PymatgenTest):
    def setUp(self):
        self.vec = TensorBase([1., 0., 0.])
        self.rand_rank2 = TensorBase(np.random.randn(3, 3))
        self.rand_rank3 = TensorBase(np.random.randn(3, 3, 3))
        self.rand_rank4 = TensorBase(np.random.randn(3, 3, 3, 3))
        a = 3.14 * 42.5 / 180
        self.non_symm = SquareTensor([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6],
                                      [0.2, 0.5, 0.5]])
        self.rotation = SquareTensor([[math.cos(a), 0,
                                       math.sin(a)], [0, 1, 0],
                                      [-math.sin(a), 0,
                                       math.cos(a)]])
        self.low_val = TensorBase([[1e-6, 1 + 1e-5, 1e-6],
                                   [1 + 1e-6, 1e-6, 1e-6],
                                   [1e-7, 1e-7, 1 + 1e-5]])
        self.symm_rank2 = TensorBase([[1, 2, 3], [2, 4, 5], [3, 5, 6]])
        self.symm_rank3 = TensorBase([[[1, 2, 3], [2, 4, 5], [3, 5, 6]],
                                      [[2, 4, 5], [4, 7, 8], [5, 8, 9]],
                                      [[3, 5, 6], [5, 8, 9], [6, 9, 10]]])
        self.symm_rank4 = TensorBase([[[[1.2, 0.4, -0.92], [0.4, 0.05, 0.11],
                                        [-0.92, 0.11, -0.02]],
                                       [[0.4, 0.05, 0.11], [0.05, -0.47, 0.09],
                                        [0.11, 0.09, -0.]],
                                       [[-0.92, 0.11, -0.02], [0.11, 0.09, 0.],
                                        [-0.02, 0., -0.3]]],
                                      [[[0.4, 0.05, 0.11], [0.05, -0.47, 0.09],
                                        [0.11, 0.09, 0.]],
                                       [[0.05, -0.47,
                                         0.09], [-0.47, 0.17, 0.62],
                                        [0.09, 0.62, 0.3]],
                                       [[0.11, 0.09, 0.], [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]]],
                                      [[[-0.92, 0.11, -0.02], [0.11, 0.09, 0.],
                                        [-0.02, 0, -0.3]],
                                       [[0.11, 0.09, 0.], [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]],
                                       [[-0.02, 0., -0.3], [0., 0.3, -0.18],
                                        [-0.3, -0.18, -0.51]]]])

        # Structural symmetries tested using BaNiO3 piezo/elastic tensors
        self.fit_r3 = TensorBase([[[0., 0., 0.03839], [0., 0., 0.],
                                   [0.03839, 0., 0.]],
                                  [[0., 0., 0.], [0., 0., 0.03839],
                                   [0., 0.03839, 0.]],
                                  [[6.89822, 0., 0.], [0., 6.89822, 0.],
                                   [0., 0., 27.4628]]])
        self.fit_r4 = TensorBase([[[[157.9, 0., 0.], [0., 63.1, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 47.4, 0.], [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.3], [0., 0., 0.], [4.3, 0.,
                                                                  0.]]],
                                  [[[0., 47.4, 0.], [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[63.1, 0., 0.], [0., 157.9, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 0., 0.], [0., 0., 4.3], [0., 4.3,
                                                                  0.]]],
                                  [[[0., 0., 4.3], [0., 0., 0.], [4.3, 0.,
                                                                  0.]],
                                   [[0., 0., 0.], [0., 0., 4.3], [0., 4.3,
                                                                  0.]],
                                   [[29.4, 0., 0.], [0., 29.4, 0.],
                                    [0., 0., 207.6]]]])

        self.unfit4 = TensorBase([[[[161.26, 0., 0.], [0., 62.76, 0.],
                                    [0., 0., 30.18]],
                                   [[0., 47.08, 0.], [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.23], [0., 0., 0.],
                                    [4.23, 0., 0.]]],
                                  [[[0., 47.08, 0.], [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[62.76, 0., 0.], [0., 155.28, -0.06],
                                    [0., -0.06, 28.53]],
                                   [[0., 0., 0.], [0., -0.06, 4.44],
                                    [0., 4.44, 0.]]],
                                  [[[0., 0., 4.23], [0., 0., 0.],
                                    [4.23, 0., 0.]],
                                   [[0., 0., 0.], [0., -0.06, 4.44],
                                    [0., 4.44, 0.]],
                                   [[30.18, 0., 0.], [0., 28.53, 0.],
                                    [0., 0., 207.57]]]])

        self.structure = self.get_structure('BaNiO3')
        ieee_file_path = os.path.join(test_dir, "ieee_conversion_data.json")
        with open(ieee_file_path) as f:
            self.ieee_data = json.load(f)

    def test_new(self):
        bad_2 = np.zeros((4, 4))
        bad_3 = np.zeros((4, 4, 4))
        self.assertRaises(ValueError, TensorBase, bad_2)
        self.assertRaises(ValueError, TensorBase, bad_3)
        self.assertEqual(self.rand_rank2.rank, 2)
        self.assertEqual(self.rand_rank3.rank, 3)
        self.assertEqual(self.rand_rank4.rank, 4)

    def test_zeroed(self):
        self.assertArrayEqual(
            self.low_val.zeroed(),
            TensorBase([[0, 1 + 1e-5, 0], [1 + 1e-6, 0, 0], [0, 0, 1 + 1e-5]]))
        self.assertArrayEqual(
            self.low_val.zeroed(tol=1e-6),
            TensorBase([[1e-6, 1 + 1e-5, 1e-6], [1 + 1e-6, 1e-6, 1e-6],
                        [0, 0, 1 + 1e-5]]))
        self.assertArrayEqual(
            TensorBase([[1e-6, -30, 1], [1e-7, 1, 0], [1e-8, 0, 1]]).zeroed(),
            TensorBase([[0, -30, 1], [0, 1, 0], [0, 0, 1]]))

    def test_transform(self):
        # Rank 3
        tensor = TensorBase(np.arange(0, 27).reshape(3, 3, 3))
        symm_op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False,
                                                         [0, 0, 1])
        new_tensor = tensor.transform(symm_op)

        self.assertArrayAlmostEqual(
            new_tensor, [[[-0.871, -2.884, -1.928], [-2.152, -6.665, -4.196],
                          [-1.026, -2.830, -1.572]],
                         [[0.044, 1.531, 1.804], [4.263, 21.008, 17.928],
                          [5.170, 23.026, 18.722]],
                         [[1.679, 7.268, 5.821], [9.268, 38.321, 29.919],
                          [8.285, 33.651, 26.000]]], 3)

    def test_rotate(self):
        self.assertArrayEqual(
            self.vec.rotate([[0, -1, 0], [1, 0, 0], [0, 0, 1]]), [0, 1, 0])
        self.assertArrayAlmostEqual(self.non_symm.rotate(self.rotation),
                                    SquareTensor([[0.531, 0.485, 0.271],
                                                  [0.700, 0.5, 0.172],
                                                  [0.171, 0.233, 0.068]]),
                                    decimal=3)
        self.assertRaises(ValueError, self.non_symm.rotate, self.symm_rank2)

    def test_symmetrized(self):
        self.assertTrue(self.rand_rank2.symmetrized.is_symmetric())
        self.assertTrue(self.rand_rank3.symmetrized.is_symmetric())
        self.assertTrue(self.rand_rank4.symmetrized.is_symmetric())

    def test_is_symmetric(self):
        self.assertTrue(self.symm_rank2.is_symmetric())
        self.assertTrue(self.symm_rank3.is_symmetric())
        self.assertTrue(self.symm_rank4.is_symmetric())
        tol_test = self.symm_rank4
        tol_test[0, 1, 2, 2] += 1e-6
        self.assertFalse(self.low_val.is_symmetric(tol=1e-8))

    def test_fit_to_structure(self):
        new_fit = self.unfit4.fit_to_structure(self.structure)
        self.assertArrayAlmostEqual(new_fit, self.fit_r4, 1)

    def test_is_fit_to_structure(self):
        self.assertFalse(self.unfit4.is_fit_to_structure(self.structure))
        self.assertTrue(self.fit_r3.is_fit_to_structure(self.structure))
        self.assertTrue(self.fit_r4.is_fit_to_structure(self.structure))

    def test_convert_to_ieee(self):
        for xtal in self.ieee_data.keys():
            orig = TensorBase(self.ieee_data[xtal]['original_tensor'])
            ieee = TensorBase(self.ieee_data[xtal]['ieee_tensor'])
            struct = Structure.from_dict(self.ieee_data[xtal]['structure'])
            diff = np.max(abs(ieee - orig.convert_to_ieee(struct)))
            err_msg = "{} IEEE conversion failed with max diff {}".format(
                xtal, diff)
            self.assertArrayAlmostEqual(ieee,
                                        orig.convert_to_ieee(struct),
                                        err_msg=err_msg,
                                        decimal=3)
Exemple #12
0
    def setUp(self):
        self.vec = TensorBase([1., 0., 0.])
        self.rand_rank2 = TensorBase(np.random.randn(3,3))
        self.rand_rank3 = TensorBase(np.random.randn(3,3,3))
        self.rand_rank4 = TensorBase(np.random.randn(3,3,3,3))
        a = 3.14 * 42.5 / 180
        self.non_symm = SquareTensor([[0.1, 0.2, 0.3],
                                      [0.4, 0.5, 0.6],
                                      [0.2, 0.5, 0.5]])
        self.rotation = SquareTensor([[math.cos(a), 0, math.sin(a)],
                                      [0, 1, 0],
                                      [-math.sin(a), 0, math.cos(a)]])
        self.low_val = TensorBase([[1e-6, 1 + 1e-5, 1e-6],
                                   [1 + 1e-6, 1e-6, 1e-6],
                                   [1e-7, 1e-7, 1 + 1e-5]])
        self.symm_rank2 = TensorBase([[1, 2, 3],
                                      [2, 4, 5],
                                      [3, 5, 6]])
        self.symm_rank3 = TensorBase([[[1, 2, 3],
                                       [2, 4, 5],
                                       [3, 5, 6]],
                                      [[2, 4, 5],
                                       [4, 7, 8],
                                       [5, 8, 9]],
                                      [[3, 5, 6],
                                       [5, 8, 9],
                                       [6, 9, 10]]])
        self.symm_rank4 = TensorBase([[[[1.2, 0.4, -0.92],
                                        [0.4, 0.05, 0.11],
                                        [-0.92, 0.11, -0.02]],
                                       [[0.4, 0.05, 0.11],
                                        [0.05, -0.47, 0.09],
                                        [0.11, 0.09, -0.]],
                                       [[-0.92, 0.11, -0.02],
                                        [0.11, 0.09, 0.],
                                        [-0.02, 0., -0.3]]],
                                      [[[0.4, 0.05, 0.11],
                                        [0.05, -0.47, 0.09],
                                        [0.11, 0.09, 0.]],
                                       [[0.05, -0.47, 0.09],
                                        [-0.47, 0.17, 0.62],
                                        [0.09, 0.62, 0.3]],
                                       [[0.11, 0.09, 0.],
                                        [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]]],
                                      [[[-0.92, 0.11, -0.02],
                                        [0.11, 0.09, 0.],
                                        [-0.02, 0, -0.3]],
                                       [[0.11, 0.09, 0.],
                                        [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]],
                                       [[-0.02, 0., -0.3],
                                        [0., 0.3, -0.18],
                                        [-0.3, -0.18, -0.51]]]])

        # Structural symmetries tested using BaNiO3 piezo/elastic tensors
        self.fit_r3 = TensorBase([[[0., 0., 0.03839],
                                 [0., 0., 0.],
                                 [0.03839, 0., 0.]],
                                [[0., 0., 0.],
                                 [0., 0., 0.03839],
                                 [0., 0.03839, 0.]],
                                [[6.89822, 0., 0.],
                                 [0., 6.89822, 0.],
                                 [0., 0., 27.4628]]])
        self.fit_r4 = TensorBase([[[[157.9, 0., 0.],
                                    [0., 63.1, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 47.4, 0.],
                                    [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.3],
                                    [0., 0., 0.],
                                    [4.3, 0., 0.]]],
                                  [[[0., 47.4, 0.],
                                    [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[63.1, 0., 0.],
                                    [0., 157.9, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 0., 0.],
                                    [0., 0., 4.3],
                                    [0., 4.3, 0.]]],
                                  [[[0., 0., 4.3],
                                    [0., 0., 0.],
                                    [4.3, 0., 0.]],
                                   [[0., 0., 0.],
                                    [0., 0., 4.3],
                                    [0., 4.3, 0.]],
                                   [[29.4, 0., 0.],
                                    [0., 29.4, 0.],
                                    [0., 0., 207.6]]]])
        
        self.unfit4 = TensorBase([[[[161.26, 0., 0.],
                                    [0., 62.76, 0.],
                                    [0., 0., 30.18]],
                                   [[0., 47.08, 0.],
                                    [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.23],
                                    [0., 0., 0.],
                                    [4.23, 0., 0.]]],
                                  [[[0., 47.08, 0.],
                                    [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[62.76, 0., 0.],
                                    [0., 155.28, -0.06],
                                    [0., -0.06, 28.53]],
                                   [[0., 0., 0.],
                                    [0., -0.06, 4.44],
                                    [0., 4.44, 0.]]],
                                  [[[0., 0., 4.23],
                                    [0., 0., 0.],
                                    [4.23, 0., 0.]],
                                   [[0., 0., 0.],
                                    [0., -0.06, 4.44],
                                    [0., 4.44, 0.]],
                                   [[30.18, 0., 0.],
                                    [0., 28.53, 0.],
                                    [0., 0., 207.57]]]])

        self.structure = self.get_structure('BaNiO3')
        ieee_file_path = os.path.join(test_dir, "ieee_conversion_data.json")
        with open(ieee_file_path) as f:
            self.ieee_data = json.load(f)
Exemple #13
0
class TensorBaseTest(PymatgenTest):
    def setUp(self):
        self.vec = TensorBase([1., 0., 0.])
        self.rand_rank2 = TensorBase(np.random.randn(3,3))
        self.rand_rank3 = TensorBase(np.random.randn(3,3,3))
        self.rand_rank4 = TensorBase(np.random.randn(3,3,3,3))
        a = 3.14 * 42.5 / 180
        self.non_symm = SquareTensor([[0.1, 0.2, 0.3],
                                      [0.4, 0.5, 0.6],
                                      [0.2, 0.5, 0.5]])
        self.rotation = SquareTensor([[math.cos(a), 0, math.sin(a)],
                                      [0, 1, 0],
                                      [-math.sin(a), 0, math.cos(a)]])
        self.low_val = TensorBase([[1e-6, 1 + 1e-5, 1e-6],
                                   [1 + 1e-6, 1e-6, 1e-6],
                                   [1e-7, 1e-7, 1 + 1e-5]])
        self.symm_rank2 = TensorBase([[1, 2, 3],
                                      [2, 4, 5],
                                      [3, 5, 6]])
        self.symm_rank3 = TensorBase([[[1, 2, 3],
                                       [2, 4, 5],
                                       [3, 5, 6]],
                                      [[2, 4, 5],
                                       [4, 7, 8],
                                       [5, 8, 9]],
                                      [[3, 5, 6],
                                       [5, 8, 9],
                                       [6, 9, 10]]])
        self.symm_rank4 = TensorBase([[[[1.2, 0.4, -0.92],
                                        [0.4, 0.05, 0.11],
                                        [-0.92, 0.11, -0.02]],
                                       [[0.4, 0.05, 0.11],
                                        [0.05, -0.47, 0.09],
                                        [0.11, 0.09, -0.]],
                                       [[-0.92, 0.11, -0.02],
                                        [0.11, 0.09, 0.],
                                        [-0.02, 0., -0.3]]],
                                      [[[0.4, 0.05, 0.11],
                                        [0.05, -0.47, 0.09],
                                        [0.11, 0.09, 0.]],
                                       [[0.05, -0.47, 0.09],
                                        [-0.47, 0.17, 0.62],
                                        [0.09, 0.62, 0.3]],
                                       [[0.11, 0.09, 0.],
                                        [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]]],
                                      [[[-0.92, 0.11, -0.02],
                                        [0.11, 0.09, 0.],
                                        [-0.02, 0, -0.3]],
                                       [[0.11, 0.09, 0.],
                                        [0.09, 0.62, 0.3],
                                        [0., 0.3, -0.18]],
                                       [[-0.02, 0., -0.3],
                                        [0., 0.3, -0.18],
                                        [-0.3, -0.18, -0.51]]]])

        # Structural symmetries tested using BaNiO3 piezo/elastic tensors
        self.fit_r3 = TensorBase([[[0., 0., 0.03839],
                                 [0., 0., 0.],
                                 [0.03839, 0., 0.]],
                                [[0., 0., 0.],
                                 [0., 0., 0.03839],
                                 [0., 0.03839, 0.]],
                                [[6.89822, 0., 0.],
                                 [0., 6.89822, 0.],
                                 [0., 0., 27.4628]]])
        self.fit_r4 = TensorBase([[[[157.9, 0., 0.],
                                    [0., 63.1, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 47.4, 0.],
                                    [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.3],
                                    [0., 0., 0.],
                                    [4.3, 0., 0.]]],
                                  [[[0., 47.4, 0.],
                                    [47.4, 0., 0.],
                                    [0., 0., 0.]],
                                   [[63.1, 0., 0.],
                                    [0., 157.9, 0.],
                                    [0., 0., 29.4]],
                                   [[0., 0., 0.],
                                    [0., 0., 4.3],
                                    [0., 4.3, 0.]]],
                                  [[[0., 0., 4.3],
                                    [0., 0., 0.],
                                    [4.3, 0., 0.]],
                                   [[0., 0., 0.],
                                    [0., 0., 4.3],
                                    [0., 4.3, 0.]],
                                   [[29.4, 0., 0.],
                                    [0., 29.4, 0.],
                                    [0., 0., 207.6]]]])
        
        self.unfit4 = TensorBase([[[[161.26, 0., 0.],
                                    [0., 62.76, 0.],
                                    [0., 0., 30.18]],
                                   [[0., 47.08, 0.],
                                    [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[0., 0., 4.23],
                                    [0., 0., 0.],
                                    [4.23, 0., 0.]]],
                                  [[[0., 47.08, 0.],
                                    [47.08, 0., 0.],
                                    [0., 0., 0.]],
                                   [[62.76, 0., 0.],
                                    [0., 155.28, -0.06],
                                    [0., -0.06, 28.53]],
                                   [[0., 0., 0.],
                                    [0., -0.06, 4.44],
                                    [0., 4.44, 0.]]],
                                  [[[0., 0., 4.23],
                                    [0., 0., 0.],
                                    [4.23, 0., 0.]],
                                   [[0., 0., 0.],
                                    [0., -0.06, 4.44],
                                    [0., 4.44, 0.]],
                                   [[30.18, 0., 0.],
                                    [0., 28.53, 0.],
                                    [0., 0., 207.57]]]])

        self.structure = self.get_structure('BaNiO3')
        ieee_file_path = os.path.join(test_dir, "ieee_conversion_data.json")
        with open(ieee_file_path) as f:
            self.ieee_data = json.load(f)

    def test_new(self):
        bad_2 = np.zeros((4, 4))
        bad_3 = np.zeros((4, 4, 4))
        self.assertRaises(ValueError, TensorBase, bad_2)
        self.assertRaises(ValueError, TensorBase, bad_3)
        self.assertEqual(self.rand_rank2.rank, 2)
        self.assertEqual(self.rand_rank3.rank, 3)
        self.assertEqual(self.rand_rank4.rank, 4)

    def test_zeroed(self):
        self.assertArrayEqual(self.low_val.zeroed(),
                              TensorBase([[0, 1 + 1e-5, 0],
                                          [1 + 1e-6, 0, 0],
                                          [0, 0, 1 + 1e-5]]))
        self.assertArrayEqual(self.low_val.zeroed(tol=1e-6),
                              TensorBase([[1e-6, 1 + 1e-5, 1e-6],
                                          [1 + 1e-6, 1e-6, 1e-6],
                                          [0, 0, 1 + 1e-5]]))
        self.assertArrayEqual(TensorBase([[1e-6, -30, 1],
                                          [1e-7, 1, 0],
                                          [1e-8, 0, 1]]).zeroed(),
                              TensorBase([[0, -30, 1],
                                          [0, 1, 0],
                                          [0, 0, 1]]))

    def test_transform(self):
        # Rank 3
        tensor = TensorBase(np.arange(0, 27).reshape(3, 3, 3))
        symm_op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30,
                                                         False, [0, 0, 1])
        new_tensor = tensor.transform(symm_op)

        self.assertArrayAlmostEqual(new_tensor,
                                    [[[-0.871, -2.884, -1.928],
                                      [-2.152, -6.665, -4.196],
                                      [-1.026, -2.830, -1.572]],
                                     [[0.044, 1.531, 1.804],
                                      [4.263, 21.008, 17.928],
                                      [5.170, 23.026, 18.722]],
                                     [[1.679, 7.268, 5.821],
                                      [9.268, 38.321, 29.919],
                                      [8.285, 33.651, 26.000]]], 3)

    
    def test_rotate(self):
        self.assertArrayEqual(self.vec.rotate([[0, -1, 0],
                                               [1, 0, 0],
                                               [0, 0, 1]]),
                              [0, 1, 0])
        self.assertArrayAlmostEqual(self.non_symm.rotate(self.rotation),
                                    SquareTensor([[0.531, 0.485, 0.271],
                                                  [0.700, 0.5, 0.172],
                                                  [0.171, 0.233, 0.068]]),
                                    decimal=3)
        self.assertRaises(ValueError, self.non_symm.rotate, 
                          self.symm_rank2)

    def test_symmetrized(self):
        self.assertTrue(self.rand_rank2.symmetrized.is_symmetric())
        self.assertTrue(self.rand_rank3.symmetrized.is_symmetric())
        self.assertTrue(self.rand_rank4.symmetrized.is_symmetric())
    
    
    def test_is_symmetric(self):
        self.assertTrue(self.symm_rank2.is_symmetric())
        self.assertTrue(self.symm_rank3.is_symmetric())
        self.assertTrue(self.symm_rank4.is_symmetric())
        tol_test = self.symm_rank4
        tol_test[0, 1, 2, 2] += 1e-6
        self.assertFalse(self.low_val.is_symmetric(tol=1e-8))

    def test_fit_to_structure(self):
        new_fit = self.unfit4.fit_to_structure(self.structure)
        self.assertArrayAlmostEqual(new_fit, self.fit_r4, 1)

    def test_is_fit_to_structure(self):
        self.assertFalse(self.unfit4.is_fit_to_structure(self.structure))
        self.assertTrue(self.fit_r3.is_fit_to_structure(self.structure))
        self.assertTrue(self.fit_r4.is_fit_to_structure(self.structure))

    def test_convert_to_ieee(self):
        for xtal in self.ieee_data.keys():
            orig = TensorBase(self.ieee_data[xtal]['original_tensor'])
            ieee = TensorBase(self.ieee_data[xtal]['ieee_tensor'])
            struct = Structure.from_dict(self.ieee_data[xtal]['structure'])
            diff = np.max(abs(ieee - orig.convert_to_ieee(struct)))
            err_msg = "{} IEEE conversion failed with max diff {}".format(
                xtal, diff) 
            self.assertArrayAlmostEqual(ieee, orig.convert_to_ieee(struct),
                                        err_msg = err_msg, decimal=3)
Exemple #14
0
def toec_fit(strains, stresses, eq_stress = None, zero_crit=1e-10):
    """
    A third-order elastic constant fitting function based on 
    central-difference derivatives with respect to distinct
    strain states.  The algorithm is summarized as follows:

    1. Identify distinct strain states as sets of indices 
       for which nonzero strain values exist, typically
       [(0), (1), (2), (3), (4), (5), (0, 1) etc.]
    2. For each strain state, find and sort strains and
       stresses by strain value.
    3. Find first and second derivatives of each stress
       with respect to scalar variable corresponding to
       the smallest perturbation in the strain.
    4. Use the pseudoinverse of a matrix-vector expression 
       corresponding to the parameterized stress-strain
       relationship and multiply that matrix by the respective 
       calculated first or second derivatives from the
       previous step.
    5. Place the calculated second and third-order elastic 
       constants appropriately.

    Args:
        strains (nx3x3 array-like): Array of 3x3 strains
            to use in fitting of TOEC and SOEC
        stresses (nx3x3 array-like): Array of 3x3 stresses
            to use in fitting of TOEC and SOEC.  These
            should be PK2 stresses.
        eq_stress (3x3 array-like): stress corresponding to
            equilibrium strain (i. e. "0" strain state).
            If not specified, function will try to find
            the state in the list of provided stresses
            and strains.  If not found, defaults to 0.
        zero_crit (float): value for which strains below
            are ignored in identifying strain states.
    """

    if len(stresses) != len(strains):
        raise ValueError("Length of strains and stresses are not equivalent")
    vstresses = np.array([Stress(stress).voigt for stress in stresses])
    vstrains = np.array([Strain(strain).voigt for strain in strains])
    vstrains[np.abs(vstrains) < zero_crit] = 0

    # Try to find eq_stress if not specified
    if eq_stress is not None:
        veq_stress = Stress(eq_stress).voigt
    else:
        veq_stress = vstresses[np.all(vstrains==0, axis=1)]
        if veq_stress:
            if np.shape(veq_stress) > 1 and not \
               (abs(veq_stress - veq_stress[0]) < 1e-8).all():
                raise ValueError("Multiple stresses found for equilibrium strain"
                                 " state, please specify equilibrium stress or  "
                                 " remove extraneous stresses.")
            veq_stress = veq_stress[0]
        else:
            veq_stress = np.zeros(6)

    # Collect independent strain states:
    independent = set([tuple(np.nonzero(vstrain)[0].tolist())
                       for vstrain in vstrains])
    
    strain_states = []
    dsde = np.zeros((6, len(independent)))
    d2sde2 = np.zeros((6, len(independent)))
    for n, ind in enumerate(independent):
        # match strains with templates
        template = np.zeros(6, dtype=bool)
        np.put(template, ind, True)
        template = np.tile(template, [vstresses.shape[0], 1])
        mode = (template == (np.abs(vstrains) > 1e-10)).all(axis=1)
        mstresses = vstresses[mode]
        mstrains = vstrains[mode]
        # add zero strain state
        mstrains = np.vstack([mstrains, np.zeros(6)])
        mstresses = np.vstack([mstresses, np.zeros(6)])
        # sort strains/stresses by strain values
        mstresses = mstresses[mstrains[:, ind[0]].argsort()]
        mstrains = mstrains[mstrains[:, ind[0]].argsort()]
        strain_states.append(mstrains[-1] / \
                             np.min(mstrains[-1][np.nonzero(mstrains[0])]))
        diff = np.diff(mstrains, axis=0)
        if not (abs(diff - diff[0]) < 1e-8).all():
            raise ValueError("Stencil for strain state {} must be odd-sampling"
                             " centered at 0.".format(ind))
        h = np.min(diff[np.nonzero(diff)])
        coef1 = central_diff_weights(len(mstresses), 1)
        coef2 = central_diff_weights(len(mstresses), 2)
        if eq_stress is not None:
            mstresses[len(mstresses) // 2] = veq_stress
        dsde[:, n] = np.dot(np.transpose(mstresses), coef1) / h
        d2sde2[:, n] = np.dot(np.transpose(mstresses), coef2) / h**2

    m2i, m3i = generate_pseudo(strain_states)
    s2vec = np.ravel(dsde.T)
    c2vec = np.dot(m2i, s2vec)
    c2 = np.zeros((6, 6))
    c2[np.triu_indices(6)] = c2vec
    c2 = c2 + c2.T - np.diag(np.diag(c2))
    c3 = np.zeros((6, 6, 6))
    s3vec = np.ravel(d2sde2.T)
    c3vec = np.dot(m3i, s3vec)
    list_indices = list(itertools.combinations_with_replacement(range(6), r=3))
    indices_ij = itertools.combinations_with_replacement(range(6), r=3)

    indices = list(itertools.combinations_with_replacement(range(6), r=3))
    for n, (i, j, k) in enumerate(indices):
        c3[i,j,k] = c3[i,k,j] = c3[j,i,k] = c3[j,k,i] = \
                c3[k,i,j] = c3[k,j,i] = c3vec[n]
    return TensorBase.from_voigt(c2), TensorBase.from_voigt(c3)
Exemple #15
0
 def test_convert_to_ieee(self):
     for xtal in self.ieee_data.keys():
         orig = TensorBase(self.ieee_data[xtal]['original_tensor'])
         ieee = TensorBase(self.ieee_data[xtal]['ieee_tensor'])
         struct = Structure.from_dict(self.ieee_data[xtal]['structure'])
         self.assertArrayAlmostEqual(ieee, orig.convert_to_ieee(struct))