Esempio n. 1
0
def test_minimahop():
    # Make Pt 111 slab with Cu2 adsorbate.
    atoms = fcc111('Pt', (2, 2, 1), vacuum=7., orthogonal=True)
    adsorbate = Atoms([
        Atom('Cu', atoms[2].position + (0., 0., 2.5)),
        Atom('Cu', atoms[2].position + (0., 0., 5.0))
    ])
    atoms.extend(adsorbate)

    # Constrain the surface to be fixed and a Hookean constraint between
    # the adsorbate atoms.
    constraints = [
        FixAtoms(indices=[atom.index for atom in atoms
                          if atom.symbol == 'Pt']),
        Hookean(a1=4, a2=5, rt=2.6, k=15.),
        Hookean(a1=4, a2=(0., 0., 1., -15.), k=15.)
    ]
    atoms.set_constraint(constraints)

    # Set the calculator.
    calc = EMT()
    atoms.set_calculator(calc)

    # Instantiate and run the minima hopping algorithm.
    hop = MinimaHopping(atoms,
                        Ediff0=2.5,
                        T0=2000.,
                        beta1=1.2,
                        beta2=1.2,
                        mdmin=1)
    hop(totalsteps=3)
    # Test ability to restart and temperature stopping.
    hop(maxtemp=3000)
    def _constrain(self):
        """Puts Hookean constraint on Pb atoms."""
        #        self._Pb_positions = self._atoms.positions[self._Pb_indices]
        #        self._Br_positions = self._atoms.positions[self._Br_indices]
        constraints_inorganic_Hookean = [
            Hookean(a1=int(self._Pb_indices[i]),
                    a2=int(j),
                    rt=self._atoms.get_distances(
                        int(self._Pb_indices[i]), int(j), mic=True) +
                    self._rt1,
                    k=self._k1) for i in range(len(self._Pb_indices))
            for j in self._indices_list[i]
        ]
        constraint_Br1 = [
            Hookean(a1=self._Br_index1,
                    a2=self._Br_index4,
                    rt=self._atoms.get_distances(
                        self._Br_index1, self._Br_index4, mic=True) +
                    self._rt2,
                    k=self._k2)
        ]
        constraint_Br2 = [
            Hookean(a1=self._Br_index2,
                    a2=self._Br_index3,
                    rt=self._atoms.get_distances(
                        self._Br_index2, self._Br_index3, mic=True) +
                    self._rt2,
                    k=self._k2)
        ]

        #        Pb_z = self._Pb_positions[:,2]
        #        Pb_average_z = np.average(Pb_z)
        #        Pb_upper_indices = self._Pb_indices[np.where(Pb_z > Pb_average_z)]
        #        Pb_bottom_indices = self._Pb_indices[np.where(Pb_z < Pb_average_z)]
        #        constraint_fix = [FixAtoms(indices=[Pb_upper_indices[0]])]
        #        constraint_upper_to_fix = [Hookean(a1=Pb_upper_indices[0], a2=int(i), rt=self._atoms.get_distance(Pb_upper_indices[0], i, mic=True) + 0.1, k=5.) for i in Pb_upper_indices[1:]]
        #        constraint_upper = [Hookean(a1=int(i), a2=int(j), rt=self._atoms.get_distance(i, j, mic=True) + 0.1, k=5.) for i in Pb_upper_indices[1:] for j in Pb_upper_indices[1:] if i < j]
        #        constraint_bottom = [Hookean(a1=int(i), a2=int(j), rt=self._atoms.get_distance(i, j, mic=True) + 0.1, k=5.) for i in Pb_bottom_indices for j in Pb_bottom_indices if i < j]
        constraints_non_flat = [
            constrain for constrain in
            [constraints_inorganic_Hookean, constraint_Br1, constraint_Br2]
            if constrain != []
        ]
        constraints = [
            item for sublist in constraints_non_flat for item in sublist
        ]
        #        constraints = [Hookean(a1=self._Pb_indices[i], a2=self._Pb_positions[i], rt=0.01, k=15.) for i in range(len(self._Pb_indices))]
        #        self._atoms.set_constraint(constraints)
        self._atoms.set_constraint(constraints)
