Source code for ase.constraints.fix_bond_lengths
import numpy as np
from ase.constraints.constraint import FixConstraint
from ase.geometry import find_mic
[docs]
class FixBondLengths(FixConstraint):
maxiter = 500
def __init__(
self, pairs, tolerance=1e-13, bondlengths=None, iterations=None
):
"""iterations:
Ignored"""
self.pairs = np.asarray(pairs)
self.tolerance = tolerance
self.bondlengths = bondlengths
def get_removed_dof(self, atoms):
return len(self.pairs)
def adjust_positions(self, atoms, new):
old = atoms.positions
masses = atoms.get_masses()
if self.bondlengths is None:
self.bondlengths = self.initialize_bond_lengths(atoms)
for i in range(self.maxiter):
converged = True
for j, ab in enumerate(self.pairs):
a = ab[0]
b = ab[1]
cd = self.bondlengths[j]
r0 = old[a] - old[b]
d0, _ = find_mic(r0, atoms.cell, atoms.pbc)
d1 = new[a] - new[b] - r0 + d0
m = 1 / (1 / masses[a] + 1 / masses[b])
x = 0.5 * (cd**2 - np.dot(d1, d1)) / np.dot(d0, d1)
if abs(x) > self.tolerance:
new[a] += x * m / masses[a] * d0
new[b] -= x * m / masses[b] * d0
converged = False
if converged:
break
else:
raise RuntimeError('Did not converge')
def adjust_momenta(self, atoms, p):
old = atoms.positions
masses = atoms.get_masses()
if self.bondlengths is None:
self.bondlengths = self.initialize_bond_lengths(atoms)
for i in range(self.maxiter):
converged = True
for j, ab in enumerate(self.pairs):
a = ab[0]
b = ab[1]
cd = self.bondlengths[j]
d = old[a] - old[b]
d, _ = find_mic(d, atoms.cell, atoms.pbc)
dv = p[a] / masses[a] - p[b] / masses[b]
m = 1 / (1 / masses[a] + 1 / masses[b])
x = -np.dot(dv, d) / cd**2
if abs(x) > self.tolerance:
p[a] += x * m * d
p[b] -= x * m * d
converged = False
if converged:
break
else:
raise RuntimeError('Did not converge')
def adjust_forces(self, atoms, forces):
self.constraint_forces = -forces
self.adjust_momenta(atoms, forces)
self.constraint_forces += forces
def initialize_bond_lengths(self, atoms):
bondlengths = np.zeros(len(self.pairs))
for i, ab in enumerate(self.pairs):
bondlengths[i] = atoms.get_distance(ab[0], ab[1], mic=True)
return bondlengths
def get_indices(self):
return np.unique(self.pairs.ravel())
def todict(self):
return {
'name': 'FixBondLengths',
'kwargs': {
'pairs': self.pairs.tolist(),
'tolerance': self.tolerance,
},
}
def index_shuffle(self, atoms, ind):
"""Shuffle the indices of the two atoms in this constraint"""
map = np.zeros(len(atoms), int)
map[ind] = 1
n = map.sum()
map[:] = -1
map[ind] = range(n)
pairs = map[self.pairs]
self.pairs = pairs[(pairs != -1).all(1)]
if len(self.pairs) == 0:
raise IndexError('Constraint not part of slice')
[docs]
def FixBondLength(a1, a2):
"""Fix distance between atoms with indices a1 and a2."""
return FixBondLengths([(a1, a2)])