def solve_for_fermi_energy(self, temperature, chemical_potentials, bulk_dos):
        """
        Solve for the Fermi energy self-consistently as a function of T
        Observations are Defect concentrations, electron and hole conc
        Args:
            temperature: Temperature to equilibrate fermi energies for
            chemical_potentials: dict of chemical potentials to use for calculation fermi level
            bulk_dos: bulk system dos (pymatgen Dos object)
        Returns:
            Fermi energy dictated by charge neutrality
        """

        fdos = FermiDos(bulk_dos, bandgap=self.band_gap)
        _,fdos_vbm = fdos.get_cbm_vbm()

        def _get_total_q(ef):

            qd_tot = sum([
                d['charge'] * d['conc']
                for d in self.defect_concentrations(
                    chemical_potentials=chemical_potentials, temperature=temperature, fermi_level=ef)
            ])
            qd_tot += fdos.get_doping(fermi=ef + fdos_vbm, T=temperature)
            return qd_tot

        return bisect(_get_total_q, -1., self.band_gap + 1.)
Exemple #2
0
    def test_doping_fermi(self):
        T = 300
        fermi0 = self.dos.efermi
        frange = [fermi0 - 0.5, fermi0, fermi0 + 2.0, fermi0 + 2.2]
        dopings = [self.dos.get_doping(fermi=f, T=T) for f in frange]
        ref_dopings = [3.48077e+21, 1.9235e+18, -2.6909e+16, -4.8723e+19]
        for i, c_ref in enumerate(ref_dopings):
            self.assertLessEqual(abs(dopings[i] / c_ref - 1.0), 0.01)

        calc_fermis = [self.dos.get_fermi(c=c, T=T) for c in ref_dopings]
        for j, f_ref in enumerate(frange):
            self.assertAlmostEqual(calc_fermis[j], f_ref, 4)

        sci_dos = FermiDos(self.dos, bandgap=3.0)
        for i, c_ref in enumerate(ref_dopings):
            if c_ref < 0:
                self.assertAlmostEqual(sci_dos.get_fermi(c_ref, T=T) -
                                       frange[i],
                                       0.47,
                                       places=2)
            else:
                self.assertAlmostEqual(sci_dos.get_fermi(c_ref, T=T) -
                                       frange[i],
                                       -0.47,
                                       places=2)
Exemple #3
0
    def solve_for_non_equilibrium_fermi_energy(self, temperature, quench_temperature,
                                               chemical_potentials, bulk_dos):
        """
        Solve for the Fermi energy after quenching in the defect concentrations at a higher
        temperature (the quench temperature),
        as outlined in P. Canepa et al (2017) Chemistry of Materials (doi: 10.1021/acs.chemmater.7b02909)

        Args:
            temperature: Temperature to equilibrate fermi energy at after quenching in defects
            quench_temperature: Temperature to equilibrate defect concentrations at (higher temperature)
            chemical_potentials: dict of chemical potentials to use for calculation fermi level
            bulk_dos: bulk system dos (pymatgen Dos object)
        Returns:
            Fermi energy dictated by charge neutrality with respect to frozen in defect concentrations
        """

        high_temp_fermi_level = self.solve_for_fermi_energy(quench_temperature, chemical_potentials,
                                                            bulk_dos)
        fixed_defect_charge = sum([
            d['charge'] * d['conc']
            for d in self.defect_concentrations(
                chemical_potentials=chemical_potentials, temperature=quench_temperature,
                fermi_level=high_temp_fermi_level)
        ])

        fdos = FermiDos(bulk_dos, bandgap=self.band_gap)
        _, fdos_vbm = fdos.get_cbm_vbm()

        def _get_total_q(ef):
            qd_tot = fixed_defect_charge
            qd_tot += fdos.get_doping(fermi_level=ef + fdos_vbm, temperature=temperature)
            return qd_tot

        return bisect(_get_total_q, -1., self.band_gap + 1.)