def test_momenta_hookean(atoms):
    atoms.set_constraint(Hookean(0, 1, rt=1., k=10.))
    atoms.set_momenta(np.zeros(atoms.get_momenta().shape))
    actual = atoms.get_momenta()
    desired = np.zeros(atoms.get_momenta().shape)
    # Why zero memoenta?  Should we not test something juicier?
    assert (actual == desired).all()
def test_set_momenta():
    """Test that set_momenta behaves as expected when constraints are
    involved."""

    import numpy as np
    from ase import Atoms, Atom
    from ase.constraints import Hookean, FixAtoms


    # FixAtoms check
    atoms = Atoms([Atom('H', (0., 0., 0.)),
                   Atom('H', (2., 0., 0.))])
    atoms.set_constraint(FixAtoms(indices=[0]))
    atoms.set_momenta(np.ones(atoms.get_momenta().shape))
    desired = np.ones(atoms.get_momenta().shape)
    desired[0] = 0.
    actual = atoms.get_momenta()
    assert (actual == desired).all()

    # Hookean check
    atoms = Atoms([Atom('H', (0., 0., 0.)),
                   Atom('H', (2., 0., 0.))])
    atoms.set_constraint(Hookean(0, 1, rt=1., k=10.))
    atoms.set_momenta(np.zeros(atoms.get_momenta().shape))
    actual = atoms.get_momenta()
    desired = np.zeros(atoms.get_momenta().shape)
Esempio n. 5
0
def _shift_constraints(shift, rigid_atoms, bonds, angles, dihedrals, hookean_lst):
    """Helper function for place_and_preoptimize_adsorbates
    
    Args:
        shift (int)         :   constant by which to shift the atom indices
        rigid_atoms (list)  :   indices of atoms to be fixed
        bonds (list)        :   each element contains a tuple of bondlength and atom indices
        angles (list)       :   each element contains a tuple of angle and atom indices
        dihedrals (list)    :   each element contains a tuple of dihedral and atom indices
        hookean_lst (list)  :   ase.constraints.Hookean bond constraints

    Returns:
        tuple :     All indices of rigid_atoms, bonds, angles, dihedrals and hookean_lst are
                    shifted by shift
    """
    new_rigid_atoms = []
    new_bonds = []
    new_angles = []
    new_dihedrals = []
    new_hookean_lst = []

    for rigid_atom in rigid_atoms:
        new_rigid_atoms.append(rigid_atom + shift)
    for value, bond in bonds:
        new_bonds.append([value, [bond[0]+shift, bond[1]+shift]])
        c = Hookean(a1=int(bond[0]) + shift, a2=int(bond[1]) + shift,
                rt=value, k=200.)
        new_hookean_lst.append(c)
    for value, angle in angles:
        new_angles.append([value, [angle[0]+shift,angle[1]+shift,angle[2]+shift]])
    for value, dihedral in dihedrals:
        new_dihedrals.append([value, [dihedral[0]+shift,dihedral[1]+shift,dihedral[2]+shift, dihedral[3]+shift ]])

    return new_rigid_atoms, new_bonds, new_angles, new_dihedrals, new_hookean_lst
Esempio n. 6
0
def generate_data(count, filename, temp, hook, cons_t=False):
    """Generates test or training data with a simple MD simulation."""
    traj = ase.io.Trajectory(filename, "w")
    slab = fcc100("Cu", size=(3, 3, 3))
    ads = molecule("CO")
    add_adsorbate(slab, ads, 5, offset=(1, 1))
    cons = FixAtoms(indices=[
        atom.index for atom in slab if (atom.tag == 2 or atom.tag == 3)
    ])
    if hook:
        cons2 = Hookean(a1=28, a2=27, rt=1.58, k=10.0)
        slab.set_constraint([cons, cons2])
    else:
        slab.set_constraint(cons)
    slab.center(vacuum=13., axis=2)
    slab.set_pbc(True)
    slab.wrap(pbc=[True] * 3)
    slab.set_calculator(EMT())
    slab.get_forces()
    dyn = QuasiNewton(slab)
    dyn.run(fmax=0.05)
    traj.write(slab)
    if cons_t is True:
        dyn = Langevin(slab, 1.0 * units.fs, temp * units.kB, 0.002)
    else:
        dyn = VelocityVerlet(slab, dt=1.0 * units.fs)
    for step in range(count):
        dyn.run(20)
        traj.write(slab)
 def _constrain(self):
     """Puts Hookean constraint on Pb atoms."""
     self._Pb_positions = self._atoms.positions[self._Pb_indices]
     constraints = [
         Hookean(a1=self._Pb_indices[i],
                 a2=self._Pb_positions[i],
                 rt=0.01,
                 k=15.) for i in range(len(self._Pb_indices))
     ]
     self._atoms.set_constraint(constraints)
