def epsilon_infty(pot, at, deltafield=0.001, zerotol=1e-5): """ Calculate dielectric tensor. Potential must be polarisable and allow an external electic field to be applied. """ dielectric_tensor = fzeros((3,3)) celldip = fzeros((3,3)) # Calculation with no applied field pot.calc(at, force=True, restart=True, applied_efield=False) celldip0 = at.dipoles.sum(axis=2)/BOHR at.add_property('ext_efield', 0., n_cols=3) # Now we apply field along each of x,y,z in turn, and # calculate overall cell dipole moment for i in frange(3): at.ext_efield[:] = 0.0 at.ext_efield[i,:] += deltafield/(BOHR/HARTREE) pot.calc(at, force=True, applied_efield=True) celldip[:,i] = at.dipoles.sum(axis=2)/BOHR dielectric_tensor[:,i] = celldip[:,i] - celldip0 dielectric_tensor = 4.0*PI*dielectric_tensor/deltafield/(at.cell_volume()/BOHR**3) + fidentity(3) at.ext_efield[:] = 0.0 dielectric_tensor[dielectric_tensor < zerotol] = 0.0 return dielectric_tensor
def __getitem__(self, i): if not self.initialised: if self is self.parent.hysteretic_connect: self.calc_connect_hysteretic(self.parent) else: self.calc_connect(self.parent) distance = farray(0.0) diff = fzeros(3) cosines = fzeros(3) shift = fzeros(3, dtype=np.int32) res = [] if not get_fortran_indexing(): i = i + 1 # convert to 1-based indexing for n in frange(self.n_neighbours(i)): j = self.neighbour(self.parent, i, n, distance, diff, cosines, shift) if not get_fortran_indexing(): j = j - 1 res.append(NeighbourInfo(j, distance, diff, cosines, shift)) if get_fortran_indexing(): res = farray(res) # to give 1-based indexing return res
def find_mapping(at_in, vectors, lattice_constant, tol=1e-4): """ Find a mapping between pairs of atoms displaced by a given vector (or by one of a number of given vectors). """ class FoundMapping: pass mapping = fzeros(at_in.n, dtype=np.int32) at = at_in.copy() # cutoff should be larger than all displacment vectors vlen = [farray(vector).norm()*lattice_constant for vector in vectors] at.set_cutoff(max(vlen)+0.5) print 'cutoff = ', at.cutoff at.calc_connect() for i in at.indices: try: for neighb in at.neighbours[i]: for vector in vectors: if ((neighb.diff/lattice_constant - vector)**2).sum() < tol: print i, neighb.j, vector mapping[i] = neighb.j raise FoundMapping # corresponding atom is off the edge, so map atom onto itself print i, 'not found!' mapping[i] = i except FoundMapping: continue return mapping
def calc_effective_charge_vectors(a, born_tensor): effective_charge = fzeros(3) for i in frange(a.n): p_norm = a.phonon[i]/np.sqrt(np.dot(a.phonon[i],a.phonon[i])) disp = (p_norm/np.sqrt(ElementMass[str(a.species[i])]*MASSCONVERT))/BOHR print disp effective_charge = effective_charge + dot(born_tensor[:,:,i], disp) return effective_charge
def calc(self, at, grad=False, args_str=None, **calc_args): """ Calculates all descriptors of this type in the Atoms object, and gradients if grad=True. Results can be accessed dictionary- or attribute-style; 'descriptor' contains descriptor values, 'descriptor_index_0based' contains the 0-based indices of the central atom(s) in each descriptor, 'grad' contains gradients, 'grad_index_0based' contains indices to gradients (descriptor, atom). Cutoffs and gradients of cutoffs are also returned. """ if args_str is None: args_str = dict_to_args_str(calc_args) n_index = fzeros(1, 'i') n_desc, n_cross = self.descriptor_sizes(at, n_index=n_index) n_index = n_index[1] data = fzeros((self.n_dim, n_desc)) cutoff = fzeros(n_desc) data_index = fzeros((n_index, n_desc), 'i') if grad: # n_cross is number of cross-terms, proportional to n_desc data_grad = fzeros((self.n_dim, 3, n_cross)) data_grad_index = fzeros((2, n_cross), 'i') cutoff_grad = fzeros((3, n_cross)) if not grad: RawDescriptor.calc(self, at, descriptor_out=data, covariance_cutoff=cutoff, descriptor_index=data_index, args_str=args_str) else: RawDescriptor.calc(self, at, descriptor_out=data, covariance_cutoff=cutoff, descriptor_index=data_index, grad_descriptor_out=data_grad, grad_descriptor_index=data_grad_index, grad_covariance_cutoff=cutoff_grad, args_str=args_str) results = DescriptorCalcResult() convert = lambda data: np.array(data).T results.descriptor = convert(data) results.cutoff = convert(cutoff) results.descriptor_index_0based = convert(data_index - 1) if grad: results.grad = convert(data_grad) results.grad_index_0based = convert(data_grad_index - 1) results.cutoff_grad = convert(cutoff_grad) return results
def screened_effective_charge(born, eps): """ Compute screened effective charge tensor from Born and dielectric tensors """ screened = fzeros((3,3,3,3)) for i in frange(3): for j in frange(3): for k in frange(3): for l in frange(3): if eps[k,l] == 0.0: continue screened[i,j,k,l] = born[i,j]/np.sqrt(eps[k,l]) return screened
def force_test(at, p, dx=1e-4): """ Compare analyric and numeric forces for the Potential `p` with Atoms `at` Finite difference derivates are calculated by moving each atom by `dx`. """ analytic_f = fzeros((3, at.n)) p.calc(at, force=analytic_f) num_f = fzeros((3, at.n)) ep, em = farray(0.0), farray(0.0) for i in frange(at.n): for j in (1, 2, 3): ap = at.copy() ap.pos[j, i] += dx p.calc(ap, energy=ep) print 'e+', j, i, ep ap.pos[j, i] -= 2.0 * dx p.calc(ap, energy=em) print 'e-', j, i, em num_f[j, i] = -(ep - em) / (2 * dx) return analytic_f, num_f, analytic_f - num_f
def born_effective_charge(pot, at0, dx=1e-5, args_str=None): """ Calculate Born effective charges for all atoms in at0 Potential must be polarizable, i.e. compute dipole moments. """ born_tensor = fzeros((3,3,at0.n)) restart = True for i in frange(at0.n): for j in frange(3): at = at0.copy() at.pos[j,i] -= dx at.calc_connect() pot.calc(at, force=True, restart=restart, args_str=args_str) restart = True dip1 = fzeros(3) for k in frange(at.n): dip1 += at.dipoles[k] + at.charge[k]*at.pos[:,k] at = at0.copy() at.pos[j,i] += dx at.calc_connect() pot.calc(at, force=True, restart=restart, args_str=args_str) dip2 = fzeros(3) for k in frange(at.n): dip2 += at.dipoles[k] + at.charge[k]*at.pos[:,k] born_tensor[:,j,i] = (dip2 - dip1)/(dx*2.0) return born_tensor
def unpack_reftraj_output_str_to_results(data): lines = data.strip().split('\n') nstep = int(lines[0]) natoms = int(lines[1]) energy = float(lines[2]) force = farray(np.loadtxt(lines[3:-1])).T v6 = [float(v) for v in lines[-1].split()] virial = fzeros((3, 3)) virial[1, 1], virial[2, 2], virial[3, 3], virial[1, 2], virial[2, 3], virial[1, 3] = v6 virial[2, 1] = virial[1, 2] virial[3, 2] = virial[2, 3] virial[3, 1] = virial[1, 3] return (nstep, natoms, energy, force, virial)
def read_header(xdatcar): comment = p.readline().rstrip() if comment.find("Direct configuration") == 0: # is the beginning of some positions return (None, None, comment, None, None, None) l = p.readline().strip(); lc_factor=float(l) l = p.readline().strip(); a1 = np.real(l.split()) l = p.readline().strip(); a2 = np.real(l.split()) l = p.readline().strip(); a3 = np.real(l.split()) l = p.readline().strip(); at_species = l.split() try: ns = [ int(n) for n in at_species ] no_species_read = True except: no_species_read = False if species is not None: at_species = species.split() have_species = True else: if no_species_read: have_species = False for i in range(len(ns)): at_species[i] = ("%d" % (i+1)) else: have_species = True if not no_species_read: l = p.readline().strip(); ns = [ int(n) for n in l.split() ] n=0 for i in range(len(ns)): n += ns[i] lat = fzeros( (3,3) ) lat[:,1] = a1[0:3] lat[:,2] = a2[0:3] lat[:,3] = a3[0:3] lat *= lc_factor return (lat, n, comment, ns, at_species, have_species)
def callback(at): at.set_calculator(calculator) if at.calc_energy: at.params['energy'] = at.get_potential_energy() if at.calc_local_e: at.add_property('local_e', 0.0, overwrite=True) at.local_e[:] = at.get_potential_energies() if at.calc_force: at.add_property('force', 0.0, n_cols=3, overwrite=True) at.force[:] = at.get_forces().T if at.calc_virial: stress = at.get_stress() virial = fzeros((3, 3)) virial[:, :] = stress_matrix(-stress * at.get_volume()) at.params['virial'] = virial if at.calc_local_virial: stresses = at.get_stresses() at.add_property('local_virial', 0.0, n_cols=9, overwrite=True) lv = at.local_virial.view(np.ndarray) vol_per_atom = at.get_volume() / len(at) lv[...] = -stresses.T.reshape((len(at), 9)) * vol_per_atom
def rotation_matrix(unit, y, z=None, x=None, tol=1e-5): """Return 3x3 matrix rotation matrix defining a crack with open surface defined by the plane `y`=(l,m.n) or (h,k,i,l), and either crack tip line `z` or crack propagation direction `x`.""" axes = fzeros((3, 3)) y = MillerIndex(y).as3() if (x is None and z is None) or (x is not None and z is not None): raise ValueError('exactly one of x and z must be non-null') axes[:, 2] = np.dot(unit.g.T, y) # plane defined by y=(lmn) if z is not None: z = MillerIndex(z).as3() axes[:, 3] = np.dot(unit.lattice, z) # line defined by z=[pqr] axes[:, 2] = axes[:, 2] / axes[:, 2].norm() axes[:, 3] = axes[:, 3] / axes[:, 3].norm() if abs(np.dot(axes[:, 2], axes[:, 3])) > tol: raise ValueError( 'y (%s) and z (%s) directions are not perpendicular' % (y, z)) axes[:, 1] = np.cross(axes[:, 2], axes[:, 3]) else: x = MillerIndex(x).as3() axes[:, 1] = np.dot(unit.lattice, x) axes[:, 2] = axes[:, 2] / axes[:, 2].norm() axes[:, 1] = axes[:, 1] / axes[:, 1].norm() if abs(np.dot(axes[:, 2], axes[:, 3])) > tol: raise ValueError( 'y (%s) and x (%s) directions are not perpendicular' % (y, x)) axes[:, 3] = np.cross(axes[:, 1], axes[:, 2]) # Rotation matrix is transpose of axes matrix return axes.T
from quippy.farray import fzeros infile = sys.argv[1] basename = os.path.splitext(infile)[0] a = Atoms(infile) grid_size = 0.25 min_void_size = 2.0 nx = int((a.pos[1,:].max() - a.pos[1,:].min())/grid_size) ny = int((a.pos[2,:].max() - a.pos[2,:].min())/grid_size) nz = int((a.pos[3,:].max() - a.pos[3,:].min())/grid_size) n = nx*ny*nz grid = fzeros((3, n)) radii = fzeros(n) void_analysis(a, grid_size, min_void_size, grid, radii) extent = (grid[:,-1] - grid[:,1]) grid = np.array(grid.reshape(3, nx, ny, nz)) radii = np.array(radii.reshape((nx, ny, nz))) a.write(basename+'.cube', data=radii, extent=extent, origin=[a.pos[1,:].min(), a.pos[2,:].min(), a.pos[3,:].min()]) voids = (radii > min_void_size).nonzero() void_positions = []
def orthorhombic_slab(at, tol=1e-5, min_nrep=1, max_nrep=5, graphics=False, rot=None, periodicity=None, vacuum=None, shift=None, verbose=True): """Try to construct an orthorhombic cell equivalent to the primitive cell `at`, using supercells up to at most `max_nrep` repeats. Symmetry must be exact within a tolerance of `tol`. If `rot` is not None, we first transform `at` by the rotation matrix `rot`. The optional argument `periodicity` can be used to fix the periodicity one or more directions. It should be a three component vector with value zero in the unconstrained directions. The vector `vacuum` can be used to add vacuum in one or more directions. `shift` is a three component vector which can be used to shift the positions in the final cell. """ def atoms_near_plane(at, n, d, tol=1e-5): """Return a list of atoms within a distance `tol` of the plane defined by np.dot(n, at.pos) == d""" pd = np.dot(n, at.pos) - d return (abs(pd) < tol).nonzero()[0] def sort_by_distance(at, ref_atom, dir, candidates): """Return a copy of `candidates` sorted by perpendicular distance from `ref_atom` in direction `dir`""" distances_candidates = zip( [at.pos[dir, i] - at.pos[dir, ref_atom] for i in candidates], candidates) distances_candidates.sort() return [p for (d, p) in distances_candidates] def orthorhombic_box(at): """Return a copy of `at` in an orthorhombic box surrounded by vacuum""" at = at.copy() at.map_into_cell() at.set_lattice( [[2.0 * (at.pos[1, :].max() - at.pos[1, :].min()), 0.0, 0.0], [0.0, 2.0 * (at.pos[2, :].max() - at.pos[2, :].min()), 0.0], [0.0, 0.0, 2.0 * (at.pos[3, :].max() - at.pos[3, :].min())]], scale_positions=False) at.map_into_cell() return at def discard_outliers(at, indices, dirs, keep_fraction=0.5): """Return a copy of `indices` with the atoms with fractional coordinates along directions in `dirs` outside +/-`keep_fraction`/2 excluded. Lattice used is close fitting, `at.lattice`/2.""" g = np.linalg.inv(at.lattice / 2) t = np.dot(g, at.pos[:, indices]) return indices[np.logical_and( t[dirs, :] >= -keep_fraction / 2.0, t[dirs, :] < keep_fraction / 2.0).all(axis=1)] def check_candidate_plane(at, ref_plane, cand_plane, dirs, verbose=False, label=''): """Check whether in-plane displacements of atoms listed in `ref_plane` match those of `cand_plane` in directions given by `dirs`""" # Which pair of planes has more atoms, reference or candidate? if len(ref_plane) < len(cand_plane): smaller = ref_plane larger = cand_plane else: smaller = cand_plane larger = ref_plane matches = {} for j in smaller: for k in larger: if at.z[k] == at.z[j] and abs(at.pos[dirs, k] - at.pos[dirs, j]).max() < tol: matches[j] = k break if verbose: print ' ', label, len(matches), '/', len(smaller), 'matches' return len(matches) == len(smaller) if rot is not None: at = transform(at, rot) xyz = fidentity(3) nrep = min_nrep - 1 max_dist = fzeros(3) if periodicity is not None: periodicity = farray(periodicity) periodicity = dict( zip((periodicity != 0).nonzero()[0], periodicity[periodicity != 0])) else: periodicity = {} if verbose: for (dir, p) in periodicity.iteritems(): print 'Periodicity in direction %d fixed at %f' % (dir, p) if graphics: import atomeye viewer = atomeye.AtomEyeViewer() while sorted(periodicity.keys()) != [1, 2, 3]: nrep += 1 if nrep > max_nrep: raise ValueError('Maximum size of supercell (%d) exceeded' % max_nrep) if verbose: print '\n\nSupercell %d' % nrep sup = supercell(at, nrep, nrep, nrep) box = orthorhombic_box(sup) box.pos[:] = box.pos - np.tile(box.pos.mean(axis=2), [box.n, 1]).T for dir in set([1, 2, 3]) - set(periodicity.keys()): if verbose: print ' Direction %d' % dir other_dirs = list(set([1, 2, 3]) - set([dir])) pos_index = zip(box.pos[dir, :], frange(box.n)) pos_index.sort() # Find a pair of planes while pos_index: ref_pos1, ref_atom1 = pos_index.pop(0) # Find atom to define second plane while pos_index: ref_pos2, ref_atom2 = pos_index.pop(0) if abs(ref_pos2 - ref_pos1) > tol: break else: continue ref_plane1 = atoms_near_plane(box, xyz[:, dir], box.pos[dir, ref_atom1], tol) ref_plane2 = atoms_near_plane(box, xyz[:, dir], box.pos[dir, ref_atom2], tol) # Only keep reference atoms in the centre of the cell ref_plane1 = discard_outliers(box, ref_plane1, other_dirs) ref_plane2 = discard_outliers(box, ref_plane2, other_dirs) if len(ref_plane1) > 2 and len(ref_plane2) > 2: # Now we've got two planes, both with more than two atoms in them break else: # Used up all atoms without finding two planes if verbose: print ' No valid reference planes found.\n' continue if verbose: print ' Reference plane #1 through atom %d ' % ref_atom1 print ' Reference plane #2 through atom %d at distance %r\n' % ( ref_atom2, ref_pos2 - ref_pos1) if graphics: highlight = fzeros(box.n) highlight[ref_plane1] = 1 highlight[ref_plane2] = 2 box.add_property('highlight', highlight, overwrite=True) viewer.show(box, 'highlight') viewer.wait() raw_input('continue') candidates = [ i for i in frange(box.n) if box.pos[dir, i] > box.pos[dir, ref_atom2] + max_dist[dir] + tol ] candidates = sort_by_distance(box, ref_atom1, dir, candidates) while candidates: cand1 = candidates.pop(0) max_dist[dir] = box.pos[dir, cand1] - box.pos[dir, ref_atom1] if verbose: print ' Trying plane through atom %d distance %r' % ( cand1, max_dist[dir]) cand_plane1 = atoms_near_plane(box, xyz[:, dir], box.pos[dir, cand1], tol) for cand2 in sort_by_distance( box, ref_atom1, dir, set(candidates) - set(cand_plane1)): if abs((box.pos[dir, cand2] - box.pos[dir, cand1]) - (box.pos[dir, ref_atom2] - box.pos[dir, ref_atom1])) < tol: if verbose: print ' Found pair to plane, passing through atom %d distance %r ' % ( cand2, box.pos[dir, cand2] - box.pos[dir, ref_atom1]) break else: if verbose: print ' Cannot find second candidate plane.\n' candidates = sort_by_distance( box, ref_atom1, dir, set(candidates) - set(cand_plane1)) continue if graphics: highlight[cand_plane1] = 3 box.highlight[:] = highlight viewer.show(box, 'highlight') viewer.wait() cand_plane2 = atoms_near_plane(box, xyz[:, dir], box.pos[dir, cand2], tol) if graphics: highlight[cand_plane2] = 4 box.highlight[:] = highlight viewer.show(box, 'highlight') viewer.wait() highlight[cand_plane1] = 0 highlight[cand_plane2] = 0 # Remove cand_plane1 from list of candidates candidates = sort_by_distance( box, ref_atom1, dir, set(candidates) - set(cand_plane1)) # Check ref_plane1 against cand_plane1 and ref_plane2 against cand_plane2 in directions # listed in other_dirs match1 = check_candidate_plane(box, ref_plane1, cand_plane1, other_dirs, verbose, 'Plane #1:') match2 = check_candidate_plane(box, ref_plane2, cand_plane2, other_dirs, verbose, 'Plane #2:') if match1 and match2: periodicity[dir] = box.pos[dir, cand1] - box.pos[dir, ref_atom1] if verbose: print '\n Periodicity in direction %d is %f\n' % ( dir, box.pos[dir, cand1] - box.pos[dir, ref_atom1]) if graphics: highlight[cand_plane1] = 3 highlight[cand_plane2] = 3 box.highlight[:] = highlight viewer.show(box, 'highlight') viewer.wait() raw_input('continue...') break if graphics: raw_input('continue...') else: # Failed to find match for direction dir continue # Finally, construct new cell by selecting atoms within first unit cell lattice = farray(np.diag([periodicity[1], periodicity[2], periodicity[3]])) g = np.linalg.inv(lattice) nrepx, nrepy, nrepz = fit_box_in_cell(periodicity[1], periodicity[2], periodicity[3], at.lattice) sup = supercell(at, nrepx, nrepy, nrepz) sup.map_into_cell() # small shift to avoid coincidental cell alignments delta = np.tile([0.01, 0.02, 0.03], [sup.n, 1]).T if shift is not None and vacuum is not None: delta = delta + np.tile(shift, [sup.n, 1]).T t = np.dot(g, sup.pos) + delta orthorhombic = sup.select(np.logical_and(t >= -0.5, t < 0.5).all(axis=1)) if vacuum: lattice = farray(np.diag(lattice.diagonal() + vacuum)) if shift is not None and vacuum is None: if verbose: print 'Shifting positions by %s' % np.dot(lattice, shift) orthorhombic.pos += np.tile(np.dot(lattice, shift), [orthorhombic.n, 1]).T orthorhombic.set_lattice(lattice, scale_positions=False) orthorhombic.map_into_cell() return orthorhombic
def calculate(self, atoms, properties, system_changes): Calculator.calculate(self, atoms, properties, system_changes) # we will do the calculation in place, to minimise number of copies, # unless atoms is not a quippy Atoms if isinstance(atoms, Atoms): self.quippy_atoms = weakref.proxy(atoms) else: potlog.debug( 'Potential atoms is not quippy.Atoms instance, copy forced!') self.quippy_atoms = Atoms(atoms) initial_arrays = self.quippy_atoms.arrays.keys() initial_info = self.quippy_atoms.info.keys() if properties is None: properties = ['energy', 'forces', 'stress'] # Add any default properties properties = set(self.get_default_properties() + properties) if len(properties) == 0: raise RuntimeError('Nothing to calculate') if not self.calculation_required(atoms, properties): return args_map = { 'energy': { 'energy': None }, 'energies': { 'local_energy': None }, 'forces': { 'force': None }, 'stress': { 'virial': None }, 'numeric_forces': { 'force': 'numeric_force', 'force_using_fd': True, 'force_fd_delta': 1.0e-5 }, 'stresses': { 'local_virial': None }, 'elastic_constants': {}, 'unrelaxed_elastic_constants': {} } # list of properties that require a call to Potential.calc() calc_properties = [ 'energy', 'energies', 'forces', 'numeric_forces', 'stress', 'stresses' ] # list of other properties we know how to calculate other_properties = ['elastic_constants', 'unrelaxed_elastic_constants'] calc_args = {} calc_required = False for property in properties: if property in calc_properties: calc_required = True calc_args.update(args_map[property]) elif property not in other_properties: raise RuntimeError( "Don't know how to calculate property '%s'" % property) if calc_required: self.calc(self.quippy_atoms, args_str=dict_to_args_str(calc_args)) if 'energy' in properties: self.results['energy'] = float(self.quippy_atoms.energy) if 'energies' in properties: self.results['energies'] = self.quippy_atoms.local_energy.copy( ).view(np.ndarray) if 'forces' in properties: self.results['forces'] = self.quippy_atoms.force.copy().view( np.ndarray).T if 'numeric_forces' in properties: self.results[ 'numeric_forces'] = self.quippy_atoms.numeric_force.copy( ).view(np.ndarray).T if 'stress' in properties: stress = -self.quippy_atoms.virial.copy().view( np.ndarray) / self.quippy_atoms.get_volume() # convert to 6-element array in Voigt order self.results['stress'] = np.array([ stress[0, 0], stress[1, 1], stress[2, 2], stress[1, 2], stress[0, 2], stress[0, 1] ]) if 'stresses' in properties: lv = np.array(self.quippy_atoms.local_virial) # make a copy vol_per_atom = self.get( 'vol_per_atom', self.quippy_atoms.get_volume() / len(atoms)) if isinstance(vol_per_atom, basestring): vol_per_atom = self.quippy_atoms.arrays[vol_per_atom] self.results['stresses'] = -lv.T.reshape( (len(atoms), 3, 3), order='F') / vol_per_atom if 'elastic_constants' in properties: cij_dx = self.get('cij_dx', 1e-2) cij = fzeros((6, 6)) self.calc_elastic_constants(self.quippy_atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c=cij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): cij = cij.view(np.ndarray) self.results['elastic_constants'] = cij if 'unrelaxed_elastic_constants' in properties: cij_dx = self.get('cij_dx', 1e-2) c0ij = fzeros((6, 6)) self.calc_elastic_constants(self.quippy_atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c0=c0ij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): c0ij = c0ij.view(np.ndarray) self.results['unrelaxed_elastic_constants'] = c0ij # copy back any additional output data to results dictionary skip_keys = ['energy', 'force', 'virial', 'numeric_force'] for key in self.quippy_atoms.arrays.keys(): if key not in initial_arrays and key not in skip_keys: self.results[key] = self.quippy_atoms.arrays[key].copy() for key in self.quippy_atoms.info.keys(): if key not in initial_info and key not in skip_keys: if isinstance(self.quippy_atoms.info[key], np.ndarray): self.results[key] = self.quippy_atoms.info[key].copy() else: self.results[key] = self.quippy_atoms.info[key]
def CP2KOutputReader(fh, module=None, type_map=None, kind_map=None, format=None): # mapping from run type to (default module index, list of available module) run_types = { 'QS': ['QUICKSTEP'], 'QMMM': ['FIST', 'QM/MM', 'QUICKSTEP'], 'MM': ['FIST'] } filename, lines = read_text_file(fh) run_type = cp2k_run_type(cp2k_output=lines) if type_map is None: type_map = {} if kind_map is None: kind_map = {} try: available_modules = run_types[run_type] except KeyError: raise ValueError('Unknown CP2K run type %s' % run_type) if module is None: module = available_modules[0] try: cell_index = available_modules.index(module) except ValueError: raise ValueError("Don't know how to read module %s from file %s" % (module, filename)) cell_lines = [ i for i, line in enumerate(lines) if line.startswith(" CELL| Vector a") ] if cell_lines == []: raise ValueError("Cannot find cell in file %s" % filename) try: cell_line = cell_lines[cell_index] except IndexError: raise ValueError( "Cannot find cell with index %d in file %s for module %s" % (cell_index, filename, module)) lattice = fzeros((3, 3)) for i in [0, 1, 2]: lattice[:, i + 1] = [float(c) for c in lines[cell_line + i].split()[4:7]] try: start_line = lines.index( " MODULE %s: ATOMIC COORDINATES IN angstrom\n" % module) except ValueError: raise ValueError( "Cannot find atomic positions for module %s in file %s" % (module, filename)) kinds = [] species = [] Zs = [] pos = [] masses = [] Zeffs = [] types = [] qeffs = [] for line in lines[start_line + 4:]: if line.strip() == '': break if module == 'FIST': atom, kind, typ, x, y, z, qeff, mass = line.split() types.append(typ) Z = type_map.get(typ, 0) kind = int(kind) if Z == 0: Z = kind_map.get(kind, 0) Zs.append(Z) qeffs.append(float(qeff)) else: atom, kind, sp, Z, x, y, z, Zeff, mass = line.split() species.append(sp) Zs.append(int(Z)) Zeffs.append(float(Zeff)) kinds.append(int(kind)) pos.append([float(x), float(y), float(z)]) masses.append(float(mass)) at = Atoms(n=len(kinds), lattice=lattice) at.pos[...] = farray(pos).T at.set_atoms(Zs) at.add_property('mass', farray(masses) * MASSCONVERT) at.add_property('kind', kinds) if module == 'FIST': at.add_property('type', ' ' * TABLE_STRING_LENGTH) at.add_property('qm', False) at.qm[:] = (at.type.stripstrings() == '_QM_') | (at.type.stripstrings() == '_LNK') at.type[...] = s2a(types, TABLE_STRING_LENGTH) at.add_property('qeff', qeffs) else: at.species[...] = s2a(species, TABLE_STRING_LENGTH) at.add_property('zeff', Zeffs) yield at
if opt.tetra: print 'Converting topology from Si-O-Si to Si-Si' si_si, si_o, si_si_cutoff = tetrahedra_to_bonds(q) if opt.si_si_cutoff is None: opt.si_si_cutoff = si_si_cutoff dm = distance_map(q, q.n, q.n, diameter=diameter) print 'Distance map diameter %d' % diameter print 'Max ring size %d' % opt.max_ring_size assert (diameter >= opt.max_ring_size) print 'Using Si-Si cutoff of %.3f' % opt.si_si_cutoff ring_counts = fzeros(opt.max_ring_size, dtype=np.int32) count_sp_rings(q, opt.si_si_cutoff, dm, opt.max_ring_size, ring_counts) rings_per_si = np.array(ring_counts.astype(float) / (q.z == 14).sum()) # do it again saving the rings n_rings = ring_counts.sum() ring_counts[:] = 0 rings_array = fzeros((opt.max_ring_size + 1, n_rings), dtype=np.int32) dr = fzeros((3, opt.max_ring_size, n_rings)) count_sp_rings(q, opt.si_si_cutoff, dm, opt.max_ring_size, ring_counts, rings_out=rings_array,
def VASP_POSCAR_Reader(poscar, species=None, format=None): """Read a configuration from a VASP POSCAR file. Following POSCAR, optionally also read a trajectory from an OUTCAR file.""" p = open(poscar, 'r') comment = p.readline().rstrip() l = p.readline().strip() lc_factor = float(l) l = p.readline().strip() a1 = np.real(l.split()) l = p.readline().strip() a2 = np.real(l.split()) l = p.readline().strip() a3 = np.real(l.split()) l = p.readline().strip() at_species = l.split() try: ns = [int(n) for n in at_species] no_species = True except: no_species = False have_species = True if (no_species): for i in range(len(ns)): if (species is not None): species_cli = species.split() at_species[i] = species_cli[i - 1] else: have_species = False at_species[i] = ("%d" % (i + 1)) else: l = p.readline().strip() ns = [int(n) for n in l.split()] l = p.readline().strip() if (re.compile("^\s*s", re.IGNORECASE).match(l)): dyn_type = l coord_type = p.readline().strip() else: coord_type = l n = 0 for i in range(len(ns)): n += ns[i] lat = fzeros((3, 3)) lat[:, 1] = a1[0:3] lat[:, 2] = a2[0:3] lat[:, 3] = a3[0:3] lat *= lc_factor at = Atoms(n=n, lattice=lat) if (len(comment) > 0): at.params['VASP_Comment'] = comment coord_direct = re.compile("^\s*d", re.IGNORECASE).match(coord_type) ii = 1 for ti in range(len(ns)): for i in range(ns[ti]): l = p.readline().strip() pos = np.array(l.split()[0:3], float) if (coord_direct): at.pos[:, ii] = np.dot(at.lattice[:, :], pos[:]) else: at.pos[:, ii] = pos[:] * lc_factor at.species[:, ii] = at_species[ti] ii += 1 if (have_species): at.set_zs() else: at.Z[:] = [int("".join(n)) for n in at.species[:]] yield at
def VASP_POSCAR_Reader(outcar, species=None, format=None): """Read a configuration from a VASP OUTCAR file.""" if (outcar == 'stdin' or outcar == '-'): p = sys.stdin else: p = open(outcar, 'r') re_comment = re.compile("\s*POSCAR:\s*(.+)") re_potcar = re.compile("\s*POTCAR:\s*\S+\s+(\S+)") re_n_atoms = re.compile("\s*ions per type =\s*((?:\d+\s*)*)") energy_i = -1 at_i = -1 lat_i = -1 elements = [] n_at = -1 at_cur = None for lr in p: l = lr.rstrip() if (n_at <= 0): # parse header type things m = re_comment.match(l) if (m is not None): VASP_Comment = m.group(1) # print "got VASP_Comment '%s'" % VASP_Comment m = re_potcar.match(l) if (m is not None): elements.append(m.group(1)) m = re_n_atoms.match(l) if (m is not None): # print "got ions per type, groups are:" # print m.groups() lat = fzeros((3, 3)) n_types = [int(f) for f in m.group(1).split()] n_at = sum(n_types) at = Atoms(n=n_at, latttice=lat) i_at = 0 for type_i in range(len(n_types)): for j in range(n_types[type_i]): i_at += 1 # print "set species of atom %d to '%s'" % (i_at, elements[type_i]) at.species[i_at] = elements[type_i] at.set_zs() else: # parse per-config lattice/pos/force if (l.find("direct lattice vectors") >= 0): # get ready to read lattice vectors at_cur = at.copy() lat_cur = fzeros((3, 3)) lat_i = 1 elif (lat_i >= 1 and lat_i <= 3): # read lattice vectors lat_cur[:, lat_i] = [ float(r) for r in l.replace("-", " -").split()[0:3] ] lat_i += 1 elif (l.find("TOTAL-FORCE (eV/Angst)") >= 0): # get ready to read atomic positions and forces if (not hasattr(at_cur, "force")): at_cur.add_property("force", 0.0, n_cols=3) at_i = 1 p.next() elif (at_i >= 1 and at_i <= at_cur.n): # read atomic positions and forces pos_force = [ float(r) for r in l.replace("-", " -").split()[0:6] ] at_cur.pos[:, at_i] = pos_force[0:3] at_cur.force[:, at_i] = pos_force[3:6] at_i += 1 elif (l.find("free energy") >= 0): # get ready to read energy at_cur.params['Energy'] = float(l.split()[4]) energy_i = 1 p.next() elif (energy_i == 1): # read energy # print "energy(sigma->0) line" # print l.split() at_cur.params['Energy_sigma_to_zero'] = float(l.split()[6]) energy_i += 1 yield at_cur if (at_cur is not None and at_i == at_cur.n): # at end of configuration, set lattice at_cur.set_lattice(lat_cur, False) __all__ = ['VASP_POSCAR_Reader', 'VASP_OUTCAR_Reader', 'VASPWriter']
def CP2KDirectoryReader(run_dir, at_ref=None, proj='quip', calc_qm_charges=None, calc_virial=False, out_i=None, qm_vacuum=6.0, run_suffix='_extended', format=None): if at_ref is None: filepot_xyz = os.path.join(run_dir, 'filepot.xyz') if not os.path.exists(filepot_xyz): # try looking up one level filepot_xyz = os.path.join(run_dir, '../filepot.xyz') if os.path.exists(filepot_xyz): at_ref = Atoms(filepot_xyz) else: at_ref = Atoms(os.path.join(run_dir, 'cp2k_output.out'), format='cp2k_output') at = at_ref.copy() cp2k_output_filename, cp2k_output = read_text_file( os.path.join(run_dir, 'cp2k_output.out')) cp2k_params = CP2KInputHeader( os.path.join(run_dir, 'cp2k_input.inp.header')) at.params.update(cp2k_params) run_type = cp2k_run_type(cp2k_output=cp2k_output, cp2k_input_header=cp2k_params) try: cluster_mark = getattr(at, 'cluster_mark' + run_suffix) qm_list_a = ((cluster_mark != HYBRID_NO_MARK).nonzero()[0]).astype( np.int32) except AttributeError: qm_list_a = fzeros(0, dtype=np.int32) if calc_qm_charges is None: calc_qm_charges = '' try: cur_qmmm_qm_abc = [ float(cp2k_params['QMMM_ABC_X']), float(cp2k_params['QMMM_ABC_Y']), float(cp2k_params['QMMM_ABC_Z']) ] except KeyError: if 'QM_cell' + run_suffix in at.params: cur_qmmm_qm_abc = at.params['QM_cell' + run_suffix] else: cur_qmmm_qm_abc = qmmm_qm_abc(at, qm_list_a, qm_vacuum) quip_cp2k_at = Atoms(os.path.join(run_dir, 'quip_cp2k.xyz')) rev_sort_index_file = os.path.join(run_dir, '../quip_rev_sort_index') fields = [int(x) for x in open(rev_sort_index_file).read().split()] rev_sort_index = farray(fields, dtype=np.int32) #verbosity_push(PRINT_SILENT) cp2k_energy, cp2k_force = read_output(quip_cp2k_at, qm_list_a, cur_qmmm_qm_abc, run_dir, proj, calc_qm_charges, calc_virial, True, 3, at.n, out_i) #verbosity_pop() qm_list = None if os.path.exists(os.path.join(run_dir, 'cp2k_input.qmmm_qm_kind')): qm_kind_grep_cmd = "grep MM_INDEX %s/cp2k_input.qmmm_qm_kind | awk '{print $2}'" % run_dir qm_list = [int(i) for i in os.popen(qm_kind_grep_cmd).read().split()] if qm_list is not None: if run_type == 'QMMM': reordering_index = getattr(at, 'reordering_index', None) at.add_property('qm', False, overwrite=True) if reordering_index is not None: qm_list = reordering_index[qm_list] at.qm[qm_list] = True elif run_type == 'QS': at.add_property('qm_orig_index', 0, overwrite=True) for i, qm_at in fenumerate(qm_list): at.qm_orig_index[i] = sort_index[qm_at] at.add_property('force', cp2k_force, overwrite=True) at.params['energy'] = cp2k_energy yield at
def calculate(self, atoms, quantities=None): """ Perform a calculation of `quantities` for `atoms` using this Potential. Automatically determines if a new calculation is required or if previous results are still appliciable (i.e. if the atoms haven't moved since last call) Called internally by :meth:`get_potential_energy`, :meth:`get_forces`, etc. """ if quantities is None: quantities = ['energy', 'forces', 'stress'] # Add any default quantities quantities = set(self.get_default_quantities() + quantities) if len(quantities) == 0: raise RuntimeError('Nothing to calculate') if not self.calculation_required(atoms, quantities): return args_map = { 'energy': { 'energy': None }, 'energies': { 'local_energy': None }, 'forces': { 'force': None }, 'stress': { 'virial': None }, 'numeric_forces': { 'force': 'numeric_force', 'force_using_fd': True, 'force_fd_delta': 1.0e-5 }, 'stresses': { 'local_virial': None }, 'elastic_constants': {}, 'unrelaxed_elastic_constants': {} } # list of quantities that require a call to Potential.calc() calc_quantities = [ 'energy', 'energies', 'forces', 'numeric_forces', 'stress', 'stresses' ] # list of other quantities we know how to calculate other_quantities = ['elastic_constants', 'unrelaxed_elastic_constants'] calc_args = {} calc_required = False for quantity in quantities: if quantity in calc_quantities: calc_required = True calc_args.update(args_map[quantity]) elif quantity not in other_quantities: raise RuntimeError( "Don't know how to calculate quantity '%s'" % quantity) if calc_required: self.calc(self.atoms, args_str=dict_to_args_str(calc_args)) if 'energy' in quantities: self.energy = float(self.atoms.energy) if 'energies' in quantities: self.energies = self.atoms.local_energy.view(np.ndarray) if 'forces' in quantities: self.forces = self.atoms.force.view(np.ndarray).T if 'numeric_forces' in quantities: self.numeric_forces = self.atoms.numeric_force.view(np.ndarray).T if 'stress' in quantities: stress = -self.atoms.virial.view( np.ndarray) / self.atoms.get_volume() # convert to 6-element array in Voigt order self.stress = np.array([ stress[0, 0], stress[1, 1], stress[2, 2], stress[1, 2], stress[0, 2], stress[0, 1] ]) if 'stresses' in quantities: lv = np.array(self.atoms.local_virial) # make a copy vol_per_atom = self.get('vol_per_atom', self.atoms.get_volume() / len(atoms)) if isinstance(vol_per_atom, basestring): vol_per_atom = self.atoms.arrays[vol_per_atom] self.stresses = -lv.T.reshape( (len(atoms), 3, 3), order='F') / vol_per_atom if 'elastic_constants' in quantities: cij_dx = self.get('cij_dx', 1e-2) cij = fzeros((6, 6)) self.calc_elastic_constants(self.atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c=cij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): cij = cij.view(np.ndarray) self.elastic_constants = cij if 'unrelaxed_elastic_constants' in quantities: cij_dx = self.get('cij_dx', 1e-2) c0ij = fzeros((6, 6)) self.calc_elastic_constants(self.atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c0=c0ij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): c0ij = c0ij.view(np.ndarray) self.unrelaxed_elastic_constants = c0ij