def do(self): cache = context.application.cache parent = cache.node unit_cell = None if isinstance(parent, UnitCell): unit_cell = parent Atom = context.application.plugins.get_node("Atom") atoms = [] coordinates = [] for child in parent.children: if isinstance(child, Atom): atoms.append(child) coordinates.append(child.transformation.t) coordinates = numpy.array(coordinates) cf = ClusterFactory() for i0, i1, delta, distance in PairSearchIntra( coordinates, periodic.max_radius * 0.4, unit_cell): atom0 = atoms[i0] atom1 = atoms[i1] if atom0.number == atom1.number: if distance < periodic[atom0.number].vdw_radius * 0.4: cf.add_related(atom0, atom1) clusters = cf.get_clusters() del cf # define the new singles singles = [] for cluster in clusters: number = iter(cluster.items).next().number single = Atom(name="Single " + periodic[number].symbol) single.set_number(number) singles.append((single, list(cluster.items))) # calculate their positions for single, overlappers in singles: # in the following algorithm, we suppose that the cluster of # atoms is small compared to the parent's periodic sizes # (if the parent is a periodic system) first_pos = overlappers[0].transformation.t delta_to_mean = numpy.zeros(3, float) for atom in overlappers[1:]: delta_to_mean += parent.shortest_vector(atom.transformation.t - first_pos) delta_to_mean /= float(len(overlappers)) single.set_transformation(Translation(first_pos + delta_to_mean)) # modify the model for single, overlappers in singles: lowest_index = min([atom.get_index() for atom in overlappers]) primitive.Add(single, parent, index=lowest_index) for atom in overlappers: while len(atom.references) > 0: primitive.SetTarget(atom.references[0], single) primitive.Delete(atom)
def do(self): cache = context.application.cache parent = cache.node unit_cell = None if isinstance(parent, UnitCell): unit_cell = parent Atom = context.application.plugins.get_node("Atom") atoms = [] coordinates = [] for child in parent.children: if isinstance(child, Atom): atoms.append(child) coordinates.append(child.transformation.t) coordinates = numpy.array(coordinates) cf = ClusterFactory() for i0, i1, delta, distance in PairSearchIntra(coordinates, periodic.max_radius*0.4, unit_cell): atom0 = atoms[i0] atom1 = atoms[i1] if atom0.number == atom1.number: if distance < periodic[atom0.number].vdw_radius*0.4: cf.add_related(atom0, atom1) clusters = cf.get_clusters() del cf # define the new singles singles = [] for cluster in clusters: number = iter(cluster.items).next().number single = Atom(name="Single " + periodic[number].symbol) single.set_number(number) singles.append((single, list(cluster.items))) # calculate their positions for single, overlappers in singles: # in the following algorithm, we suppose that the cluster of # atoms is small compared to the parent's periodic sizes # (if the parent is a periodic system) first_pos = overlappers[0].transformation.t delta_to_mean = numpy.zeros(3, float) for atom in overlappers[1:]: delta_to_mean += parent.shortest_vector(atom.transformation.t - first_pos) delta_to_mean /= float(len(overlappers)) single.set_transformation(Translation(first_pos + delta_to_mean)) # modify the model for single, overlappers in singles: lowest_index = min([atom.get_index() for atom in overlappers]) primitive.Add(single, parent, index=lowest_index) for atom in overlappers: while len(atom.references) > 0: primitive.SetTarget(atom.references[0], single) primitive.Delete(atom)
def remove_duplicate(self, threshold=0.1): '''Return a system object in which the duplicate atoms and bonds are removed. **Optional argument:** threshold The minimum distance between two atoms that are supposed to be different. When it makes sense, properties of overlapping atoms are averaged out. In other cases, the atom with the lowest index in a cluster of overlapping atoms defines the new value of a property. ''' # compute distances ndist = (self.natom*(self.natom-1))/2 if ndist == 0: # single atom systems, go home ... return dists = np.zeros(ndist) self.cell.compute_distances(dists, self.pos) # find clusters of overlapping atoms from molmod import ClusterFactory cf = ClusterFactory() counter = 0 for i0 in xrange(self.natom): for i1 in xrange(i0): if dists[counter] < threshold: cf.add_related(i0, i1) counter += 1 clusters = [c.items for c in cf.get_clusters()] # make a mapping from new to old atoms newold = {} oldnew = {} counter = 0 for cluster in clusters: # all merged atoms come first newold[counter] = sorted(cluster) for item in cluster: oldnew[item] = counter counter += 1 if len(clusters) > 0: old_reduced = set.union(*clusters) else: old_reduced = [] for item in xrange(self.natom): # all remaining atoms follow if item not in old_reduced: newold[counter] = [item] oldnew[item] = counter counter += 1 natom = len(newold) def reduce_int_array(old): if old is None: return None else: new = np.zeros(natom, old.dtype) for inew, iolds in newold.iteritems(): new[inew] = old[iolds[0]] return new def reduce_float_array(old): if old is None: return None else: new = np.zeros(natom, old.dtype) for inew, iolds in newold.iteritems(): new[inew] = old[iolds].mean() return new def reduce_float_matrix(old): '''Reduce array with dim=2''' if old is None: return None else: new = np.zeros((natom,np.shape(old)[1]), old.dtype) for inew, iolds in newold.iteritems(): new[inew] = old[iolds].mean(axis=0) return new # trivial cases numbers = reduce_int_array(self.numbers) scope_ids = reduce_int_array(self.scope_ids) ffatype_ids = reduce_int_array(self.ffatype_ids) charges = reduce_float_array(self.charges) radii = reduce_float_array(self.radii) dipoles = reduce_float_matrix(self.dipoles) radii2 = reduce_float_array(self.radii2) masses = reduce_float_array(self.masses) # create averaged positions pos = np.zeros((natom, 3), float) for inew, iolds in newold.iteritems(): # move to the same image oldposs = self.pos[iolds].copy() assert oldposs.ndim == 2 ref = oldposs[0] for oldpos in oldposs[1:]: delta = oldpos-ref self.cell.mic(delta) oldpos[:] = delta+ref # compute mean position pos[inew] = oldposs.mean(axis=0) # create reduced list of bonds if self.bonds is None: bonds = None else: bonds = set((oldnew[ia], oldnew[ib]) for ia, ib in self.bonds) bonds = np.array([bond for bond in bonds]) return self.__class__(numbers, pos, self.scopes, scope_ids, self.ffatypes, ffatype_ids, bonds, self.cell.rvecs, charges, radii, dipoles, radii2, masses)
def remove_duplicate(self, threshold=0.1): '''Return a system object in which the duplicate atoms and bonds are removed. **Optional argument:** threshold The minimum distance between two atoms that are supposed to be different. When it makes sense, properties of overlapping atoms are averaged out. In other cases, the atom with the lowest index in a cluster of overlapping atoms defines the new value of a property. ''' # compute distances ndist = (self.natom * (self.natom - 1)) // 2 if ndist == 0: # single atom systems, go home ... return dists = np.zeros(ndist) self.cell.compute_distances(dists, self.pos) # find clusters of overlapping atoms from molmod import ClusterFactory cf = ClusterFactory() counter = 0 for i0 in range(self.natom): for i1 in range(i0): if dists[counter] < threshold: cf.add_related(i0, i1) counter += 1 clusters = [c.items for c in cf.get_clusters()] # make a mapping from new to old atoms newold = {} oldnew = {} counter = 0 for cluster in clusters: # all merged atoms come first newold[counter] = sorted(cluster) for item in cluster: oldnew[item] = counter counter += 1 if len(clusters) > 0: old_reduced = set.union(*clusters) else: old_reduced = [] for item in range(self.natom): # all remaining atoms follow if item not in old_reduced: newold[counter] = [item] oldnew[item] = counter counter += 1 natom = len(newold) def reduce_int_array(old): if old is None: return None else: new = np.zeros(natom, old.dtype) for inew, iolds in newold.items(): new[inew] = old[iolds[0]] return new def reduce_float_array(old): if old is None: return None else: new = np.zeros(natom, old.dtype) for inew, iolds in newold.items(): new[inew] = old[iolds].mean() return new def reduce_float_matrix(old): '''Reduce array with dim=2''' if old is None: return None else: new = np.zeros((natom, np.shape(old)[1]), old.dtype) for inew, iolds in newold.items(): new[inew] = old[iolds].mean(axis=0) return new # trivial cases numbers = reduce_int_array(self.numbers) scope_ids = reduce_int_array(self.scope_ids) ffatype_ids = reduce_int_array(self.ffatype_ids) charges = reduce_float_array(self.charges) radii = reduce_float_array(self.radii) valence_charges = reduce_float_array(self.valence_charges) dipoles = reduce_float_matrix(self.dipoles) radii2 = reduce_float_array(self.radii2) masses = reduce_float_array(self.masses) # create averaged positions pos = np.zeros((natom, 3), float) for inew, iolds in newold.items(): # move to the same image oldposs = self.pos[iolds].copy() assert oldposs.ndim == 2 ref = oldposs[0] for oldpos in oldposs[1:]: delta = oldpos - ref self.cell.mic(delta) oldpos[:] = delta + ref # compute mean position pos[inew] = oldposs.mean(axis=0) # create reduced list of bonds if self.bonds is None: bonds = None else: bonds = set((oldnew[ia], oldnew[ib]) for ia, ib in self.bonds) bonds = np.array([bond for bond in bonds]) return self.__class__(numbers, pos, self.scopes, scope_ids, self.ffatypes, ffatype_ids, bonds, self.cell.rvecs, charges, radii, valence_charges, dipoles, radii2, masses)