예제 #1
0
    def optimize_rotation(self, ellipsoid, e_matrix, init_rot):
        """Optimize a rotation."""
        self._check_ellipsoid(ellipsoid)
        axes, angles = unwrap_euler_angles(init_rot)
        orig_strain = to_full_tensor(self.eigenstrain.copy())
        aspect = np.array(ellipsoid["aspect"])

        self.eshelby = StrainEnergy.get_eshelby(aspect, self.poisson)
        opt_args = {
            "ellipsoid": ellipsoid,
            "elast_matrix": e_matrix,
            "strain_energy_obj": self,
            "orig_strain": orig_strain,
            "rot_axes": axes
        }
        opts = {"eps": 1.0}
        res = minimize(cost_minimize_strain_energy,
                       angles,
                       args=(opt_args, ),
                       options=opts)
        rot_seq = combine_axes_and_angles(axes, res["x"])
        optimal_orientation = {
            "energy": res["fun"],
            "rot_sequence": rot_seq,
            "rot_matrix": rot_matrix(rot_seq)
        }

        # Reset the eigenstrain back
        self.eigenstrain = to_voigt(orig_strain)
        return optimal_orientation
예제 #2
0
    def plot(self, scale_factor, elast_matrix, rot_seq=None, latex=False):
        """Create a plot of the energy as different aspect ratios."""
        from matplotlib import pyplot as plt
        a_over_c = np.logspace(0, 3, 100)
        b_over_c = [1, 2, 5, 10, 50, 100]

        orig_strain = self.eigenstrain.copy()

        if rot_seq is not None:
            strain = to_full_tensor(orig_strain)
            rot_mat = rot_matrix(rot_seq)
            strain = rotate_tensor(strain, rot_mat)
            self.eigenstrain = to_voigt(strain)

        fig = plt.figure()
        ax = fig.add_subplot(1, 1, 1)
        for b in b_over_c:
            W = []
            a_plot = []
            for a in a_over_c:
                if a < b:
                    continue
                aspect = [a, b, 1.0]
                self.eshelby = StrainEnergy.get_eshelby(aspect, self.poisson)
                E = self.strain_energy(scale_factor, elast_matrix) * 1000.0
                W.append(E)
                a_plot.append(a)
            ax.plot(a_plot, W, label="{}".format(int(b)))

        # Separation line
        b_sep = np.logspace(0, 3, 100)
        W = []
        for b in b_sep:
            aspect = [b, b, 1.0]
            self.eshelby = StrainEnergy.get_eshelby(aspect, self.poisson)
            E = self.strain_energy(scale_factor, elast_matrix) * 1000.0
            W.append(E)

        ax.plot(b_sep, W, "--", color="grey")
        ax.legend(frameon=False, loc="best")
        if latex:
            xlab = "\$a/c\$"
            ylab = "Strain energy (meV/\$\SI{}{\\angstrom^3}\$)"
        else:
            xlab = "$a/c$"
            ylab = "Strain enregy (meV/A^3)"
        ax.set_xlabel(xlab)
        ax.set_ylabel(ylab)
        ax.spines["right"].set_visible(False)
        ax.spines["top"].set_visible(False)
        ax.set_xscale("log")
        self.eigenstrain = orig_strain
        return fig
예제 #3
0
    def test_mandel_transformation(self):
        if not available:
            self.skipTest(reason)
        mandel_vec = np.linspace(1.0, 6.0, 6)
        mandel_full = to_full_tensor(mandel_vec)
        self.assertTrue(np.allclose(to_mandel(mandel_full), mandel_vec))

        # Try rank for tensors
        mandel_tensor = np.random.rand(6, 6)
        mandel_full = to_full_rank4(mandel_tensor)
        self.assertTrue(
            np.allclose(to_mandel_rank4(mandel_full), mandel_tensor))
예제 #4
0
    def test_tensor_rotation(self):
        if not available:
            self.skipTest(reason)
        tens = np.linspace(1.0, 6.0, 6)
        ca = np.cos(0.3)
        sa = np.sin(0.3)
        rot_matrix = np.array([[ca, sa, 0.0], [-sa, ca, 0.0], [0.0, 0.0, 1.0]])

        # Rotate the rank 2 tensor
        full_tensor = to_full_tensor(tens)
        rotated_tensor = rotate_tensor(full_tensor, rot_matrix)

        x = np.array([-0.1, 0.5, 0.9])

        x_rot = rot_matrix.dot(x)

        # If we contract all indices the scalar product should remain the same
        scalar1 = x.dot(full_tensor).dot(x)
        scalar2 = x_rot.dot(rotated_tensor).dot(x_rot)
        self.assertAlmostEqual(scalar1, scalar2)
예제 #5
0
    def test_rank4_tensor_rotation(self):
        if not available:
            self.skipTest(reason)

        vec = np.random.rand(6)
        full2x2 = to_full_tensor(vec)

        ca = np.cos(0.8)
        sa = np.sin(0.8)
        rot_matrix = np.array([[ca, sa, 0.0], [-sa, ca, 0.0], [0.0, 0.0, 1.0]])

        tensor = np.random.rand(6, 6)
        rotated = rotate_rank4_mandel(tensor, rot_matrix)

        rotated2x2 = rotate_tensor(full2x2, rot_matrix)
        rot_vec = to_mandel(rotated2x2)

        # Contract indices
        scalar1 = vec.dot(tensor).dot(vec)
        scalar2 = rot_vec.dot(rotated).dot(rot_vec)
        self.assertAlmostEqual(scalar1, scalar2)
