def get_rand_IST(self, max_force=1):
        """
        Generate a random internal strain tensor which obeys a structure's
        symmetry and the acoustic sum rule

        Args:
            max_force(float): maximum born effective charge value

        Return:
            InternalStrainTensor object
        """

        l = len(self.structure)
        IST = np.zeros((l, 3, 3, 3))
        for atom, ops in enumerate(self.IST_operations):
            temp_tensor = np.zeros([3, 3, 3])
            for op in ops:
                temp_tensor += op[1].transform_tensor(IST[op[0]])

            if len(ops) == 0:
                temp_tensor = Tensor(np.random.rand(3, 3, 3) - 0.5)
                for dim in range(3):
                    temp_tensor[dim] = (temp_tensor[dim] +
                                        temp_tensor[dim].T) / 2
                temp_tensor = sum([
                    temp_tensor.transform(symm_op)
                    for symm_op in self.pointops[atom]
                ]) / len(self.pointops[atom])
            IST[atom] = temp_tensor
            if len(ops) != 0:
                IST[atom] = IST[atom] / len(ops)

        return IST * max_force
Beispiel #2
0
 def setUp(self):
     with open(os.path.join(test_dir, 'test_toec_data.json')) as f:
         self.data_dict = json.load(f)
     self.strains = [Strain(sm) for sm in self.data_dict['strains']]
     self.pk_stresses = [Stress(d) for d in self.data_dict['pk_stresses']]
     self.c2 = self.data_dict["C2_raw"]
     self.c3 = self.data_dict["C3_raw"]
     self.exp = ElasticTensorExpansion.from_voigt([self.c2, self.c3])
     self.cu = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.623),
                                         ["Cu"], [[0] * 3])
     indices = [(0, 0), (0, 1), (3, 3)]
     values = [167.8, 113.5, 74.5]
     cu_c2 = ElasticTensor.from_values_indices(values,
                                               indices,
                                               structure=self.cu,
                                               populate=True)
     indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2), (0, 3, 3), (0, 5, 5),
                (3, 4, 5)]
     values = [-1507., -965., -71., -7., -901., 45.]
     cu_c3 = Tensor.from_values_indices(values,
                                        indices,
                                        structure=self.cu,
                                        populate=True)
     self.exp_cu = ElasticTensorExpansion([cu_c2, cu_c3])
     cu_c4 = Tensor.from_voigt(self.data_dict["Cu_fourth_order"])
     self.exp_cu_4 = ElasticTensorExpansion([cu_c2, cu_c3, cu_c4])
     warnings.simplefilter("ignore")
Beispiel #3
0
 def test_einsum_sequence(self):
     x = [1, 0, 0]
     test = Tensor(np.arange(0, 3**4).reshape((3, 3, 3, 3)))
     self.assertArrayAlmostEqual([0, 27, 54], test.einsum_sequence([x] * 3))
     self.assertEqual(360, test.einsum_sequence([np.eye(3)] * 2))
     self.assertRaises(ValueError, test.einsum_sequence,
                       Tensor(np.zeros(3)))
Beispiel #4
0
    def test_transform(self):
        # Rank 3
        tensor = Tensor(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,
        )
Beispiel #5
0
    def test_serialization(self):
        # Test base serialize-deserialize
        d = self.symm_rank2.as_dict()
        new = Tensor.from_dict(d)
        self.assertArrayAlmostEqual(new, self.symm_rank2)

        d = self.symm_rank3.as_dict(voigt=True)
        new = Tensor.from_dict(d)
        self.assertArrayAlmostEqual(new, self.symm_rank3)
Beispiel #6
0
 def test_convert_strain_to_deformation(self):
     strain = Tensor(np.random.random((3, 3))).symmetrized
     while not (np.linalg.eigvals(strain) > 0).all():
         strain = Tensor(np.random.random((3, 3))).symmetrized
     upper = convert_strain_to_deformation(strain, shape="upper")
     symm = convert_strain_to_deformation(strain, shape="symmetric")
     self.assertArrayAlmostEqual(np.triu(upper), upper)
     self.assertTrue(Tensor(symm).is_symmetric())
     for defo in upper, symm:
         self.assertArrayAlmostEqual(defo.green_lagrange_strain, strain)
Beispiel #7
0
    def ff(prop):
        # format tensor properties
        if isinstance(prop, np.ndarray):
            if prop.shape == (3, 3):
                return _tensor_str.format(*prop.ravel())
            elif prop.shape == (3, 3, 3):
                return _piezo_tensor_str.format(*Tensor(prop).voigt.ravel())
            elif prop.shape == (3, 3, 3, 3):
                return _elastic_tensor_str.format(*Tensor(prop).voigt.ravel())

        return prop
Beispiel #8
0
 def test_tensor_mapping(self):
     # Test get
     tbs = [Tensor.from_voigt(row) for row in np.eye(6) * 0.01]
     reduced = symmetry_reduce(tbs, self.get_structure("Sn"))
     tkey = Tensor.from_values_indices([0.01], [(0, 0)])
     tval = reduced[tkey]
     for tens_1, tens_2 in zip(tval, reduced[tbs[0]]):
         self.assertAlmostEqual(tens_1, tens_2)
     # Test set
     reduced[tkey] = "test_val"
     self.assertEqual(reduced[tkey], "test_val")
     # Test empty initialization
     empty = TensorMapping()
     self.assertEqual(empty._tensor_list, [])
Beispiel #9
0
 def test_zeroed(self):
     self.assertArrayEqual(
         self.low_val.zeroed(),
         Tensor([[0, 1 + 1e-5, 0], [1 + 1e-6, 0, 0], [0, 0, 1 + 1e-5]]),
     )
     self.assertArrayEqual(
         self.low_val.zeroed(tol=1e-6),
         Tensor([[1e-6, 1 + 1e-5, 1e-6], [1 + 1e-6, 1e-6, 1e-6],
                 [0, 0, 1 + 1e-5]]),
     )
     self.assertArrayEqual(
         Tensor([[1e-6, -30, 1], [1e-7, 1, 0], [1e-8, 0, 1]]).zeroed(),
         Tensor([[0, -30, 1], [0, 1, 0], [0, 0, 1]]),
     )
