Пример #1
0
    def __new__(cls, strain_matrix, dfm=None):
        """
        Create a Strain object.  Note that the constructor uses __new__
        rather than __init__ according to the standard method of
        subclassing numpy ndarrays.  Note also that the default constructor
        does not include the deformation gradient

        Args:
            strain_matrix (3x3 array-like): the 3x3 array-like
                representing the Green-Lagrange strain
        """

        obj = SquareTensor(strain_matrix).view(cls)
        obj._dfm = dfm
        if not obj.is_symmetric():
            raise ValueError("Strain objects must be initialized "
                             "with a symmetric array-like.")

        if dfm is None:
            warnings.warn(
                "Constructing a strain object without a deformation "
                "matrix makes many methods unusable.  Use "
                "Strain.from_deformation to construct a Strain object"
                " from a deformation gradient.")
        elif (np.array(dfm) - obj < 1e-5).all():
            warnings.warn("Warning: deformation matrix does not correspond "
                          "to input strain_matrix value")
        return obj
Пример #2
0
    def __new__(cls, strain_matrix, dfm=None):
        """
        Create a Strain object.  Note that the constructor uses __new__
        rather than __init__ according to the standard method of
        subclassing numpy ndarrays.  Note also that the default constructor
        does not include the deformation gradient

        Args:
            strain_matrix (3x3 array-like): the 3x3 array-like
                representing the Green-Lagrange strain
        """

        obj = SquareTensor(strain_matrix).view(cls)
        obj._dfm = dfm
        if not obj.is_symmetric():
            raise ValueError("Strain objects must be initialized "
                             "with a symmetric array-like.")

        if dfm is None:
            warnings.warn("Constructing a strain object without a deformation "
                          "matrix makes many methods unusable.  Use "
                          "Strain.from_deformation to construct a Strain object"
                          " from a deformation gradient.")
        elif (np.array(dfm) - obj < 1e-5).all():
            warnings.warn("Warning: deformation matrix does not correspond "
                          "to input strain_matrix value")
        return obj
Пример #3
0
    def thermal_expansion_coeff(self, structure, temperature, mode="debye"):
        """
        Gets thermal expansion coefficient from third-order constants.

        Args:
            temperature (float): Temperature in kelvin, if not specified
                will return non-cv-normalized value
            structure (Structure): Structure to be used in directional heat
                capacity determination, only necessary if temperature
                is specified
            mode (string): mode for finding average heat-capacity,
                current supported modes are 'debye' and 'dulong-petit'
        """
        soec = ElasticTensor(self[0])
        v0 = (structure.volume * 1e-30 / structure.num_sites)
        if mode == "debye":
            td = soec.debye_temperature(structure)
            t_ratio = temperature / td
            integrand = lambda x: (x**4 * np.exp(x)) / (np.exp(x) - 1)**2
            cv = 9 * 8.314 * t_ratio**3 * quad(integrand, 0, t_ratio**-1)[0]
        elif mode == "dulong-petit":
            cv = 3 * 8.314
        else:
            raise ValueError("Mode must be debye or dulong-petit")
        tgt = self.get_tgt(temperature, structure)
        alpha = np.einsum('ijkl,ij', soec.compliance_tensor, tgt)
        alpha *= cv / (1e9 * v0 * 6.022e23)
        return SquareTensor(alpha)
Пример #4
0
def convert_strain_to_deformation(strain):
    strain = SquareTensor(strain)
    ftdotf = 2 * strain + np.eye(3)
    eigs, eigvecs = np.linalg.eigh(ftdotf)
    rotated = ftdotf.rotate(np.transpose(eigvecs))
    rotated = rotated.round(10)
    defo = Deformation(np.sqrt(rotated))
    result = defo.rotate(eigvecs)
    return result
Пример #5
0
 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)
