Coverage for ase / constraints / external_force.py: 63.33%
30 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-30 08:22 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-30 08:22 +0000
1import numpy as np
3from ase.constraints.constraint import FixConstraint, slice2enlist
6class ExternalForce(FixConstraint):
7 """Constraint object for pulling two atoms apart by an external force.
9 You can combine this constraint for example with FixBondLength but make
10 sure that *ExternalForce* comes first in the list if there are overlaps
11 between atom1-2 and atom3-4:
13 >>> from ase.build import bulk
15 >>> atoms = bulk('Cu', 'fcc', a=3.6)
16 >>> atom1, atom2, atom3, atom4 = atoms[:4]
17 >>> fext = 1.0
18 >>> con1 = ExternalForce(atom1, atom2, f_ext)
19 >>> con2 = FixBondLength(atom3, atom4)
20 >>> atoms.set_constraint([con1, con2])
22 see ase/test/external_force.py"""
24 def __init__(self, a1, a2, f_ext):
25 self.indices = [a1, a2]
26 self.external_force = f_ext
28 def get_removed_dof(self, atoms):
29 return 0
31 def adjust_positions(self, atoms, new):
32 pass
34 def adjust_forces(self, atoms, forces):
35 dist = np.subtract.reduce(atoms.positions[self.indices])
36 force = self.external_force * dist / np.linalg.norm(dist)
37 forces[self.indices] += (force, -force)
39 def adjust_potential_energy(self, atoms):
40 dist = np.subtract.reduce(atoms.positions[self.indices])
41 return -np.linalg.norm(dist) * self.external_force
43 def index_shuffle(self, atoms, ind):
44 """Shuffle the indices of the two atoms in this constraint"""
45 newa = [-1, -1] # Signal error
46 for new, old in slice2enlist(ind, len(atoms)):
47 for i, a in enumerate(self.indices):
48 if old == a:
49 newa[i] = new
50 if newa[0] == -1 or newa[1] == -1:
51 raise IndexError('Constraint not part of slice')
52 self.indices = newa
54 def __repr__(self):
55 return 'ExternalForce(%d, %d, %f)' % (
56 self.indices[0],
57 self.indices[1],
58 self.external_force,
59 )
61 def todict(self):
62 return {
63 'name': 'ExternalForce',
64 'kwargs': {
65 'a1': self.indices[0],
66 'a2': self.indices[1],
67 'f_ext': self.external_force,
68 },
69 }