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
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
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))
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)
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)
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
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