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

1import numpy as np 

2 

3from ase.constraints.constraint import FixConstraint, slice2enlist 

4 

5 

6class ExternalForce(FixConstraint): 

7 """Constraint object for pulling two atoms apart by an external force. 

8 

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: 

12 

13 >>> from ase.build import bulk 

14 

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]) 

21 

22 see ase/test/external_force.py""" 

23 

24 def __init__(self, a1, a2, f_ext): 

25 self.indices = [a1, a2] 

26 self.external_force = f_ext 

27 

28 def get_removed_dof(self, atoms): 

29 return 0 

30 

31 def adjust_positions(self, atoms, new): 

32 pass 

33 

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) 

38 

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 

42 

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 

53 

54 def __repr__(self): 

55 return 'ExternalForce(%d, %d, %f)' % ( 

56 self.indices[0], 

57 self.indices[1], 

58 self.external_force, 

59 ) 

60 

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 }