Пример #6
0
    def test_properties(self):
        # transpose
        self.assertArrayEqual(self.non_symm.trans, 
                              SquareTensor([[0.1, 0.4, 0.2],
                                            [0.2, 0.5, 0.5],
                                            [0.3, 0.6, 0.5]]))
        self.assertArrayEqual(self.rand_sqtensor.trans,
                              np.transpose(self.rand_sqtensor))
        self.assertArrayEqual(self.symm_sqtensor,
                              self.symm_sqtensor.trans)
        # inverse
        self.assertArrayEqual(self.non_symm.inv,
                              np.linalg.inv(self.non_symm))
        with self.assertRaises(ValueError):
            self.non_invertible.inv

        # determinant
        self.assertEqual(self.rand_sqtensor.det,
                         np.linalg.det(self.rand_sqtensor))
        self.assertEqual(self.non_invertible.det,
                         0.0)
        self.assertEqual(self.non_symm.det, 0.009)

        # symmetrized
        self.assertArrayEqual(self.rand_sqtensor.symmetrized,
                              0.5 * (self.rand_sqtensor + self.rand_sqtensor.trans))
        self.assertArrayEqual(self.symm_sqtensor,
                              self.symm_sqtensor.symmetrized)
        self.assertArrayAlmostEqual(self.non_symm.symmetrized,
                                    SquareTensor([[0.1, 0.3, 0.25],
                                                  [0.3, 0.5, 0.55],
                                                  [0.25, 0.55, 0.5]]))

        # invariants
        i1 = np.trace(self.rand_sqtensor)
        i2 = self.rand_sqtensor[0, 0] * self.rand_sqtensor[1, 1] + \
             self.rand_sqtensor[1, 1] * self.rand_sqtensor[2, 2] + \
             self.rand_sqtensor[2, 2] * self.rand_sqtensor[0, 0] - \
             self.rand_sqtensor[0, 1] * self.rand_sqtensor[1, 0] - \
             self.rand_sqtensor[0, 2] * self.rand_sqtensor[2, 0] - \
             self.rand_sqtensor[2, 1] * self.rand_sqtensor[1, 2]
        i3 = np.linalg.det(self.rand_sqtensor)
        self.assertArrayAlmostEqual([i1, i2, i3],
                                    self.rand_sqtensor.principal_invariants)
Пример #7
0
def generate_elastic_workflow(structure, tags=None):
    """
    Generates a standard production workflow.

    Notes:
        Uses a primitive structure transformed into
        the conventional basis (for equivalent deformations).

        Adds the "minimal" category to the minimal portion
        of the workflow necessary to generate the elastic tensor,
        and the "minimal_full_stencil" category to the portion that
        includes all of the strain stencil, but is symmetrically complete
    """
    if tags == None:
        tags = []
    # transform the structure
    ieee_rot = Tensor.get_ieee_rotation(structure)
    if not SquareTensor(ieee_rot).is_rotation(tol=0.005):
        raise ValueError("Rotation matrix does not satisfy rotation conditions")
    symm_op = SymmOp.from_rotation_and_translation(ieee_rot)
    ieee_structure = structure.copy()
    ieee_structure.apply_operation(symm_op)

    # construct workflow
    wf = wf_elastic_constant(ieee_structure)

    # Set categories, starting with optimization
    opt_fws = get_fws_and_tasks(wf, fw_name_constraint="optimization")
    wf.fws[opt_fws[0][0]].spec['elastic_category'] = "minimal"

    # find minimal set of fireworks using symmetry reduction
    fws_by_strain = {Strain(fw.tasks[-1]['pass_dict']['strain']): n
                     for n, fw in enumerate(wf.fws) if 'deformation' in fw.name}
    unique_tensors = symmetry_reduce(list(fws_by_strain.keys()), ieee_structure)
    for unique_tensor in unique_tensors:
        fw_index = get_tkd_value(fws_by_strain, unique_tensor)
        if np.isclose(unique_tensor, 0.005).any():
            wf.fws[fw_index].spec['elastic_category'] = "minimal"
        else:
            wf.fws[fw_index].spec['elastic_category'] = "minimal_full_stencil"

    # Add tags
    if tags:
        wf = add_tags(wf, tags)

    wf = add_modify_incar(wf)
    priority = 500 - structure.num_sites
    wf = add_priority(wf, priority)
    for fw in wf.fws:
        if fw.spec.get('elastic_category') == 'minimal':
            fw.spec['_priority'] += 2000
        elif fw.spec.get('elastic_category') == 'minimal_full_stencil':
            fw.spec['_priority'] += 1000
    return wf
Пример #8
0
    def piola_kirchoff_1(self, def_grad):
        """
        calculates the first Piola-Kirchoff stress

        Args:
            def_grad (3x3 array-like): deformation gradient tensor
        """
        if not self.is_symmetric:
            raise ValueError("The stress tensor is not symmetric, \
                             PK stress is based on a symmetric stress tensor.")
        def_grad = SquareTensor(def_grad)
        return def_grad.det * np.dot(self, def_grad.inv.trans)
