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
« 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
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.
16 If the structure matches the experimental crystal structure, you can
17look up the lattice constant, otherwise you have to specify it
18yourself.""")
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)]
33structures, crystal, orthogonal, functions = zip(*surfaces)
35py_template = """
36from ase.build import {func}
38atoms = {func}(symbol='{symbol}', size={size},
39 a={a}, {c}vacuum={vacuum}, orthogonal={ortho})
40"""
43class SetupSurfaceSlab:
44 '''Window for setting up a surface.'''
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('')
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)])
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()
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()
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()
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']
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()
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
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()
162 ortho_warn_even = _('Please enter an even value for orthogonal cell')
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
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)
194 if vacuum is not None:
195 vacuumtext = _(' Vacuum: {} Å.').format(vacuum)
196 else:
197 vacuumtext = ''
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)
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)
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
226 def ok(self, *args):
227 if self.apply():
228 self.win.close()