Statistiques
| Révision :

root / ase / gui / calculator.py @ 3

Historique | Voir | Annoter | Télécharger (67,5 ko)

1
# encoding: utf-8
2
"calculator.py - module for choosing a calculator."
3

    
4
import gtk
5
import os
6
import numpy as np
7
from copy import copy
8
from ase.gui.setupwindow import SetupWindow
9
from ase.gui.progress import DefaultProgressIndicator, GpawProgressIndicator
10
from ase.gui.widgets import pack, oops, cancel_apply_ok
11
from ase import Atoms
12
from ase.data import chemical_symbols
13
import ase
14

    
15
# Asap and GPAW may be imported if selected.
16

    
17
introtext = """\
18
To make most calculations on the atoms, a Calculator object must first
19
be associated with it.  ASE supports a number of calculators, supporting
20
different elements, and implementing different physical models for the
21
interatomic interactions.\
22
"""
23

    
24
# Informational text about the calculators
25
lj_info_txt = """\
26
The Lennard-Jones pair potential is one of the simplest
27
possible models for interatomic interactions, mostly
28
suitable for noble gasses and model systems.
29

30
Interactions are described by an interaction length and an
31
interaction strength.\
32
"""
33

    
34
emt_info_txt = """\
35
The EMT potential is a many-body potential, giving a
36
good description of the late transition metals crystalling
37
in the FCC crystal structure.  The elements described by the
38
main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and
39
Au, the Al potential is however not suitable for materials
40
science application, as the stacking fault energy is wrong.
41

42
A number of parameter sets are provided.
43

44
<b>Default parameters:</b>
45

46
The default EMT parameters, as published in K. W. Jacobsen,
47
P. Stoltze and J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).
48

49
<b>Alternative Cu, Ag and Au:</b>
50

51
An alternative set of parameters for Cu, Ag and Au,
52
reoptimized to experimental data including the stacking
53
fault energies by Torben Rasmussen (partly unpublished).
54

55
<b>Ruthenium:</b>
56

57
Parameters for Ruthenium, as published in J. Gavnholt and
58
J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).
59

60
<b>Metallic glasses:</b>
61

62
Parameters for MgCu and CuZr metallic glasses. MgCu
63
parameters are in N. P. Bailey, J. Schiøtz and
64
K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004).
65
CuZr in A. Paduraru, A. Kenoufi, N. P. Bailey and
66
J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).
67
"""
68

    
69
aseemt_info_txt = """\
70
The EMT potential is a many-body potential, giving a
71
good description of the late transition metals crystalling
72
in the FCC crystal structure.  The elements described by the
73
main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and
74
Au.  In addition, this implementation allows for the use of
75
H, N, O and C adatoms, although the description of these is
76
most likely not very good.
77

78
<b>This is the ASE implementation of EMT.</b> For large
79
simulations the ASAP implementation is more suitable; this
80
implementation is mainly to make EMT available when ASAP is
81
not installed.
82
"""
83

    
84
brenner_info_txt = """\
85
The Brenner potential is a reactive bond-order potential for
86
carbon and hydrocarbons.  As a bond-order potential, it takes
87
into account that carbon orbitals can hybridize in different
88
ways, and that carbon can form single, double and triple
89
bonds.  That the potential is reactive means that it can
90
handle gradual changes in the bond order as chemical bonds
91
are formed or broken.
92

93
The Brenner potential is implemented in Asap, based on a
94
C implentation published at http://www.rahul.net/pcm/brenner/ .
95

96
The potential is documented here:
97
  Donald W Brenner, Olga A Shenderova, Judith A Harrison,
98
  Steven J Stuart, Boris Ni and Susan B Sinnott:
99
  "A second-generation reactive empirical bond order (REBO)
100
  potential energy expression for hydrocarbons",
101
  J. Phys.: Condens. Matter 14 (2002) 783-802.
102
  doi: 10.1088/0953-8984/14/4/312
103
"""
104

    
105

    
106
gpaw_info_txt = """\
107
GPAW implements Density Functional Theory using a
108
<b>G</b>rid-based real-space representation of the wave
109
functions, and the <b>P</b>rojector <b>A</b>ugmented <b>W</b>ave
110
method for handling the core regions.  
111
"""
112

    
113
aims_info_txt = """\
114
FHI-aims is an external package implementing density 
115
functional theory and quantum chemical methods using 
116
all-electron methods and a numeric local orbital basis set. 
117
For full details, see http://www.fhi-berlin.mpg.de/aims/ 
118
or Comp. Phys. Comm. v180 2175 (2009). The ASE 
119
documentation contains information on the keywords and 
120
functionalities available within this interface. 
121
"""
122

    
123
aims_pbc_warning_text = """\
124
WARNING:
125
Your system seems to have more than zero but less than 
126
three periodic dimensions. Please check that this is 
127
really what you want to compute. Assuming full 
128
3D periodicity for this calculator."""
129

    
130
vasp_info_txt = """\
131
VASP is an external package implementing density 
132
functional functional theory using pseudopotentials 
133
or the projector-augmented wave method together 
134
with a plane wave basis set. For full details, see
135
http://cms.mpi.univie.ac.at/vasp/vasp/
136
"""
137

    
138
emt_parameters = (
139
    ("Default (Al, Ni, Cu, Pd, Ag, Pt, Au)", None),
140
    ("Alternative Cu, Ag and Au", "EMTRasmussenParameters"),
141
    ("Ruthenium", "EMThcpParameters"),
142
    ("CuMg and CuZr metallic glass", "EMTMetalGlassParameters")
143
    )
144

    
145
class SetCalculator(SetupWindow):
146
    "Window for selecting a calculator."
147

    
148
    # List the names of the radio button attributes
149
    radios = ("none", "lj", "emt", "aseemt", "brenner", "gpaw", "aims", "vasp")
150
    # List the names of the parameter dictionaries
151
    paramdicts = ("lj_parameters",)
152
    # The name used to store parameters on the gui object
153
    classname = "SetCalculator"
154
    
155
    def __init__(self, gui):
156
        SetupWindow.__init__(self)
157
        self.set_title("Select calculator")
158
        vbox = gtk.VBox()
159
        
160
        # Intoductory text
161
        self.packtext(vbox, introtext)
162
        
163
        pack(vbox, [gtk.Label("Calculator:")])
164

    
165
        # No calculator (the default)
166
        self.none_radio = gtk.RadioButton(None, "None")
167
        pack(vbox, [self.none_radio])
168

    
169
        # Lennard-Jones
170
        self.lj_radio = gtk.RadioButton(self.none_radio, "Lennard-Jones (ASAP)")
171
        self.lj_setup = gtk.Button("Setup")
172
        self.lj_info = InfoButton(lj_info_txt)
173
        self.lj_setup.connect("clicked", self.lj_setup_window)
174
        self.pack_line(vbox, self.lj_radio, self.lj_setup, self.lj_info)
175

    
176
        # EMT
177
        self.emt_radio = gtk.RadioButton(
178
            self.none_radio, "EMT - Effective Medium Theory (ASAP)")
179
        self.emt_setup = gtk.combo_box_new_text()
180
        self.emt_param_info = {}
181
        for p in emt_parameters:
182
            self.emt_setup.append_text(p[0])
183
            self.emt_param_info[p[0]] = p[1]
184
        self.emt_setup.set_active(0)
185
        self.emt_info = InfoButton(emt_info_txt)
186
        self.pack_line(vbox, self.emt_radio, self.emt_setup, self.emt_info)
187

    
188
        # EMT (ASE implementation)
189
        self.aseemt_radio = gtk.RadioButton(
190
            self.none_radio, "EMT - Effective Medium Theory (ASE)")
191
        self.aseemt_info = InfoButton(aseemt_info_txt)
192
        self.pack_line(vbox, self.aseemt_radio, None, self.aseemt_info)
193

    
194
        # Brenner potential
195
        self.brenner_radio = gtk.RadioButton(
196
            self.none_radio, "Brenner Potential (ASAP)")
197
        self.brenner_info = InfoButton(brenner_info_txt)
198
        self.pack_line(vbox, self.brenner_radio, None, self.brenner_info)
199
        
200
        # GPAW
201
        self.gpaw_radio = gtk.RadioButton(self.none_radio,
202
                                          "Density Functional Theory (GPAW)")
203
        self.gpaw_setup = gtk.Button("Setup")
204
        self.gpaw_info = InfoButton(gpaw_info_txt)
205
        self.gpaw_setup.connect("clicked", self.gpaw_setup_window)
206
        self.pack_line(vbox, self.gpaw_radio, self.gpaw_setup, self.gpaw_info)
207
        
208
        # FHI-aims
209
        self.aims_radio = gtk.RadioButton(self.none_radio, 
210
                                          "Density Functional Theory (FHI-aims)")
211
        self.aims_setup = gtk.Button("Setup")
212
        self.aims_info = InfoButton(aims_info_txt)
213
        self.aims_setup.connect("clicked", self.aims_setup_window)
214
        self.pack_line(vbox, self.aims_radio, self.aims_setup, self.aims_info)
215
        
216
        # VASP
217
        self.vasp_radio = gtk.RadioButton(self.none_radio, 
218
                                          "Density Functional Theory (VASP)")
219
        self.vasp_setup = gtk.Button("Setup")
220
        self.vasp_info = InfoButton(vasp_info_txt)
221
        self.vasp_setup.connect("clicked", self.vasp_setup_window)
222
        self.pack_line(vbox, self.vasp_radio, self.vasp_setup, self.vasp_info)
223

    
224
        # Buttons etc.
225
        pack(vbox, gtk.Label(""))
226
        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
227
                               apply=self.apply,
228
                               ok=self.ok)
229
        pack(vbox, [buts], end=True, bottom=True)
