Exemple #1
0
    def __init__(self,
                 atoms,
                 timestep,
                 temperature,
                 taut=0.5e3 * units.fs,
                 pressure=1.01325,
                 taup=1e3 * units.fs,
                 compressibility=4.57e-5,
                 fixcm=True,
                 trajectory=None,
                 logfile=None,
                 loginterval=1,
                 append_trajectory=False):

        NVTBerendsen.__init__(self,
                              atoms,
                              timestep,
                              temperature,
                              taut,
                              fixcm,
                              trajectory,
                              logfile,
                              loginterval,
                              append_trajectory=append_trajectory)
        self.taup = taup
        self.pressure = pressure
        self.compressibility = compressibility
Exemple #2
0
    def step(self, f):
        """ move one timestep forward using Berenden NPT molecular dynamics."""

        NVTBerendsen.scale_velocities(self)
        self.scale_positions_and_cell()

        #one step velocity verlet
        atoms = self.atoms
        p = self.atoms.get_momenta()
        p += 0.5 * self.dt * f

        if self.fixcm:
            # calculate the center of mass
            # momentum and subtract it
            psum = p.sum(axis=0) / float(len(p))
            p = p - psum

        self.atoms.set_positions(self.atoms.get_positions() + self.dt * p /
                                 self.atoms.get_masses()[:, np.newaxis])

        # We need to store the momenta on the atoms before calculating
        # the forces, as in a parallel Asap calculation atoms may
        # migrate during force calculations, and the momenta need to
        # migrate along with the atoms.  For the same reason, we
        # cannot use self.masses in the line above.

        self.atoms.set_momenta(p)
        f = self.atoms.get_forces()
        atoms.set_momenta(self.atoms.get_momenta() + 0.5 * self.dt * f)

        return f
    def step(self, f):
        """ move one timestep forward using Berenden NPT molecular dynamics."""

        NVTBerendsen.scale_velocities(self)
        self.scale_positions_and_cell()

        #one step velocity verlet
        atoms = self.atoms
        p = self.atoms.get_momenta()
        p += 0.5 * self.dt * f

        if self.fixcm:
            # calculate the center of mass
            # momentum and subtract it
            psum = p.sum(axis=0) / float(len(p))
            p = p - psum

        self.atoms.set_positions(
            self.atoms.get_positions() +
            self.dt * p / self.atoms.get_masses()[:, np.newaxis])

        # We need to store the momenta on the atoms before calculating
        # the forces, as in a parallel Asap calculation atoms may
        # migrate during force calculations, and the momenta need to
        # migrate along with the atoms.  For the same reason, we
        # cannot use self.masses in the line above.

        self.atoms.set_momenta(p)
        f = self.atoms.get_forces()
        atoms.set_momenta(self.atoms.get_momenta() + 0.5 * self.dt * f)

        return f
Exemple #4
0
    def __init__(self, atoms, timestep, temperature, taut, fixcm=True,
                 trajectory=None, **kwargs):

        NVTBerendsen.__init__(self, atoms, timestep, temperature, taut, 
                              fixcm, trajectory)
 
        OTF.__init__(self, **kwargs)

        self.md_engine = 'NVTBerendsen'
    def __init__(self, atoms, timestep, temperature, taut=0.5e3 *
                 units.fs, pressure=1.01325, taup=1e3 * units.fs,
                 compressibility=4.57e-5, fixcm=True, trajectory=None,
                 logfile=None, loginterval=1):

        NVTBerendsen.__init__(self, atoms, timestep, temperature,
                              taut, fixcm, trajectory, logfile,
                              loginterval)
        self.taup = taup
        self.pressure = pressure
        self.compressibility = compressibility
Exemple #6
0
def generate_data(count, filename='cmd.traj'):
    """Generates test or training data with a simple MD simulation."""
    if os.path.exists(filename):
        return
    traj = io.Trajectory(filename, 'w')
    atoms = io.read('2.xyz')
    atoms.set_calculator(Amp.load('sio2.amp'))
    atoms.get_potential_energy()
    traj.write(atoms)
    mb(atoms, 300. * units.kB)
    #    dyn = vv(atoms, dt=1. * units.fs, logfile='cmd.log')
    dyn = NVTB(atoms,
               timestep=0.5 * units.fs,
               temperature=300,
               taut=2.0 * units.fs,
               logfile='cmd.log')
    for step in range(count - 1):
        dyn.run(1)
        traj.write(atoms)