Beispiel #10
0
def diff_fit(strains, stresses, eq_stress=None, order=2, tol=1e-10):
    """
    nth 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, second .. nth 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 nth-order elastic
       constants appropriately.

    Args:
        order (int): order of the elastic tensor set to return
        strains (nx3x3 array-like): Array of 3x3 strains
            to use in fitting of ECs
        stresses (nx3x3 array-like): Array of 3x3 stresses
            to use in fitting ECs.  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.
        tol (float): value for which strains below
            are ignored in identifying strain states.

    Returns:
        Set of tensors corresponding to nth order expansion of
        the stress/strain relation
    """
    strain_state_dict = get_strain_state_dict(
        strains, stresses, eq_stress=eq_stress, tol=tol,
        add_eq=True, sort=True)

    # Collect derivative data
    c_list = []
    dei_dsi = np.zeros((order - 1, 6, len(strain_state_dict)))
    for n, (strain_state, data) in enumerate(strain_state_dict.items()):
        hvec = data["strains"][:, strain_state.index(1)]
        for i in range(1, order):
            coef = get_diff_coeff(hvec, i)
            dei_dsi[i - 1, :, n] = np.dot(coef, data["stresses"])

    m, absent = generate_pseudo(list(strain_state_dict.keys()), order)
    for i in range(1, order):
        cvec, carr = get_symbol_list(i+1)
        svec = np.ravel(dei_dsi[i-1].T)
        cmap = dict(zip(cvec, np.dot(m[i-1], svec)))
        c_list.append(v_subs(carr, cmap))
    return [Tensor.from_voigt(c) for c in c_list]
Beispiel #11
0
def cast_elastic_tensor(
    elastic_tensor: Union[int, float, List[List[float]], np.ndarray]
) -> np.ndarray:
    """Cast elastic tensor from single value or Voigt to full 3x3x3x3 tensor.

    Args:
        elastic_tensor: A single number, 6x6 Voigt tensor, or 3x3x3x3 tensor.

    Returns:
        The elastic constant as a 3x3x3x3 tensor.
    """
    from pymatgen.core.tensors import Tensor

    from amset.constants import numeric_types

    if isinstance(elastic_tensor, numeric_types):
        elastic_tensor = np.eye(6) * elastic_tensor
        elastic_tensor[([3, 4, 5], [3, 4, 5])] /= 2

    elastic_tensor = np.array(elastic_tensor)
    if elastic_tensor.shape == (6, 6):
        elastic_tensor = Tensor.from_voigt(elastic_tensor)

    if elastic_tensor.shape != (3, 3, 3, 3):
        raise ValueError(
            "Unsupported elastic tensor shape. Should be (6, 6) or (3, 3, 3, 3)."
        )

    return np.array(elastic_tensor)
Beispiel #12
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
Beispiel #13
0
    def get_symmetric_wallace_tensor(self, tau):
        """
        Gets the symmetrized wallace tensor for determining
        yield strength criteria.

        Args:
            tau (3x3 array-like): stress at which to evaluate
                the wallace tensor.
        """
        wallace = self.get_wallace_tensor(tau)
        return Tensor(0.5 * (wallace + np.transpose(wallace, [2, 3, 0, 1])))
Beispiel #14
0
 def test_symmetry_reduce(self):
     tbs = [Tensor.from_voigt(row) for row in np.eye(6) * 0.01]
     reduced = symmetry_reduce(tbs, self.get_structure("Sn"))
     self.assertEqual(len(reduced), 2)
     self.assertArrayEqual([len(i) for i in reduced.values()], [2, 2])
     reconstructed = []
     for k, v in reduced.items():
         reconstructed.extend([k.voigt] +
                              [k.transform(op).voigt for op in v])
     reconstructed = sorted(reconstructed, key=lambda x: np.argmax(x))
     self.assertArrayAlmostEqual([tb for tb in reconstructed],
                                 np.eye(6) * 0.01)
Beispiel #15
0
    def test_populate(self):
        test_data = loadfn(
            os.path.join(PymatgenTest.TEST_FILES_DIR, "test_toec_data.json"))

        sn = self.get_structure("Sn")
        vtens = np.zeros((6, 6))
        vtens[0, 0] = 259.31
        vtens[0, 1] = 160.71
        vtens[3, 3] = 73.48
        et = Tensor.from_voigt(vtens)
        populated = et.populate(sn, prec=1e-3).voigt.round(2)
        self.assertAlmostEqual(populated[1, 1], 259.31)
        self.assertAlmostEqual(populated[2, 2], 259.31)
        self.assertAlmostEqual(populated[0, 2], 160.71)
        self.assertAlmostEqual(populated[1, 2], 160.71)
        self.assertAlmostEqual(populated[4, 4], 73.48)
        self.assertAlmostEqual(populated[5, 5], 73.48)
        # test a rank 6 example
        vtens = np.zeros([6] * 3)
        indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2), (0, 3, 3), (0, 5, 5),
                   (3, 4, 5)]
        values = [-1271.0, -814.0, -50.0, -3.0, -780.0, -95.0]
        for v, idx in zip(values, indices):
            vtens[idx] = v
        toec = Tensor.from_voigt(vtens)
        toec = toec.populate(sn, prec=1e-3, verbose=True)
        self.assertAlmostEqual(toec.voigt[1, 1, 1], -1271)
        self.assertAlmostEqual(toec.voigt[0, 1, 1], -814)
        self.assertAlmostEqual(toec.voigt[0, 2, 2], -814)
        self.assertAlmostEqual(toec.voigt[1, 4, 4], -3)
        self.assertAlmostEqual(toec.voigt[2, 5, 5], -3)
        self.assertAlmostEqual(toec.voigt[1, 2, 0], -50)
        self.assertAlmostEqual(toec.voigt[4, 5, 3], -95)

        et = Tensor.from_voigt(test_data["C3_raw"]).fit_to_structure(sn)
        new = np.zeros(et.voigt.shape)
        for idx in indices:
            new[idx] = et.voigt[idx]
        new = Tensor.from_voigt(new).populate(sn)
        self.assertArrayAlmostEqual(new, et, decimal=2)