230
        self.check = gtk.CheckButton("Check that the calculator is reasonable.")
231
        self.check.set_active(True)
232
        fr = gtk.Frame()
233
        fr.add(self.check)
234
        fr.show_all()
235
        pack(vbox, [fr], end=True, bottom=True)
236
        
237
        # Finalize setup
238
        self.add(vbox)
239
        vbox.show()
240
        self.show()
241
        self.gui = gui
242
        self.load_state()
243
        
244
    def pack_line(self, box, radio, setup, info):
245
        hbox = gtk.HBox()
246
        hbox.pack_start(radio, 0, 0)
247
        hbox.pack_start(gtk.Label("  "), 0, 0)
248
        hbox.pack_end(info, 0, 0)
249
        if setup is not None:
250
            radio.connect("toggled", self.radio_toggled, setup)
251
            setup.set_sensitive(False)
252
            hbox.pack_end(setup, 0, 0)
253
        hbox.show_all()
254
        box.pack_start(hbox, 0, 0)
255

    
256
    def radio_toggled(self, radio, button):
257
        button.set_sensitive(radio.get_active())
258

    
259
    def lj_setup_window(self, widget):
260
        if not self.get_atoms():
261
            return
262
        lj_param = getattr(self, "lj_parameters", None)
263
        LJ_Window(self, lj_param, "lj_parameters")
264
        # When control is retuned, self.lj_parameters has been set.
265
        
266
    def gpaw_setup_window(self, widget):
267
        if not self.get_atoms():
268
            return
269
        gpaw_param = getattr(self, "gpaw_parameters", None)
270
        GPAW_Window(self, gpaw_param, "gpaw_parameters")
271
        # When control is retuned, self.gpaw_parameters has been set.
272
        
273
    def aims_setup_window(self, widget):
274
        if not self.get_atoms():
275
            return
276
        aims_param = getattr(self, "aims_parameters", None)
277
        AIMS_Window(self, aims_param, "aims_parameters")        
278
        # When control is retuned, self.aims_parameters has been set.
279

    
280
    def vasp_setup_window(self, widget):
281
        if not self.get_atoms():
282
            return
283
        vasp_param = getattr(self, "vasp_parameters", None)
284
        VASP_Window(self, vasp_param, "vasp_parameters")
285
        # When control is retuned, self.vasp_parameters has been set.
286

    
287
    def get_atoms(self):
288
        "Make an atoms object from the active image"
289
        images = self.gui.images
290
        if images.natoms < 1:
291
            oops("No atoms present")
292
            return False
293
        self.atoms = Atoms(positions=images.P[0], symbols=images.Z,
294
                           cell=images.A[0], pbc=images.pbc)
295
        if not images.dynamic.all(): 
296
            from ase.constraints import FixAtoms
297
            self.atoms.set_constraint(FixAtoms(mask=1-images.dynamic))
298
        return True
299

    
300
    def apply(self, *widget):
301
        if self.do_apply():
302
            self.save_state()
303
            return True
304
        else:
305
            return False
306
        
307
    def do_apply(self):
308
        nochk = not self.check.get_active()
309
        self.gui.simulation["progress"] = DefaultProgressIndicator()
310
        if self.none_radio.get_active():
311
            self.gui.simulation['calc'] = None
312
            return True
313
        elif self.lj_radio.get_active():
314
            if nochk or self.lj_check():
315
                self.choose_lj()
316
                return True
317
        elif self.emt_radio.get_active():
318
            if nochk or self.emt_check():
319
                self.choose_emt()
320
                return True
321
        elif self.aseemt_radio.get_active():
322
            if nochk or self.aseemt_check():
323
                self.choose_aseemt()
324
                return True
325
        elif self.brenner_radio.get_active():
326
            if nochk or self.brenner_check():
327
                self.choose_brenner()
328
                return True
329
        elif self.gpaw_radio.get_active():
330
            if nochk or self.gpaw_check():
331
                self.choose_gpaw()
332
                return True
333
        elif self.aims_radio.get_active():
334
            if nochk or self.aims_check():
335
                self.choose_aims()
336
                return True
337
        elif self.vasp_radio.get_active():
338
            if nochk or self.vasp_check():
339
                self.choose_vasp()
340
                return True  
341
        return False
342

    
343
    def ok(self, *widget):
344
        if self.apply():
345
            self.destroy()
346

    
347
    def save_state(self):
348
        state = {}
349
        for r in self.radios:
350
            radiobutton = getattr(self, r+"_radio")
351
            if radiobutton.get_active():
352
                state["radio"] = r
353
        state["emtsetup"] = self.emt_setup.get_active()
354
        state["check"] = self.check.get_active()
355
        for p in self.paramdicts:
356
            if hasattr(self, p):
357
                state[p] = getattr(self, p)
358
        self.gui.module_state[self.classname] = state
359

    
360
    def load_state(self):
361
        try:
362
            state = self.gui.module_state[self.classname]
363
        except KeyError:
364
            return
365
        r = state["radio"]
366
        radiobutton = getattr(self, r+"_radio")
367
        radiobutton.set_active(True)
368
        self.emt_setup.set_active(state["emtsetup"])
369
        self.check.set_active(state["check"])
370
        for p in self.paramdicts:
371
            if state.has_key(p):
372
                setattr(self, p, state[p])
373
            
374
    def lj_check(self):
375
        try:
376
            import asap3
377
        except ImportError:
378
            oops("ASAP is not installed. (Failed to import asap3)")
379
            return False
380
        if not hasattr(self, "lj_parameters"):
381
            oops("You must set up the Lennard-Jones parameters")
382
            return False
383
        try:
384
            self.atoms.set_calculator(asap3.LennardJones(**self.lj_parameters))
385
        except (asap3.AsapError, TypeError, ValueError), e:
386
            oops("Could not create useful Lennard-Jones calculator.",
387
                 str(e))
388
            return False
389
        return True
390

    
391
    def choose_lj(self):
392
        # Define a function on the fly!
393
        import asap3
394
        def lj_factory(p=self.lj_parameters, lj=asap3.LennardJones):
395
            return lj(**p)
396
        self.gui.simulation["calc"] = lj_factory
397

    
398
    def emt_get(self):
399
        import asap3
400
        provider_name = self.emt_setup.get_active_text()
401
        provider =  self.emt_param_info[provider_name]
402
        if provider is not None:
403
            provider = getattr(asap3, provider)
404
        return (asap3.EMT, provider, asap3)
405
                                      
406
    def emt_check(self):
407
        if not self.get_atoms():
408
            return False
409
        try:
410
            emt, provider, asap3 = self.emt_get()
411
        except ImportError:
412
            oops("ASAP is not installed. (Failed to import asap3)")
413
            return False
414
        try:
415
            if provider is not None:
416
                self.atoms.set_calculator(emt(provider()))
417
            else:
418
                self.atoms.set_calculator(emt())
419
        except (asap3.AsapError, TypeError, ValueError), e:
420
            oops("Could not attach EMT calculator to the atoms.",
421
                 str(e))
422
            return False
423
        return True
424

    
425
    def choose_emt(self):
426
        emt, provider, asap3 = self.emt_get()
427
        if provider is None:
428
            emt_factory = emt
429
        else:
430
            def emt_factory(emt=emt, prov=provider):
431
                return emt(prov())
432
        self.gui.simulation["calc"] = emt_factory
433

    
434
    def aseemt_check(self):
435
        return self.element_check("ASE EMT", ['H', 'Al', 'Cu', 'Ag', 'Au',
436
                                              'Ni', 'Pd', 'Pt', 'C', 'N', 'O'])
437

    
438
    def choose_aseemt(self):
439
        self.gui.simulation["calc"] = ase.EMT
440
        ase.EMT.disabled = False  # In case Asap has been imported.
441

    
442
    def brenner_check(self):
443
        try:
444
            import asap3
445
        except ImportError:
446
            oops("ASAP is not installed. (Failed to import asap3)")
447
            return False
448
        return self.element_check("Brenner potential", ['H', 'C', 'Si'])
449

    
450
    def choose_brenner(self):
451
        import asap3
452
        self.gui.simulation["calc"] = asap3.BrennerPotential
453

    
454
    def choose_aseemt(self):
455
        self.gui.simulation["calc"] = ase.EMT
456
        ase.EMT.disabled = False  # In case Asap has been imported.
457

    
458
    def gpaw_check(self):
459
        try:
460
            import gpaw
461
        except ImportError:
462
            oops("GPAW is not installed. (Failed to import gpaw)")
463
            return False
464
        if not hasattr(self, "gpaw_parameters"):
465
            oops("You must set up the GPAW parameters")
466
            return False
467
        return True
468

    
469
    def choose_gpaw(self):
470
        # This reuses the same GPAW object.
471
        try:
472
            import gpaw
473
        except ImportError:
474
            oops("GPAW is not installed. (Failed to import gpaw)")
475
            return False
476
        p = self.gpaw_parameters
477
        use = ["xc", "kpts", "mode"]
478
        if p["use_h"]:
479
            use.append("h")
480
        else:
481
            use.append("gpts")
482
        if p["mode"] == "lcao":
483
            use.append("basis")
484
        gpaw_param = {}
485
        for s in use:
486
            gpaw_param[s] = p[s]
487
        if p["use mixer"]:
488
            mx = getattr(gpaw, p["mixer"])
489
            mx_args = {}
490
            mx_arg_n = ["beta", "nmaxold", "weight"]
491
            if p["mixer"] == "MixerDiff":
492
                mx_arg_n.extend(["beta_m", "nmaxold_m", "weight_m"])
493
            for s in mx_arg_n:
494
                mx_args[s] = p[s]
495
            gpaw_param["mixer"] = mx(**mx_args)
