def test_get_wavelength(self): wavelength = ks.get_wavelength(200000) self.assertEqual(np.round(wavelength * 1000, 3), 2.508) wavelength = ks.get_wavelength(60000) self.assertEqual(np.round(wavelength * 1000, 3), 4.866) with self.assertRaises(TypeError): ks.get_wavelength('lattice_parameter')
def test_ring_pattern_calculation(self): tags = ks.example(verbose=False) new_tags = ks.ring_pattern_calculation(tags) self.assertTrue('{2 6 4}' in new_tags) self.assertAlmostEqual(new_tags['{1 1 1}']['reciprocal_distance'], 3.369748652857738) self.assertAlmostEqual(new_tags['{1 1 1}']['real_distance'], 1 / 3.369748652857738) self.assertAlmostEqual(new_tags['{1 1 1}']['F'], 17.53103039316424) self.assertEqual(new_tags['{1 1 1}']['multiplicity'], 8)
def test_get_rotation_matrix(self): matrix, theta, phi = ks.get_rotation_matrix([1, 0, 0]) self.assertEqual(theta, 90.) self.assertEqual(phi, 0.) matrix, theta, phi = ks.get_rotation_matrix([1, 1, 1]) matrix_desired = [[0.40824829, -0.70710678, 0.57735027], [0.40824829, 0.70710678, 0.57735027], [-0.81649658, 0., 0.57735027]] np.testing.assert_allclose(matrix, matrix_desired)
def test_Zuo_fig_3_18(self): tags = ks.Zuo_fig_3_18(verbose=False) self.assertIsInstance(tags, dict) self.assertEqual(tags['crystal_name'], 'Silicon') self.assertEqual(tags['lattice_parameter_nm'], 0.514) self.assertEqual(tags['acceleration_voltage_V'], 101.6 * 1000.0) self.assertEqual(tags['convergence_angle_mrad'], 7.1) np.testing.assert_allclose(tags['zone_hkl'], np.array([-2, 2, 1]))
def test_ball_and_stick(self): in_tags = { 'unit_cell': np.identity(3), 'base': [[0, 0, 0], [0.5, 0.5, 0.5]], 'elements': ['Fe', 'Fe'] } corners, balls, atomic_number, bonds = ks.ball_and_stick( in_tags, extend=1, max_bond_length=1.) corners_desired = [[(0.0, 0.0), (0.0, 0.0), (0.0, 1.0)], [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)], [(0.0, 0.0), (1.0, 1.0), (1.0, 0.0)], [(0.0, 0.0), (1.0, 0.0), (0.0, 0.0)], [(0.0, 1.0), (0.0, 0.0), (0.0, 0.0)], [(1.0, 1.0), (0.0, 0.0), (0.0, 1.0)], [(1.0, 1.0), (0.0, 1.0), (1.0, 1.0)], [(1.0, 1.0), (1.0, 1.0), (0.0, 1.0)], [(1.0, 1.0), (1.0, 0.0), (0.0, 0.0)], [(0.0, 1.0), (0.0, 0.0), (1.0, 1.0)], [(0.0, 1.0), (1.0, 1.0), (0.0, 0.0)], [(0.0, 1.0), (1.0, 1.0), (1.0, 1.0)]] np.testing.assert_allclose(corners, corners_desired) balls_desired = [[0., 0., 0.], [0., 0., 1.], [0., 1., 0.], [0., 1., 1.], [1., 0., 0.], [1., 0., 1.], [1., 1., 0.], [1., 1., 1.], [0.5, 0.5, 0.5]] np.testing.assert_allclose(balls, balls_desired) self.assertTrue(len(atomic_number) == 9) bonds_desired = [[(0.0, 0.5), (0.0, 0.5), (0.0, 0.5)], [(0.0, 0.5), (0.0, 0.5), (1.0, 0.5)], [(0.0, 0.5), (1.0, 0.5), (0.0, 0.5)], [(0.0, 0.5), (1.0, 0.5), (1.0, 0.5)], [(1.0, 0.5), (0.0, 0.5), (0.0, 0.5)], [(1.0, 0.5), (0.0, 0.5), (1.0, 0.5)], [(1.0, 0.5), (1.0, 0.5), (0.0, 0.5)], [(1.0, 0.5), (1.0, 0.5), (1.0, 0.5)], [(0.5, 0.0), (0.5, 1.0), (0.5, 0.0)], [(0.5, 0.0), (0.5, 1.0), (0.5, 1.0)], [(0.5, 1.0), (0.5, 0.0), (0.5, 0.0)], [(0.5, 1.0), (0.5, 0.0), (0.5, 1.0)], [(0.5, 1.0), (0.5, 1.0), (0.5, 0.0)], [(0.5, 0.0), (0.5, 0.0), (0.5, 1.0)], [(0.5, 1.0), (0.5, 1.0), (0.5, 1.0)]]
def test_zone_mistilt(self): rotated_zone_axis = ks.zone_mistilt([1, 0, 0], [45, 0, 0]) np.testing.assert_allclose(rotated_zone_axis, [1, 0, 0]) rotated_zone_axis = ks.zone_mistilt([1, 0, 0], [0, 10, 0]) np.testing.assert_allclose(rotated_zone_axis, [0.98480775, 0., 0.17364818]) with self.assertRaises(TypeError): ks.zone_mistilt([1, 0, 0], [0, 0]) with self.assertRaises(TypeError): ks.zone_mistilt([1j, 0, 0], [0, 0])
def test_get_symmetry(self): # Todo: better test self.assertTrue( ks.get_symmetry(np.identity(3), [[0, 0, 0], [0.5, 0.5, 0.5]], ['Fe', 'Fe']))
def test_example(self): tags = ks.example(verbose=False) self.assertEqual(tags['plot HOLZ'], 1) self.assertEqual(tags['plot HOLZ'], 1)
def test_feq(self): self.assertAlmostEqual(ks.feq('Au', 3.6), 7.43164303450277) self.assertAlmostEqual(ks.feq('Si', 12.6), 0.5398190143297035)
def test_diffraction_pattern(self): tags = ks.example(verbose=False) ks.kinematic_scattering(tags)
def test_make_pretty_labels(self): labels = ks.make_pretty_labels(np.array([[1, 0, 0], [1, 1, -1]])) self.assertEqual(labels[0], '[$\\bar {1},0,0} $]') self.assertEqual(labels[1], '[$\\bar {1},1,\\bar {1} $]')
def deficient_holz_line(exact_bragg=False, shift=False, laue_zone=1, color='black'): """ Ewald sphere construction to explain Laue Circle and deficient HOLZ lines Parameters: exact_bragg: boolean whether to tilt into exact Bragg condition or along zone axis shift: boolean whether to shift exact Bragg condition onto zone axis origin laue_zone: int first or second Laue zone only color: string color of wave vectors and Ewald sphere """ k_0 = [0, 1 / ks.get_wavelength(600)] d = .5 # lattice parameter in nm if laue_zone == 0: s_g = 1 / d + 0.6 else: s_g = 1 g = np.linspace(-5, 6, 12) * 1 / d g_d = np.array([5. / d + laue_zone * 1 / d / 2, laue_zone * 1 / d]) g_sg = g_d.copy() g_sg[1] = g_d[1] + s_g # point on Ewald sphere # reciprocal lattice plt.scatter(g[:-1], [0] * 11, color='red') plt.scatter(g - 1 / d / 2, [1 / d] * 12, color='blue') shift_x = shift_y = 0. d_theta = d_theta1 = d_theta2 = 0 if exact_bragg: d_theta1 = np.arctan((1 / d * laue_zone + s_g) / g_d[0]) d_theta2 = np.arctan((1 / d * laue_zone) / g_d[0]) d_theta = -(d_theta1 - d_theta2) s_g = 0 s = np.sin(d_theta) c = np.cos(d_theta) k_0 = [-s * k_0[1], c * k_0[1]] if shift: shift_x = -k_0[0] shift_y = np.linalg.norm(k_0) - k_0[1] d_theta = np.degrees(d_theta) k_0[0] += shift_x k_0[1] += shift_y # Ewald Sphere ewald_sphere = patches.Circle((k_0[0], k_0[1]), radius=np.linalg.norm(k_0), clip_on=False, zorder=10, linewidth=1, edgecolor=color, fill=False) plt.gca().add_artist(ewald_sphere) plt.gca().arrow(g[-1] + 1 / d / 4, 1 / d / 2, 0, 1 / d / 2, head_width=0.3, head_length=0.4, fc='k', ec='k', length_includes_head=True) plt.gca().arrow(g[-1] + 1 / d / 4, 1 / d / 2, 0, -1 / d / 2, head_width=0.3, head_length=0.4, fc='k', ec='k', length_includes_head=True) plt.gca().annotate("$|g_{HOLZ}|$", xytext=(g[-1] + 1 / d / 3, 1 / d / 3), xy=(g[-1] + 1 / d / 3, 1 / d / 3)) # k_0 plt.scatter(k_0[0], k_0[1]) plt.gca().arrow(k_0[0], k_0[1], -k_0[0] + shift_x, -k_0[1] + shift_y, head_width=0.3, head_length=0.4, fc=color, ec=color, length_includes_head=True) plt.gca().annotate("K$_0$", xytext=(k_0[0] / 2, k_0[1] / 3), xy=(k_0[0] / 2, k_0[1] / 2)) # K_d Bragg of HOLZ reflection plt.gca().arrow(k_0[0], k_0[1], -k_0[0] + g_d[0] + shift_x, -k_0[1] + g_d[1] + s_g + shift_y, head_width=0.3, head_length=0.4, fc=color, ec=color, length_includes_head=True) plt.gca().annotate("K$_d$", xytext=(k_0[0] + (g_d[0] - k_0[0]) / 2, k_0[1] / 2), xy=(6.5 / d / 2, k_0[1] / 2)) # s_g excitation Error of HOLZ reflection if s_g > 0: plt.gca().arrow(g_d[0], g_d[1], 0, s_g, head_width=0.3, head_length=0.4, fc='k', ec='k', length_includes_head=True) plt.gca().annotate("s$_g$", xytext=(g_d[0] * 1.01, g_d[1] + s_g / 3), xy=(g_d[0] * 1.01, g_d[1] + s_g / 3)) # Bragg angle g_sg = g_d g_sg[1] = g_d[1] + s_g plt.plot([0 + shift_x, g_sg[0] + shift_x], [0 + shift_y, g_d[1] + shift_y], color=color, linewidth=1, alpha=0.5, linestyle='--') plt.plot([k_0[0], g_sg[0] / 2 + shift_x], [k_0[1], g_sg[1] / 2 + shift_y], color=color, linewidth=1, alpha=0.5, linestyle='--') # d_theta = np.degrees(np.arctan(k_0[0]/k_0[1])) bragg_angle = patches.Arc( (k_0[0], k_0[1]), width=k_0[1], height=k_0[1], theta1=-90 + d_theta, theta2=-90 + d_theta + np.degrees(np.arcsin(np.linalg.norm(g_sg / 2) / k_0[1])), fc=color, ec=color) plt.gca().annotate(r"$\theta $", xytext=(k_0[0] / 1.3, k_0[1] / 1.5), xy=(k_0[0] / 2 + g_d[0] / 4, k_0[1] / 2)) plt.gca().add_patch(bragg_angle) # deviation/tilt angle if np.abs(d_theta) > 0: if shift: deviation_angle = patches.Arc((k_0[0], k_0[1]), width=k_0[1] * 1.5, height=k_0[1] * 1.5, theta1=-90 + d_theta, theta2=-90, fc=color, ec=color, linewidth=3) plt.gca().annotate(r"$d \theta $", xytext=(k_0[0] - 1.3, k_0[1] / 3.7), xy=(k_0[0] + g_d[0] / 4, k_0[1] / 2)) plt.gca().arrow(shift_x, -2, 0, 2, head_width=0.5, head_length=0.6, fc=color, ec='black', length_includes_head=True, linewidth=3) plt.gca().annotate("deficient line", xytext=(shift_x * 2, -2), xy=(shift_x, 0)) else: deviation_angle = patches.Arc((0, 0), width=k_0[1], height=k_0[1], theta1=np.degrees(d_theta2), theta2=np.degrees(d_theta1), fc=color, ec=color, linewidth=3) plt.gca().annotate(r"$d \theta $", xytext=(g_d[0] * .8, 1 / d / 3), xy=(g_d[0], 1 / d)) plt.gca().add_patch(deviation_angle) plt.gca().set_aspect('equal') plt.xlim(-14, 14) plt.ylim(-4, k_0[1] * 1.1)
def test_kinematic_scattering(self): tags = ks.example(verbose=False) ks.kinematic_scattering(tags) self.assertIsInstance(tags['HOLZ'], dict) self.assertAlmostEqual(tags['wave_length_nm'], 0.0036695, delta=1e-6)
def test_check_sanity(self): self.assertFalse(ks.check_sanity({})) tags = ks.example(verbose=False) self.assertTrue(ks.check_sanity(tags))
def test_plotCBED(self): tags = ks.example(verbose=False) ks.kinematic_scattering(tags)
def test_metric_tensor(self): # Todo: better testing np.testing.assert_allclose(ks.metric_tensor(np.identity(3)), np.identity(3))
def test_circles(self): tags = ks.example(verbose=False) ks.kinematic_scattering(tags)
def deficient_kikuchi_line(s_g=0., color_B='black'): k_len = 1 / ks.get_wavelength(200) d = .2 # lattice parameter in nm g = np.linspace(-2, 2, 5) * 1 / d g_d = np.array([1 / d, 0]) # recirocal lattice plt.scatter(g, [0] * 5, color='blue') alpha = -np.arctan(s_g / g_d[0]) theta = -np.arcsin(g_d[0] / 2 / k_len) k_0 = np.array( [-np.sin(theta - alpha) * k_len, np.cos(theta - alpha) * k_len]) k_d = np.array( [-np.sin(-theta - alpha) * k_len, np.cos(-theta - alpha) * k_len]) k_i = np.array([-np.sin(theta - alpha) * 3, np.cos(theta - alpha) * 3]) k_i_t = np.array([-np.sin(-alpha) * 3, np.cos(-alpha) * 3]) kk_e = np.array([-np.sin(-theta) * k_len, np.cos(-theta) * k_len]) kk_d = np.array([-np.sin(theta) * k_len, np.cos(theta) * k_len]) # Ewald Sphere ewald_sphere = patches.Circle((k_0[0], k_0[1]), radius=np.linalg.norm(k_0), clip_on=False, zorder=10, linewidth=1, edgecolor=color_B, fill=False) plt.gca().add_artist(ewald_sphere) # K_0 plt.plot([k_0[0], k_0[0]], [k_0[1], k_0[1] + 4], color='gray', linestyle='-', alpha=0.3) plt.gca().arrow(k_0[0] + k_i[0], k_0[1] + k_i[1], -k_i[0], -k_i[1], head_width=0.3, head_length=0.4, fc=color_B, ec=color_B, length_includes_head=True) plt.plot([k_0[0] + k_i_t[0], k_0[0] - k_i_t[0]], [k_0[1] + k_i_t[1], k_0[1] - k_i_t[1]], color='black', linestyle='--', alpha=0.5) plt.scatter(k_0[0], k_0[1], color='black') plt.gca().arrow(k_0[0], k_0[1], -k_0[0], -k_0[1], head_width=0.3, head_length=0.4, fc=color_B, ec=color_B, length_includes_head=True) plt.gca().annotate("K$_0$", xytext=(-k_0[0] / 2, 0), xy=(k_0[0] / 2, 0)) plt.gca().arrow(k_0[0], k_0[1], -k_d[0], -k_d[1], head_width=0.3, head_length=0.4, fc=color_B, ec=color_B, length_includes_head=True) # K_e exces line plt.gca().arrow(k_0[0], k_0[1], -kk_e[0], -kk_e[1], head_width=0.3, head_length=0.4, fc='red', ec='red', length_includes_head=True) plt.gca().annotate("excess", xytext=(k_0[0] - kk_e[0], -1), xy=(-kk_e[0] + k_0[0], 0)) plt.plot([k_0[0] - kk_e[0], k_0[0] - kk_e[0]], [-1, 1], color='red') # k_d deficient line plt.gca().arrow(k_0[0], k_0[1], -kk_d[0], -kk_d[1], head_width=0.3, head_length=0.4, fc='blue', ec='blue', length_includes_head=True) plt.plot([k_0[0] - kk_d[0], k_0[0] - kk_d[0]], [-1, 1], color='blue') plt.gca().annotate("deficient", xytext=(k_0[0] - kk_d[0], -1), xy=(k_0[0] - kk_d[0], 0)) # s_g excitation Error of HOLZ reflection plt.gca().arrow(g_d[0], g_d[1], 0, s_g, head_width=0.3, head_length=0.4, fc='k', ec='k', length_includes_head=True) plt.gca().annotate("s$_g$", xytext=(g_d[0] * 1.01, g_d[1] + s_g / 3), xy=(g_d[0] * 1.01, g_d[1] + s_g / 3)) theta = np.degrees(theta) alpha = np.degrees(alpha) bragg_angle = patches.Arc((k_0[0], k_0[1]), width=5.5, height=5.5, theta1=90 + theta - alpha, theta2=90 - alpha, fc='black', ec='black') if alpha > 0: deviation_angle = patches.Arc((k_0[0], k_0[1]), width=6, height=6, theta1=90 - alpha, theta2=90, fc='black', ec='red') else: deviation_angle = patches.Arc((k_0[0], k_0[1]), width=6, height=6, theta1=90, theta2=90 - alpha, fc='black', ec='red') plt.gca().annotate(r"$\theta$", xytext=(k_0[0] + k_i_t[0] / 1.3, k_0[1] + 2), xy=(k_0[0] + k_i_t[0], k_0[1] + 2)) plt.gca().annotate(r"$\alpha$", xytext=(k_0[0] + k_i_t[0] / 1.3, k_0[1] + 3), xy=(k_0[0] + k_i_t[0], k_0[1] + 3), color='red') plt.gca().add_patch(bragg_angle) plt.gca().add_patch(deviation_angle) plt.xlim(-12, 12) plt.ylim(-2, k_0[1] * 1.4) plt.gca().set_aspect('equal')
def test_vector_norm(self): g = [[2, 3, 4], [4, 5, 6]] vector_length = ks.vector_norm(g) np.testing.assert_allclose(vector_length, np.linalg.norm(g, axis=1))