def _set_voronoi_bond_orders(self): ''' approximate bond order, from purely geometric voronoi solid angles' returns def bond_order(cd): ''' p = self.positions bond_orders = [] o_index = p.shape[1] / 2 n_images = p.shape[1] for i in range(p.shape[2]): #[atomj * image, axis] vt = VoronoiTess(p[:, :, i].reshape((-1, 3))) bo = np.zeros([p.shape[0] * p.shape[1]] * 2) vts = np.array(vt.vertices) for (k1, k2), v in vt.ridges.items(): if (k1 % n_images != o_index) and (k2 % n_images != o_index): continue if -10.101 in vts[v]: continue val = solid_angle(vt.points[k1], vts[v]) bo[k1, k2] = val bo[k2, k1] = val nshape = (p.shape[0], p.shape[0], p.shape[1], 1) bond_orders.append(bo[o_index::n_images].reshape(nshape)) self._voronoi_bond_orders = np.concatenate(bond_orders, -1)
def setup_voronoi_list(self, indices, voronoi_cutoff): """ Set up of the voronoi list of neighbours by calling qhull :param indices: indices of the sites for which the Voronoi is needed :param voronoi_cutoff: Voronoi cutoff for the search of neighbours :raise RuntimeError: If an infinite vertex is found in the voronoi construction """ self.voronoi_list = [None] * len(self.structure) logging.info('Getting all neighbors in structure') struct_neighbors = self.structure.get_all_neighbors(voronoi_cutoff, include_index=True) t1 = time.clock() logging.info('Setting up Voronoi list :') for jj, isite in enumerate(indices): logging.info( ' - Voronoi analysis for site #{:d} ({:d}/{:d})'.format( isite, jj + 1, len(indices))) site = self.structure[isite] neighbors1 = [(site, 0.0, isite)] neighbors1.extend(struct_neighbors[isite]) distances = [i[1] for i in sorted(neighbors1, key=lambda s: s[1])] neighbors = [i[0] for i in sorted(neighbors1, key=lambda s: s[1])] qvoronoi_input = [s.coords for s in neighbors] voro = VoronoiTess(qvoronoi_input) all_vertices = voro.vertices results = [] maxangle = 0.0 mindist = 10000.0 for nn, vind in list(voro.ridges.items()): if 0 in nn: if 0 in vind: raise RuntimeError("This structure is pathological," " infinite vertex in the voronoi " "construction") facets = [all_vertices[i] for i in vind] try: sa = solid_angle(site.coords, facets) except ValueError: sa = my_solid_angle(site.coords, facets) maxangle = max([sa, maxangle]) mindist = min([mindist, distances[nn[1]]]) for iii, sss in enumerate(self.structure): if neighbors[nn[1]].is_periodic_image(sss): myindex = iii break results.append((neighbors[nn[1]], { 'angle': sa, 'distance': distances[nn[1]], 'index': myindex })) for (nn, dd) in results: dd['weighted_angle'] = dd['angle'] / maxangle dd['weighted_distance'] = dd['distance'] / mindist self.voronoi_list[isite] = results t2 = time.clock() logging.info('Voronoi list set up in {:.2f} seconds'.format(t2 - t1))
def setup_voronoi_list(self, indices, voronoi_cutoff): """ Set up of the voronoi list of neighbours by calling qhull :param indices: indices of the sites for which the Voronoi is needed :param voronoi_cutoff: Voronoi cutoff for the search of neighbours :raise RuntimeError: If an infinite vertex is found in the voronoi construction """ self.voronoi_list2 = [None] * len(self.structure) logging.info('Getting all neighbors in structure') struct_neighbors = self.structure.get_all_neighbors(voronoi_cutoff, include_index=True) t1 = time.clock() logging.info('Setting up Voronoi list :') for jj, isite in enumerate(indices): logging.info(' - Voronoi analysis for site #{:d} ({:d}/{:d})'.format(isite, jj+1, len(indices))) site = self.structure[isite] neighbors1 = [(site, 0.0, isite)] neighbors1.extend(struct_neighbors[isite]) distances = [i[1] for i in sorted(neighbors1, key=lambda s: s[1])] neighbors = [i[0] for i in sorted(neighbors1, key=lambda s: s[1])] qvoronoi_input = [s.coords for s in neighbors] voro = Voronoi(points=qvoronoi_input, qhull_options="o Fv") all_vertices = voro.vertices results2 = [] maxangle = 0.0 mindist = 10000.0 for iridge, ridge_points in enumerate(voro.ridge_points): if 0 in ridge_points: ridge_vertices_indices = voro.ridge_vertices[iridge] if -1 in ridge_vertices_indices: raise RuntimeError("This structure is pathological," " infinite vertex in the voronoi " "construction") ridge_point2 = max(ridge_points) facets = [all_vertices[i] for i in ridge_vertices_indices] try: sa = solid_angle(site.coords, facets) except ValueError: sa = my_solid_angle(site.coords, facets) maxangle = max([sa, maxangle]) mindist = min([mindist, distances[ridge_point2]]) for iii, sss in enumerate(self.structure): if neighbors[ridge_point2].is_periodic_image(sss): myindex = iii break results2.append({'site': neighbors[ridge_point2], 'angle': sa, 'distance': distances[ridge_point2], 'index': myindex}) for dd in results2: dd['normalized_angle'] = dd['angle'] / maxangle dd['normalized_distance'] = dd['distance'] / mindist self.voronoi_list2[isite] = results2 t2 = time.clock() logging.info('Voronoi list set up in {:.2f} seconds'.format(t2-t1))
def test_solid_angle(self): center = [2.294508207929496, 4.4078057081404, 2.299997773791287] coords = [[1.627286218099362, 3.081185538926995, 3.278749383217061], [1.776793751092763, 2.93741167455471, 3.058701096568852], [3.318412187495734, 2.997331084033472, 2.022167590167672], [3.874524708023352, 4.425301459451914, 2.771990305592935], [2.055778446743566, 4.437449313863041, 4.061046832034642]] self.assertAlmostEqual(solid_angle(center, coords), 1.83570965938, 7, "Wrong result returned by solid_angle")
def _delaunay_midpoints(self, structure): """ returns the midpoints of the delaunay triangulation for a structure. returns in the format of a list of lists, where the first index corresponds to the index of the site in the original structure that neighbors the point. I.e. dms[0] is the list of delaunay midpoints around the site structure[0] We can't simply use the ridges from the voronoi tesselation because the ridges don't include nearest neighbors with a solid angle of 0 (whose voronoi volumes are edge sharing) """ logging.debug("Starting calculating Delaunay points") all_coords = self._fake_periodic(structure) vt = VoronoiTess(all_coords) dt = DelaunayTri(all_coords) v_array = np.array(vt.vertices) p_array = np.array(vt.points) dms = [[] for i in range(len(structure))] for simplex in dt.vertices: if min(simplex) >= len(structure): continue for pt1, pt2 in itertools.permutations(simplex, 2): if pt1 >= len(structure): continue center = structure[pt1].coords # get points that make up the shared plane if pt2 > pt1: vertices = vt.ridges.get((pt1, pt2), None) else: vertices = vt.ridges.get((pt2, pt1), None) # get the solid angle, if pt1 and pt2 share a plane if vertices is not None: sa = solid_angle(center, v_array[vertices]) else: sa = 0 new_coord = (p_array[pt1] + p_array[pt2]) / 2 if sa >= self._msa: dms[pt1].append(new_coord) logging.debug("Finished calculating Delaunay points") return dms
def get_voronoi_dicts(structure, cutoff_radius=VORONOI_CUTOFF_RADIUS, cutoff_weight_fraction=VORONOI_CUTOFF_WEIGHT_FRACTION): voronoi_finder = VoronoiCoordFinder(structure) voronoi_finder.cutoff = cutoff_radius voronoi_finder_2 = VoronoiNN() voronoi_finder_2.cutoff = cutoff_radius all_neighbor_sites = {} all_neighbor_pairs = {} for center_site_index in range(0, len(structure.sites)): neighbor_sites_dict = {} neighbor_pairs_list = [] # print "center_site_index: ", center_site_index # Construct initial voronoi polyhedra using cutoff_radius neighborhood_sites = [structure[center_site_index] ] # begin list with center site polyhedra_sites = voronoi_finder.get_voronoi_polyhedra( center_site_index) polyhedra_sites_2 = voronoi_finder_2.get_voronoi_polyhedra( structure, center_site_index) cutoff_weight = cutoff_weight_fraction * sum(polyhedra_sites.values()) for neighbor_site, neighbor_weight in polyhedra_sites.items(): if neighbor_weight > cutoff_weight: neighborhood_sites.append(neighbor_site) # Re-tesselate only with sites that also meet cutoff_weight_fraction criteria voronoi_input_coords = [site.coords for site in neighborhood_sites] try: voronoi_tess = VoronoiTess(voronoi_input_coords) except IndexError: raise RuntimeError('VoronoiTess: details unknown') local_solid_angles = [] center_coords = structure[center_site_index].coords n_neighborhood_sites = len(neighborhood_sites) n_voronoi_vertices = len(voronoi_tess.vertices) neighbor_lookup = np.zeros(shape=(n_voronoi_vertices, n_voronoi_vertices, 2), dtype=np.int8) # Loop over neighbor sites (which surrond center_site) to: # - calculate solid angles # - construct neighbor_lookup array (which maps each voronoi edge to two adjacent neighbors) for neighbor_index in range(1, n_neighborhood_sites): facet_vertex_list = voronoi_tess.ridges[( 0, neighbor_index)] # 0 = center site if 0 in facet_vertex_list: raise RuntimeError( "Pathological structure: voronoi facet includes vertex at infinity" ) # Calculate solid angle of facet between center site and this neighbor site facet_coords = [ voronoi_tess.vertices[i] for i in facet_vertex_list ] local_solid_angles.append(solid_angle(center_coords, facet_coords)) # Add this voronoi ridge to neighbor_lookup array, to identify adjacent voronoi facets n_vertices = len(facet_vertex_list) for vertex1_index in range(0, n_vertices): vertex2_index = vertex1_index + 1 if vertex2_index == n_vertices: vertex2_index = 0 # wrap vertex2_index # Properly order vertex indicies to only use upper triangle of neighbor_lookup array (low_vert_index, high_vert_index) = (facet_vertex_list[vertex1_index], facet_vertex_list[vertex2_index]) if low_vert_index > high_vert_index: (low_vert_index, high_vert_index) = (high_vert_index, low_vert_index) # Store adjacent voronoi neighbors on different levels of neighbor_lookup, at location defined # by common voronoi edge running between low_vert_index (row) and high_vert_index (column). if neighbor_lookup[low_vert_index, high_vert_index, 0] == 0: neighbor_lookup[low_vert_index, high_vert_index, 0] = neighbor_index # first neighbor else: neighbor_lookup[low_vert_index, high_vert_index, 1] = neighbor_index # second neighbor # Loop over neighbor sites again to construct neighbor_sites_dict with solid angle weights max_local_solid_angle = max(local_solid_angles) for neighbor_index in range(1, n_neighborhood_sites): # Note: neighborhood_sites (which starts with center) is one longer than local_solid_angles neighbor_sites_dict[neighborhood_sites[neighbor_index]] = \ local_solid_angles[neighbor_index - 1] / max_local_solid_angle # Loop through upper triangle of neighbor_lookup and build a list of tuples of neighbor pair sites for low_vert_index in range(1, n_voronoi_vertices): for high_vert_index in range(low_vert_index + 1, n_voronoi_vertices): neighbor1_index = neighbor_lookup[low_vert_index, high_vert_index, 0] neighbor2_index = neighbor_lookup[low_vert_index, high_vert_index, 1] if neighbor1_index > 0 and neighbor2_index > 0: # Both are non-zero neighbor_pairs_list.append( (neighborhood_sites[neighbor1_index], neighborhood_sites[neighbor2_index])) elif neighbor1_index != 0 or neighbor2_index != 0: # Only one is non-zero raise RuntimeError( "Unexpected neighbor_lookup matrix: non-paired edge") # Append per site values all_neighbor_sites[ structure.sites[center_site_index]] = neighbor_sites_dict all_neighbor_pairs[ structure.sites[center_site_index]] = neighbor_pairs_list return (all_neighbor_sites, all_neighbor_pairs)