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 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 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 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 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
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)
#v = -1.0 * at.get_volume() * at.get_stress(voigt=False) print(i, j, config_type) print(e / len(at)) print(at.get_volume() / len(at)) at.arrays["force"] = f #at.info["virial"] = v at.info["config_type"] = "FLD_" + config_type at.info["energy_TB"] = e #write("hcp_FLD_{}_{}_{}_{}.xyz".format(i,j,k,l), at) al.append(at) return al calc = Potential( "TB NRL-TB", param_filename= "./16x16x16k_mesh_param_file_tightbind.parms.NRL_TB.Ti_spline.xml") # at_bcc = read("./NRLTB_bcc.xyz") # at_hcp = read("./NRLTB_hcp.xyz") # PH_bcc = get_phonon_configs(calc, at_bcc, [0.1], 3, "bcc") # PH_hcp = get_phonon_configs(calc, at_hcp, [0.1], 3, "hcp") FLD_bcc = get_FLD_configs(calc, "./NRLTB_bcc.xyz", "bcc") FLD_hcp = get_FLD_configs(calc, "./NRLTB_hcp.xyz", "hcp") al = FLD_bcc + FLD_hcp #+ PH_bcc + PH_hcp write("./FLD16_hcp_bcc_sm3-6+3p.xyz", al)
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])
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 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.")
#!/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)
# ******* 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]
# 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 __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)
def quippy_client(ip, port, client_id, maxsteps): pot = Potential('IP SW') args_str = 'force energy virial' print 'Starting client with ID %d maxsteps %d' % (client_id, maxsteps) for step in range(maxsteps): # open connection to server sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, port)) try: # say hello and ask server for some Atoms (status 'A') # identify ourselves with an ID number, formatted as an 8-byte ascii string id_str = ('A%7d' % client_id).encode('ascii') totalsent = 0 while totalsent < MSG_LEN_SIZE: sent = sock.send(id_str[totalsent:]) if sent == 0: raise RuntimeError('socket connection broken while sending client ID') totalsent += sent # now receive the length of the data, formatted as an 8-byte ascii string data_length = '' while len(data_length) < MSG_LEN_SIZE: chunk = sock.recv(MSG_LEN_SIZE - len(data_length)) if not chunk: raise RuntimeError('socket connection broken while reading length') data_length += chunk data_length = int(data_length) # now receive the data itself data = '' while len(data) < data_length: chunk = sock.recv(data_length - len(data)) if not chunk: raise RuntimeError('socket connection broken while reading data') data += chunk # and finally receive the end marker marker = '' while len(marker) < MSG_END_MARKER_SIZE: chunk = sock.recv(MSG_END_MARKER_SIZE - len(marker)) if not chunk: raise RuntimeError('socket connection broken while reading end marker') marker += chunk assert marker == MSG_END_MARKER finally: sock.close() # construct Atoms object from data string in REFTRAJ format at = unpack_reftraj_str_to_atoms(data) # do the calculation - this will take a long time in real use-case pot.calc(at, args_str=args_str) print 'client %d calculation on %d atoms complete' % (client_id, len(at)) # format the results into a string, cf. vasp_driver REJTRAJ_OUTPUT format data = pack_results_to_reftraj_output_str(at) # open a new connection to the server to send back the results sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, port)) try: # say hello and tell server we've got some results (status 'R') # identify ourselves with an ID number, formatted as an 8-byte ascii string id_str = ('R%7d' % client_id).encode('ascii') totalsent = 0 while totalsent < MSG_LEN_SIZE: sent = sock.send(id_str[totalsent:]) if sent == 0: raise RuntimeError('socket connection broken while sending client ID') totalsent += sent # send back results of the calculation totalsent = 0 while totalsent < len(data): sent = sock.send(data[totalsent:]) if sent == 0: raise RuntimeError('socket connection broken while sending results') totalsent += sent # wait to receive the end marker marker = '' while len(marker) < MSG_END_MARKER_SIZE: chunk = sock.recv(MSG_END_MARKER_SIZE - len(marker)) if not chunk: raise RuntimeError('socket connection broken while reading end marker') marker += chunk assert marker == MSG_END_MARKER finally: sock.close()
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
########################### 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'])
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')
# we need an input Queue for each client: this is so that we can # exploit wavefunction reuse by sending consecutive clusters belonging # to the same atom to the same QM partition input_qs = [Queue() for i in range(N_JOBS)] output_q = Queue() cluster_map = {} # setup clusters to be calculated d = diamond(5.44, 14) at = supercell(d, 2, 2, 2) at.rattle(0.05) at.calc_connect() clusters = list( iter_atom_centered_clusters(at, buffer_hops=4, randomise_buffer=False)) pot = Potential('IP SW', param_filename='params.xml') for i, c in enumerate(clusters): client_id, nstep = i % N_JOBS, i // N_JOBS data = pack_atoms_to_reftraj_str(c, nstep) cluster_map[(client_id, nstep)] = (c, i) input_qs[client_id].put(data) pot.calc(c, args_str='energy force virial') t_clus = time.time() # write initial input file for each client for client_id, input_q in enumerate(input_qs): data = input_q.get() c, i = cluster_map[(client_id, 0)] c.write('atoms.%d.xyz' % client_id, real_format=MSG_FLOAT_FORMAT)
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)
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 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
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
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 '+
procs = 96 # need to have procs % n_par == 0 n_par = 1 if procs <= 8: n_par = procs else: for _npar in range(2, int(np.sqrt(1.*procs))): if procs % int(_npar) == 0: n_par = procs // int(_npar) vasp_client = VaspClient(client_id=0, npj=procs, ppn=1, exe=vasp, mpirun=mpirun, parmode='mpi', ibrion=13, nsw=1000000, npar=8, **vasp_args) qm_pot = Potential(calculator=SocketCalculator(vasp_client)) if not args.restart: atoms = io.read('POSCAR') else: atoms = AtomsReader(args.input_file)[-1] atoms.set_calculator(qm_pot) if args.restart: #delete certain keys so they don't cause problem in write_xyz. del_keys = ['force','forces', 'forces0'] array_keys = atoms.arrays.keys() prop_keys = atoms.properties.keys() for key in del_keys: if key in array_keys: