Coverage for /builds/kinetik161/ase/ase/gui/surfaceslab.py: 70.75%

147 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +0000

1'''surfaceslab.py - Window for setting up surfaces 

2''' 

3import ase.build as build 

4import ase.gui.ui as ui 

5from ase.data import reference_states 

6from ase.gui.i18n import _, ngettext 

7from ase.gui.widgets import Element, pybutton 

8 

9introtext = _("""\ 

10 Use this dialog to create surface slabs. Select the element by 

11writing the chemical symbol or the atomic number in the box. Then 

12select the desired surface structure. Note that some structures can 

13be created with an othogonal or a non-orthogonal unit cell, in these 

14cases the non-orthogonal unit cell will contain fewer atoms. 

15 

16 If the structure matches the experimental crystal structure, you can 

17look up the lattice constant, otherwise you have to specify it 

18yourself.""") 

19 

20# Name, structure, orthogonal, function 

21surfaces = [(_('FCC(100)'), _('fcc'), 'ortho', build.fcc100), 

22 (_('FCC(110)'), _('fcc'), 'ortho', build.fcc110), 

23 (_('FCC(111)'), _('fcc'), 'both', build.fcc111), 

24 (_('FCC(211)'), _('fcc'), 'ortho', build.fcc211), 

25 (_('BCC(100)'), _('bcc'), 'ortho', build.bcc100), 

26 (_('BCC(110)'), _('bcc'), 'both', build.bcc110), 

27 (_('BCC(111)'), _('bcc'), 'both', build.bcc111), 

28 (_('HCP(0001)'), _('hcp'), 'both', build.hcp0001), 

29 (_('HCP(10-10)'), _('hcp'), 'ortho', build.hcp10m10), 

30 (_('DIAMOND(100)'), _('diamond'), 'ortho', build.diamond100), 

31 (_('DIAMOND(111)'), _('diamond'), 'non-ortho', build.diamond111)] 

32 

33structures, crystal, orthogonal, functions = zip(*surfaces) 

34 

35py_template = """ 

36from ase.build import {func} 

37 

38atoms = {func}(symbol='{symbol}', size={size}, 

39 a={a}, {c}vacuum={vacuum}, orthogonal={ortho}) 

40""" 

41 

42 

43class SetupSurfaceSlab: 

44 '''Window for setting up a surface.''' 

45 

46 def __init__(self, gui): 

47 self.element = Element('', self.apply) 

48 self.structure = ui.ComboBox(structures, structures, 

49 self.structure_changed) 

50 self.structure_warn = ui.Label('', 'red') 

51 self.orthogonal = ui.CheckButton('', True, self.make) 

52 self.lattice_a = ui.SpinBox(3.2, 0.0, 10.0, 0.001, self.make) 

53 self.retrieve = ui.Button(_('Get from database'), 

54 self.structure_changed) 

55 self.lattice_c = ui.SpinBox(None, 0.0, 10.0, 0.001, self.make) 

56 self.x = ui.SpinBox(1, 1, 30, 1, self.make) 

57 self.x_warn = ui.Label('', 'red') 

58 self.y = ui.SpinBox(1, 1, 30, 1, self.make) 

59 self.y_warn = ui.Label('', 'red') 

60 self.z = ui.SpinBox(1, 1, 30, 1, self.make) 

61 self.vacuum_check = ui.CheckButton('', False, self.vacuum_checked) 

62 self.vacuum = ui.SpinBox(5, 0, 40, 0.01, self.make) 

63 self.description = ui.Label('') 

64 

65 win = self.win = ui.Window(_('Surface'), wmtype='utility') 

66 win.add(ui.Text(introtext)) 

67 win.add(self.element) 

68 win.add([_('Structure:'), self.structure, self.structure_warn]) 

69 win.add([_('Orthogonal cell:'), self.orthogonal]) 

70 win.add([_('Lattice constant:')]) 

71 win.add([_('\ta'), self.lattice_a, ('Å'), self.retrieve]) 

72 win.add([_('\tc'), self.lattice_c, ('Å')]) 

73 win.add([_('Size:')]) 

74 win.add([_('\tx: '), self.x, _(' unit cells'), self.x_warn]) 

75 win.add([_('\ty: '), self.y, _(' unit cells'), self.y_warn]) 

76 win.add([_('\tz: '), self.z, _(' unit cells')]) 

77 win.add([_('Vacuum: '), self.vacuum_check, self.vacuum, ('Å')]) 

78 win.add(self.description) 

79 # TRANSLATORS: This is a title of a window. 

80 win.add([pybutton(_('Creating a surface.'), self.make), 

81 ui.Button(_('Apply'), self.apply), 

82 ui.Button(_('OK'), self.ok)]) 

83 

84 self.element.grab_focus() 

85 self.gui = gui 

86 self.atoms = None 

87 self.lattice_c.active = False 

88 self.vacuum.active = False 

89 self.structure_changed() 

90 

91 def vacuum_checked(self, *args): 

92 if self.vacuum_check.var.get(): 

93 self.vacuum.active = True 

94 else: 

95 self.vacuum.active = False 

96 self.make() 

97 

98 def get_lattice(self, *args): 

99 if self.element.symbol is None: 

