Repository: bjmorgan/py-sc-fermi

Issue:
add a warning when defect concentrations are "beyond dilute limit"
It would be nice to warn the user if the code predicts defect concentrations beyond the dilute limit. This would be simple to implement, we would just need a definition of when the dilute limit approximation is becoming questionable. This could easily be heuristic.

Comments:

Patch:
diff --git a/py_sc_fermi/defect_system.py b/py_sc_fermi/defect_system.py
index 8206caf..0f61630 100644
--- a/py_sc_fermi/defect_system.py
+++ b/py_sc_fermi/defect_system.py
@@ -149,7 +149,6 @@ class DefectSystem(object):
         direction = +1.0
         e_fermi = (emin + emax) / 2.0
         step = 1.0
-        converged = False
         reached_e_min = False
         reached_e_max = False
 
@@ -167,7 +166,6 @@ class DefectSystem(object):
                 reached_e_min = True
                 direction = +1.0
             if abs(q_tot) < self.convergence_tolerance:
-                converged = True
                 break
             if q_tot > 0.0:
                 if direction == +1.0:
@@ -201,7 +199,7 @@ class DefectSystem(object):
         for ds in self.defect_species:
             concall = ds.get_concentration(e_fermi, self.temperature)
             if ds.fixed_concentration == None:
-                string += f"{ds.name:9}      : {concall * 1e24 / self.volume} cm^-3\n"
+                string += f"{ds.name:9}      : {concall * 1e24 / self.volume} cm^-3, (percentage of defective sites: {(concall / ds.nsites) * 100:.3} %)\n"
             else:
                 string += (
                     f"{ds.name:9}      : {concall * 1e24 / self.volume} cm^-3 [fixed]\n"
@@ -336,3 +334,25 @@ class DefectSystem(object):
                 for ds in self.defect_species
             }
             return {**run_stats, **decomp_concs}
+
+    def site_percentages(
+        self, 
+    ) -> Dict[str, float]:
+        """Returns a dictionary of the DefectSpecies in the DefectSystem which
+        giving the percentage of the sites in the structure that will host that 
+        defect.
+
+        Returns:
+            Dict[str, Any]: dictionary specifying the per-DefectSpecies site
+            concentrations.
+        """
+
+        e_fermi = self.get_sc_fermi()[0]
+
+        sum_concs = {
+                str(ds.name): float(
+                    (ds.get_concentration(e_fermi, self.temperature) / ds.nsites) * 100
+                )
+                for ds in self.defect_species
+            }
+        return sum_concs


Test patch:
diff --git a/tests/test_defect_system.py b/tests/test_defect_system.py
index 1903787..d68afb0 100644
--- a/tests/test_defect_system.py
+++ b/tests/test_defect_system.py
@@ -121,6 +121,19 @@ class TestDefectSystem(unittest.TestCase):
             {"Fermi Energy": 1, "p0": 1, "n0": 1, "O_i": 1, "v_O": 1},
         )
 
+    def test_site_percentages(self):
+        self.defect_system.get_sc_fermi = Mock(return_value=[1, {}])
+        self.defect_system.dos.carrier_concentrations = Mock(return_value=(1, 1))
+        self.defect_system.defect_species[0].get_concentration = Mock(return_value=1)
+        self.defect_system.defect_species[1].get_concentration = Mock(return_value=1)
+        self.defect_system.defect_species[0].nsites = 1
+        self.defect_system.defect_species[1].nsites = 1
+        self.defect_system.defect_species[0].name = "v_O"
+        self.defect_system.defect_species[1].name = "O_i"
+        self.assertEqual(
+            self.defect_system.site_percentages(), {"v_O": 100, "O_i": 100}
+        )
+
     def test__get_report_string(self):
         self.defect_system.get_sc_fermi = Mock(return_value=[0.5, {}])
         self.defect_system.dos.carrier_concentrations = Mock(return_value=(100, 100))