root / ase / optimize / basin.py @ 14
Historique | Voir | Annoter | Télécharger (3,84 ko)
| 1 |
import numpy as np |
|---|---|
| 2 |
|
| 3 |
from ase.optimize.optimize import Dynamics |
| 4 |
from ase.optimize.fire import FIRE |
| 5 |
from ase.units import kB |
| 6 |
from ase.parallel import world |
| 7 |
from ase.io.trajectory import PickleTrajectory |
| 8 |
|
| 9 |
class BasinHopping(Dynamics): |
| 10 |
"""Basin hopping algorythm.
|
| 11 |
|
| 12 |
After Wales and Doye, J. Phys. Chem. A, vol 101 (1997) 5111-5116"""
|
| 13 |
|
| 14 |
def __init__(self, atoms, |
| 15 |
temperature=100 * kB,
|
| 16 |
optimizer=FIRE, |
| 17 |
fmax=0.1,
|
| 18 |
dr=.1,
|
| 19 |
logfile='-',
|
| 20 |
trajectory='lowest.traj',
|
| 21 |
optimizer_logfile='-',
|
| 22 |
local_minima_trajectory='local_minima.traj',
|
| 23 |
adjust_cm=True):
|
| 24 |
Dynamics.__init__(self, atoms, logfile, trajectory)
|
| 25 |
self.kT = temperature
|
| 26 |
self.optimizer = optimizer
|
| 27 |
self.fmax = fmax
|
| 28 |
self.dr = dr
|
| 29 |
if adjust_cm:
|
| 30 |
self.cm = atoms.get_center_of_mass()
|
| 31 |
else:
|
| 32 |
self.cm = None |
| 33 |
|
| 34 |
self.optimizer_logfile = optimizer_logfile
|
| 35 |
self.lm_trajectory = local_minima_trajectory
|
| 36 |
if isinstance(local_minima_trajectory, str): |
| 37 |
self.lm_trajectory = PickleTrajectory(local_minima_trajectory,
|
| 38 |
'w', atoms)
|
| 39 |
|
| 40 |
self.initialize()
|
| 41 |
|
| 42 |
def initialize(self): |
| 43 |
self.positions = 0. * self.atoms.get_positions() |
| 44 |
self.Emin = self.get_energy(self.atoms.get_positions()) |
| 45 |
self.rmin = self.atoms.get_positions() |
| 46 |
self.positions = self.atoms.get_positions() |
| 47 |
self.call_observers()
|
| 48 |
self.log(-1, self.Emin, self.Emin) |
| 49 |
|
| 50 |
def run(self, steps): |
| 51 |
"""Hop the basins for defined number of steps."""
|
| 52 |
|
| 53 |
ro = self.positions
|
| 54 |
Eo = self.get_energy(ro)
|
| 55 |
|
| 56 |
for step in range(steps): |
| 57 |
rn = self.move(ro)
|
| 58 |
En = self.get_energy(rn)
|
| 59 |
|
| 60 |
if En < self.Emin: |
| 61 |
# new minimum found
|
| 62 |
self.Emin = En
|
| 63 |
self.rmin = self.atoms.get_positions() |
| 64 |
self.call_observers()
|
| 65 |
rn = self.rmin
|
| 66 |
self.log(step, En, self.Emin) |
| 67 |
|
| 68 |
accept = np.exp((Eo - En) / self.kT) > np.random.uniform()
|
| 69 |
if accept:
|
| 70 |
ro = rn |
| 71 |
Eo = En |
| 72 |
|
| 73 |
def log(self, step, En, Emin): |
| 74 |
if self.logfile is None: |
| 75 |
return
|
| 76 |
name = self.__class__.__name__
|
| 77 |
self.logfile.write('%s: step %d, energy %15.6f, emin %15.6f\n' |
| 78 |
% (name, step, En, self.Emin))
|
| 79 |
self.logfile.flush()
|
| 80 |
|
| 81 |
def move(self, ro): |
| 82 |
atoms = self.atoms
|
| 83 |
# displace coordinates
|
| 84 |
disp = np.random.uniform(-1., 1., (len(atoms), 3)) |
| 85 |
rn = ro + self.dr * disp
|
| 86 |
atoms.set_positions(rn) |
| 87 |
if self.cm is not None: |
| 88 |
cm = atoms.get_center_of_mass() |
| 89 |
atoms.translate(self.cm - cm)
|
| 90 |
rn = atoms.get_positions() |
| 91 |
if world is not None: |
| 92 |
world.broadcast(rn, 0)
|
| 93 |
atoms.set_positions(rn) |
| 94 |
return atoms.get_positions()
|
| 95 |
|
| 96 |
def get_minimum(self): |
| 97 |
atoms = self.atoms.copy()
|
| 98 |
atoms.set_positions(self.rmin)
|
| 99 |
return self.Emin, atoms |
| 100 |
|
| 101 |
def get_energy(self, positions): |
| 102 |
"""Return the energy of the nearest local minimum."""
|
| 103 |
if np.sometrue(self.positions != positions): |
| 104 |
self.positions = positions
|
| 105 |
self.atoms.set_positions(positions)
|
| 106 |
|
| 107 |
try:
|
| 108 |
opt = self.optimizer(self.atoms, logfile=self.optimizer_logfile) |
| 109 |
opt.run(fmax=self.fmax)
|
| 110 |
if self.lm_trajectory is not None: |
| 111 |
self.lm_trajectory.write(self.atoms) |
| 112 |
|
| 113 |
self.energy = self.atoms.get_potential_energy() |
| 114 |
except:
|
| 115 |
# the atoms are probably to near to each other
|
| 116 |
self.energy = 1.e32 |
| 117 |
|
| 118 |
return self.energy |
| 119 |
|