From e3bf65121857af4453e26c7490a747c1f02a5ac5 Mon Sep 17 00:00:00 2001 From: wolearyc Date: Sun, 4 Aug 2024 13:39:42 -0700 Subject: [PATCH] added exception testing --- docs/coverage-badge.svg | 2 +- docs/tests-badge.svg | 2 +- .../polarizability/interpolation/__init__.py | 7 +- test/conftest.py | 16 +- test/data/TiO2/known_gaussian_spectrum.npz | Bin 0 -> 16530 bytes test/data/TiO2/known_lorentzian_spectrum.npz | Bin 0 -> 16530 bytes test/tests/test_interpolation.py | 254 ++++++++++++++++++ test/tests/test_polarizability.py | 77 ------ test/tests/test_spectrum.py | 214 ++++++++++++++- 9 files changed, 484 insertions(+), 88 deletions(-) create mode 100644 test/data/TiO2/known_gaussian_spectrum.npz create mode 100644 test/data/TiO2/known_lorentzian_spectrum.npz create mode 100644 test/tests/test_interpolation.py delete mode 100644 test/tests/test_polarizability.py diff --git a/docs/coverage-badge.svg b/docs/coverage-badge.svg index a3bab45..d3ffa3d 100644 --- a/docs/coverage-badge.svg +++ b/docs/coverage-badge.svg @@ -1 +1 @@ -coverage: 88.69%coverage88.69% +coverage: 91.42%coverage91.42% diff --git a/docs/tests-badge.svg b/docs/tests-badge.svg index a6ead51..8a38d99 100644 --- a/docs/tests-badge.svg +++ b/docs/tests-badge.svg @@ -1 +1 @@ -tests: 28tests28 +tests: 58tests58 diff --git a/ramannoodle/polarizability/interpolation/__init__.py b/ramannoodle/polarizability/interpolation/__init__.py index 174e5f7..ddc007a 100644 --- a/ramannoodle/polarizability/interpolation/__init__.py +++ b/ramannoodle/polarizability/interpolation/__init__.py @@ -14,7 +14,7 @@ is_collinear_with_all, ) from ...symmetry import StructuralSymmetry -from ...exceptions import InvalidDOFException +from ...exceptions import InvalidDOFException, get_type_error from ... import io from ...io.io_utils import pathify_as_list @@ -233,9 +233,8 @@ def add_dof( # pylint: disable=too-many-locals ) from exc raise exc except TypeError as exc: - raise TypeError( - "interpolation_order should be int, not " - f"{type(interpolation_order).__name__}" + raise get_type_error( + "interpolation_order", interpolation_order, "int" ) from exc self._cartesian_basis_vectors += basis_vectors_to_add diff --git a/test/conftest.py b/test/conftest.py index 0e1b5f8..005dc72 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -11,13 +11,13 @@ from ramannoodle import io -@pytest.fixture +@pytest.fixture(scope="session") def outcar_path_fixture(request: FixtureRequest) -> Path: """Return an outcar path.""" return Path(request.param) -@pytest.fixture +@pytest.fixture(scope="session") def outcar_file_fixture( request: FixtureRequest, ) -> Generator[TextIO, None, None]: @@ -29,7 +29,15 @@ def outcar_file_fixture( file.close() -@pytest.fixture +# HACK: indirect fixtures are unable to be scoped, so manually cache. +symmetry_cache = {} + + +@pytest.fixture(scope="session") def outcar_symmetry_fixture(request: FixtureRequest) -> StructuralSymmetry: """Return a structural symmetry.""" - return io.read_structural_symmetry(request.param, file_format="outcar") + if request.param not in symmetry_cache: + symmetry_cache[request.param] = io.read_structural_symmetry( + request.param, file_format="outcar" + ) + return symmetry_cache[request.param] diff --git a/test/data/TiO2/known_gaussian_spectrum.npz b/test/data/TiO2/known_gaussian_spectrum.npz new file mode 100644 index 0000000000000000000000000000000000000000..1a31b21529fbfd2ce83f2a8f413d90d6ca27f10a GIT binary patch literal 16530 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 None: + """Test for find_duplicates (success).""" + assert find_duplicates(vectors) == known + + +@pytest.mark.parametrize( + "vectors, exception_type, in_reason", + [ + (1, TypeError, "should have type Iterable, not int"), + (["string", TypeError, "not array_like"]), + ], +) +def test_find_duplicates_exception( + vectors: NDArray[np.float64], exception_type: Type[Exception], in_reason: str +) -> None: + """Test for find_duplicates (exception).""" + with pytest.raises(exception_type) as error: + find_duplicates(vectors) + assert in_reason in str(error.value) + + +@pytest.mark.parametrize( + "outcar_symmetry_fixture,displaced_atom_index, amplitudes, known_dof_added", + [ + ("test/data/STO_RATTLED_OUTCAR", 0, np.array([-0.05, 0.05, 0.01, -0.01]), 1), + ("test/data/TiO2/phonons_OUTCAR", 0, np.array([0.01]), 72), + ], + indirect=["outcar_symmetry_fixture"], +) +def test_add_dof( + outcar_symmetry_fixture: StructuralSymmetry, + displaced_atom_index: int, + amplitudes: NDArray[np.float64], + known_dof_added: int, +) -> None: + """Test add_dof (normal).""" + symmetry = outcar_symmetry_fixture + model = InterpolationPolarizabilityModel(symmetry, np.zeros((3, 3))) + displacement = symmetry._fractional_positions * 0 + displacement[displaced_atom_index][0] = 1.0 + polarizabilities = np.zeros((len(amplitudes), 3, 3)) + model.add_dof(displacement, amplitudes, polarizabilities, 1) + assert len(model._cartesian_basis_vectors) == known_dof_added + assert np.isclose(np.linalg.norm(model._cartesian_basis_vectors[0]), 1) + + +@pytest.mark.parametrize( + "outcar_symmetry_fixture,displaced_atom_indexes, amplitudes,polarizabilities," + "interpolation_order,exception_type,in_reason", + [ + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1]), + np.zeros((1, 3, 3)), + 4, + InvalidDOFException, + "insufficient points", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1, 0.1]), + np.zeros((1, 3, 3)), + 1, + ValueError, + "polarizabilities has wrong shape", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1]), + np.zeros((2, 3, 3)), + 1, + ValueError, + "polarizabilities has wrong shape", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1]), + np.zeros((2, 3, 3)), + 1, + ValueError, + "polarizabilities has wrong shape", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0], [0, 1]], + np.array([0.1]), + np.zeros((1, 3, 3)), + 1, + InvalidDOFException, + "not orthogonal", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + [0.1], + np.zeros((1, 3, 3)), + 1, + TypeError, + "amplitudes should have type ndarray, not list", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1]), + list(np.zeros((1, 3, 3))), + 1, + TypeError, + "polarizabilities should have type ndarray, not list", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1]), + np.zeros((1, 2, 2)), + 1, + ValueError, + "polarizabilities has wrong shape: (1,2,2) != (1,3,3)", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1]), + np.zeros((1, 3, 3)), + -1.6, + TypeError, + "interpolation_order should have type int, not float", + ), + ( + "test/data/STO_RATTLED_OUTCAR", + [[0]], + np.array([0.1, 0.1]), + np.zeros((2, 3, 3)), + -1.6, + InvalidDOFException, + "due to symmetry, amplitude 0.1 should not be specified", + ), + ], + indirect=["outcar_symmetry_fixture"], +) +def test_add_dof_exception( + outcar_symmetry_fixture: StructuralSymmetry, + displaced_atom_indexes: list[list[int]], + amplitudes: NDArray[np.float64], + polarizabilities: NDArray[np.float64], + interpolation_order: int, + exception_type: Type[Exception], + in_reason: str, +) -> None: + """Test add_dof (exception).""" + symmetry = outcar_symmetry_fixture + model = InterpolationPolarizabilityModel(symmetry, np.zeros((3, 3))) + with pytest.raises(exception_type) as error: + for atom_indexes in displaced_atom_indexes: + for atom_index in atom_indexes: + displacement = symmetry.get_fractional_positions() * 0 + displacement[atom_index] = 1 + model.add_dof( + displacement, amplitudes, polarizabilities, interpolation_order + ) + + assert in_reason in str(error.value) + + +@pytest.mark.parametrize( + "outcar_symmetry_fixture,outcar_files,interpolation_order,exception_type,in_reason", + [ + ( + "test/data/STO_RATTLED_OUTCAR", + ["test/data/TiO2/Ti5_0.1x_eps_OUTCAR"], + 1, + InvalidDOFException, + "incompatible outcar", + ), + ( + "test/data/TiO2/phonons_OUTCAR", + ["test/data/TiO2/Ti5_0.1x_eps_OUTCAR"], + 3, + InvalidDOFException, + "insufficient points (3)", + ), + ( + "test/data/TiO2/phonons_OUTCAR", + [ + "test/data/TiO2/Ti5_0.1x_eps_OUTCAR", + "test/data/TiO2/Ti5_0.1x_eps_OUTCAR", + ], + 1, + InvalidDOFException, + "due to symmetry, amplitude", + ), + ( + "test/data/TiO2/phonons_OUTCAR", + [ + "this_outcar_does_not_exist", + ], + 1, + FileNotFoundError, + "No such file or directory", + ), + ( + "test/data/TiO2/phonons_OUTCAR", + [ + "test/data/TiO2/Ti5_0.1x_eps_OUTCAR", + "test/data/TiO2/Ti5_0.1y_eps_OUTCAR", + ], + 1, + InvalidDOFException, + "is not collinear", + ), + ], + indirect=["outcar_symmetry_fixture"], +) +def test_add_dof_from_files_exception( + outcar_symmetry_fixture: StructuralSymmetry, + outcar_files: list[str], + interpolation_order: int, + exception_type: Type[Exception], + in_reason: str, +) -> None: + """Test exceptions in add_dof_from_files.""" + symmetry = outcar_symmetry_fixture + model = InterpolationPolarizabilityModel(symmetry, np.zeros((3, 3))) + with pytest.raises(exception_type) as error: + model.add_dof_from_files(outcar_files, "outcar", interpolation_order) + assert in_reason in str(error.value) diff --git a/test/tests/test_polarizability.py b/test/tests/test_polarizability.py deleted file mode 100644 index badde58..0000000 --- a/test/tests/test_polarizability.py +++ /dev/null @@ -1,77 +0,0 @@ -"""Testing for the polarizability.""" - -from pathlib import Path - -import numpy as np -from numpy.typing import NDArray - -import pytest - -from ramannoodle.polarizability.polarizability_utils import find_duplicates -from ramannoodle.polarizability.interpolation import InterpolationPolarizabilityModel -from ramannoodle.io.vasp import read_structural_symmetry_from_outcar -from ramannoodle.exceptions import InvalidDOFException - -# pylint: disable=protected-access - - -@pytest.mark.parametrize( - "vectors, known", - [ - (np.array([-0.05, 0.05, 0.01, -0.01]), None), - (np.array([-0.05, 0.05, -0.05, -0.01]), -0.05), - ], -) -def test_find_duplicates(vectors: list[NDArray[np.float64]], known: bool) -> None: - """Test.""" - assert find_duplicates(vectors) == known - - -@pytest.mark.parametrize( - "outcar_path_fixture,displaced_atom_index, amplitudes, known_dof_added", - [ - ("test/data/STO_RATTLED_OUTCAR", 0, np.array([-0.05, 0.05, 0.01, -0.01]), 1), - ("test/data/TiO2/phonons_OUTCAR", 0, np.array([0.01]), 72), - ], - indirect=["outcar_path_fixture"], -) -def test_add_dof( - outcar_path_fixture: Path, - displaced_atom_index: int, - amplitudes: NDArray[np.float64], - known_dof_added: int, -) -> None: - """Test.""" - symmetry = read_structural_symmetry_from_outcar(outcar_path_fixture) - model = InterpolationPolarizabilityModel(symmetry, np.zeros((3, 3))) - displacement = symmetry._fractional_positions * 0 - displacement[displaced_atom_index][0] = 1.0 - polarizabilities = np.zeros((len(amplitudes), 3, 3)) - model.add_dof(displacement, amplitudes, polarizabilities, 1) - assert len(model._cartesian_basis_vectors) == known_dof_added - assert np.isclose(np.linalg.norm(model._cartesian_basis_vectors[0]), 1) - - -@pytest.mark.parametrize( - "outcar_path_fixture,displaced_atom_index, amplitudes", - [ - ("test/data/STO_RATTLED_OUTCAR", 0, np.array([0.01, 0.01])), - ("test/data/TiO2/phonons_OUTCAR", 0, np.array([-0.01, 0.01])), - ], - indirect=["outcar_path_fixture"], -) -def test_overspecified_dof( - outcar_path_fixture: Path, - displaced_atom_index: int, - amplitudes: NDArray[np.float64], -) -> None: - """Test.""" - symmetry = read_structural_symmetry_from_outcar(outcar_path_fixture) - model = InterpolationPolarizabilityModel(symmetry, np.zeros((3, 3))) - displacement = symmetry._fractional_positions * 0 - displacement[displaced_atom_index][0] = 1.0 - polarizabilities = np.zeros((len(amplitudes), 3, 3)) - with pytest.raises(InvalidDOFException) as error: - model.add_dof(displacement, amplitudes, polarizabilities, 1) - - assert "should not be specified" in str(error.value) diff --git a/test/tests/test_spectrum.py b/test/tests/test_spectrum.py index 9e4f644..6d1bda1 100644 --- a/test/tests/test_spectrum.py +++ b/test/tests/test_spectrum.py @@ -1,20 +1,27 @@ """Testing for spectra.""" from pathlib import Path +from typing import Type import numpy as np +from numpy.typing import NDArray import pytest from ramannoodle.polarizability.interpolation import InterpolationPolarizabilityModel from ramannoodle.symmetry import StructuralSymmetry from ramannoodle import io +from ramannoodle.spectrum.spectrum_utils import ( + convolve_intensities, + get_bose_einstein_correction, + get_laser_correction, +) # pylint: disable=protected-access def _get_all_eps_outcars(directory: str) -> list[str]: - """Return name of all eps OUTCARs in a directory.""" + """Return name of all *eps_OUTCARs in a directory.""" path = Path(directory) return [str(item) for item in path.glob("*eps_OUTCAR")] @@ -22,6 +29,10 @@ def _get_all_eps_outcars(directory: str) -> list[str]: def _validate_polarizabilities( model: InterpolationPolarizabilityModel, data_directory: str ) -> None: + """Check that a model accurately predicts polarizabilities. + + This function will use all *eps_OUTCAR's in a directory as references. + """ for outcar_path in _get_all_eps_outcars(data_directory): positions, known_polarizability = io.read_positions_and_polarizability( f"{outcar_path}", file_format="outcar" @@ -94,3 +105,204 @@ def test_spectrum( assert np.isclose(wavenumbers, known_wavenumbers).all() assert np.isclose(intensities, known_intensities, atol=1e-4).all() + + +@pytest.mark.parametrize( + "spectrum_path,known_gaussian_spectrum_path,known_lorentzian_spectrum_path", + [ + ( + "test/data/TiO2/known_spectrum.npz", + "test/data/TiO2/known_gaussian_spectrum.npz", + "test/data/TiO2/known_lorentzian_spectrum.npz", + ) + ], +) +def test_convolve_intensities( + spectrum_path: str, + known_gaussian_spectrum_path: str, + known_lorentzian_spectrum_path: str, +) -> None: + """Test convolutions.""" + with np.load(spectrum_path) as spectrum: + wavenumbers = spectrum["wavenumbers"] + intensities = spectrum["intensities"] + + gaussian_wavenumbers, gaussian_intensities = convolve_intensities( + wavenumbers, intensities, "gaussian" + ) + with np.load(known_gaussian_spectrum_path) as known_spectrum: + known_wavenumbers = known_spectrum["wavenumbers"] + known_intensities = known_spectrum["intensities"] + assert np.isclose(gaussian_wavenumbers, known_wavenumbers).all() + assert np.isclose(gaussian_intensities, known_intensities).all() + + lorentzian_wavenumbers, lorentzian_intensities = convolve_intensities( + wavenumbers, intensities, "lorentzian" + ) + with np.load(known_lorentzian_spectrum_path) as known_spectrum: + known_wavenumbers = known_spectrum["wavenumbers"] + known_intensities = known_spectrum["intensities"] + assert np.isclose(lorentzian_wavenumbers, known_wavenumbers).all() + assert np.isclose(lorentzian_intensities, known_intensities).all() + + +@pytest.mark.parametrize( + "wavenumbers,intensities,function,width,out_wavenumbers,exception_type,in_reason", + [ + ( + np.array([1, 2, 3]), + [0, 3, 0], + "gaussian", + 5, + None, + TypeError, + "intensities should have type ndarray, not list", + ), + ( + [1, 2, 3], + np.array([0, 3, 0]), + "gaussian", + 5, + None, + TypeError, + "wavenumbers should have type ndarray, not list", + ), + ( + np.array([1, 2, 3, 4]), + np.array([0, 3, 0]), + "gaussian", + 5, + None, + ValueError, + "intensities has wrong shape: (3,) != (4,)", + ), + ( + np.array([[1, 2, 3, 5], [2, 3, 4, 5]]), + np.array([0, 3, 0, 4]), + "gaussian", + 5, + None, + ValueError, + "wavenumbers has wrong shape: (2,4) != (_,)", + ), + ( + np.array([1, 2, 3]), + np.array([0, 3, 0]), + "smoother", + 5, + None, + ValueError, + "unsupported convolution type: smoother", + ), + ( + np.array([1, 2, 3]), + np.array([0, 3, 0]), + "gaussian", + -4, + None, + ValueError, + "invalid width: -4 <= 0", + ), + ( + np.array([1, 2, 3]), + np.array([0, 3, 0]), + "gaussian", + "not_a_int", + None, + TypeError, + "width should have type float, not str", + ), + ( + np.array([1, 2, 3]), + np.array([0, 3, 0]), + "gaussian", + 5.0, + np.array(([1, 2], [1, 2])), + ValueError, + "out_wavenumbers has wrong shape: (2,2) != (_,)", + ), + ], +) +def test_convolve_intensities_exception( # pylint: disable=too-many-arguments + wavenumbers: NDArray[np.float64], + intensities: NDArray[np.float64], + function: str, + width: float, + out_wavenumbers: NDArray[np.float64], + exception_type: Type[Exception], + in_reason: str, +) -> None: + """Test exceptions raised by convolve_intensities.""" + with pytest.raises(exception_type) as error: + convolve_intensities(wavenumbers, intensities, function, width, out_wavenumbers) + assert in_reason in str(error.value) + + +@pytest.mark.parametrize( + "wavenumbers,temperature,exception_type,in_reason", + [ + ( + [1, 2, 3], + 300, + TypeError, + "wavenumbers should have type ndarray, not list", + ), + ( + [1, 2, 3], + -300, + ValueError, + "invalid temperature: -300 <= 0", + ), + ( + [1, 2, 3], + "string temperature", + TypeError, + "temperature should have type float, not str", + ), + ], +) +def test_get_bose_einstein_correction_exceptions( + wavenumbers: NDArray[np.float64], + temperature: float, + exception_type: Type[Exception], + in_reason: str, +) -> None: + """Test exceptions raised by get_bose_einstein_correction.""" + with pytest.raises(exception_type) as error: + get_bose_einstein_correction(wavenumbers, temperature) + assert in_reason in str(error.value) + + +@pytest.mark.parametrize( + "wavenumbers,laser_wavenumber,exception_type,in_reason", + [ + ( + [1, 2, 3], + 600, + TypeError, + "wavenumbers should have type ndarray, not list", + ), + ( + [1, 2, 3], + -600, + ValueError, + "invalid laser_wavenumber: -600 <= 0", + ), + ( + [1, 2, 3], + "string wavelength", + TypeError, + "laser_wavenumber should have type float, not str", + ), + ], +) +def test_get_laser_correction( + wavenumbers: NDArray[np.float64], + laser_wavenumber: float, + exception_type: Type[Exception], + in_reason: str, +) -> None: + """Test exceptions raised by get_laser_correction.""" + with pytest.raises(exception_type) as error: + get_laser_correction(wavenumbers, laser_wavenumber) + assert in_reason in str(error.value)