def apply_transformation(self, structure, return_ranked_list=False):
        """
        Args:
            structure (Structure): Input structure to dope

        Returns:
            [{"structure": Structure, "energy": float}]
        """
        comp = structure.composition
        logger.info("Composition: %s" % comp)

        for sp in comp:
            try:
                sp.oxi_state
            except AttributeError:
                analyzer = BVAnalyzer()
                structure = analyzer.get_oxi_state_decorated_structure(
                    structure)
                comp = structure.composition
                break

        ox = self.dopant.oxi_state
        radius = self.dopant.ionic_radius

        compatible_species = [
            sp for sp in comp if sp.oxi_state == ox and
            abs(sp.ionic_radius / radius - 1) < self.ionic_radius_tol]

        if (not compatible_species) and self.alio_tol:
            # We only consider aliovalent doping if there are no compatible
            # isovalent species.
            compatible_species = [
                sp for sp in comp
                if abs(sp.oxi_state - ox) <= self.alio_tol and
                abs(sp.ionic_radius / radius - 1) < self.ionic_radius_tol and
                sp.oxi_state * ox >= 0]

        if self.allowed_doping_species is not None:
            # Only keep allowed doping species.
            compatible_species = [
                sp for sp in compatible_species
                if sp in [get_el_sp(s) for s in self.allowed_doping_species]]

        logger.info("Compatible species: %s" % compatible_species)

        lengths = structure.lattice.abc
        scaling = [max(1, int(round(math.ceil(self.min_length / x))))
                   for x in lengths]
        logger.info("Lengths are %s" % str(lengths))
        logger.info("Scaling = %s" % str(scaling))

        all_structures = []
        t = EnumerateStructureTransformation(**self.kwargs)

        for sp in compatible_species:
            supercell = structure * scaling
            nsp = supercell.composition[sp]
            if sp.oxi_state == ox:
                supercell.replace_species({sp: {sp: (nsp - 1) / nsp,
                                                self.dopant: 1 / nsp}})
                logger.info("Doping %s for %s at level %.3f" % (
                    sp, self.dopant, 1 / nsp))
            elif self.codopant:
                codopant = _find_codopant(sp, 2 * sp.oxi_state - ox)
                supercell.replace_species({sp: {sp: (nsp - 2) / nsp,
                                                self.dopant: 1 / nsp,
                                                codopant: 1 / nsp}})
                logger.info("Doping %s for %s + %s at level %.3f" % (
                    sp, self.dopant, codopant, 1 / nsp))
            elif abs(sp.oxi_state) < abs(ox):
                # Strategy: replace the target species with a
                # combination of dopant and vacancy.
                # We will choose the lowest oxidation state species as a
                # vacancy compensation species as it is likely to be lower in
                # energy
                sp_to_remove = min([s for s in comp if s.oxi_state * ox > 0],
                                   key=lambda ss: abs(ss.oxi_state))

                if sp_to_remove == sp:
                    common_charge = lcm(int(abs(sp.oxi_state)), int(abs(ox)))
                    ndopant = common_charge / abs(ox)
                    nsp_to_remove = common_charge / abs(sp.oxi_state)
                    logger.info("Doping %d %s with %d %s." %
                                (nsp_to_remove, sp, ndopant, self.dopant))
                    supercell.replace_species(
                        {sp: {sp: (nsp - nsp_to_remove) / nsp,
                              self.dopant: ndopant / nsp}})
                else:
                    ox_diff = int(abs(round(sp.oxi_state - ox)))
                    vac_ox = int(abs(sp_to_remove.oxi_state))
                    common_charge = lcm(vac_ox, ox_diff)
                    ndopant = common_charge / ox_diff
                    nx_to_remove = common_charge / vac_ox
                    nx = supercell.composition[sp_to_remove]
                    logger.info("Doping %d %s with %s and removing %d %s." %
                                (ndopant, sp, self.dopant,
                                 nx_to_remove, sp_to_remove))
                    supercell.replace_species(
                        {sp: {sp: (nsp - ndopant) / nsp,
                              self.dopant: ndopant / nsp},
                         sp_to_remove: {
                             sp_to_remove: (nx - nx_to_remove) / nx}})
            elif abs(sp.oxi_state) > abs(ox):
                # Strategy: replace the target species with dopant and also
                # remove some opposite charged species for charge neutrality
                if ox > 0:
                    sp_to_remove = max(supercell.composition.keys(),
                                       key=lambda el: el.X)
                else:
                    sp_to_remove = min(supercell.composition.keys(),
                                       key=lambda el: el.X)
                # Confirm species are of opposite oxidation states.
                assert sp_to_remove.oxi_state * sp.oxi_state < 0

                ox_diff = int(abs(round(sp.oxi_state - ox)))
                anion_ox = int(abs(sp_to_remove.oxi_state))
                nx = supercell.composition[sp_to_remove]
                common_charge = lcm(anion_ox, ox_diff)
                ndopant = common_charge / ox_diff
                nx_to_remove = common_charge / anion_ox
                logger.info("Doping %d %s with %s and removing %d %s." %
                            (ndopant, sp, self.dopant,
                             nx_to_remove, sp_to_remove))
                supercell.replace_species(
                    {sp: {sp: (nsp - ndopant) / nsp,
                          self.dopant: ndopant / nsp},
                     sp_to_remove: {sp_to_remove: (nx - nx_to_remove) / nx}})

            ss = t.apply_transformation(
                supercell, return_ranked_list=self.max_structures_per_enum)
            logger.info("%s distinct structures" % len(ss))
            all_structures.extend(ss)

        logger.info("Total %s doped structures" % len(all_structures))
        if return_ranked_list:
            return all_structures[:return_ranked_list]

        return all_structures[0]["structure"]
