Computes and returns the StructureEnvironments object containing all the information about the coordination
        environments in the structure
        :param excluded_atoms: Atoms for which the coordination geometries does not have to be identified
        :param only_atoms: If not set to None, atoms for which the coordination geometries have to be identified
        :param only_cations: If set to True, will only compute environments for cations
        :param only_indices: If not set to None, will only compute environments the atoms of the given indices
        :param maximum_distance_factor: If not set to None, neighbors beyond
                                        maximum_distance_factor*closest_neighbor_distance are not considered
        :param minimum_angle_factor: If not set to None, neighbors for which the angle is lower than
                                     minimum_angle_factor*largest_angle_neighbor are not considered
        :param max_cn: maximum coordination number to be considered
        :param min_cn: minimum coordination number to be considered
        :param only_symbols: if not set to None, consider only coordination environments with the given symbols
        :param valences: valences of the atoms
        :param additional_conditions: additional conditions to be considered in the bonds (example : only bonds
                                      between cation and anion
        :param info: additional info about the calculation
        :param timelimit: time limit (in secs) after which the calculation of the StructureEnvironments object stops
        :param initial_structure_environments: initial StructureEnvironments object (most probably incomplete)
        :param get_from_hints: whether to add neighbors sets from "hints" (e.g. capped environment => test the
                               neighbors without the cap)
        :param voronoi_normalized_distance_tolerance: tolerance for the normalized distance used to distinguish
                                                      neighbors sets
        :param voronoi_normalized_angle_tolerance: tolerance for the normalized angle used to distinguish
                                                   neighbors sets
        :param recompute: whether to recompute the sites already computed (when initial_structure_environments
                          is not None)
        :param optimization: optimization algorithm
        :return: The StructureEnvironments object containing all the information about the coordination
        environments in the structure

def compute_structure_environments(self,
                                       excluded_atoms=None,
                                       only_atoms=None,
                                       only_cations=True,
                                       only_indices=None,
                                       maximum_distance_factor=PRESETS['DEFAULT']['maximum_distance_factor'],
                                       minimum_angle_factor=PRESETS['DEFAULT']['minimum_angle_factor'],
                                       max_cn=None,
                                       min_cn=None,
                                       only_symbols=None,
                                       valences='undefined',
                                       additional_conditions=None,
                                       info=None,
                                       timelimit=None,
                                       initial_structure_environments=None,
                                       get_from_hints=False,
                                       voronoi_normalized_distance_tolerance=PRESETS['DEFAULT']
                                       ['voronoi_normalized_distance_tolerance'],
                                       voronoi_normalized_angle_tolerance=PRESETS['DEFAULT']
                                       ['voronoi_normalized_angle_tolerance'],
                                       recompute=None,
                                       optimization=PRESETS['DEFAULT']['optimization']):
        """
        Computes and returns the StructureEnvironments object containing all the information about the coordination
        environments in the structure
        :param excluded_atoms: Atoms for which the coordination geometries does not have to be identified
        :param only_atoms: If not set to None, atoms for which the coordination geometries have to be identified
        :param only_cations: If set to True, will only compute environments for cations
        :param only_indices: If not set to None, will only compute environments the atoms of the given indices
        :param maximum_distance_factor: If not set to None, neighbors beyond
                                        maximum_distance_factor*closest_neighbor_distance are not considered
        :param minimum_angle_factor: If not set to None, neighbors for which the angle is lower than
                                     minimum_angle_factor*largest_angle_neighbor are not considered
        :param max_cn: maximum coordination number to be considered
        :param min_cn: minimum coordination number to be considered
        :param only_symbols: if not set to None, consider only coordination environments with the given symbols
        :param valences: valences of the atoms
        :param additional_conditions: additional conditions to be considered in the bonds (example : only bonds
                                      between cation and anion
        :param info: additional info about the calculation
        :param timelimit: time limit (in secs) after which the calculation of the StructureEnvironments object stops
        :param initial_structure_environments: initial StructureEnvironments object (most probably incomplete)
        :param get_from_hints: whether to add neighbors sets from "hints" (e.g. capped environment => test the
                               neighbors without the cap)
        :param voronoi_normalized_distance_tolerance: tolerance for the normalized distance used to distinguish
                                                      neighbors sets
        :param voronoi_normalized_angle_tolerance: tolerance for the normalized angle used to distinguish
                                                   neighbors sets
        :param recompute: whether to recompute the sites already computed (when initial_structure_environments
                          is not None)
        :param optimization: optimization algorithm
        :return: The StructureEnvironments object containing all the information about the coordination
        environments in the structure
        """
        time_init = time.clock()
        if info is None:
            info = {}
        info.update({'local_geometry_finder':
                         {'parameters':
                              {'centering_type': self.centering_type,
                               'include_central_site_in_centroid': self.include_central_site_in_centroid,
                               'structure_refinement': self.structure_refinement,
                               'spg_analyzer_options': self.spg_analyzer_options
                               }
                          }
                     })
        if only_symbols is not None:
            self.allcg = AllCoordinationGeometries(
                permutations_safe_override=self.permutations_safe_override,
                only_symbols=only_symbols)

        self.valences = valences

        # Get a list of indices of unequivalent sites from the initial structure
        self.equivalent_sites = [[site] for site in self.structure]
        self.struct_sites_to_irreducible_site_list_map = list(
            range(len(self.structure)))
        self.sites_map = list(range(len(self.structure)))
        indices = list(range(len(self.structure)))

        # Get list of unequivalent sites with valence >= 0
        if only_cations and self.valences != 'undefined':
            sites_indices = [isite for isite in indices if
                             self.valences[isite] >= 0]
        else:
            sites_indices = [isite for isite in indices]

        # Include atoms that are in the list of "only_atoms" if it is provided
        if only_atoms is not None:
            sites_indices = [isite for isite in sites_indices
                             if any([at in [sp.symbol for sp in self.structure[
                    isite].species]
                                     for at in only_atoms])]

        # Exclude atoms that are in the list of excluded atoms
        if excluded_atoms:
            sites_indices = [isite for isite in sites_indices
                             if not any([at in [sp.symbol for sp in
                                                self.structure[
                                                    isite].species]
                                         for at in excluded_atoms])]

        if only_indices is not None:
            sites_indices = [isite for isite in indices if
                             isite in only_indices]

        # Get the VoronoiContainer for the sites defined by their indices (sites_indices)
        logging.info('Getting DetailedVoronoiContainer')
        if voronoi_normalized_distance_tolerance is None:
            normalized_distance_tolerance = DetailedVoronoiContainer.default_normalized_distance_tolerance
        else:
            normalized_distance_tolerance = voronoi_normalized_distance_tolerance
        if voronoi_normalized_angle_tolerance is None:
            normalized_angle_tolerance = DetailedVoronoiContainer.default_normalized_angle_tolerance
        else:
            normalized_angle_tolerance = voronoi_normalized_angle_tolerance
        self.detailed_voronoi = DetailedVoronoiContainer(self.structure,
                                                         isites=sites_indices,
                                                         valences=self.valences,
                                                         maximum_distance_factor=maximum_distance_factor,
                                                         minimum_angle_factor=minimum_angle_factor,
                                                         additional_conditions=additional_conditions,
                                                         normalized_distance_tolerance=normalized_distance_tolerance,
                                                         normalized_angle_tolerance=normalized_angle_tolerance)
        logging.info('DetailedVoronoiContainer has been set up')

        # Initialize the StructureEnvironments object (either from initial_structure_environments or from scratch)
        if initial_structure_environments is not None:
            se = initial_structure_environments
            if se.structure != self.structure:
                raise ValueError('Structure is not the same in initial_structure_environments')
            if se.voronoi != self.detailed_voronoi:
                if self.detailed_voronoi.is_close_to(se.voronoi):
                    self.detailed_voronoi = se.voronoi
                else:
                    raise ValueError('Detailed Voronoi is not the same in initial_structure_environments')
            se.info = info
        else:
            se = StructureEnvironments(voronoi=self.detailed_voronoi, valences=self.valences,
                                       sites_map=self.sites_map, equivalent_sites=self.equivalent_sites,
                                       ce_list=[None]*len(self.structure), structure=self.structure,
                                       info=info)

        # Set up the coordination numbers that have to be computed based on min_cn, max_cn and possibly the settings
        # for an update (argument "recompute") of an existing StructureEnvironments
        if min_cn is None:
            min_cn = 1
        if max_cn is None:
            max_cn = 13
        all_cns = range(min_cn, max_cn+1)
        do_recompute = False
        if recompute is not None:
            if 'cns' in recompute:
                cns_to_recompute = recompute['cns']
                all_cns = list(set(all_cns).intersection(cns_to_recompute))
            do_recompute = True

        # Variables used for checking timelimit
        max_time_one_site = 0.0
        breakit = False

        if optimization > 0:
            self.detailed_voronoi.local_planes = [None]*len(self.structure)
            self.detailed_voronoi.separations = [None]*len(self.structure)

        # Loop on all the sites
        for isite in range(len(self.structure)):
            if isite not in sites_indices:
                logging.info(' ... in site #{:d}/{:d} ({}) : '
                             'skipped'.format(isite, len(self.structure),
                                              self.structure[isite].species_string))
                continue
            if breakit:
                logging.info(' ... in site #{:d}/{:d} ({}) : '
                             'skipped (timelimit)'.format(isite, len(self.structure),
                                                          self.structure[isite].species_string))
                continue
            logging.info(' ... in site #{:d}/{:d} ({})'.format(isite, len(self.structure),
                                                               self.structure[isite].species_string))
            t1 = time.clock()
            if optimization > 0:
                self.detailed_voronoi.local_planes[isite] = OrderedDict()
                self.detailed_voronoi.separations[isite] = {}
            se.init_neighbors_sets(isite=isite, additional_conditions=additional_conditions, valences=valences)

            to_add_from_hints = []
            nb_sets_info = {}

            for cn, nb_sets in se.neighbors_sets[isite].items():
                if cn not in all_cns:
                    continue
                for inb_set, nb_set in enumerate(nb_sets):
                    logging.debug('    ... getting environments for nb_set ({:d}, {:d})'.format(cn, inb_set))
                    tnbset1 = time.clock()
                    ce = self.update_nb_set_environments(se=se, isite=isite, cn=cn, inb_set=inb_set, nb_set=nb_set,
                                                         recompute=do_recompute, optimization=optimization)
                    tnbset2 = time.clock()
                    if cn not in nb_sets_info:
                        nb_sets_info[cn] = {}
                    nb_sets_info[cn][inb_set] = {'time': tnbset2 - tnbset1}
                    if get_from_hints:
                        for cg_symbol, cg_dict in ce:
                            cg = self.allcg[cg_symbol]
                            # Get possibly missing neighbors sets
                            if cg.neighbors_sets_hints is None:
                                continue
                            logging.debug('       ... getting hints from cg with mp_symbol "{}" ...'.format(cg_symbol))
                            hints_info = {'csm': cg_dict['symmetry_measure'],
                                          'nb_set': nb_set,
                                          'permutation': cg_dict['permutation']}
                            for nb_sets_hints in cg.neighbors_sets_hints:
                                suggested_nb_set_voronoi_indices = nb_sets_hints.hints(hints_info)
                                for inew, new_nb_set_voronoi_indices in enumerate(suggested_nb_set_voronoi_indices):
                                    logging.debug('           hint # {:d}'.format(inew))
                                    new_nb_set = se.NeighborsSet(structure=se.structure, isite=isite,
                                                                 detailed_voronoi=se.voronoi,
                                                                 site_voronoi_indices=new_nb_set_voronoi_indices,
                                                                 sources={'origin': 'nb_set_hints',
                                                                          'hints_type': nb_sets_hints.hints_type,
                                                                          'suggestion_index': inew,
                                                                          'cn_map_source': [cn, inb_set],
                                                                          'cg_source_symbol': cg_symbol})
                                    cn_new_nb_set = len(new_nb_set)
                                    if max_cn is not None and cn_new_nb_set > max_cn:
                                        continue
                                    if min_cn is not None and cn_new_nb_set < min_cn:
                                        continue
                                    if new_nb_set in [ta['new_nb_set'] for ta in to_add_from_hints]:
                                        has_nb_set = True
                                    elif not cn_new_nb_set in se.neighbors_sets[isite]:
                                        has_nb_set = False
                                    else:
                                        has_nb_set = new_nb_set in se.neighbors_sets[isite][cn_new_nb_set]
                                    if not has_nb_set:
                                        to_add_from_hints.append({'isite': isite,
                                                                  'new_nb_set': new_nb_set,
                                                                  'cn_new_nb_set': cn_new_nb_set})
                                        logging.debug('              => to be computed'.format(inew))
                                    else:
                                        logging.debug('              => already present'.format(inew))
            logging.debug('    ... getting environments for nb_sets added from hints')
            for missing_nb_set_to_add in to_add_from_hints:
                se.add_neighbors_set(isite=isite, nb_set=missing_nb_set_to_add['new_nb_set'])
            for missing_nb_set_to_add in to_add_from_hints:
                isite_new_nb_set = missing_nb_set_to_add['isite']
                cn_new_nb_set = missing_nb_set_to_add['cn_new_nb_set']
                new_nb_set = missing_nb_set_to_add['new_nb_set']
                inew_nb_set = se.neighbors_sets[isite_new_nb_set][cn_new_nb_set].index(new_nb_set)
                logging.debug('    ... getting environments for nb_set ({:d}, {:d}) - '
                              'from hints'.format(cn_new_nb_set, inew_nb_set))
                tnbset1 = time.clock()
                self.update_nb_set_environments(se=se,
                                                isite=isite_new_nb_set,
                                                cn=cn_new_nb_set,
                                                inb_set=inew_nb_set,
                                                nb_set=new_nb_set,
                                                optimization=optimization)
                tnbset2 = time.clock()
                if cn not in nb_sets_info:
                    nb_sets_info[cn] = {}
                nb_sets_info[cn][inew_nb_set] = {'time': tnbset2 - tnbset1}
            t2 = time.clock()
            se.update_site_info(isite=isite, info_dict={'time': t2 - t1, 'nb_sets_info': nb_sets_info})
            if timelimit is not None:
                time_elapsed = t2 - time_init
                time_left = timelimit - time_elapsed
                if time_left < 2.0 * max_time_one_site:
                    breakit = True
            max_time_one_site = max(max_time_one_site, t2 - t1)
            logging.info('    ... computed in {:.2f} seconds'.format(t2 - t1))
        time_end = time.clock()
        logging.info('    ... compute_structure_environments ended in {:.2f} seconds'.format(time_end-time_init))
        return se