100 return 

101 ref = reference_states[self.element.Z] 

102 symmetry = "unknown" 

103 for struct in surfaces: 

104 if struct[0] == self.structure.value: 

105 symmetry = struct[1] 

106 if ref['symmetry'] != symmetry: 

107 # TRANSLATORS: E.g. "... assume fcc crystal structure for Au" 

108 self.structure_warn.text = (_('Error: Reference values assume {} ' 

109 'crystal structure for {}!'). 

110 format(ref['symmetry'], 

111 self.element.symbol)) 

112 else: 

113 if symmetry == 'fcc' or symmetry == 'bcc' or symmetry == 'diamond': 

114 self.lattice_a.value = ref['a'] 

115 elif symmetry == 'hcp': 

116 self.lattice_a.value = ref['a'] 

117 self.lattice_c.value = ref['a'] * ref['c/a'] 

118 self.make() 

119 

120 def structure_changed(self, *args): 

121 for surface in surfaces: 

122 if surface[0] == self.structure.value: 

123 if surface[2] == 'ortho': 

124 self.orthogonal.var.set(True) 

125 self.orthogonal.check['state'] = ['disabled'] 

126 elif surface[2] == 'non-ortho': 

127 self.orthogonal.var.set(False) 

128 self.orthogonal.check['state'] = ['disabled'] 

129 else: 

130 self.orthogonal.check['state'] = ['normal'] 

131 

132 if surface[1] == _('hcp'): 

133 self.lattice_c.active = True 

134 self.lattice_c.value = round(self.lattice_a.value * 

135 ((8.0 / 3.0) ** (0.5)), 3) 

136 else: 

137 self.lattice_c.active = False 

138 self.lattice_c.value = 'None' 

139 self.get_lattice() 

140 

141 def make(self, *args): 

142 symbol = self.element.symbol 

143 self.atoms = None 

144 self.description.text = '' 

145 self.python = None 

146 self.x_warn.text = '' 

147 self.y_warn.text = '' 

148 if symbol is None: 

149 return 

150 

151 x = self.x.value 

152 y = self.y.value 

153 z = self.z.value 

154 size = (x, y, z) 

155 a = self.lattice_a.value 

156 c = self.lattice_c.value 

157 vacuum = self.vacuum.value 

158 if not self.vacuum_check.var.get(): 

159 vacuum = None 

160 ortho = self.orthogonal.var.get() 

161 

162 ortho_warn_even = _('Please enter an even value for orthogonal cell') 

163 

164 struct = self.structure.value 

165 if struct == _('BCC(111)') and (not (y % 2 == 0) and ortho): 

166 self.y_warn.text = ortho_warn_even 

167 return 

168 if struct == _('BCC(110)') and (not (y % 2 == 0) and ortho): 

169 self.y_warn.text = ortho_warn_even 

170 return 

171 if struct == _('FCC(111)') and (not (y % 2 == 0) and ortho): 

172 self.y_warn.text = ortho_warn_even 

173 return 

174 if struct == _('FCC(211)') and (not (x % 3 == 0) and ortho): 

175 self.x_warn.text = _('Please enter a value divisible by 3' 

176 ' for orthogonal cell') 

177 return 

178 if struct == _('HCP(0001)') and (not (y % 2 == 0) and ortho): 

179 self.y_warn.text = ortho_warn_even 

180 return 

181 if struct == _('HCP(10-10)') and (not (y % 2 == 0) and ortho): 

182 self.y_warn.text = ortho_warn_even 

183 return 

184 

185 for surface in surfaces: 

186 if surface[0] == struct: 

187 c_py = "" 

188 if surface[1] == _('hcp'): 

189 self.atoms = surface[3](symbol, size, a, c, vacuum, ortho) 

190 c_py = f"{c}, " 

191 else: 

192 self.atoms = surface[3](symbol, size, a, vacuum, ortho) 

193 

194 if vacuum is not None: 

195 vacuumtext = _(' Vacuum: {} Å.').format(vacuum) 

196 else: 

197 vacuumtext = '' 

198 

199 natoms = len(self.atoms) 

200 label = ngettext( 

201 # TRANSLATORS: e.g. "Au fcc100 surface with 2 atoms." 

202 # or "Au fcc100 surface with 2 atoms. Vacuum: 5 Å." 

203 '{symbol} {surf} surface with one atom.{vacuum}', 

204 '{symbol} {surf} surface with {natoms} atoms.{vacuum}', 

205 natoms).format(symbol=symbol, 

206 surf=surface[3].__name__, 

207 natoms=natoms, 

208 vacuum=vacuumtext) 

209 

210 self.description.text = label 

211 return py_template.format(func=surface[3].__name__, a=a, 

212 c=c_py, symbol=symbol, size=size, 

213 ortho=ortho, vacuum=vacuum) 

214 

215 def apply(self, *args): 

216 self.make() 

217 if self.atoms is not None: 

218 self.gui.new_atoms(self.atoms) 

219 return True 

220 else: 

221 ui.error(_('No valid atoms.'), 

222 _('You have not (yet) specified a consistent ' 

223 'set of parameters.')) 

224 return False 

225 

226 def ok(self, *args): 

227 if self.apply(): 

228 self.win.close()