Beispiel #16
0
    def get_rand_BEC(self, max_charge=1):
        """
        Generate a random born effective charge tensor which obeys a structure's
        symmetry and the acoustic sum rule

        Args:
            max_charge (float): maximum born effective charge value

        Return:
            np.array Born effective charge tensor
        """

        struc = self.structure
        symstruc = sga(struc)
        symstruc = symstruc.get_symmetrized_structure()

        l = len(struc)
        BEC = np.zeros((l, 3, 3))
        for atom, ops in enumerate(self.BEC_operations):
            if ops[0] == ops[1]:
                temp_tensor = Tensor(np.random.rand(3, 3) - 0.5)
                temp_tensor = sum(temp_tensor.transform(symm_op) for symm_op in self.pointops[atom]) / len(
                    self.pointops[atom]
                )
                BEC[atom] = temp_tensor
            else:
                tempfcm = np.zeros([3, 3])
                for op in ops[2]:

                    tempfcm += op.transform_tensor(BEC[self.BEC_operations[atom][1]])
                BEC[ops[0]] = tempfcm
                if len(ops[2]) != 0:
                    BEC[ops[0]] = BEC[ops[0]] / len(ops[2])

        #     Enforce Acoustic Sum
        disp_charge = np.einsum("ijk->jk", BEC) / l
        add = np.zeros([l, 3, 3])

        for atom, ops in enumerate(self.BEC_operations):

            if ops[0] == ops[1]:
                temp_tensor = Tensor(disp_charge)
                temp_tensor = sum(temp_tensor.transform(symm_op) for symm_op in self.pointops[atom]) / len(
                    self.pointops[atom]
                )
                add[ops[0]] = temp_tensor
            else:
                temp_tensor = np.zeros([3, 3])
                for op in ops[2]:

                    temp_tensor += op.transform_tensor(add[self.BEC_operations[atom][1]])

                add[ops[0]] = temp_tensor

                if len(ops) != 0:
                    add[ops[0]] = add[ops[0]] / len(ops[2])

        BEC = BEC - add

        return BEC * max_charge
Beispiel #17
0
 def test_from_values_indices(self):
     sn = self.get_structure("Sn")
     indices = [(0, 0), (0, 1), (3, 3)]
     values = [259.31, 160.71, 73.48]
     et = Tensor.from_values_indices(values,
                                     indices,
                                     structure=sn,
                                     populate=True).voigt.round(4)
     self.assertAlmostEqual(et[1, 1], 259.31)
     self.assertAlmostEqual(et[2, 2], 259.31)
     self.assertAlmostEqual(et[0, 2], 160.71)
     self.assertAlmostEqual(et[1, 2], 160.71)
     self.assertAlmostEqual(et[4, 4], 73.48)
     self.assertAlmostEqual(et[5, 5], 73.48)
 def setUp(self):
     with open(os.path.join(test_dir, 'test_toec_data.json')) as f:
         self.data_dict = json.load(f)
     self.strains = [Strain(sm) for sm in self.data_dict['strains']]
     self.pk_stresses = [Stress(d) for d in self.data_dict['pk_stresses']]
     self.c2 = self.data_dict["C2_raw"]
     self.c3 = self.data_dict["C3_raw"]
     self.exp = ElasticTensorExpansion.from_voigt([self.c2, self.c3])
     self.cu = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.623),
                                         ["Cu"], [[0]*3])
     indices = [(0, 0), (0, 1), (3, 3)]
     values = [167.8, 113.5, 74.5]
     cu_c2 = ElasticTensor.from_values_indices(values, indices, structure=self.cu,
                                               populate=True)
     indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2),
                (0, 3, 3), (0, 5, 5), (3, 4, 5)]
     values = [-1507., -965., -71., -7., -901., 45.]
     cu_c3 = Tensor.from_values_indices(values, indices, structure=self.cu,
                                        populate=True)
     self.exp_cu = ElasticTensorExpansion([cu_c2, cu_c3])
     cu_c4 = Tensor.from_voigt(self.data_dict["Cu_fourth_order"])
     self.exp_cu_4 = ElasticTensorExpansion([cu_c2, cu_c3, cu_c4])
     warnings.simplefilter("ignore")
Beispiel #19
0
 def test_from_voigt(self):
     with self.assertRaises(ValueError):
         Tensor.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
     Tensor.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],
     ])
     # Rank 3
     Tensor.from_voigt(np.zeros((3, 6)))
     # Rank 2
     Tensor.from_voigt(np.zeros(6))
     # Addresses occasional cast issues for integers
     Tensor.from_voigt(np.arange(6))
Beispiel #20
0
    def test_serialization(self):
        # Test base serialize-deserialize
        d = self.rand_sqtensor.as_dict()
        new = SquareTensor.from_dict(d)
        self.assertArrayAlmostEqual(new, self.rand_sqtensor)
        self.assertIsInstance(new, SquareTensor)

        # Ensure proper object-independent deserialization
        obj = MontyDecoder().process_decoded(d)
        self.assertIsInstance(obj, SquareTensor)

        with warnings.catch_warnings(record=True):
            vsym = self.rand_sqtensor.voigt_symmetrized
            d_vsym = vsym.as_dict(voigt=True)
            new_voigt = Tensor.from_dict(d_vsym)
            self.assertArrayAlmostEqual(vsym, new_voigt)
Beispiel #21
0
def cast_piezoelectric_tensor(
    piezoelectric_tensor: Union[np.ndarray, List[List[float]], np.ndarray]
) -> np.ndarray:
    """Cast piezoelectric tensor from Voigt form to full 3x3x3 tensor.

    Args:
        piezoelectric_tensor: A 3x6 Voigt tensor, or 3x3x3 tensor.

    Returns:
        The piezoelectric constant as a 3x3x3 tensor.
    """
    from pymatgen.core.tensors import Tensor

    piezoelectric_tensor = np.array(piezoelectric_tensor)
    if piezoelectric_tensor.shape == (3, 6):
        piezoelectric_tensor = Tensor.from_voigt(piezoelectric_tensor)

    if piezoelectric_tensor.shape != (3, 3, 3):
        raise ValueError(
            "Unsupported piezoelectric tensor shape. Should be (3, 6) or (3, 3, 3)."
        )

    return np.array(piezoelectric_tensor)
