Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add grain boundary generator #1200

Merged
merged 5 commits into from Jul 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1,682 changes: 1,682 additions & 0 deletions pymatgen/core/grain_boundary_generator.py

Large diffs are not rendered by default.

225 changes: 225 additions & 0 deletions pymatgen/core/tests/test_grain_boundary_generator.py
@@ -0,0 +1,225 @@
from __future__ import division, unicode_literals

__author__ = 'Xiang-Guo Li'
__copyright__ = 'Copyright 2018, The Materials Virtual Lab'
__email__ = 'xil110@eng.ucsd.edu'
__date__ = '05/18/18'

from pymatgen.util.testing import PymatgenTest
import os
import numpy as np
from pymatgen import Structure
from pymatgen.core.grain_boundary_generator import GBGenerator

test_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..",
"test_files", "grain_boundary")


class Test_grain_boundary_generator(PymatgenTest):
@classmethod
def setUpClass(cls):
cls.Cu_prim = Structure.from_file(os.path.join(test_dir, "Cu_mp-30_primitive.cif"))
cls.GB_Cu_prim = GBGenerator(cls.Cu_prim)
cls.Cu_conv = Structure.from_file(os.path.join(test_dir,
"Cu_mp-30_conventional_standard.cif"))
cls.GB_Cu_conv = GBGenerator(cls.Cu_conv)
cls.Be = Structure.from_file(os.path.join(test_dir,
"Be_mp-87_conventional_standard.cif"))
cls.GB_Be = GBGenerator(cls.Be)
cls.Pa = Structure.from_file(os.path.join(test_dir,
"Pa_mp-62_conventional_standard.cif"))
cls.GB_Pa = GBGenerator(cls.Pa)
cls.Br = Structure.from_file(os.path.join(test_dir,
"Br_mp-23154_conventional_standard.cif"))
cls.GB_Br = GBGenerator(cls.Br)
cls.Bi = Structure.from_file(os.path.join(test_dir,
"Bi_mp-23152_primitive.cif"))
cls.GB_Bi = GBGenerator(cls.Bi)

def test_gb_from_parameters(self):
# from fcc primitive cell,axis[1,2,3],sigma 9.
gb_cu_123_prim1 = self.GB_Cu_prim.gb_from_parameters([1, 2, 3],
123.74898859588858,
expand_times=2)
lat_mat1 = gb_cu_123_prim1.lattice.matrix
c_vec1 = np.cross(lat_mat1[0], lat_mat1[1]) / np.linalg.norm(np.cross(lat_mat1[0], lat_mat1[1]))
c_len1 = np.dot(lat_mat1[2], c_vec1)
vol_ratio = gb_cu_123_prim1.volume / self.Cu_prim.volume
self.assertAlmostEqual(vol_ratio, 9 * 2 * 2, 8)
# test expand_times
gb_cu_123_prim2 = self.GB_Cu_prim.gb_from_parameters([1, 2, 3],
123.74898859588858,
expand_times=4)
lat_mat2 = gb_cu_123_prim2.lattice.matrix
c_vec2 = np.cross(lat_mat2[0], lat_mat2[1]) / np.linalg.norm(np.cross(lat_mat2[0], lat_mat2[1]))
c_len2 = np.dot(lat_mat2[2], c_vec2)
self.assertAlmostEqual(c_len2 / c_len1, 2)
# test vacuum layer
gb_cu_123_prim3 = self.GB_Cu_prim.gb_from_parameters([1, 2, 3],
123.74898859588858,
expand_times=4,
vacuum_thickness=1.5)
lat_mat3 = gb_cu_123_prim3.lattice.matrix
c_vec3 = np.cross(lat_mat3[0], lat_mat3[1]) / np.linalg.norm(np.cross(lat_mat3[0], lat_mat3[1]))
c_len3 = np.dot(lat_mat3[2], c_vec3)
self.assertAlmostEqual(c_len3 - c_len2, 1.5 * 2)

