Get dopant suggestions based on Shannon radii differences.

    Args:
        bonded_structure (StructureGraph): A pymatgen structure graph
            decorated with oxidation states. For example, generated using the
            CrystalNN.get_bonded_structure() method.
        num_dopants (int): The nummber of suggestions to return for
            n- and p-type dopants.
        match_oxi_sign (bool): Whether to force the dopant and original species
            to have the same sign of oxidation state. E.g. If the original site
            is in a negative charge state, then only negative dopants will be
            returned.

    Returns:
        (dict): Dopant suggestions, given as a dictionary with keys "n_type" and
        "p_type". The suggestions for each doping type are given as a list of
        dictionaries, each with they keys:

        - "radii_diff": The difference between the Shannon radii of the species.
        - "dopant_spcies": The dopant species.
        - "original_species": The substituted species.

def get_dopants_from_shannon_radii(bonded_structure, num_dopants=5,
                                   match_oxi_sign=False):
    """
    Get dopant suggestions based on Shannon radii differences.

    Args:
        bonded_structure (StructureGraph): A pymatgen structure graph
            decorated with oxidation states. For example, generated using the
            CrystalNN.get_bonded_structure() method.
        num_dopants (int): The nummber of suggestions to return for
            n- and p-type dopants.
        match_oxi_sign (bool): Whether to force the dopant and original species
            to have the same sign of oxidation state. E.g. If the original site
            is in a negative charge state, then only negative dopants will be
            returned.

    Returns:
        (dict): Dopant suggestions, given as a dictionary with keys "n_type" and
        "p_type". The suggestions for each doping type are given as a list of
        dictionaries, each with they keys:

        - "radii_diff": The difference between the Shannon radii of the species.
        - "dopant_spcies": The dopant species.
        - "original_species": The substituted species.
    """
    # get a list of all Specie for all elements in all their common oxid states
    all_species = [Specie(el, oxi) for el in Element
                   for oxi in el.common_oxidation_states]

    # get a series of tuples with (coordination number, specie)
    cn_and_species = set((bonded_structure.get_coordination_of_site(i),
                          bonded_structure.structure[i].specie)
                         for i in range(bonded_structure.structure.num_sites))

    cn_to_radii_map = {}
    possible_dopants = []

    for cn, species in cn_and_species:
        cn_roman = _int_to_roman(cn)

        try:
            species_radius = species.get_shannon_radius(cn_roman)
        except KeyError:
            warnings.warn("Shannon radius not found for {} with coordination "
                          "number {}.\nSkipping...".format(species, cn))
            continue

        if cn not in cn_to_radii_map:
            cn_to_radii_map[cn] = _shannon_radii_from_cn(
                all_species, cn_roman, radius_to_compare=species_radius)

        shannon_radii = cn_to_radii_map[cn]

        possible_dopants += [{'radii_diff': p['radii_diff'],
                              'dopant_species': p['species'],
                              'original_species': species}
                             for p in shannon_radii]

    possible_dopants.sort(key=lambda x: abs(x['radii_diff']))

    return _get_dopants(possible_dopants, num_dopants, match_oxi_sign)