496
        progress = GpawProgressIndicator()
497
        self.gui.simulation["progress"] = progress
498
        gpaw_param["txt"] = progress.get_gpaw_stream()
499
        gpaw_calc = gpaw.GPAW(**gpaw_param)
500
        def gpaw_factory(calc = gpaw_calc):
501
            return calc
502
        self.gui.simulation["calc"] = gpaw_factory
503
                
504
    def aims_check(self):
505
        if not hasattr(self, "aims_parameters"):
506
            oops("You must set up the FHI-aims parameters")
507
            return False
508
        return True
509

    
510
    def choose_aims(self):
511
        param = self.aims_parameters
512
        from ase.calculators.aims import Aims
513
        calc_aims = Aims(**param)
514
        def aims_factory(calc = calc_aims):
515
            return calc
516
        self.gui.simulation["calc"] = aims_factory
517

    
518
    def vasp_check(self):
519
        if not hasattr(self, "vasp_parameters"):
520
            oops("You must set up the VASP parameters")
521
            return False
522
        return True
523

    
524
    def choose_vasp(self):
525
        param = self.vasp_parameters
526
        from ase.calculators.vasp import Vasp
527
        calc_vasp = Vasp(**param)
528
        def vasp_factory(calc = calc_vasp):
529
            return calc
530
        self.gui.simulation["calc"] = vasp_factory
531

    
532
    def element_check(self, name, elements):
533
        "Check that all atoms are allowed"
534
        elements = [ase.data.atomic_numbers[s] for s in elements]
535
        elements_dict = {}
536
        for e in elements:
537
            elements_dict[e] = True
538
        if not self.get_atoms():
539
            return False
540
        try:
541
            for e in self.atoms.get_atomic_numbers():
542
                elements_dict[e]
543
        except KeyError:
544
            oops("Element %s is not allowed by the '%s' calculator"
545
                 % (ase.data.chemical_symbols[e], name))
546
            return False
547
        return True
548
    
549
class InfoButton(gtk.Button):
550
    def __init__(self, txt):
551
        gtk.Button.__init__(self, "Info")
552
        self.txt = txt
553
        self.connect('clicked', self.run)
554

    
555
    def run(self, widget):
556
        dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL,
557
                                   type=gtk.MESSAGE_INFO,
558
                                   buttons=gtk.BUTTONS_CLOSE)
559
        dialog.set_markup(self.txt)
560
        dialog.connect('response', lambda x, y: dialog.destroy())
561
        dialog.show()
562

    
563

    
564
class LJ_Window(gtk.Window):
565
    def __init__(self, owner, param, attrname):
566
        gtk.Window.__init__(self)
567
        self.set_title("Lennard-Jones parameters")
568
        self.owner = owner
569
        self.attrname = attrname
570
        atoms = owner.atoms
571
        atnos = atoms.get_atomic_numbers()
572
        found = {}
573
        for z in atnos:
574
            found[z] = True
575
        self.present = found.keys()
576
        self.present.sort()  # Sorted list of atomic numbers
577
        nelem = len(self.present)
578
        vbox = gtk.VBox()
579
        label = gtk.Label("Specify the Lennard-Jones parameters here")
580
        pack(vbox, [label])
581
        pack(vbox, gtk.Label(""))
582
        pack(vbox, [gtk.Label("Epsilon (eV):")])
583
        tbl, self.epsilon_adj = self.makematrix(self.present)
584
        pack(vbox, [tbl])
585
        pack(vbox, gtk.Label(""))
586
        pack(vbox, [gtk.Label("Sigma (Å):")])
587
        tbl, self.sigma_adj = self.makematrix(self.present)
588
        pack(vbox, [tbl])
589
        self.modif = gtk.CheckButton("Shift to make smooth at cutoff")
590
        self.modif.set_active(True)
591
        pack(vbox, gtk.Label(""))
592
        pack(vbox, self.modif)
593
        pack(vbox, gtk.Label(""))
594
        butbox = gtk.HButtonBox()
595
        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
596
        cancel_but.connect('clicked', lambda widget: self.destroy())
597
        ok_but = gtk.Button(stock=gtk.STOCK_OK)
598
        ok_but.connect('clicked', self.ok)
599
        butbox.pack_start(cancel_but, 0, 0)
600
        butbox.pack_start(ok_but, 0, 0)
601
        butbox.show_all()
602
        pack(vbox, [butbox], end=True, bottom=True)
603
        vbox.show()
604
        self.add(vbox)
605

    
606
        # Now, set the parameters
607
        if param and param['elements'] == self.present:
608
            self.set_param(self.epsilon_adj, param["epsilon"], nelem)
609
            self.set_param(self.sigma_adj, param["sigma"], nelem)
610
            self.modif.set_active(param["modified"])
611

    
612
        self.show()
613
        self.grab_add()  # Lock all other windows
614
        
615
    def makematrix(self, present):
616
        nelem = len(present)
617
        adjdict = {}
618
        tbl = gtk.Table(2+nelem, 2+nelem)
619
        for i in range(nelem):
620
            s = chemical_symbols[present[i]]
621
            tbl.attach(gtk.Label(" " + str(present[i])), 0, 1, i, i+1)
622
            tbl.attach(gtk.Label("  "+s+" "), 1, 2, i, i+1)
623
            tbl.attach(gtk.Label(str(present[i])), i+2, i+3, 1+nelem, 2+nelem)
624
            tbl.attach(gtk.Label(s), i+2, i+3, nelem, 1+nelem)
625
            for j in range(i+1):
626
                adj = gtk.Adjustment(1.0, 0.0, 100.0, 0.1)
627
                spin = gtk.SpinButton(adj, 0.1, 3)
628
                tbl.attach(spin, 2+j, 3+j, i, i+1)
629
                adjdict[(i,j)] = adj
630
        tbl.show_all()
631
        return tbl, adjdict
632
    
633
    def set_param(self, adj, params, n):
634
        for i in range(n):
635
            for j in range(n):
636
                if j <= i:
637
                    adj[(i,j)].value = params[i,j]
638

    
639
    def get_param(self, adj, params, n):
640
        for i in range(n):
641
            for j in range(n):
642
                if j <= i:
643
                    params[i,j] = params[j,i] = adj[(i,j)].value
644

    
645

    
646
    def destroy(self):
647
        self.grab_remove()
648
        gtk.Window.destroy(self)
649

    
650
    def ok(self, *args):
651
        params = {}
652
        params["elements"] = copy(self.present)
653
        n = len(self.present)
654
        eps = np.zeros((n,n))
655
        self.get_param(self.epsilon_adj, eps, n)
656
        sigma = np.zeros((n,n))
657
        self.get_param(self.sigma_adj, sigma, n)
658
        params["epsilon"] = eps
659
        params["sigma"] = sigma
660
        params["modified"] = self.modif.get_active()
661
        setattr(self.owner, self.attrname, params)
662
        self.destroy()
663

    
664

    
665
class GPAW_Window(gtk.Window):
666
    gpaw_xc_list = ['LDA', 'PBE', 'RPBE', 'revPBE']
667
    gpaw_xc_default = 'PBE'
668
    def __init__(self, owner, param, attrname):
669
        gtk.Window.__init__(self)
670
        self.set_title("GPAW parameters")
671
        self.owner = owner
672
        self.attrname = attrname
673
        atoms = owner.atoms
674
        self.ucell = atoms.get_cell()
675
        self.size = tuple([self.ucell[i,i] for i in range(3)])
676
        self.pbc = atoms.get_pbc()
677
        self.orthogonal = self.isorthogonal(self.ucell)
678
        self.natoms = len(atoms)
679
        
680
        vbox = gtk.VBox()
681
        #label = gtk.Label("Specify the GPAW parameters here")
682
        #pack(vbox, [label])
683

    
684
        # Print some info
685
        txt = "%i atoms.\n" % (self.natoms,)
686
        if self.orthogonal:
687
            txt += "Orthogonal unit cell: %.2f x %.2f x %.2f Å." % self.size
688
        else:
689
            txt += "Non-orthogonal unit cell:\n"
690
            txt += str(self.ucell)
691
        pack(vbox, [gtk.Label(txt)])
692
        
693
        # XC potential
694
        self.xc = gtk.combo_box_new_text()
695
        for i, x in enumerate(self.gpaw_xc_list):
696
            self.xc.append_text(x)
697
            if x == self.gpaw_xc_default:
698
                self.xc.set_active(i)
699
        pack(vbox, [gtk.Label("Exchange-correlation functional: "),
700
                    self.xc])
701
        
702
        # Grid spacing
703
        self.radio_h = gtk.RadioButton(None, "Grid spacing")
704
        self.h = gtk.Adjustment(0.18, 0.0, 1.0, 0.01)
705
        self.h_spin = gtk.SpinButton(self.h, 0, 2)
706
        pack(vbox, [self.radio_h, gtk.Label(" h = "), self.h_spin,
707
                    gtk.Label("Å")])
708
        self.radio_gpts = gtk.RadioButton(self.radio_h, "Grid points")
709
        self.gpts = []
710
        self.gpts_spin = []
711
        for i in range(3):
712
            g = gtk.Adjustment(4, 4, 1000, 4)
713
            s = gtk.SpinButton(g, 0, 0)
714
            self.gpts.append(g)
715
            self.gpts_spin.append(s)
716
        self.gpts_hlabel = gtk.Label("")
717
        self.gpts_hlabel_format = "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
718
        pack(vbox, [self.radio_gpts, gtk.Label(" gpts = ("), self.gpts_spin[0],
719
                    gtk.Label(", "), self.gpts_spin[1], gtk.Label(", "),
720
                    self.gpts_spin[2], gtk.Label(")  "), self.gpts_hlabel])