Beispiel #22
0
 def test_convert_to_ieee(self):
     for entry in self.ieee_data:
         xtal = entry["xtal"]
         struct = entry["structure"]
         orig = Tensor(entry["original_tensor"])
         ieee = Tensor(entry["ieee_tensor"])
         diff = np.max(abs(ieee - orig.convert_to_ieee(struct)))
         err_msg = f"{xtal} IEEE conversion failed with max diff {diff}. Numpy version: {np.__version__}"
         converted = orig.convert_to_ieee(struct, refine_rotation=False)
         self.assertArrayAlmostEqual(ieee,
                                     converted,
                                     err_msg=err_msg,
                                     decimal=3)
         converted_refined = orig.convert_to_ieee(struct,
                                                  refine_rotation=True)
         err_msg = "{} IEEE conversion with refinement failed with max diff {}. Numpy version: {}".format(
             xtal, diff, np.__version__)
         self.assertArrayAlmostEqual(ieee,
                                     converted_refined,
                                     err_msg=err_msg,
                                     decimal=2)
Beispiel #23
0
 def test_init(self):
     cijkl = Tensor.from_voigt(self.c2)
     cijklmn = Tensor.from_voigt(self.c3)
     exp = ElasticTensorExpansion([cijkl, cijklmn])
     from_voigt = ElasticTensorExpansion.from_voigt([self.c2, self.c3])
     self.assertEqual(exp.order, 3)