Пример #9
0
    def __new__(cls, stress_matrix):
        """
        Create a Stress object.  Note that the constructor uses __new__
        rather than __init__ according to the standard method of
        subclassing numpy ndarrays.

        Args:
            stress_matrix (3x3 array-like): the 3x3 array-like
                representing the stress
        """
        obj = SquareTensor(stress_matrix).view(cls)
        return obj
Пример #10
0
 def setUp(self):
     self.rand_sqtensor = SquareTensor(np.random.randn(3, 3))
     self.symm_sqtensor = SquareTensor([[0.1, 0.3, 0.4],
                                        [0.3, 0.5, 0.2],
                                        [0.4, 0.2, 0.6]])
     self.non_invertible = SquareTensor([[0.1, 0, 0],
                                         [0.2, 0, 0],
                                         [0, 0, 0]])
     self.non_symm = SquareTensor([[0.1, 0.2, 0.3],
                                   [0.4, 0.5, 0.6],
                                   [0.2, 0.5, 0.5]])
     self.low_val = SquareTensor([[1e-6, 1 + 1e-5, 1e-6],
                                  [1 + 1e-6, 1e-6, 1e-6],
                                  [1e-7, 1e-7, 1 + 1e-5]])
     self.low_val_2 = SquareTensor([[1e-6, -1 - 1e-6, 1e-6],
                                    [1 + 1e-7, 1e-6, 1e-6],
                                    [1e-7, 1e-7, 1 + 1e-6]])
     a = 3.14 * 42.5 / 180
     self.rotation = SquareTensor([[math.cos(a), 0, math.sin(a)],
                                   [0, 1, 0],
                                   [-math.sin(a), 0, math.cos(a)]])
Пример #11
0
def convert_strain_to_deformation(strain, tol=1e-5):
    strain = SquareTensor(strain)
    ftdotf = 2 * strain + np.eye(3)
    eigs, eigvecs = np.linalg.eig(ftdotf)
    rotated = ftdotf.rotate(np.transpose(eigvecs))
    rotated = rotated.round(10)
    defo = Deformation(np.sqrt(rotated))
    result = defo.rotate(eigvecs)
    rd = np.abs(strain - 0.5 *
                (np.dot(np.transpose(result), result) - np.eye(3)))
    assert (rd < tol).all(), "Strain-generated deformation is not valid!"
    return result
Пример #12
0
    def __new__(cls, deformation_gradient):
        """
        Create a Deformation object.  Note that the constructor uses __new__
        rather than __init__ according to the standard method of subclassing
        numpy ndarrays.

        Args:
            deformation_gradient (3x3 array-like): the 3x3 array-like
                representing the deformation gradient
        """

        obj = SquareTensor(deformation_gradient).view(cls)
        return obj
Пример #13
0
    def piola_kirchoff_2(self, def_grad):
        """
        calculates the second Piola-Kirchoff stress

        Args:
            def_grad (3x3 array-like): rate of deformation tensor
        """

        def_grad = SquareTensor(def_grad)
        if not self.is_symmetric:
            raise ValueError("The stress tensor is not symmetric, \
                             PK stress is based on a symmetric stress tensor.")
        return def_grad.det * np.dot(np.dot(def_grad.inv, self),
                                     def_grad.inv.trans)
Пример #14
0
def convert_strain_to_deformation(strain):
    """
    This function converts a strain to a deformation gradient that will
    produce that strain
    
    Args:
        strain (3x3 array-like): strain matrix
    """
    strain = SquareTensor(strain)
    ftdotf = 2*strain + np.eye(3)
    eigs, eigvecs = np.linalg.eigh(ftdotf)
    rotated = ftdotf.rotate(np.transpose(eigvecs))
    rotated = rotated.round(10)
    defo = Deformation(np.sqrt(rotated))
    result = defo.rotate(eigvecs)
    return result
