def canonical(atoms,dt=1.0,steps=1000,output=10,name=None,verbose=False): """ Perform little canonical simulation. parameters: ----------- atoms: atoms with calculator attached dt: time step in fs steps: how many md steps output: output frequency name: TrajectoryRecording name verbose: increase verbosity """ if name==None: try: name=atoms.get_chemical_formula(mode="hill") except: name='microcanonical' name+='.trj' traj=PickleTrajectory(name,'w',atoms) rec=TrajectoryRecording(atoms,verbose) md=VelocityVerlet(atoms,dt*fs) md.attach(rec,interval=output) md.attach(traj.write,interval=output) md.run(steps) return rec
def microcanonical(atoms, dt=1.0, steps=100, output=1, name=None, verbose=False): """ Perform little microcanonical simulation. parameters: ----------- atoms: dt: time step in fs steps: how many md steps output: output frequency name: TrajectoryRecording name verbose: increase verbosity Return TrajectoryRecording object for further analysis. """ if name == None: try: name = atoms.get_chemical_formula(mode="hill") except: name = 'microcanonical' name += '.trj' traj = Trajectory(name, 'w', atoms) rec = TrajectoryRecording(atoms, verbose) md = VelocityVerlet(atoms, dt * fs) md.attach(rec, interval=output) md.attach(traj.write, interval=output) md.run(steps) return rec
def _molecular_dynamics(self, step, N): """Performs a molecular dynamics simulation, until mdmin is exceeded. If resuming, the file number (md%05i) is expected.""" mincount = 0 energies, oldpositions = [], [] thermalized = False if not thermalized: self.MaxwellBoltzmannDistribution(N, temp=self.temperature * kB, force_temp=True) traj = io.Trajectory('md.traj', 'a', self.atoms) dyn = VelocityVerlet(self.atoms, dt=self.timestep * units.fs) log = MDLogger(dyn, self.atoms, 'md.log', header=True, stress=False, peratom=False) dyn.attach(log, interval=1) dyn.attach(traj, interval=1) os.remove('md.log') os.remove('md.traj') while mincount < self.mdmin: dyn.run(1) energies.append(self.atoms.get_potential_energy()) passedmin = self.passedminimum(energies) if passedmin: mincount += 1 oldpositions.append(self.atoms.positions.copy()) # Reset atoms to minimum point. self.atoms.positions = oldpositions[passedmin[0]]
def run(self, calc, filename): slab = self.starting_geometry.copy() slab.set_calculator(calc) np.random.seed(1) MaxwellBoltzmannDistribution(slab, self.temp * units.kB) if self.ensemble == "NVE": dyn = VelocityVerlet(slab, self.dt * units.fs) elif self.ensemble == "nvtberendsen": dyn = nvtberendsen.NVTBerendsen(slab, self.dt * units.fs, self.temp, taut=300 * units.fs) elif self.ensemble == "langevin": dyn = Langevin(slab, self.dt * units.fs, self.temp * units.kB, 0.002) traj = ase.io.Trajectory(filename + ".traj", "w", slab) dyn.attach(traj.write, interval=1) try: fixed_atoms = len(slab.constraints[0].get_indices()) except: fixed_atoms = 0 pass def printenergy(a=slab): """Function to print( the potential, kinetic, and total energy)""" epot = a.get_potential_energy() / len(a) ekin = a.get_kinetic_energy() / (len(a) - fixed_atoms) print("Energy per atom: Epot = %.3feV Ekin = %.3feV (T=%3.0fK) " "Etot = %.3feV" % (epot, ekin, ekin / (1.5 * units.kB), epot + ekin)) if printenergy: dyn.attach(printenergy, interval=10) dyn.run(self.count)
def _molecular_dynamics(self, resume=None): """Performs a molecular dynamics simulation, until mdmin is exceeded. If resuming, the file number (md%05i) is expected.""" self._log('msg', 'Molecular dynamics: md%05i' % self._counter) mincount = 0 energies, oldpositions = [], [] thermalized = False if resume: self._log('msg', 'Resuming MD from md%05i.traj' % resume) if os.path.getsize('md%05i.traj' % resume) == 0: self._log( 'msg', 'md%05i.traj is empty. Resuming from ' 'qn%05i.traj.' % (resume, resume - 1)) atoms = io.read('qn%05i.traj' % (resume - 1), index=-1) else: with io.Trajectory('md%05i.traj' % resume, 'r') as images: for atoms in images: energies.append(atoms.get_potential_energy()) oldpositions.append(atoms.positions.copy()) passedmin = self._passedminimum(energies) if passedmin: mincount += 1 self._atoms.set_momenta(atoms.get_momenta()) thermalized = True self._atoms.positions = atoms.get_positions() self._log('msg', 'Starting MD with %i existing energies.' % len(energies)) if not thermalized: MaxwellBoltzmannDistribution(self._atoms, temperature_K=self._temperature, force_temp=True) traj = io.Trajectory('md%05i.traj' % self._counter, 'a', self._atoms) dyn = VelocityVerlet(self._atoms, timestep=self._timestep * units.fs) log = MDLogger(dyn, self._atoms, 'md%05i.log' % self._counter, header=True, stress=False, peratom=False) with traj, dyn, log: dyn.attach(log, interval=1) dyn.attach(traj, interval=1) while mincount < self._mdmin: dyn.run(1) energies.append(self._atoms.get_potential_energy()) passedmin = self._passedminimum(energies) if passedmin: mincount += 1 oldpositions.append(self._atoms.positions.copy()) # Reset atoms to minimum point. self._atoms.positions = oldpositions[passedmin[0]]
def test(): # Generate atomic system to create test data. atoms = fcc110('Cu', (2, 2, 2), vacuum=7.) adsorbate = Atoms([ Atom('H', atoms[7].position + (0., 0., 2.)), Atom('H', atoms[7].position + (0., 0., 5.)) ]) atoms.extend(adsorbate) atoms.set_constraint(FixAtoms(indices=[0, 2])) calc = EMT() # cheap calculator atoms.set_calculator(calc) # Run some molecular dynamics to generate data. trajectory = io.Trajectory('data.traj', 'w', atoms=atoms) MaxwellBoltzmannDistribution(atoms, temp=300. * units.kB) dynamics = VelocityVerlet(atoms, dt=1. * units.fs) dynamics.attach(trajectory) for step in range(50): dynamics.run(5) trajectory.close() # Train the calculator. train_images, test_images = randomize_images('data.traj') calc = Amp(descriptor=Behler(), regression=NeuralNetwork()) calc.train(train_images, energy_goal=0.001, force_goal=None) # Plot and test the predictions. import matplotlib matplotlib.use('Agg') from matplotlib import pyplot fig, ax = pyplot.subplots() for image in train_images: actual_energy = image.get_potential_energy() predicted_energy = calc.get_potential_energy(image) ax.plot(actual_energy, predicted_energy, 'b.') for image in test_images: actual_energy = image.get_potential_energy() predicted_energy = calc.get_potential_energy(image) ax.plot(actual_energy, predicted_energy, 'r.') ax.set_xlabel('Actual energy, eV') ax.set_ylabel('Amp energy, eV') fig.savefig('parityplot.png')
def test(): # Generate atomic system to create test data. atoms = fcc110('Cu', (2, 2, 2), vacuum=7.) adsorbate = Atoms([Atom('H', atoms[7].position + (0., 0., 2.)), Atom('H', atoms[7].position + (0., 0., 5.))]) atoms.extend(adsorbate) atoms.set_constraint(FixAtoms(indices=[0, 2])) calc = EMT() # cheap calculator atoms.set_calculator(calc) # Run some molecular dynamics to generate data. trajectory = io.Trajectory('data.traj', 'w', atoms=atoms) MaxwellBoltzmannDistribution(atoms, temp=300. * units.kB) dynamics = VelocityVerlet(atoms, dt=1. * units.fs) dynamics.attach(trajectory) for step in range(50): dynamics.run(5) trajectory.close() # Train the calculator. train_images, test_images = randomize_images('data.traj') calc = Amp(descriptor=Behler(), regression=NeuralNetwork()) calc.train(train_images, energy_goal=0.001, force_goal=None) # Plot and test the predictions. import matplotlib matplotlib.use('Agg') from matplotlib import pyplot fig, ax = pyplot.subplots() for image in train_images: actual_energy = image.get_potential_energy() predicted_energy = calc.get_potential_energy(image) ax.plot(actual_energy, predicted_energy, 'b.') for image in test_images: actual_energy = image.get_potential_energy() predicted_energy = calc.get_potential_energy(image) ax.plot(actual_energy, predicted_energy, 'r.') ax.set_xlabel('Actual energy, eV') ax.set_ylabel('Amp energy, eV') fig.savefig('parityplot.png')
def _molecular_dynamics(self, resume=None): """Performs a molecular dynamics simulation, until mdmin is exceeded. If resuming, the file number (md%05i) is expected.""" self._log('msg', 'Molecular dynamics: md%05i' % self._counter) mincount = 0 energies, oldpositions = [], [] thermalized = False if resume: self._log('msg', 'Resuming MD from md%05i.traj' % resume) if os.path.getsize('md%05i.traj' % resume) == 0: self._log('msg', 'md%05i.traj is empty. Resuming from ' 'qn%05i.traj.' % (resume, resume - 1)) atoms = io.read('qn%05i.traj' % (resume - 1), index=-1) else: images = io.PickleTrajectory('md%05i.traj' % resume, 'r') for atoms in images: energies.append(atoms.get_potential_energy()) oldpositions.append(atoms.positions.copy()) passedmin = self._passedminimum(energies) if passedmin: mincount += 1 self._atoms.set_momenta(atoms.get_momenta()) thermalized = True self._atoms.positions = atoms.get_positions() self._log('msg', 'Starting MD with %i existing energies.' % len(energies)) if not thermalized: MaxwellBoltzmannDistribution(self._atoms, temp=self._temperature * units.kB, force_temp=True) traj = io.PickleTrajectory('md%05i.traj' % self._counter, 'a', self._atoms) dyn = VelocityVerlet(self._atoms, dt=self._timestep * units.fs) log = MDLogger(dyn, self._atoms, 'md%05i.log' % self._counter, header=True, stress=False, peratom=False) dyn.attach(log, interval=1) dyn.attach(traj, interval=1) while mincount < self._mdmin: dyn.run(1) energies.append(self._atoms.get_potential_energy()) passedmin = self._passedminimum(energies) if passedmin: mincount += 1 oldpositions.append(self._atoms.positions.copy()) # Reset atoms to minimum point. self._atoms.positions = oldpositions[passedmin[0]]
def test_verlet(): with seterr(all='raise'): a = Atoms('4X', masses=[1, 2, 3, 4], positions=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.1, 0.2, 0.7)], calculator=TstPotential()) print(a.get_forces()) md = VelocityVerlet(a, timestep=0.5 * fs, logfile='-', loginterval=500) traj = Trajectory('4N.traj', 'w', a) md.attach(traj.write, 100) e0 = a.get_total_energy() md.run(steps=10000) del traj assert abs(read('4N.traj').get_total_energy() - e0) < 0.0001 qn = QuasiNewton(a) qn.run(0.001) assert abs(a.get_potential_energy() - 1.0) < 0.000002
def test_verlet_asap(asap3): with seterr(all='raise'): a = bulk('Au').repeat((2, 2, 2)) a[5].symbol = 'Ag' a.pbc = (True, True, False) print(a) a.calc = asap3.EMT() MaxwellBoltzmannDistribution(a, 300 * kB, force_temp=True) Stationary(a) assert abs(a.get_temperature() - 300) < 0.0001 print(a.get_forces()) md = VelocityVerlet(a, timestep=2 * fs, logfile='-', loginterval=500) traj = Trajectory('Au7Ag.traj', 'w', a) md.attach(traj.write, 100) e0 = a.get_total_energy() md.run(steps=10000) del traj assert abs(a.get_total_energy() - e0) < 0.0001 assert abs(read('Au7Ag.traj').get_total_energy() - e0) < 0.0001
def test_md(): from ase import Atoms from ase.calculators.emt import EMT from ase.md import VelocityVerlet from ase.io import Trajectory a = 3.6 b = a / 2 fcc = Atoms('Cu', positions=[(0, 0, 0)], cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=1) fcc *= (2, 1, 1) fcc.set_calculator(EMT()) fcc.set_momenta([(0.9, 0.0, 0.0), (-0.9, 0, 0)]) md = VelocityVerlet(fcc, timestep=0.1) def f(): print(fcc.get_potential_energy(), fcc.get_total_energy()) md.attach(f) md.attach(Trajectory('Cu2.traj', 'w', fcc).write, interval=3) md.run(steps=20) Trajectory('Cu2.traj', 'r')[-1]
from ase import Atoms from ase.units import fs from ase.calculators.test import TestPotential from ase.md import VelocityVerlet from ase.io import Trajectory, read from ase.optimize import QuasiNewton from ase.utils import seterr with seterr(all='raise'): a = Atoms('4X', masses=[1, 2, 3, 4], positions=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.1, 0.2, 0.7)], calculator=TestPotential()) print(a.get_forces()) md = VelocityVerlet(a, timestep=0.5 * fs, logfile='-', loginterval=500) traj = Trajectory('4N.traj', 'w', a) md.attach(traj.write, 100) e0 = a.get_total_energy() md.run(steps=10000) del traj assert abs(read('4N.traj').get_total_energy() - e0) < 0.0001 qn = QuasiNewton(a) qn.run(0.001) assert abs(a.get_potential_energy() - 1.0) < 0.000002
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
from ase import Atoms from ase.calculators.emt import EMT from ase.md import VelocityVerlet from ase.io import Trajectory a = 3.6 b = a / 2 fcc = Atoms('Cu', positions=[(0, 0, 0)], cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=1) fcc *= (2, 1, 1) fcc.set_calculator(EMT()) fcc.set_momenta([(0.9, 0.0, 0.0), (-0.9, 0, 0)]) md = VelocityVerlet(fcc, dt=0.1) def f(): print(fcc.get_potential_energy(), fcc.get_total_energy()) md.attach(f) md.attach(Trajectory('Cu2.traj', 'w', fcc).write, interval=3) md.run(steps=20) fcc2 = Trajectory('Cu2.traj', 'r')[-1]
import numpy as np from ase import Atoms from ase.units import fs from ase.calculators.test import TestPotential from ase.calculators.emt import EMT from ase.md import VelocityVerlet from ase.io import PickleTrajectory, read from ase.optimize import QuasiNewton np.seterr(all='raise') a = Atoms('4X', masses=[1, 2, 3, 4], positions=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0.1, 0.2, 0.7)], calculator=TestPotential()) print a.get_forces() md = VelocityVerlet(a, dt=0.5 * fs, logfile='-', loginterval=500) traj = PickleTrajectory('4N.traj', 'w', a) md.attach(traj.write, 100) e0 = a.get_total_energy() md.run(steps=10000) del traj assert abs(read('4N.traj').get_total_energy() - e0) < 0.0001 qn = QuasiNewton(a) qn.run(0.001) assert abs(a.get_potential_energy() - 1.0) < 0.000002
def test_verlet_thermostats_asap(asap3): rng = np.random.RandomState(0) calculator = asap3.EMT() T_low = 10 T_high = 300 md_kwargs = {'timestep': 0.5 * fs, 'logfile': '-', 'loginterval': 500} with seterr(all='raise'): a = bulk('Au').repeat((4, 4, 4)) a[5].symbol = 'Ag' # test thermalization by MaxwellBoltzmannDistribution thermalize(T_high, a, rng) assert abs(a.get_temperature() - T_high) < 0.0001 # test conservation of total energy e0 using Verlet a_verlet, traj = prepare_md(a, calculator) e0 = a_verlet.get_total_energy() md = VelocityVerlet(a_verlet, **md_kwargs) md.attach(traj.write, 100) md.run(steps=10000) traj_verlet = read('Au7Ag.traj', index=':') assert abs(traj_verlet[-1].get_total_energy() - e0) < 0.0001 # test reproduction of Verlet by Langevin and Andersen for thermostats # switched off pos_verlet = [t.get_positions() for t in traj_verlet[:3]] md_kwargs.update({'temperature_K': T_high}) for MDalgo in [Langevin, Andersen]: a_md, traj = prepare_md(a, calculator) if MDalgo is Langevin: md = MDalgo(a_md, friction=0.0, rng=rng, **md_kwargs) elif MDalgo is Andersen: md = MDalgo(a_md, andersen_prob=0.0, rng=rng, **md_kwargs) md.attach(traj, 100) md.run(steps=200) traj_md = read('Au7Ag.traj', index=':') pos_md = [t.get_positions() for t in traj_md[:3]] assert np.allclose(pos_verlet, pos_md) # Verlet reproduced? # test thermalization to target temperature by thermostats and # conservation of average temperature by thermostats md.set_timestep(4 * fs) if MDalgo is Langevin: md.set_friction(0.01) elif MDalgo is Andersen: md.set_andersen_prob(0.01) thermalize(T_low, a_md, rng) # thermalize with low temperature (T) assert abs(a_md.get_temperature() - T_low) < 0.0001 md.run(steps=500) # equilibration, i.e. thermalization to high T temp = [] def recorder(): temp.append(a_md.get_temperature()) md.attach(recorder, interval=1) md.run(7000) temp = np.array(temp) avgtemp = np.mean(temp) fluct = np.std(temp) print("Temperature is {:.2f} K +/- {:.2f} K".format( avgtemp, fluct)) assert abs(avgtemp - T_high) < 10.0
ase.io.write('crack_2.xyz', c, format='extxyz') c.set_calculator(calc) # relax initial structure #opt = FIRE(c) #opt.run(fmax=1e-3) ase.io.write('crack_3.xyz', c, format='extxyz') dyn = VelocityVerlet(c, params.dt, logfile=None) set_initial_velocities(dyn.atoms) crack_pos = [] traj = NetCDFTrajectory('traj.nc', 'w', c) dyn.attach(traj.write, 10, dyn.atoms, arrays=['stokes', 'momenta']) dyn.attach(find_crack_tip, 10, dyn.atoms, 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(20): dyn.run(100) 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)
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]
ase.io.write('crack_2.xyz', c, format='extxyz') c.set_calculator(calc) # relax initial structure #opt = FIRE(c) #opt.run(fmax=1e-3) ase.io.write('crack_3.xyz', c, format='extxyz') dyn = VelocityVerlet(c, params.dt, logfile=None) set_initial_velocities(dyn.atoms) crack_pos = [] traj = NetCDFTrajectory('traj.nc', 'w', c) dyn.attach(traj.write, 10, dyn.atoms, arrays=['stokes', 'momenta']) dyn.attach(find_crack_tip, 10, dyn.atoms, 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(20): dyn.run(100) if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum): set_constraints(dyn.atoms, params.a, delta_strain=None) # start decreasing strain set_constraints(dyn.atoms, params.a, delta_strain=delta_strain) for i in range(1000): dyn.run(100) if extend_strip(dyn.atoms, params.a, params.N, params.M, params.vacuum):
calc = EMT() atoms.set_calculator(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, dt=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
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()
from ase import Atoms from ase.calculators.emt import EMT from ase.md import VelocityVerlet from ase.io import Trajectory a = 3.6 b = a / 2 fcc = Atoms('Cu', positions=[(0, 0, 0)], cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=1) fcc *= (2, 1, 1) fcc.set_calculator(EMT()) fcc.set_momenta([(0.9, 0.0, 0.0), (-0.9, 0, 0)]) md = VelocityVerlet(fcc, dt=0.1) def f(): print(fcc.get_potential_energy(), fcc.get_total_energy()) md.attach(f) md.attach(Trajectory('Cu2.traj', 'w', fcc).write, interval=3) md.run(steps=20) fcc2 = Trajectory('Cu2.traj', 'r')[-1]