Exemple #4
0
    def solve_for_fermi_energy(self, temperature, chemical_potentials,
                               bulk_dos):
        """
        Solve for the Fermi energy self-consistently as a function of T
        Observations are Defect concentrations, electron and hole conc
        Args:
            temperature: Temperature to equilibrate fermi energies for
            chemical_potentials: dict of chemical potentials to use for calculation fermi level
            bulk_dos: bulk system dos (pymatgen Dos object)
        Returns:
            Fermi energy dictated by charge neutrality
        """

        fdos = FermiDos(bulk_dos, bandgap=self.band_gap)
        _, fdos_vbm = fdos.get_cbm_vbm()

        def _get_total_q(ef):
            qd_tot = sum([
                d['charge'] * d['conc'] for d in self.defect_concentrations(
                    chemical_potentials=chemical_potentials,
                    temperature=temperature,
                    fermi_level=ef)
            ])
            qd_tot += fdos.get_doping(fermi_level=ef + fdos_vbm,
                                      temperature=temperature)
            return qd_tot

        return bisect(_get_total_q, -1., self.band_gap + 1.)
Exemple #5
0
class FermiDosTest(unittest.TestCase):
    def setUp(self):
        with open(os.path.join(test_dir, "complete_dos.json"), "r") as f:
            self.dos = CompleteDos.from_dict(json.load(f))
        self.dos = FermiDos(self.dos)

    def test_doping_fermi(self):
        T = 300
        fermi0 = self.dos.efermi
        frange = [fermi0 - 0.5, fermi0, fermi0 + 2.0, fermi0 + 2.2]
        dopings = [self.dos.get_doping(fermi=f, T=T) for f in frange]
        ref_dopings = [3.48077e+21, 1.9235e+18, -2.6909e+16, -4.8723e+19]
        for i, c_ref in enumerate(ref_dopings):
            self.assertLessEqual(abs(dopings[i] / c_ref - 1.0), 0.01)

        calc_fermis = [self.dos.get_fermi(c=c, T=T) for c in ref_dopings]
        for j, f_ref in enumerate(frange):
            self.assertAlmostEqual(calc_fermis[j], f_ref, 4)

        sci_dos = FermiDos(self.dos, bandgap=3.0)
        for i, c_ref in enumerate(ref_dopings):
            if c_ref < 0:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, T=T) - frange[i], 0.47, places=2)
            else:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, T=T) - frange[i], -0.47, places=2)

        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(-1e26, 300),
                               7.5108, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(1e26, 300),
                               -1.6884, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(0.0, 300),
                               3.2382, 4)
Exemple #6
0
class FermiDosTest(unittest.TestCase):
    def setUp(self):
        with open(os.path.join(test_dir, "complete_dos.json"), "r") as f:
            self.dos = CompleteDos.from_dict(json.load(f))
        self.dos = FermiDos(self.dos)

    def test_doping_fermi(self):
        T = 300
        fermi0 = self.dos.efermi
        frange = [fermi0 - 0.5, fermi0, fermi0 + 2.0, fermi0 + 2.2]
        dopings = [self.dos.get_doping(fermi=f, T=T) for f in frange]
        ref_dopings = [3.48077e+21, 1.9235e+18, -2.6909e+16, -4.8723e+19]
        for i, c_ref in enumerate(ref_dopings):
            self.assertLessEqual(abs(dopings[i] / c_ref - 1.0), 0.01)

        calc_fermis = [self.dos.get_fermi(c=c, T=T) for c in ref_dopings]
        for j, f_ref in enumerate(frange):
            self.assertAlmostEqual(calc_fermis[j], f_ref, 4)

        sci_dos = FermiDos(self.dos, bandgap=3.0)
        for i, c_ref in enumerate(ref_dopings):
            if c_ref < 0:
                self.assertAlmostEqual(sci_dos.get_fermi(c_ref, T=T) -
                                       frange[i],
                                       0.47,
                                       places=2)
            else:
                self.assertAlmostEqual(sci_dos.get_fermi(c_ref, T=T) -
                                       frange[i],
                                       -0.47,
                                       places=2)