Esempio n. 8
0
def _hookean_bonds(atoms, nb_lst):
    """Helper function for place_and_preoptimize_adsorbates.

    Args:
        atoms (ase.Atoms)   :   adsorbate molecule
        nb_lst (ase.neighborlist.NeighborList)  : neighborlist

    Returns:
        ase.constraints.Hookean :   Hookean bond contraints of all bonds
                                    in the molecule
    """
    hookean_lst = []
    for neighbors in nb_lst:
        c = Hookean(a1=int(neighbors[0]), a2=int(neighbors[1]), rt=atoms.get_distance(neighbors[0], neighbors[1]), k=20.)
        hookean_lst.append(c)
    return hookean_lst
Esempio n. 9
0
def generate_data(count, filename, temp, hook, cons_t=False):
    """Generates test or training data with a simple MD simulation."""
    traj = ase.io.Trajectory(filename, "w")
    pair = molecule("CO")
    cons = Hookean(a1=0, a2=1, rt=1.58, k=10.0)
    # pair.set_constraint(cons)
    pair.set_calculator(EMT())
    pair.get_potential_energy()
    dyn = QuasiNewton(pair, trajectory=(filename[:-5] + "_relax.traj"))
    dyn.run(fmax=0.05)
    traj.write(pair)
    MaxwellBoltzmannDistribution(pair, temp * units.kB)
    if cons_t is True:
        dyn = Langevin(pair, 5 * units.fs, temp * units.kB, 0.002)
    else:
        dyn = VelocityVerlet(pair, dt=1.0 * units.fs)
    for step in range(count - 1):
        dyn.run(50)
        traj.write(pair)
Esempio n. 10
0
def test_getindices():
    from ase.build import fcc111
    from ase.constraints import (FixAtoms, FixBondLengths, FixLinearTriatomic,
                                 FixInternals, Hookean, constrained_indices)

    slab = fcc111('Pt', (4, 4, 4))

    C1 = FixAtoms([0, 2, 4])
    C2 = FixBondLengths([[0, 1], [0, 2]])
    C3 = FixInternals(bonds=[[1, [7, 8]], [1, [8, 9]]])
    C4 = Hookean(a1=30, a2=40, rt=1.79, k=5.)
    C5 = FixLinearTriatomic(triples=[(0, 1, 2), (3, 4, 5)])

    slab.set_constraint([C1, C2, C3, C4, C5])
    assert all(
        constrained_indices(slab, (FixAtoms, FixBondLengths)) == [0, 1, 2, 4])
    assert all(
        constrained_indices(slab, (FixBondLengths,
                                   FixLinearTriatomic)) == [0, 1, 2, 3, 4, 5])
    assert all(
        constrained_indices(slab) == [0, 1, 2, 3, 4, 5, 7, 8, 9, 30, 40])
