Ejemplo n.º 1
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)
Ejemplo n.º 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")
Ejemplo n.º 3
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]
Ejemplo n.º 4
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)
Ejemplo n.º 5
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)
Ejemplo n.º 6
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, [])
Ejemplo n.º 7
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))
Ejemplo n.º 8
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)
Ejemplo n.º 9
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")
Ejemplo n.º 10
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)
Ejemplo n.º 11
0
                        - 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)
Ejemplo n.º 12
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)
Ejemplo n.º 13
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)