Coverage for ase / constraints / fixed_plane.py: 100.00%
16 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
1from ase.constraints.constraint import (
2 IndexedConstraint,
3 _normalize,
4 _projection,
5)
8class FixedPlane(IndexedConstraint):
9 """
10 Constraint object for fixing chosen atoms to only move in a plane.
12 The plane is defined by its normal vector *direction*
13 """
15 def __init__(self, indices, direction):
16 """Constrain chosen atoms.
18 Parameters
19 ----------
20 indices : int or list of int
21 Index or indices for atoms that should be constrained
22 direction : list of 3 int
23 Direction of the normal vector
25 Examples
26 --------
27 Fix all Copper atoms to only move in the yz-plane:
29 >>> from ase.build import bulk
30 >>> from ase.constraints import FixedPlane
32 >>> atoms = bulk('Cu', 'fcc', a=3.6)
33 >>> c = FixedPlane(
34 ... indices=[atom.index for atom in atoms if atom.symbol == 'Cu'],
35 ... direction=[1, 0, 0],
36 ... )
37 >>> atoms.set_constraint(c)
39 or constrain a single atom with the index 0 to move in the xy-plane:
41 >>> c = FixedPlane(indices=0, direction=[0, 0, 1])
42 >>> atoms.set_constraint(c)
43 """
44 super().__init__(indices=indices)
45 self.dir = _normalize(direction)
47 def adjust_positions(self, atoms, newpositions):
48 step = newpositions[self.index] - atoms.positions[self.index]
49 newpositions[self.index] -= _projection(step, self.dir)
51 def adjust_forces(self, atoms, forces):
52 forces[self.index] -= _projection(forces[self.index], self.dir)
54 def get_removed_dof(self, atoms):
55 return len(self.index)
57 def todict(self):
58 return {
59 'name': 'FixedPlane',
60 'kwargs': {
61 'indices': self.index.tolist(),
62 'direction': self.dir.tolist(),
63 },
64 }
66 def __repr__(self):
67 return f'FixedPlane(indices={self.index}, {self.dir.tolist()})'