Esempio n. 11
0
def test_hookean_pbc():
    from ase import Atoms
    from ase.calculators.emt import EMT
    from ase.constraints import Hookean

    L = 8.  # length of the cubic box
    d = 2.3  # Au-Au distance
    cell = [L] * 3
    positions = [[(L - d/2) % L , L/2, L/2], [(L + d/2) % L, L/2, L/2]]
    a = Atoms('AuAu', cell=cell, positions=positions, pbc=True)

    a.set_calculator(EMT())
    e1 = a.get_potential_energy()

    constraint = Hookean(a1=0, a2=1, rt=1.1*d, k=10.)
    a.set_constraint(constraint)
    e2 = a.get_potential_energy()

    a.set_pbc([False, True, True])
    e3 = a.get_potential_energy()

    assert abs(e1 - e2) < 1e-8
    assert not abs(e1 - e3) < 1e-8
 def _constrain(self):
     """Constrain atoms."""
     self._inorganic_positions = self._atoms.positions[
         self._inorganic_indices]
     constraints = [
         Hookean(a1=self._inorganic_indices[i],
                 a2=self._inorganic_positions[i],
                 rt=0.1,
                 k=15.) for i in range(len(self._inorganic_indices))
     ]
     #        self._Pb_positions = self._atoms.positions[self._Pb_indices]
     #        Pb_z = self._Pb_positions[:,2]
     #        Pb_average_z = np.average(Pb_z)
     #        Pb_upper_indices = self._Pb_indices[np.where(Pb_z > Pb_average_z)]
     #        Pb_bottom_indices = self._Pb_indices[np.where(Pb_z < Pb_average_z)]
     #        constraint_fix = [FixAtoms(indices=self._inorganic_indices)]
     #        constraint_upper_to_fix = [Hookean(a1=Pb_upper_indices[0], a2=int(i), rt=self._atoms.get_distance(Pb_upper_indices[0], i, mic=True) + 0.1, k=5.) for i in Pb_upper_indices[1:]]
     #        constraint_upper = [Hookean(a1=int(i), a2=int(j), rt=self._atoms.get_distance(i, j, mic=True) + 0.1, k=5.) for i in Pb_upper_indices[1:] for j in Pb_upper_indices[1:] if i < j]
     #        constraint_bottom = [Hookean(a1=int(i), a2=int(j), rt=self._atoms.get_distance(i, j, mic=True) + 0.1, k=5.) for i in Pb_bottom_indices for j in Pb_bottom_indices if i < j]
     #        constraints_non_flat = [constrain for constrain in [constraint_fix, constraint_upper_to_fix, constraint_upper, constraint_bottom] if constrain != []]
     #        constraints = [item for sublist in constraints_non_flat for item in sublist]
     #        constraints = [Hookean(a1=self._Pb_indices[i], a2=self._Pb_positions[i], rt=0.01, k=15.) for i in range(len(self._Pb_indices))]
     self._atoms.set_constraint(constraints)
Esempio n. 13
0
def test_hookean():
    """
    Test of Hookean constraint.

    Checks for activity in keeping a bond, preventing vaporization, and
    that energy is conserved in NVE dynamics.
    """

    import numpy as np
    from ase import Atoms, Atom
    from ase.build import fcc110
    from ase.calculators.emt import EMT
    from ase.constraints import FixAtoms, Hookean
    from ase.md import VelocityVerlet
    from ase import units

    class SaveEnergy:
        """Class to save energy."""
        def __init__(self, atoms):
            self.atoms = atoms
            self.energies = []

        def __call__(self):
            self.energies.append(atoms.get_total_energy())

    # Make Pt 110 slab with Cu2 adsorbate.
    atoms = fcc110('Pt', (2, 2, 2), vacuum=7.)
    adsorbate = Atoms([
        Atom('Cu', atoms[7].position + (0., 0., 2.5)),
        Atom('Cu', atoms[7].position + (0., 0., 5.0))
    ])
    atoms.extend(adsorbate)
    calc = EMT()
    atoms.calc = calc

    # Constrain the surface to be fixed and a Hookean constraint between
    # the adsorbate atoms.
    constraints = [
        FixAtoms(indices=[atom.index for atom in atoms
                          if atom.symbol == 'Pt']),
        Hookean(a1=8, a2=9, rt=2.6, k=15.),
        Hookean(a1=8, a2=(0., 0., 1., -15.), k=15.)
    ]
    atoms.set_constraint(constraints)

    # Give it some kinetic energy.
    momenta = atoms.get_momenta()
    momenta[9, 2] += 20.
    momenta[9, 1] += 2.
    atoms.set_momenta(momenta)

    # Propagate in Velocity Verlet (NVE).
    dyn = VelocityVerlet(atoms, timestep=1.0 * units.fs)
    energies = SaveEnergy(atoms)
    dyn.attach(energies)
    dyn.run(steps=100)

    # Test the max bond length and position.
    bondlength = np.linalg.norm(atoms[8].position - atoms[9].position)
    assert bondlength < 3.0
    assert atoms[9].z < 15.0

    # Test that energy was conserved.
    assert max(energies.energies) - min(energies.energies) < 0.01

    # Make sure that index shuffle works.
    neworder = list(range(len(atoms)))
    neworder[8] = 9  # Swap two atoms.
    neworder[9] = 8
    atoms = atoms[neworder]
    assert atoms.constraints[1].indices[0] == 9
    assert atoms.constraints[1].indices[1] == 8
    assert atoms.constraints[2].index == 9
