|
| 1 | +from pathlib import Path |
| 2 | +from typing import Literal, Tuple |
| 3 | +from ase.build import surface, make_supercell |
| 4 | +from ase.io import read |
| 5 | + |
| 6 | +from abacustest.constant import A2BOHR |
| 7 | +from abacustest.lib_prepare.stru import AbacusSTRU |
| 8 | + |
| 9 | +def build_slab(stru_file: Path, |
| 10 | + stru_type: Literal["cif", "poscar", "abacus/stru"] = "cif", |
| 11 | + miller_indices: Tuple[int, int, int] = (1, 0, 0), |
| 12 | + layers: int = 3, |
| 13 | + surface_supercell: Tuple[int, int] = (1, 1), |
| 14 | + vacuum: float = 15.0, |
| 15 | + vacuum_direction: Literal['a', 'b', 'c'] = 'b'): |
| 16 | + """ |
| 17 | + Build slab from given structure file. |
| 18 | +
|
| 19 | + Args: |
| 20 | + stru_file (Path): Path to structure file. |
| 21 | + stru_type (Literal["cif", "poscar", "abacus/stru"]): Type of structure file. Defaults to "cif". |
| 22 | + miller_indices (Tuple[int, int, int]): Miller indices of the surface. Defaults to (1, 0, 0), which means (100) surface of the structure. |
| 23 | + layers (int, optional): Number of layers of the surface. Note that the layers is number of equivalent layers, not number of layers of atoms. Defaults to 3. |
| 24 | + surface_supercell (Tuple[int, int], optional): Supercell size of the surface. Default is (1, 1), which means no supercell. |
| 25 | + vacuum (float, optional): Vacuum space between the cleaved surface and its periodic image. The total vacuum size will be twice this value. Units in Angstrom. Defaults to 15.0. |
| 26 | + vacuum_direction (Literal['a', 'b', 'c']): The direction of the vacuum space. Defaults to 'b'. |
| 27 | + Returns: |
| 28 | + A dictionary containing the path to the surface structure file. |
| 29 | + Keys: |
| 30 | + - surface_stru_file: Path to the surface structure file. The format of the generated structure file depends on the input structure file. |
| 31 | + Raises: |
| 32 | + ValueError: If stru_type is not supported. |
| 33 | + """ |
| 34 | + if stru_type == 'abacus/stru': |
| 35 | + stru = AbacusSTRU.read(stru_file, fmt="stru") |
| 36 | + stru_ase = stru.to("ase") |
| 37 | + elif stru_type in ['cif', 'poscar']: |
| 38 | + stru_ase = read(stru_file, format=stru_type) |
| 39 | + else: |
| 40 | + raise ValueError(f"Unsupported structure file type: {stru_type}") |
| 41 | + |
| 42 | + stru_surface = surface(stru_ase, miller_indices, layers, vacuum=vacuum/2, periodic=True) |
| 43 | + stru_surface = make_supercell(stru_surface, [[surface_supercell[0], 0, 0], [0, surface_supercell[1], 0], [0, 0, 1]]) |
| 44 | + stru_surface_abacusstru = AbacusSTRU.from_ase(stru_surface, metadata={ |
| 45 | + "lattice_constant": A2BOHR, |
| 46 | + "atom_type": "cartesian", |
| 47 | + }) |
| 48 | + stru_surface_abacusstru.sort() |
| 49 | + |
| 50 | + # Permute axis to set vacuum direction along given axis. The vacuum direction create by ase.build.surface is always along z axis. |
| 51 | + if vacuum_direction == "a": |
| 52 | + stru_surface_abacusstru.permute_lat_vec(mode="cab", rotate_cart_coord=True) |
| 53 | + elif vacuum_direction == "b": |
| 54 | + stru_surface_abacusstru.permute_lat_vec(mode="bca", rotate_cart_coord=True) |
| 55 | + elif vacuum_direction == "c": |
| 56 | + pass |
| 57 | + |
| 58 | + h, k, l = miller_indices |
| 59 | + surface_stru_file = Path(f"./{stru_file.stem}_{h}{k}{l}_{layers}layer.STRU").absolute() |
| 60 | + stru_surface_abacusstru.write(surface_stru_file, fmt=stru_type) |
| 61 | + |
| 62 | + return {'surface_stru_file': surface_stru_file} |
0 commit comments