Exemple #7
0
 def set_initial_velocities(self, distribution, energy):
     distribution(self.atoms, energy)
     try:
         os.remove(self.traj)
     except:
         pass
     try:
         os.remove(self._log)
     except:
         pass
     NVTBerendsen.__init__(self,
                           atoms=self.atoms,
                           timestep=self.dt,
                           trajectory=self.traj,
                           loginterval=1,
                           logfile=self._log,
                           temperature=self.target_temperature,
                           taut=self.tau,
                           append_trajectory=False)
     self.update_properties()
Exemple #8
0
def serve_md(nuclear_charges, coordinates, calculator=None, temp=None):
    """
    """

    if calculator is None:
        parameters = {}
        parameters["offset"] = -97084.83100465109
        parameters["sigma"] = 10.0
        alphas = np.load(FILENAME_ALPHAS)
        X = np.load(FILENAME_REPRESENTATIONS)
        Q = np.load(FILENAME_CHARGES, allow_pickle=True)
        alphas = np.array(alphas, order="F")
        X = np.array(X, order="F")
        calculator = QMLCalculator(parameters, X, Q, alphas)


    # SET MOLECULE
    molecule = ase.Atoms(nuclear_charges, coordinates)
    molecule.set_calculator(calculator)

    # SET ASE MD
    # Set the momenta corresponding to T=300K
    MaxwellBoltzmannDistribution(molecule, 200 * units.kB)

    time = 0.5

    if temp is None:
        # We want to run MD with constant energy using the VelocityVerlet algorithm.
        dyn = VelocityVerlet(molecule, time * units.fs)  # 1 fs time step.

    else:
        dyn = NVTBerendsen(molecule, time*units.fs, temp, time*units.fs, fixcm=False)

    # SET AND SERVE NARUPA MD
    imd = ASEImdServer(dyn)

    while True:
        imd.run(10)
        print("")
        dump_xyz(molecule, tmpdir + "snapshot.xyz")

    return
def md_run(
    parent_dataset,
    dft_calculator,
    starting_image,
    temp,
    dt,
    count,
    label,
    ensemble="NVE",
):
    slab = starting_image.copy()
    slab.set_calculator(AMPOnlineCalc(parent_dataset, dft_calculator, 3))
    # MaxwellBoltzmannDistribution(slab, temp * units.kB)
    if ensemble == "nvtberendsen":
        dyn = NVTBerendsen(slab,
                           dt * ase.units.fs,
                           temp,
                           taut=300 * ase.units.fs)
    traj = ase.io.Trajectory(label + ".traj", "w", slab)
    dyn.attach(traj.write, interval=1)
    dyn.run(count - 1)
Exemple #10
0
# Parameters for the Morse potential for Cu
D = 0.3429
r0 = 2.866
alpha = 1.3588

atoms = read('hexagonal_lattice.xyz')

atoms.set_calculator(MorsePotential(D=D, alpha=alpha, r0=r0))

vel = np.zeros((len(atoms), 3))

# initialize velocities with uniform random numbers between [-0.005, 0.005]
# gives random initial velocity

vel[:, 0] = (np.random.rand(len(atoms)) - 0.5) * 1e-2
vel[:, 1] = (np.random.rand(len(atoms)) - 0.5) * 1e-2
atoms.set_velocities(vel)

open('berendsen.log', 'w').close()
dyn = NVTBerendsen(atoms,
                   timestep=1 * units.fs,
                   temperature=600,
                   taut=100 * units.fs,
                   logfile='berendsen.log',
                   trajectory='berendsen.traj')
# Note that the temperature in NVTBerendsen is given in K
# taut = relaxtation time
dyn.run(1000)
write('berendsen.xyz', read('berendsen.traj@:'))
Exemple #11
0
from ase.units import fs
from run_opt import read_incar, write_contcar, get_ele_dict
import os, sys

calc = GAP(rcut=6.0)

nsw, pstress, tebeg, potim, _ = read_incar()
my_atoms = read('POSCAR', format='vasp')
my_atoms.set_calculator(calc)
"""
for NVT 
"""
#print(tebeg,'tebeg')
dyn = NVTBerendsen(my_atoms,
                   timestep=potim * fs,
                   temperature=tebeg,
                   taut=0.5 * 1000 * fs,
                   trajectory='ase.traj')