Esempio n. 14
0
def delete_single_atom_with_constraint(atoms=None, indice_to_delete=None):
    if indice_to_delete is None:
        return # nothing to delete
    elif indice_to_delete > len(atoms)-1:
        raise RuntimeError("try too remove too many atoms?")

    # I have to teach each fix objects individually, how can I make it smarter?
    hookean_pairs = []
    fix_atoms_indices = []
    fix_bondlengths_indices = None
    never_delete_indices = []
    fix_lines_indices = []
    for c in atoms.constraints:
        if isinstance(c, FixAtoms):
            fix_atoms_indices.append(c.get_indices())
        elif isinstance(c, Hookean):
            hookean_pairs.append([c.get_indices(), c.threshold, c.spring])
        elif isinstance(c, FixBondLengths):
            if fix_bondlengths_indices is not None:
                raise RuntimeError("Do you have more than one FixBondLengths?")
            fix_bondlengths_indices = c.pairs.copy()
        elif isinstance(c, NeverDelete):
            for ind in c.get_indices():
                never_delete_indices.append(ind)
        elif isinstance(c, FixedLine):
            fix_lines_indices.append([c.a, c.dir])
        else:
            try:
                cn = c.__class__.__name__
            except AttributeError:
                cn = "Unknown"
            raise RuntimeError("constraint type %s is not supported" % cn)

    new_constraints = []
    for old_inds in fix_atoms_indices:
        new_inds = []
        for ind in old_inds:
            if ind < indice_to_delete:
                new_inds.append(ind)
            elif ind > indice_to_delete:
                new_inds.append(ind - 1)
            else:
                continue
        if len(new_inds) > 0:
            new_constraints.append(FixAtoms(indices=new_inds))
    for old_inds, rt, k in hookean_pairs:
        if indice_to_delete in old_inds:
            continue
        new_inds = []
        for ind in old_inds:
            if ind < indice_to_delete:
                new_inds.append(ind)
            else:
                new_inds.append(ind-1)
        new_constraints.append(Hookean(a1=new_inds[0], a2=new_inds[1], rt=rt, k=k))
    for ind, direction in fix_lines_indices:
        if ind == indice_to_delete:
            continue
        elif ind < indice_to_delete:
            new_constraints.append(FixedLine(ind, direction=direction))
        else:
            new_constraints.append(FixedLine(ind-1,direction=direction))

    if fix_bondlengths_indices is not None:
        number_of_cons, ncols = fix_bondlengths_indices.shape
        assert ncols == 2
        new_inds = []
        for ic in range(number_of_cons):
            ind1, ind2 = fix_bondlengths_indices[ic][0], fix_bondlengths_indices[ic][1]
            if ind1 == indice_to_delete or ind2 == indice_to_delete:
                continue
            else:
                pairs = []
                if ind1 < indice_to_delete:
                    pairs.append(ind1)
                else:
                    pairs.append(ind1-1)
                if ind2 < indice_to_delete:
                    pairs.append(ind2)
                else:
                    pairs.append(ind2-1)
                new_inds.append(pairs)
        if len(new_inds) > 0:
            new_constraints.append(FixBondLengths(pairs=new_inds))
    if len(never_delete_indices) > 0:
        new_inds = []
        for ind in never_delete_indices:
            if ind < indice_to_delete:
                new_inds.append(ind)
            elif ind > indice_to_delete:
                new_inds.append(ind - 1)
        if len(new_inds) > 0:
            new_constraints.append(NeverDelete(indices=new_inds))

    # remove all the constraints
    atoms.set_constraint()
    del atoms[indice_to_delete]

    # add back the constraints
    atoms.set_constraint(new_constraints)
    return
Esempio n. 15
0
"""Test that set_momenta behaves as expected when constraints are
involved."""

import numpy as np
from ase import Atoms, Atom
from ase.constraints import Hookean, FixAtoms

# FixAtoms check
atoms = Atoms([Atom('H', (0., 0., 0.)), Atom('H', (2., 0., 0.))])
atoms.set_constraint(FixAtoms(indices=[0]))
atoms.set_momenta(np.ones(atoms.get_momenta().shape))
desired = np.ones(atoms.get_momenta().shape)
desired[0] = 0.
actual = atoms.get_momenta()
assert (actual == desired).all()

