Coverage for wulffpack/winterbottom.py: 100%

25 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-02 08:51 +0000

1from typing import Dict 

2import numpy as np 

3from ase import Atoms 

4from .core import BaseParticle 

5from .core.form import setup_forms 

6from .core.geometry import (get_symmetries, 

7 break_symmetry, 

8 get_angle, 

9 get_rotation_matrix, 

10 get_standardized_structure) 

11 

12 

13class Winterbottom(BaseParticle): 

14 """ 

15 A `Winterbottom` object is a Winterbottom construction, i.e., 

16 the lowest energy shape adopted by a single crystalline particle 

17 in contact with an interface. 

18 

19 Parameters 

20 ---------- 

21 surface_energies 

22 A dictionary with surface energies, where keys are 

23 Miller indices and values surface energies (per area) 

24 in a unit of choice, such as J/m^2. 

25 interface_direction 

26 Miller indices for the interface facet. 

27 interface_energy 

28 Energy per area for twin boundaries. 

29 primtive_structure 

30 primitive structure to implicitly define the point group as 

31 well as the atomic structure used if an atomic structure 

32 is requested. By default, an Au FCC structure is used. 

33 natoms 

34 Together with `primitive_structure`, this parameter 

35 defines the volume of the particle. If an atomic structure 

36 is requested, the number of atoms will as closely as possible 

37 match this value. 

38 symprec 

39 Numerical tolerance for symmetry analysis, forwarded to spglib. 

40 tol 

41 Numerical tolerance parameter. 

42 

43 Example 

44 ------- 

45 The following example illustrates some possible uses of a 

46 `Winterbottom` object:: 

47 

48 >>> from wulffpack import Winterbottom 

49 >>> from ase.build import bulk 

50 >>> from ase.io import write 

51 >>> surface_energies = {(1, 1, 0): 1.0, (1, 0, 0): 1.08} 

52 >>> prim = bulk('Fe', a=4.1, crystalstructure='bcc') 

53 >>> particle = Winterbottom(surface_energies=surface_energies, 

54 ... interface_direction=(3, 2, 1), 

55 ... interface_energy=0.4, 

56 ... primitive_structure=prim) 

57 >>> particle.view() 

58 >>> write('winterbottom.xyz', particle.atoms) # Writes atomic structure to file 

59 

60 """ 

61 

62 def __init__(self, 

63 surface_energies: Dict[tuple, float], 

64 interface_direction: tuple, 

65 interface_energy: float, 

66 primitive_structure: Atoms = None, 

67 natoms: int = 1000, 

68 symprec: float = 1e-5, 

69 tol: float = 1e-5): 

70 standardized_structure = get_standardized_structure(primitive_structure, symprec=symprec) 

71 full_symmetries = get_symmetries(standardized_structure, symprec=symprec) 

72 broken_symmetries = break_symmetry(full_symmetries, 

73 [interface_direction]) 

74 

75 if interface_energy > min(surface_energies.values()): 

76 raise ValueError('The construction expects an interface energy ' 

77 'that is smaller than than the smallest ' 

78 'surface energy.') 

79 surface_energies = surface_energies.copy() 

80 surface_energies['interface'] = interface_energy 

81 forms = setup_forms(surface_energies, 

82 standardized_structure.cell.T, 

83 broken_symmetries, 

84 full_symmetries, 

85 interface=interface_direction) 

86 

87 super().__init__(forms=forms, 

88 standardized_structure=standardized_structure, 

89 natoms=natoms, 

90 tol=tol) 

91 

92 # Rotate particle such that the interface aligns with the plane z=0 

93 target = (0, 0, -1) 

94 rotation_axis = np.cross(interface_direction, target) 

95 angle = get_angle(interface_direction, target) 

96 R = get_rotation_matrix(angle, rotation_axis.astype(float)) 

97 self.rotate_particle(R) 

98 

99 @property 

100 def atoms(self): 

101 """ 

102 Returns an ASE Atoms object 

103 """ 

104 return self._get_atoms()