root / ase / gui / scaling.py @ 4
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 |
|