Пример #15
0
    def get_tgt(self, temperature=None, structure=None, quad=None):
        """
        Gets the thermodynamic Gruneisen tensor (TGT) by via an
        integration of the GGT weighted by the directional heat
        capacity.

        See refs:
            R. N. Thurston and K. Brugger, Phys. Rev. 113, A1604 (1964).
            K. Brugger Phys. Rev. 137, A1826 (1965).

        Args:
            temperature (float): Temperature in kelvin, if not specified
                will return non-cv-normalized value
            structure (float): Structure to be used in directional heat
                capacity determination, only necessary if temperature
                is specified
            quad (dict): quadrature for integration, should be
                dictionary with "points" and "weights" keys defaults
                to quadpy.sphere.Lebedev(19) as read from file
        """
        if temperature and not structure:
            raise ValueError("If using temperature input, you must also "
                             "include structure")

        if not quad:
            quad = loadfn(
                os.path.join(os.path.dirname(__file__), "quad_data.json"))
        points = quad['points']
        weights = quad['weights']
        num, denom, c = np.zeros((3, 3)), 0, 1
        for p, w in zip(points, weights):
            gk = ElasticTensor(self[0]).green_kristoffel(p)
            rho_wsquareds, us = np.linalg.eigh(gk)
            us = [u / np.linalg.norm(u) for u in np.transpose(us)]
            for u in us:
                # TODO: this should be benchmarked
                if temperature:
                    c = self.get_heat_capacity(temperature, structure, p, u)
                num += c * self.get_ggt(p, u) * w
                denom += c * w
        return SquareTensor(num / denom)
Пример #16
0
def convert_strain_to_deformation(strain, shape="upper"):
    """
    This function converts a strain to a deformation gradient that will
    produce that strain.  Supports three methods:

    Args:
        strain (3x3 array-like): strain matrix
        shape: (string): method for determining deformation, supports
            "upper" produces an upper triangular defo
            "lower" produces a lower triangular defo
            "symmetric" produces a symmetric defo
    """
    strain = SquareTensor(strain)
    ftdotf = 2 * strain + np.eye(3)
    if shape == "upper":
        result = scipy.linalg.cholesky(ftdotf)
    elif shape == "symmetric":
        result = scipy.linalg.sqrtm(ftdotf)
    else:
        raise ValueError("shape must be \"upper\" or \"symmetric\"")
    return Deformation(result)
Пример #17
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)
Пример #18
0
 def setUp(self):
     self.rand_sqtensor = SquareTensor(np.random.randn(3, 3))
     self.symm_sqtensor = SquareTensor([[0.1, 0.3, 0.4], [0.3, 0.5, 0.2],
                                        [0.4, 0.2, 0.6]])
     self.non_invertible = SquareTensor([[0.1, 0, 0], [0.2, 0, 0],
                                         [0, 0, 0]])
     self.non_symm = SquareTensor([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6],
                                   [0.2, 0.5, 0.5]])
     self.low_val = SquareTensor([[1e-6, 1 + 1e-5, 1e-6],
                                  [1 + 1e-6, 1e-6, 1e-6],
                                  [1e-7, 1e-7, 1 + 1e-5]])
     self.low_val_2 = SquareTensor([[1e-6, -1 - 1e-6, 1e-6],
                                    [1 + 1e-7, 1e-6, 1e-6],
                                    [1e-7, 1e-7, 1 + 1e-6]])
     a = 3.14 * 42.5 / 180
     self.rotation = SquareTensor([[math.cos(a), 0,
                                    math.sin(a)], [0, 1, 0],
                                   [-math.sin(a), 0,
                                    math.cos(a)]])
Пример #19
0
 def test_get_scaled(self):
     self.assertArrayEqual(self.non_symm.get_scaled(10.),
                           SquareTensor([[1, 2, 3], [4, 5, 6], [2, 5, 5]]))
