Skip to content

Commit

Permalink
General FermiSolver tests updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kavanase committed Jan 6, 2025
1 parent 522463f commit bd19bfc
Showing 1 changed file with 45 additions and 67 deletions.
112 changes: 45 additions & 67 deletions tests/test_fermisolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,19 @@ def setUp(self):
# Mock the _DOS attribute for py-sc-fermi backend if needed
self.solver_py_sc_fermi._DOS = MagicMock()

def test_default_initialization(self):
"""
Test default initialization, which uses ``doped`` backend.
"""
solver = FermiSolver(defect_thermodynamics=self.example_thermo)
assert solver.backend == "doped"
assert solver.defect_thermodynamics == self.example_thermo
assert solver.volume is not None

@patch("doped.thermodynamics.importlib.util.find_spec")
def test_valid_initialization_doped_backend(self, mock_find_spec):
"""
Test initialization with doped backend.
Test initialization with ``doped`` backend.
"""
mock_find_spec.return_value = None # Simulate py_sc_fermi not installed

Expand All @@ -195,8 +204,6 @@ def test_valid_initialization_doped_backend(self, mock_find_spec):

# Initialize FermiSolver
solver = FermiSolver(defect_thermodynamics=self.example_thermo, backend="doped")

# Assertions
assert solver.backend == "doped"
assert solver.defect_thermodynamics == self.example_thermo
assert solver.volume is not None
Expand All @@ -205,50 +212,41 @@ def test_valid_initialization_doped_backend(self, mock_find_spec):
@patch("doped.thermodynamics.FermiSolver._activate_py_sc_fermi_backend")
def test_valid_initialization_py_sc_fermi_backend(self, mock_activate_backend, mock_find_spec):
"""
Test initialization with py-sc-fermi backend.
Test initialization with ``py-sc-fermi`` backend.
"""
mock_find_spec.return_value = True # Simulate py_sc_fermi installed
mock_activate_backend.return_value = None

# Initialize FermiSolver
solver = FermiSolver(defect_thermodynamics=self.example_thermo, backend="py-sc-fermi")

# Assertions
assert solver.backend == "py-sc-fermi"
assert solver.defect_thermodynamics == self.example_thermo
assert solver.volume is not None
mock_activate_backend.assert_called_once()

@patch("doped.thermodynamics.importlib.util.find_spec")
def test_missing_bulk_dos(self, mock_find_spec):
def test_missing_bulk_dos(self):
"""
Test initialization failure due to missing bulk_dos.
"""
mock_find_spec.return_value = None

# Remove bulk_dos
self.example_thermo.bulk_dos = None
self.example_thermo.bulk_dos = None # Remove bulk_dos

with pytest.raises(ValueError) as context:
FermiSolver(defect_thermodynamics=self.example_thermo, backend="doped")

assert "No bulk DOS calculation" in str(context.value)

@patch("doped.thermodynamics.importlib.util.find_spec")
def test_invalid_backend(self, mock_find_spec):
def test_invalid_backend(self):
"""
Test initialization failure due to invalid backend.
"""
mock_find_spec.return_value = None

with pytest.raises(ValueError) as context:
FermiSolver(defect_thermodynamics=self.example_thermo, backend="invalid_backend")

assert "Unrecognised `backend`" in str(context.value)