Exemple #7
0
    def test_doping_fermi(self):
        T = 300
        fermi0 = self.dos.efermi
        frange = [fermi0 - 0.5, fermi0, fermi0 + 2.0, fermi0 + 2.2]
        dopings = [self.dos.get_doping(fermi_level=f, temperature=T) for f in frange]
        ref_dopings = [3.48077e21, 1.9235e18, -2.6909e16, -4.8723e19]
        for i, c_ref in enumerate(ref_dopings):
            self.assertLessEqual(abs(dopings[i] / c_ref - 1.0), 0.01)

        calc_fermis = [self.dos.get_fermi(concentration=c, temperature=T) for c in ref_dopings]
        for j, f_ref in enumerate(frange):
            self.assertAlmostEqual(calc_fermis[j], f_ref, 4)

        sci_dos = FermiDos(self.dos, bandgap=3.0)
        self.assertEqual(sci_dos.get_gap(), 3.0)
        old_cbm, old_vbm = self.dos.get_cbm_vbm()
        old_gap = old_cbm - old_vbm
        new_cbm, new_vbm = sci_dos.get_cbm_vbm()
        self.assertAlmostEqual(new_cbm - old_cbm, (3.0 - old_gap) / 2.0)
        self.assertAlmostEqual(old_vbm - new_vbm, (3.0 - old_gap) / 2.0)
        for i, c_ref in enumerate(ref_dopings):
            if c_ref < 0:
                self.assertAlmostEqual(sci_dos.get_fermi(c_ref, temperature=T) - frange[i], 0.47, places=2)
            else:
                self.assertAlmostEqual(sci_dos.get_fermi(c_ref, temperature=T) - frange[i], -0.47, places=2)

        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(-1e26, 300), 7.5108, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(1e26, 300), -1.4182, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(0.0, 300), 2.9071, 4)
Exemple #8
0
class FermiDosTest(unittest.TestCase):
    def setUp(self):
        with open(
                os.path.join(PymatgenTest.TEST_FILES_DIR, "complete_dos.json"),
                "r") as f:
            self.dos = CompleteDos.from_dict(json.load(f))
        self.dos = FermiDos(self.dos)

    def test_doping_fermi(self):
        T = 300
        fermi0 = self.dos.efermi
        frange = [fermi0 - 0.5, fermi0, fermi0 + 2.0, fermi0 + 2.2]
        dopings = [
            self.dos.get_doping(fermi_level=f, temperature=T) for f in frange
        ]
        ref_dopings = [3.48077e21, 1.9235e18, -2.6909e16, -4.8723e19]
        for i, c_ref in enumerate(ref_dopings):
            self.assertLessEqual(abs(dopings[i] / c_ref - 1.0), 0.01)

        calc_fermis = [
            self.dos.get_fermi(concentration=c, temperature=T)
            for c in ref_dopings
        ]
        for j, f_ref in enumerate(frange):
            self.assertAlmostEqual(calc_fermis[j], f_ref, 4)

        sci_dos = FermiDos(self.dos, bandgap=3.0)
        self.assertEqual(sci_dos.get_gap(), 3.0)
        old_cbm, old_vbm = self.dos.get_cbm_vbm()
        old_gap = old_cbm - old_vbm
        new_cbm, new_vbm = sci_dos.get_cbm_vbm()
        self.assertAlmostEqual(new_cbm - old_cbm, (3.0 - old_gap) / 2.0)
        self.assertAlmostEqual(old_vbm - new_vbm, (3.0 - old_gap) / 2.0)
        for i, c_ref in enumerate(ref_dopings):
            if c_ref < 0:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, temperature=T) - frange[i],
                    0.47,
                    places=2)
            else:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, temperature=T) - frange[i],
                    -0.47,
                    places=2)

        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(-1e26, 300),
                               7.5108, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(1e26, 300),
                               -1.4182, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(0.0, 300),
                               2.5226, 4)
    def test_doping_fermi(self):
        T = 300
        fermi0 = self.dos.efermi
        frange = [fermi0 - 0.5, fermi0, fermi0 + 2.0, fermi0 + 2.2]
        dopings = [self.dos.get_doping(fermi=f, T=T) for f in frange]
        ref_dopings = [3.48077e+21, 1.9235e+18, -2.6909e+16, -4.8723e+19]
        for i, c_ref in enumerate(ref_dopings):
            self.assertLessEqual(abs(dopings[i] / c_ref - 1.0), 0.01)

        calc_fermis = [self.dos.get_fermi(c=c, T=T) for c in ref_dopings]
        for j, f_ref in enumerate(frange):
            self.assertAlmostEqual(calc_fermis[j], f_ref, 4)

        sci_dos = FermiDos(self.dos, bandgap=3.0)
        self.assertEqual( sci_dos.get_gap(), 3.)
        old_cbm, old_vbm = self.dos.get_cbm_vbm()
        old_gap = old_cbm - old_vbm
        new_cbm, new_vbm = sci_dos.get_cbm_vbm()
        self.assertAlmostEqual( new_cbm - old_cbm, (3. - old_gap) / 2.)
        self.assertAlmostEqual( old_vbm - new_vbm, (3. - old_gap) / 2.)
        for i, c_ref in enumerate(ref_dopings):
            if c_ref < 0:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, T=T) - frange[i], 0.47, places=2)
            else:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, T=T) - frange[i], -0.47, places=2)

        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(-1e26, 300),
                               7.5108, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(1e26, 300),
                               -1.4182, 4)
        self.assertAlmostEqual(sci_dos.get_fermi_interextrapolated(0.0, 300),
                               2.5226, 4)
    def solve_for_fermi_energy(self, temperature, chemical_potentials,
                               bulk_dos):
        """
        Solve for the Fermi energy self-consistently as a function of T
        and p_O2
        Observations are Defect concentrations, electron and hole conc
        Args:
            bulk_dos: bulk system dos (pymatgen Dos object)
            gap: Can be used to specify experimental gap.
                Will be useful if the self consistent Fermi level
                is > DFT gap
        Returns:
            Fermi energy
        """

        fdos = FermiDos(bulk_dos, bandgap=self.band_gap)

        def _get_total_q(ef):

            qd_tot = sum([
                d['charge'] * d['conc'] for d in self.defect_concentrations(
                    chemical_potentials=chemical_potentials,
                    temperature=temperature,
                    fermi_level=ef)
            ])
            qd_tot += fdos.get_doping(fermi=ef + self.vbm, T=temperature)
            return qd_tot

        return bisect(_get_total_q, -1., self.band_gap + 1.)