Beispiel #24
0
class TensorTest(PymatgenTest):
    _multiprocess_shared_ = True

    def setUp(self):
        self.vec = Tensor([1.0, 0.0, 0.0])
        self.rand_rank2 = Tensor(np.random.randn(3, 3))
        self.rand_rank3 = Tensor(np.random.randn(3, 3, 3))
        self.rand_rank4 = Tensor(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 = Tensor([[1e-6, 1 + 1e-5, 1e-6], [1 + 1e-6, 1e-6, 1e-6],
                               [1e-7, 1e-7, 1 + 1e-5]])
        self.symm_rank2 = Tensor([[1, 2, 3], [2, 4, 5], [3, 5, 6]])
        self.symm_rank3 = Tensor([
            [[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 = Tensor([
            [
                [[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]],
                [[-0.92, 0.11, -0.02], [0.11, 0.09, 0.0], [-0.02, 0.0, -0.3]],
            ],
            [
                [[0.4, 0.05, 0.11], [0.05, -0.47, 0.09], [0.11, 0.09, 0.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], [0.09, 0.62, 0.3], [0.0, 0.3, -0.18]],
            ],
            [
                [[-0.92, 0.11, -0.02], [0.11, 0.09, 0.0], [-0.02, 0, -0.3]],
                [[0.11, 0.09, 0.0], [0.09, 0.62, 0.3], [0.0, 0.3, -0.18]],
                [[-0.02, 0.0, -0.3], [0.0, 0.3, -0.18], [-0.3, -0.18, -0.51]],
            ],
        ])

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

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

        self.structure = self.get_structure("BaNiO3")
        ieee_file_path = os.path.join(PymatgenTest.TEST_FILES_DIR,
                                      "ieee_conversion_data.json")
        self.ones = Tensor(np.ones((3, 3)))
        self.ieee_data = loadfn(ieee_file_path)

    def test_new(self):
        bad_2 = np.zeros((4, 4))
        bad_3 = np.zeros((4, 4, 4))
        self.assertRaises(ValueError, Tensor, bad_2)
        self.assertRaises(ValueError, Tensor, 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(),
            Tensor([[0, 1 + 1e-5, 0], [1 + 1e-6, 0, 0], [0, 0, 1 + 1e-5]]),
        )
        self.assertArrayEqual(
            self.low_val.zeroed(tol=1e-6),
            Tensor([[1e-6, 1 + 1e-5, 1e-6], [1 + 1e-6, 1e-6, 1e-6],
                    [0, 0, 1 + 1e-5]]),
        )
        self.assertArrayEqual(
            Tensor([[1e-6, -30, 1], [1e-7, 1, 0], [1e-8, 0, 1]]).zeroed(),
            Tensor([[0, -30, 1], [0, 1, 0], [0, 0, 1]]),
        )

    def test_transform(self):
        # Rank 3
        tensor = Tensor(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_einsum_sequence(self):
        x = [1, 0, 0]
        test = Tensor(np.arange(0, 3**4).reshape((3, 3, 3, 3)))
        self.assertArrayAlmostEqual([0, 27, 54], test.einsum_sequence([x] * 3))
        self.assertEqual(360, test.einsum_sequence([np.eye(3)] * 2))
        self.assertRaises(ValueError, test.einsum_sequence,
                          Tensor(np.zeros(3)))

    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 entry in self.ieee_data:
            xtal = entry["xtal"]
            struct = entry["structure"]
            orig = Tensor(entry["original_tensor"])
            ieee = Tensor(entry["ieee_tensor"])
            diff = np.max(abs(ieee - orig.convert_to_ieee(struct)))
            err_msg = f"{xtal} IEEE conversion failed with max diff {diff}. Numpy version: {np.__version__}"
            converted = orig.convert_to_ieee(struct, refine_rotation=False)
            self.assertArrayAlmostEqual(ieee,
                                        converted,
                                        err_msg=err_msg,
                                        decimal=3)
            converted_refined = orig.convert_to_ieee(struct,
                                                     refine_rotation=True)
            err_msg = "{} IEEE conversion with refinement failed with max diff {}. Numpy version: {}".format(
                xtal, diff, np.__version__)
            self.assertArrayAlmostEqual(ieee,
                                        converted_refined,
                                        err_msg=err_msg,
                                        decimal=2)

    def test_structure_transform(self):
        # Test trivial case
        trivial = self.fit_r4.structure_transform(self.structure,
                                                  self.structure.copy())
        self.assertArrayAlmostEqual(trivial, self.fit_r4)

        # Test simple rotation
        rot_symm_op = SymmOp.from_axis_angle_and_translation([1, 1, 1], 55.5)
        rot_struct = self.structure.copy()
        rot_struct.apply_operation(rot_symm_op)
        rot_tensor = self.fit_r4.rotate(rot_symm_op.rotation_matrix)
        trans_tensor = self.fit_r4.structure_transform(self.structure,
                                                       rot_struct)
        self.assertArrayAlmostEqual(rot_tensor, trans_tensor)

        # Test supercell
        bigcell = self.structure.copy()
        bigcell.make_supercell([2, 2, 3])
        trans_tensor = self.fit_r4.structure_transform(self.structure, bigcell)
        self.assertArrayAlmostEqual(self.fit_r4, trans_tensor)

        # Test rotated primitive to conventional for fcc structure
        sn = self.get_structure("Sn")
        sn_prim = SpacegroupAnalyzer(sn).get_primitive_standard_structure()
        sn_prim.apply_operation(rot_symm_op)
        rotated = self.fit_r4.rotate(rot_symm_op.rotation_matrix)
        transformed = self.fit_r4.structure_transform(sn, sn_prim)
        self.assertArrayAlmostEqual(rotated, transformed)

    def test_from_voigt(self):
        with self.assertRaises(ValueError):
            Tensor.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
        Tensor.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],
        ])
        # Rank 3
        Tensor.from_voigt(np.zeros((3, 6)))
        # Rank 2
        Tensor.from_voigt(np.zeros(6))
        # Addresses occasional cast issues for integers
        Tensor.from_voigt(np.arange(6))

    def test_symmetry_reduce(self):
        tbs = [Tensor.from_voigt(row) for row in np.eye(6) * 0.01]
        reduced = symmetry_reduce(tbs, self.get_structure("Sn"))
        self.assertEqual(len(reduced), 2)
        self.assertArrayEqual([len(i) for i in reduced.values()], [2, 2])
        reconstructed = []
        for k, v in reduced.items():
            reconstructed.extend([k.voigt] +
                                 [k.transform(op).voigt for op in v])
        reconstructed = sorted(reconstructed, key=lambda x: np.argmax(x))
        self.assertArrayAlmostEqual([tb for tb in reconstructed],
                                    np.eye(6) * 0.01)

    def test_tensor_mapping(self):
        # Test get
        tbs = [Tensor.from_voigt(row) for row in np.eye(6) * 0.01]
        reduced = symmetry_reduce(tbs, self.get_structure("Sn"))
        tkey = Tensor.from_values_indices([0.01], [(0, 0)])
        tval = reduced[tkey]
        for tens_1, tens_2 in zip(tval, reduced[tbs[0]]):
            self.assertAlmostEqual(tens_1, tens_2)
        # Test set
        reduced[tkey] = "test_val"
        self.assertEqual(reduced[tkey], "test_val")
        # Test empty initialization
        empty = TensorMapping()
        self.assertEqual(empty._tensor_list, [])

    def test_populate(self):
        test_data = loadfn(
            os.path.join(PymatgenTest.TEST_FILES_DIR, "test_toec_data.json"))

        sn = self.get_structure("Sn")
        vtens = np.zeros((6, 6))
        vtens[0, 0] = 259.31
        vtens[0, 1] = 160.71
        vtens[3, 3] = 73.48
        et = Tensor.from_voigt(vtens)
        populated = et.populate(sn, prec=1e-3).voigt.round(2)
        self.assertAlmostEqual(populated[1, 1], 259.31)
        self.assertAlmostEqual(populated[2, 2], 259.31)
        self.assertAlmostEqual(populated[0, 2], 160.71)
        self.assertAlmostEqual(populated[1, 2], 160.71)
        self.assertAlmostEqual(populated[4, 4], 73.48)
        self.assertAlmostEqual(populated[5, 5], 73.48)
        # test a rank 6 example
        vtens = np.zeros([6] * 3)
        indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2), (0, 3, 3), (0, 5, 5),
                   (3, 4, 5)]
        values = [-1271.0, -814.0, -50.0, -3.0, -780.0, -95.0]
        for v, idx in zip(values, indices):
            vtens[idx] = v
        toec = Tensor.from_voigt(vtens)
        toec = toec.populate(sn, prec=1e-3, verbose=True)
        self.assertAlmostEqual(toec.voigt[1, 1, 1], -1271)
        self.assertAlmostEqual(toec.voigt[0, 1, 1], -814)
        self.assertAlmostEqual(toec.voigt[0, 2, 2], -814)
        self.assertAlmostEqual(toec.voigt[1, 4, 4], -3)
        self.assertAlmostEqual(toec.voigt[2, 5, 5], -3)
        self.assertAlmostEqual(toec.voigt[1, 2, 0], -50)
        self.assertAlmostEqual(toec.voigt[4, 5, 3], -95)

        et = Tensor.from_voigt(test_data["C3_raw"]).fit_to_structure(sn)
        new = np.zeros(et.voigt.shape)
        for idx in indices:
            new[idx] = et.voigt[idx]
        new = Tensor.from_voigt(new).populate(sn)
        self.assertArrayAlmostEqual(new, et, decimal=2)

    def test_from_values_indices(self):
        sn = self.get_structure("Sn")
        indices = [(0, 0), (0, 1), (3, 3)]
        values = [259.31, 160.71, 73.48]
        et = Tensor.from_values_indices(values,
                                        indices,
                                        structure=sn,
                                        populate=True).voigt.round(4)
        self.assertAlmostEqual(et[1, 1], 259.31)
        self.assertAlmostEqual(et[2, 2], 259.31)
        self.assertAlmostEqual(et[0, 2], 160.71)
        self.assertAlmostEqual(et[1, 2], 160.71)
        self.assertAlmostEqual(et[4, 4], 73.48)
        self.assertAlmostEqual(et[5, 5], 73.48)

    def test_serialization(self):
        # Test base serialize-deserialize
        d = self.symm_rank2.as_dict()
        new = Tensor.from_dict(d)
        self.assertArrayAlmostEqual(new, self.symm_rank2)

        d = self.symm_rank3.as_dict(voigt=True)
        new = Tensor.from_dict(d)
        self.assertArrayAlmostEqual(new, self.symm_rank3)

    def test_projection_methods(self):
        self.assertAlmostEqual(self.rand_rank2.project([1, 0, 0]),
                               self.rand_rank2[0, 0])
        self.assertAlmostEqual(self.rand_rank2.project([1, 1, 1]),
                               np.sum(self.rand_rank2) / 3)
        # Test integration
        self.assertArrayAlmostEqual(self.ones.average_over_unit_sphere(), 1)

    def test_summary_methods(self):
        self.assertEqual(
            set(self.ones.get_grouped_indices()[0]),
            set(itertools.product(range(3), range(3))),
        )
        self.assertEqual(
            self.ones.get_grouped_indices(voigt=True)[0],
            [(i, ) for i in range(6)])
        self.assertEqual(self.ones.get_symbol_dict(), {"T_1": 1})
        self.assertEqual(self.ones.get_symbol_dict(voigt=False), {"T_11": 1})

    def test_round(self):
        test = self.non_symm + 0.01
        rounded = test.round(1)
        self.assertArrayAlmostEqual(rounded, self.non_symm)
        self.assertTrue(isinstance(rounded, Tensor))
Beispiel #25
0
    def setUp(self):
        self.vec = Tensor([1.0, 0.0, 0.0])
        self.rand_rank2 = Tensor(np.random.randn(3, 3))
        self.rand_rank3 = Tensor(np.random.randn(3, 3, 3))
        self.rand_rank4 = Tensor(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 = Tensor([[1e-6, 1 + 1e-5, 1e-6], [1 + 1e-6, 1e-6, 1e-6],
                               [1e-7, 1e-7, 1 + 1e-5]])
        self.symm_rank2 = Tensor([[1, 2, 3], [2, 4, 5], [3, 5, 6]])
        self.symm_rank3 = Tensor([
            [[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 = Tensor([
            [
                [[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]],
                [[-0.92, 0.11, -0.02], [0.11, 0.09, 0.0], [-0.02, 0.0, -0.3]],
            ],
            [
                [[0.4, 0.05, 0.11], [0.05, -0.47, 0.09], [0.11, 0.09, 0.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], [0.09, 0.62, 0.3], [0.0, 0.3, -0.18]],
            ],
            [
                [[-0.92, 0.11, -0.02], [0.11, 0.09, 0.0], [-0.02, 0, -0.3]],
                [[0.11, 0.09, 0.0], [0.09, 0.62, 0.3], [0.0, 0.3, -0.18]],
                [[-0.02, 0.0, -0.3], [0.0, 0.3, -0.18], [-0.3, -0.18, -0.51]],
            ],
        ])

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

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

        self.structure = self.get_structure("BaNiO3")
        ieee_file_path = os.path.join(PymatgenTest.TEST_FILES_DIR,
                                      "ieee_conversion_data.json")
        self.ones = Tensor(np.ones((3, 3)))
        self.ieee_data = loadfn(ieee_file_path)
Beispiel #26
0
    def calc(self, item):
        """
        Process the tasks and materials into a dielectrics collection

        Args:
            item dict: a dict of material_id, structure, and tasks

        Returns:
            dict: a dieletrics dictionary
        """
        def poly(matrix):
            diags = np.diagonal(matrix)
            return np.prod(diags) / np.sum(
                np.prod(comb) for comb in combinations(diags, 2))

        if item["bandstructure"]["band_gap"] > 0:

            structure = Structure.from_dict(
                item.get("dielectric", {}).get("structure", None))

            ionic = Tensor(
                item["dielectric"]["ionic"]).convert_to_ieee(structure)
            static = Tensor(
                item["dielectric"]["static"]).convert_to_ieee(structure)
            total = ionic + static

            d = {
                "dielectric": {
                    "total": total,
                    "ionic": ionic,
                    "static": static,
                    "e_total": np.average(np.diagonal(total)),
                    "e_ionic": np.average(np.diagonal(ionic)),
                    "e_static": np.average(np.diagonal(static)),
                    "n": np.sqrt(np.average(np.diagonal(static))),
                }
            }

            sga = SpacegroupAnalyzer(structure)
            # Update piezo if non_centrosymmetric
            if item.get("piezo", False) and not sga.is_laue():
                static = PiezoTensor.from_voigt(
                    np.array(item["piezo"]["static"]))
                ionic = PiezoTensor.from_voigt(np.array(
                    item["piezo"]["ionic"]))
                total = ionic + static

                # Symmeterize Convert to IEEE orientation
                total = total.convert_to_ieee(structure)
                ionic = ionic.convert_to_ieee(structure)
                static = static.convert_to_ieee(structure)

                directions, charges, strains = np.linalg.svd(
                    total.voigt, full_matrices=False)

                max_index = np.argmax(np.abs(charges))

                max_direction = directions[max_index]

                # Allow a max miller index of 10
                min_val = np.abs(max_direction)
                min_val = min_val[min_val > (np.max(min_val) /
                                             self.max_miller)]
                min_val = np.min(min_val)

                d["piezo"] = {
                    "total": total.zeroed().voigt,
                    "ionic": ionic.zeroed().voigt,
                    "static": static.zeroed().voigt,
                    "e_ij_max": charges[max_index],
                    "max_direction": np.round(max_direction / min_val),
                    "strain_for_max": strains[max_index],
                }
        else:
            d = {
                "dielectric": {},
                "_warnings": [
                    "Dielectric calculated for likely metal. Values are unlikely to be converged"
                ],
            }

            if item.get("piezo", False):
                d.update({
                    "piezo": {},
                    "_warnings": [
                        "Piezoelectric calculated for likely metal. Values are unlikely to be converged"
                    ],
                })

        return d
    def get_asum_FCM(self, fcm, numiter=15):
        """
        Generate a symmeterized force constant matrix that obeys the objects symmetry
        constraints and obeys the acoustic sum rule through an iterative procedure

        Args:
            fcm (numpy array): 3Nx3N unsymmeterized force constant matrix
            numiter (int): number of iterations to attempt to obey the acoustic sum
                rule

        Return:
            numpy array representing the force constant matrix
        """

        # set max force in reciprocal space
        operations = self.FCM_operations
        numsites = len(self.structure)

        D = np.ones([numsites * 3, numsites * 3])
        for num in range(numiter):
            X = np.real(fcm)

            # symmetry operations
            pastrow = 0
            total = np.zeros([3, 3])
            for col in range(numsites):
                total = total + X[0:3, col * 3:col * 3 + 3]

            total = total / (numsites)
            for op in operations:
                same = 0
                transpose = 0
                if op[0] == op[1] and op[0] == op[2] and op[0] == op[3]:
                    same = 1
                if op[0] == op[3] and op[1] == op[2]:
                    transpose = 1
                if transpose == 0 and same == 0:
                    D[3 * op[0]:3 * op[0] + 3,
                      3 * op[1]:3 * op[1] + 3] = np.zeros([3, 3])

                    for symop in op[4]:

                        tempfcm = D[3 * op[2]:3 * op[2] + 3,
                                    3 * op[3]:3 * op[3] + 3]
                        tempfcm = symop.transform_tensor(tempfcm)

                        D[3 * op[0]:3 * op[0] + 3,
                          3 * op[1]:3 * op[1] + 3] += tempfcm

                    if len(op[4]) != 0:
                        D[3 * op[0]:3 * op[0] + 3, 3 * op[1]:3 * op[1] +
                          3] = D[3 * op[0]:3 * op[0] + 3,
                                 3 * op[1]:3 * op[1] + 3] / len(op[4])
                    D[3 * op[1]:3 * op[1] + 3,
                      3 * op[0]:3 * op[0] + 3] = D[3 * op[0]:3 * op[0] + 3,
                                                   3 * op[1]:3 * op[1] + 3].T
                    continue
                # Get the difference in the sum up to this point
                currrow = op[0]
                if currrow != pastrow:
                    total = np.zeros([3, 3])
                    for col in range(numsites):
                        total = total + X[currrow * 3:currrow * 3 + 3,
                                          col * 3:col * 3 + 3]
                    for col in range(currrow):
                        total = total - D[currrow * 3:currrow * 3 + 3,
                                          col * 3:col * 3 + 3]
                    total = total / (numsites - currrow)
                pastrow = currrow

                # Apply the point symmetry operations of the site
                temp_tensor = Tensor(total)
                temp_tensor_sum = sum([
                    temp_tensor.transform(symm_op)
                    for symm_op in self.sharedops[op[0]][op[1]]
                ])

                if len(self.sharedops[op[0]][op[1]]) != 0:
                    temp_tensor_sum = temp_tensor_sum / (len(
                        self.sharedops[op[0]][op[1]]))

                # Apply the proper transformation if there is an equivalent already
                if op[0] != op[1]:

                    for pair in range(len(op[4])):

                        temp_tensor2 = temp_tensor_sum.T
                        temp_tensor2 = op[4][pair].transform_tensor(
                            temp_tensor2)
                        temp_tensor_sum = (temp_tensor_sum + temp_tensor2) / 2

                else:
                    temp_tensor_sum = (temp_tensor_sum + temp_tensor_sum.T) / 2

                D[3 * op[0]:3 * op[0] + 3,
                  3 * op[1]:3 * op[1] + 3] = temp_tensor_sum
                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = temp_tensor_sum.T
            fcm = fcm - D

        return fcm
    def get_symmetrized_FCM(self, unsymmetrized_fcm, max_force=1):
        """
        Generate a symmeterized force constant matrix from an unsymmeterized matrix

        Args:
            unsymmetrized_fcm (numpy array): unsymmeterized force constant matrix
            max_charge (float): maximum born effective charge value

        Return:
            3Nx3N numpy array representing the force constant matrix
        """

        operations = self.FCM_operations
        D = unsymmetrized_fcm
        for op in operations:
            same = 0
            transpose = 0
            if op[0] == op[1] and op[0] == operations[2] and op[0] == op[3]:
                same = 1
            if op[0] == op[3] and op[1] == op[2]:
                transpose = 1
            if transpose == 0 and same == 0:
                D[3 * op[0]:3 * op[0] + 3,
                  3 * op[1]:3 * op[1] + 3] = np.zeros([3, 3])

                for symop in op[4]:

                    tempfcm = D[3 * op[2]:3 * op[2] + 3,
                                3 * op[3]:3 * op[3] + 3]
                    tempfcm = symop.transform_tensor(tempfcm)

                    D[3 * op[0]:3 * op[0] + 3,
                      3 * op[1]:3 * op[1] + 3] += tempfcm

                if len(op[4]) != 0:
                    D[3 * op[0]:3 * op[0] + 3, 3 * op[1]:3 * op[1] +
                      3] = D[3 * op[0]:3 * op[0] + 3,
                             3 * op[1]:3 * op[1] + 3] / len(op[4])
                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = D[3 * op[0]:3 * op[0] + 3,
                                               3 * op[1]:3 * op[1] + 3].T
                continue

            temp_tensor = Tensor(D[3 * op[0]:3 * op[0] + 3,
                                   3 * op[1]:3 * op[1] + 3])
            temp_tensor_sum = sum([
                temp_tensor.transform(symm_op)
                for symm_op in self.sharedops[op[0]][op[1]]
            ])
            if len(self.sharedops[op[0]][op[1]]) != 0:
                temp_tensor_sum = temp_tensor_sum / (len(
                    self.sharedops[op[0]][op[1]]))

            # Apply the proper transformation if there is an equivalent already
            if op[0] != op[1]:

                for pair in range(len(op[4])):

                    temp_tensor2 = temp_tensor_sum.T
                    temp_tensor2 = op[4][pair].transform_tensor(temp_tensor2)
                    temp_tensor_sum = (temp_tensor_sum + temp_tensor2) / 2

            else:
                temp_tensor_sum = (temp_tensor_sum + temp_tensor_sum.T) / 2

            D[3 * op[0]:3 * op[0] + 3,
              3 * op[1]:3 * op[1] + 3] = temp_tensor_sum
            D[3 * op[1]:3 * op[1] + 3,
              3 * op[0]:3 * op[0] + 3] = temp_tensor_sum.T

        return D
    def get_unstable_FCM(self, max_force=1):
        """
        Generate an unsymmeterized force constant matrix

        Args:
            max_charge (float): maximum born effective charge value

        Return:
            numpy array representing the force constant matrix
        """

        struc = self.structure
        operations = self.FCM_operations
        # set max force in reciprocal space
        numsites = len(struc.sites)
        D = (1 / max_force) * 2 * (np.ones([numsites * 3, numsites * 3]))
        for op in operations:
            same = 0
            transpose = 0
            if op[0] == op[1] and op[0] == op[2] and op[0] == op[3]:
                same = 1
            if op[0] == op[3] and op[1] == op[2]:
                transpose = 1
            if transpose == 0 and same == 0:
                D[3 * op[0]:3 * op[0] + 3,
                  3 * op[1]:3 * op[1] + 3] = np.zeros([3, 3])
                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = np.zeros([3, 3])

                for symop in op[4]:

                    tempfcm = D[3 * op[2]:3 * op[2] + 3,
                                3 * op[3]:3 * op[3] + 3]
                    tempfcm = symop.transform_tensor(tempfcm)
                    D[3 * op[0]:3 * op[0] + 3,
                      3 * op[1]:3 * op[1] + 3] += tempfcm

                if len(op[4]) != 0:
                    D[3 * op[0]:3 * op[0] + 3, 3 * op[1]:3 * op[1] +
                      3] = D[3 * op[0]:3 * op[0] + 3,
                             3 * op[1]:3 * op[1] + 3] / len(op[4])

                D[3 * op[1]:3 * op[1] + 3,
                  3 * op[0]:3 * op[0] + 3] = D[3 * op[0]:3 * op[0] + 3,
                                               3 * op[1]:3 * op[1] + 3].T
                continue

            temp_tensor = Tensor(np.random.rand(3, 3) - 0.5) * max_force

            temp_tensor_sum = sum([
                temp_tensor.transform(symm_op)
                for symm_op in self.sharedops[op[0]][op[1]]
            ])
            temp_tensor_sum = temp_tensor_sum / (len(
                self.sharedops[op[0]][op[1]]))
            if op[0] != op[1]:
                for pair in range(len(op[4])):

                    temp_tensor2 = temp_tensor_sum.T
                    temp_tensor2 = op[4][pair].transform_tensor(temp_tensor2)
                    temp_tensor_sum = (temp_tensor_sum + temp_tensor2) / 2

            else:
                temp_tensor_sum = (temp_tensor_sum + temp_tensor_sum.T) / 2

            D[3 * op[0]:3 * op[0] + 3,
              3 * op[1]:3 * op[1] + 3] = temp_tensor_sum
            D[3 * op[1]:3 * op[1] + 3,
              3 * op[0]:3 * op[0] + 3] = temp_tensor_sum.T

        return D
                        - np.identity(3)) / 2.0, h)))
eps_voigt = np.empty([6, eps_mat.shape[0]], "d")
eps_voigt[0] = eps_mat[:, 0, 0]
eps_voigt[1] = eps_mat[:, 1, 1]
eps_voigt[2] = eps_mat[:, 2, 2]
eps_voigt[3] = eps_mat[:, 2, 1]
eps_voigt[4] = eps_mat[:, 2, 0]
eps_voigt[5] = eps_mat[:, 1, 0]
eps_voigt_mean = np.mean(eps_voigt, axis=1)
Smat = np.zeros([6, 6], "d")
for i in range(6):
    for j in range(i, 6):
        Smat[i,
             j] = np.mean(eps_voigt[i] *
                          eps_voigt[j]) - eps_voigt_mean[i] * eps_voigt_mean[j]
        if i != j: Smat[j, i] = Smat[i, j]
T = 300
V = np.linalg.det(h0)
Cmat = np.linalg.inv(Smat) * ((1.3806488E-23 * T) / (V * 1E-30)) * 1E-9
Cmat = np.around(Cmat, decimals=2)
print(Cmat)
Cmat_eig = np.linalg.eigvals(Cmat)
mechanical_eig_max.append(max(Cmat_eig))
mechanical_eig_min.append(min(Cmat_eig))
print(mechanical_eig_max)
print(mechanical_eig_min)

mat = Tensor.from_voigt(Cmat)
elastic = ElasticTensor(mat)
print(elastic.k_vrh)
#np.savetxt("pcu_1.cij", Cmat)
 def test_init(self):
     cijkl = Tensor.from_voigt(self.c2)
     cijklmn = Tensor.from_voigt(self.c3)
     exp = ElasticTensorExpansion([cijkl, cijklmn])
     from_voigt = ElasticTensorExpansion.from_voigt([self.c2, self.c3])
     self.assertEqual(exp.order, 3)
Beispiel #32
0
    def test_list_based_functions(self):
        # zeroed
        tc = TensorCollection([1e-4 * Tensor(np.eye(3))] * 4)
        for t in tc.zeroed():
            self.assertArrayEqual(t, np.zeros((3, 3)))
        for t in tc.zeroed(1e-5):
            self.assertArrayEqual(t, 1e-4 * np.eye(3))
        self.list_based_function_check("zeroed", tc)
        self.list_based_function_check("zeroed", tc, tol=1e-5)

        # transform
        symm_op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False,
                                                         [0, 0, 1])
        self.list_based_function_check("transform",
                                       self.seq_tc,
                                       symm_op=symm_op)

        # symmetrized
        self.list_based_function_check("symmetrized", self.seq_tc)

        # rotation
        a = 3.14 * 42.5 / 180
        rotation = SquareTensor([[math.cos(a), 0, math.sin(a)], [0, 1, 0],
                                 [-math.sin(a), 0,
                                  math.cos(a)]])
        self.list_based_function_check("rotate",
                                       self.diff_rank,
                                       matrix=rotation)

        # is_symmetric
        self.assertFalse(self.seq_tc.is_symmetric())
        self.assertTrue(self.diff_rank.is_symmetric())

        # fit_to_structure
        self.list_based_function_check("fit_to_structure", self.diff_rank,
                                       self.struct)
        self.list_based_function_check("fit_to_structure", self.seq_tc,
                                       self.struct)

        # fit_to_structure
        self.list_based_function_check("fit_to_structure", self.diff_rank,
                                       self.struct)
        self.list_based_function_check("fit_to_structure", self.seq_tc,
                                       self.struct)

        # voigt
        self.list_based_function_check("voigt", self.diff_rank)

        # is_voigt_symmetric
        self.assertTrue(self.diff_rank.is_voigt_symmetric())
        self.assertFalse(self.seq_tc.is_voigt_symmetric())

        # Convert to ieee
        for entry in self.ieee_data[:2]:
            entry["xtal"]
            tc = TensorCollection([entry["original_tensor"]] * 3)
            struct = entry["structure"]
            self.list_based_function_check("convert_to_ieee", tc, struct)

        # from_voigt
        tc_input = [t for t in np.random.random((3, 6, 6))]
        tc = TensorCollection.from_voigt(tc_input)
        for t_input, t in zip(tc_input, tc):
            self.assertArrayAlmostEqual(Tensor.from_voigt(t_input), t)