예제 #6
0
    def explore_orientations(self,
                             ellipsoid,
                             e_matrix,
                             step=10,
                             print_summary=False):
        """Explore the strain energy as a function of ellipse orientation."""
        self._check_ellipsoid(ellipsoid)
        scale_factor = ellipsoid["scale_factor"]
        aspect = np.array(ellipsoid["aspect"])
        self.eshelby = StrainEnergy.get_eshelby(aspect, self.poisson)
        result = []
        eigenstrain_orig = to_full_tensor(self.eigenstrain)
        theta = np.arange(0.0, np.pi, step * np.pi / 180.0)
        phi = np.arange(0.0, 2.0 * np.pi, step * np.pi / 180.0)

        for th in theta:
            for p in phi:
                matrix = rot_matrix_spherical_coordinates(p, th)
                strain = rotate_tensor(eigenstrain_orig, matrix)
                self.eigenstrain = to_voigt(strain)
                energy = self.strain_energy(scale_factor, e_matrix)
                res = {"energy": energy, "theta": th, "phi": p}
                a = matrix.T.dot([1.0, 0.0, 0.0])
                b = matrix.T.dot([0.0, 1.0, 0.0])
                c = matrix.T.dot([0.0, 0.0, 1.0])
                res["half_axes"] = {"a": a, "b": b, "c": c}
                res["eigenstrain"] = self.eigenstrain
                result.append(res)

        if print_summary:
            self.summarize_orientation_serch(result)

        # Sort the result from low energy to high energy
        energies = [res["energy"] for res in result]
        sorted_indx = np.argsort(energies)
        result = [result[indx] for indx in sorted_indx]

        # Reset the strain
        self.eigenstrain = to_voigt(eigenstrain_orig)
        return result
예제 #7
0
    def explore_orientations(self,
                             ellipsoid,
                             C_matrix,
                             step=10,
                             fname="",
                             theta_ax="y",
                             phi_ax="z"):
        """Explore the strain energy as a function of ellipse orientation.
        
        :param dict ellipsoid: Dictionary with information of the ellipsoid
            The format should be {"aspect": [1.0, 1.0, 1.0], "C_prec": ..., 
            "scale_factor": 1.0}
            C_prec is the elastic tensor of the precipitate material.
            If not given, scale_factor has to be given, and the elastic
            tensor of the precipitate material is taken as this factor
            multiplied by the elastic tensor of the matrix material
        :param numpy.ndarray C_matrix: Elastic tensor of the matrix material
        :param float step: Angle step size in degree
        :param str fname: If given the result of the exploration is 
            stored in a csv file with this filename
        :param str theta_ax: The first rotation is performed
            around this axis
        :param str phi_ax: The second rotation is performed around this
            axis (in the new coordinate system after the first rotation
            is performed)
        """
        from itertools import product
        #self._check_ellipsoid(ellipsoid)
        scale_factor = ellipsoid.get("scale_factor", None)
        C_prec = ellipsoid.get("C_prec", None)

        if C_prec is None:
            C_prec = scale_factor * C_matrix

        # Convert the tensors to their isotropic representation
        C_matrix = self.make_isotropic(C_matrix)
        C_prec = self.make_isotropic(C_prec)

        aspect = np.array(ellipsoid["aspect"])
        self.eshelby = StrainEnergy.get_eshelby(aspect, self.poisson)
        result = []
        misfit_orig = to_full_tensor(self.misfit)
        theta = np.arange(0.0, np.pi, step * np.pi / 180.0)
        phi = np.arange(0.0, 2.0 * np.pi, step * np.pi / 180.0)
        theta = np.append(theta, [np.pi])
        phi = np.append(phi, [2.0 * np.pi])

        C_matrix_orig = C_matrix.copy()
        C_prec_orig = C_prec.copy()
        for ang in product(theta, phi):
            th = ang[0]
            p = ang[1]
            theta_deg = th * 180 / np.pi
            phi_deg = p * 180 / np.pi
            seq = [(theta_ax, -theta_deg), (phi_ax, -phi_deg)]
            matrix = rot_matrix(seq)
            #matrix = rot_matrix_spherical_coordinates(p, th)

            # Rotate the strain tensor
            strain = rotate_tensor(misfit_orig, matrix)
            self.misfit = to_mandel(strain)

            # Rotate the elastic tensor of the matrix material
            C_matrix = rotate_rank4_mandel(C_matrix_orig, matrix)

            # Rotate the elastic tensor of the precipitate material
            C_prec = rotate_rank4_mandel(C_prec_orig, matrix)
            if abs(p) < 1E-3 and (abs(th - np.pi / 4.0) < 1E-3
                                  or abs(th - 3.0 * np.pi / 4.0) < 1E-3):
                print(self.eshelby.aslist())

            energy = self.strain_energy(C_matrix=C_matrix, C_prec=C_prec)
            res = {"energy": energy, "theta": th, "phi": p}
            a = matrix.T.dot([1.0, 0.0, 0.0])
            b = matrix.T.dot([0.0, 1.0, 0.0])
            c = matrix.T.dot([0.0, 0.0, 1.0])
            res["half_axes"] = {"a": a, "b": b, "c": c}
            res["misfit"] = self.misfit
            result.append(res)

        if fname != "":
            self.save_orientation_result(result, fname)

        # Sort the result from low energy to high energy
        energies = [res["energy"] for res in result]
        sorted_indx = np.argsort(energies)
        result = [result[indx] for indx in sorted_indx]

        # Reset the strain
        self.misfit = to_mandel(misfit_orig)
        return result