Exemple #11
0
    def featurize(self, dos, bandgap=None):
        """
        Args:
            dos (pymatgen Dos, CompleteDos or FermiDos):
            bandgap (float): for example the experimentally measured band gap
                or one that is calculated via more accurate methods than the
                one used to generate dos. dos will be scissored to have the
                same electronic band gap as bandgap.

        Returns ([float]): features are fermi levels in eV at the given
            concentrations and temperature + eref in eV if return_eref
        """
        dos = FermiDos(dos, bandgap=bandgap)
        feats = []
        eref = 0.0
        for c in self.dopings:
            fermi = dos.get_fermi(concentration=c,
                                  temperature=self.T,
                                  nstep=50)
            if isinstance(self.eref, str):
                if self.eref == "dos_fermi":
                    eref = dos.efermi
                elif self.eref in ["midgap", "vbm", "cbm"]:
                    ecbm, evbm = dos.get_cbm_vbm()
                    if self.eref == "midgap":
                        eref = (evbm + ecbm) / 2.0
                    elif self.eref == "vbm":
                        eref = evbm
                    elif self.eref == "cbm":
                        eref = ecbm
                elif self.eref == "band center":
                    eref = self.BC.featurize(dos.structure.composition)[0]
                else:
                    raise ValueError('Unsupported "eref": {}'.format(
                        self.eref))
            else:
                eref = self.eref
            if not self.return_eref:
                fermi -= eref
            feats.append(fermi)
        if self.return_eref:
            feats.append(eref)
        return feats
Exemple #12
0
    def test_doping_fermi(self):
        T = 300
        fermi0 = self.dos.efermi
        frange = [fermi0-0.5, fermi0, fermi0+2.0, fermi0+2.2]
        dopings = [self.dos.get_doping(fermi=f, T=T) for f in frange]
        ref_dopings = [3.48077e+21, 1.9235e+18, -2.6909e+16, -4.8723e+19]
        for i, c_ref in enumerate(ref_dopings):
            self.assertLessEqual(abs(dopings[i]/c_ref-1.0), 0.01)

        calc_fermis = [self.dos.get_fermi(c=c, T=T) for c in ref_dopings]
        for j, f_ref in enumerate(frange):
            self.assertAlmostEqual(calc_fermis[j], f_ref, 4)

        sci_dos = FermiDos(self.dos, bandgap=3.0)
        for i, c_ref in enumerate(ref_dopings):
            if c_ref < 0:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, T=T) - frange[i], 0.47, places=2)
            else:
                self.assertAlmostEqual(
                    sci_dos.get_fermi(c_ref, T=T) - frange[i], -0.47, places=2)
