def _init_derived_bonds(self): # 1-bond neighbors self.neighs1 = dict((i,set([])) for i in xrange(self.natom)) for i0, i1 in self.bonds: self.neighs1[i0].add(i1) self.neighs1[i1].add(i0) # 2-bond neighbors self.neighs2 = dict((i,set([])) for i in xrange(self.natom)) for i0, n0 in self.neighs1.iteritems(): for i1 in n0: for i2 in self.neighs1[i1]: # Require that there are no shorter paths than two bonds between # i0 and i2. Also avoid duplicates. if i2 > i0 and i2 not in self.neighs1[i0]: self.neighs2[i0].add(i2) self.neighs2[i2].add(i0) # 3-bond neighbors self.neighs3 = dict((i,set([])) for i in xrange(self.natom)) for i0, n0 in self.neighs1.iteritems(): for i1 in n0: for i3 in self.neighs2[i1]: # Require that there are no shorter paths than three bonds # between i0 and i3. Also avoid duplicates. if i3 != i0 and i3 not in self.neighs1[i0] and i3 not in self.neighs2[i0]: self.neighs3[i0].add(i3) self.neighs3[i3].add(i0) # report some basic stuff on screen if log.do_medium: log('Analysis of the bonds:') bond_types = {} for i0, i1 in self.bonds: key = tuple(sorted([self.numbers[i0], self.numbers[i1]])) bond_types[key] = bond_types.get(key, 0) + 1 log.hline() log(' First Second Count') for (num0, num1), count in sorted(bond_types.iteritems()): log('%6i %6i %5i' % (num0, num1, count)) log.hline() log.blank() log('Analysis of the neighbors:') log.hline() log('Number of first neighbors: %6i' % (sum(len(n) for n in self.neighs1.itervalues())/2)) log('Number of second neighbors: %6i' % (sum(len(n) for n in self.neighs2.itervalues())/2)) log('Number of third neighbors: %6i' % (sum(len(n) for n in self.neighs3.itervalues())/2)) # Collect all types of 'environments' for each element. This is # useful to double check the bonds envs = {} for i0 in xrange(self.natom): num0 = self.numbers[i0] nnums = tuple(sorted(self.numbers[i1] for i1 in self.neighs1[i0])) key = (num0, nnums) envs[key] = envs.get(key, 0)+1 # Print the environments on screen log.hline() log('Element Neighboring elements Count') for (num0, nnums), count in sorted(envs.iteritems()): log('%7i %20s %5i' % (num0, ','.join(str(num1) for num1 in nnums), count)) log.hline() log.blank()
def _init_derived_scopes(self): if self.scope_ids is None: if len(self.scopes) != self.natom: raise TypeError('When the scope_ids are derived automatically, the length of the scopes list must match the number of atoms.') lookup = {} scopes = [] self.scope_ids = np.zeros(self.natom, int) for i in xrange(self.natom): scope = self.scopes[i] scope_id = lookup.get(scope) if scope_id is None: scope_id = len(scopes) scopes.append(scope) lookup[scope] = scope_id self.scope_ids[i] = scope_id self.scopes = scopes for scope in self.scopes: check_name(scope) # check the range of the ids if self.scope_ids.min() != 0 or self.scope_ids.max() != len(self.scopes)-1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following scopes are present in the system:') log.hline() log(' Scope ID Number of atoms') log.hline() for scope_id, scope in enumerate(self.scopes): log('%22s %3i %3i' % (scope, scope_id, (self.scope_ids==scope_id).sum())) log.hline() log.blank()
def _init_derived_scopes(self): if self.scope_ids is None: if len(self.scopes) != self.natom: raise TypeError( 'When the scope_ids are derived automatically, the length of the scopes list must match the number of atoms.' ) lookup = {} scopes = [] self.scope_ids = np.zeros(self.natom, int) for i in range(self.natom): scope = self.scopes[i] scope_id = lookup.get(scope) if scope_id is None: scope_id = len(scopes) scopes.append(scope) lookup[scope] = scope_id self.scope_ids[i] = scope_id self.scopes = scopes for scope in self.scopes: check_name(scope) # check the range of the ids if self.scope_ids.min() != 0 or self.scope_ids.max() != len( self.scopes) - 1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following scopes are present in the system:') log.hline() log(' Scope ID Number of atoms') log.hline() for scope_id, scope in enumerate(self.scopes): log('%22s %3i %3i' % (scope, scope_id, (self.scope_ids == scope_id).sum())) log.hline() log.blank()
def _init_log(self): if log.do_medium: log('Unit cell') log.hline() log('Number of periodic dimensions: %i' % self.cell.nvec) lengths, angles = self.cell.parameters names = 'abc' for i in xrange(len(lengths)): log('Cell parameter %5s: %10s' % (names[i], log.length(lengths[i]))) names = 'alpha', 'beta', 'gamma' for i in xrange(len(angles)): log('Cell parameter %5s: %10s' % (names[i], log.angle(angles[i]))) log.hline() log.blank()
def _init_log(self): if log.do_medium: log('Unit cell') log.hline() log('Number of periodic dimensions: %i' % self.cell.nvec) lengths, angles = self.cell.parameters names = 'abc' for i in range(len(lengths)): log('Cell parameter %5s: %10s' % (names[i], log.length(lengths[i]))) names = 'alpha', 'beta', 'gamma' for i in range(len(angles)): log('Cell parameter %5s: %10s' % (names[i], log.angle(angles[i]))) log.hline() log.blank()
def _init_derived_ffatypes(self): if self.ffatype_ids is None: if len(self.ffatypes) != self.natom: raise TypeError('When the ffatype_ids are derived automatically, the length of the ffatypes list must match the number of atoms.') lookup = {} ffatypes = [] self.ffatype_ids = np.zeros(self.natom, int) for i in xrange(self.natom): if self.scope_ids is None: ffatype = self.ffatypes[i] key = ffatype, None else: scope_id = self.scope_ids[i] ffatype = self.ffatypes[i] key = ffatype, scope_id ffatype_id = lookup.get(key) if ffatype_id is None: ffatype_id = len(ffatypes) ffatypes.append(ffatype) lookup[key] = ffatype_id self.ffatype_ids[i] = ffatype_id self.ffatypes = ffatypes for ffatype in self.ffatypes: check_name(ffatype) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(self.ffatypes)-1: raise ValueError('The ffatype_ids have incorrect bounds.') # differentiate ffatype_ids if the same ffatype_id is used in different # scopes if self.scopes is not None: self.ffatype_id_to_scope_id = {} fixed_fids = {} for i in xrange(self.natom): fid = self.ffatype_ids[i] sid = self.ffatype_id_to_scope_id.get(fid) if sid is None: self.ffatype_id_to_scope_id[fid] = self.scope_ids[i] elif sid != self.scope_ids[i]: # We found the same ffatype_id in a different scope_id. This # must be fixed. First check if we have already a new # scope_id ready sid = self.scope_ids[i] new_fid = fixed_fids.get((sid, fid)) if new_fid is None: # No previous new fid create, do it now. new_fid = len(self.ffatypes) # Copy the ffatype label self.ffatypes.append(self.ffatypes[fid]) # Keep track of the new fid fixed_fids[(sid, fid)] = new_fid if log.do_warning: log.warn('Atoms with type ID %i in scope %s were changed to type ID %i.' % (fid, self.scopes[sid], new_fid)) # Apply the new fid self.ffatype_ids[i] = new_fid self.ffatype_id_to_scope_id[new_fid] = sid # Turn the ffatypes in the scopes into array if self.ffatypes is not None: self.ffatypes = np.array(self.ffatypes, copy=False) if self.scopes is not None: self.scopes = np.array(self.scopes, copy=False) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len(self.ffatypes)-1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following atom types are present in the system:') log.hline() if self.scopes is None: log(' Atom type ID Number of atoms') log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): log('%22s %3i %3i' % (ffatype, ffatype_id, (self.ffatype_ids==ffatype_id).sum())) else: log(' Scope Atom type ID Number of atoms') log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): scope = self.scopes[self.ffatype_id_to_scope_id[ffatype_id]] log('%22s %22s %3i %3i' % (scope, ffatype, ffatype_id, (self.ffatype_ids==ffatype_id).sum())) log.hline() log.blank()
def check_mic(self, system): '''Check if each scale2 and scale3 are uniquely defined. **Arguments:** system An instance of the system class, i.e. the one that is used to create this scaling object. This check is done by constructing for each scaled pair, all possible bond paths between the two atoms. For each path, the bond vectors (after applying the minimum image convention) are added. If for a given pair, these sums of bond vectors differ between all possible paths, the differences are expanded in cell vectors which can be used to construct a proper supercell in which scale2 and scale3 pairs are all uniquely defined. ''' if system.cell.nvec == 0: return troubles = False with log.section('SCALING'): for i0, i1, scale, nbond in self.stab: if nbond == 1: continue all_deltas = [] paths = [] for path in iter_paths(system, i0, i1, nbond): delta_total = 0 for j0 in range(nbond): j1 = j0 + 1 delta = system.pos[path[j0]] - system.pos[path[j1]] system.cell.mic(delta) delta_total += delta all_deltas.append(delta_total) paths.append(path) all_deltas = np.array(all_deltas) if abs(all_deltas.mean(axis=0) - all_deltas).max() > 1e-10: troubles = True if log.do_warning: log.warn('Troublesome pair scaling detected.') log('The following bond paths connect the same pair of ' 'atoms, yet the relative vectors are different.') for ipath in range(len(paths)): log('%2i %27s %10s %10s %10s' % ( ipath, ','.join(str(index) for index in paths[ipath]), log.length(all_deltas[ipath, 0]), log.length(all_deltas[ipath, 1]), log.length(all_deltas[ipath, 2]), )) log('Differences between relative vectors in fractional ' 'coordinates:') for ipath0 in range(1, len(paths)): for ipath1 in range(ipath0): diff = all_deltas[ipath0] - all_deltas[ipath1] diff_frac = np.dot(system.cell.gvecs, diff) log('%2i %2i %10.4f %10.4f %10.4f' % (ipath0, ipath1, diff_frac[0], diff_frac[1], diff_frac[2])) log.blank() if troubles: raise AssertionError( 'Due to the small spacing between some crystal planes, the scaling of non-bonding interactions will not work properly. Use a supercell to avoid this problem.' )
def check_mic(self, system): '''Check if each scale2 and scale3 are uniquely defined. **Arguments:** system An instance of the system class, i.e. the one that is used to create this scaling object. This check is done by constructing for each scaled pair, all possible bond paths between the two atoms. For each path, the bond vectors (after applying the minimum image convention) are added. If for a given pair, these sums of bond vectors differ between all possible paths, the differences are expanded in cell vectors which can be used to construct a proper supercell in which scale2 and scale3 pairs are all uniquely defined. ''' if system.cell.nvec == 0: return troubles = False with log.section('SCALING'): for i0, i1, scale, nbond in self.stab: if nbond == 1: continue all_deltas = [] paths = [] for path in iter_paths(system, i0, i1, nbond): delta_total = 0 for j0 in xrange(nbond): j1 = j0 + 1 delta = system.pos[path[j0]] - system.pos[path[j1]] system.cell.mic(delta) delta_total += delta all_deltas.append(delta_total) paths.append(path) all_deltas = np.array(all_deltas) if abs(all_deltas.mean(axis=0) - all_deltas).max() > 1e-10: troubles = True if log.do_warning: log.warn('Troublesome pair scaling detected.') log('The following bond paths connect the same pair of ' 'atoms, yet the relative vectors are different.') for ipath in xrange(len(paths)): log('%2i %27s %10s %10s %10s' % ( ipath, ','.join(str(index) for index in paths[ipath]), log.length(all_deltas[ipath,0]), log.length(all_deltas[ipath,1]), log.length(all_deltas[ipath,2]), )) log('Differences between relative vectors in fractional ' 'coordinates:') for ipath0 in xrange(1, len(paths)): for ipath1 in xrange(ipath0): diff = all_deltas[ipath0] - all_deltas[ipath1] diff_frac = np.dot(system.cell.gvecs, diff) log('%2i %2i %10.4f %10.4f %10.4f' % ( ipath0, ipath1, diff_frac[0], diff_frac[1], diff_frac[2] )) log.blank() if troubles: raise AssertionError('Due to the small spacing between some crystal planes, the scaling of non-bonding interactions will not work properly. Use a supercell to avoid this problem.')
def _init_derived_ffatypes(self): if self.ffatype_ids is None: if len(self.ffatypes) != self.natom: raise TypeError( 'When the ffatype_ids are derived automatically, the length of the ffatypes list must match the number of atoms.' ) lookup = {} ffatypes = [] self.ffatype_ids = np.zeros(self.natom, int) for i in range(self.natom): if self.scope_ids is None: ffatype = self.ffatypes[i] key = ffatype, None else: scope_id = self.scope_ids[i] ffatype = self.ffatypes[i] key = ffatype, scope_id ffatype_id = lookup.get(key) if ffatype_id is None: ffatype_id = len(ffatypes) ffatypes.append(ffatype) lookup[key] = ffatype_id self.ffatype_ids[i] = ffatype_id self.ffatypes = ffatypes for ffatype in self.ffatypes: check_name(ffatype) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len( self.ffatypes) - 1: raise ValueError('The ffatype_ids have incorrect bounds.') # differentiate ffatype_ids if the same ffatype_id is used in different # scopes if self.scopes is not None: self.ffatype_id_to_scope_id = {} fixed_fids = {} for i in range(self.natom): fid = self.ffatype_ids[i] sid = self.ffatype_id_to_scope_id.get(fid) if sid is None: self.ffatype_id_to_scope_id[fid] = self.scope_ids[i] elif sid != self.scope_ids[i]: # We found the same ffatype_id in a different scope_id. This # must be fixed. First check if we have already a new # scope_id ready sid = self.scope_ids[i] new_fid = fixed_fids.get((sid, fid)) if new_fid is None: # No previous new fid create, do it now. new_fid = len(self.ffatypes) # Copy the ffatype label self.ffatypes.append(self.ffatypes[fid]) # Keep track of the new fid fixed_fids[(sid, fid)] = new_fid if log.do_warning: log.warn( 'Atoms with type ID %i in scope %s were changed to type ID %i.' % (fid, self.scopes[sid], new_fid)) # Apply the new fid self.ffatype_ids[i] = new_fid self.ffatype_id_to_scope_id[new_fid] = sid # Turn the ffatypes in the scopes into array if self.ffatypes is not None: self.ffatypes = np.array(self.ffatypes, copy=False) if self.scopes is not None: self.scopes = np.array(self.scopes, copy=False) # check the range of the ids if self.ffatype_ids.min() != 0 or self.ffatype_ids.max() != len( self.ffatypes) - 1: raise ValueError('The ffatype_ids have incorrect bounds.') if log.do_medium: log('The following atom types are present in the system:') log.hline() if self.scopes is None: log(' Atom type ID Number of atoms') log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): log('%22s %3i %3i' % (ffatype, ffatype_id, (self.ffatype_ids == ffatype_id).sum())) else: log(' Scope Atom type ID Number of atoms' ) log.hline() for ffatype_id, ffatype in enumerate(self.ffatypes): scope = self.scopes[ self.ffatype_id_to_scope_id[ffatype_id]] log('%22s %22s %3i %3i' % (scope, ffatype, ffatype_id, (self.ffatype_ids == ffatype_id).sum())) log.hline() log.blank()
def _init_derived_bonds(self): # 1-bond neighbors self.neighs1 = dict((i, set([])) for i in range(self.natom)) for i0, i1 in self.bonds: self.neighs1[i0].add(i1) self.neighs1[i1].add(i0) # 2-bond neighbors self.neighs2 = dict((i, set([])) for i in range(self.natom)) for i0, n0 in self.neighs1.items(): for i1 in n0: for i2 in self.neighs1[i1]: # Require that there are no shorter paths than two bonds between # i0 and i2. Also avoid duplicates. if i2 > i0 and i2 not in self.neighs1[i0]: self.neighs2[i0].add(i2) self.neighs2[i2].add(i0) # 3-bond neighbors self.neighs3 = dict((i, set([])) for i in range(self.natom)) for i0, n0 in self.neighs1.items(): for i1 in n0: for i3 in self.neighs2[i1]: # Require that there are no shorter paths than three bonds # between i0 and i3. Also avoid duplicates. if i3 != i0 and i3 not in self.neighs1[ i0] and i3 not in self.neighs2[i0]: self.neighs3[i0].add(i3) self.neighs3[i3].add(i0) # 4-bond neighbors self.neighs4 = dict((i, set([])) for i in range(self.natom)) for i0, n0 in self.neighs1.items(): for i1 in n0: for i4 in self.neighs3[i1]: # Require that there are no shorter paths than three bonds # between i0 and i4. Also avoid duplicates. if i4 != i0 and i4 not in self.neighs1[ i0] and i4 not in self.neighs2[ i0] and i4 not in self.neighs3[i0]: self.neighs4[i0].add(i4) self.neighs4[i4].add(i0) # report some basic stuff on screen if log.do_medium: log('Analysis of the bonds:') bond_types = {} for i0, i1 in self.bonds: key = tuple(sorted([self.numbers[i0], self.numbers[i1]])) bond_types[key] = bond_types.get(key, 0) + 1 log.hline() log(' First Second Count') for (num0, num1), count in sorted(bond_types.items()): log('%6i %6i %5i' % (num0, num1, count)) log.hline() log.blank() log('Analysis of the neighbors:') log.hline() log('Number of first neighbors: %6i' % (sum(len(n) for n in self.neighs1.values()) // 2)) log('Number of second neighbors: %6i' % (sum(len(n) for n in self.neighs2.values()) // 2)) log('Number of third neighbors: %6i' % (sum(len(n) for n in self.neighs3.values()) // 2)) # Collect all types of 'environments' for each element. This is # useful to double check the bonds envs = {} for i0 in range(self.natom): num0 = self.numbers[i0] nnums = tuple( sorted(self.numbers[i1] for i1 in self.neighs1[i0])) key = (num0, nnums) envs[key] = envs.get(key, 0) + 1 # Print the environments on screen log.hline() log('Element Neighboring elements Count') for (num0, nnums), count in sorted(envs.items()): log('%7i %20s %5i' % (num0, ','.join(str(num1) for num1 in nnums), count)) log.hline() log.blank()