def apply_transformation ( self , structure , return_ranked_list = False ) : comp = structure . composition logger . info ( "Composition: %s" % comp ) for sp in comp : try : sp . oxi_state except AttributeError : analyzer = BVAnalyzer ( ) structure = analyzer . get_oxi_state_decorated_structure ( structure ) comp = structure . composition break ox = self . dopant . oxi_state radius = self . dopant . ionic_radius compatible_species = [ sp for sp in comp if sp . oxi_state == ox and abs ( sp . ionic_radius / radius - 1 ) < self . ionic_radius_tol ] if ( not compatible_species ) and self . alio_tol : compatible_species = [ sp for sp in comp if abs ( sp . oxi_state - ox ) <= self . alio_tol and abs ( sp . ionic_radius / radius - 1 ) < self . ionic_radius_tol and sp . oxi_state * ox >= 0 ] if self . allowed_doping_species is not None : compatible_species = [ sp for sp in compatible_species if sp in [ get_el_sp ( s ) for s in self . allowed_doping_species ] ] logger . info ( "Compatible species: %s" % compatible_species ) lengths = structure . lattice . abc scaling = [ max ( 1 , int ( round ( math . ceil ( self . min_length / x ) ) ) ) for x in lengths ] logger . info ( "Lengths are %s" % str ( lengths ) ) logger . info ( "Scaling = %s" % str ( scaling ) ) all_structures = [ ] t = EnumerateStructureTransformation ( ** self . kwargs ) for sp in compatible_species : supercell = structure * scaling nsp = supercell . composition [ sp ] if sp . oxi_state == ox : supercell . replace_species ( { sp : { sp : ( nsp - 1 ) / nsp , self . dopant : 1 / nsp } } ) logger . info ( "Doping %s for %s at level %.3f" % ( sp , self . dopant , 1 / nsp ) ) elif self . codopant : codopant = _find_codopant ( sp , 2 * sp . oxi_state - ox ) supercell . replace_species ( { sp : { sp : ( nsp - 2 ) / nsp , self . dopant : 1 / nsp , codopant : 1 / nsp } } ) logger . info ( "Doping %s for %s + %s at level %.3f" % ( sp , self . dopant , codopant , 1 / nsp ) ) elif abs ( sp . oxi_state ) < abs ( ox ) : sp_to_remove = min ( [ s for s in comp if s . oxi_state * ox > 0 ] , key = lambda ss : abs ( ss . oxi_state ) ) if sp_to_remove == sp : common_charge = lcm ( int ( abs ( sp . oxi_state ) ) , int ( abs ( ox ) ) ) ndopant = common_charge / abs ( ox ) nsp_to_remove = common_charge / abs ( sp . oxi_state ) logger . info ( "Doping %d %s with %d %s." % ( nsp_to_remove , sp , ndopant , self . dopant ) ) supercell . replace_species ( { sp : { sp : ( nsp - nsp_to_remove ) / nsp , self . dopant : ndopant / nsp } } ) else : ox_diff = int ( abs ( round ( sp . oxi_state - ox ) ) ) vac_ox = int ( abs ( sp_to_remove . oxi_state ) ) common_charge = lcm ( vac_ox , ox_diff ) ndopant = common_charge / ox_diff nx_to_remove = common_charge / vac_ox nx = supercell . composition [ sp_to_remove ] logger . info ( "Doping %d %s with %s and removing %d %s." % ( ndopant , sp , self . dopant , nx_to_remove , sp_to_remove ) ) supercell . replace_species ( { sp : { sp : ( nsp - ndopant ) / nsp , self . dopant : ndopant / nsp } , sp_to_remove : { sp_to_remove : ( nx - nx_to_remove ) / nx } } ) elif abs ( sp . oxi_state ) > abs ( ox ) : if ox > 0 : sp_to_remove = max ( supercell . composition . keys ( ) , key = lambda el : el . X ) else : sp_to_remove = min ( supercell . composition . keys ( ) , key = lambda el : el . X ) assert sp_to_remove . oxi_state * sp . oxi_state < 0 ox_diff = int ( abs ( round ( sp . oxi_state - ox ) ) ) anion_ox = int ( abs ( sp_to_remove . oxi_state ) ) nx = supercell . composition [ sp_to_remove ] common_charge = lcm ( anion_ox , ox_diff ) ndopant = common_charge / ox_diff nx_to_remove = common_charge / anion_ox logger . info ( "Doping %d %s with %s and removing %d %s." % ( ndopant , sp , self . dopant , nx_to_remove , sp_to_remove ) ) supercell . replace_species ( { sp : { sp : ( nsp - ndopant ) / nsp , self . dopant : ndopant / nsp } , sp_to_remove : { sp_to_remove : ( nx - nx_to_remove ) / nx } } ) ss = t . apply_transformation ( supercell , return_ranked_list = self . max_structures_per_enum ) logger . info ( "%s distinct structures" % len ( ss ) ) all_structures . extend ( ss ) logger . info ( "Total %s doped structures" % len ( all_structures ) ) if return_ranked_list : return all_structures [ : return_ranked_list ] return all_structures [ 0 ] [ "structure" ]
Args:
            structure (Structure): Input structure to dope

        Returns:
            [{"structure": Structure, "energy": float}]