# from fcc conventional cell,axis [1,2,3], siamg 9
gb_cu_123_conv1 = self.GB_Cu_conv.gb_from_parameters([1, 2, 3],
123.74898859588858,
expand_times=4,
vacuum_thickness=1.5)
lat_mat1 = gb_cu_123_conv1.lattice.matrix
self.assertAlmostEqual(np.dot(lat_mat1[0], [1, 2, 3]), 0)
self.assertAlmostEqual(np.dot(lat_mat1[1], [1, 2, 3]), 0)
# test normal
gb_cu_123_conv2 = self.GB_Cu_conv.gb_from_parameters([1, 2, 3],
123.74898859588858,
expand_times=4,
vacuum_thickness=1.5,
normal=True)
lat_mat2 = gb_cu_123_conv2.lattice.matrix
c_vec2 = np.cross(lat_mat2[0], lat_mat2[1]) / np.linalg.norm(np.cross(lat_mat2[0], lat_mat2[1]))
ab_len2 = np.linalg.norm(np.cross(lat_mat2[2], c_vec2))
self.assertAlmostEqual(ab_len2, 0)
# test plane
gb_cu_123_conv3 = self.GB_Cu_conv.gb_from_parameters([1, 2, 3],
123.74898859588858,
expand_times=4,
vacuum_thickness=1.5,
plane=[1, 3, 1])
lat_mat3 = gb_cu_123_conv3.lattice.matrix
self.assertAlmostEqual(np.dot(lat_mat3[0], [1, 3, 1]), 0)
self.assertAlmostEqual(np.dot(lat_mat3[1], [1, 3, 1]), 0)

# from hex cell,axis [1,1,1], sigma 21
gb_Be_111_1 = self.GB_Be.gb_from_parameters([1, 1, 1],
128.246619878345,
ratio=[12, 5],
expand_times=4,
vacuum_thickness=1.5,
plane=[1, 2, 1])
lat_priv = self.Be.lattice.matrix
lat_mat1 = np.matmul(gb_Be_111_1.lattice.matrix, np.linalg.inv(lat_priv))
self.assertAlmostEqual(np.dot(lat_mat1[0], [1, 2, 1]), 0)
self.assertAlmostEqual(np.dot(lat_mat1[1], [1, 2, 1]), 0)
# test volume associated with sigma value
gb_Be_111_2 = self.GB_Be.gb_from_parameters([1, 1, 1], 128.246619878345, ratio=[12, 5],
expand_times=4)
vol_ratio = gb_Be_111_2.volume / self.Be.volume
self.assertAlmostEqual(vol_ratio, 21 * 2 * 4)
# test ratio = None, axis [0,0,1], sigma 7
gb_Be_111_3 = self.GB_Be.gb_from_parameters([0, 0, 1], 21.786789298261812,
ratio=[12, 5],
expand_times=4)
gb_Be_111_4 = self.GB_Be.gb_from_parameters([0, 0, 1], 21.786789298261812, ratio=None,
expand_times=4)
self.assertTupleEqual(gb_Be_111_3.lattice.abc, gb_Be_111_4.lattice.abc)
self.assertTupleEqual(gb_Be_111_3.lattice.angles, gb_Be_111_4.lattice.angles)
gb_Be_111_5 = self.GB_Be.gb_from_parameters([3, 1, 0], 180.0, ratio=[12, 5],
expand_times=4)
ouc_supercell_gb_Be_111_6 = self.GB_Be.gb_from_parameters([3, 1, 0], 180.0, ratio=None,
expand_times=4)
gb_Be_111_6 = ouc_supercell_gb_Be_111_6[2]
self.assertTupleEqual(gb_Be_111_5.lattice.abc, gb_Be_111_6.lattice.abc)
self.assertTupleEqual(gb_Be_111_5.lattice.angles, gb_Be_111_6.lattice.angles)

# gb from tetragonal cell, axis[1,1,1], sigma 15
gb_Pa_111_1 = self.GB_Pa.gb_from_parameters([1, 1, 1], 86.17744627072565, ratio=[4, 5],
expand_times=4)
vol_ratio = gb_Pa_111_1.volume / self.Pa.volume
self.assertAlmostEqual(vol_ratio, 15 * 2 * 4)