Exemple #13
0
    def featurize(self, dos, bandgap=None):
        """
        Args:
            dos (pymatgen Dos, CompleteDos or FermiDos):
            bandgap (float): for example the experimentally measured band gap
                or one that is calculated via more accurate methods than the
                one used to generate dos. dos will be scissored to have the
                same electronic band gap as bandgap.

        Returns ([float]): features are fermi levels in eV at the given
            concentrations and temperature + eref in eV if return_eref
        """
        dos = FermiDos(dos, bandgap=bandgap)
        feats = []
        eref = 0.0
        for c in self.dopings:
            fermi = dos.get_fermi(c=c, T=self.T, nstep=50)
            if isinstance(self.eref, str):
                if self.eref == "dos_fermi":
                    eref = dos.efermi
                elif self.eref in ["midgap", "vbm", "cbm"]:
                    ecbm, evbm = dos.get_cbm_vbm()
                    if self.eref == "midgap":
                        eref = (evbm + ecbm) / 2.0
                    elif self.eref == "vbm":
                        eref = evbm
                    elif self.eref == "cbm":
                        eref = ecbm
                elif self.eref == "band center":
                    eref = self.BC.featurize(dos.structure.composition)[0]
                else:
                    raise ValueError('Unsupported "eref": {}'.format(self.eref))
            else:
                eref = self.eref
            if not self.return_eref:
                fermi -= eref
            feats.append(fermi)
        if self.return_eref:
            feats.append(eref)
        return feats
    def solve_for_non_equilibrium_fermi_energy(self, temperature, quench_temperature,
                                               chemical_potentials, bulk_dos):
        """
        Solve for the Fermi energy after quenching in the defect concentrations at a higher
        temperature (the quench temperature),
        as outlined in P. Canepa et al (2017) Chemistry of Materials (doi: 10.1021/acs.chemmater.7b02909)

        Args:
            temperature: Temperature to equilibrate fermi energy at after quenching in defects
            quench_temperature: Temperature to equilibrate defect concentrations at (higher temperature)
            chemical_potentials: dict of chemical potentials to use for calculation fermi level
            bulk_dos: bulk system dos (pymatgen Dos object)
        Returns:
            Fermi energy dictated by charge neutrality with respect to frozen in defect concentrations
        """

        high_temp_fermi_level = self.solve_for_fermi_energy( quench_temperature, chemical_potentials,
                                                             bulk_dos)
        fixed_defect_charge = sum([
            d['charge'] * d['conc']
            for d in self.defect_concentrations(
                chemical_potentials=chemical_potentials, temperature=quench_temperature,
                fermi_level=high_temp_fermi_level)
            ])

        fdos = FermiDos(bulk_dos, bandgap=self.band_gap)
        _,fdos_vbm = fdos.get_cbm_vbm()

        def _get_total_q(ef):

            qd_tot = fixed_defect_charge
            qd_tot += fdos.get_doping(fermi=ef + fdos_vbm, T=temperature)
            return qd_tot

        return bisect(_get_total_q, -1., self.band_gap + 1.)

        return
Exemple #15
0
 def setUp(self):
     with open(
             os.path.join(PymatgenTest.TEST_FILES_DIR,
                          "complete_dos.json")) as f:
         self.dos = CompleteDos.from_dict(json.load(f))
     self.dos = FermiDos(self.dos)
Exemple #16
0
 def setUp(self):
     with open(os.path.join(test_dir, "complete_dos.json"), "r") as f:
         self.dos = CompleteDos.from_dict(json.load(f))
     self.dos = FermiDos(self.dos)
Exemple #17
0
 def setUp(self):
     with open(os.path.join(test_dir, "complete_dos.json"), "r") as f:
         self.dos = CompleteDos.from_dict(json.load(f))
     self.dos = FermiDos(self.dos)