721
        self.radio_h.connect("toggled", self.radio_grid_toggled)
722
        self.radio_gpts.connect("toggled", self.radio_grid_toggled)
723
        self.radio_grid_toggled(None)
724
        for g in self.gpts:
725
            g.connect("value-changed", self.gpts_changed)
726
        self.h.connect("value-changed", self.h_changed)
727
        
728
        # K-points
729
        self.kpts = []
730
        self.kpts_spin = []
731
        for i in range(3):
732
            if self.pbc[i] and self.orthogonal:
733
                default = np.ceil(20.0 / self.size[i])
734
            else:
735
                default = 1
736
            g = gtk.Adjustment(default, 1, 100, 1)
737
            s = gtk.SpinButton(g, 0, 0)
738
            self.kpts.append(g)
739
            self.kpts_spin.append(s)
740
            if not self.pbc[i]:
741
                s.set_sensitive(False)
742
            g.connect("value-changed", self.k_changed)
743
        pack(vbox, [gtk.Label("k-points  k = ("), self.kpts_spin[0],
744
                    gtk.Label(", "), self.kpts_spin[1], gtk.Label(", "),
745
                    self.kpts_spin[2], gtk.Label(")")])
746
        self.kpts_label = gtk.Label("")
747
        self.kpts_label_format = "k-points x size:  (%.1f, %.1f, %.1f) Å"
748
        pack(vbox, [self.kpts_label])
749
        self.k_changed()
750
        
751
        # Spin polarized
752
        self.spinpol = gtk.CheckButton("Spin polarized")
753
        pack(vbox, [self.spinpol])
754
        pack(vbox, gtk.Label(""))
755

    
756
        # Mode and basis functions
757
        self.mode = gtk.combo_box_new_text()
758
        self.mode.append_text("FD - Finite Difference (grid) mode")
759
        self.mode.append_text("LCAO - Linear Combination of Atomic Orbitals")
760
        self.mode.set_active(0)
761
        pack(vbox, [gtk.Label("Mode: "), self.mode])
762
        self.basis = gtk.combo_box_new_text()
763
        self.basis.append_text("sz - Single Zeta")
764
        self.basis.append_text("szp - Single Zeta polarized")
765
        self.basis.append_text("dzp - Double Zeta polarized")
766
        self.basis.set_active(2) # dzp
767
        pack(vbox, [gtk.Label("Basis functions: "), self.basis])
768
        pack(vbox, gtk.Label(""))
769
        self.mode.connect("changed", self.mode_changed)
770
        self.mode_changed()
771
        
772
        # Mixer
773
        self.use_mixer = gtk.CheckButton("Non-standard mixer parameters")
774
        pack(vbox, [self.use_mixer])
775
        self.radio_mixer = gtk.RadioButton(None, "Mixer   ")
776
        self.radio_mixersum = gtk.RadioButton(self.radio_mixer, "MixerSum   ")
777
        self.radio_mixerdiff = gtk.RadioButton(self.radio_mixer, "MixerDiff")
778
        pack(vbox, [self.radio_mixer, self.radio_mixersum,
779
                    self.radio_mixerdiff])
780
        self.beta_adj = gtk.Adjustment(0.25, 0.0, 1.0, 0.05)
781
        self.beta_spin = gtk.SpinButton(self.beta_adj, 0, 2)
782
        self.nmaxold_adj = gtk.Adjustment(3, 1, 10, 1)
783
        self.nmaxold_spin = gtk.SpinButton(self.nmaxold_adj, 0, 0)
784
        self.weight_adj = gtk.Adjustment(50, 1, 500, 1)
785
        self.weight_spin = gtk.SpinButton(self.weight_adj, 0, 0)
786
        pack(vbox, [gtk.Label("beta = "), self.beta_spin,
787
                    gtk.Label("  nmaxold = "), self.nmaxold_spin,
788
                    gtk.Label("  weight = "), self.weight_spin])
789
        self.beta_m_adj = gtk.Adjustment(0.70, 0.0, 1.0, 0.05)
790
        self.beta_m_spin = gtk.SpinButton(self.beta_m_adj, 0, 2)
791
        self.nmaxold_m_adj = gtk.Adjustment(2, 1, 10, 1)
792
        self.nmaxold_m_spin = gtk.SpinButton(self.nmaxold_m_adj, 0, 0)
793
        self.weight_m_adj = gtk.Adjustment(10, 1, 500, 1)
794
        self.weight_m_spin = gtk.SpinButton(self.weight_m_adj, 0, 0)
795
        pack(vbox, [gtk.Label("beta_m = "), self.beta_m_spin,
796
                    gtk.Label("  nmaxold_m = "), self.nmaxold_m_spin,
797
                    gtk.Label("  weight_m = "), self.weight_m_spin])
798
        for but in (self.spinpol, self.use_mixer, self.radio_mixer,
799
                    self.radio_mixersum, self.radio_mixerdiff):
800
            but.connect("clicked", self.mixer_changed)
801
        self.mixer_changed()
802
        
803
        # Eigensolver
804
        # Poisson-solver
805
        
806
        vbox.show()
807
        self.add(vbox)
808

    
809
        # Buttons at the bottom
810
        pack(vbox, gtk.Label(""))
811
        butbox = gtk.HButtonBox()
812
        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
813
        cancel_but.connect('clicked', lambda widget: self.destroy())
814
        ok_but = gtk.Button(stock=gtk.STOCK_OK)
815
        ok_but.connect('clicked', self.ok)
816
        butbox.pack_start(cancel_but, 0, 0)
817
        butbox.pack_start(ok_but, 0, 0)
818
        butbox.show_all()
819
        pack(vbox, [butbox], end=True, bottom=True)
820

    
821
        # Set stored parameters
822
        if param:
823
            self.xc.set_active(param["xc#"])
824
            if param["use_h"]:
825
                self.radio_h.set_active(True)
826
            else:
827
                self.radio_gpts.set_active(True)
828
            for i in range(3):
829
                self.gpts[i].value = param["gpts"][i]
830
                self.kpts[i].value = param["kpts"][i]
831
            self.spinpol.set_active(param["spinpol"])
832
            self.mode.set_active(param["mode#"])
833
            self.basis.set_active(param["basis#"])
834
            self.use_mixer.set_active(param["use mixer"])
835
            getattr(self, "radio_"+param["mixer"].lower()).set_active(True)
836
            for t in ("beta", "nmaxold", "weight", "beta_m", "nmaxold_m",
837
                      "weight_m"):                    
838
                getattr(self, t+"_adj").value = param[t]
839

    
840
        self.show()
841
        self.grab_add()  # Lock all other windows
842

    
843
    def radio_grid_toggled(self, widget):
844
        hmode = self.radio_h.get_active()
845
        self.h_spin.set_sensitive(hmode)
846
        for s in self.gpts_spin:
847
            s.set_sensitive(not hmode)
848
        self.gpts_changed()
849

    
850
    def gpts_changed(self, *args):
851
        if self.radio_gpts.get_active():
852
            g = np.array([int(g.value) for g in self.gpts])
853
            size = np.array([self.ucell[i,i] for i in range(3)])
854
            txt = self.gpts_hlabel_format % tuple(size / g)
855
            self.gpts_hlabel.set_markup(txt)
856
        else:
857
            self.gpts_hlabel.set_markup("")
858

    
859
    def h_changed(self, *args):
860
        h = self.h.value
861
        for i in range(3):
862
            g = 4 * round(self.ucell[i,i] / (4*h))
863
            self.gpts[i].value = g
864

    
865
    def k_changed(self, *args):
866
        if self.orthogonal:
867
            size = [self.kpts[i].value * self.size[i] for i in range(3)]
868
        self.kpts_label.set_text(self.kpts_label_format % tuple(size))
869

    
870
    def mode_changed(self, *args):
871
        self.basis.set_sensitive(self.mode.get_active() == 1)
872

    
873
    def mixer_changed(self, *args):
874
        radios = (self.radio_mixer, self.radio_mixersum, self.radio_mixerdiff)
875
        spin1 = (self.beta_spin, self.nmaxold_spin, self.weight_spin)
876
        spin2 = (self.beta_m_spin, self.nmaxold_m_spin, self.weight_m_spin)
877
        if self.use_mixer.get_active():
878
            # Mixer parameters can be specified.
879
            if self.spinpol.get_active():
880
                self.radio_mixer.set_sensitive(False)
881
                self.radio_mixersum.set_sensitive(True)
882
                self.radio_mixerdiff.set_sensitive(True)
883
                if self.radio_mixer.get_active():
884
                    self.radio_mixersum.set_active(True)
885
            else:
886
                self.radio_mixer.set_sensitive(True)
887
                self.radio_mixersum.set_sensitive(False)
888
                self.radio_mixerdiff.set_sensitive(False)
889
                self.radio_mixer.set_active(True)
890
            if self.radio_mixerdiff.get_active():
891
                active = spin1 + spin2
892
                passive = ()
893
            else:
894
                active = spin1
895
                passive = spin2
896
            for widget in active:
897
                widget.set_sensitive(True)
898
            for widget in passive:
899
                widget.set_sensitive(False)
900
        else:
901
            # No mixer parameters
902
            for widget in radios + spin1 + spin2:
903
                widget.set_sensitive(False)
904
                
905
    def isorthogonal(self, matrix):
906
        ortho = True
907
        for i in range(3):
908
            for j in range(3):
909
                if i != j and matrix[i][j] != 0.0:
910
                    ortho = False
911
        return ortho
912

    
913
    def ok(self, *args):
914
        param = {}
915
        param["xc"] = self.xc.get_active_text()
916
        param["xc#"] = self.xc.get_active()
917
        param["use_h"] = self.radio_h.get_active()
