diff --git a/doped/interface/fermi_solver.py b/doped/interface/fermi_solver.py index b9dca573..53b151be 100644 --- a/doped/interface/fermi_solver.py +++ b/doped/interface/fermi_solver.py @@ -803,7 +803,7 @@ def pseudo_equilibrium_solve( Returns: pd.DataFrame: DataFrame containing defect and carrier concentrations - and the self consistent Fermi energy + and the self-consistent Fermi energy """ ( fermi_level, @@ -811,7 +811,7 @@ def pseudo_equilibrium_solve( holes, concentrations, ) = self.defect_thermodynamics.get_quenched_fermi_level_and_concentrations( - bulk_dos_vr=self.bulk_dos, + bulk_dos=self.bulk_dos, chempots=chempots, limit=None, annealing_temperature=annealing_temperature, diff --git a/doped/thermodynamics.py b/doped/thermodynamics.py index 1d801811..96c7afd0 100644 --- a/doped/thermodynamics.py +++ b/doped/thermodynamics.py @@ -1179,13 +1179,13 @@ def get_equilibrium_concentrations( dilute limit approximation. Note that these are the `equilibrium` defect concentrations! - DefectThermodynamics.get_quenched_fermi_level_and_concentrations() can - instead be used to calculate the Fermi level and defect concentrations - for a material grown/annealed at higher temperatures and then cooled - (quenched) to room/operating temperature (where defect concentrations - are assumed to remain fixed) - this is known as the frozen defect - approach and is typically the most valid approximation (see its - docstring for more information). + ``DefectThermodynamics.get_quenched_fermi_level_and_concentrations()`` + can instead be used to calculate the Fermi level and defect + concentrations for a material grown/annealed at higher temperatures + and then cooled (quenched) to room/operating temperature (where defect + concentrations are assumed to remain fixed) - this is known as the + frozen defect approach and is typically the most valid approximation + (see its docstring for more information). The degeneracy/multiplicity factor "g" is an important parameter in the defect concentration equation (see discussion in https://doi.org/10.1039/D2FD00043A @@ -1375,39 +1375,6 @@ def _parse_fermi_dos( ) return fdos - def _add_effective_dopant_concentration( - self, conc_df: pd.DataFrame, effective_dopant_concentration: Optional[float] = None - ): - """ - Add the effective dopant concentration to the concentration - ``DataFrame``. - """ - if effective_dopant_concentration is not None: - eff_dopant_df = pd.DataFrame( - { - "Defect": "Effective Dopant", - "Charge": np.sign(effective_dopant_concentration), - "Formation Energy (eV)": "N/A", - "Concentration (cm^-3)": np.abs(effective_dopant_concentration), - "Charge State Population": "100.0%", - }, - index=[0], - ) - for col in conc_df.columns: - if col not in eff_dopant_df.columns: - eff_dopant_df[col] = "N/A" # e.g. concentration per site, if per_site=True - - if "Charge" not in conc_df.columns: - eff_dopant_df = eff_dopant_df.drop( - columns=["Charge", "Formation Energy (eV)", "Charge State Population"] - ) - eff_dopant_df = eff_dopant_df.set_index("Defect") # Defect is index with summed conc df - return pd.concat([conc_df, eff_dopant_df]) - - return pd.concat([conc_df, eff_dopant_df], ignore_index=True) - - return conc_df - def get_equilibrium_fermi_level( self, bulk_dos: Optional[Union[FermiDos, Vasprun, PathLike]] = None, @@ -1431,13 +1398,13 @@ def get_equilibrium_fermi_level( default, unless ``bulk_band_gap_vr`` is set during defect parsing. Note that this assumes `equilibrium` defect concentrations! - DefectThermodynamics.get_quenched_fermi_level_and_concentrations() can - instead be used to calculate the Fermi level and defect concentrations - for a material grown/annealed at higher temperatures and then cooled - (quenched) to room/operating temperature (where defect concentrations - are assumed to remain fixed) - this is known as the frozen defect - approach and is typically the most valid approximation (see its - docstring for more information). + ``DefectThermodynamics.get_quenched_fermi_level_and_concentrations()`` + can instead be used to calculate the Fermi level and defect + concentrations for a material grown/annealed at higher temperatures and + then cooled (quenched) to room/operating temperature (where defect + concentrations are assumed to remain fixed) - this is known as the + frozen defect approach and is typically the most valid approximation + (see its docstring for more information). Note that the bulk DOS calculation should be well-converged with respect to k-points for accurate Fermi level predictions! @@ -3141,6 +3108,49 @@ def __repr__(self): ) +def _add_effective_dopant_concentration( + conc_df: pd.DataFrame, effective_dopant_concentration: Optional[float] = None +): + """ + Add the effective dopant concentration to the concentration ``DataFrame``. + + Args: + conc_df (pd.DataFrame): + DataFrame of defect concentrations. + effective_dopant_concentration (float): + The effective dopant concentration to add to the DataFrame. + + Returns: + pd.DataFrame: DataFrame of defect concentrations with the effective + dopant concentration + """ + if effective_dopant_concentration is None: + return conc_df + + eff_dopant_df = pd.DataFrame( + { + "Defect": "Effective Dopant", + "Charge": np.sign(effective_dopant_concentration), + "Formation Energy (eV)": "N/A", + "Concentration (cm^-3)": np.abs(effective_dopant_concentration), + "Charge State Population": "100.0%", + }, + index=[0], + ) + for col in conc_df.columns: + if col not in eff_dopant_df.columns: + eff_dopant_df[col] = "N/A" # e.g. concentration per site, if per_site=True + + if "Charge" not in conc_df.columns: + eff_dopant_df = eff_dopant_df.drop( + columns=["Charge", "Formation Energy (eV)", "Charge State Population"] + ) + eff_dopant_df = eff_dopant_df.set_index("Defect") # Defect is index with summed conc df + return pd.concat([conc_df, eff_dopant_df]) + + return pd.concat([conc_df, eff_dopant_df], ignore_index=True) + + def _group_defect_charge_state_concentrations( conc_df: pd.DataFrame, per_site: bool = False, skip_formatting: bool = False ): diff --git a/tests/test_interface_fermi_solver.py b/tests/test_interface_fermi_solver.py index 66be3132..83b3debc 100644 --- a/tests/test_interface_fermi_solver.py +++ b/tests/test_interface_fermi_solver.py @@ -55,7 +55,7 @@ def test_assert_scan_temperature_raises(self): annealing_temperature_range=[1, 2, 3], quenching_temperature_range=None, ) - print(exc.value) # for debugging + print(exc.value) # for debugging with pytest.raises(ValueError) as exc: self.fs.scan_temperature( @@ -64,7 +64,7 @@ def test_assert_scan_temperature_raises(self): annealing_temperature_range=None, quenching_temperature_range=[1, 2, 3], ) - print(exc.value) # for debugging + print(exc.value) # for debugging with pytest.raises(ValueError) as exc: self.fs.scan_temperature( @@ -73,7 +73,7 @@ def test_assert_scan_temperature_raises(self): annealing_temperature_range=None, quenching_temperature_range=None, ) - print(exc.value) # for debugging + print(exc.value) # for debugging def test_get_interpolated_chempots(self):