dyn.attach(MDLogger(dyn,
                    my_atoms,
                    'OUTCAR',
                    header=True,
                    stress=True,
                    pstress=pstress,
                    peratom=False,
                    mode="w"),
           interval=1)
dyn.run(steps=nsw)

atoms_lat = my_atoms.cell
atoms_pos = my_atoms.positions
Exemple #12
0
    def __init__(self, atoms, timestep, temperature=None,
                 *, temperature_K=None, pressure=None, pressure_au=None,
                 taut=0.5e3 * units.fs, taup=1e3 * units.fs,
                 compressibility=None, compressibility_au=None, fixcm=True,
                 trajectory=None,
                 logfile=None, loginterval=1, append_trajectory=False):
        """Berendsen (constant N, P, T) molecular dynamics.

        This dynamics scale the velocities and volumes to maintain a constant
        pressure and temperature.  The shape of the simulation cell is not
        altered, if that is desired use Inhomogenous_NPTBerendsen.

        Parameters:

        atoms: Atoms object
            The list of atoms.

        timestep: float
            The time step in ASE time units.

        temperature: float
            The desired temperature, in Kelvin.

        temperature_K: float
            Alias for ``temperature``.

        pressure: float (deprecated)
            The desired pressure, in bar (1 bar = 1e5 Pa).  Deprecated,
            use ``pressure_au`` instead.

        pressure: float
            The desired pressure, in atomic units (eV/Å^3).

        taut: float
            Time constant for Berendsen temperature coupling in ASE
            time units.  Default: 0.5 ps.

        taup: float
            Time constant for Berendsen pressure coupling.  Default: 1 ps.

        compressibility: float (deprecated)
            The compressibility of the material, in bar-1.  Deprecated,
            use ``compressibility_au`` instead.

        compressibility_au: float
            The compressibility of the material, in atomic units (Å^3/eV).

        fixcm: bool (optional)
            If True, the position and momentum of the center of mass is
            kept unperturbed.  Default: True.

        trajectory: Trajectory object or str (optional)
            Attach trajectory object.  If *trajectory* is a string a
            Trajectory will be constructed.  Use *None* for no
            trajectory.

        logfile: file object or str (optional)
            If *logfile* is a string, a file with that name will be opened.
            Use '-' for stdout.

        loginterval: int (optional)
            Only write a log line for every *loginterval* time steps.  
            Default: 1

        append_trajectory: boolean (optional)
            Defaults to False, which causes the trajectory file to be
            overwriten each time the dynamics is restarted from scratch.
            If True, the new structures are appended to the trajectory
            file instead.


        """

        NVTBerendsen.__init__(self, atoms, timestep, temperature=temperature,
                              temperature_K=temperature_K,
                              taut=taut, fixcm=fixcm, trajectory=trajectory,
                              logfile=logfile, loginterval=loginterval,
                              append_trajectory=append_trajectory)
        self.taup = taup
        self.pressure = self._process_pressure(pressure, pressure_au)
        if compressibility is not None and compressibility_au is not None:
            raise TypeError(
                "Do not give both 'compressibility' and 'compressibility_au'")
        if compressibility is not None:
            # Specified in bar, convert to atomic units
            warnings.warn(FutureWarning(
                "Specify the compressibility in atomic units."))
            self.set_compressibility(
                compressibility_au=compressibility / (1e5 * units.Pascal))
        else:
            self.set_compressibility(compressibility_au=compressibility_au)
Exemple #13
0
                if atom.position[i] > cell[i,i]-self.margin:
                    atom.momentum[i] = -abs(atom.momentum[i])