918
        param["h"] = self.h.value
919
        param["gpts"] = [int(g.value) for g in self.gpts]
920
        param["kpts"] = [int(k.value) for k in self.kpts]
921
        param["spinpol"] = self.spinpol.get_active()
922
        param["mode"] = self.mode.get_active_text().split()[0].lower()
923
        param["mode#"] = self.mode.get_active()
924
        param["basis"] = self.basis.get_active_text().split()[0].lower()
925
        param["basis#"] = self.basis.get_active()
926
        param["use mixer"] = self.use_mixer.get_active()
927
        if self.radio_mixer.get_active():
928
            m = "Mixer"
929
        elif self.radio_mixersum.get_active():
930
            m = "MixerSum"
931
        else:
932
            assert self.radio_mixerdiff.get_active()
933
            m = "MixerDiff"
934
        param["mixer"] = m
935
        for t in ("beta", "nmaxold", "weight", "beta_m", "nmaxold_m",
936
                  "weight_m"):
937
            param[t] = getattr(self, t+"_adj").value
938
        setattr(self.owner, self.attrname, param)
939
        self.destroy()
940

    
941
class AIMS_Window(gtk.Window):
942
    aims_xc_cluster = ['pw-lda','pz-lda','pbe','pbesol','rpbe','revpbe',
943
                    'blyp','am05','b3lyp','hse03','hse06','pbe0','pbesol0',
944
                    'hf','mp2']
945
    aims_xc_periodic = ['pw-lda','pz-lda','pbe','pbesol','rpbe','revpbe',
946
                        'blyp','am05']
947
    aims_xc_default = 'pbe'
948
    aims_relativity_list = ['none','atomic_zora','zora']
949
    aims_keyword_gui_list = ['xc','vdw_correction_hirshfeld','k_grid','spin','charge','relativistic',
950
                             'sc_accuracy_etot','sc_accuracy_eev','sc_accuracy_rho','sc_accuracy_forces',
951
                             'compute_forces','run_command','species_dir']
952
    def __init__(self, owner, param, attrname):
953
        self.owner = owner
954
        self.attrname = attrname
955
        atoms = owner.atoms
956
        self.periodic = atoms.get_pbc().all()
957
        if not self.periodic and atoms.get_pbc().any():
958
            aims_periodic_warning = True
959
            self.periodic = True 
960
        else:
961
            aims_periodic_warning = False
962
        from ase.calculators.aims import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,input_keys
963
        self.aims_keyword_list =float_keys+exp_keys+string_keys+int_keys+bool_keys+list_keys+input_keys
964
        self.expert_keywords = []
965

    
966
        natoms = len(atoms)
967
        gtk.Window.__init__(self)
968
        self.set_title("FHI-aims parameters")
969
        vbox = gtk.VBox()
970
        # Print some info
971
        txt = "%i atoms.\n" % (natoms)
972
        if self.periodic:
973
            self.ucell = atoms.get_cell()
974
            txt += "Periodic geometry, unit cell is: \n"
975
            for i in range(3):
976
                txt += "(%8.3f %8.3f %8.3f)\n" % (self.ucell[i][0], self.ucell[i][1], self.ucell[i][2])
977
            self.xc_list = self.aims_xc_periodic
978
        else:
979
            txt += "Non-periodic geometry. \n"
980
            self.xc_list = self.aims_xc_cluster
981
        pack(vbox, [gtk.Label(txt)])
982

    
983
        # XC functional & dispersion correction
984
        self.xc = gtk.combo_box_new_text()
985
        self.xc_setup = False
986
        self.TS = gtk.CheckButton("Hirshfeld-based dispersion correction")
987
        pack(vbox, [gtk.Label("Exchange-correlation functional: "),self.xc])
988
        pack(vbox, [self.TS])
989
        pack(vbox, [gtk.Label("")])
990
        
991
        # k-grid?
992
        if self.periodic:
993
            self.kpts = []
994
            self.kpts_spin = []
995
            for i in range(3):
996
                default = np.ceil(20.0 / np.sqrt(np.vdot(self.ucell[i],self.ucell[i])))
997
                g = gtk.Adjustment(default, 1, 100, 1)
998
                s = gtk.SpinButton(g, 0, 0)
999
                self.kpts.append(g)
1000
                self.kpts_spin.append(s)
1001
                g.connect("value-changed", self.k_changed)
1002
            pack(vbox, [gtk.Label("k-points  k = ("), self.kpts_spin[0],
1003
                        gtk.Label(", "), self.kpts_spin[1], gtk.Label(", "),
1004
                        self.kpts_spin[2], gtk.Label(")")])
1005
            self.kpts_label = gtk.Label("")
1006
            self.kpts_label_format = "k-points x size:  (%.1f, %.1f, %.1f) Å"
1007
            pack(vbox, [self.kpts_label])
1008
            self.k_changed()
1009
            pack(vbox, gtk.Label(""))
1010

    
1011
        # Spin polarized, charge, relativity
1012
        self.spinpol = gtk.CheckButton("Spin polarized")
1013
        self.charge  = gtk.Adjustment(0,-100,100,0.1)
1014
        self.charge_spin = gtk.SpinButton(self.charge, 0, 0)
1015
        self.charge_spin.set_digits(2)
1016
        self.relativity_type = gtk.combo_box_new_text()
1017
        for i, x in enumerate(self.aims_relativity_list):
1018
            self.relativity_type.append_text(x)
1019
        self.relativity_type.connect('changed',self.relativity_changed)
1020
        self.relativity_threshold = gtk.Entry(max=8)
1021
        self.relativity_threshold.set_text('1.00e-12')
1022
        self.relativity_threshold.set_sensitive(False)
1023
        pack(vbox, [self.spinpol, 
1024
                    gtk.Label("   Charge"), 
1025
                    self.charge_spin, 
1026
                    gtk.Label("   Relativity"),
1027
                    self.relativity_type,
1028
                    gtk.Label(" Threshold"),
1029
                    self.relativity_threshold])
1030
        pack(vbox, gtk.Label(""))
1031

    
1032
        # self-consistency criteria
1033
        pack(vbox,[gtk.Label("Self-consistency convergence:")])
1034
        self.sc_tot_energy      = gtk.Adjustment(1e-6, 1e-6, 1e0, 1e-6)
1035
        self.sc_tot_energy_spin = gtk.SpinButton(self.sc_tot_energy, 0, 0)
1036
        self.sc_tot_energy_spin.set_digits(6)
1037
        self.sc_tot_energy_spin.set_numeric(True)
1038
        self.sc_sum_eigenvalue      = gtk.Adjustment(1e-3, 1e-6, 1e0, 1e-6)
1039
        self.sc_sum_eigenvalue_spin = gtk.SpinButton(self.sc_sum_eigenvalue, 0, 0)
1040
        self.sc_sum_eigenvalue_spin.set_digits(6)
1041
        self.sc_sum_eigenvalue_spin.set_numeric(True)
1042
        self.sc_density      = gtk.Adjustment(1e-4, 1e-6, 1e0, 1e-6)
1043
        self.sc_density_spin = gtk.SpinButton(self.sc_density, 0, 0)
1044
        self.sc_density_spin.set_digits(6)
1045
        self.sc_density_spin.set_numeric(True)
1046
        self.compute_forces = gtk.CheckButton("Compute forces")
1047
        self.compute_forces.set_active(True)
1048
        self.compute_forces.connect("toggled", self.compute_forces_toggled,"")
1049
        self.sc_forces      = gtk.Adjustment(1e-4, 1e-6, 1e0, 1e-6)
1050
        self.sc_forces_spin = gtk.SpinButton(self.sc_forces, 0, 0)
1051
        self.sc_forces_spin.set_numeric(True)
1052
        self.sc_forces_spin.set_digits(6)
1053
        pack(vbox, [gtk.Label("Energy:                 "),
1054
                    self.sc_tot_energy_spin, 
1055
                    gtk.Label(" eV   Sum of eigenvalues:  "),
1056
                    self.sc_sum_eigenvalue_spin,
1057
                    gtk.Label(" eV")])
1058
        pack(vbox, [gtk.Label("Electron density: "),
1059
                    self.sc_density_spin,
1060
                    gtk.Label("        Force convergence:  "),
1061
                    self.sc_forces_spin,
1062
                    gtk.Label(" eV/Ang  ")])
1063

    
1064
        pack(vbox, [self.compute_forces])
1065
        pack(vbox, gtk.Label(""))
1066

    
1067
        self.expert_keyword_set = gtk.Entry(max = 55)
1068
        self.expert_keyword_add = gtk.Button(stock = gtk.STOCK_ADD)
1069
        self.expert_keyword_add.connect("clicked", self.expert_keyword_import)
1070
        self.expert_keyword_set.connect("activate", self.expert_keyword_import)
1071
        pack(vbox,[gtk.Label("Additional keywords: "),
1072
                   self.expert_keyword_set, 
1073
                   self.expert_keyword_add])
1074
        self.expert_vbox = gtk.VBox()
1075
        pack(vbox, self.expert_vbox)
1076
        pack(vbox, gtk.Label(""))
1077

    
1078
        # run command and species defaults:
1079
        pack(vbox, gtk.Label('FHI-aims execution command: '))
1080
        self.run_command = pack(vbox, gtk.Entry(max=0))
1081
        pack(vbox, gtk.Label('Directory for species defaults: '))
1082
        self.species_defaults = pack(vbox, gtk.Entry(max=0))
1083

    
1084
        # set defaults from previous instance of the calculator, if applicable:
1085
        if param is not None:
1086
            self.set_param(param)
1087
        else:
1088
            self.set_defaults()
1089

    
1090
        # Buttons at the bottom
1091
        pack(vbox, gtk.Label(""))
1092
        butbox = gtk.HButtonBox()
1093
        default_but = gtk.Button("Set Defaults")
1094
        default_but.connect("clicked",self.set_defaults)
1095
        import_control_but = gtk.Button("Import control.in")
1096
        import_control_but.connect("clicked",self.import_control)
1097
        export_control_but = gtk.Button("Export control.in")
1098
        export_control_but.connect("clicked", self.export_control)
1099
        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
1100
        cancel_but.connect('clicked', lambda widget: self.destroy())
1101
        ok_but = gtk.Button(stock=gtk.STOCK_OK)
1102
        ok_but.connect('clicked', self.ok)
1103
        butbox.pack_start(default_but, 0, 0)
1104
        butbox.pack_start(import_control_but, 0, 0)
1105
        butbox.pack_start(export_control_but, 0, 0)
1106
        butbox.pack_start(cancel_but, 0, 0)
1107
        butbox.pack_start(ok_but, 0, 0)
1108
        butbox.show_all()
1109
        pack(vbox, [butbox], end=True, bottom=True)
1110
        self.expert_vbox.show()
1111
        vbox.show()
1112
        self.add(vbox)
1113
        self.show()
1114
        self.grab_add() 
1115
        if aims_periodic_warning:
1116
            oops(aims_pbc_warning_text)
1117

    
1118
    def set_defaults(self, *args):
1119
        atoms = self.owner.atoms.copy()
1120
        if not self.xc_setup:
1121
            self.xc_setup = True 
1122
            for i, x in enumerate(self.xc_list):
1123
                self.xc.append_text(x)
1124
        for i, x in enumerate(self.xc_list):
1125
            if x == self.aims_xc_default:
1126
                self.xc.set_active(i)
1127
        self.TS.set_active(False)
1128
        if self.periodic:
1129
            self.ucell = atoms.get_cell()
1130
            for i in range(3):
1131
                default = np.ceil(20.0 / np.sqrt(np.vdot(self.ucell[i],self.ucell[i])))
1132
                self.kpts_spin[i].set_value(default)
1133
        self.spinpol.set_active(False)
1134
        self.charge.set_value(0)
1135
        aims_relativity_default = 'none'
1136
        for a in atoms:
1137
            if a.get_atomic_number() > 20: 
1138
                aims_relativity_default = 'atomic_zora'
1139
        for i, x in enumerate(self.aims_relativity_list):
1140
            if x == aims_relativity_default:
1141
                self.relativity_type.set_active(i)
1142
        self.sc_tot_energy.set_value(1e-6)
1143
        self.sc_sum_eigenvalue.set_value(1e-3)
1144
        self.sc_density.set_value(1e-4)
1145
        self.sc_forces.set_value(1e-4)
1146
        for key in self.expert_keywords:
1147
            key[0].destroy()
1148
            key[1].destroy()
1149
            key[2].destroy()
1150
            key[3] = False
1151
        if os.environ.has_key('AIMS_COMMAND'):
1152
            text = os.environ['AIMS_COMMAND']
1153
        else:
1154
            text = ""
1155
        self.run_command.set_text(text)
1156
        if os.environ.has_key('AIMS_SPECIES_DIR'):
1157
            text = os.environ['AIMS_SPECIES_DIR']
1158
        else:
1159
            text = ""
1160
        self.species_defaults.set_text(text)
1161

    
1162
    def set_attributes(self, *args):
1163
        param = {}
1164
        param["xc"] = self.xc.get_active_text()
1165
        if self.periodic:
1166
            param["k_grid"] = (int(self.kpts[0].value),
1167
                               int(self.kpts[1].value),
1168
                               int(self.kpts[2].value))
1169
        if self.spinpol.get_active():
1170
            param["spin"] = "collinear"
1171
        else:
1172
            param["spin"] = "none"
1173
        param["vdw_correction_hirshfeld"] = self.TS.get_active()
1174
        param["charge"]             = self.charge.value
1175
        param["relativistic"]       = self.relativity_type.get_active_text()
1176
        if param["relativistic"] == 'atomic_zora':
1177
            param["relativistic"] += " scalar "
1178
        if param["relativistic"] == 'zora':
1179
            param["relativistic"] += " scalar "+self.relativity_threshold.get_text() 
1180
        param["sc_accuracy_etot"]   = self.sc_tot_energy.value
1181
        param["sc_accuracy_eev"]    = self.sc_sum_eigenvalue.value
1182
        param["sc_accuracy_rho"]    = self.sc_density.value
1183
        param["compute_forces"]     = self.compute_forces.get_active()
1184
        param["sc_accuracy_forces"] = self.sc_forces.value
1185
        param["run_command"]        = self.run_command.get_text()
1186
        param["species_dir"]        = self.species_defaults.get_text()
1187
        from ase.calculators.aims import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,input_keys
1188
        for option in self.expert_keywords:
1189
            if option[3]:   # set type of parameter accoding to which list it is in
1190
                key = option[0].get_text().strip()
1191
                val = option[1].get_text().strip()
1192
                if key == 'output':
1193
                    if param.has_key('output'): 
1194
                        param[key] += [val]
1195
                    else:
1196
                        param[key] = [val]
1197
                elif key in float_keys or key in exp_keys:
1198
                    param[key] = float(val)
1199
                elif key in list_keys or key in string_keys or key in input_keys:
1200
                    param[key] = val
1201
                elif key in int_keys:
1202
                    param[key] = int(val)
1203
                elif key in bool_keys:
1204
                    param[key] = bool(val)
1205
        setattr(self.owner, self.attrname, param)
1206

    
1207
    def set_param(self, param):
1208
        if param["xc"] is not None:
1209
            for i, x in enumerate(self.xc_list):
1210
                if x == param["xc"]:
1211
                    self.xc.set_active(i)
1212
        if isinstance(param["vdw_correction_hirshfeld"],bool):
1213
            self.TS.set_active(param["vdw_correction_hirshfeld"])
1214
        if self.periodic and param["k_grid"] is not None:
1215
            self.kpts[0].value = int(param["k_grid"][0])
1216
            self.kpts[1].value = int(param["k_grid"][1])
1217
            self.kpts[2].value = int(param["k_grid"][2])
1218
        if param["spin"] is not None:
1219
            self.spinpol.set_active(param["spin"] == "collinear")
1220
        if param["charge"] is not None:
1221
            self.charge.value = param["charge"]
1222
        if param["relativistic"] is not None:
1223
            if isinstance(param["relativistic"],(tuple,list)):
1224
                rel = param["relativistic"]
1225
            else:
1226
                rel = param["relativistic"].split()
1227
            for i, x in enumerate(self.aims_relativity_list):
1228
                if x == rel[0]:
1229
                    self.relativity_type.set_active(i)
1230
                    if x == 'zora':
1231
                        self.relativity_threshold.set_text(rel[2])
1232
                        self.relativity_threshold.set_sensitive(True)
1233
        if param["sc_accuracy_etot"] is not None:
1234
            self.sc_tot_energy.value     = param["sc_accuracy_etot"]
1235
        if param["sc_accuracy_eev"] is not None:
1236
            self.sc_sum_eigenvalue.value = param["sc_accuracy_eev"]
1237
        if param["sc_accuracy_rho"] is not None:
1238
            self.sc_density.value        = param["sc_accuracy_rho"]
1239
        if param["compute_forces"] is not None:
1240
            if param["compute_forces"]:
1241
                if param["sc_accuracy_forces"] is not None:
1242
                    self.sc_forces.value = param["sc_accuracy_forces"]
1243
                self.compute_forces.set_active(param["compute_forces"])
1244
            else: 
1245
                self.compute_forces.set_active(False)
1246
        if param["run_command"] is not None:
1247
            self.run_command.set_text(param["run_command"])
1248
        if param["species_dir"] is not None:
1249
            self.species_defaults.set_text(param["species_dir"])
1250
        for (key,val) in param.items():
1251
            if key in self.aims_keyword_list and key not in self.aims_keyword_gui_list:
1252
                if val is not None:  # = existing "expert keyword"
1253
                    if key == 'output': # 'output' can be used more than once
1254
                        options = val
1255
                        if isinstance(options,str): options = [options]
1256
                        for arg in options:
1257
                            self.expert_keyword_create([key]+[arg])
1258
                    else:
1259
                        if isinstance(val,str):
1260
                            arg = [key]+val.split()
1261
                        elif isinstance(val,(tuple,list)):
1262
                            arg = [key]+[str(a) for a in val]
1263
                        else:
1264
                            arg = [key]+[str(val)]
1265
                        self.expert_keyword_create(arg)
1266

    
1267
    def ok(self, *args):
1268
        self.set_attributes(*args)
1269
        self.destroy()
1270

    
1271
    def export_control(self, *args):
1272
        filename = "control.in"