Пример #20
0
class SquareTensorTest(PymatgenTest):
    def setUp(self):
        self.rand_sqtensor = SquareTensor(np.random.randn(3, 3))
        self.symm_sqtensor = SquareTensor([[0.1, 0.3, 0.4], [0.3, 0.5, 0.2],
                                           [0.4, 0.2, 0.6]])
        self.non_invertible = SquareTensor([[0.1, 0, 0], [0.2, 0, 0],
                                            [0, 0, 0]])
        self.non_symm = SquareTensor([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6],
                                      [0.2, 0.5, 0.5]])
        self.low_val = SquareTensor([[1e-6, 1 + 1e-5, 1e-6],
                                     [1 + 1e-6, 1e-6, 1e-6],
                                     [1e-7, 1e-7, 1 + 1e-5]])
        self.low_val_2 = SquareTensor([[1e-6, -1 - 1e-6, 1e-6],
                                       [1 + 1e-7, 1e-6, 1e-6],
                                       [1e-7, 1e-7, 1 + 1e-6]])
        a = 3.14 * 42.5 / 180
        self.rotation = SquareTensor([[math.cos(a), 0,
                                       math.sin(a)], [0, 1, 0],
                                      [-math.sin(a), 0,
                                       math.cos(a)]])

    def test_new(self):
        non_sq_matrix = [[0.1, 0.2, 0.1], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3],
                         [0.1, 0.1, 0.1]]
        bad_matrix = [[0.1, 0.2], [0.2, 0.3, 0.4], [0.2, 0.3, 0.5]]
        too_high_rank = np.zeros((3, 3, 3))
        self.assertRaises(ValueError, SquareTensor, non_sq_matrix)
        self.assertRaises(ValueError, SquareTensor, bad_matrix)
        self.assertRaises(ValueError, SquareTensor, too_high_rank)

    def test_properties(self):
        # transpose
        self.assertArrayEqual(
            self.non_symm.trans,
            SquareTensor([[0.1, 0.4, 0.2], [0.2, 0.5, 0.5], [0.3, 0.6, 0.5]]))
        self.assertArrayEqual(self.rand_sqtensor.trans,
                              np.transpose(self.rand_sqtensor))
        self.assertArrayEqual(self.symm_sqtensor, self.symm_sqtensor.trans)
        # inverse
        self.assertArrayEqual(self.non_symm.inv, np.linalg.inv(self.non_symm))
        with self.assertRaises(ValueError):
            self.non_invertible.inv

        # determinant
        self.assertEqual(self.rand_sqtensor.det,
                         np.linalg.det(self.rand_sqtensor))
        self.assertEqual(self.non_invertible.det, 0.0)
        self.assertEqual(self.non_symm.det, 0.009)

        # symmetrized
        self.assertArrayEqual(
            self.rand_sqtensor.symmetrized,
            0.5 * (self.rand_sqtensor + self.rand_sqtensor.trans))
        self.assertArrayEqual(self.symm_sqtensor,
                              self.symm_sqtensor.symmetrized)
        self.assertArrayAlmostEqual(
            self.non_symm.symmetrized,
            SquareTensor([[0.1, 0.3, 0.25], [0.3, 0.5, 0.55],
                          [0.25, 0.55, 0.5]]))

        # invariants
        i1 = np.trace(self.rand_sqtensor)
        i2 = self.rand_sqtensor[0, 0] * self.rand_sqtensor[1, 1] + \
             self.rand_sqtensor[1, 1] * self.rand_sqtensor[2, 2] + \
             self.rand_sqtensor[2, 2] * self.rand_sqtensor[0, 0] - \
             self.rand_sqtensor[0, 1] * self.rand_sqtensor[1, 0] - \
             self.rand_sqtensor[0, 2] * self.rand_sqtensor[2, 0] - \
             self.rand_sqtensor[2, 1] * self.rand_sqtensor[1, 2]
        i3 = np.linalg.det(self.rand_sqtensor)
        self.assertArrayAlmostEqual([i1, i2, i3],
                                    self.rand_sqtensor.principal_invariants)

    def test_is_rotation(self):
        self.assertTrue(self.rotation.is_rotation())
        self.assertFalse(self.symm_sqtensor.is_rotation())
        self.assertTrue(self.low_val_2.is_rotation())
        self.assertFalse(self.low_val_2.is_rotation(tol=1e-8))

    def test_get_scaled(self):
        self.assertArrayEqual(self.non_symm.get_scaled(10.),
                              SquareTensor([[1, 2, 3], [4, 5, 6], [2, 5, 5]]))

    def test_polar_decomposition(self):
        u, p = self.rand_sqtensor.polar_decomposition()
        self.assertArrayAlmostEqual(np.dot(u, p), self.rand_sqtensor)
        self.assertArrayAlmostEqual(np.eye(3),
                                    np.dot(u, np.conjugate(np.transpose(u))))
