def cluster_from_grs(filename, rI, rII, new_rI=None, r=None): '''Reads in the .grs file output after a successful cluster-based dislocation simulation and use it to construct a <TwoRegionCluster>. <new_rI> allows us to change the size of region I, whether by enlarging or reducing it. ''' # read in the .grs file and convert it to cartesian coordinates grs_struc = cry.Crystal() sysinfo = parse_gulp(filename, grs_struc) for atom in grs_struc: # arrange coords in order x, y, z atom.from_cluster() # convert z from pcell units to length units atom.to_cart(grs_struc) # determine the region I radius to use for the new cluster if new_rI is None: new_rI = rI else: # check that <new_rI> is not ridiculous if new_rI >= rII: raise ValueError( "New region I radius must be less than total radius.") # create the cluster height = norm(grs_struc.getC()) new_cluster = rs.TwoRegionCluster(R=rI, regionI=new_rI, regionII=rII, height=height, periodic_atoms=grs_struc) return new_cluster, sysinfo
def read_unit_cell(cellname, program, shift, permutation=[0, 1, 2], path='./'): '''Reads in the unit from which the dislocation-bearing cluster will be built. ''' # read in cell using the program-appropriate parse function basestruc = cry.Crystal() if program == 'gulp': parse_fn = parse_gulp elif program == 'qe': parse_fn = parse_qe elif program == 'castep': parse_fn = parse_castep else: raise ValueError("Program {} not supported.".format(program)) sinfo = parse_fn(cellname, basestruc, path=path) if len(shift) < 3: # create 3-vector, populating first n elements from shift new_shift = np.zeros(3) for i in range(len(shift)): new_shift[i] = shift[i] shift = new_shift basestruc.translate_cell(np.array(shift), modulo=True) # permute the coordinates of the unit cell. Usually necessary as the glide # plane normal is usually aligned along z for GSF calculations, whereas here # it should be along y permute.check_permutation(permutation) permute.permute_cell(basestruc, program, permutation) return basestruc
def perfect_bonds(cellname, atom_index, max_bond_length, use_species=False, bonded_type=None): '''Extracts all bonds between atom <atom_index> and adjacent atoms of type <atom_type> out to the given distance in the undeformed crystal <cellname>. ''' # read in crystal structure unit_cell = cry.Crystal() sinfo = gulp.parse_gulp(cellname, unit_cell) # extract the coordinates of the atom specified and convert to angstroms atom0 = unit_cell[atom_index] x0 = atom0.getCoordinates() lattice = unit_cell.getLattice() x0 = x0[0] * lattice[0] + x0[1] * lattice[1] + x0[2] * lattice[2] # if <bonded_type> is not given, assume that we are calculating the Nye # tensor on the sublattice to which <atom_index> belongs if bonded_type is None: bonded_type = atom0.getSpecies() # extract bonds P = [] for i in range(len(unit_cell)): # check that atom i is of the correct species if use_species: if unit_cell[i].getSpecies() != bonded_type: continue # convert coordinates of atom <i> to angstroms y0 = unit_cell[i].getCoordinates() y0 = y0[0] * lattice[0] + y0[1] * lattice[1] + y0[2] * lattice[2] # iterate through all periodic images of the cell that share a face, # edge, or corner with the original cell for j in range(-1, 2): for k in range(-1, 2): for l in range(-1, 2): # check that this is not the original atom if i == atom_index and (j == k == l == 0): continue # calculate and store the bond vector from <atom_index> to # this site y = y0 + j * lattice[0] + k * lattice[1] + l * lattice[2] if norm(x0 - y) < max_bond_length: P.append(x0 - y) return P
def bond_candidates_sc(dis_cell, atom_type, max_bond_length, use_species=False, bonded_type=None): '''Extracts candidate bonds for atoms in a 3D-periodic supercell, which may contain topological defects ''' if bonded_type is None: # use <atom_type> sublattice bonded_type = atom_type # extract cell parameters of <discell> dis_sc = cry.Crystal() sinfo = gulp.parse_gulp(dis_cell, dis_sc) lattice = dis_sc.getLattice() Qpot = dict() for i in range(dis_sc.numberOfAtoms): if use_species: atomspecies = dis_sc[i].getSpecies() if atomspecies != atom_type: continue # extract coordinates of atom i (in \AA, a.u., etc.) xi = dis_sc[i].getCoordinates() xi = xi[0] * lattice[0] + xi[1] * lattice[1] + xi[2] * lattice[2] # search neighbouring atoms Qpoti = [] for j in range(dis_sc.numberOfAtoms): # extract coordinates and convert from fractional to cartesian xj0 = dis_sc[j].getCoordinates() xj0 = xj0[0] * lattice[0] + xj0[1] * lattice[1] + xj0[2] * lattice[ 2] # iterate through periodic images of atom j for l in range(-1, 2): for m in range(-1, 2): for n in range(-1, 2): if j == i and (l == 0 and m == 0 and n == 0): # original atom, skip continue xj = xj0 + l * lattice[0] + m * lattice[ 1] + n * lattice[2] if norm(xi - xj) < max_bond_length: Qpoti.append([j, xi - xj]) Qpot[i] = [xi, Qpoti] return Qpot
def make_supercell(self): #NEW '''Constructs the supercell that will be used in subsequent simulations. ''' base_sc = cry.Crystal() #NEW self.sysinfo = gulp.parse_gulp(self.control('dislocation_file'), base_sc) #NEW # extend normal to dislocation line self.supercell = cry.superConstructor(base_sc, np.array( [1., 1., self.control('n')])) #NEW
def main(): '''Make supercell. This is just a utility script for personal use, so we'll hard code all of the parameters. CURRENT_VERSION: GULP ''' options = input_options() args = options.parse_args() base_struc = cry.Crystal() ab_initio = False if args.prog == 'gulp': read_fn = gulp.parse_gulp write_fn = gulp.write_gulp elif args.prog == 'qe': read_fn = qe.parse_qe write_fn = qe.write_qe ab_initio = True elif args.prog == 'castep': read_fn = castep.parse_castep write_fn = castep.write_castep ab_initio = True else: raise ValueError( "{} is not a supported atomistic simulation code".format( args.prog)) sys_info = read_fn(args.unitcell, base_struc) if ab_initio: atm.scale_kpoints(sys_info['cards']['K_POINTS'], np.array(args.dims)) if args.prog == 'qe': qe.scale_nbands(sys_info['namelists']['&system'], np.array(args.dims)) supercell = cry.superConstructor(base_struc, np.array(args.dims)) outstream = open(args.supercell_name, 'w') write_fn(outstream, supercell, sys_info, defected=False, do_relax=args.relax, relax_type=args.calc_type, prop=args.prop, to_cart=False)
def main(): '''Read in and permute structure. ''' options = input_options() args = options.parse_args() base_struc = cry.Crystal() ab_initio = False if args.prog == 'gulp': read_fn = gulp.parse_gulp write_fn = gulp.write_gulp elif args.prog == 'qe': read_fn = qe.parse_qe write_fn = qe.write_qe ab_initio = True elif args.prog == 'castep': read_fn = castep.parse_castep write_fn = castep.write_castep ab_initio = True else: raise ValueError( "{} is not a supported atomistic simulation code".format( args.prog)) sys_info = read_fn(args.input_struc, base_struc) # check permutation and permute coordinates check_permutation(args.perm) permute_cell(base_struc, args.prog, args.perm) if ab_initio: # permute order of k-points permute_kgrid(sys_info['cards']['K_POINTS'], args.perm) # write to output if args.output_name: ostream = open(args.output_name, 'w') else: # overwrite input file ostream = open(args.input_struc, 'w') write_fn(ostream, base_struc, sys_info, to_cart=False) return
def excess_energy_edge(energy_grid, Edict, parse_fn, in_suffix): '''Calculate the excess energy due to the presence of a dislocation multipole by subtracting from the energy of a dislocated cell the energies of all its atoms calculated individually in the reference (i.e. undislocated) state. ''' for discell in energy_grid: # extract atoms in simulation cell temp_crystal = cry.Crystal() sysinfo = parse_fn('{}.{}'.format(discell[-1], in_suffix), temp_crystal) # calculate total energy of atoms in cell if no dislocations were present Eperf = 0. for atom in temp_crystal: Eperf += Edict[atom.getSpecies()] E_excess.append([discell[0], discell[1], discell[2] - Eperf]) return E_excess
def main(): '''Controller program. ''' if not sys.argv[1:]: # test to see if command line options have been passed. If they have # not, start interactive prompt args = manual_options() elif len(sys.argv[1:]) == 1: # assume that the user is providing the name of an input file args = read_control(sys.argv[1]) else: # parse arguments options = command_line_options() args = options.parse_args() if args.control: # read simulation parameters from control file args = read_control(args.control) else: pass # make sure that the atomic simulation code specified by the user is supported if args.prog.lower() in supported_codes: pass else: raise ValueError("{} is not a supported atomistic simulation code." + "Supported codes are: GULP; QE; CASTEP.") # extract unit cell and GULP run parameters from file <cell_name> ab_initio = False if 'gulp' == args.prog.lower(): unit_cell = cry.Crystal() parse_fn = gulp.parse_gulp elif 'qe' == args.prog.lower(): unit_cell = cry.Crystal() parse_fn = qe.parse_qe ab_initio = True elif 'castep' == args.prog.lower(): unit_cell = castep.CastepCrystal() parse_fn = castep.parse_castep ab_initio = True sys_info = parse_fn(args.cell_name, unit_cell) # if the calculation uses an ab initio solver, scale the k-point grid if ab_initio: if 'qe' == args.prog.lower(): atm.scale_kpoints(sys_info['cards']['K_POINTS'], np.array([1., 1., args.n])) qe.scale_nbands(sys_info['namelists']['&system'], np.array([1., 1., args.n])) elif 'castep' == args.prog.lower(): atm.scale_kpoints(sys_info['mp_kgrid'], np.array([1., 1., args.n])) # shift origin of cell unit_cell.translate_cell(np.array([0., 0., -1*args.shift]), modulo=True) # select output mode appropriate for the atomistic calculator used, together # with the correct suffix for the input files (may be better to let the # output functions determine the suffix?) if 'gulp' == args.prog.lower(): write_fn = gulp.write_gulp suffix = 'gin' relax = None elif 'qe' == args.prog.lower(): write_fn = qe.write_qe suffix = 'in' relax = 'relax' elif 'castep' == args.prog.lower(): write_fn = castep.write_castep suffix = 'cell' relax = None # make the slab and construct the gamma surface/line new_slab = gsf.make_slab(unit_cell, args.n, args.vac, d_fix=args.d_fix, free_atoms=args.free_atoms) if args.simulation_type == 'gsurface': limits = (args.max_x, args.max_y) gsf.gamma_surface(new_slab, args.res, write_fn, sys_info, suffix=suffix, limits=limits, basename=args.sim_name, vacuum=args.vac, relax=relax) # run the calculations, if an executable has been provided. Otherwise, # assume that that the input files will be transferred to another machine # and run by the user. if not (args.progexec is None): # extract increments N, M = gsf.gs_sampling(new_slab.getLattice(), args.res, limits) for n in range(0, N+1): for m in range(0, M+1): print("Relaxing cell with generalized stacking fault vector" + " ({}, {})...".format(n, m), end="") basename = '{}.{}.{}'.format(args.sim_name, n, m) if 'gulp' == args.prog.lower(): gulp.run_gulp(args.progexec, basename) elif 'qe' == args.prog.lower(): qe.run_qe(args.progexec, basename) elif 'castep' == args.prog.lower(): castep.run_castep(args.progexec, basename) print("complete.") else: pass elif args.simulation_type == 'gline': gsf.gamma_line(new_slab, np.array(args.line_vec), args.res, write_fn, sys_info, suffix=suffix, limits=args.max_x, basename=args.sim_name, vacuum=args.vac, relax=relax) if not (args.progexec is None): # extract limits N = gsf.gl_sampling(new_slab.getLattice(), resolution=args.res, vector=np.array(args.line_vec), limits=args.max_x) # run calculations for n in range(0, N+1): print("Relaxing cell {}...".format(n), end="") basename = '{}.{}'.format(args.sim_name, n) if 'gulp' == args.prog.lower(): gulp.run_gulp(args.progexec, basename) elif 'qe' == args.prog.lower(): qe.run_qe(args.progexec, basename) elif 'castep' == args.prog.lower(): castep.run_castep(args.progexec, basename) print("complete.") else: pass else: pass