1273
        chooser = gtk.FileChooserDialog(
1274
            'Export parameters ... ', None, gtk.FILE_CHOOSER_ACTION_SAVE,
1275
            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
1276
             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
1277
        chooser.set_filename(filename)
1278
        save = chooser.run()
1279
        if save == gtk.RESPONSE_OK or save == gtk.RESPONSE_SAVE:
1280
            filename = chooser.get_filename()
1281
            self.set_attributes(*args)
1282
            param = getattr(self.owner, "aims_parameters")
1283
            from ase.calculators.aims import Aims
1284
            calc_temp = Aims(**param)
1285
            atoms_temp = self.owner.atoms.copy()
1286
            atoms_temp.set_calculator(calc_temp)
1287
            atoms_temp.calc.write_control(file = filename)
1288
            atoms_temp.calc.write_species(file = filename)
1289
        chooser.destroy()
1290

    
1291
    def import_control(self, *args):
1292
        filename = "control.in"
1293
        chooser = gtk.FileChooserDialog(
1294
            'Import control.in file ... ', None, gtk.FILE_CHOOSER_ACTION_SAVE,
1295
            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
1296
             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
1297
        chooser.set_filename(filename)
1298
        save = chooser.run()
1299
        if save == gtk.RESPONSE_OK:
1300
            self.set_defaults()
1301
            filename = chooser.get_filename()
1302
            control = open(filename,'r')
1303
            while True:
1304
                line = control.readline()
1305
                if not line:
1306
                    break
1307
                if "List of parameters used to initialize the calculator:" in line:
1308
                    control.readline()
1309
                    from ase.io.aims import read_aims_calculator
1310
                    calc = read_aims_calculator(control)
1311
                    found_aims_calculator = True
1312
            control.close()
1313
            if found_aims_calculator:
1314
                param = calc.float_params
1315
                for key in calc.exp_params:
1316
                    param[key] = calc.exp_params[key]
1317
                for key in calc.string_params:
1318
                    param[key] = calc.string_params[key]
1319
                for key in calc.int_params:
1320
                    param[key] = calc.int_params[key]
1321
                for key in calc.bool_params:
1322
                    param[key] = calc.bool_params[key]
1323
                for key in calc.list_params:
1324
                    param[key] = calc.list_params[key]
1325
                for key in calc.input_parameters:
1326
                    param[key] = calc.input_parameters[key]
1327
                self.set_defaults()
1328
                self.set_param(param)
1329
        chooser.destroy()
1330

    
1331
    def k_changed(self, *args):
1332
        size = [self.kpts[i].value * np.sqrt(np.vdot(self.ucell[i],self.ucell[i])) for i in range(3)]
1333
        self.kpts_label.set_text(self.kpts_label_format % tuple(size))
1334

    
1335
    def compute_forces_toggled(self, *args):
1336
        self.sc_forces_spin.set_sensitive(self.compute_forces.get_active())
1337

    
1338
    def relativity_changed(self, *args):
1339
        self.relativity_threshold.set_sensitive(self.relativity_type.get_active() == 2)
1340

    
1341
    def expert_keyword_import(self, *args):
1342
        command = self.expert_keyword_set.get_text().split()
1343
        if len(command) > 0 and command[0] in self.aims_keyword_list and not command[0] in self.aims_keyword_gui_list:
1344
            self.expert_keyword_create(command)
1345
        elif command[0] in self.aims_keyword_gui_list:
1346
            oops("Please use the facilities provided in this window to manipulate "+
1347
                 "the keyword:" + command[0] + "!")
1348
        else:
1349
            oops("Don't know this keyword:" + command[0] 
1350
                 + "\nPlease check!\n\n" 
1351
                 + "If you really think it should be available, "
1352
                 + "please add it to the top of ase/calculators/aims.py.")
1353
        self.expert_keyword_set.set_text("")
1354

    
1355
    def expert_keyword_create(self, command):
1356
        key = command[0]
1357
        argument = command[1]
1358
        if len(command) > 2:
1359
            for a in command[2:]:
1360
                argument += ' '+a
1361
        index = len(self.expert_keywords) 
1362
        self.expert_keywords += [[gtk.Label("    " +key+"  "),
1363
                                  gtk.Entry(max=45),
1364
                                  ExpertDeleteButton(index),
1365
                                  True]]
1366
        self.expert_keywords[index][1].set_text(argument)
1367
        self.expert_keywords[index][2].connect('clicked',self.expert_keyword_delete)
1368
        pack(self.expert_vbox, [self.expert_keywords[index][0],
1369
                                self.expert_keywords[index][1],
1370
                                self.expert_keywords[index][2]])
1371

    
1372
    def expert_keyword_delete(self, button, *args):
1373
        index = button.index   # which one to kill 
1374
        for i in [0,1,2]:
1375
            self.expert_keywords[index][i].destroy()
1376
        self.expert_keywords[index][3] = False
1377

    
1378

    
1379
class ExpertDeleteButton(gtk.Button):
1380
    def __init__(self, index):
1381
        gtk.Button.__init__(self, stock=gtk.STOCK_DELETE)
1382
        alignment = self.get_children()[0]
1383
        hbox = alignment.get_children()[0]
1384
        image, label = hbox.get_children()
1385
        if image is not None: 
1386
            label.set_text('')
1387
        self.index = index
1388

    
1389

    
1390
class VASP_Window(gtk.Window):
1391
    vasp_xc_list = ['PW91', 'PBE', 'LDA']
1392
    vasp_xc_default = 'PBE'
1393
    vasp_prec_default = 'Normal'
1394
    def __init__(self, owner, param, attrname):
1395
        self.owner = owner
1396
        self.attrname = attrname
1397
        atoms = owner.atoms
1398
        self.periodic = atoms.get_pbc().all()
1399
        self.vasp_keyword_gui_list = ['ediff','encut', 'ismear', 'ispin', 'prec', 'sigma']
1400
        from ase.calculators.vasp import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,special_keys
1401
        self.vasp_keyword_list = float_keys+exp_keys+string_keys+int_keys+bool_keys+list_keys+special_keys
1402
        self.expert_keywords = []
1403
        natoms = len(atoms)
1404
        gtk.Window.__init__(self)
1405
        self.set_title("VASP parameters")
1406
        vbox = gtk.VBox()
1407
        # Print some info
1408
        txt = "%i atoms.\n" % (natoms)
1409
        self.ucell = atoms.get_cell()
1410
        txt += "Periodic geometry, unit cell is: \n"
1411
        for i in range(3):
1412
            txt += "(%8.3f %8.3f %8.3f)\n" % (self.ucell[i][0], self.ucell[i][1], self.ucell[i][2])
1413
        pack(vbox, [gtk.Label(txt)])
1414

    
1415
        # XC functional ()
1416
        self.xc = gtk.combo_box_new_text()
1417
        for i, x in enumerate(self.vasp_xc_list):
1418
            self.xc.append_text(x)
1419
            if x == self.vasp_xc_default:
1420
                self.xc.set_active(i)
1421

    
1422
        # Spin polarized
1423
        self.spinpol = gtk.CheckButton("Spin polarized")
1424

    
1425
        pack(vbox, [gtk.Label("Exchange-correlation functional: "),
1426
                    self.xc,
1427
                    gtk.Label("    "),
1428
                    self.spinpol])
1429
        pack(vbox, gtk.Label(""))
1430

    
1431
        # k-grid
1432
        self.kpts = []
1433
        self.kpts_spin = []
1434
        for i in range(3):
1435
            default = np.ceil(20.0 / np.sqrt(np.vdot(self.ucell[i],self.ucell[i])))
1436
            g = gtk.Adjustment(default, 1, 100, 1)
1437
            s = gtk.SpinButton(g, 0, 0)
1438
            self.kpts.append(g)
1439
            self.kpts_spin.append(s)
1440
            g.connect("value-changed", self.k_changed)
1441

    
1442
        # Precision of calculation
1443
        self.prec = gtk.combo_box_new_text()
1444
        for i, x in enumerate(['Low', 'Normal', 'Accurate']):
1445
            self.prec.append_text(x)
1446
            if x == self.vasp_prec_default:
1447
                self.prec.set_active(i)
1448

    
1449
        # cutoff energy
1450
        if os.environ.has_key('VASP_PP_PATH'):
1451
            self.encut_min_default, self.encut_max_default = self.get_min_max_cutoff()
1452
        else:
1453
            self.encut_max_default = 400.0
1454
            self.encut_min_default = 100.0
1455
        self.encut = gtk.Adjustment(self.encut_max_default, 0, 9999, 10)
1456
        self.encut_spin = gtk.SpinButton(self.encut, 0, 0)
1457
        self.encut_spin.set_digits(2)
1458
        self.encut_spin.connect("value-changed",self.check_encut_warning)
1459
        self.encut_warning = gtk.Label("")
1460

    
1461
        pack(vbox, [gtk.Label("k-points  k = ("), self.kpts_spin[0],
1462
                    gtk.Label(", "), self.kpts_spin[1], gtk.Label(", "),
1463
                    self.kpts_spin[2], 
1464
                    gtk.Label(")    Cutoff: "),self.encut_spin,
1465
                    gtk.Label("    Precision: "),self.prec])
1466
        self.kpts_label = gtk.Label("")
1467
        self.kpts_label_format = "k-points x size:  (%.1f, %.1f, %.1f) Å       "
1468
        pack(vbox, [self.kpts_label, self.encut_warning])
1469
        self.k_changed()
1470
        pack(vbox, gtk.Label(""))
1471

    
1472
        self.ismear = gtk.combo_box_new_text()
1473
        for x in ['Fermi', 'Gauss', 'Methfessel-Paxton']:
1474
            self.ismear.append_text(x)
1475
        self.ismear.set_active(2)
1476
        self.smearing_order = gtk.Adjustment(2,0,9,1)
1477
        self.smearing_order_spin = gtk.SpinButton(self.smearing_order,0,0)
1478
        self.smearing_order_spin.set_digits(0)
1479
        self.ismear.connect("changed", self.check_ismear_changed)
1480
        self.sigma = gtk.Adjustment(0.1, 0.001, 9.0, 0.1)
1481
        self.sigma_spin = gtk.SpinButton(self.sigma,0,0)
1482
        self.sigma_spin.set_digits(3)
1483
        pack(vbox, [gtk.Label("Smearing: "),
1484
                    self.ismear,
1485
                    gtk.Label(" order: "),
1486
                    self.smearing_order_spin,
1487
                    gtk.Label(" width: "),
1488
                    self.sigma_spin])
1489
        pack(vbox, gtk.Label(""))
1490
        
1491
        self.ediff = gtk.Adjustment(1e-4, 1e-6, 1e0, 1e-4)
1492
        self.ediff_spin = gtk.SpinButton(self.ediff, 0, 0)
1493
        self.ediff_spin.set_digits(6)
1494
        pack(vbox,[gtk.Label("Self-consistency convergence: "),
1495
                   self.ediff_spin,
1496
                   gtk.Label(" eV")])
1497
        pack(vbox,gtk.Label(""))
1498

    
1499
        self.expert_keyword_set = gtk.Entry(max = 55)
1500
        self.expert_keyword_add = gtk.Button(stock = gtk.STOCK_ADD)
1501
        self.expert_keyword_add.connect("clicked", self.expert_keyword_import)
1502
        self.expert_keyword_set.connect("activate", self.expert_keyword_import)
1503
        pack(vbox,[gtk.Label("Additional keywords: "),
1504
                   self.expert_keyword_set, 
1505
                   self.expert_keyword_add])
1506
        self.expert_vbox = gtk.VBox()
1507
        pack(vbox, self.expert_vbox)
1508
        pack(vbox, gtk.Label(""))
1509

    
1510
        # run command and location of POTCAR files:
1511
        pack(vbox, gtk.Label('VASP execution command: '))
1512
        self.run_command = pack(vbox, gtk.Entry(max=0))
1513
        if os.environ.has_key('VASP_COMMAND'):
1514
            self.run_command.set_text(os.environ['VASP_COMMAND'])
1515
        pack(vbox, gtk.Label('Directory for species defaults: '))
1516
        self.pp_path = pack(vbox, gtk.Entry(max=0))
1517
        if os.environ.has_key('VASP_PP_PATH'):
1518
            self.pp_path.set_text(os.environ['VASP_PP_PATH'])
1519

    
1520
        # Buttons at the bottom
1521
        pack(vbox, gtk.Label(""))
1522
        butbox = gtk.HButtonBox()
1523
        export_vasp_but = gtk.Button("Export VASP files")
1524
        export_vasp_but.connect("clicked", self.export_vasp_files)
1525
        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
1526
        cancel_but.connect('clicked', lambda widget: self.destroy())
1527
        ok_but = gtk.Button(stock=gtk.STOCK_OK)
1528
        ok_but.connect('clicked', self.ok)
1529
        butbox.pack_start(export_vasp_but, 0, 0)
1530
        butbox.pack_start(cancel_but, 0, 0)
1531
        butbox.pack_start(ok_but, 0, 0)
1532
        butbox.show_all()
1533
        pack(vbox, [butbox], end=True, bottom=True)
1534
        vbox.show()
1535
        self.add(vbox)
1536
        self.show()
1537
        self.grab_add()  # Lock all other windows
1538

    
1539
    def set_attributes(self, *args):
1540
        self.param = {}
1541
        self.param["xc"] = self.xc.get_active_text()
1542
        self.param["prec"] = self.prec.get_active_text()
1543
        self.param["kpts"] = (int(self.kpts[0].value),
1544
                              int(self.kpts[1].value),
1545
                              int(self.kpts[2].value))
1546
        self.param["encut"] = self.encut.value
1547
        self.param["ediff"] = self.ediff.value
1548
        self.param["ismear"] = self.get_ismear()
1549
        self.param["sigma"] = self.sigma.value
1550
        if self.spinpol.get_active(): 
1551
            self.param["ispin"] = 2
1552
        else:
1553
            self.param["ispin"] = 1
1554
        from ase.calculators.vasp import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,special_keys
1555
        for option in self.expert_keywords:
1556
            if option[3]:   # set type of parameter accoding to which list it is in
1557
                key = option[0].get_text().strip()
1558
                val = option[1].get_text().strip()
1559
                if key in float_keys or key in exp_keys:
1560
                    self.param[key] = float(val)
1561
                elif key in list_keys or key in string_keys:
1562
                    self.param[key] = val
1563
                elif key in int_keys:
1564
                    self.param[key] = int(val)
1565
                elif key in bool_keys:
1566
                    self.param[key] = bool(val)
1567
        setattr(self.owner, self.attrname, self.param)
1568
        os.environ['VASP_COMMAND'] = self.run_command.get_text()
1569
        os.environ['VASP_PP_PATH'] = self.pp_path.get_text()
1570
        
1571
    def ok(self, *args):
1572
        self.set_attributes(*args)
1573
        self.destroy()
1574

    
1575
    def get_min_max_cutoff(self, *args):
1576
        # determine the recommended energy cutoff limits 
1577
        from ase.calculators.vasp import Vasp
1578
        calc_temp = Vasp()
1579
        atoms_temp = self.owner.atoms.copy()
1580
        calc_temp.initialize(atoms_temp)
1581
        calc_temp.write_potcar(suffix = '.check_energy_cutoff')
1582
        enmin = -1e6
1583
        enmax = -1e6
1584
        for line in open("POTCAR.check_energy_cutoff",'r').readlines():
1585
            if "ENMIN" in line:
1586
                enmax = max(enmax,float(line.split()[2].split(';')[0]))
1587
                enmin = max(enmin,float(line.split()[5]))
1588
        from os import system
1589
        system("rm POTCAR.check_energy_cutoff")
1590
        return enmin, enmax
1591

    
1592
    def k_changed(self, *args):
1593
        size = [self.kpts[i].value * np.sqrt(np.vdot(self.ucell[i],self.ucell[i])) for i in range(3)]
1594
        self.kpts_label.set_text(self.kpts_label_format % tuple(size))
1595

    
1596
    def check_encut_warning(self,*args):
1597
        if self.encut.value < self.encut_min_default:
1598
            self.encut_warning.set_markup("<b>WARNING:</b> cutoff energy is lower than recommended minimum!")
1599
        else:
1600
            self.encut_warning.set_markup("")
1601

    
1602
    def check_ismear_changed(self,*args):
1603
        if self.ismear.get_active_text() == 'Methfessel-Paxton':
1604
            self.smearing_order_spin.set_sensitive(True)
1605
        else:
1606
            self.smearing_order_spin.set_sensitive(False)
1607

    
1608
    def get_ismear(self,*args):
1609
        type = self.ismear.get_active_text()
1610
        if type == 'Methfessel-Paxton':
1611
            ismear_value = self.smearing_order.value
1612
        elif type == 'Fermi':
1613
            ismear_value = -1
1614
        else:
1615
            ismear_value = 0
1616
        return ismear_value
1617

    
1618
    def destroy(self):
1619
        self.grab_remove()
1620
        gtk.Window.destroy(self)
1621

    
1622
    def export_vasp_files(self, *args):
1623
        filename = ""
1624
        chooser = gtk.FileChooserDialog(
1625
            'Export VASP input files: choose directory ... ', 
1626
            None, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
1627
            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
1628
             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
1629
        chooser.set_filename(filename)
1630
        save = chooser.run()
1631
        if save == gtk.RESPONSE_OK or save == gtk.RESPONSE_SAVE:
1632
            filename = chooser.get_filename()
1633
            from os import chdir
1634
            chdir(filename)
1635
            self.set_attributes(*args)
1636
            param = getattr(self.owner, "vasp_parameters")
1637
            from ase.calculators.vasp import Vasp
1638
            calc_temp = Vasp(**param)
1639
            atoms_temp = self.owner.atoms.copy()
1640
            atoms_temp.set_calculator(calc_temp)
1641
            calc_temp.initialize(atoms_temp)
1642
            calc_temp.write_incar(atoms_temp)
1643
            calc_temp.write_potcar()
1644
            calc_temp.write_kpoints()
1645
            calc_temp.write_sort_file()
1646
            from ase.io.vasp import write_vasp
1647
            write_vasp('POSCAR', calc_temp.atoms_sorted, symbol_count = calc_temp.symbol_count)
1648
        chooser.destroy()
1649

    
1650
    def expert_keyword_import(self, *args):
1651
        command = self.expert_keyword_set.get_text().split()
1652
        if len(command) > 0 and command[0] in self.vasp_keyword_list and not command[0] in self.vasp_keyword_gui_list:
1653
            self.expert_keyword_create(command)
1654
        elif command[0] in self.vasp_keyword_gui_list:
1655
            oops("Please use the facilities provided in this window to manipulate "+
1656
                 "the keyword: " + command[0] + "!")
1657
        else:
1658
            oops("Don't know this keyword: " + command[0] 
1659
                 + "\nPlease check!\n\n" 
1660
                 + "If you really think it should be available, "
1661
                 + "please add it to the top of ase/calculators/vasp.py.")
1662
        self.expert_keyword_set.set_text("")
1663

    
1664
    def expert_keyword_create(self, command):
1665
        key = command[0]
1666
        if command[1] == "=":
1667
            command.remove("=")
1668
        argument = command[1]
1669
        if len(command) > 2:
1670
            for a in command[2:]:
1671
                argument += ' '+a
1672
        index = len(self.expert_keywords) 
1673
        self.expert_keywords += [[gtk.Label("    " +key+" = "),
1674
                                  gtk.Entry(max=45),
1675
                                  ExpertDeleteButton(index),
1676
                                  True]]
1677
        self.expert_keywords[index][1].set_text(argument)
1678
        self.expert_keywords[index][2].connect('clicked',self.expert_keyword_delete)
1679
        pack(self.expert_vbox, [self.expert_keywords[index][0],
1680
                                self.expert_keywords[index][1],
1681
                                self.expert_keywords[index][2]])
1682

    
1683
    def expert_keyword_delete(self, button, *args):
1684
        index = button.index   # which one to kill 
1685
        for i in [0,1,2]:
1686
            self.expert_keywords[index][i].destroy()
1687
        self.expert_keywords[index][3] = False