Statistiques
| Révision :

root / ase / gui / progress.py @ 3

Historique | Voir | Annoter | Télécharger (12,33 ko)

1
# encoding: utf-8
2

    
3
import gtk
4
import numpy as np
5
from ase.gui.widgets import pack, oops, AseGuiCancelException
6
import sys
7
import re
8
import time
9

    
10

    
11
class DummyProgressIndicator:
12
    def begin(self, **kwargs):
13
        pass
14

    
15
    def end(self):
16
        pass
17

    
18
class DefaultProgressIndicator(gtk.Window):
19
    "Window for reporting progress."
20
    waittime = 3  # Time (in sec) after which a progress bar appears.
21
    def __init__(self):
22
        gtk.Window.__init__(self)
23
        self.set_title("Progress")
24
        self.globalbox = gtk.VBox()
25

    
26
        # Scaling deformation progress frame
27
        self.scalebox = gtk.VBox()
28
        self.scaleframe = gtk.Frame("Scaling deformation:")
29
        vbox = gtk.VBox()
30
        self.scaleframe.add(vbox)
31
        pack(self.scalebox, [self.scaleframe])
32
        pack(self.scalebox, gtk.Label(""))
33

    
34
        self.label_scale_stepno_format = "Step number %s of %s."
35
        self.label_scale_stepno = gtk.Label(
36
            self.label_scale_stepno_format % ("-" , "-"))
37
        pack(vbox, [self.label_scale_stepno])
38
        self.scale_progress = gtk.ProgressBar()
39
        self.scale_progress.modify_bg(gtk.STATE_PRELIGHT,
40
                                      gtk.gdk.color_parse('#00AA00'))
41
        pack(vbox, [self.scale_progress])
42

    
43
        vbox.show()
44
        self.scaleframe.show()
45
        self.globalbox.pack_start(self.scalebox)
46
        
47
        # Minimization progress frame
48
        self.minbox = gtk.VBox()  # Box containing frame and spacing
49
        self.minframe = gtk.Frame("Energy minimization:")
50
        vbox = gtk.VBox()         # Box containing the frames content.
51
        self.minframe.add(vbox)
52
        pack(self.minbox, [self.minframe])
53
        pack(self.minbox, gtk.Label(""))
54
        
55
        self.label_min_stepno = gtk.Label("-")
56
        pack(vbox, [gtk.Label("Step number: "), self.label_min_stepno])
57
        lbl = gtk.Label()
58
        lbl.set_markup("F<sub>max</sub>: ")
59
        self.minimize_progress = gtk.ProgressBar()
60
        pack(vbox, [lbl, self.minimize_progress])
61
        self.label_min_fmax = gtk.Label("-")
62
        lbl = gtk.Label()
63
        lbl.set_markup("Convergence criterion: F<sub>max</sub> = ")
64
        pack(vbox, [lbl, self.label_min_fmax])
65
        self.label_min_maxsteps = gtk.Label("-")
66
        pack(vbox, [gtk.Label("Max. number of steps: "),
67
                    self.label_min_maxsteps])
68
        
69
        vbox.show()
70
        self.minframe.show()
71
        self.globalbox.pack_start(self.minbox)
72
        self.globalbox.show()
73
        self.add(self.globalbox)
74

    
75
        # Make the cancel button
76
        self.cancelbut = gtk.Button(stock=gtk.STOCK_CANCEL)
77
        self.cancelbut.connect('clicked', self.cancel)
78
        pack(self.globalbox, [self.cancelbut], end=True, bottom=True)
79
        
80
    def begin(self, mode=None, algo=None, fmax=None, steps=None,
81
              scalesteps=None):
82
        self.mode = mode
83
        # Hide all mode-specific boxes
84
        self.scalebox.hide()
85
        self.minbox.hide()
86
        # Activate any relevant box
87
        if mode == "scale" or mode == "scale/min":
88
            self.scalesteps = int(scalesteps)
89
            self.scalebox.show()
90
            self.set_scale_progress(0, init=True)
91
        if mode == "min" or mode == "scale/min":
92
            # It is a minimization.
93
            self.minbox.show()
94
            self.label_min_stepno.set_text("-")
95
            self.label_min_fmax.set_text("%.3f" % (fmax,))
96
            self.label_min_maxsteps.set_text(str(steps))
97
            self.minimize_progress.set_fraction(0)
98
            self.minimize_progress.set_text("unknown")
99
        # Record starting time
100
        self.starttime = time.time()
101
        self.active = None  # Becoming active
102
        self.raisecancelexception = False
103
        
104
    def end(self):
105
        self.hide()
106
        self.active = False
107

    
108
    def activity(self):
109
        "Register that activity occurred."
110
        if self.active is None and time.time() > self.starttime + self.waittime:
111
            # This has taken so long that a progress bar is needed.
112
            self.show()
113
            self.active = True
114
        # Allow GTK to update display
115
        while gtk.events_pending():
116
            gtk.main_iteration()
117
        if self.raisecancelexception:
118
            self.cancelbut.set_sensitive(True)
119
            raise AseGuiCancelException
120

    
121
    def cancel(self, widget):
122
        print "CANCEL pressed."
123
        # We cannot raise the exception here, as this function is
124
        # called by the GTK main loop.
125
        self.raisecancelexception = True
126
        self.cancelbut.set_sensitive(False)
127

    
128
    def set_scale_progress(self, step, init=False):
129
        "Set the step number in scaling deformation."
130
        self.label_scale_stepno.set_text(
131
            self.label_scale_stepno_format % (step, self.scalesteps))
132
        percent = 1.0 * step / self.scalesteps
133
        self.scale_progress.set_fraction(percent)
134
        self.scale_progress.set_text("%i%%" % (round(100*percent),))
135
        if not init:
136
            self.activity()
137
        
138
    def logger_write(self, line):
139
        if self.mode == "min" or self.mode == "scale/min":
140
            # Update the minimization progress bar.
141
            w = line.split()
142
            fmax = float(w[-1])
143
            step = w[1]
144
            self.minimize_progress.set_fraction(fmax / 1.0)
145
            self.minimize_progress.set_text(w[-1])
146
            self.label_min_stepno.set_text(step)
147
        else:
148
            raise RuntimeError(
149
                "ProgressIndicator.logger_write called unexpectedly")
150
        self.activity()
151
            
152
    def get_logger_stream(self):
153
        return LoggerStream(self)
154

    
155

    
156
class GpawProgressIndicator(DefaultProgressIndicator):
157
    "Window for reporting GPAW progress."
158

    
159
    def __init__(self):
160
        DefaultProgressIndicator.__init__(self)
161

    
162
        # GPAW progress frame
163
        self.gpawframe = gtk.Frame("GPAW progress:")
164
        vbox = self.gpawvbox = gtk.VBox()
165
        self.gpawframe.add(vbox)
166
        self.table = gtk.Table(1, 2)
167
        self.tablerows = 0
168
        pack(vbox, self.table)
169
        self.status = gtk.Label("-")
170
        self.tablepack([gtk.Label("Status: "), self.status])
171
        self.iteration = gtk.Label("-")
172
        self.tablepack([gtk.Label("Iteration: "), self.iteration])
173
        self.tablepack([gtk.Label("")])
174
        lbl = gtk.Label()
175
        lbl.set_markup("log<sub>10</sub>(change):")
176
        self.tablepack([gtk.Label(""), lbl])
177
        self.wfs_progress = gtk.ProgressBar()
178
        self.tablepack([gtk.Label("Wave functions: "), self.wfs_progress])
179
        self.dens_progress = gtk.ProgressBar()
180
        self.tablepack([gtk.Label("Density: "), self.dens_progress])
181
        self.energy_progress = gtk.ProgressBar()
182
        self.tablepack([gtk.Label("Energy: "), self.energy_progress])
183
        self.tablepack([gtk.Label("")])
184
        self.versionlabel = gtk.Label("")
185
        self.tablepack([gtk.Label("GPAW version: "), self.versionlabel])
186
        self.natomslabel = gtk.Label("")
187
        self.tablepack([gtk.Label("Number of atoms: "), self.natomslabel])
188
        self.memorylabel = gtk.Label("N/A")
189
        self.tablepack([gtk.Label("Memory estimate: "), self.memorylabel])
190
        self.globalbox.pack_start(self.gpawframe)
191
        self.gpawframe.show()
192

    
193
        vbox.show()
194
        self.active = False
195

    
196
    def tablepack(self, widgets):
197
        self.tablerows += 1
198
        self.table.resize(self.tablerows, 2)
199
        for i, w in enumerate(widgets):
200
            self.table.attach(w, i, i+1, self.tablerows-1, self.tablerows)
201
            if hasattr(w, "set_alignment"):
202
                w.set_alignment(0, 0.5)
203
            w.show()
204
            
205
    def begin(self, **kwargs):
206
        DefaultProgressIndicator.begin(self, **kwargs)
207
        # Set GPAW specific stuff.
208
        self.active = True
209
        self.oldenergy = None
210
        self.poscount = None
211
        self.reset_gpaw_bars()
212
        # With GPAW, all calculations are slow: Show progress window
213
        # immediately.
214
        self.show()
215
        while gtk.events_pending():
216
            gtk.main_iteration()
217

    
218
    def reset_gpaw_bars(self):
219
        for lbl in (self.status, self.iteration):
220
            lbl.set_text("-")