if __name__ == '__main__':
    # test
    from ase.cluster.cubic import FaceCenteredCubic
    from ase.calculators.emt import EMT
    from ase.md.nvtberendsen import NVTBerendsen
    from ase.io.trajectory import Trajectory
    from ase.md.velocitydistribution import MaxwellBoltzmannDistribution, Stationary, ZeroRotation
    from ase.units import fs, kB

    atoms = FaceCenteredCubic(
      'Pt', [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [2, 3, 1], 4.09)
    atoms.center(vacuum=5)
    atoms.set_calculator( EMT() )
    T = 8000    # K -- try to vaporize
    MaxwellBoltzmannDistribution(atoms, kB*T)
    Stationary(atoms)
    ZeroRotation(atoms)
    #dyn = NVTBerendsen(atoms, 1*fs, T, 500*fs, logfile='-')
    dyn = NVTBerendsen(atoms, 1*fs, T, 500*fs, logfile='-')
    traj = Trajectory('borders_test.traj', 'w', atoms)
    dyn.attach(traj.write, interval=10)
    fr =  Freeze(atoms)
    #fr =  Mirror(atoms)
    dyn.attach( fr, interval=20 )
    dyn.run(5000)
    traj.close()

Exemple #14
0
def ribs(params, voidcount, voidrad, frame_count=1000):

    calc = IdealBrittleSolid(rc=params.rc,
                             k=params.k,
                             a=params.a,
                             beta=params.beta)

    x_dimer = np.linspace(params.a - (params.rc - params.a),
                          params.a + 1.1 * (params.rc - params.a), 51)
    dimers = [
        Atoms('Si2', [(0, 0, 0), (x, 0, 0)], cell=[10., 10., 10.], pbc=True)
        for x in x_dimer
    ]
    calc.set_reference_crystal(dimers[0])
    e_dimer = []
    f_dimer = []
    f_num = []
    for d in dimers:
        d.set_calculator(calc)
        e_dimer.append(d.get_potential_energy())
        f_dimer.append(d.get_forces())
        f_num.append(calc.calculate_numerical_forces(d))
    e_dimer = np.array(e_dimer)
    f_dimer = np.array(f_dimer)
    f_num = np.array(f_num)
    assert abs(f_dimer - f_num).max() < 0.1

    #! crystal is created here, the length and height can be modified here as well
    #! edit 3N changed to different values to test
    crystal = triangular_lattice_slab(params.a, params.lm * params.N, params.N)
    calc.set_reference_crystal(crystal)
    crystal.set_calculator(calc)

    e0 = crystal.get_potential_energy()
    l = crystal.cell[0, 0]
    h = crystal.cell[1, 1]
    print('l=', l, 'h=', h)

    # compute surface (Griffith) energy
    b = crystal.copy()
    b.set_calculator(calc)
    shift = calc.parameters['rc'] * 2
    y = crystal.positions[:, 1]
    b.positions[y > h / 2, 1] += shift
    b.cell[1, 1] += shift
    e1 = b.get_potential_energy()
    E_G = (e1 - e0) / l
    print('Griffith energy', E_G)

    # compute Griffith strain
    eps = 0.0  # initial strain is zero
    eps_max = 2 / np.sqrt(3) * (params.rc - params.a) * np.sqrt(
        params.N - 1) / h  # Griffith strain assuming harmonic energy
    deps = eps_max / 100.  # strain increment
    e_over_l = 0.0  # initial energy per unit length is zero
    energy = []
    strain = []
    while e_over_l < E_G:
        c = crystal.copy()
        c.set_calculator(calc)
        c.positions[:, 1] *= (1.0 + eps)
        c.cell[1, 1] *= (1.0 + eps)
        e_over_l = c.get_potential_energy() / l
        energy.append(e_over_l)
        strain.append(eps)
        eps += deps

    energy = np.array(energy)
    eps_of_e = interp1d(energy, strain, kind='linear')
    eps_G = eps_of_e(E_G)

    print('Griffith strain', eps_G)

    c = crystal.copy()
    c.info['E_G'] = E_G
    c.info['eps_G'] = eps_G

    # open up the cell along x and y by introducing some vaccum
    orig_cell_width = c.cell[0, 0]
    orig_cell_height = c.cell[1, 1]
    c.center(params.vacuum, axis=0)
    c.center(params.vacuum, axis=1)

    # centre the slab on the origin
    c.positions[:, 0] -= c.positions[:, 0].mean()
    c.positions[:, 1] -= c.positions[:, 1].mean()

    c.info['cell_origin'] = [-c.cell[0, 0] / 2, -c.cell[1, 1] / 2, 0.0]
    ase.io.write('crack_1.xyz', c, format='extxyz')

    width = (c.positions[:, 0].max() - c.positions[:, 0].min())
    height = (c.positions[:, 1].max() - c.positions[:, 1].min())

    c.info['OrigHeight'] = height

    print((
        'Made slab with %d atoms, original width and height: %.1f x %.1f A^2' %
        (len(c), width, height)))

    top = c.positions[:, 1].max()
    bottom = c.positions[:, 1].min()
    left = c.positions[:, 0].min()
    right = c.positions[:, 0].max()

    crack_seed_length = 0.2 * width
    strain_ramp_length = 8.0 * params.a  # make this bigger until crack looks nicer
    delta_strain = params.strain_rate * params.dt

    # fix top and bottom rows, and setup Stokes damping mask
    # initial use constant strain
    set_constraints(c, params.a)

    # apply initial displacment field
    c.positions[:, 1] += thin_strip_displacement_y(
        c.positions[:, 0], c.positions[:, 1], params.delta * eps_G,
        left + crack_seed_length,
        left + crack_seed_length + strain_ramp_length)

    print('Applied initial load: delta=%.2f strain=%.4f' %
          (params.delta, params.delta * eps_G))

    ase.io.write('crack_2.xyz', c, format='extxyz')

    c.set_calculator(calc)

    cl, cs, cr = calc.get_wave_speeds(c)

    print("rayleigh speed = %f" % cr)

    # relax initial structure
    # opt = FIRE(c)
    # opt.run(fmax=1e-3)

    ase.io.write('crack_3.xyz', c, format='extxyz')

    #length and height of the slab is defined here
    L = params.N * params.lm
    H = params.N * 2
    #Atomic Void Simulations
    #😵------------------------------------------------------------------------------
    #the following lines of code were written to convert 1D positions into a 2D array
    #so as to make manipulation of slab easier
    if True:
        #void parameters are defined here.
        #around a max of [0.3--1.7] recommended
        y_offset = 0.1
        #y offset is a fraction of distance from the end of the slab
        x_offset = 0.4
        rad = voidrad

        #this reference code is for 160x40 slab
        #in steps of 40 i.e. the height, create a list upto the length of slab
        #row0 = range(0,6400,40)
        row0 = range(0, len(c), H)
        #the 2D array will be held in slab
        slab = []
        for col in range(H):
            row = []
            for r in row0:
                i = col + r
                row.append(c.positions[i])
            slab.append(row)

        slab = np.array(slab)
        #all items in the array reversed, needed because the salb is built from bottom left up
        #in other words, reflected in x axis
        slab = slab[::-1]
        # print(slab[0])

        # slab[modifiers.mask(h=H,w=L, center=[int(L*x_offset),int((H - 1)*y_offset)], radius=rad)] = 0

        #multiple voids if need be
        if True:
            for i in range(voidcount):
                y_offset = rand.uniform(0.1, 0.9)
                x_offset = rand.uniform(0.3, 0.95)
                slab[modifiers.mask(
                    h=H,
                    w=L,
                    center=[int(L * x_offset),
                            int((H - 1) * y_offset)],
                    radius=rad)] = 0
        #reversed the slab back again here
        slab = slab[::-1]
        # # this is a useful text-array representation of the slab, for debugging purposes
        # mtext = open('masktest.txt','w')
        # for row in slab:
        #        mtext.write(str(row).replace("\n",",")+"\n")
        # mtext.close

        slab_1d = []
        for col in range(L):
            for row in range(H):
                slab_1d.append(slab[row, col])
        slab_1d = np.array(slab_1d)

        todel = []
        for i in range(len(c)):
            if slab_1d[i][2] == 0:
                todel.append(i)
        print(todel)
        del c[todel]
        # return
    #End of Void Simulation
    #-------------------------------------------------------------------------------

    #Grain Boundary Simulations
    #-------------------------------------------------------------------------------
    if False:
        hi = 2

    #-------------------------------------------------------------------------------

    #! replaced velcityVerlet with Lagevin to add temperature parameter
    if params.v_verlet:
        dyn = VelocityVerlet(c, params.dt * units.fs, logfile=None)
        # set_initial_velocities(dyn.atoms)
    else:
        print("Using NVT!")
        # mbd(c, 20 * units.kB, force_temp = True)
        # dyn = Langevin(c,params.dt*units.fs,params.T*units.kB, 5)
        dyn = NVTBerendsen(c,
                           params.dt * units.fs,
                           params.T,
                           taut=0.5 * 1000 * units.fs)

    #dyn.atoms.rattle(1e-3) # non-deterministic simulations - adjust to suit

    #!simulation outputs numbered, avoids deleting exisiting results
    iterFile = open("simIteration.txt", 'r+')
    iteration = int(iterFile.readlines()[0]) + 1
    if params.overwrite_output:
        iteration -= 1
    iterFile.seek(0, 0)
    iterFile.write(str(iteration))
    iterFile.close()

    dir = "./.simout/void/sim_" + str(iteration) + "_" + str(
        params.keep_test).lower() + "_" + params.desc + "/"
    cf.createFolder(dir)

    #!Saving parameter values for each iteration of the simulation
    logFile = open(dir + "params_log.txt", 'w')
    for p, value in params.compose_params().iteritems():
        logFile.write(p + " ==> " + str(value) + "\n")
    logFile.close
    crack_pos = []
    if params.keep_test:
        traj = NetCDFTrajectory(dir + 'traj' + str(iteration) + '.nc', 'w', c)
        dyn.attach(traj.write, 10, dyn.atoms, arrays=['stokes', 'momenta'])

    # #! isolating crack tip_x for saving
    # crack_tip_file2 = open(dir+'tip_x.txt','w')
    # crack_tip_file2.close()
    tip_x_file = open(dir + 'tip_x.txt', 'a')
    console_output = open(dir + 'console_output.txt', 'a')
    coord_file = open(dir + 'coordinates.csv', 'a')

    coordinates = []
    distances = []

    dyn.attach(find_crack_tip,
               10,
               dyn.atoms,
               tipxfile=tip_x_file,
               cout=console_output,
               coord=coordinates,
               d=distances,
               dt=params.dt * 10,
               store=True,
               results=crack_pos)

    # run for 2000 time steps to reach steady state at initial load
    # for i in range(10):
    #     dyn.run(250)
    #     if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum):
    #         set_constraints(dyn.atoms, params.a)

    # start decreasing strain
    #set_constraints(dyn.atoms, params.a, delta_strain=delta_strain)

    # strain_atoms = ConstantStrainRate(dyn.atoms.info['OrigHeight'],
    #                                   delta_strain)
    # dyn.attach(strain_atoms.apply_strain, 1, dyn. atoms   )

    # for i in range(50):
    #     dyn.run(100)
    #     if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum):
    #         set_constraints(dyn.atoms, params.a)

    # #cleardel dyn.observers[-1] # stop increasing the strain

    # for i in range(1000):
    #     dyn.run(100)
    #     if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum):
    #         set_constraints(dyn.atoms, params.a)

    dyn.run(int(1 * frame_count) * 10 + 10)

    # print("\n\n\n\n\n -----Adding Temperature------\n\n\n\n\n")
    # # mbd(c, 2*params.T * units.kB, force_temp = True)
    # dyn.set_temperature(params.T*units.kB)
    # dyn.run(int(0.5*frame_count)*10+10)
    for c in coordinates:
        coord_file.write(str(c[0]) + ',' + str(c[1]) + '\n')
    coord_file.close()

    if params.keep_test:
        traj.close()
    tip_x_file.close()
    console_output.close()
Exemple #15
0
    si.set_calculator(calc)

    # geometry optimization
    si = optimize(si, box=True)
    print('equlirum cell para: ', si.get_cell()[0][0])
    print('equlirum energy: ', si.get_potential_energy())
    print('equlirum stress', -si.get_stress() / units.GPa)

    # Build the 2*2*2 supercell
    atoms = si * 2
    atoms.set_calculator(calc)
    print(atoms)

    # NVT MD simulation
    dyn = NVTBerendsen(atoms,
                       p.time_step * units.fs,
                       p.temperature,
                       taut=p.taut * units.fs)
    MaxwellBoltzmannDistribution(atoms, p.temperature * units.kB)
    dyn.attach(MDLogger(dyn,
                        atoms,
                        'md.log',
                        header=True,
                        stress=False,
                        mode="w"),
               interval=p.save_interval)
    traj = Trajectory('md.traj', 'w', atoms)
    dyn.attach(traj.write, interval=p.save_interval)

    dyn.run(p.run_step)