def test_activate_backend_py_sc_fermi_installed(self):
"""
Test backend activation when py_sc_fermi is installed.
Test backend activation when ``py_sc_fermi`` is installed.
"""
with patch.dict(
"sys.modules",
Expand All @@ -268,21 +266,16 @@ def test_activate_backend_py_sc_fermi_installed(self):
# Activate backend
self.solver_py_sc_fermi._activate_py_sc_fermi_backend()

# Assertions
assert self.solver_py_sc_fermi._DefectSystem is not None
assert self.solver_py_sc_fermi._DefectSpecies is not None
assert self.solver_py_sc_fermi._DefectChargeState is not None
assert self.solver_py_sc_fermi._DOS is not None
assert self.solver_py_sc_fermi.py_sc_fermi_dos is not None
assert self.solver_py_sc_fermi.multiplicity_scaling is not None
assert self.solver_py_sc_fermi._DefectSystem == DefectSystem
assert self.solver_py_sc_fermi._DefectSpecies == DefectSpecies
assert self.solver_py_sc_fermi._DefectChargeState == DefectChargeState
assert self.solver_py_sc_fermi._DOS == DOS
assert self.solver_py_sc_fermi.py_sc_fermi_dos is not None
assert self.solver_py_sc_fermi.multiplicity_scaling is not None

def test_activate_backend_py_sc_fermi_not_installed(self):
"""
Test backend activation failure when py_sc_fermi is not installed.
Test backend activation failure when ``py_sc_fermi`` is not installed.
"""
original_import = builtins.__import__

Expand Down Expand Up @@ -351,62 +344,48 @@ def test_activate_backend_non_integer_volume_scaling(self):
assert len(w) > 0
assert "non-integer" in str(w[-1].message)

# Tests for _check_required_backend_and_error

def test_check_required_backend_and_error_doped_missing_bulk_dos(self):
"""
Test that RuntimeError is raised when bulk_dos is missing for doped
backend.
"""
# Remove bulk_dos to simulate missing DOS
self.solver_doped.defect_thermodynamics.bulk_dos = None

with pytest.raises(RuntimeError) as context:
self.solver_doped._check_required_backend_and_error("doped")
assert "This function is only supported for the doped backend" in str(context.value)

# Tests for _check_required_backend_and_error:
def test_check_required_backend_and_error_doped_correct(self):
"""
Test that no error is raised when bulk_dos is present for doped
backend.
Test that no error is raised with ``_check_required_backend_and_error``
for ``doped`` backend.
"""
# bulk_dos is correctly set
try:
self.solver_doped._check_required_backend_and_error("doped")
except RuntimeError as e:
self.fail(f"RuntimeError raised unexpectedly: {e}")

def test_check_required_backend_and_error_py_sc_fermi_missing_DOS(self):
"""
Test that RuntimeError is raised when _DOS is missing for py-sc-fermi
backend.
Test that ``RuntimeError`` is raised when ``_DOS`` is missing for ``py-
sc-fermi`` backend.
"""
# first test that no error is raised when _DOS is present
self.solver_py_sc_fermi._check_required_backend_and_error("py-sc-fermi")

# Remove _DOS to simulate missing DOS
self.solver_py_sc_fermi._DOS = None

with pytest.raises(RuntimeError) as context:
self.solver_py_sc_fermi._check_required_backend_and_error("py-sc-fermi")
assert "This function is only supported for the py-sc-fermi backend" in str(context.value)

def test_check_required_backend_and_error_py_sc_fermi_correct(self):
def test_check_required_backend_and_error_py_sc_fermi_doped_backend(self):
"""
Test that no error is raised when _DOS is present for py-sc-fermi
backend.
Test that ``RuntimeError`` is raised when
``_check_required_backend_and_error`` is called when ``py-sc-fermi``
backend functionality is required, but the backend is set to ``doped``.
"""
# _DOS is correctly set
try:
self.solver_py_sc_fermi._check_required_backend_and_error("py-sc-fermi")
except RuntimeError as e:
self.fail(f"RuntimeError raised unexpectedly: {e}")
with pytest.raises(RuntimeError) as context:
self.solver_doped._check_required_backend_and_error("py-sc-fermi")
assert "This function is only supported for the py-sc-fermi backend" in str(context.value)

# Tests for _get_fermi_level_and_carriers

def test_get_fermi_level_and_carriers(self):
"""
Test _get_fermi_level_and_carriers returns correct values for doped
backend.
Test ``_get_fermi_level_and_carriers`` returns correct values for
``doped`` backend.
"""
# Use actual method
single_chempot_dict, el_refs = self.solver_py_sc_fermi._get_single_chempot_dict(limit="Te-rich")
fermi_level, electrons, holes = self.solver_doped._get_fermi_level_and_carriers(
single_chempot_dict=single_chempot_dict,
Expand All @@ -415,36 +394,35 @@ def test_get_fermi_level_and_carriers(self):
effective_dopant_concentration=None,
)

# Assertions
assert isinstance(fermi_level, float)
assert isinstance(electrons, float)
assert isinstance(holes, float)
assert np.isclose(
fermi_level, self.example_thermo.get_equilibrium_fermi_level(limit="Te-rich", temperature=300)
)
doped_e_h = get_e_h_concs(self.CdTe_fermi_dos, fermi_level + self.example_thermo.vbm, 300)
assert np.isclose(electrons, doped_e_h[0], rtol=1e-3)
assert np.isclose(holes, doped_e_h[1], rtol=1e-3)

# Tests for _get_single_chempot_dict

def test_get_single_chempot_dict_correct(self):
"""
Test that the correct chemical potential dictionary is returned.
"""
single_chempot_dict, el_refs = self.solver_py_sc_fermi._get_single_chempot_dict(limit="Te-rich")

expected_chempots = self.example_thermo.chempots["limits_wrt_el_refs"]["CdTe-Te"]
assert single_chempot_dict == expected_chempots
assert single_chempot_dict == self.example_thermo.chempots["limits_wrt_el_refs"]["CdTe-Te"]
assert el_refs == self.example_thermo.el_refs

def test_get_single_chempot_dict_limit_not_found(self):
"""
Test that ValueError is raised when the specified limit is not found.
Test that ``ValueError`` is raised when the specified limit is not
found.
"""
with pytest.raises(ValueError) as context:
self.solver_doped._get_single_chempot_dict(limit="nonexistent_limit")
assert "Limit 'nonexistent_limit' not found" in str(context.value)

# Tests for equilibrium_solve

def test_equilibrium_solve_doped_backend(self):
"""
Test equilibrium_solve method for doped backend.
Test ``equilibrium_solve`` method for doped backend.
"""
single_chempot_dict, el_refs = self.solver_py_sc_fermi._get_single_chempot_dict(limit="Te-rich")

Expand Down

0 comments on commit bd19bfc

Please sign in to comment.