221
        for bar in (self.wfs_progress, self.dens_progress,
222
                    self.energy_progress):
223
            bar.set_fraction(0.0)
224
            bar.set_text("No info")
225

    
226
    def gpaw_write(self, txt):
227
        #if not self.active:
228
        #    self.begin()
229
        sys.stdout.write(txt)
230
        versearch = re.search("\|[ |_.]+([0-9]+\.[0-9]+\.[0-9]+)", txt)
231
        if versearch:
232
            # Starting a gpaw calculation.
233
            self.versionlabel.set_text(versearch.group(1))
234
            self.status.set_text("Initializing")
235
        elif txt.startswith("Positions:"):
236
            # Start counting atoms
237
            self.poscount = True
238
            self.reset_gpaw_bars()
239
            self.status.set_text("Starting calculation")
240
            self.oldenergy = None
241
        elif txt.strip() == "":
242
            # Stop counting atoms
243
            self.poscount = False
244
        elif self.poscount:
245
            # Count atoms.
246
            w = txt.split()
247
            assert(len(w) == 5)
248
            self.natoms = int(w[0]) + 1
249
            self.natomslabel.set_text(str(self.natoms))
250
        elif txt.startswith("iter:"):
251
            # Found iteration line.
252
            wfs = txt[self.wfs_idx:self.density_idx].strip()
253
            dens = txt[self.density_idx:self.energy_idx].strip()
254
            energy = txt[self.energy_idx:self.fermi_idx].strip()
255
            if wfs:
256
                p = fraction(float(wfs), -9.0)
257
                self.wfs_progress.set_fraction(p)
258
                self.wfs_progress.set_text(wfs)
259
            if dens:
260
                p = fraction(float(dens), -4.0)
261
                self.dens_progress.set_fraction(p)
262
                self.dens_progress.set_text(dens)
263
            if energy:
264
                if self.oldenergy is None:
265
                    self.oldenergy = float(energy)
266
                else:
267
                    de = abs(self.oldenergy - float(energy))
268
                    self.oldenergy = float(energy)
269
                    if de > 1e-10:
270
                        de = np.log10(de/self.natoms)
271
                        p = fraction(de, -3.0)
272
                        self.energy_progress.set_fraction(p)
273
                        self.energy_progress.set_text("%.1f" % de)
274
                    else:
275
                        self.energy_progress.set_fraction(1)
276
                        self.energy_progress.set_text("unchanged")
277
            words = txt.split()
278
            self.iteration.set_text(words[1])
279
        elif (-1 < txt.find("WFS") < txt.find("Density") < txt.find("Energy")
280
               < txt.find("Fermi")):
281
            # Found header of convergence table
282
            self.wfs_idx = txt.find("WFS")
283
            self.density_idx = txt.find("Density")
284
            self.energy_idx = txt.find("Energy")
285
            self.fermi_idx = txt.find("Fermi")
286
            self.status.set_text("Self-consistency loop")
287
            self.iteration.set_text("0")
288
        elif txt.find("Converged After") != -1:
289
            # SCF loop has converged.
290
            words = txt.split()
291
            self.status.set_text("Calculating forces")
292
            self.iteration.set_text(words[2]+" (converged)")
293
        elif -1 < txt.find("Calculator") < txt.find("MiB"):
294
            # Memory estimate
295
            words = txt.split()
296
            self.memorylabel.set_text(words[1]+" "+words[2])
297
        self.activity()
298

    
299
    def get_gpaw_stream(self):
300
        return GpawStream(self)
301

    
302
        
303

    
304
class LoggerStream:
305
    "A file-like object feeding minimizer logs to GpawProgressWindow."
306
    def __init__(self, progresswindow):
307
        self.window = progresswindow
308
        
309
    def write(self, txt):
310
        self.window.logger_write(txt)
311

    
312
    def flush(self):
313
        pass
314
    
315
        
316
class GpawStream:
317
    "A file-like object feeding GPAWs txt file to GpawProgressWindow."
318
    def __init__(self, progresswindow):
319
        self.window = progresswindow
320
        
321
    def write(self, txt):
322
        if txt == "":
323
            return
324
        endline = txt[-1] == '\n'
325
        if endline:
326
            txt = txt[:-1]
327
        lines = txt.split("\n")
328
        if endline:
329
            for l in lines:
330
                self.window.gpaw_write(l+'\n')
331
        else:
332
            for l in lines[:-1]:
333
                self.window.gpaw_write(l+'\n')
334
            self.window.gpaw_write(lines[-1])
335

    
336
    def flush(self):
337
        pass
338

    
339
def fraction(value, maximum):
340
    p = value/maximum
341
    if p < 0.0:
342
        return 0.0
343
    elif p > 1.0:
344
        return 1.0
345
    else:
346
        return p
347
    
348