Get the information about a certain atom from the results of a tessellation

        Args:
            structure (Structure) - Structure being assessed
            site_idx (int) - Index of the atom in question
            sites ([Site]) - List of all sites in the tessellation
            targets ([Element]) - Target elements
            voro - Output of qvoronoi
            compute_adj_neighbors (boolean) - Whether to compute which neighbors are adjacent
        Returns:
            A dict of sites sharing a common Voronoi facet. Key is facet id
             (not useful) and values are dictionaries containing statistics
             about the facet:
                - site: Pymatgen site
                - solid_angle - Solid angle subtended by face
                - angle_normalized - Solid angle normalized such that the
                    faces with the largest
                - area - Area of the facet
                - face_dist - Distance between site n and the facet
                - volume - Volume of Voronoi cell for this face
                - n_verts - Number of vertices on the facet
                - adj_neighbors - Facet id's for the adjacent neighbors

def _extract_cell_info(self, structure, site_idx, sites, targets, voro,
                           compute_adj_neighbors=False):
        """Get the information about a certain atom from the results of a tessellation

        Args:
            structure (Structure) - Structure being assessed
            site_idx (int) - Index of the atom in question
            sites ([Site]) - List of all sites in the tessellation
            targets ([Element]) - Target elements
            voro - Output of qvoronoi
            compute_adj_neighbors (boolean) - Whether to compute which neighbors are adjacent
        Returns:
            A dict of sites sharing a common Voronoi facet. Key is facet id
             (not useful) and values are dictionaries containing statistics
             about the facet:
                - site: Pymatgen site
                - solid_angle - Solid angle subtended by face
                - angle_normalized - Solid angle normalized such that the
                    faces with the largest
                - area - Area of the facet
                - face_dist - Distance between site n and the facet
                - volume - Volume of Voronoi cell for this face
                - n_verts - Number of vertices on the facet
                - adj_neighbors - Facet id's for the adjacent neighbors
        """
        # Get the coordinates of every vertex
        all_vertices = voro.vertices

        # Get the coordinates of the central site
        center_coords = sites[site_idx].coords

        # Iterate through all the faces in the tessellation
        results = {}
        for nn, vind in voro.ridge_dict.items():
            # Get only those that include the cite in question
            if site_idx in nn:
                other_site = nn[0] if nn[1] == site_idx else nn[1]
                if -1 in vind:
                    # -1 indices correspond to the Voronoi cell
                    #  missing a face
                    if self.allow_pathological:
                        continue
                    else:
                        raise RuntimeError("This structure is pathological,"
                                           " infinite vertex in the voronoi "
                                           "construction")

                # Get the solid angle of the face
                facets = [all_vertices[i] for i in vind]
                angle = solid_angle(center_coords, facets)

                # Compute the volume of associated with this face
                volume = 0
                #  qvoronoi returns vertices in CCW order, so I can break
                #   the face up in to segments (0,1,2), (0,2,3), ... to compute
                #   its area where each number is a vertex size
                for j, k in zip(vind[1:], vind[2:]):
                    volume += vol_tetra(center_coords,
                                        all_vertices[vind[0]],
                                        all_vertices[j],
                                        all_vertices[k])

                # Compute the distance of the site to the face
                face_dist = np.linalg.norm(
                    center_coords - sites[other_site].coords) / 2

                # Compute the area of the face (knowing V=Ad/3)
                face_area = 3 * volume / face_dist

                # Compute the normal of the facet
                normal = np.subtract(sites[other_site].coords, center_coords)
                normal /= np.linalg.norm(normal)

                # Store by face index
                results[other_site] = {
                    'site': sites[other_site],
                    'normal': normal,
                    'solid_angle': angle,
                    'volume': volume,
                    'face_dist': face_dist,
                    'area': face_area,
                    'n_verts': len(vind)
                }

                # If we are computing which neighbors are adjacent, store the vertices
                if compute_adj_neighbors:
                    results[other_site]['verts'] = vind

        # Get only target elements
        resultweighted = {}
        for nn_index, nstats in results.items():
            # Check if this is a target site
            nn = nstats['site']
            if nn.is_ordered:
                if nn.specie in targets:
                    resultweighted[nn_index] = nstats
            else:  # is nn site is disordered
                for disordered_sp in nn.species.keys():
                    if disordered_sp in targets:
                        resultweighted[nn_index] = nstats

        # If desired, determine which neighbors are adjacent
        if compute_adj_neighbors:
            # Initialize storage for the adjacent neighbors
            adj_neighbors = dict((i, []) for i in resultweighted.keys())

            # Find the neighbors that are adjacent by finding those
            #  that contain exactly two vertices
            for a_ind, a_nninfo in resultweighted.items():
                # Get the indices for this site
                a_verts = set(a_nninfo['verts'])

                # Loop over all neighbors that have an index lower that this one
                #  The goal here is to exploit the fact that neighbor adjacency is symmetric
                #  (if A is adj to B, B is adj to A)
                for b_ind, b_nninfo in resultweighted.items():
                    if b_ind > a_ind:
                        continue
                    if len(a_verts.intersection(b_nninfo['verts'])) == 2:
                        adj_neighbors[a_ind].append(b_ind)
                        adj_neighbors[b_ind].append(a_ind)

            # Store the results in the nn_info
            for key, neighbors in adj_neighbors.items():
                resultweighted[key]['adj_neighbors'] = neighbors

        return resultweighted