# Hookean check
atoms = Atoms([Atom('H', (0., 0., 0.)), Atom('H', (2., 0., 0.))])
atoms.set_constraint(Hookean(0, 1, rt=1., k=10.))
atoms.set_momenta(np.zeros(atoms.get_momenta().shape))
actual = atoms.get_momenta()
desired = np.zeros(atoms.get_momenta().shape)
# Disabled for now:
# assert (actual == desired).all()
Esempio n. 16
0
from ase.optimize.minimahopping import MinimaHopping
from ase.calculators.emt import EMT
from ase.constraints import FixAtoms, Hookean

# Make Pt 111 slab with Cu2 adsorbate.
atoms = fcc111('Pt', (2, 2, 1), vacuum=7., orthogonal=True)
adsorbate = Atoms([
    Atom('Cu', atoms[2].position + (0., 0., 2.5)),
    Atom('Cu', atoms[2].position + (0., 0., 5.0))
])
atoms.extend(adsorbate)

# Constrain the surface to be fixed and a Hookean constraint between
# the adsorbate atoms.
constraints = [
    FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Pt']),
    Hookean(a1=4, a2=5, rt=2.6, k=15.),
    Hookean(a1=4, a2=(0., 0., 1., -15.), k=15.)
]
atoms.set_constraint(constraints)

# Set the calculator.
calc = EMT()
atoms.set_calculator(calc)

# Instantiate and run the minima hopping algorithm.
hop = MinimaHopping(atoms, Ediff0=2.5, T0=2000., beta1=1.2, beta2=1.2, mdmin=1)
hop(totalsteps=3)
# Test ability to restart and temperature stopping.
hop(maxtemp=3000)
Esempio n. 17
0
from ase.calculators.emt import EMT
from ase.constraints import FixAtoms, Hookean
from ase.optimize import BFGS

# Make the Pt 110 slab.
atoms = fcc110('Pt', (2, 2, 2), vacuum=7.)

# Add the Cu2 adsorbate.
adsorbate = Atoms([
    Atom('Cu', atoms[7].position + (0., 0., 2.5)),
    Atom('Cu', atoms[7].position + (0., 0., 5.0))
])
atoms.extend(adsorbate)

# Constrain the surface to be fixed and a Hookean constraint between
# the adsorbate atoms.
constraints = [
    FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Pt']),
    Hookean(a1=8, a2=9, rt=2.6, k=15.),
    Hookean(a1=8, a2=(0., 0., 1., -15.), k=15.),
]
atoms.set_constraint(constraints)

# Set the calculator.
calc = EMT()
atoms.set_calculator(calc)

# Instantiate and run the minima hopping algorithm.
hop = MinimaHopping(atoms, Ediff0=2.5, optimizer=BFGS, T0=4000.)
hop(totalsteps=10)
Esempio n. 18
0
from ase.build import fcc111
from ase.constraints import (FixAtoms, FixBondLengths, FixInternals, Hookean,
                             constrained_indices)

slab = fcc111('Pt', (4, 4, 4))

C1 = FixAtoms([0, 2, 4])
C2 = FixBondLengths([[0, 1], [0, 2]])
C3 = FixInternals(bonds=[[1, [7, 8]], [1, [8, 9]]])
C4 = Hookean(a1=30, a2=40, rt=1.79, k=5.)

slab.set_constraint([C1, C2, C3, C4])
assert all(
    constrained_indices(slab, (FixAtoms, FixBondLengths)) == [0, 1, 2, 4])
assert all(constrained_indices(slab) == [0, 1, 2, 4, 7, 8, 9, 30, 40])
Esempio n. 19
0
from ase import Atoms
from ase.calculators.emt import EMT
from ase.constraints import Hookean

L = 8.  # length of the cubic box
d = 2.3  # Au-Au distance 
cell = [L]*3
positions = [[(L - d/2) % L , L/2, L/2], [(L + d/2) % L, L/2, L/2]]
a = Atoms('AuAu', cell=[L]*3, positions=positions, pbc=True)

a.set_calculator(EMT())
e1 = a.get_potential_energy()

constraint = Hookean(a1=0, a2=1, rt=1.1*d, k=10.)
a.set_constraint(constraint)
e2 = a.get_potential_energy()

a.set_pbc([False, True, True])
e3 = a.get_potential_energy()

assert abs(e1 - e2) < 1e-8
assert not abs(e1 - e3) < 1e-8