# gb from orthorhombic cell, axis[1,1,1], sigma 93
gb_Br_111_1 = self.GB_Br.gb_from_parameters([1, 1, 1], 95.55344419565849,
ratio=[10, 20, 21],
expand_times=4)
vol_ratio = gb_Br_111_1.volume / self.Br.volume
self.assertAlmostEqual(vol_ratio, 93 * 2 * 4)

# gb from rhombohedra cell, axis[1,2,0], sigma 81
gb_Bi_120_1 = self.GB_Bi.gb_from_parameters([1, 2, 0], 63.612200038756995,
ratio=[39, 10],
expand_times=4)
vol_ratio = gb_Bi_120_1.volume / self.Bi.volume
self.assertAlmostEqual(vol_ratio, 81 * 2 * 4)

def test_gb_from_matrices(self):
mat1 = np.array([[30, -11, -41], [20, 3, -48], [-7, -1, 17]])
mat2 = np.array([[10, -31, 41], [20, -31, 20], [-7, 11, -7]])
gb_Br_111_1 = self.GB_Br.gb_from_matrices(mat1, mat2, expand_times=4,
vacuum_thickness=3)
lat_conv = self.Br.lattice.matrix
lat_mat1 = np.rint(np.matmul(gb_Br_111_1.lattice.matrix, np.linalg.inv(lat_conv))).astype(int)
self.assertListEqual(list(lat_mat1[0]), list(mat1[0]))
self.assertListEqual(list(lat_mat1[1]), list(mat1[1]))

def test_enum_sigma_cubic(self):
true_100 = [5, 13, 17, 25, 29, 37, 41]
true_110 = [3, 9, 11, 17, 19, 27, 33, 41, 43]
true_111 = [3, 7, 13, 19, 21, 31, 37, 39, 43, 49]
sigma_100 = list(GBGenerator.enum_sigma_cubic(50, [1, 0, 0]).keys())
sigma_110 = list(GBGenerator.enum_sigma_cubic(50, [1, 1, 0]).keys())
sigma_111 = list(GBGenerator.enum_sigma_cubic(50, [1, 1, 1]).keys())
sigma_222 = list(GBGenerator.enum_sigma_cubic(50, [2, 2, 2]).keys())
sigma_888 = list(GBGenerator.enum_sigma_cubic(50, [8, 8, 8]).keys())

self.assertListEqual(sorted(true_100), sorted(sigma_100))
self.assertListEqual(sorted(true_110), sorted(sigma_110))
self.assertListEqual(sorted(true_111), sorted(sigma_111))
self.assertListEqual(sorted(true_111), sorted(sigma_222))
self.assertListEqual(sorted(true_111), sorted(sigma_888))

def test_enum_sigma_hex(self):
true_100 = [17, 18, 22, 27, 38, 41]
true_001 = [7, 13, 19, 31, 37, 43, 49]
true_210 = [10, 11, 14, 25, 35, 49]
sigma_100 = list(GBGenerator.enum_sigma_hex(50, [1, 0, 0], [8, 3]).keys())
sigma_001 = list(GBGenerator.enum_sigma_hex(50, [0, 0, 1], [8, 3]).keys())
sigma_210 = list(GBGenerator.enum_sigma_hex(50, [2, 1, 0], [8, 3]).keys())
sigma_420 = list(GBGenerator.enum_sigma_hex(50, [4, 2, 0], [8, 3]).keys())
sigma_840 = list(GBGenerator.enum_sigma_hex(50, [8, 4, 0], [8, 3]).keys())

self.assertListEqual(sorted(true_100), sorted(sigma_100))
self.assertListEqual(sorted(true_001), sorted(sigma_001))
self.assertListEqual(sorted(true_210), sorted(sigma_210))
self.assertListEqual(sorted(true_210), sorted(sigma_420))
self.assertListEqual(sorted(true_210), sorted(sigma_840))

