def test_calc_args_4(self): pot2 = Potential('IP SW', param_str=self.xml, calc_args={ 'do_rescale_E': True, 'E_scale': 1.01 }) f = pot2.get_forces(self.at) self.assertArrayAlmostEqual(f, self.forces_ref * 1.01, tol=1E-06)
def update_qm_region_H(atoms): """ Set quantum region around the Hydrogen. Also records crackpos. """ mm_pot = Potential(mm_init_args, param_filename = param_file, cutoff_skin = cutoff_skin) crack_pos = find_crack_tip_stress_field(atoms, calc=mm_pot) #set quantum region to first hydrogen atom in list. h_pos = atoms[h_list[0]].position old_qm_list = atoms.hybrid_vec.nonzero()[0] new_qm_list = update_hysteretic_qm_region(atoms, old_qm_list, h_pos, qm_inner_radius, qm_outer_radius, update_marks=False) #lets try setting the atoms object properties by hand? atoms.hybrid[:] = 0 atoms.hybrid[new_qm_list] = 1 #Distributed Force Mixing Properties: atoms.hybrid_vec[:] = 0 atoms.hybrid_vec[new_qm_list] = 1 atoms.hybrid_1[:] = atoms.hybrid_vec[:] atoms.params['core'] = h_pos atoms.params['CrackPos'] = crack_pos return
def test_get_potential_energy(self): np.random.seed(0) ats = [ Atoms('Al4', cell=(5, 5, 5), scaled_positions=np.random.uniform(size=(4, 3)), pbc=[True] * 3) for _ in range(2) ] LJ_str = """<LJ_params n_types="1" label="default"> <!-- dummy paramters for testing purposes, no physical meaning --> <per_type_data type="1" atomic_num="13" /> <per_pair_data type1="1" type2="1" sigma="2.0" eps6="1.0" eps12="1.0" cutoff="6.0" energy_shift="T" linear_force_shift="F" /> </LJ_params> """ calc = Potential(param_str=LJ_str, args_str='IP LJ') E1 = [] for at in ats: at.calc = calc at.calc.calculate(at, properties=['energy']) E1.append(at.calc.results['energy']) E2 = [] for at in ats: at.calc = calc E2.append(at.get_potential_energy()) self.assertAlmostEqual(E1, E2)
def fit_gap(atoms: List[Atoms], energies: List[float], forces: Optional[np.ndarray] = None, **kwargs) -> Potential: """Fit a GAP potential using MAL and return a ready-to-use QUIPPy object Args: atoms: List of molecules to use for training energies: List of energies for each structure forces: If available, forces acting on each atom output_dir: Directory in which to store potential kwargs: Passed to the :meth:`GAPotential.train` method Returns: ase Potential object instantiated with a model fit to the data """ # Convert all of the structures to periodic atoms = [make_periodic(a.copy()) for a in atoms] # Conver them to PyMatgen objects conv = AseAtomsAdaptor() strcs = [conv.get_structure(a) for a in atoms] # Fit using maml gap = GAPotential() gap.train(strcs, energies, forces, **kwargs) # Save to disk and return the QUIPy object gap.write_param() return Potential(param_filename="gap.xml")
def relax_structure(system, potential, potential_filename=None, relax_positions=True, relax_cell=True): """ Run a geometry optimisation on the structure to find the energy minimum. Parameters ---------- system : ase.Atoms A system of atoms to run the minimisation on. The structure is altered in-place. potential : Potential or str A quippy Potential object with the desired potential, or a potential_str to initialise a new potential. Returns ------- minimised_structure : Atoms The geometry optimised structure. """ info("Inside minimiser.") qsystem = Atoms(system) if not isinstance(potential, Potential): if potential_filename: potential = Potential(potential, param_filename=potential_filename) else: potential = Potential(potential) qsystem.set_calculator(potential) minimiser = Minim(qsystem, relax_positions=relax_positions, relax_cell=relax_cell) with Capturing(debug_on_exit=True): minimiser.run() system.set_cell(qsystem.cell) system.set_positions(qsystem.positions) system.energy = qsystem.get_potential_energy() info("Minimiser done.") return system
def setUp(self): p0 = [] for i in range(2): for j in range(2): for k in range(2): p0.append([i * 2 - 1, j * 2 - 1, k * 2 - 1]) self.p0 = np.array(p0) self.sw_pot = Potential('IP SW', param_str=xml_string)
def load_gap_model(self, path): from quippy.potential import Potential ind = np.load(os.path.join(path, "training_indices.npy"), allow_pickle=True) gap_path = os.path.join(path, "model.xml") return Potential(param_filename=gap_path), ind
def proto_qm_pot_callback(unit_cell): global qm_pot tb = tb_pot.TightBindingPot(alat=1.00, nk=1) tb.write_control_file('ctrl.fe', unit_cell) qm_pot = Potential('IP LMTO_TBE', param_str=""" <params> <LMTO_TBE_params n_types="2" control_file="ctrl.fe"> <per_type_data type="1" atomic_num="26"/> <per_type_data type="2" atomic_num="1"/> </LMTO_TBE_params> </params>""") unit_cell.add_property('forces', 0.0, n_cols=3)
def update_qm_region_crack(atoms): mm_pot = Potential(params.mm_init_args, param_filename=params.param_file, cutoff_skin=params.cutoff_skin) crack_pos = find_crack_tip_stress_field(atoms, calc=mm_pot) old_qm_list = atoms.hybrid_vec.nonzero()[0] new_qm_list = update_hysteretic_qm_region(atoms, old_qm_list, crack_pos, params.qm_inner_radius, params.qm_outer_radius, update_marks=False) #lets try setting the atoms object properties by hand? atoms.hybrid[:] = 0 atoms.hybrid[new_qm_list] = 1 #Distributed Force Mixing Properties: atoms.hybrid_vec[:] = 0 atoms.hybrid_vec[new_qm_list] = 1 atoms.hybrid_1[:] = atoms.hybrid_vec[:] atoms.params['core'] = crack_pos atoms.params['CrackPos'] = crack_pos return
########################### parser = argparse.ArgumentParser() parser.add_argument('-i', '--input_file', help='name of input file') parser.add_argument('-pt', '--pot_type', help='Potential type, GAP. EAM.') args = parser.parse_args() fe_bulk = bcc(2.83) fe_bulk.set_atoms(26) print 'Input File', args.input_file r_scale = 1.0 if args.pot_type == 'EAM': pot = Potential('IP EAM_ErcolAd do_rescale_r=T r_scale=1.0', r_scale=r_scale, param_filename=args.input_file) elif args.pot_type == 'GAP': pot = Potential('IP GAP', param_filename='gp33b.xml') #pot.set_calc_args({'local_gap_variance': False}) else: sys.exit('Invalid pot type.') fe_bulk.set_calculator(pot) print pot.cutoff() initial_a0 = fe_bulk.get_cell()[0][0] print 'Before Relaxation' print 'Energy', fe_bulk.get_potential_energy() / len(fe_bulk) print fe_bulk.get_cell().round(5) #fe_bulk.set_array('local_gap_variance', pot.results['local_gap_variance'])
import itertools import sys from quippy.atoms import Atoms from quippy.atomslist import AtomsList from quippy.potential import Potential from glob import glob from pylab import figure, plot, legend, draw, clf, xlabel, ylabel do_calc = False do_plot = False extra_args = {'molpro': {'template': 'dft'}} codes = ['castep', 'cp2k', 'gpaw', 'molpro', 'vasp'] castep = Potential('FilePot', command='./castep-driver.sh') cp2k = Potential('FilePot', command='./cp2k-driver.sh') gpaw = Potential('FilePot', command='./gpaw-driver.sh') molpro = Potential('FilePot', command='./molpro-driver.sh') vasp = Potential('FilePot', command='./vasp-driver.sh') potentials = [globals()[code] for code in codes] def h2_molecule(a, vacuum=10.0): h2 = Atoms(n=2, lattice=np.diag([vacuum, vacuum, vacuum])) h2.set_atoms([1, 1]) h2.params['bond_length'] = a h2.pos[1, 1] = -a / 2.0 h2.pos[1, 2] = +a / 2.0 return h2
def test_calc_args_3(self): pot2 = Potential('IP SW', param_str=self.xml, calc_args="do_rescale_E E_scale=1.01") f = pot2.get_forces(self.at) self.assertArrayAlmostEqual(f, self.forces_ref * 1.01, tol=1E-06)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import ase.io from ase import Atom, Atoms from ase.optimize import BFGS from ase.constraints import StrainFilter # optimize the unit cell while keeping scaled positiions fixed from ase.constraints import FixAtoms from quippy.potential import Potential from quippy import descriptors if __name__ == '__main__': # load GAP and frames gap = Potential(param_filename='./gap-2/GAP.xml') train_frames = ase.io.read('./train.xyz', ':') # ase atom frames print(len(train_frames)) """ # check lattice optimization atoms = train_frames[6].copy() atoms.set_calculator(gap) #print(atoms.get_stress(voigt=False)) #print(atoms.get_calculator().get_virial(atoms)) #print(atoms.get_potential_energy()) print(atoms.get_cell()) sf = StrainFilter(atoms) opt = BFGS(sf) opt.run(0.005)
from quippy.potential import Potential calculator = Potential("TB NRL-TB", param_filename="./quip_params.xml") import utilities import vacancy from ase.io import read at = read("NRLTB_hcp3.xyz") at.rattle(0.001) at.set_calculator(calculator) properties = vacancy.do_one_vacancy(at, calculator) print(properties[4])
def crack_strain_energy_release_rate(at, bulk=None, f_min=.8, f_max=.9, stem=None, avg_pos=False): """ Compute strain energy release rate G from elastic potential energy in a strip """ print 'Analytical effective elastic modulus E\' = ', at.YoungsModulus/(1-at.PoissonRatio_yx**2), 'GPa' print 'Analytical energy release rate G = ', crack_measure_g(at, at.YoungsModulus, at.PoissonRatio_yx, at.OrigHeight), 'J/m^2' if bulk is None: if stem is None: raise ValueError('Either "bulk" or "stem" must be present') bulk = Atoms(stem+'_bulk.xyz') if not hasattr(at, 'local_energy') or not hasattr(bulk, 'energy'): if stem is None: raise ValueError('local_energy property not found in Atoms and "stem" is missing') xmlfile = stem+'.xml' params = CrackParams(xmlfile) pot = Potential(params.classical_args, param_filename=stem+'.xml') pot.print_() if not hasattr(at, 'local_energy'): if avg_pos: tmp_pos = at.pos.copy() at.pos[...] = at.avgpos at.set_cutoff(pot.cutoff()+1.) at.calc_connect() pot.calc(at, args_str="local_energy") if avg_pos: at.pos[...] = tmp_pos if not hasattr(bulk, 'energy'): bulk.set_cutoff(pot.cutoff()+1.) bulk.calc_connect() pot.calc(bulk, args_str='energy') h = at.pos[2,:].max() - at.pos[2,:].min() h0 = at.OrigHeight strain = (h - h0)/h0 print 'Applied strain', strain x_min = f_min*at.OrigWidth - at.OrigWidth/2. x_max = f_max*at.OrigWidth - at.OrigWidth/2. strip = np.logical_and(at.move_mask == 1, np.logical_and(at.pos[1,:] > x_min, at.pos[1,:] < x_max)) at.add_property('strip', strip, overwrite=True) strip_depth = at.lattice[3,3] strip_width = at.pos[1,strip].max() - at.pos[1,strip].min() strip_height = at.pos[2,strip].max() - at.pos[2,strip].min() strip_volume = strip_width*strip_height*strip_depth print 'Strip contains', strip.sum(), 'atoms', 'width', strip_width, 'height', strip_height, 'volume', strip_volume strain_energy_density = (at.local_energy[strip].sum() - bulk.energy/bulk.n*strip.sum())/strip_volume print 'Strain energy density in strip', strain_energy_density, 'eV/A**3' E_effective = 2*strain_energy_density/strain**2*GPA print 'Effective elastic modulus E =', E_effective, 'GPa' G_effective = strain_energy_density*strip_height*J_PER_M2 print 'Effective energy release rate G =', G_effective, 'J/m^2' return G_effective
# ******* End of parameters ************* set_fortran_indexing(False) # ********** Build unit cell ************ # 8-atom diamond cubic unit cell for silicon, with guess at lattice # constant of 5.44 A si_bulk = bulk('Si', 'diamond', a=5.44, cubic=True) # ********** Setup potential ************ # Stillinger-Weber (SW) classical interatomic potential, from QUIP mm_pot = Potential(mm_init_args, param_filename=param_file) # ***** Find eqm. lattice constant ****** # find the equilibrium lattice constant by minimising atoms wrt virial # tensor given by SW pot (possibly replace this with parabola fit in # another script and hardcoded a0 here) si_bulk.set_calculator(mm_pot) print('Minimising bulk unit cell...') minim = Minim(si_bulk, relax_positions=True, relax_cell=True) minim.run(fmax=1e-4) a0 = si_bulk.cell[0, 0] print('Lattice constant %.3f A\n' % a0)
width = 200.0*units.Ang # Width of crack slab height = 100.0*units.Ang # Height of crack slab vacuum = 100.0*units.Ang # Amount of vacuum around slab crack_seed_length = 40.0*units.Ang # Length of seed crack strain_ramp_length = 30.0*units.Ang # Distance over which strain is ramped up initial_G = 5.0*(units.J/units.m**2) # Initial energy flow to crack tip relax_fmax = 0.025*units.eV/units.Ang # Maximum force criteria for relaxation output_file = 'crack.xyz' # File to which structure will be written set_fortran_indexing(False) si_bulk = bulk('Si', 'diamond', a=5.431, cubic=True) mm_pot = Potential("IP SW") si_bulk.set_calculator(mm_pot) c = mm_pot.get_elastic_constants(si_bulk) E = youngs_modulus(c, cleavage_plane) nu = poisson_ratio(c, cleavage_plane, crack_direction) unit_slab = Diamond(directions=[crack_direction, cleavage_plane, crack_front], size=(1, 1, 1), symbol='Si', pbc=True, latticeconstant=5.431)
# ******* End of parameters ************* # ********** Build unit cell ************ # 8-atom diamond cubic unit cell for silicon, with guess at lattice # constant of 5.44 A si_bulk = bulk('Si', 'diamond', a=5.44, cubic=True) # ********** Setup potential ************ # Stillinger-Weber (SW) classical interatomic potential, from QUIP mm_pot = Potential(mm_init_args, param_filename=param_file, fortran_indexing=False) # ***** Find eqm. lattice constant ****** # find the equilibrium lattice constant by minimising atoms wrt virial # tensor given by SW pot (possibly replace this with parabola fit in # another script and hardcoded a0 here) si_bulk.set_calculator(mm_pot) print('Minimising bulk unit cell...') minim = Minim(si_bulk, relax_positions=True, relax_cell=True) minim.run(fmax=1e-4) a0 = si_bulk.cell[0, 0]
def __init__(self, mm_clients, qm_clients, ip, port=0, rundir=None, bulk_scale=None, mpi_obj=None, callback=None, calculator=None, cutoff_skin=1.0, atoms=None, qm_list=None, fpointer=None, finalise=True, error=None, cluster_args=None, test_mode=False, save_clusters=False, force_restart=False, **kwargs): def callback_factory(force_prop_name): def callback(at): at.add_property('force', getattr(at, force_prop_name), overwrite=True) return callback if isinstance(mm_clients, Potential): self.mm_local = True self.pot1 = mm_clients self.mm_clients = [] else: self.mm_local = False self.mm_clients = mm_clients if isinstance(qm_clients, Potential): self.qm_pot = qm_clients self.qm_clients = [] else: self.qm_clients = qm_clients clients = self.mm_clients + self.qm_clients print 'Clients', clients if len(clients) > 0: self.server = AtomsServerAsync((ip, port), AtomsRequestHandler, clients, bgq=True) self.server_thread = threading.Thread( target=self.server.serve_forever) self.server_thread.daemon = True self.server_thread.start() if rundir is None: rundir = os.getcwd() self.rundir = rundir self.label = 1 self.cluster_args = {} if cluster_args is not None: self.cluster_args = cluster_args if not self.mm_local: self.pot1 = Potential('CallbackPot', callback=callback_factory('mm_force')) self.pot2 = Potential('CallbackPot', callback=callback_factory('qm_force')) self.test_mode = test_mode self.save_clusters = save_clusters self.force_restart = force_restart ForceMixingPotential.__init__(self, self.pot1, self.pot2, bulk_scale=bulk_scale, mpi_obj=mpi_obj, cutoff_skin=cutoff_skin, atoms=atoms, qm_list=qm_list, fpointer=fpointer, finalise=finalise, error=error, **kwargs)
fixed_mask = ((abs(atoms.positions[:, 1] - top) < 1.0) | (abs(atoms.positions[:, 1] - bottom) < 1.0)) fix_atoms = FixAtoms(mask=fixed_mask) print('Fixed %d atoms\n' % fixed_mask.sum()) # Increase epsilon_yy applied to all atoms at constant strain rate strain_atoms = ConstantStrainRate(orig_height, strain_rate*timestep) atoms.set_constraint([fix_atoms, strain_atoms]) # ******* Set up potentials and calculators ******** mm_pot = Potential(mm_init_args, param_filename=param_file, cutoff_skin=cutoff_skin) # Request Potential to compute per-atom stresses whenever we # compute forces, to save time when locating the crack tip mm_pot.set_default_quantities(['stresses']) # Density functional tight binding (DFTB) potential qm_pot = Potential(qm_init_args, param_filename=param_file) # Parameters which control how the QM calculation is carried out: # we use a single cluster, periodic in the z direction and terminated # with hydrogen atoms. The positions of the outer layer of buffer atoms # are not randomised. qm_args_str = ('single_cluster cluster_periodic_z '+
right = atoms.positions[:, 0].max() # fix atoms in the top and bottom rows fixed_mask = ((abs(atoms.positions[:, 1] - top) < 1.0) | (abs(atoms.positions[:, 1] - bottom) < 1.0)) fix_atoms = FixAtoms(mask=fixed_mask) print('Fixed %d atoms\n' % fixed_mask.sum()) atoms.set_constraint([fix_atoms]) # Increase epsilon_yy applied to all atoms at constant strain rate strain_atoms = ConstantStrainRate(orig_height, strain_rate * timestep) # ******* Set up potentials and calculators ******** mm_pot = Potential(mm_init_args, param_filename=param_file, cutoff_skin=cutoff_skin) # Density functional tight binding (DFTB) potential qm_pot = Potential(qm_init_args, param_filename=param_file) # Construct the QM/MM potential, which mixes QM and MM forces. # The qm_args_str parameters control how the QM calculation is carried out: # we use a single cluster, periodic in the z direction and terminated # with hydrogen atoms. The positions of the outer layer of buffer atoms # are not randomised. qmmm_pot = ForceMixingPotential( pot1=mm_pot, pot2=qm_pot, qm_args_str='single_cluster cluster_periodic_z carve_cluster ' + 'terminate cluster_hopping=F randomise_buffer=F',
default='PDOS_USER.dat', help='PDOS Data File') parser.add_argument('-v', '--valid', \ default='DOS.png', help='DOS Figure') parser.add_argument('-g', '--gap', \ default='DOS.png', help='DOS Figure') args = parser.parse_args() # read structures #train_frames = ase.io.read('./train.xyz', ':') #valid_frames = ase.io.read('./validate.xyz', ':') #gap = Potential(param_filename='./GAP.xml') train_frames = ase.io.read(args.train, ':') valid_frames = ase.io.read(args.valid, ':') gap = Potential(param_filename=args.gap) # read energy using vasp train_forces_vasp, train_forces_gap, train_energies_vasp, train_energies_gap = \ extract_energy_and_forces(train_frames,calc=gap) valid_forces_vasp, valid_forces_gap, valid_energies_vasp, valid_energies_gap = \ extract_energy_and_forces(valid_frames,calc=gap) # plot fig, axarr = plt.subplots(nrows=2, ncols=2, \ gridspec_kw={'hspace': 0.3}, figsize=(12,8)) axarr = axarr.flat[:] plt.suptitle('Electronic Free Energy and Hellman-Feynman Forces')
def molecular_dynamics(system, potential, potential_filename=None, temperature=300, total_steps=1100000, timestep=1.0, connect_interval=200, write_interval=20000, equilibration_steps=100000, out_of_plane=None, random_seed=None): """ Run very simple molecular dynamics to generate some configurations. Writes configurations out as xyz and CASTEP files. """ info("Inside MD.") if random_seed is None: random_seed = random.SystemRandom().randint(0, 2**63) quippy.system.system_set_random_seeds(random_seed) info("Quippy Random Seed {0}.".format(random_seed)) system = Atoms(system) # Can take Potential objects, or just use a string if not isinstance(potential, Potential): if potential_filename: potential = Potential(potential, param_filename=potential_filename) else: potential = Potential(potential) system.set_calculator(potential) dynamical_system = DynamicalSystem(system) with Capturing(debug_on_exit=True): dynamical_system.rescale_velo(temperature) if out_of_plane is not None: # Stop things moving vertically in the cell dynamical_system.atoms.velo[3, :] = 0 base_dir = os.getcwd() run_path = '{0}_{1:g}/'.format(system.info['name'], temperature) info("Putting files in {0}.".format(run_path)) os.mkdir(run_path) os.chdir(run_path) trajectory = 'traj_{0}_{1:g}.xyz'.format(system.info['name'], temperature) out = AtomsWriter(trajectory) dynamical_system.atoms.set_cutoff(potential.cutoff() + 2.0) dynamical_system.atoms.calc_connect() potential.calc(dynamical_system.atoms, force=True, energy=True, virial=True) structure_count = 0 # Basic NVE molecular dynamics for step_number in range(1, total_steps + 1): dynamical_system.advance_verlet1(timestep, virial=dynamical_system.atoms.virial) potential.calc(dynamical_system.atoms, force=True, energy=True, virial=True) dynamical_system.advance_verlet2(timestep, f=dynamical_system.atoms.force, virial=dynamical_system.atoms.virial) # Maintenance of the system if not step_number % connect_interval: debug("Connect at step {0}".format(step_number)) dynamical_system.atoms.calc_connect() if step_number < equilibration_steps: with Capturing(debug_on_exit=True): dynamical_system.rescale_velo(temperature) if not step_number % write_interval: debug("Status at step {0}".format(step_number)) # Print goes to captured stdout with Capturing(debug_on_exit=True): dynamical_system.print_status( epot=dynamical_system.atoms.energy) dynamical_system.rescale_velo(temperature) if step_number > equilibration_steps: debug("Write at step {0}".format(step_number)) out.write(dynamical_system.atoms) sp_path = '{0:03d}'.format(structure_count) write_filename = '{0}_{1:g}.{2:03d}'.format( system.info['name'], temperature, structure_count) os.mkdir(sp_path) os.chdir(sp_path) castep_write(dynamical_system.atoms, filename=write_filename) espresso_write(dynamical_system.atoms, prefix=write_filename) write_extxyz("{0}.xyz".format(write_filename), dynamical_system.atoms) info("Wrote a configuration {0}.".format(write_filename)) os.chdir('..') structure_count += 1 out.close() os.chdir(base_dir) info("MD Done.")
output_file = 'crack.xyz' # File to which structure will be written # ******* End of parameters ************* set_fortran_indexing(False) # ********** Build unit cell ************ # 8-atom diamond cubic unit cell for silicon, with guess at lattice # constant of 5.44 A si_bulk = bulk('Si', 'diamond', a=5.44, cubic=True) # ********** Setup potential ************ # Stillinger-Weber (SW) classical interatomic potential, from QUIP mm_pot = Potential(mm_init_args, param_filename=param_file) # ***** Find eqm. lattice constant ****** # find the equilibrium lattice constant by minimising atoms wrt virial # tensor given by SW pot (possibly replace this with parabola fit in # another script and hardcoded a0 here) si_bulk.set_calculator(mm_pot) print('Minimising bulk unit cell...') minim = Minim(si_bulk, relax_positions=True, relax_cell=True) minim.run(fmax=1e-4) a0 = si_bulk.cell[0, 0] print('Lattice constant %.3f A\n' % a0)
def setUp(self): self.xml = """ <SW_params n_types="2" label="PRB_31_plus_H"> <comment> Stillinger and Weber, Phys. Rev. B 31 p 5262 (1984), extended for other elements </comment> <per_type_data type="1" atomic_num="1" /> <per_type_data type="2" atomic_num="14" /> <per_pair_data atnum_i="1" atnum_j="1" AA="0.0" BB="0.0" p="0" q="0" a="1.0" sigma="1.0" eps="0.0" /> <per_pair_data atnum_i="1" atnum_j="14" AA="8.581214" BB="0.0327827" p="4" q="0" a="1.25" sigma="2.537884" eps="2.1672" /> <per_pair_data atnum_i="14" atnum_j="14" AA="7.049556277" BB="0.6022245584" p="4" q="0" a="1.80" sigma="2.0951" eps="2.1675" /> <!-- triplet terms: atnum_c is the center atom, neighbours j and k --> <per_triplet_data atnum_c="1" atnum_j="1" atnum_k="1" lambda="21.0" gamma="1.20" eps="2.1675" /> <per_triplet_data atnum_c="1" atnum_j="1" atnum_k="14" lambda="21.0" gamma="1.20" eps="2.1675" /> <per_triplet_data atnum_c="1" atnum_j="14" atnum_k="14" lambda="21.0" gamma="1.20" eps="2.1675" /> <per_triplet_data atnum_c="14" atnum_j="1" atnum_k="1" lambda="21.0" gamma="1.20" eps="2.1675" /> <per_triplet_data atnum_c="14" atnum_j="1" atnum_k="14" lambda="21.0" gamma="1.20" eps="2.1675" /> <per_triplet_data atnum_c="14" atnum_j="14" atnum_k="14" lambda="21.0" gamma="1.20" eps="2.1675" /> </SW_params> """ quippy.system_module.system_reseed_rng(2065775975) self.pot_calculator = Potential('IP SW', param_str=self.xml) self.at = Atoms('Si8', positions=diamond_pos, pbc=True, cell=[5.44, 5.44, 5.44]) self.f = np.zeros((3, len(self.at)), order='F') self.df = np.zeros((3, len(self.at)), order='F') self.v = np.zeros((3, 3), order='F') self.energy_ref = -34.5038375509 self.forces_ref = np.array([[0.89920374, -0.38025157, -0.38727027], [0.36623356, -0.52403757, 0.7200206], [-0.36952654, 0.12899529, 0.00458111], [-0.19912365, -0.1632057, 1.08509495], [-0.67565314, -0.59410498, -0.47921521], [0.17097454, 0.5847822, -0.31088749], [0.43613712, 0.90811269, 0.1600328], [-0.62824563, 0.03970963, -0.79235649]]) self.virial_ref = np.array([[-0.34103601, 0.60925144, -0.02138795], [0.60925144, -0.36145702, -0.19375487], [-0.02138795, -0.19375487, -0.34640615]]).T # Voigt notation by hand from virial self.stress_ref = -np.array([ -0.34103601, -0.36145702, -0.34640615, -0.19375487, -0.02138795, 0.60925144 ]) / self.at.get_volume() self.at.set_calculator(self.pot_calculator)
# A module defining a module needs to define only one variable, # named `calculator`, which should be an instance of the ase.calculator.Calculator, # a subclass of this, or a compatible class implementing the calculator interface. calculator = Potential('IP Tersoff', param_str=""" <Tersoff_params n_types="3" label="PRB_39"> <comment> Tersoff, Phys. Rev. B v 39, p 5566 (1989) </comment> <per_type_data type="1" atomic_num="6" A="1393.6" B="346.7" lambda="3.4879" mu="2.2119" beta="0.00000015724" n="0.72751" c="38049" d="4.384" h="-0.57058" R="1.8" S="2.1" /> <per_type_data type="2" atomic_num="14" A="1830.8" B="471.18" lambda="2.4799" mu="1.7322" beta="0.0000011" n="0.78734" c="100390" d="16.217" h="-0.59825" R="2.7" S="3.0" /> <per_type_data type="3" atomic_num="32" A="1769" B="419.23" lambda="2.4451" mu="1.7047" beta="0.00000090166" n="0.75627" c="106430" d="15.652" h="-0.43884" R="2.8" S="3.1" /> <per_pair_data type1="1" type2="1" chi="1.0" /> <per_pair_data type1="2" type2="1" chi="0.9776" /> <per_pair_data type1="2" type2="2" chi="1.0" /> <per_pair_data type1="3" type2="1" chi="1.0" /> <per_pair_data type1="3" type2="2" chi="1.00061" /> <per_pair_data type1="3" type2="3" chi="1.0" /> </Tersoff_params> """) no_checkpoint = True name = 'Tersoff'
def makecrack_main(params, stem): """Given a CrackParams object `param`, construct and return a new crack slab Atoms object.""" xmlfilename = stem+'.xml' print_title('Initialisation') verbosity_push(params.io_verbosity) params.print_() print("Initialising classical potential with args " + params.classical_args.strip() + " from file " + xmlfilename) classicalpot = Potential(params.classical_args, param_filename=xmlfilename) classicalpot.print_() mpi_glob = MPI_context() crack_slab, width, height, E, v, v2, bulk = crack_make_slab(params, classicalpot) if params.crack_free_surfaces: depth = crack_slab.pos[3,:].max() - crack_slab.pos[3,:].min() else: depth = crack_slab.lattice[3,3] # Save bulk cube (used for qm_rescale_r parameter in crack code) if params.qm_args.startswith('TB'): bigger_bulk = supercell(bulk, 2, 2, 2) bulk = bigger_bulk bulk.write(stem+'_bulk.xyz') crack_slab.write(stem+'_slab.xyz') crack_slab.params['OrigWidth'] = width crack_slab.params['OrigHeight'] = height crack_slab.params['OrigDepth'] = depth crack_slab.params['YoungsModulus'] = E crack_slab.params['PoissonRatio_yx'] = v crack_slab.params['PoissonRatio_yz'] = v2 # Open surfaces, remain periodic in z direction (normal to plane) # and optionally also in x direction if crack_double_ended is true if not params.crack_double_ended: crack_slab.lattice[1,1] = crack_slab.lattice[1,1] + params.crack_vacuum_size crack_slab.lattice[2,2] = crack_slab.lattice[2,2] + params.crack_vacuum_size crack_slab.set_lattice(crack_slab.lattice, False) # 3D crack with free surfaces at z = +/- depth/2 if params.crack_free_surfaces: crack_slab.pos[3,:] -= crack_slab.pos[3,:].mean() # center on z=0 crack_slab.lattice[3,3] = crack_slab.lattice[3,3] + params.crack_vacuum_size crack_slab.set_lattice(crack_slab.lattice, False) # Map atoms into cell AFTER changing to the new lattice crack_slab.map_into_cell() miny, maxy = crack_slab.pos[2,:].min(), crack_slab.pos[2,:].max() assert abs((maxy-miny) - height) < 1e-5 # be sure that remapping didn't change height of slab # Add various properties to crack_slab crack_slab.add_property('hybrid', 0) crack_slab.add_property('hybrid_mark', HYBRID_NO_MARK) crack_slab.add_property('changed_nn', 0) crack_slab.add_property('move_mask', 0) crack_slab.add_property('nn', 0) crack_slab.add_property('old_nn', 0) crack_slab.add_property('md_old_changed_nn', 0) crack_slab.add_property('edge_mask', 0) crack_slab.add_property('crack_surface', False) crack_slab.add_property('crack_front', False) if params.crack_fix_dipoles: crack_slab.add_property('fixdip', False) print_title('Fixing Atoms') # Fix top and bottom edges - anything within crack_edge_fix_tol of ymax or ymin is fixed miny, maxy = crack_slab.pos[2,:].min(), crack_slab.pos[2,:].max() crack_slab.move_mask[:] = 1 crack_slab.move_mask[(abs(crack_slab.pos[2,:]-maxy) < params.crack_edge_fix_tol) | (abs(crack_slab.pos[2,:]-miny) < params.crack_edge_fix_tol)] = 0 if params.crack_fix_sides: maxx, minx = crack_slab.pos[1,:].min(), crack_slab.pos[1,:].max() crack_slab.move_mask[(abs(crack_slab.pos[1,:]-maxx) < params.crack_edge_fix_tol) | (abs(crack_slab.pos[1,:]-minx) < params.crack_edge_fix_tol)] = 0 print('%d atoms. %d fixed atoms' % (crack_slab.n, crack_slab.n - crack_slab.move_mask.sum())) print_title('Setting edge mask') crack_slab.edge_mask[:] = 0 minx, maxx = crack_slab.pos[1,:].min(), crack_slab.pos[1,:].max() crack_slab.edge_mask[(abs(crack_slab.pos[1,:]-minx) < params.selection_edge_tol) | (abs(crack_slab.pos[1,:]-maxx) < params.selection_edge_tol)] = 1 miny, maxy = crack_slab.pos[2,:].min(), crack_slab.pos[2,:].max() crack_slab.edge_mask[(abs(crack_slab.pos[2,:]-miny) < params.selection_edge_tol) | (abs(crack_slab.pos[2,:]-maxy) < params.selection_edge_tol)] = 1 if params.crack_free_surfaces: # Open surfaces at +/- z minz, maxz = crack_slab.pos[3,:].min(), crack_slab.pos[3,:].max() crack_slab.edge_mask[(abs(crack_slab.pos[3,:]-minz) < params.selection_edge_tol) | (abs(crack_slab.pos[3,:]-maxz) < params.selection_edge_tol)] = 1 if params.crack_fix_dipoles: print_title('Fixing dipoles') crack_slab.fixdip[(abs(crack_slab.pos[2,:]-maxy) < params.crack_fix_dipoles_tol) | (abs(crack_slab.pos[2,:]-miny) < params.crack_fix_dipoles_tol)] = 1 if params.crack_fix_sides: maxx, minx = crack_slab.pos[1,:].min(), crack_slab.pos[1,:].max() crack_slab.fixdip[(abs(crack_slab.pos[1,:]-maxx) < params.crack_fix_dipoles_tol) | (abs(crack_slab.pos[1,:]-minx) < params.crack_fix_dipoles_tol)] = 1 if params.crack_curved_front: crack_make_seed_curved_front(crack_slab, params) else: crack_make_seed(crack_slab, params) if params.crack_apply_initial_load: crack_calc_load_field(crack_slab, params, classicalpot, params.crack_loading, overwrite_pos=True, mpi=mpi_glob) crack_slab.write('dump.xyz') crack_update_connect(crack_slab, params) if not params.simulation_classical: if (params.selection_method.strip() == 'crack_front' or params.crack_tip_method.strip() == 'local_energy'): classicalpot.calc(crack_slab, local_energy=True) crack_setup_marks(crack_slab, params) crack_update_selection(crack_slab, params) if params.any_per_atom_tau(): # Set up per_atom_tau property for ramped Langevin thermostat: # # tau # ^ # |\ /| |\ /| max_tau # | \ / | | \ / | # | \ / | constant E | \ / | # | \ / | (tau = 0) | \ / | # | \/ | | \/ | # +----------+---------------------+----------+---> x # -w/2 -w/2+r w/2-r w/2 w_by_2 = crack_slab.OrigWidth/2. ramp_len = params.crack_thermostat_ramp_length max_tau = params.crack_thermostat_ramp_max_tau print 'Adding thermostat ramp with length', ramp_len, 'max_tau', max_tau @np.vectorize def tau(x): if x < -w_by_2 + ramp_len/2: q = (x+w_by_2)/(ramp_len/2.) return max_tau*(1.- q) elif (x > -w_by_2 + ramp_len/2 and x < -w_by_2 + ramp_len): q = (x+w_by_2-ramp_len/2.)/(ramp_len/2.) return max_tau*q elif (x > -w_by_2 + ramp_len and x < w_by_2 - ramp_len): return 0. elif (x > w_by_2 - ramp_len and x < w_by_2 - ramp_len/2): q = (x-w_by_2+ramp_len)/(ramp_len/2.) return max_tau*(1.- q) else: q = (x-w_by_2+ramp_len/2.)/(ramp_len/2.) return max_tau*q crack_slab.add_property('per_atom_tau', tau(crack_slab.pos[1,:])) return crack_slab
print restart if restart: print 'Restarting job' input_file = args.input_file pot_file = args.pot_file if args.output_file: traj_file = args.output_file print 'Output_file: ', traj_file print 'POT FILE', pot_file #Need to add an arg parser here learn_on_the_fly = True if learn_on_the_fly: print 'Initialize Potentials' atoms = Atoms(input_file) cluster = Atoms('cluster.xyz') mm_pot = Potential(mm_init_args, param_filename=pot_file, cutoff_skin=cutoff_skin) unit_cell = BodyCenteredCubic(directions = [[1,0,0], [0,1,0],[0,0,1]], size = (4,4,4), symbol='Fe', pbc=(1,1,1), latticeconstant = 2.87) def proto_qm_pot_callback(unit_cell): global qm_pot tb = tb_pot.TightBindingPot(alat=1.00, nk=1) tb.write_control_file('ctrl.fe', unit_cell) qm_pot = Potential('IP LMTO_TBE', param_str=""" <params> <LMTO_TBE_params n_types="2" control_file="ctrl.fe"> <per_type_data type="1" atomic_num="26"/> <per_type_data type="2" atomic_num="1"/> </LMTO_TBE_params>
def __init__(self, fpointer=None, finalise=True, error=None): Potential.__init__(self, callback=self.do_cp2k_calc)
last_traj = traj_files[-1] input_file = last_traj + '@-1' # loading reference configuration for Nye tensor evaluation # convert to quippy Atoms - FIXME in long term, this should not be necesary x0 = Atoms(params.reference_file) x0 = Atoms(x0) x0.set_cutoff(3.0) x0.calc_connect() print params.param_file # ******* Set up potentials and calculators ******** system_timer('init_fm_pot') pot_file = os.path.join(params.pot_dir, params.param_file) mm_pot = Potential(params.mm_init_args, param_filename=params.param_file, cutoff_skin=params.cutoff_skin) cluster_args = params.cluster_args.copy() if params.test_mode: # dummy QM potential made by swapping Ni and Al species in MM potential qm_pot = Potential(params.mm_init_args, param_filename='m2004flipNiAl.xml', cutoff_skin=params.cutoff_skin) qm_clients = qm_pot # convergence of EAM forces with buffer size is suprisingly slow, # so we need to use a big buffer to avoid messing up predictor/corrector # error plot cluster_args['hysteretic_buffer_inner_radius'] = 12.0
orig_height = atoms.info['OrigHeight'] orig_crack_pos = atoms.info['CrackPos'].copy() top = atoms.positions[:, 1].max() bottom = atoms.positions[:, 1].min() left = atoms.positions[:, 0].min() right = atoms.positions[:, 0].max() fixed_mask = ((abs(atoms.positions[:, 1] - top) < 1.0) | (abs(atoms.positions[:, 1] - bottom) < 1.0)) fix_atoms = FixAtoms(mask=fixed_mask) strain_atoms = ConstantStrainRate(orig_height, strain_rate*timestep) atoms.set_constraint([fix_atoms, strain_atoms]) mm_pot = Potential("IP SW", cutoff_skin=cutoff_skin) mm_pot.set_default_properties(['stresses']) atoms.set_calculator(mm_pot) # ********* Setup and run MD *********** MaxwellBoltzmannDistribution(atoms, 2.0*sim_T) dynamics = VelocityVerlet(atoms, timestep) def printstatus(): if dynamics.nsteps == 1: print """ State Time/fs Temp/K Strain G/(J/m^2) CrackPos/A D(CrackPos)/A ---------------------------------------------------------------------------------"""
fixed_mask = ((abs(atoms.positions[:, 1] - top) < 1.0) | (abs(atoms.positions[:, 1] - bottom) < 1.0)) fix_atoms = FixAtoms(mask=fixed_mask) print('Fixed %d atoms\n' % fixed_mask.sum()) # Increase epsilon_yy applied to all atoms at constant strain rate strain_atoms = ConstantStrainRate(orig_height, strain_rate*timestep) atoms.set_constraint([fix_atoms, strain_atoms]) # ******* Set up potentials and calculators ******** mm_pot = Potential(mm_init_args, param_filename=param_file, cutoff_skin=cutoff_skin) # Request Potential to compute per-atom stresses whenever we # compute forces, to save time when locating the crack tip mm_pot.set_default_quantities(['stresses']) atoms.set_calculator(mm_pot) # ********* Setup and run MD *********** # Set the initial temperature to 2*simT: it will then equilibriate to # simT, by the virial theorem MaxwellBoltzmannDistribution(atoms, 2.0*sim_T) # Initialise the dynamical system