Пример #21
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)
Пример #22
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)
Пример #23
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)
Пример #24
0
class SquareTensorTest(PymatgenTest):
    def setUp(self):
        self.rand_sqtensor = SquareTensor(np.random.randn(3, 3))
        self.symm_sqtensor = SquareTensor([[0.1, 0.3, 0.4],
                                           [0.3, 0.5, 0.2],
                                           [0.4, 0.2, 0.6]])
        self.non_invertible = SquareTensor([[0.1, 0, 0],
                                            [0.2, 0, 0],
                                            [0, 0, 0]])
        self.non_symm = SquareTensor([[0.1, 0.2, 0.3],
                                      [0.4, 0.5, 0.6],
                                      [0.2, 0.5, 0.5]])
        self.low_val = SquareTensor([[1e-6, 1 + 1e-5, 1e-6],
                                     [1 + 1e-6, 1e-6, 1e-6],
                                     [1e-7, 1e-7, 1 + 1e-5]])
        self.low_val_2 = SquareTensor([[1e-6, -1 - 1e-6, 1e-6],
                                       [1 + 1e-7, 1e-6, 1e-6],
                                       [1e-7, 1e-7, 1 + 1e-6]])
        a = 3.14 * 42.5 / 180
        self.rotation = SquareTensor([[math.cos(a), 0, math.sin(a)],
                                      [0, 1, 0],
                                      [-math.sin(a), 0, math.cos(a)]])
        
    def test_new(self):
        non_sq_matrix = [[0.1, 0.2, 0.1],
                         [0.1, 0.2, 0.3],
                         [0.1, 0.2, 0.3],
                         [0.1, 0.1, 0.1]]
        bad_matrix = [[0.1, 0.2],
                      [0.2, 0.3, 0.4],
                      [0.2, 0.3, 0.5]]
        too_high_rank = np.zeros((3,3,3))
        self.assertRaises(ValueError, SquareTensor, non_sq_matrix)
        self.assertRaises(ValueError, SquareTensor, bad_matrix)
        self.assertRaises(ValueError, SquareTensor, too_high_rank)

    def test_properties(self):
        # transpose
        self.assertArrayEqual(self.non_symm.trans, 
                              SquareTensor([[0.1, 0.4, 0.2],
                                            [0.2, 0.5, 0.5],
                                            [0.3, 0.6, 0.5]]))
        self.assertArrayEqual(self.rand_sqtensor.trans,
                              np.transpose(self.rand_sqtensor))
        self.assertArrayEqual(self.symm_sqtensor,
                              self.symm_sqtensor.trans)
        # inverse
        self.assertArrayEqual(self.non_symm.inv,
                              np.linalg.inv(self.non_symm))
        with self.assertRaises(ValueError):
            self.non_invertible.inv

        # determinant
        self.assertEqual(self.rand_sqtensor.det,
                         np.linalg.det(self.rand_sqtensor))
        self.assertEqual(self.non_invertible.det,
                         0.0)
        self.assertEqual(self.non_symm.det, 0.009)

        # symmetrized
        self.assertArrayEqual(self.rand_sqtensor.symmetrized,
                              0.5 * (self.rand_sqtensor + self.rand_sqtensor.trans))
        self.assertArrayEqual(self.symm_sqtensor,
                              self.symm_sqtensor.symmetrized)
        self.assertArrayAlmostEqual(self.non_symm.symmetrized,
                                    SquareTensor([[0.1, 0.3, 0.25],
                                                  [0.3, 0.5, 0.55],
                                                  [0.25, 0.55, 0.5]]))

        # invariants
        i1 = np.trace(self.rand_sqtensor)
        i2 = self.rand_sqtensor[0, 0] * self.rand_sqtensor[1, 1] + \
             self.rand_sqtensor[1, 1] * self.rand_sqtensor[2, 2] + \
             self.rand_sqtensor[2, 2] * self.rand_sqtensor[0, 0] - \
             self.rand_sqtensor[0, 1] * self.rand_sqtensor[1, 0] - \
             self.rand_sqtensor[0, 2] * self.rand_sqtensor[2, 0] - \
             self.rand_sqtensor[2, 1] * self.rand_sqtensor[1, 2]
        i3 = np.linalg.det(self.rand_sqtensor)
        self.assertArrayAlmostEqual([i1, i2, i3],
                                    self.rand_sqtensor.principal_invariants)

    def test_is_rotation(self):
        self.assertTrue(self.rotation.is_rotation())
        self.assertFalse(self.symm_sqtensor.is_rotation())
        self.assertTrue(self.low_val_2.is_rotation())
        self.assertFalse(self.low_val_2.is_rotation(tol=1e-8))

    def test_get_scaled(self):
        self.assertArrayEqual(self.non_symm.get_scaled(10.),
                              SquareTensor([[1, 2, 3], [4, 5, 6], [2, 5, 5]]))

    def test_polar_decomposition(self):
        u, p = self.rand_sqtensor.polar_decomposition()
        self.assertArrayAlmostEqual(np.dot(u, p), self.rand_sqtensor)
        self.assertArrayAlmostEqual(np.eye(3),
                                    np.dot(u, np.conjugate(np.transpose(u))))