def test_enum_sigma_tet(self):
true_100 = [5, 37, 41, 13, 3, 15, 39, 25, 17, 29]
true_331 = [9, 3, 21, 39, 7, 31, 43, 13, 19, 37, 49]
sigma_100 = list(GBGenerator.enum_sigma_tet(50, [1, 0, 0], [9, 1]).keys())
sigma_331 = list(GBGenerator.enum_sigma_tet(50, [3, 3, 1], [9, 1]).keys())

self.assertListEqual(sorted(true_100), sorted(sigma_100))
self.assertListEqual(sorted(true_331), sorted(sigma_331))

def test_enum_sigma_ort(self):
true_100 = [41, 37, 39, 5, 15, 17, 13, 3, 25, 29]
sigma_100 = list(GBGenerator.enum_sigma_ort(50, [1, 0, 0], [270, 30, 29]).keys())

self.assertListEqual(sorted(true_100), sorted(sigma_100))

def test_enum_sigma_rho(self):
true_100 = [7, 11, 43, 13, 41, 19, 47, 31]
sigma_100 = list(GBGenerator.enum_sigma_rho(50, [1, 0, 0], [15, 4]).keys())

self.assertListEqual(sorted(true_100), sorted(sigma_100))

def test_get_trans_mat(self):
mat1, mat2 = GBGenerator.get_trans_mat([1, 1, 1], 95.55344419565849, lat_type='o', ratio=[10, 20, 21],
surface=[21, 20, 10], normal=True)
self.assertAlmostEqual(np.dot(mat1[0], [21, 20, 10]), 0)
self.assertAlmostEqual(np.dot(mat1[1], [21, 20, 10]), 0)
self.assertAlmostEqual(np.linalg.det(mat1), np.linalg.det(mat2))
ab_len1 = np.linalg.norm(np.cross(mat1[2], [1, 1, 1]))
self.assertAlmostEqual(ab_len1, 0)

