root / ase / gui / scaling.py @ 3
Historique | Voir | Annoter | Télécharger (14,54 ko)
1 |
# encoding: utf-8
|
---|---|
2 |
|
3 |
"Module for homogeneous deformation and calculations of elastic constants."
|
4 |
|
5 |
import gtk |
6 |
from ase.gui.simulation import Simulation |
7 |
from ase.gui.minimize import MinimizeMixin |
8 |
from ase.gui.energyforces import OutputFieldMixin |
9 |
from ase.gui.widgets import oops, pack, AseGuiCancelException |
10 |
import ase |
11 |
import numpy as np |
12 |
|
13 |
scaling_txt = """\
|
14 |
This module is intended for calculating elastic constants by homogeneously
|
15 |
deforming a system."""
|
16 |
|
17 |
class HomogeneousDeformation(Simulation, MinimizeMixin, OutputFieldMixin): |
18 |
"Window for homogeneous deformation and elastic constants."
|
19 |
|
20 |
def __init__(self, gui): |
21 |
Simulation.__init__(self, gui)
|
22 |
self.set_title("Homogeneous scaling") |
23 |
vbox = gtk.VBox() |
24 |
self.packtext(vbox, scaling_txt)
|
25 |
self.packimageselection(vbox, txt1="", txt2="") |
26 |
self.start_radio_nth.set_active(True) |
27 |
pack(vbox, gtk.Label(""))
|
28 |
|
29 |
# Radio buttons for choosing deformation mode.
|
30 |
tbl = gtk.Table(4,3) |
31 |
for i, l in enumerate(('3D', '2D', '1D')): |
32 |
l = l + " deformation "
|
33 |
lbl = gtk.Label(l) |
34 |
tbl.attach(lbl, i, i+1, 0, 1) |
35 |
self.radio_bulk = gtk.RadioButton(None, "Bulk") |
36 |
tbl.attach(self.radio_bulk, 0, 1, 1, 2) |
37 |
self.radio_xy = gtk.RadioButton(self.radio_bulk, "xy-plane") |
38 |
tbl.attach(self.radio_xy, 1, 2, 1, 2) |
39 |
self.radio_xz = gtk.RadioButton(self.radio_bulk, "xz-plane") |
40 |
tbl.attach(self.radio_xz, 1, 2, 2, 3) |
41 |
self.radio_yz = gtk.RadioButton(self.radio_bulk, "yz-plane") |
42 |
tbl.attach(self.radio_yz, 1, 2, 3, 4) |
43 |
self.radio_x = gtk.RadioButton(self.radio_bulk, "x-axis") |
44 |
tbl.attach(self.radio_x, 2, 3, 1, 2) |
45 |
self.radio_y = gtk.RadioButton(self.radio_bulk, "y-axis") |
46 |
tbl.attach(self.radio_y, 2, 3, 2, 3) |
47 |
self.radio_z = gtk.RadioButton(self.radio_bulk, "z-axis") |
48 |
tbl.attach(self.radio_z, 2, 3, 3, 4) |
49 |
tbl.show_all() |
50 |
pack(vbox, [tbl]) |
51 |
self.deformtable = [
|
52 |
(self.radio_bulk, (1,1,1)), |
53 |
(self.radio_xy, (1,1,0)), |
54 |
(self.radio_xz, (1,0,1)), |
55 |
(self.radio_yz, (0,1,1)), |
56 |
(self.radio_x, (1,0,0)), |
57 |
(self.radio_y, (0,1,0)), |
58 |
(self.radio_z, (0,0,1))] |
59 |
self.deform_label = gtk.Label("") |
60 |
pack(vbox, [self.deform_label])
|
61 |
self.choose_possible_deformations(first=True) |
62 |
|
63 |
# Parameters for the deformation
|
64 |
framedef = gtk.Frame("Deformation:")
|
65 |
vbox2 = gtk.VBox() |
66 |
vbox2.show() |
67 |
framedef.add(vbox2) |
68 |
self.max_scale = gtk.Adjustment(0.010, 0.001, 10.0, 0.001) |
69 |
max_scale_spin = gtk.SpinButton(self.max_scale, 10.0, 3) |
70 |
pack(vbox2, [gtk.Label("Maximal scale factor: "), max_scale_spin])
|
71 |
self.scale_offset = gtk.Adjustment(0.0, -10.0, 10.0, 0.001) |
72 |
scale_offset_spin = gtk.SpinButton(self.scale_offset, 10.0, 3) |
73 |
pack(vbox2, [gtk.Label("Scale offset: "), scale_offset_spin])
|
74 |
self.nsteps = gtk.Adjustment(5, 3, 100, 1) |
75 |
nsteps_spin = gtk.SpinButton(self.nsteps, 1, 0) |
76 |
pack(vbox2, [gtk.Label("Number of steps: "), nsteps_spin])
|
77 |
|
78 |
# Atomic relaxations
|
79 |
framerel = gtk.Frame("Atomic relaxations:")
|
80 |
vbox2 = gtk.VBox() |
81 |
vbox2.show() |
82 |
framerel.add(vbox2) |
83 |
self.radio_relax_on = gtk.RadioButton(None, "On ") |
84 |
self.radio_relax_off = gtk.RadioButton(self.radio_relax_on, "Off") |
85 |
self.radio_relax_off.set_active(True) |
86 |
pack(vbox2, [self.radio_relax_on, self.radio_relax_off]) |
87 |
self.make_minimize_gui(vbox2)
|
88 |
for r in (self.radio_relax_on, self.radio_relax_off): |
89 |
r.connect("toggled", self.relax_toggled) |
90 |
self.relax_toggled()
|
91 |
pack(vbox, [framedef, gtk.Label(" "), framerel])
|
92 |
pack(vbox, gtk.Label(""))
|
93 |
|
94 |
# Results
|
95 |
pack(vbox, [gtk.Label("Results:")])
|
96 |
self.radio_results_optimal = gtk.RadioButton(
|
97 |
None, "Load optimal configuration") |
98 |
self.radio_results_all = gtk.RadioButton(
|
99 |
self.radio_results_optimal, "Load all configurations") |
100 |
self.radio_results_optimal.set_active(True) |
101 |
pack(vbox, [self.radio_results_optimal])
|
102 |
pack(vbox, [self.radio_results_all])
|
103 |
|
104 |
# Output field
|
105 |
outframe = self.makeoutputfield(None) |
106 |
fitframe = gtk.Frame("Fit:")
|
107 |
vbox2 = gtk.VBox() |
108 |
vbox2.show() |
109 |
fitframe.add(vbox2) |
110 |
self.radio_fit_2 = gtk.RadioButton(None, "2nd") |
111 |
self.radio_fit_3 = gtk.RadioButton(self.radio_fit_2, "3rd") |
112 |
self.radio_fit_2.connect("toggled", self.change_fit) |
113 |
self.radio_fit_3.connect("toggled", self.change_fit) |
114 |
self.radio_fit_3.set_active(True) |
115 |
pack(vbox2, [gtk.Label("Order of fit: "), self.radio_fit_2, |
116 |
self.radio_fit_3])
|
117 |
pack(vbox2, [gtk.Label("")])
|
118 |
scrwin = gtk.ScrolledWindow() |
119 |
scrwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
120 |
self.fit_output = gtk.TextBuffer()
|
121 |
txtview = gtk.TextView(self.fit_output)
|
122 |
txtview.set_editable(False)
|
123 |
scrwin.add(txtview) |
124 |
scrwin.show_all() |
125 |
self.fit_win = scrwin
|
126 |
print "SIZE RQ:", scrwin.size_request() |
127 |
vbox2.pack_start(scrwin, True, True, 0) |
128 |
hbox = gtk.HBox(homogeneous=True)
|
129 |
for w in [outframe, fitframe]: |
130 |
hbox.pack_start(w) |
131 |
w.show() |
132 |
pack(vbox, hbox) |
133 |
pack(vbox, gtk.Label(""))
|
134 |
|
135 |
# Status field
|
136 |
self.status_label = gtk.Label("") |
137 |
pack(vbox, [self.status_label])
|
138 |
|
139 |
# Run buttons etc.
|
140 |
self.makebutbox(vbox)
|
141 |
vbox.show() |
142 |
self.add(vbox)
|
143 |
self.show()
|
144 |
self.gui.register_vulnerable(self) |
145 |
|
146 |
def choose_possible_deformations(self, first=False): |
147 |
"""Turn on sensible radio buttons.
|
148 |
|
149 |
Only radio buttons corresponding to deformations in directions
|
150 |
with periodic boundary conditions should be turned on.
|
151 |
"""
|
152 |
if self.setup_atoms(): |
153 |
pbc = self.atoms.get_pbc()
|
154 |
else:
|
155 |
pbc = [False, False, False] |
156 |
for radio, requirement in self.deformtable: |
157 |
ok = True
|
158 |
for i in range(3): |
159 |
if requirement[i] and not pbc[i]: |
160 |
ok = False
|
161 |
radio.set_sensitive(ok) |
162 |
if first and ok: |
163 |
# The first acceptable choice, choose it to prevent
|
164 |
# inconsistent state.
|
165 |
radio.set_active(True)
|
166 |
first = False
|
167 |
|
168 |
def relax_toggled(self, *args): |
169 |
"Turn minimization widgets on or off."
|
170 |
state = self.radio_relax_on.get_active()
|
171 |
for widget in (self.algo, self.fmax_spin, self.steps_spin): |
172 |
widget.set_sensitive(state) |
173 |
|
174 |
def notify_atoms_changed(self): |
175 |
"When atoms have changed, check for the number of images."
|
176 |
self.setupimageselection()
|
177 |
self.choose_possible_deformations()
|
178 |
|
179 |
def get_deformation_axes(self): |
180 |
"""Return which axes the user wants to deform along."""
|
181 |
for but, deform in self.deformtable: |
182 |
if but.get_active():
|
183 |
return np.array(deform)
|
184 |
# No deformation chosen!
|
185 |
oops("No deformation chosen: Please choose a deformation mode.")
|
186 |
return False |
187 |
|
188 |
def run(self, *args): |
189 |
"""Make the deformation."""
|
190 |
self.output.set_text("") |
191 |
if not self.setup_atoms(): |
192 |
return
|
193 |
deform_axes = self.get_deformation_axes()
|
194 |
if deform_axes is False: |
195 |
return #Nothing to do! |
196 |
|
197 |
# Prepare progress bar
|
198 |
if self.radio_relax_on.get_active(): |
199 |
fmax = self.fmax.value
|
200 |
mininame = self.minimizers[self.algo.get_active()] |
201 |
self.begin(mode="scale/min", algo=mininame, fmax=fmax, |
202 |
steps=self.steps.value, scalesteps=self.nsteps.value) |
203 |
else:
|
204 |
self.begin(mode="scale", scalesteps=self.nsteps.value) |
205 |
try:
|
206 |
logger_func = self.gui.simulation['progress'].get_logger_stream |
207 |
except (KeyError, AttributeError): |
208 |
logger = None
|
209 |
else:
|
210 |
logger = logger_func() # Don't catch errors in the function.
|
211 |
|
212 |
# Display status message
|
213 |
self.status_label.set_text("Running ...") |
214 |
self.status_label.modify_fg(gtk.STATE_NORMAL,
|
215 |
gtk.gdk.color_parse('#AA0000'))
|
216 |
while gtk.events_pending():
|
217 |
gtk.main_iteration() |
218 |
|
219 |
# Do the scaling
|
220 |
scale = self.max_scale.value
|
221 |
steps = np.linspace(-scale, scale, self.nsteps.value)
|
222 |
steps += self.scale_offset.value
|
223 |
undef_cell = self.atoms.get_cell()
|
224 |
results = [] |
225 |
txt = "Strain\t\tEnergy [eV]\n"
|
226 |
# If we load all configurations, prepare it.
|
227 |
if self.radio_results_all.get_active(): |
228 |
self.prepare_store_atoms()
|
229 |
|
230 |
try:
|
231 |
# Now, do the deformation
|
232 |
for i, d in enumerate(steps): |
233 |
deformation = np.diag(1.0 + d * deform_axes)
|
234 |
self.atoms.set_cell(np.dot(undef_cell, deformation),
|
235 |
scale_atoms=True)
|
236 |
if self.gui.simulation.has_key('progress'): |
237 |
self.gui.simulation['progress'].set_scale_progress(i) |
238 |
if self.radio_relax_on.get_active(): |
239 |
algo = getattr(ase.optimize, mininame)
|
240 |
minimizer = algo(self.atoms, logfile=logger)
|
241 |
minimizer.run(fmax=fmax, steps=self.steps.value)
|
242 |
e = self.atoms.get_potential_energy()
|
243 |
results.append((d, e)) |
244 |
txt = txt + ("%.3f\t\t%.3f\n" % (d, e))
|
245 |
self.output.set_text(txt)
|
246 |
if self.radio_results_all.get_active(): |
247 |
self.store_atoms()
|
248 |
except AseGuiCancelException:
|
249 |
# Update display to reflect cancellation of simulation.
|
250 |
self.status_label.set_text("Calculation CANCELLED.") |
251 |
self.status_label.modify_fg(gtk.STATE_NORMAL,
|
252 |
gtk.gdk.color_parse('#AA4000'))
|
253 |
except MemoryError: |
254 |
self.status_label.set_text("Out of memory, consider using LBFGS instead") |
255 |
self.status_label.modify_fg(gtk.STATE_NORMAL,
|
256 |
gtk.gdk.color_parse('#AA4000'))
|
257 |
|
258 |
else:
|
259 |
# Update display to reflect succesful end of simulation.
|
260 |
self.status_label.set_text("Calculation completed.") |
261 |
self.status_label.modify_fg(gtk.STATE_NORMAL,
|
262 |
gtk.gdk.color_parse('#007700'))
|
263 |
|
264 |
if results:
|
265 |
self.do_fit(np.array(results))
|
266 |
if self.radio_results_optimal.get_active(): |
267 |
if self.minimum_ok: |
268 |
deformation = np.diag(1.0 + self.x0 * deform_axes) |
269 |
self.atoms.set_cell(np.dot(undef_cell, deformation),
|
270 |
scale_atoms=True)
|
271 |
if self.radio_relax_on.get_active(): |
272 |
if self.gui.simulation.has_key('progress'): |
273 |
self.gui.simulation['progress'].set_scale_progress( |
274 |
len(steps))
|
275 |
algo = getattr(ase.optimize, mininame)
|
276 |
minimizer = algo(self.atoms, logfile=logger)
|
277 |
minimizer.run(fmax=fmax, steps=self.steps.value)
|
278 |
# Store the optimal configuration.
|
279 |
self.prepare_store_atoms()
|
280 |
self.store_atoms()
|
281 |
else:
|
282 |
oops("No trustworthy minimum: Old configuration kept.")
|
283 |
self.activate_output()
|
284 |
self.gui.notify_vulnerable()
|
285 |
self.end()
|
286 |
|
287 |
# If we store all configurations: Open movie window and energy graph
|
288 |
if self.gui.images.nimages > 1: |
289 |
self.gui.movie()
|
290 |
assert not np.isnan(self.gui.images.E[0]) |
291 |
if not self.gui.plot_graphs_newatoms(): |
292 |
expr = 'i, e - E[-1]'
|
293 |
self.gui.plot_graphs(expr=expr)
|
294 |
# Continuations should use the best image
|
295 |
nbest = np.argmin(np.array(results)[:,1])
|
296 |
self.start_nth_adj.value = nbest
|
297 |
|
298 |
|
299 |
def change_fit(self, widget): |
300 |
"Repeat the fitting if the order is changed."
|
301 |
# It may be called both for the button being turned on and the
|
302 |
# one being turned off. But we only want to call do_fit once.
|
303 |
# And only if there are already cached results (ie. if the
|
304 |
# order is changed AFTER the calculation is done).
|
305 |
if widget.get_active() and getattr(self, "results", None) is not None: |
306 |
self.do_fit(None) |
307 |
|
308 |
def do_fit(self, results): |
309 |
"Fit the results to a polynomial"
|
310 |
if results is None: |
311 |
results = self.results # Use cached results |
312 |
else:
|
313 |
self.results = results # Keep for next time |
314 |
self.minimum_ok = False |
315 |
if self.radio_fit_3.get_active(): |
316 |
order = 3
|
317 |
else:
|
318 |
order = 2
|
319 |
|
320 |
if len(results) < 3: |
321 |
txt = ("Insufficent data for a fit\n(only %i data points)\n"
|
322 |
% (len(results),) )
|
323 |
order = 0
|
324 |
elif len(results) == 3 and order == 3: |
325 |
txt = "REVERTING TO 2ND ORDER FIT\n(only 3 data points)\n\n"
|
326 |
order = 2
|
327 |
else:
|
328 |
txt = ""
|
329 |
|
330 |
if order > 0: |
331 |
fit0 = np.poly1d(np.polyfit(results[:,0], results[:,1], order)) |
332 |
fit1 = np.polyder(fit0, 1)
|
333 |
fit2 = np.polyder(fit1, 1)
|
334 |
|
335 |
x0 = None
|
336 |
for t in np.roots(fit1): |
337 |
if fit2(t) > 0: |
338 |
x0 = t |
339 |
break
|
340 |
if x0 is None: |
341 |
txt = txt + "No minimum found!"
|
342 |
else:
|
343 |
e0 = fit0(x0) |
344 |
e2 = fit2(x0) |
345 |
txt += "E = "
|
346 |
if order == 3: |
347 |
txt += "A(x - x0)³ + "
|
348 |
txt += "B(x - x0)² + C\n\n"
|
349 |
txt += "B = %.5g eV\n" % (e2,)
|
350 |
txt += "C = %.5g eV\n" % (e0,)
|
351 |
txt += "x0 = %.5g\n" % (x0,)
|
352 |
lowest = self.scale_offset.value - self.max_scale.value |
353 |
highest = self.scale_offset.value + self.max_scale.value |
354 |
if x0 < lowest or x0 > highest: |
355 |
txt += "\nWARNING: Minimum is outside interval\n"
|
356 |
txt += "It is UNRELIABLE!\n"
|
357 |
else:
|
358 |
self.minimum_ok = True |
359 |
self.x0 = x0
|
360 |
self.fit_output.set_text(txt)
|
361 |
|