def test_get_rotation_angle_from_sigma(self):
true_angle = [12.680383491819821, 167.3196165081802]
angle = GBGenerator.get_rotation_angle_from_sigma(41, [1, 0, 0], lat_type='o', ratio=[270, 30, 29])
self.assertArrayAlmostEqual(true_angle, angle)
close_angle = [36.86989764584403, 143.13010235415598]
angle = GBGenerator.get_rotation_angle_from_sigma(6, [1, 0, 0], lat_type='o', ratio=[270, 30, 29])
self.assertArrayAlmostEqual(close_angle, angle)
28 changes: 28 additions & 0 deletions test_files/grain_boundary/Be_mp-87_conventional_standard.cif
@@ -0,0 +1,28 @@
# generated using pymatgen
data_Be
_symmetry_space_group_name_H-M 'P 1'
_cell_length_a 2.27983889
_cell_length_b 2.27983889
_cell_length_c 3.52777283
_cell_angle_alpha 90.00000000
_cell_angle_beta 90.00000000
_cell_angle_gamma 120.00000000
_symmetry_Int_Tables_number 1
_chemical_formula_structural Be
_chemical_formula_sum Be2
_cell_volume 15.8795999213
_cell_formula_units_Z 2
loop_
_symmetry_equiv_pos_site_id
_symmetry_equiv_pos_as_xyz
1 'x, y, z'
loop_
_atom_site_type_symbol
_atom_site_label
_atom_site_symmetry_multiplicity
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Be Be1 1 0.333333 0.666667 0.750000 1
Be Be2 1 0.666667 0.333333 0.250000 1
28 changes: 28 additions & 0 deletions test_files/grain_boundary/Bi_mp-23152_primitive.cif
@@ -0,0 +1,28 @@
# generated using pymatgen
data_Bi
_symmetry_space_group_name_H-M 'P 1'
_cell_length_a 4.79829946
_cell_length_b 4.79829946
_cell_length_c 4.79829946
_cell_angle_alpha 57.26305593
_cell_angle_beta 57.26305593
_cell_angle_gamma 57.26305593
_symmetry_Int_Tables_number 1
_chemical_formula_structural Bi
_chemical_formula_sum Bi2
_cell_volume 73.1939584902
_cell_formula_units_Z 2
loop_
_symmetry_equiv_pos_site_id
_symmetry_equiv_pos_as_xyz
1 'x, y, z'
loop_
_atom_site_type_symbol
_atom_site_label
_atom_site_symmetry_multiplicity
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Bi Bi1 1 0.765615 0.765615 0.765615 1
Bi Bi2 1 0.234385 0.234385 0.234385 1
34 changes: 34 additions & 0 deletions test_files/grain_boundary/Br_mp-23154_conventional_standard.cif
@@ -0,0 +1,34 @@
# generated using pymatgen
data_Br
_symmetry_space_group_name_H-M 'P 1'
_cell_length_a 4.25864728
_cell_length_b 8.45219038
_cell_length_c 8.74379641
_cell_angle_alpha 90.00000000
_cell_angle_beta 90.00000000
_cell_angle_gamma 90.00000000
_symmetry_Int_Tables_number 1
_chemical_formula_structural Br
_chemical_formula_sum Br8
_cell_volume 314.732056167
_cell_formula_units_Z 8
loop_
_symmetry_equiv_pos_site_id
_symmetry_equiv_pos_as_xyz
1 'x, y, z'
loop_
_atom_site_type_symbol
_atom_site_label
_atom_site_symmetry_multiplicity
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Br Br1 1 0.857527 0.000000 0.617751 1
Br Br2 1 0.142473 0.000000 0.382249 1
Br Br3 1 0.642473 0.000000 0.117751 1
Br Br4 1 0.357527 0.000000 0.882249 1
Br Br5 1 0.357527 0.500000 0.617751 1
Br Br6 1 0.642473 0.500000 0.382249 1
Br Br7 1 0.142473 0.500000 0.117751 1
Br Br8 1 0.857527 0.500000 0.882249 1
30 changes: 30 additions & 0 deletions test_files/grain_boundary/Cu_mp-30_conventional_standard.cif
@@ -0,0 +1,30 @@
# generated using pymatgen
data_Cu
_symmetry_space_group_name_H-M 'P 1'
_cell_length_a 3.61640724
_cell_length_b 3.61640724
_cell_length_c 3.61640724
_cell_angle_alpha 90.00000000
_cell_angle_beta 90.00000000
_cell_angle_gamma 90.00000000
_symmetry_Int_Tables_number 1
_chemical_formula_structural Cu
_chemical_formula_sum Cu4
_cell_volume 47.2968251068
_cell_formula_units_Z 4
loop_
_symmetry_equiv_pos_site_id
_symmetry_equiv_pos_as_xyz
1 'x, y, z'
loop_
_atom_site_type_symbol
_atom_site_label
_atom_site_symmetry_multiplicity
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Cu Cu1 1 0.000000 0.000000 0.000000 1
Cu Cu2 1 0.000000 0.500000 0.500000 1
Cu Cu3 1 0.500000 0.000000 0.500000 1
Cu Cu4 1 0.500000 0.500000 0.000000 1
27 changes: 27 additions & 0 deletions test_files/grain_boundary/Cu_mp-30_primitive.cif
@@ -0,0 +1,27 @@
# generated using pymatgen
data_Cu
_symmetry_space_group_name_H-M 'P 1'
_cell_length_a 2.55718608
_cell_length_b 2.55718608
_cell_length_c 2.55718608
_cell_angle_alpha 60.00000000
_cell_angle_beta 60.00000000
_cell_angle_gamma 60.00000000
_symmetry_Int_Tables_number 1
_chemical_formula_structural Cu
_chemical_formula_sum Cu1
_cell_volume 11.8242062767
_cell_formula_units_Z 1
loop_
_symmetry_equiv_pos_site_id
_symmetry_equiv_pos_as_xyz
1 'x, y, z'
loop_
_atom_site_type_symbol
_atom_site_label
_atom_site_symmetry_multiplicity
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Cu Cu1 1 0.000000 0.000000 0.000000 1