From 666f90f7e6244fbb1c1288239855fd2223e54126 Mon Sep 17 00:00:00 2001 From: Janosh Riebesell Date: Thu, 19 Jan 2023 21:28:38 -0800 Subject: [PATCH] unittest2pytest --write --nobackups pymatgen/analysis followed by adding `from pytest import approx` isort --add-import 'from pytest import approx' $(git status -s | cut -c4-) --- pymatgen/analysis/gb/tests/test_grain.py | 137 +++--- pymatgen/analysis/local_env.py | 18 +- .../analysis/magnetism/tests/test_analyzer.py | 108 ++-- .../tests/test_dopant_predictor.py | 48 +- .../tests/test_substitution_probability.py | 28 +- .../tests/test_volume_predictor.py | 56 ++- pymatgen/analysis/tests/test_disorder.py | 10 +- pymatgen/analysis/tests/test_energy_models.py | 22 +- pymatgen/analysis/tests/test_eos.py | 31 +- pymatgen/analysis/tests/test_ewald.py | 79 ++- .../analysis/tests/test_functional_groups.py | 44 +- pymatgen/analysis/tests/test_graphs.py | 278 +++++------ pymatgen/analysis/tests/test_hhi.py | 20 +- .../tests/test_interface_reactions.py | 208 +++----- pymatgen/analysis/tests/test_local_env.py | 464 +++++++++--------- .../analysis/tests/test_molecule_matcher.py | 208 ++++---- pymatgen/analysis/tests/test_nmr.py | 41 +- pymatgen/analysis/tests/test_path_finder.py | 4 +- pymatgen/analysis/tests/test_piezo.py | 10 +- .../analysis/tests/test_pourbaix_diagram.py | 166 ++++--- pymatgen/analysis/tests/test_prototypes.py | 45 +- .../tests/test_reaction_calculator.py | 138 +++--- .../analysis/tests/test_structure_analyzer.py | 71 ++- .../analysis/tests/test_structure_matcher.py | 329 +++++++------ .../analysis/tests/test_surface_analysis.py | 81 ++- .../analysis/tests/test_transition_state.py | 4 +- pymatgen/analysis/tests/test_wulff.py | 34 +- pymatgen/analysis/tests/test_xps.py | 4 +- .../topological/tests/test_spillage.py | 4 +- pymatgen/analysis/xas/tests/test_spectrum.py | 104 ++-- pymatgen/io/vasp/outputs.py | 2 +- pymatgen/io/vasp/sets.py | 2 +- 32 files changed, 1352 insertions(+), 1446 deletions(-) diff --git a/pymatgen/analysis/gb/tests/test_grain.py b/pymatgen/analysis/gb/tests/test_grain.py index 47ab0447844..6f8f40b9165 100644 --- a/pymatgen/analysis/gb/tests/test_grain.py +++ b/pymatgen/analysis/gb/tests/test_grain.py @@ -5,6 +5,7 @@ import warnings import numpy as np +from pytest import approx from pymatgen.analysis.gb.grain import GrainBoundary, GrainBoundaryGenerator from pymatgen.core.structure import Structure @@ -47,13 +48,13 @@ def tearDownClass(cls): warnings.simplefilter("default") def test_init(self): - self.assertAlmostEqual(self.Cu_GB1.rotation_angle, 123.74898859588858) - self.assertAlmostEqual(self.Cu_GB1.vacuum_thickness, 1.5) - self.assertListEqual(self.Cu_GB2.rotation_axis, [1, 2, 3]) + assert self.Cu_GB1.rotation_angle == approx(123.74898859588858) + assert self.Cu_GB1.vacuum_thickness == approx(1.5) + assert self.Cu_GB2.rotation_axis == [1, 2, 3] self.assertArrayAlmostEqual(np.array(self.Cu_GB1.ab_shift), np.array([0.0, 0.0])) self.assertArrayAlmostEqual(np.array(self.Cu_GB2.ab_shift), np.array([0.2, 0.2])) - self.assertListEqual(self.Cu_GB1.gb_plane, [1, 3, 1]) - self.assertListEqual(self.Cu_GB2.gb_plane, [1, 2, 3]) + assert self.Cu_GB1.gb_plane == [1, 3, 1] + assert self.Cu_GB2.gb_plane == [1, 2, 3] self.assertArrayAlmostEqual( np.array(self.Cu_GB1.init_cell.lattice.matrix), np.array(self.Cu_conv.lattice.matrix), @@ -61,10 +62,10 @@ def test_init(self): def test_copy(self): Cu_GB1_copy = self.Cu_GB1.copy() - self.assertAlmostEqual(Cu_GB1_copy.sigma, self.Cu_GB1.sigma) - self.assertAlmostEqual(Cu_GB1_copy.rotation_angle, self.Cu_GB1.rotation_angle) - self.assertListEqual(Cu_GB1_copy.rotation_axis, self.Cu_GB1.rotation_axis) - self.assertListEqual(Cu_GB1_copy.gb_plane, self.Cu_GB1.gb_plane) + assert Cu_GB1_copy.sigma == approx(self.Cu_GB1.sigma) + assert Cu_GB1_copy.rotation_angle == approx(self.Cu_GB1.rotation_angle) + assert Cu_GB1_copy.rotation_axis == self.Cu_GB1.rotation_axis + assert Cu_GB1_copy.gb_plane == self.Cu_GB1.gb_plane self.assertArrayAlmostEqual(Cu_GB1_copy.init_cell.lattice.matrix, self.Cu_GB1.init_cell.lattice.matrix) self.assertArrayAlmostEqual( Cu_GB1_copy.oriented_unit_cell.lattice.matrix, @@ -73,40 +74,40 @@ def test_copy(self): self.assertArrayAlmostEqual(Cu_GB1_copy.lattice.matrix, self.Cu_GB1.lattice.matrix) def test_sigma(self): - self.assertAlmostEqual(self.Cu_GB1.sigma, 9) - self.assertAlmostEqual(self.Cu_GB2.sigma, 9) + assert self.Cu_GB1.sigma == approx(9) + assert self.Cu_GB2.sigma == approx(9) def test_top_grain(self): - self.assertAlmostEqual(self.Cu_GB1.num_sites, self.Cu_GB1.top_grain.num_sites * 2) + assert self.Cu_GB1.num_sites == approx(self.Cu_GB1.top_grain.num_sites * 2) self.assertArrayAlmostEqual(self.Cu_GB1.lattice.matrix, self.Cu_GB1.top_grain.lattice.matrix) def test_bottom_grain(self): - self.assertAlmostEqual(self.Cu_GB1.num_sites, self.Cu_GB1.bottom_grain.num_sites * 2) + assert self.Cu_GB1.num_sites == approx(self.Cu_GB1.bottom_grain.num_sites * 2) self.assertArrayAlmostEqual(self.Cu_GB1.lattice.matrix, self.Cu_GB1.bottom_grain.lattice.matrix) def test_coincidents(self): - self.assertAlmostEqual(self.Cu_GB1.num_sites / self.Cu_GB1.sigma, len(self.Cu_GB1.coincidents)) - self.assertAlmostEqual(self.Cu_GB2.num_sites / self.Cu_GB2.sigma, len(self.Cu_GB2.coincidents)) + assert self.Cu_GB1.num_sites / self.Cu_GB1.sigma == approx(len(self.Cu_GB1.coincidents)) + assert self.Cu_GB2.num_sites / self.Cu_GB2.sigma == approx(len(self.Cu_GB2.coincidents)) def test_as_dict_and_from_dict(self): d1 = self.Cu_GB1.as_dict() d2 = self.Cu_GB2.as_dict() Cu_GB1_new = GrainBoundary.from_dict(d1) Cu_GB2_new = GrainBoundary.from_dict(d2) - self.assertAlmostEqual(Cu_GB1_new.sigma, self.Cu_GB1.sigma) - self.assertAlmostEqual(Cu_GB1_new.rotation_angle, self.Cu_GB1.rotation_angle) - self.assertListEqual(Cu_GB1_new.rotation_axis, self.Cu_GB1.rotation_axis) - self.assertListEqual(Cu_GB1_new.gb_plane, self.Cu_GB1.gb_plane) + assert Cu_GB1_new.sigma == approx(self.Cu_GB1.sigma) + assert Cu_GB1_new.rotation_angle == approx(self.Cu_GB1.rotation_angle) + assert Cu_GB1_new.rotation_axis == self.Cu_GB1.rotation_axis + assert Cu_GB1_new.gb_plane == self.Cu_GB1.gb_plane self.assertArrayAlmostEqual(Cu_GB1_new.init_cell.lattice.matrix, self.Cu_GB1.init_cell.lattice.matrix) self.assertArrayAlmostEqual( Cu_GB1_new.oriented_unit_cell.lattice.matrix, self.Cu_GB1.oriented_unit_cell.lattice.matrix, ) self.assertArrayAlmostEqual(Cu_GB1_new.lattice.matrix, self.Cu_GB1.lattice.matrix) - self.assertAlmostEqual(Cu_GB2_new.sigma, self.Cu_GB2.sigma) - self.assertAlmostEqual(Cu_GB2_new.rotation_angle, self.Cu_GB2.rotation_angle) - self.assertListEqual(Cu_GB2_new.rotation_axis, self.Cu_GB2.rotation_axis) - self.assertListEqual(Cu_GB2_new.gb_plane, self.Cu_GB2.gb_plane) + assert Cu_GB2_new.sigma == approx(self.Cu_GB2.sigma) + assert Cu_GB2_new.rotation_angle == approx(self.Cu_GB2.rotation_angle) + assert Cu_GB2_new.rotation_axis == self.Cu_GB2.rotation_axis + assert Cu_GB2_new.gb_plane == self.Cu_GB2.gb_plane self.assertArrayAlmostEqual(Cu_GB2_new.init_cell.lattice.matrix, self.Cu_GB2.init_cell.lattice.matrix) self.assertArrayAlmostEqual( Cu_GB2_new.oriented_unit_cell.lattice.matrix, @@ -143,7 +144,7 @@ def test_gb_from_parameters(self): c_vec1 = np.cross(lat_mat1[0], lat_mat1[1]) / np.linalg.norm(np.cross(lat_mat1[0], lat_mat1[1])) c_len1 = np.dot(lat_mat1[2], c_vec1) vol_ratio = gb_cu_123_prim1.volume / self.Cu_prim.volume - self.assertAlmostEqual(vol_ratio, 9 * 2 * 2, 8) + assert vol_ratio == approx(9 * 2 * 2, 8) # test expand_times and vacuum layer gb_cu_123_prim2 = self.GB_Cu_prim.gb_from_parameters( [1, 2, 3], 123.74898859588858, expand_times=4, vacuum_thickness=1.5 @@ -151,14 +152,14 @@ def test_gb_from_parameters(self): lat_mat2 = gb_cu_123_prim2.lattice.matrix c_vec2 = np.cross(lat_mat2[0], lat_mat2[1]) / np.linalg.norm(np.cross(lat_mat2[0], lat_mat2[1])) c_len2 = np.dot(lat_mat2[2], c_vec2) - self.assertAlmostEqual((c_len2 - 1.5 * 2) / c_len1, 2) + assert (c_len2 - 1.5 * 2) / c_len1 == approx(2) # test normal gb_cu_123_prim3 = self.GB_Cu_prim.gb_from_parameters([1, 2, 3], 123.74898859588858, expand_times=2, normal=True) lat_mat3 = gb_cu_123_prim3.lattice.matrix c_vec3 = np.cross(lat_mat3[0], lat_mat3[1]) / np.linalg.norm(np.cross(lat_mat3[0], lat_mat3[1])) ab_len3 = np.linalg.norm(np.cross(lat_mat3[2], c_vec3)) - self.assertAlmostEqual(ab_len3, 0) + assert ab_len3 == approx(0) # test normal in tilt boundary # The 'finfo(np.float32).eps' is the smallest representable positive number in float32, @@ -173,16 +174,16 @@ def test_gb_from_parameters(self): plane=[0, 0, 1], normal=True, ) - self.assertTrue(np.all(-np.finfo(np.float32).eps <= gb_cu_010_conv1.frac_coords)) - self.assertTrue(np.all(1 + np.finfo(np.float32).eps >= gb_cu_010_conv1.frac_coords)) + assert np.all(-np.finfo(np.float32).eps <= gb_cu_010_conv1.frac_coords) + assert np.all(1 + np.finfo(np.float32).eps >= gb_cu_010_conv1.frac_coords) # from fcc conventional cell,axis [1,2,3], siamg 9 gb_cu_123_conv1 = self.GB_Cu_conv.gb_from_parameters( [1, 2, 3], 123.74898859588858, expand_times=4, vacuum_thickness=1.5 ) lat_mat1 = gb_cu_123_conv1.lattice.matrix - self.assertAlmostEqual(np.dot(lat_mat1[0], [1, 2, 3]), 0) - self.assertAlmostEqual(np.dot(lat_mat1[1], [1, 2, 3]), 0) + assert np.dot(lat_mat1[0], [1, 2, 3]) == approx(0) + assert np.dot(lat_mat1[1], [1, 2, 3]) == approx(0) # test plane gb_cu_123_conv2 = self.GB_Cu_conv.gb_from_parameters( [1, 2, 3], @@ -193,8 +194,8 @@ def test_gb_from_parameters(self): plane=[1, 3, 1], ) lat_mat2 = gb_cu_123_conv2.lattice.matrix - self.assertAlmostEqual(np.dot(lat_mat2[0], [1, 3, 1]), 0) - self.assertAlmostEqual(np.dot(lat_mat2[1], [1, 3, 1]), 0) + assert np.dot(lat_mat2[0], [1, 3, 1]) == approx(0) + assert np.dot(lat_mat2[1], [1, 3, 1]) == approx(0) # from hex cell,axis [1,1,1], sigma 21 gb_Be_111_1 = self.GB_Be.gb_from_parameters( @@ -207,28 +208,28 @@ def test_gb_from_parameters(self): ) lat_priv = self.Be.lattice.matrix lat_mat1 = np.matmul(gb_Be_111_1.lattice.matrix, np.linalg.inv(lat_priv)) - self.assertAlmostEqual(np.dot(lat_mat1[0], [1, 2, 1]), 0) - self.assertAlmostEqual(np.dot(lat_mat1[1], [1, 2, 1]), 0) + assert np.dot(lat_mat1[0], [1, 2, 1]) == approx(0) + assert np.dot(lat_mat1[1], [1, 2, 1]) == approx(0) # test volume associated with sigma value gb_Be_111_2 = self.GB_Be.gb_from_parameters([1, 1, 1], 147.36310249644626, ratio=[5, 2], expand_times=4) vol_ratio = gb_Be_111_2.volume / self.Be.volume - self.assertAlmostEqual(vol_ratio, 19 * 2 * 4) + assert vol_ratio == approx(19 * 2 * 4) # test ratio = None, axis [0,0,1], sigma 7 gb_Be_111_3 = self.GB_Be.gb_from_parameters([0, 0, 1], 21.786789298261812, ratio=[5, 2], expand_times=4) gb_Be_111_4 = self.GB_Be.gb_from_parameters([0, 0, 1], 21.786789298261812, ratio=None, expand_times=4) - self.assertTupleEqual(gb_Be_111_3.lattice.abc, gb_Be_111_4.lattice.abc) - self.assertTupleEqual(gb_Be_111_3.lattice.angles, gb_Be_111_4.lattice.angles) + assert gb_Be_111_3.lattice.abc == gb_Be_111_4.lattice.abc + assert gb_Be_111_3.lattice.angles == gb_Be_111_4.lattice.angles gb_Be_111_5 = self.GB_Be.gb_from_parameters([3, 1, 0], 180.0, ratio=[5, 2], expand_times=4) gb_Be_111_6 = self.GB_Be.gb_from_parameters([3, 1, 0], 180.0, ratio=None, expand_times=4) - self.assertTupleEqual(gb_Be_111_5.lattice.abc, gb_Be_111_6.lattice.abc) - self.assertTupleEqual(gb_Be_111_5.lattice.angles, gb_Be_111_6.lattice.angles) + assert gb_Be_111_5.lattice.abc == gb_Be_111_6.lattice.abc + assert gb_Be_111_5.lattice.angles == gb_Be_111_6.lattice.angles # gb from tetragonal cell, axis[1,1,1], sigma 15 gb_Pa_111_1 = self.GB_Pa.gb_from_parameters( [1, 1, 1], 151.92751306414706, ratio=[2, 3], expand_times=4, max_search=10 ) vol_ratio = gb_Pa_111_1.volume / self.Pa.volume - self.assertAlmostEqual(vol_ratio, 17 * 2 * 4) + assert vol_ratio == approx(17 * 2 * 4) # gb from orthorhombic cell, axis[1,1,1], sigma 83 gb_Br_111_1 = self.GB_Br.gb_from_parameters( @@ -239,30 +240,30 @@ def test_gb_from_parameters(self): max_search=10, ) vol_ratio = gb_Br_111_1.volume / self.Br.volume - self.assertAlmostEqual(vol_ratio, 83 * 2 * 4) + assert vol_ratio == approx(83 * 2 * 4) # gb from rhombohedra cell, axis[1,2,0], sigma 63 gb_Bi_120_1 = self.GB_Bi.gb_from_parameters( [1, 2, 0], 63.310675060280246, ratio=[19, 5], expand_times=4, max_search=5 ) vol_ratio = gb_Bi_120_1.volume / self.Bi.volume - self.assertAlmostEqual(vol_ratio, 59 * 2 * 4) + assert vol_ratio == approx(59 * 2 * 4) def test_get_ratio(self): # hexagnal Be_ratio = self.GB_Be.get_ratio(max_denominator=2) - self.assertListEqual(Be_ratio, [5, 2]) + assert Be_ratio == [5, 2] Be_ratio = self.GB_Be.get_ratio(max_denominator=5) - self.assertListEqual(Be_ratio, [12, 5]) + assert Be_ratio == [12, 5] # tetragonal Pa_ratio = self.GB_Pa.get_ratio(max_denominator=5) - self.assertListEqual(Pa_ratio, [2, 3]) + assert Pa_ratio == [2, 3] # orthorombic Br_ratio = self.GB_Br.get_ratio(max_denominator=5) - self.assertListEqual(Br_ratio, [21, 20, 5]) + assert Br_ratio == [21, 20, 5] # orthorombic Bi_ratio = self.GB_Bi.get_ratio(max_denominator=5) - self.assertListEqual(Bi_ratio, [19, 5]) + assert Bi_ratio == [19, 5] def test_enum_sigma_cubic(self): true_100 = [5, 13, 17, 25, 29, 37, 41] @@ -274,11 +275,11 @@ def test_enum_sigma_cubic(self): sigma_222 = list(GrainBoundaryGenerator.enum_sigma_cubic(50, [2, 2, 2])) sigma_888 = list(GrainBoundaryGenerator.enum_sigma_cubic(50, [8, 8, 8])) - self.assertListEqual(sorted(true_100), sorted(sigma_100)) - self.assertListEqual(sorted(true_110), sorted(sigma_110)) - self.assertListEqual(sorted(true_111), sorted(sigma_111)) - self.assertListEqual(sorted(true_111), sorted(sigma_222)) - self.assertListEqual(sorted(true_111), sorted(sigma_888)) + assert sorted(true_100) == sorted(sigma_100) + assert sorted(true_110) == sorted(sigma_110) + assert sorted(true_111) == sorted(sigma_111) + assert sorted(true_111) == sorted(sigma_222) + assert sorted(true_111) == sorted(sigma_888) def test_enum_sigma_hex(self): true_100 = [17, 18, 22, 27, 38, 41] @@ -290,11 +291,11 @@ def test_enum_sigma_hex(self): sigma_420 = list(GrainBoundaryGenerator.enum_sigma_hex(50, [4, 2, 0], [8, 3])) sigma_840 = list(GrainBoundaryGenerator.enum_sigma_hex(50, [8, 4, 0], [8, 3])) - self.assertListEqual(sorted(true_100), sorted(sigma_100)) - self.assertListEqual(sorted(true_001), sorted(sigma_001)) - self.assertListEqual(sorted(true_210), sorted(sigma_210)) - self.assertListEqual(sorted(true_210), sorted(sigma_420)) - self.assertListEqual(sorted(true_210), sorted(sigma_840)) + assert sorted(true_100) == sorted(sigma_100) + assert sorted(true_001) == sorted(sigma_001) + assert sorted(true_210) == sorted(sigma_210) + assert sorted(true_210) == sorted(sigma_420) + assert sorted(true_210) == sorted(sigma_840) def test_enum_sigma_tet(self): true_100 = [5, 37, 41, 13, 3, 15, 39, 25, 17, 29] @@ -302,26 +303,26 @@ def test_enum_sigma_tet(self): sigma_100 = list(GrainBoundaryGenerator.enum_sigma_tet(50, [1, 0, 0], [9, 1])) sigma_331 = list(GrainBoundaryGenerator.enum_sigma_tet(50, [3, 3, 1], [9, 1])) - self.assertListEqual(sorted(true_100), sorted(sigma_100)) - self.assertListEqual(sorted(true_331), sorted(sigma_331)) + assert sorted(true_100) == sorted(sigma_100) + assert sorted(true_331) == sorted(sigma_331) def test_enum_sigma_ort(self): true_100 = [41, 37, 39, 5, 15, 17, 13, 3, 25, 29] sigma_100 = list(GrainBoundaryGenerator.enum_sigma_ort(50, [1, 0, 0], [270, 30, 29])) - self.assertListEqual(sorted(true_100), sorted(sigma_100)) + assert sorted(true_100) == sorted(sigma_100) def test_enum_sigma_rho(self): true_100 = [7, 11, 43, 13, 41, 19, 47, 31] sigma_100 = list(GrainBoundaryGenerator.enum_sigma_rho(50, [1, 0, 0], [15, 4])) - self.assertListEqual(sorted(true_100), sorted(sigma_100)) + assert sorted(true_100) == sorted(sigma_100) def test_enum_possible_plane_cubic(self): all_plane = GrainBoundaryGenerator.enum_possible_plane_cubic(4, [1, 1, 1], 60) - self.assertEqual(len(all_plane["Twist"]), 1) - self.assertEqual(len(all_plane["Symmetric tilt"]), 6) - self.assertEqual(len(all_plane["Normal tilt"]), 12) + assert len(all_plane["Twist"]) == 1 + assert len(all_plane["Symmetric tilt"]) == 6 + assert len(all_plane["Normal tilt"]) == 12 def test_get_trans_mat(self): mat1, mat2 = GrainBoundaryGenerator.get_trans_mat( @@ -332,11 +333,11 @@ def test_get_trans_mat(self): surface=[21, 20, 10], normal=True, ) - self.assertAlmostEqual(np.dot(mat1[0], [21, 20, 10]), 0) - self.assertAlmostEqual(np.dot(mat1[1], [21, 20, 10]), 0) - self.assertAlmostEqual(np.linalg.det(mat1), np.linalg.det(mat2)) + assert np.dot(mat1[0], [21, 20, 10]) == approx(0) + assert np.dot(mat1[1], [21, 20, 10]) == approx(0) + assert np.linalg.det(mat1) == approx(np.linalg.det(mat2)) ab_len1 = np.linalg.norm(np.cross(mat1[2], [1, 1, 1])) - self.assertAlmostEqual(ab_len1, 0) + assert ab_len1 == approx(0) def test_get_rotation_angle_from_sigma(self): true_angle = [12.680383491819821, 167.3196165081802] diff --git a/pymatgen/analysis/local_env.py b/pymatgen/analysis/local_env.py index be37ae95e94..e915044632e 100644 --- a/pymatgen/analysis/local_env.py +++ b/pymatgen/analysis/local_env.py @@ -289,7 +289,7 @@ def extend_structure_molecules(self) -> bool: """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ raise NotImplementedError("extend_structures_molecule is not defined!") @@ -1263,7 +1263,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return True @@ -1365,7 +1365,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return True @@ -1460,7 +1460,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return False @@ -1621,7 +1621,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return False @@ -1769,7 +1769,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return True @@ -3660,7 +3660,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return True @@ -4227,7 +4227,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return True @@ -4330,7 +4330,7 @@ def extend_structure_molecules(self): """ Boolean property: Do Molecules need to be converted to Structures to use this NearNeighbors class? Note: this property is not defined for classes - for which molecules_allowed == False. + for which molecules_allowed is False. """ return True diff --git a/pymatgen/analysis/magnetism/tests/test_analyzer.py b/pymatgen/analysis/magnetism/tests/test_analyzer.py index 9bcaa6e157a..ca4ee9e5a42 100644 --- a/pymatgen/analysis/magnetism/tests/test_analyzer.py +++ b/pymatgen/analysis/magnetism/tests/test_analyzer.py @@ -9,7 +9,9 @@ from shutil import which import numpy as np +import pytest from monty.serialization import loadfn +from pytest import approx from pymatgen.analysis.magnetism import ( CollinearMagneticStructureAnalyzer, @@ -80,121 +82,122 @@ def test_get_representations(self): # test we store magnetic moments on site properties self.Fe.add_site_property("magmom", [5]) msa = CollinearMagneticStructureAnalyzer(self.Fe) - self.assertEqual(msa.structure.site_properties["magmom"][0], 5) + assert msa.structure.site_properties["magmom"][0] == 5 # and that we can retrieve a spin representation Fe_spin = msa.get_structure_with_spin() - self.assertFalse("magmom" in Fe_spin.site_properties) - self.assertEqual(Fe_spin[0].specie.spin, 5) + assert "magmom" not in Fe_spin.site_properties + assert Fe_spin[0].specie.spin == 5 # test we can remove magnetic moment information msa.get_nonmagnetic_structure() - self.assertFalse("magmom" in Fe_spin.site_properties) + assert "magmom" not in Fe_spin.site_properties # test with disorder on magnetic site self.Fe[0] = { Species("Fe", oxidation_state=0, properties={"spin": 5}): 0.5, "Ni": 0.5, } - self.assertRaises(NotImplementedError, CollinearMagneticStructureAnalyzer, self.Fe) + with pytest.raises(NotImplementedError): + CollinearMagneticStructureAnalyzer(self.Fe) def test_matches(self): - self.assertTrue(self.NiO.matches(self.NiO_AFM_111)) - self.assertTrue(self.NiO.matches(self.NiO_AFM_001)) + assert self.NiO.matches(self.NiO_AFM_111) + assert self.NiO.matches(self.NiO_AFM_001) # MSA adds magmoms to Structure, so not equal msa = CollinearMagneticStructureAnalyzer(self.NiO, overwrite_magmom_mode="replace_all") - self.assertFalse(msa.matches_ordering(self.NiO)) - self.assertFalse(msa.matches_ordering(self.NiO_AFM_111)) - self.assertFalse(msa.matches_ordering(self.NiO_AFM_001)) + assert not msa.matches_ordering(self.NiO) + assert not msa.matches_ordering(self.NiO_AFM_111) + assert not msa.matches_ordering(self.NiO_AFM_001) msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_001, overwrite_magmom_mode="respect_sign") - self.assertFalse(msa.matches_ordering(self.NiO)) - self.assertFalse(msa.matches_ordering(self.NiO_AFM_111)) - self.assertTrue(msa.matches_ordering(self.NiO_AFM_001)) - self.assertTrue(msa.matches_ordering(self.NiO_AFM_001_opposite)) + assert not msa.matches_ordering(self.NiO) + assert not msa.matches_ordering(self.NiO_AFM_111) + assert msa.matches_ordering(self.NiO_AFM_001) + assert msa.matches_ordering(self.NiO_AFM_001_opposite) msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_111, overwrite_magmom_mode="respect_sign") - self.assertFalse(msa.matches_ordering(self.NiO)) - self.assertTrue(msa.matches_ordering(self.NiO_AFM_111)) - self.assertFalse(msa.matches_ordering(self.NiO_AFM_001)) - self.assertFalse(msa.matches_ordering(self.NiO_AFM_001_opposite)) + assert not msa.matches_ordering(self.NiO) + assert msa.matches_ordering(self.NiO_AFM_111) + assert not msa.matches_ordering(self.NiO_AFM_001) + assert not msa.matches_ordering(self.NiO_AFM_001_opposite) def test_modes(self): mode = "none" msa = CollinearMagneticStructureAnalyzer(self.NiO, overwrite_magmom_mode=mode) magmoms = msa.structure.site_properties["magmom"] - self.assertEqual(magmoms, [0, 0]) + assert magmoms == [0, 0] mode = "respect_sign" msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical, overwrite_magmom_mode=mode) magmoms = msa.structure.site_properties["magmom"] - self.assertEqual(magmoms, [-5, 0, 0, 0]) + assert magmoms == [-5, 0, 0, 0] mode = "respect_zeros" msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical, overwrite_magmom_mode=mode) magmoms = msa.structure.site_properties["magmom"] - self.assertEqual(magmoms, [5, 0, 0, 0]) + assert magmoms == [5, 0, 0, 0] mode = "replace_all" msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical, overwrite_magmom_mode=mode, make_primitive=False) magmoms = msa.structure.site_properties["magmom"] - self.assertEqual(magmoms, [5, 5, 0, 0]) + assert magmoms == [5, 5, 0, 0] mode = "replace_all_if_undefined" msa = CollinearMagneticStructureAnalyzer(self.NiO, overwrite_magmom_mode=mode) magmoms = msa.structure.site_properties["magmom"] - self.assertEqual(magmoms, [5, 0]) + assert magmoms == [5, 0] mode = "normalize" msa = CollinearMagneticStructureAnalyzer(msa.structure, overwrite_magmom_mode="normalize") magmoms = msa.structure.site_properties["magmom"] - self.assertEqual(magmoms, [1, 0]) + assert magmoms == [1, 0] def test_net_positive(self): msa = CollinearMagneticStructureAnalyzer(self.NiO_unphysical) magmoms = msa.structure.site_properties["magmom"] - self.assertEqual(magmoms, [3, 0, 0, 0]) + assert magmoms == [3, 0, 0, 0] def test_get_ferromagnetic_structure(self): msa = CollinearMagneticStructureAnalyzer(self.NiO, overwrite_magmom_mode="replace_all_if_undefined") s1 = msa.get_ferromagnetic_structure() s1_magmoms = [float(m) for m in s1.site_properties["magmom"]] s1_magmoms_ref = [5.0, 0.0] - self.assertListEqual(s1_magmoms, s1_magmoms_ref) + assert s1_magmoms == s1_magmoms_ref _ = CollinearMagneticStructureAnalyzer(self.NiO_AFM_111, overwrite_magmom_mode="replace_all_if_undefined") s2 = msa.get_ferromagnetic_structure(make_primitive=False) s2_magmoms = [float(m) for m in s2.site_properties["magmom"]] s2_magmoms_ref = [5.0, 0.0] - self.assertListEqual(s2_magmoms, s2_magmoms_ref) + assert s2_magmoms == s2_magmoms_ref s2_prim = msa.get_ferromagnetic_structure(make_primitive=True) - self.assertTrue(CollinearMagneticStructureAnalyzer(s1).matches_ordering(s2_prim)) + assert CollinearMagneticStructureAnalyzer(s1).matches_ordering(s2_prim) def test_magnetic_properties(self): msa = CollinearMagneticStructureAnalyzer(self.GdB4) - self.assertFalse(msa.is_collinear) + assert not msa.is_collinear msa = CollinearMagneticStructureAnalyzer(self.Fe) - self.assertFalse(msa.is_magnetic) + assert not msa.is_magnetic self.Fe.add_site_property("magmom", [5]) msa = CollinearMagneticStructureAnalyzer(self.Fe) - self.assertTrue(msa.is_magnetic) - self.assertTrue(msa.is_collinear) - self.assertEqual(msa.ordering, Ordering.FM) + assert msa.is_magnetic + assert msa.is_collinear + assert msa.ordering == Ordering.FM msa = CollinearMagneticStructureAnalyzer( self.NiO, make_primitive=False, overwrite_magmom_mode="replace_all_if_undefined", ) - self.assertEqual(msa.number_of_magnetic_sites, 4) - self.assertEqual(msa.number_of_unique_magnetic_sites(), 1) - self.assertEqual(msa.types_of_magnetic_species, (Element.Ni,)) - self.assertEqual(msa.get_exchange_group_info(), ("Fm-3m", 225)) + assert msa.number_of_magnetic_sites == 4 + assert msa.number_of_unique_magnetic_sites() == 1 + assert msa.types_of_magnetic_species == (Element.Ni,) + assert msa.get_exchange_group_info() == ("Fm-3m", 225) def test_str(self): msa = CollinearMagneticStructureAnalyzer(self.NiO_AFM_001) @@ -215,26 +218,23 @@ def test_str(self): # just compare lines form 'Magmoms Sites', # since lattice param string can vary based on machine precision - self.assertEqual( - "\n".join(str(msa).split("\n")[-5:-1]), - "\n".join(ref_msa_str.split("\n")[-5:-1]), - ) + assert "\n".join(str(msa).split("\n")[-5:-1]) == "\n".join(ref_msa_str.split("\n")[-5:-1]) def test_round_magmoms(self): struct = self.NiO_AFM_001.copy() struct.add_site_property("magmom", [-5.0143, -5.02, 0.147, 0.146]) msa = CollinearMagneticStructureAnalyzer(struct, round_magmoms=0.001, make_primitive=False) - self.assertTrue(np.allclose(msa.magmoms, [5.0171, 5.0171, -0.1465, -0.1465])) - self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"], 5.0171) - self.assertAlmostEqual(msa.magnetic_species_and_magmoms["O"], 0.1465) + assert np.allclose(msa.magmoms, [5.0171, 5.0171, -0.1465, -0.1465]) + assert msa.magnetic_species_and_magmoms["Ni"] == approx(5.0171) + assert msa.magnetic_species_and_magmoms["O"] == approx(0.1465) struct.add_site_property("magmom", [-5.0143, 4.5, 0.147, 0.146]) msa = CollinearMagneticStructureAnalyzer(struct, round_magmoms=0.001, make_primitive=False) - self.assertTrue(np.allclose(msa.magmoms, [5.0143, -4.5, -0.1465, -0.1465])) - self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"][0], 4.5) - self.assertAlmostEqual(msa.magnetic_species_and_magmoms["Ni"][1], 5.0143) - self.assertAlmostEqual(msa.magnetic_species_and_magmoms["O"], 0.1465) + assert np.allclose(msa.magmoms, [5.0143, -4.5, -0.1465, -0.1465]) + assert msa.magnetic_species_and_magmoms["Ni"][0] == approx(4.5) + assert msa.magnetic_species_and_magmoms["Ni"][1] == approx(5.0143) + assert msa.magnetic_species_and_magmoms["O"] == approx(0.1465) class MagneticStructureEnumeratorTest(unittest.TestCase): @@ -243,17 +243,17 @@ def test_ordering_enumeration(self): # simple afm structure = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "magnetic_orderings/LaMnO3.json")) enumerator = MagneticStructureEnumerator(structure) - self.assertEqual(enumerator.input_origin, "afm") + assert enumerator.input_origin == "afm" # ferrimagnetic (Cr produces net spin) structure = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "magnetic_orderings/Cr2NiO4.json")) enumerator = MagneticStructureEnumerator(structure) - self.assertEqual(enumerator.input_origin, "ferri_by_Cr") + assert enumerator.input_origin == "ferri_by_Cr" # antiferromagnetic on single magnetic site structure = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "magnetic_orderings/Cr2WO6.json")) enumerator = MagneticStructureEnumerator(structure) - self.assertEqual(enumerator.input_origin, "afm_by_Cr") + assert enumerator.input_origin == "afm_by_Cr" # afm requiring large cell size # (enable for further development of workflow, too slow for CI) @@ -272,7 +272,7 @@ def test_ordering_enumeration(self): truncate_by_symmetry=False, transformation_kwargs={"max_cell_size": 2}, ) - self.assertEqual(enumerator.input_origin, "afm_by_motif_2a") + assert enumerator.input_origin == "afm_by_motif_2a" class MagneticDeformationTest(unittest.TestCase): @@ -280,8 +280,8 @@ def test_magnetic_deformation(self): test_structs = loadfn(os.path.join(PymatgenTest.TEST_FILES_DIR, "magnetic_deformation.json")) mag_def = magnetic_deformation(test_structs[0], test_structs[1]) - self.assertEqual(mag_def.type, "NM-FM") - self.assertAlmostEqual(mag_def.deformation, 5.0130859485170971) + assert mag_def.type == "NM-FM" + assert mag_def.deformation == approx(5.0130859485170971) if __name__ == "__main__": diff --git a/pymatgen/analysis/structure_prediction/tests/test_dopant_predictor.py b/pymatgen/analysis/structure_prediction/tests/test_dopant_predictor.py index 7c725dc82fc..9c769cc3fd9 100644 --- a/pymatgen/analysis/structure_prediction/tests/test_dopant_predictor.py +++ b/pymatgen/analysis/structure_prediction/tests/test_dopant_predictor.py @@ -6,6 +6,8 @@ import unittest +from pytest import approx + from pymatgen.analysis.local_env import CrystalNN from pymatgen.analysis.structure_prediction.dopant_predictor import ( get_dopants_from_shannon_radii, @@ -34,44 +36,44 @@ def setUp(self): def test_dopants_from_substitution_probabilities(self): dopants = get_dopants_from_substitution_probabilities(self.tin_dioxide, num_dopants=5) - self.assertTrue("n_type" in dopants) - self.assertTrue("p_type" in dopants) + assert "n_type" in dopants + assert "p_type" in dopants - self.assertTrue(len(dopants["n_type"]) <= 5) + assert len(dopants["n_type"]) <= 5 - self.assertTrue(len(dopants["p_type"]) <= 5) + assert len(dopants["p_type"]) <= 5 - self.assertAlmostEqual(dopants["n_type"][0]["probability"], 0.06692682583342474) - self.assertEqual(dopants["n_type"][0]["dopant_species"], Species("F", -1)) - self.assertEqual(dopants["n_type"][0]["original_species"], Species("O", -2)) + assert dopants["n_type"][0]["probability"] == approx(0.06692682583342474) + assert dopants["n_type"][0]["dopant_species"] == Species("F", -1) + assert dopants["n_type"][0]["original_species"] == Species("O", -2) - self.assertAlmostEqual(dopants["p_type"][0]["probability"], 0.023398867249112935) - self.assertEqual(dopants["p_type"][0]["dopant_species"], Species("Co", 2)) - self.assertEqual(dopants["p_type"][0]["original_species"], Species("Sn", 4)) + assert dopants["p_type"][0]["probability"] == approx(0.023398867249112935) + assert dopants["p_type"][0]["dopant_species"] == Species("Co", 2) + assert dopants["p_type"][0]["original_species"] == Species("Sn", 4) # test oxidation sign matching dopants = get_dopants_from_substitution_probabilities(self.tin_dioxide, num_dopants=15, match_oxi_sign=False) - self.assertEqual(dopants["n_type"][14]["dopant_species"], Species("Li", 1)) - self.assertEqual(dopants["n_type"][14]["original_species"], Species("O", -2)) + assert dopants["n_type"][14]["dopant_species"] == Species("Li", 1) + assert dopants["n_type"][14]["original_species"] == Species("O", -2) dopants = get_dopants_from_substitution_probabilities(self.tin_dioxide, num_dopants=15, match_oxi_sign=True) - self.assertNotEqual(dopants["n_type"][14]["dopant_species"], Species("Li", 1)) + assert dopants["n_type"][14]["dopant_species"] != Species("Li", 1) def test_dopants_from_shannon_radii(self): bonded_structure = CrystalNN().get_bonded_structure(self.tin_dioxide) dopants = get_dopants_from_shannon_radii(bonded_structure, num_dopants=5) - self.assertTrue("n_type" in dopants) - self.assertTrue("p_type" in dopants) + assert "n_type" in dopants + assert "p_type" in dopants - self.assertTrue(len(dopants["n_type"]) <= 5) - self.assertTrue(len(dopants["p_type"]) <= 5) + assert len(dopants["n_type"]) <= 5 + assert len(dopants["p_type"]) <= 5 - self.assertAlmostEqual(dopants["n_type"][0]["radii_diff"], 0.04) - self.assertEqual(dopants["n_type"][0]["dopant_species"], Species("U", 6)) - self.assertEqual(dopants["n_type"][0]["original_species"], Species("Sn", 4)) + assert dopants["n_type"][0]["radii_diff"] == approx(0.04) + assert dopants["n_type"][0]["dopant_species"] == Species("U", 6) + assert dopants["n_type"][0]["original_species"] == Species("Sn", 4) - self.assertAlmostEqual(dopants["p_type"][0]["radii_diff"], 0.0) - self.assertEqual(dopants["p_type"][0]["dopant_species"], Species("Ni", 2)) - self.assertEqual(dopants["p_type"][0]["original_species"], Species("Sn", 4)) + assert dopants["p_type"][0]["radii_diff"] == approx(0.0) + assert dopants["p_type"][0]["dopant_species"] == Species("Ni", 2) + assert dopants["p_type"][0]["original_species"] == Species("Sn", 4) diff --git a/pymatgen/analysis/structure_prediction/tests/test_substitution_probability.py b/pymatgen/analysis/structure_prediction/tests/test_substitution_probability.py index a325ecd26dc..b4dedb8d669 100644 --- a/pymatgen/analysis/structure_prediction/tests/test_substitution_probability.py +++ b/pymatgen/analysis/structure_prediction/tests/test_substitution_probability.py @@ -8,6 +8,8 @@ import os import unittest +from pytest import approx + from pymatgen.analysis.structure_prediction.substitution_probability import ( SubstitutionPredictor, SubstitutionProbability, @@ -44,15 +46,15 @@ def test_full_lambda_table(self): sp1 = Species("Fe", 4) sp3 = Species("Mn", 3) prob1 = sp.prob(sp1, sp3) - self.assertAlmostEqual(prob1, 1.69243954552e-05, 5, "probability isn't correct") + assert prob1 == approx(1.69243954552e-05, 5), "probability isn't correct" sp2 = Species("Pt", 4) sp4 = Species("Pd", 4) prob2 = sp.prob(sp2, sp4) - self.assertAlmostEqual(prob2, 4.7174906021e-05, 5, "probability isn't correct") + assert prob2 == approx(4.7174906021e-05, 5), "probability isn't correct" corr = sp.pair_corr(Species("Cu", 2), Species("Fe", 2)) - self.assertAlmostEqual(corr, 6.82496631637, 5, "probability isn't correct") + assert corr == approx(6.82496631637, 5), "probability isn't correct" prob3 = sp.cond_prob_list([sp1, sp2], [sp3, sp4]) - self.assertAlmostEqual(prob3, 0.000300298841302, 6, "probability isn't correct") + assert prob3 == approx(0.000300298841302, 6), "probability isn't correct" def test_mini_lambda_table(self): sp = SubstitutionProbability(lambda_table=get_table(), alpha=-5.0) @@ -60,10 +62,10 @@ def test_mini_lambda_table(self): s2 = Species("S", -2) li1 = Species("Li", 1) na1 = Species("Na", 1) - self.assertAlmostEqual(sp.prob(s2, o2), 0.124342317272, 5, "probability isn't correct") - self.assertAlmostEqual(sp.pair_corr(li1, na1), 1.65425296864, 5, "correlation isn't correct") + assert sp.prob(s2, o2) == approx(0.124342317272, 5), "probability isn't correct" + assert sp.pair_corr(li1, na1) == approx(1.65425296864, 5), "correlation isn't correct" prob = sp.cond_prob_list([o2, li1], [na1, li1]) - self.assertAlmostEqual(prob, 0.00102673915742, 5, "probability isn't correct") + assert prob == approx(0.00102673915742, 5), "probability isn't correct" class SubstitutionPredictorTest(unittest.TestCase): @@ -71,19 +73,19 @@ def test_prediction(self): sp = SubstitutionPredictor(threshold=8e-3) result = sp.list_prediction(["Na+", "Cl-"], to_this_composition=True)[5] cprob = sp.p.cond_prob_list(result["substitutions"].keys(), result["substitutions"].values()) - self.assertAlmostEqual(result["probability"], cprob) - self.assertEqual(set(result["substitutions"].values()), {"Na+", "Cl-"}) + assert result["probability"] == approx(cprob) + assert set(result["substitutions"].values()) == {"Na+", "Cl-"} result = sp.list_prediction(["Na+", "Cl-"], to_this_composition=False)[5] cprob = sp.p.cond_prob_list(result["substitutions"].keys(), result["substitutions"].values()) - self.assertAlmostEqual(result["probability"], cprob) - self.assertNotEqual(set(result["substitutions"].values()), {"Na+", "Cl-"}) + assert result["probability"] == approx(cprob) + assert set(result["substitutions"].values()) != {"Na+", "Cl-"} c = Composition({"Ag2+": 1, "Cl-": 2}) result = sp.composition_prediction(c, to_this_composition=True)[2] - self.assertEqual(set(result["substitutions"].values()), set(c.elements)) + assert set(result["substitutions"].values()) == set(c.elements) result = sp.composition_prediction(c, to_this_composition=False)[2] - self.assertEqual(set(result["substitutions"]), set(c.elements)) + assert set(result["substitutions"]) == set(c.elements) if __name__ == "__main__": diff --git a/pymatgen/analysis/structure_prediction/tests/test_volume_predictor.py b/pymatgen/analysis/structure_prediction/tests/test_volume_predictor.py index 847317ab41b..3978c4d18bc 100644 --- a/pymatgen/analysis/structure_prediction/tests/test_volume_predictor.py +++ b/pymatgen/analysis/structure_prediction/tests/test_volume_predictor.py @@ -7,6 +7,9 @@ import unittest import warnings +import pytest +from pytest import approx + from pymatgen.analysis.structure_prediction.volume_predictor import ( DLSVolumePredictor, RLSVolumePredictor, @@ -30,31 +33,31 @@ def test_predict(self): nacl.replace_species({"Cs": "Na"}) nacl.scale_lattice(184.384551033) p = RLSVolumePredictor(radii_type="ionic") - self.assertAlmostEqual(p.predict(s, nacl), 342.84905395082535) + assert p.predict(s, nacl) == approx(342.84905395082535) p = RLSVolumePredictor(radii_type="atomic") - self.assertAlmostEqual(p.predict(s, nacl), 391.884366481) + assert p.predict(s, nacl) == approx(391.884366481) lif = PymatgenTest.get_structure("CsCl") lif.replace_species({"Cs": "Li", "Cl": "F"}) p = RLSVolumePredictor(radii_type="ionic") - self.assertAlmostEqual(p.predict(lif, nacl), 74.268402413690467) + assert p.predict(lif, nacl) == approx(74.268402413690467) p = RLSVolumePredictor(radii_type="atomic") - self.assertAlmostEqual(p.predict(lif, nacl), 62.2808125839) + assert p.predict(lif, nacl) == approx(62.2808125839) lfpo = PymatgenTest.get_structure("LiFePO4") lmpo = PymatgenTest.get_structure("LiFePO4") lmpo.replace_species({"Fe": "Mn"}) p = RLSVolumePredictor(radii_type="ionic") - self.assertAlmostEqual(p.predict(lmpo, lfpo), 310.08253254420134) + assert p.predict(lmpo, lfpo) == approx(310.08253254420134) p = RLSVolumePredictor(radii_type="atomic") - self.assertAlmostEqual(p.predict(lmpo, lfpo), 299.607967711) + assert p.predict(lmpo, lfpo) == approx(299.607967711) sto = PymatgenTest.get_structure("SrTiO3") scoo = PymatgenTest.get_structure("SrTiO3") scoo.replace_species({"Ti4+": "Co4+"}) p = RLSVolumePredictor(radii_type="ionic") - self.assertAlmostEqual(p.predict(scoo, sto), 56.162534974936463) + assert p.predict(scoo, sto) == approx(56.162534974936463) p = RLSVolumePredictor(radii_type="atomic") - self.assertAlmostEqual(p.predict(scoo, sto), 57.4777835108) + assert p.predict(scoo, sto) == approx(57.4777835108) # Use Ag7P3S11 as a test case: @@ -63,15 +66,15 @@ def test_predict(self): apo = Structure.from_file(os.path.join(dir_path, "Ag7P3S11_mp-683910_primitive.cif")) apo.replace_species({"S": "O"}) p = RLSVolumePredictor(radii_type="atomic", check_isostructural=False) - self.assertAlmostEqual(p.predict(apo, aps), 1196.31384276) + assert p.predict(apo, aps) == approx(1196.31384276) # (ii) Oxidation states are assigned. apo.add_oxidation_state_by_element({"Ag": 1, "P": 5, "O": -2}) aps.add_oxidation_state_by_element({"Ag": 1, "P": 5, "S": -2}) p = RLSVolumePredictor(radii_type="ionic") - self.assertAlmostEqual(p.predict(apo, aps), 1165.23259079) + assert p.predict(apo, aps) == approx(1165.23259079) p = RLSVolumePredictor(radii_type="atomic") - self.assertAlmostEqual(p.predict(apo, aps), 1196.31384276) + assert p.predict(apo, aps) == approx(1196.31384276) def test_modes(self): s = PymatgenTest.get_structure("CsCl") @@ -79,11 +82,12 @@ def test_modes(self): nacl.replace_species({"Cs": "Na"}) nacl.scale_lattice(184.384551033) p = RLSVolumePredictor(radii_type="ionic", use_bv=False) - self.assertRaises(ValueError, p.predict, s, nacl) + with pytest.raises(ValueError): + p.predict(s, nacl) p = RLSVolumePredictor(radii_type="ionic-atomic", use_bv=False) - self.assertAlmostEqual(p.predict(s, nacl), 391.884366481) + assert p.predict(s, nacl) == approx(391.884366481) p = RLSVolumePredictor(radii_type="ionic-atomic", use_bv=True) - self.assertAlmostEqual(p.predict(s, nacl), 342.84905395082535) + assert p.predict(s, nacl) == approx(342.84905395082535) class DLSVolumePredictorTest(PymatgenTest): @@ -94,28 +98,28 @@ def test_predict(self): fen = Structure.from_file(os.path.join(dir_path, "FeN_mp-6988.cif")) - self.assertAlmostEqual(p.predict(fen), 18.2252568873) + assert p.predict(fen) == approx(18.2252568873) fen.scale_lattice(fen.volume * 3.0) - self.assertAlmostEqual(p_nolimit.predict(fen), 18.2252568873) - self.assertAlmostEqual(p.predict(fen), fen.volume * 0.5) + assert p_nolimit.predict(fen) == approx(18.2252568873) + assert p.predict(fen) == approx(fen.volume * 0.5) fen.scale_lattice(fen.volume * 0.1) - self.assertAlmostEqual(p_nolimit.predict(fen), 18.2252568873) - self.assertAlmostEqual(p.predict(fen), fen.volume * 1.5) - self.assertAlmostEqual(p_fast.predict(fen), fen.volume * 1.5) + assert p_nolimit.predict(fen) == approx(18.2252568873) + assert p.predict(fen) == approx(fen.volume * 1.5) + assert p_fast.predict(fen) == approx(fen.volume * 1.5) lfpo = PymatgenTest.get_structure("LiFePO4") lfpo.scale_lattice(lfpo.volume * 3.0) - self.assertAlmostEqual(p_nolimit.predict(lfpo), 291.62094410192924) - self.assertAlmostEqual(p.predict(lfpo), lfpo.volume * 0.5) + assert p_nolimit.predict(lfpo) == approx(291.62094410192924) + assert p.predict(lfpo) == approx(lfpo.volume * 0.5) lfpo.scale_lattice(lfpo.volume * 0.1) - self.assertAlmostEqual(p_nolimit.predict(lfpo), 291.62094410192924) - self.assertAlmostEqual(p.predict(lfpo), lfpo.volume * 1.5) - self.assertAlmostEqual(p_fast.predict(lfpo), lfpo.volume * 1.5) + assert p_nolimit.predict(lfpo) == approx(291.62094410192924) + assert p.predict(lfpo) == approx(lfpo.volume * 1.5) + assert p_fast.predict(lfpo) == approx(lfpo.volume * 1.5) lmpo = PymatgenTest.get_structure("LiFePO4") lmpo.replace_species({"Fe": "Mn"}) - self.assertAlmostEqual(p.predict(lmpo), 290.795329052) + assert p.predict(lmpo) == approx(290.795329052) if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_disorder.py b/pymatgen/analysis/tests/test_disorder.py index 6fe5d7e7748..1ceb4c824a9 100644 --- a/pymatgen/analysis/tests/test_disorder.py +++ b/pymatgen/analysis/tests/test_disorder.py @@ -2,6 +2,8 @@ import unittest +from pytest import approx + from pymatgen.analysis.disorder import get_warren_cowley_parameters from pymatgen.core.periodic_table import Element from pymatgen.core.structure import Structure @@ -12,9 +14,9 @@ class OrderParameterTest(PymatgenTest): def test_compute_warren_cowley_parameters(self): s = Structure.from_prototype("CsCl", ["Mo", "W"], a=4) aij = get_warren_cowley_parameters(s, r=3.4, dr=0.3) - self.assertAlmostEqual(aij[(Element.Mo, Element.W)], -1.0) + assert aij[(Element.Mo, Element.W)] == approx(-1.0) aij = get_warren_cowley_parameters(s, r=4, dr=0.2) - self.assertAlmostEqual(aij[(Element.Mo, Element.Mo)], 1.0) + assert aij[(Element.Mo, Element.Mo)] == approx(1.0) s = Structure.from_prototype("CsCl", ["Mo", "W"], a=4) s = s * 4 @@ -23,8 +25,8 @@ def test_compute_warren_cowley_parameters(self): s[len(s) - 1] = "Mo" aij = get_warren_cowley_parameters(s, r=3.4, dr=0.3) - self.assertAlmostEqual(aij[(Element.Mo, Element.W)], -0.9453125) - self.assertEqual(aij[(Element.Mo, Element.W)], aij[(Element.W, Element.Mo)]) + assert aij[(Element.Mo, Element.W)] == approx(-0.9453125) + assert aij[(Element.Mo, Element.W)] == aij[(Element.W, Element.Mo)] if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_energy_models.py b/pymatgen/analysis/tests/test_energy_models.py index dd24f0c8984..064148cd535 100644 --- a/pymatgen/analysis/tests/test_energy_models.py +++ b/pymatgen/analysis/tests/test_energy_models.py @@ -7,6 +7,8 @@ import unittest import warnings +from pytest import approx + from pymatgen.analysis.energy_models import ( EwaldElectrostaticModel, IsingModel, @@ -40,28 +42,28 @@ def test_get_energy(self): m = EwaldElectrostaticModel() # large tolerance because scipy constants changed between 0.16.1 and 0.17 - self.assertAlmostEqual(m.get_energy(s), -264.66364858, 2) # Result from GULP + assert m.get_energy(s) == approx(-264.66364858, 2) # Result from GULP s2 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Li2O.cif")) - self.assertAlmostEqual(m.get_energy(s2), -145.39050015844839, 4) + assert m.get_energy(s2) == approx(-145.39050015844839, 4) def test_to_from_dict(self): m = EwaldElectrostaticModel() d = m.as_dict() - self.assertIsInstance(EwaldElectrostaticModel.from_dict(d), EwaldElectrostaticModel) + assert isinstance(EwaldElectrostaticModel.from_dict(d), EwaldElectrostaticModel) class SymmetryModelTest(unittest.TestCase): def test_get_energy(self): m = SymmetryModel() s2 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Li2O.cif")) - self.assertAlmostEqual(m.get_energy(s2), -225) + assert m.get_energy(s2) == approx(-225) def test_to_from_dict(self): m = SymmetryModel(symprec=0.2) d = m.as_dict() o = SymmetryModel.from_dict(d) - self.assertIsInstance(o, SymmetryModel) - self.assertAlmostEqual(o.symprec, 0.2) + assert isinstance(o, SymmetryModel) + assert o.symprec == approx(0.2) class IsingModelTest(unittest.TestCase): @@ -71,17 +73,17 @@ def test_get_energy(self): s = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "LiFePO4.cif")) s.replace_species({"Fe": Species("Fe", 2, {"spin": 4})}) - self.assertAlmostEqual(m.get_energy(s), 172.81260515787977) + assert m.get_energy(s) == approx(172.81260515787977) s[4] = Species("Fe", 2, {"spin": -4}) s[5] = Species("Fe", 2, {"spin": -4}) - self.assertAlmostEqual(m.get_energy(s), 51.97424405382921) + assert m.get_energy(s) == approx(51.97424405382921) def test_to_from_dict(self): m = IsingModel(5, 4) d = m.as_dict() o = IsingModel.from_dict(d) - self.assertIsInstance(o, IsingModel) - self.assertAlmostEqual(o.j, 5) + assert isinstance(o, IsingModel) + assert o.j == approx(5) if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_eos.py b/pymatgen/analysis/tests/test_eos.py index 1734fdb5297..8e2cc33a373 100644 --- a/pymatgen/analysis/tests/test_eos.py +++ b/pymatgen/analysis/tests/test_eos.py @@ -7,6 +7,7 @@ import unittest import numpy as np +from pytest import approx from pymatgen.analysis.eos import EOS, NumericalEOS from pymatgen.util.testing import PymatgenTest @@ -225,11 +226,11 @@ def test_fitting(self): np.testing.assert_array_almost_equal(fit.func(mp153_volumes), mp153_known_energies_vinet, decimal=5) - self.assertAlmostEqual(mp153_known_e0_vinet, fit.e0, places=4) - self.assertAlmostEqual(mp153_known_v0_vinet, fit.v0, places=4) + assert mp153_known_e0_vinet == approx(fit.e0, 4) + assert mp153_known_v0_vinet == approx(fit.v0, 4) # expt. value 35.5, known fit 36.16 - self.assertAlmostEqual(fit.b0_GPa, 36.16258687442761, 4) + assert fit.b0_GPa == approx(36.16258687442761, 4) # Si @@ -316,11 +317,11 @@ def test_fitting(self): np.testing.assert_array_almost_equal(fit.func(mp149_volumes), mp149_known_energies_vinet, decimal=5) - self.assertAlmostEqual(mp149_known_e0_vinet, fit.e0, places=4) - self.assertAlmostEqual(mp149_known_v0_vinet, fit.v0, places=4) + assert mp149_known_e0_vinet == approx(fit.e0, 4) + assert mp149_known_v0_vinet == approx(fit.v0, 4) # expt. value 97.9, known fit 88.39 - self.assertAlmostEqual(fit.b0_GPa, 88.38629337404822, 4) + assert fit.b0_GPa == approx(88.38629337404822, 4) # Ti @@ -407,21 +408,21 @@ def test_fitting(self): np.testing.assert_array_almost_equal(fit.func(mp72_volumes), mp72_known_energies_vinet, decimal=5) - self.assertAlmostEqual(mp72_known_e0_vinet, fit.e0, places=4) - self.assertAlmostEqual(mp72_known_v0_vinet, fit.v0, places=4) + assert mp72_known_e0_vinet == approx(fit.e0, 4) + assert mp72_known_v0_vinet == approx(fit.v0, 4) # expt. value 107.3, known fit 112.63 - self.assertAlmostEqual(fit.b0_GPa, 112.62927187296167, 4) + assert fit.b0_GPa == approx(112.62927187296167, 4) def test_numerical_eoswrapper(self): # using numerical eos directly vs via EOS wrapper numerical_eos = NumericalEOS(self.volumes, self.energies) numerical_eos.fit() - self.assertGreater(len(numerical_eos.eos_params), 3) - self.assertAlmostEqual(float(numerical_eos.e0), self.num_eos_fit.e0, 3) - self.assertAlmostEqual(float(numerical_eos.v0), self.num_eos_fit.v0, 3) - self.assertAlmostEqual(float(numerical_eos.b0), self.num_eos_fit.b0, 3) - self.assertAlmostEqual(float(numerical_eos.b1), self.num_eos_fit.b1, 3) + assert len(numerical_eos.eos_params) > 3 + assert float(numerical_eos.e0) == approx(self.num_eos_fit.e0, 3) + assert float(numerical_eos.v0) == approx(self.num_eos_fit.v0, 3) + assert float(numerical_eos.b0) == approx(self.num_eos_fit.b0, 3) + assert float(numerical_eos.b1) == approx(self.num_eos_fit.b1, 3) self.assertArrayAlmostEqual(numerical_eos.eos_params, self.num_eos_fit.eos_params) def test_numerical_eos_values(self): @@ -468,7 +469,7 @@ def test_summary_dict(self): "b1": self.num_eos_fit.b1, "v0": self.num_eos_fit.v0, } - self.assertDictEqual(self.num_eos_fit.results, d) + assert self.num_eos_fit.results == d if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_ewald.py b/pymatgen/analysis/tests/test_ewald.py index afcc5aa98d5..e3bd990d8d0 100644 --- a/pymatgen/analysis/tests/test_ewald.py +++ b/pymatgen/analysis/tests/test_ewald.py @@ -9,6 +9,8 @@ import warnings import numpy as np +import pytest +from pytest import approx from pymatgen.analysis.ewald import EwaldMinimizer, EwaldSummation from pymatgen.io.vasp.inputs import Poscar @@ -29,22 +31,19 @@ def tearDown(self): def test_init(self): ham = EwaldSummation(self.s, compute_forces=True) - self.assertAlmostEqual(ham.real_space_energy, -502.23549897772602, 4) - self.assertAlmostEqual(ham.reciprocal_space_energy, 6.1541071599534654, 4) - self.assertAlmostEqual(ham.point_energy, -620.22598358035918, 4) - self.assertAlmostEqual(ham.total_energy, -1123.00766, 1) - self.assertAlmostEqual(ham.forces[0, 0], -1.98818620e-01, 4) - self.assertAlmostEqual(sum(sum(abs(ham.forces))), 915.925354346, 4, "Forces incorrect") - self.assertAlmostEqual(sum(sum(ham.real_space_energy_matrix)), ham.real_space_energy, 4) - self.assertAlmostEqual(sum(sum(ham.reciprocal_space_energy_matrix)), ham.reciprocal_space_energy, 4) - self.assertAlmostEqual(sum(ham.point_energy_matrix), ham.point_energy, 4) - self.assertAlmostEqual( - sum(sum(ham.total_energy_matrix)) + ham._charged_cell_energy, - ham.total_energy, - 2, - ) - - self.assertRaises(ValueError, EwaldSummation, self.original_s) + assert ham.real_space_energy == approx(-502.23549897772602, 4) + assert ham.reciprocal_space_energy == approx(6.1541071599534654, 4) + assert ham.point_energy == approx(-620.22598358035918, 4) + assert ham.total_energy == approx(-1123.00766, 1) + assert ham.forces[0, 0] == approx(-1.98818620e-01, 4) + assert sum(sum(abs(ham.forces))) == approx(915.925354346, 4), "Forces incorrect" + assert sum(sum(ham.real_space_energy_matrix)) == approx(ham.real_space_energy, 4) + assert sum(sum(ham.reciprocal_space_energy_matrix)) == approx(ham.reciprocal_space_energy, 4) + assert sum(ham.point_energy_matrix) == approx(ham.point_energy, 4) + assert sum(sum(ham.total_energy_matrix)) + ham._charged_cell_energy == approx(ham.total_energy, 2) + + with pytest.raises(ValueError): + EwaldSummation(self.original_s) # try sites with charge. charges = [] for site in self.original_s: @@ -59,34 +58,34 @@ def test_init(self): self.original_s.add_site_property("charge", charges) ham2 = EwaldSummation(self.original_s) - self.assertAlmostEqual(ham2.real_space_energy, -502.23549897772602, 4) + assert ham2.real_space_energy == approx(-502.23549897772602, 4) def test_from_dict(self): ham = EwaldSummation(self.s, compute_forces=True) ham2 = EwaldSummation.from_dict(ham.as_dict()) - self.assertIsNone(ham._real) - self.assertFalse(ham._initialized) - self.assertIsNone(ham2._real) - self.assertFalse(ham2._initialized) - self.assertTrue(np.array_equal(ham.total_energy_matrix, ham2.total_energy_matrix)) + assert ham._real is None + assert not ham._initialized + assert ham2._real is None + assert not ham2._initialized + assert np.array_equal(ham.total_energy_matrix, ham2.total_energy_matrix) # check lazy eval - self.assertAlmostEqual(ham.total_energy, -1123.00766, 1) - self.assertIsNotNone(ham._real) - self.assertTrue(ham._initialized) + assert ham.total_energy == approx(-1123.00766, 1) + assert ham._real is not None + assert ham._initialized ham2 = EwaldSummation.from_dict(ham.as_dict()) - self.assertIsNotNone(ham2._real) - self.assertTrue(ham2._initialized) - self.assertTrue(np.array_equal(ham.total_energy_matrix, ham2.total_energy_matrix)) + assert ham2._real is not None + assert ham2._initialized + assert np.array_equal(ham.total_energy_matrix, ham2.total_energy_matrix) def test_as_dict(self): ham = EwaldSummation(self.s, compute_forces=True) d = ham.as_dict() - self.assertTrue(d["compute_forces"]) - self.assertEqual(d["eta"], ham._eta) - self.assertEqual(d["acc_factor"], ham._acc_factor) - self.assertEqual(d["real_space_cut"], ham._rmax) - self.assertEqual(d["recip_space_cut"], ham._gmax) - self.assertEqual(ham.as_dict(), EwaldSummation.from_dict(d).as_dict()) + assert d["compute_forces"] + assert d["eta"] == ham._eta + assert d["acc_factor"] == ham._acc_factor + assert d["real_space_cut"] == ham._rmax + assert d["recip_space_cut"] == ham._gmax + assert ham.as_dict() == EwaldSummation.from_dict(d).as_dict() class EwaldMinimizerTest(unittest.TestCase): @@ -116,9 +115,9 @@ def test_init(self): e_min = EwaldMinimizer(matrix, m_list, 50) - self.assertEqual(len(e_min.output_lists), 15, "Wrong number of permutations returned") - self.assertAlmostEqual(e_min.minimized_sum, 111.63, 3, "Returned wrong minimum value") - self.assertEqual(len(e_min.best_m_list), 6, "Returned wrong number of permutations") + assert len(e_min.output_lists) == 15, "Wrong number of permutations returned" + assert e_min.minimized_sum == approx(111.63, 3), "Returned wrong minimum value" + assert len(e_min.best_m_list) == 6, "Returned wrong number of permutations" def test_site(self): """Test that uses an uncharged structure""" @@ -130,9 +129,9 @@ def test_site(self): # Comparison to LAMMPS result ham = EwaldSummation(s, compute_forces=True) - self.assertAlmostEqual(-1226.3335, ham.total_energy, 3) - self.assertAlmostEqual(-45.8338, ham.get_site_energy(0), 3) - self.assertAlmostEqual(-27.2978, ham.get_site_energy(8), 3) + assert -1226.3335 == approx(ham.total_energy, 3) + assert -45.8338 == approx(ham.get_site_energy(0), 3) + assert -27.2978 == approx(ham.get_site_energy(8), 3) if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_functional_groups.py b/pymatgen/analysis/tests/test_functional_groups.py index 527ac1b4bd7..465bf3e5356 100644 --- a/pymatgen/analysis/tests/test_functional_groups.py +++ b/pymatgen/analysis/tests/test_functional_groups.py @@ -54,37 +54,37 @@ def test_init(self): extractor_mol = FunctionalGroupExtractor(self.mol) extractor_mg = self.extractor - self.assertEqual(extractor_str.molgraph, extractor_mol.molgraph) - self.assertEqual(extractor_str.molgraph, extractor_mg.molgraph) - self.assertEqual(extractor_str.species, extractor_mol.species) - self.assertEqual(extractor_str.species, extractor_mg.species) + assert extractor_str.molgraph == extractor_mol.molgraph + assert extractor_str.molgraph == extractor_mg.molgraph + assert extractor_str.species == extractor_mol.species + assert extractor_str.species == extractor_mg.species # Test optimization file_no_h = os.path.join(test_dir, "func_group_test_no_h.mol") extractor_no_h = FunctionalGroupExtractor(file_no_h, optimize=True) - self.assertEqual(len(extractor_no_h.molecule), len(extractor_mol.molecule)) - self.assertEqual(extractor_no_h.species, extractor_mol.species) + assert len(extractor_no_h.molecule) == len(extractor_mol.molecule) + assert extractor_no_h.species == extractor_mol.species def test_get_heteroatoms(self): heteroatoms = self.extractor.get_heteroatoms() hetero_species = [self.extractor.species[x] for x in heteroatoms] - self.assertEqual(len(heteroatoms), 3) - self.assertEqual(sorted(hetero_species), ["N", "O", "O"]) + assert len(heteroatoms) == 3 + assert sorted(hetero_species) == ["N", "O", "O"] # Test with limitation hetero_no_o = self.extractor.get_heteroatoms(elements=["N"]) - self.assertEqual(len(hetero_no_o), 1) + assert len(hetero_no_o) == 1 def test_get_special_carbon(self): special_cs = self.extractor.get_special_carbon() - self.assertEqual(len(special_cs), 4) + assert len(special_cs) == 4 # Test with limitation special_cs_no_o = self.extractor.get_special_carbon(elements=["N"]) - self.assertEqual(len(special_cs_no_o), 2) + assert len(special_cs_no_o) == 2 def test_link_marked_atoms(self): heteroatoms = self.extractor.get_heteroatoms() @@ -92,8 +92,8 @@ def test_link_marked_atoms(self): link = self.extractor.link_marked_atoms(heteroatoms | special_cs) - self.assertEqual(len(link), 1) - self.assertEqual(len(link[0]), 9) + assert len(link) == 1 + assert len(link[0]) == 9 # Exclude Oxygen-related functional groups heteroatoms_no_o = self.extractor.get_heteroatoms(elements=["N"]) @@ -102,17 +102,17 @@ def test_link_marked_atoms(self): link_no_o = self.extractor.link_marked_atoms(all_marked) - self.assertEqual(len(link_no_o), 2) + assert len(link_no_o) == 2 def test_get_basic_functional_groups(self): basics = self.extractor.get_basic_functional_groups() # Molecule has one methyl group which will be caught. - self.assertEqual(len(basics), 1) - self.assertEqual(len(basics[0]), 4) + assert len(basics) == 1 + assert len(basics[0]) == 4 basics_no_methyl = self.extractor.get_basic_functional_groups(func_groups=["phenyl"]) - self.assertEqual(len(basics_no_methyl), 0) + assert len(basics_no_methyl) == 0 def test_get_all_functional_groups(self): heteroatoms = self.extractor.get_heteroatoms() @@ -123,18 +123,18 @@ def test_get_all_functional_groups(self): all_func = self.extractor.get_all_functional_groups() - self.assertEqual(len(all_func), (len(link) + len(basics))) - self.assertEqual(sorted(all_func), sorted(link + basics)) + assert len(all_func) == (len(link) + len(basics)) + assert sorted(all_func) == sorted(link + basics) def test_categorize_functional_groups(self): all_func = self.extractor.get_all_functional_groups() categorized = self.extractor.categorize_functional_groups(all_func) - self.assertTrue("O=C1C=CC(=O)[N]1" in categorized) - self.assertTrue("[CH3]" in categorized) + assert "O=C1C=CC(=O)[N]1" in categorized + assert "[CH3]" in categorized total_count = sum(c["count"] for c in categorized.values()) - self.assertEqual(total_count, 2) + assert total_count == 2 if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_graphs.py b/pymatgen/analysis/tests/test_graphs.py index b97f5ef1cb7..058da3d671d 100644 --- a/pymatgen/analysis/tests/test_graphs.py +++ b/pymatgen/analysis/tests/test_graphs.py @@ -12,7 +12,9 @@ import networkx as nx import networkx.algorithms.isomorphism as iso +import pytest from monty.serialization import loadfn +from pytest import approx from pymatgen.analysis.graphs import ( MoleculeGraph, @@ -117,29 +119,23 @@ def tearDown(self): def test_inappropriate_construction(self): # Check inappropriate strategy - with self.assertRaises(ValueError): + with pytest.raises(ValueError): StructureGraph.with_local_env_strategy(self.NiO, CovalentBondNN()) def test_properties(self): - self.assertEqual(self.mos2_sg.name, "bonds") - self.assertEqual(self.mos2_sg.edge_weight_name, "bond_length") - self.assertEqual(self.mos2_sg.edge_weight_unit, "Ã…") - self.assertEqual(self.mos2_sg.get_coordination_of_site(0), 6) - self.assertEqual(len(self.mos2_sg.get_connected_sites(0)), 6) - self.assertTrue(isinstance(self.mos2_sg.get_connected_sites(0)[0].site, PeriodicSite)) - self.assertEqual(str(self.mos2_sg.get_connected_sites(0)[0].site.specie), "S") - self.assertAlmostEqual( - self.mos2_sg.get_connected_sites(0, jimage=(0, 0, 100))[0].site.frac_coords[2], - 100.303027, - ) + assert self.mos2_sg.name == "bonds" + assert self.mos2_sg.edge_weight_name == "bond_length" + assert self.mos2_sg.edge_weight_unit == "Ã…" + assert self.mos2_sg.get_coordination_of_site(0) == 6 + assert len(self.mos2_sg.get_connected_sites(0)) == 6 + assert isinstance(self.mos2_sg.get_connected_sites(0)[0].site, PeriodicSite) + assert str(self.mos2_sg.get_connected_sites(0)[0].site.specie) == "S" + assert self.mos2_sg.get_connected_sites(0, jimage=(0, 0, 100))[0].site.frac_coords[2] == approx(100.303027) # these two graphs should be equivalent for n in range(len(self.bc_square_sg)): - self.assertEqual( - self.bc_square_sg.get_coordination_of_site(n), - self.bc_square_sg_r.get_coordination_of_site(n), - ) + assert self.bc_square_sg.get_coordination_of_site(n) == self.bc_square_sg_r.get_coordination_of_site(n) # test we're not getting duplicate connected sites # thanks to Jack D. Sundberg for reporting this bug @@ -157,8 +153,8 @@ def test_properties(self): nacl_graph = StructureGraph.with_local_env_strategy(nacl, CutOffDictNN({("Cl", "Cl"): 5.0})) - self.assertEqual(len(nacl_graph.get_connected_sites(1)), 12) - self.assertEqual(len(nacl_graph.graph.get_edge_data(1, 1)), 6) + assert len(nacl_graph.get_connected_sites(1)) == 12 + assert len(nacl_graph.graph.get_edge_data(1, 1)) == 6 def test_set_node_attributes(self): self.square_sg.set_node_attributes() @@ -168,11 +164,11 @@ def test_set_node_attributes(self): properties = nx.get_node_attributes(self.square_sg.graph, "properties") for idx, site in enumerate(self.square_sg.structure): - self.assertEqual(str(specie[idx]), str(site.specie)) - self.assertEqual(coords[idx][0], site.coords[0]) - self.assertEqual(coords[idx][1], site.coords[1]) - self.assertEqual(coords[idx][2], site.coords[2]) - self.assertEqual(properties[idx], site.properties) + assert str(specie[idx]) == str(site.specie) + assert coords[idx][0] == site.coords[0] + assert coords[idx][1] == site.coords[1] + assert coords[idx][2] == site.coords[2] + assert properties[idx] == site.properties def test_edge_editing(self): square = copy.deepcopy(self.square_sg) @@ -185,12 +181,12 @@ def test_edge_editing(self): new_edge_properties={"foo": "bar"}, ) new_edge = square.graph.get_edge_data(0, 0)[0] - self.assertEqual(new_edge["weight"], 0.0) - self.assertEqual(new_edge["foo"], "bar") + assert new_edge["weight"] == 0.0 + assert new_edge["foo"] == "bar" square.break_edge(0, 0, to_jimage=(1, 0, 0)) - self.assertEqual(len(square.graph.get_edge_data(0, 0)), 1) + assert len(square.graph.get_edge_data(0, 0)) == 1 def test_insert_remove(self): @@ -200,17 +196,17 @@ def test_insert_remove(self): # Ensure that insert_node appropriately wraps Structure.insert() struct_copy.insert(1, "O", [0.5, 0.5, 0.5]) square_copy.insert_node(1, "O", [0.5, 0.5, 0.5]) - self.assertEqual(struct_copy, square_copy.structure) + assert struct_copy == square_copy.structure # Test that removal is also equivalent between Structure and StructureGraph.structure struct_copy.remove_sites([1]) square_copy.remove_nodes([1]) - self.assertEqual(struct_copy, square_copy.structure) + assert struct_copy == square_copy.structure square_copy.insert_node( 1, "O", [0.5, 0.5, 0.5], edges=[{"from_index": 1, "to_index": 0, "to_jimage": (0, 0, 0)}] ) - self.assertEqual(square_copy.get_coordination_of_site(1), 1) + assert square_copy.get_coordination_of_site(1) == 1 # Test that StructureGraph.graph is correctly updated square_copy.insert_node( @@ -218,8 +214,8 @@ def test_insert_remove(self): ) square_copy.remove_nodes([1]) - self.assertEqual(square_copy.graph.number_of_nodes(), 2) - self.assertEqual(square_copy.graph.number_of_edges(), 3) + assert square_copy.graph.number_of_nodes() == 2 + assert square_copy.graph.number_of_edges() == 3 def test_substitute(self): structure = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Li2O.cif")) @@ -234,11 +230,11 @@ def test_substitute(self): # Ensure that strings and molecules lead to equivalent substitutions sg.substitute_group(1, molecule, MinimumDistanceNN) sg_copy.substitute_group(1, "methyl", MinimumDistanceNN) - self.assertEqual(sg, sg_copy) + assert sg == sg_copy # Ensure that the underlying structure has been modified as expected structure_copy.substitute(1, "methyl") - self.assertEqual(structure_copy, sg.structure) + assert structure_copy == sg.structure # Test inclusion of graph dictionary graph_dict = { @@ -250,14 +246,14 @@ def test_substitute(self): sg_with_graph = StructureGraph.with_local_env_strategy(structure_copy_graph, MinimumDistanceNN()) sg_with_graph.substitute_group(1, "methyl", MinimumDistanceNN, graph_dict=graph_dict) edge = sg_with_graph.graph.get_edge_data(11, 13)[0] - self.assertEqual(edge["weight"], 0.5) + assert edge["weight"] == 0.5 def test_auto_image_detection(self): sg = StructureGraph.with_empty_graph(self.structure) sg.add_edge(0, 0) - self.assertEqual(len(list(sg.graph.edges(data=True))), 3) + assert len(list(sg.graph.edges(data=True))) == 3 def test_str(self): @@ -348,16 +344,16 @@ def test_mul(self): sq_sg_1 = self.square_sg * (2, 2, 1) sq_sg_1 = sq_sg_1 * (2, 2, 1) sq_sg_2 = self.square_sg * (4, 4, 1) - self.assertEqual(sq_sg_1.graph.number_of_edges(), sq_sg_2.graph.number_of_edges()) + assert sq_sg_1.graph.number_of_edges() == sq_sg_2.graph.number_of_edges() # TODO: the below test still gives 8 != 4 # self.assertEqual(self.square_sg.get_coordination_of_site(0), 4) mos2_sg_mul = self.mos2_sg * (3, 3, 1) for idx in mos2_sg_mul.structure.indices_from_symbol("Mo"): - self.assertEqual(mos2_sg_mul.get_coordination_of_site(idx), 6) + assert mos2_sg_mul.get_coordination_of_site(idx) == 6 mos2_sg_premul = StructureGraph.with_local_env_strategy(self.structure * (3, 3, 1), MinimumDistanceNN()) - self.assertTrue(mos2_sg_mul == mos2_sg_premul) + assert mos2_sg_mul == mos2_sg_premul # test 3D Structure @@ -365,7 +361,7 @@ def test_mul(self): nio_sg = nio_sg * 3 for n in range(len(nio_sg)): - self.assertEqual(nio_sg.get_coordination_of_site(n), 6) + assert nio_sg.get_coordination_of_site(n) == 6 @unittest.skipIf(pygraphviz is None or not (which("neato") and which("fdp")), "graphviz executables not present") def test_draw(self): @@ -419,25 +415,25 @@ def test_to_from_dict(self): d = self.mos2_sg.as_dict() sg = StructureGraph.from_dict(d) d2 = sg.as_dict() - self.assertDictEqual(d, d2) + assert d == d2 def test_from_local_env_and_equality_and_diff(self): nn = MinimumDistanceNN() sg = StructureGraph.with_local_env_strategy(self.structure, nn) - self.assertEqual(sg.graph.number_of_edges(), 6) + assert sg.graph.number_of_edges() == 6 nn2 = MinimumOKeeffeNN() sg2 = StructureGraph.with_local_env_strategy(self.structure, nn2) - self.assertTrue(sg == sg2) - self.assertTrue(sg == self.mos2_sg) + assert sg == sg2 + assert sg == self.mos2_sg # TODO: find better test case where graphs are different diff = sg.diff(sg2) - self.assertEqual(diff["dist"], 0) + assert diff["dist"] == 0 - self.assertEqual(self.square_sg.get_coordination_of_site(0), 2) + assert self.square_sg.get_coordination_of_site(0) == 2 def test_from_edges(self): edges = { @@ -451,7 +447,7 @@ def test_from_edges(self): sg = StructureGraph.with_edges(structure, edges) - self.assertEqual(sg, self.square_sg) + assert sg == self.square_sg def test_extract_molecules(self): @@ -466,34 +462,34 @@ def test_extract_molecules(self): sg = StructureGraph.with_local_env_strategy(s, nn) molecules = sg.get_subgraphs_as_molecules() - self.assertEqual(molecules[0].composition.formula, "H3 C1") - self.assertEqual(len(molecules), 1) + assert molecules[0].composition.formula == "H3 C1" + assert len(molecules) == 1 molecules = self.mos2_sg.get_subgraphs_as_molecules() - self.assertEqual(len(molecules), 0) + assert len(molecules) == 0 def test_types_and_weights_of_connections(self): types = self.mos2_sg.types_and_weights_of_connections - self.assertEqual(len(types["Mo-S"]), 6) - self.assertAlmostEqual(types["Mo-S"][0], 2.416931678417331) + assert len(types["Mo-S"]) == 6 + assert types["Mo-S"][0] == approx(2.416931678417331) def test_weight_statistics(self): weight_statistics = self.mos2_sg.weight_statistics - self.assertEqual(len(weight_statistics["all_weights"]), 6) - self.assertAlmostEqual(weight_statistics["min"], 2.4169314100201875) - self.assertAlmostEqual(weight_statistics["variance"], 0) + assert len(weight_statistics["all_weights"]) == 6 + assert weight_statistics["min"] == approx(2.4169314100201875) + assert weight_statistics["variance"] == approx(0) def test_types_of_coordination_environments(self): types = self.mos2_sg.types_of_coordination_environments() - self.assertListEqual(types, ["Mo-S(6)", "S-Mo(3)"]) + assert types == ["Mo-S(6)", "S-Mo(3)"] types_anonymous = self.mos2_sg.types_of_coordination_environments(anonymous=True) - self.assertListEqual(types_anonymous, ["A-B(3)", "A-B(6)"]) + assert types_anonymous == ["A-B(3)", "A-B(6)"] def test_no_duplicate_hops(self): @@ -507,7 +503,7 @@ def test_no_duplicate_hops(self): sg = StructureGraph.with_local_env_strategy(test_structure, nn) - self.assertEqual(sg.graph.number_of_edges(), 3) + assert sg.graph.number_of_edges() == 3 def test_sort(self): @@ -516,9 +512,9 @@ def test_sort(self): sg.graph.add_edge(3, 1, to_jimage=(0, 0, 0)) sg.graph.add_edge(2, 1, to_jimage=(0, 0, 0)) - self.assertEqual(list(sg.graph.edges)[-2:], [(3, 1, 0), (2, 1, 0)]) + assert list(sg.graph.edges)[-2:] == [(3, 1, 0), (2, 1, 0)] sg.sort() - self.assertEqual(list(sg.graph.edges)[-2:], [(1, 3, 0), (1, 2, 0)]) + assert list(sg.graph.edges)[-2:] == [(1, 3, 0), (1, 2, 0)] class MoleculeGraphTest(unittest.TestCase): @@ -630,55 +626,43 @@ def test_construction(self): mol_graph = MoleculeGraph.with_edges(self.pc_frag1, edges_frag) # dumpfn(mol_graph.as_dict(), os.path.join(module_dir,"pc_frag1_mg.json")) ref_mol_graph = loadfn(os.path.join(module_dir, "pc_frag1_mg.json")) - self.assertEqual(mol_graph, ref_mol_graph) - self.assertEqual(mol_graph.graph.adj, ref_mol_graph.graph.adj) + assert mol_graph == ref_mol_graph + assert mol_graph.graph.adj == ref_mol_graph.graph.adj for node in mol_graph.graph.nodes: - self.assertEqual( - mol_graph.graph.nodes[node]["specie"], - ref_mol_graph.graph.nodes[node]["specie"], - ) + assert mol_graph.graph.nodes[node]["specie"] == ref_mol_graph.graph.nodes[node]["specie"] for ii in range(3): - self.assertEqual( - mol_graph.graph.nodes[node]["coords"][ii], - ref_mol_graph.graph.nodes[node]["coords"][ii], - ) + assert mol_graph.graph.nodes[node]["coords"][ii] == ref_mol_graph.graph.nodes[node]["coords"][ii] edges_pc = {(e[0], e[1]): {"weight": 1.0} for e in self.pc_edges} mol_graph = MoleculeGraph.with_edges(self.pc, edges_pc) # dumpfn(mol_graph.as_dict(), os.path.join(module_dir,"pc_mg.json")) ref_mol_graph = loadfn(os.path.join(module_dir, "pc_mg.json")) - self.assertEqual(mol_graph, ref_mol_graph) - self.assertEqual(mol_graph.graph.adj, ref_mol_graph.graph.adj) + assert mol_graph == ref_mol_graph + assert mol_graph.graph.adj == ref_mol_graph.graph.adj for node in mol_graph.graph: - self.assertEqual( - mol_graph.graph.nodes[node]["specie"], - ref_mol_graph.graph.nodes[node]["specie"], - ) + assert mol_graph.graph.nodes[node]["specie"] == ref_mol_graph.graph.nodes[node]["specie"] for ii in range(3): - self.assertEqual( - mol_graph.graph.nodes[node]["coords"][ii], - ref_mol_graph.graph.nodes[node]["coords"][ii], - ) + assert mol_graph.graph.nodes[node]["coords"][ii] == ref_mol_graph.graph.nodes[node]["coords"][ii] mol_graph_edges = MoleculeGraph.with_edges(self.pc, edges=edges_pc) mol_graph_strat = MoleculeGraph.with_local_env_strategy(self.pc, OpenBabelNN()) - self.assertTrue(mol_graph_edges.isomorphic_to(mol_graph_strat)) + assert mol_graph_edges.isomorphic_to(mol_graph_strat) # Check inappropriate strategy - with self.assertRaises(ValueError): + with pytest.raises(ValueError): MoleculeGraph.with_local_env_strategy(self.pc, VoronoiNN()) def test_properties(self): - self.assertEqual(self.cyclohexene.name, "bonds") - self.assertEqual(self.cyclohexene.edge_weight_name, "strength") - self.assertEqual(self.cyclohexene.edge_weight_unit, "") - self.assertEqual(self.cyclohexene.get_coordination_of_site(0), 4) - self.assertEqual(self.cyclohexene.get_coordination_of_site(2), 3) - self.assertEqual(self.cyclohexene.get_coordination_of_site(15), 1) - self.assertEqual(len(self.cyclohexene.get_connected_sites(0)), 4) - self.assertTrue(isinstance(self.cyclohexene.get_connected_sites(0)[0].site, Site)) - self.assertEqual(str(self.cyclohexene.get_connected_sites(0)[0].site.specie), "H") + assert self.cyclohexene.name == "bonds" + assert self.cyclohexene.edge_weight_name == "strength" + assert self.cyclohexene.edge_weight_unit == "" + assert self.cyclohexene.get_coordination_of_site(0) == 4 + assert self.cyclohexene.get_coordination_of_site(2) == 3 + assert self.cyclohexene.get_coordination_of_site(15) == 1 + assert len(self.cyclohexene.get_connected_sites(0)) == 4 + assert isinstance(self.cyclohexene.get_connected_sites(0)[0].site, Site) + assert str(self.cyclohexene.get_connected_sites(0)[0].site.specie) == "H" def test_set_node_attributes(self): self.ethylene.set_node_attributes() @@ -688,28 +672,28 @@ def test_set_node_attributes(self): properties = nx.get_node_attributes(self.ethylene.graph, "properties") for idx, site in enumerate(self.ethylene.molecule): - self.assertEqual(str(specie[idx]), str(site.specie)) - self.assertEqual(coords[idx][0], site.coords[0]) - self.assertEqual(coords[idx][1], site.coords[1]) - self.assertEqual(coords[idx][2], site.coords[2]) - self.assertEqual(properties[idx], site.properties) + assert str(specie[idx]) == str(site.specie) + assert coords[idx][0] == site.coords[0] + assert coords[idx][1] == site.coords[1] + assert coords[idx][2] == site.coords[2] + assert properties[idx] == site.properties def test_coordination(self): molecule = Molecule(["C", "C"], [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]]) mg = MoleculeGraph.with_empty_graph(molecule) - self.assertEqual(mg.get_coordination_of_site(0), 0) + assert mg.get_coordination_of_site(0) == 0 - self.assertEqual(self.cyclohexene.get_coordination_of_site(0), 4) + assert self.cyclohexene.get_coordination_of_site(0) == 4 def test_edge_editing(self): self.cyclohexene.alter_edge(0, 1, new_weight=0.0, new_edge_properties={"foo": "bar"}) new_edge = self.cyclohexene.graph.get_edge_data(0, 1)[0] - self.assertEqual(new_edge["weight"], 0.0) - self.assertEqual(new_edge["foo"], "bar") + assert new_edge["weight"] == 0.0 + assert new_edge["foo"] == "bar" self.cyclohexene.break_edge(0, 1) - self.assertTrue(self.cyclohexene.graph.get_edge_data(0, 1) is None) + assert self.cyclohexene.graph.get_edge_data(0, 1) is None # Replace the now-broken edge self.cyclohexene.add_edge(0, 1, weight=1.0) @@ -721,12 +705,12 @@ def test_insert_remove(self): # Ensure that insert_node appropriately wraps Molecule.insert() mol_copy.insert(1, "O", [0.5, 0.5, 0.5]) eth_copy.insert_node(1, "O", [0.5, 0.5, 0.5]) - self.assertEqual(mol_copy, eth_copy.molecule) + assert mol_copy == eth_copy.molecule # Test that removal is also equivalent between Molecule and MoleculeGraph.molecule mol_copy.remove_sites([1]) eth_copy.remove_nodes([1]) - self.assertEqual(mol_copy, eth_copy.molecule) + assert mol_copy == eth_copy.molecule eth_copy.insert_node( 1, @@ -734,12 +718,12 @@ def test_insert_remove(self): [0.5, 0.5, 0.5], edges=[{"from_index": 1, "to_index": 2}, {"from_index": 1, "to_index": 3}], ) - self.assertEqual(eth_copy.get_coordination_of_site(1), 2) + assert eth_copy.get_coordination_of_site(1) == 2 # Test that MoleculeGraph.graph is correctly updated eth_copy.remove_nodes([1, 2]) - self.assertEqual(eth_copy.graph.number_of_nodes(), 5) - self.assertEqual(eth_copy.graph.number_of_edges(), 2) + assert eth_copy.graph.number_of_nodes() == 5 + assert eth_copy.graph.number_of_edges() == 2 def test_get_disconnected(self): disconnected = Molecule( @@ -774,9 +758,9 @@ def test_get_disconnected(self): dis_mg.add_edge(0, 4) fragments = dis_mg.get_disconnected_fragments() - self.assertEqual(len(fragments), 2) - self.assertEqual(fragments[0].molecule, no_he) - self.assertEqual(fragments[1].molecule, just_he) + assert len(fragments) == 2 + assert fragments[0].molecule == no_he + assert fragments[1].molecule == just_he con_mg = MoleculeGraph.with_empty_graph(no_he) con_mg.add_edge(0, 1) @@ -784,7 +768,7 @@ def test_get_disconnected(self): con_mg.add_edge(0, 3) con_mg.add_edge(0, 4) fragments = con_mg.get_disconnected_fragments() - self.assertEqual(len(fragments), 1) + assert len(fragments) == 1 def test_split(self): bonds = [(0, 1), (4, 5)] @@ -796,14 +780,14 @@ def test_split(self): } # Perform retro-Diels-Alder reaction - turn product into reactants reactants = self.cyclohexene.split_molecule_subgraphs(bonds, allow_reverse=True, alterations=alterations) - self.assertTrue(isinstance(reactants, list)) + assert isinstance(reactants, list) reactants = sorted(reactants, key=len) # After alterations, reactants should be ethylene and butadiene - self.assertEqual(reactants[0], self.ethylene) - self.assertEqual(reactants[1], self.butadiene) + assert reactants[0] == self.ethylene + assert reactants[1] == self.butadiene - with self.assertRaises(MolGraphSplitError): + with pytest.raises(MolGraphSplitError): self.cyclohexene.split_molecule_subgraphs([(0, 1)]) # Test naive charge redistribution @@ -815,9 +799,9 @@ def test_split(self): new_mgs = oh_mg.split_molecule_subgraphs([(0, 1)]) for mg in new_mgs: if str(mg.molecule[0].specie) == "O": - self.assertEqual(mg.molecule.charge, -1) + assert mg.molecule.charge == -1 else: - self.assertEqual(mg.molecule.charge, 0) + assert mg.molecule.charge == 0 # Trying to test to ensure that remapping of nodes to atoms works diff_species = Molecule( @@ -846,7 +830,7 @@ def test_split(self): for j in range(len(split_mg.graph.nodes)): atom = split_mg.molecule[j] - self.assertEqual(species[j], str(atom.specie)) + assert species[j] == str(atom.specie) def test_build_unique_fragments(self): edges = {(e[0], e[1]): None for e in self.pc_edges} @@ -856,42 +840,37 @@ def test_build_unique_fragments(self): for key in unique_fragment_dict: for fragment in unique_fragment_dict[key]: unique_fragments.append(fragment) - self.assertEqual(len(unique_fragments), 295) + assert len(unique_fragments) == 295 nm = iso.categorical_node_match("specie", "ERROR") for ii in range(295): # Test that each fragment is unique for jj in range(ii + 1, 295): - self.assertFalse( - nx.is_isomorphic( - unique_fragments[ii].graph, - unique_fragments[jj].graph, - node_match=nm, - ) + assert not nx.is_isomorphic( + unique_fragments[ii].graph, + unique_fragments[jj].graph, + node_match=nm, ) # Test that each fragment correctly maps between Molecule and graph - self.assertEqual( - len(unique_fragments[ii].molecule), - len(unique_fragments[ii].graph.nodes), - ) + assert len(unique_fragments[ii].molecule) == len(unique_fragments[ii].graph.nodes) species = nx.get_node_attributes(unique_fragments[ii].graph, "specie") coords = nx.get_node_attributes(unique_fragments[ii].graph, "coords") mol = unique_fragments[ii].molecule for ss, site in enumerate(mol): - self.assertEqual(str(species[ss]), str(site.specie)) - self.assertEqual(coords[ss][0], site.coords[0]) - self.assertEqual(coords[ss][1], site.coords[1]) - self.assertEqual(coords[ss][2], site.coords[2]) + assert str(species[ss]) == str(site.specie) + assert coords[ss][0] == site.coords[0] + assert coords[ss][1] == site.coords[1] + assert coords[ss][2] == site.coords[2] # Test that each fragment is connected - self.assertTrue(nx.is_connected(unique_fragments[ii].graph.to_undirected())) + assert nx.is_connected(unique_fragments[ii].graph.to_undirected()) def test_find_rings(self): rings = self.cyclohexene.find_rings(including=[0]) - self.assertEqual(sorted(rings[0]), [(0, 5), (1, 0), (2, 1), (3, 2), (4, 3), (5, 4)]) + assert sorted(rings[0]) == [(0, 5), (1, 0), (2, 1), (3, 2), (4, 3), (5, 4)] no_rings = self.butadiene.find_rings() - self.assertEqual(no_rings, []) + assert no_rings == [] def test_isomorphic(self): ethylene = Molecule.from_file( @@ -915,8 +894,8 @@ def test_isomorphic(self): ) # If they are equal, they must also be isomorphic eth_copy = copy.deepcopy(self.ethylene) - self.assertTrue(self.ethylene.isomorphic_to(eth_copy)) - self.assertFalse(self.butadiene.isomorphic_to(self.ethylene)) + assert self.ethylene.isomorphic_to(eth_copy) + assert not self.butadiene.isomorphic_to(self.ethylene) def test_substitute(self): molecule = FunctionalGroups["methyl"] @@ -930,7 +909,7 @@ def test_substitute(self): # Ensure that strings and molecules lead to equivalent substitutions eth_mol.substitute_group(5, molecule, MinimumDistanceNN) eth_str.substitute_group(5, "methyl", MinimumDistanceNN) - self.assertEqual(eth_mol, eth_str) + assert eth_mol == eth_str graph_dict = { (0, 1): {"weight": 1.0}, @@ -943,8 +922,8 @@ def test_substitute(self): # Check that MoleculeGraph input is handled properly eth_graph.substitute_group(5, molecule, MinimumDistanceNN, graph_dict=graph_dict) eth_mg.substitute_group(5, molgraph, MinimumDistanceNN) - self.assertEqual(eth_graph.graph.get_edge_data(5, 6)[0]["weight"], 1.0) - self.assertEqual(eth_mg, eth_graph) + assert eth_graph.graph.get_edge_data(5, 6)[0]["weight"] == 1.0 + assert eth_mg == eth_graph def test_replace(self): eth_copy_sub = copy.deepcopy(self.ethylene) @@ -953,35 +932,32 @@ def test_replace(self): eth_copy_sub.substitute_group(5, "methyl", MinimumDistanceNN) eth_copy_repl.replace_group(5, "methyl", MinimumDistanceNN) # Test that replacement on a terminal atom is equivalent to substitution - self.assertEqual(eth_copy_repl.molecule, eth_copy_sub.molecule) - self.assertEqual(eth_copy_repl, eth_copy_sub) + assert eth_copy_repl.molecule == eth_copy_sub.molecule + assert eth_copy_repl == eth_copy_sub # Methyl carbon should have coordination 4 - self.assertEqual(eth_copy_repl.get_coordination_of_site(5), 4) + assert eth_copy_repl.get_coordination_of_site(5) == 4 # Now swap one functional group for another eth_copy_repl.replace_group(5, "amine", MinimumDistanceNN) - self.assertEqual( - ["C", "C", "H", "H", "H", "N", "H", "H"], - [str(s) for s in eth_copy_repl.molecule.species], - ) - self.assertEqual(len(eth_copy_repl.graph.nodes), 8) + assert ["C", "C", "H", "H", "H", "N", "H", "H"] == [str(s) for s in eth_copy_repl.molecule.species] + assert len(eth_copy_repl.graph.nodes) == 8 # Amine nitrogen should have coordination 3 - self.assertEqual(eth_copy_repl.get_coordination_of_site(5), 3) + assert eth_copy_repl.get_coordination_of_site(5) == 3 def test_as_from_dict(self): d = self.cyclohexene.as_dict() mg = MoleculeGraph.from_dict(d) d2 = mg.as_dict() - self.assertEqual(str(d), str(d2)) + assert str(d) == str(d2) def test_sort(self): sg = copy.deepcopy(self.ethylene) # insert an unsorted edge, don't use sg.add_edge as it auto-sorts - self.assertEqual(list(sg.graph.edges), [(0, 1, 0), (0, 2, 0), (0, 3, 0), (1, 4, 0), (1, 5, 0)]) + assert list(sg.graph.edges) == [(0, 1, 0), (0, 2, 0), (0, 3, 0), (1, 4, 0), (1, 5, 0)] sg.sort() - self.assertEqual(list(sg.graph.edges), [(4, 5, 0), (0, 4, 0), (1, 4, 0), (2, 5, 0), (3, 5, 0)]) + assert list(sg.graph.edges) == [(4, 5, 0), (0, 4, 0), (1, 4, 0), (2, 5, 0), (3, 5, 0)] if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_hhi.py b/pymatgen/analysis/tests/test_hhi.py index 47629f3435d..4f9877c7371 100644 --- a/pymatgen/analysis/tests/test_hhi.py +++ b/pymatgen/analysis/tests/test_hhi.py @@ -5,23 +5,25 @@ import unittest +from pytest import approx + from pymatgen.analysis.hhi import HHIModel class HHIModelTest(unittest.TestCase): def test_hhi(self): hhi = HHIModel() - self.assertEqual(hhi.get_hhi("He"), (3200, 3900)) - self.assertEqual(hhi.get_hhi_production("He"), 3200) - self.assertEqual(hhi.get_hhi_reserve("He"), 3900) + assert hhi.get_hhi("He") == (3200, 3900) + assert hhi.get_hhi_production("He") == 3200 + assert hhi.get_hhi_reserve("He") == 3900 - self.assertAlmostEqual(hhi.get_hhi_production("Li2O"), 1614.96, 1) - self.assertAlmostEqual(hhi.get_hhi_reserve("Li2O"), 2218.90, 1) + assert hhi.get_hhi_production("Li2O") == approx(1614.96, 1) + assert hhi.get_hhi_reserve("Li2O") == approx(2218.90, 1) - self.assertEqual(hhi.get_hhi_designation(1400), "low") - self.assertEqual(hhi.get_hhi_designation(1800), "medium") - self.assertEqual(hhi.get_hhi_designation(3000), "high") - self.assertEqual(hhi.get_hhi_designation(None), None) + assert hhi.get_hhi_designation(1400) == "low" + assert hhi.get_hhi_designation(1800) == "medium" + assert hhi.get_hhi_designation(3000) == "high" + assert hhi.get_hhi_designation(None) is None if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_interface_reactions.py b/pymatgen/analysis/tests/test_interface_reactions.py index 17e116f557d..300ae335086 100644 --- a/pymatgen/analysis/tests/test_interface_reactions.py +++ b/pymatgen/analysis/tests/test_interface_reactions.py @@ -4,6 +4,7 @@ import warnings import numpy as np +import pytest from matplotlib.figure import Figure as mpl_figure from pandas import DataFrame from plotly.graph_objects import Figure as plotly_figure @@ -144,13 +145,13 @@ def setUp(self): norm=True, use_hull_energy=False, ) - with self.assertRaises(Exception) as context1: + with pytest.raises(Exception) as context1: _ = InterfacialReactivity(Composition("Li2O2"), Composition("Li"), pd=self.gpd, norm=True) - self.assertTrue( + assert ( "Please use the GrandPotentialInterfacialReactivity " "class for interfacial reactions with open elements!" == str(context1.exception) ) - with self.assertRaises(Exception) as context2: + with pytest.raises(Exception) as context2: _ = GrandPotentialInterfacialReactivity( Composition("O2"), Composition("Mn"), @@ -159,9 +160,7 @@ def setUp(self): norm=False, include_no_mixing_energy=True, ) - self.assertTrue( - "Please provide non-grand phase diagram to compute no_mixing_energy!" == str(context2.exception) - ) + assert "Please provide non-grand phase diagram to compute no_mixing_energy!" == str(context2.exception) self.ir = [ir_0, ir_1, ir_2, ir_3, ir_4, ir_5, ir_6, ir_7, ir_8, ir_9, ir_10, ir_11, ir_12] @@ -170,71 +169,58 @@ def test_get_entry_energy(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") energy = InterfacialReactivity._get_entry_energy(self.pd, comp) - self.assertTrue(len(w) == 1) - self.assertTrue( + assert len(w) == 1 + assert ( "The reactant MnO3 has no matching entry with" " negative formation energy, instead convex " "hull energy for this composition will be used" " for reaction energy calculation." in str(w[-1].message) ) test1 = np.isclose(energy, -30, atol=1e-03) - self.assertTrue( - test1, - f"_get_entry_energy: energy for {comp.reduced_formula} is wrong!", - ) + assert test1, f"_get_entry_energy: energy for {comp.reduced_formula} is wrong!" # Test normal functionality comp = Composition("MnO2") test2 = np.isclose(InterfacialReactivity._get_entry_energy(self.pd, comp), -30, atol=1e-03) - self.assertTrue( - test2, - f"_get_entry_energy: energy for {comp.reduced_formula} is wrong!", - ) + assert test2, f"_get_entry_energy: energy for {comp.reduced_formula} is wrong!" def test_get_grand_potential(self): comp = Composition("LiMnO2") # Test non-normalized case test1 = np.isclose(self.ir[1]._get_grand_potential(comp), -27, atol=1e-03) - self.assertTrue(test1, "_get_grand_potential: Non-normalized case gets error!") + assert test1, "_get_grand_potential: Non-normalized case gets error!" # Test normalized case test2 = np.isclose(self.ir[2]._get_grand_potential(comp), -9, atol=1e-03) - self.assertTrue(test2, "_get_grand_potential: Normalized case gets error!") + assert test2, "_get_grand_potential: Normalized case gets error!" comp2 = Composition("Li2O2") # Test use_hull_energy option. test3 = np.isclose(self.ir[8]._get_grand_potential(comp2), -4, atol=1e-03) - self.assertTrue(test3, "_get_grand_potential: get hull energy gets error!") + assert test3, "_get_grand_potential: get hull energy gets error!" test4 = np.isclose(self.ir[9]._get_grand_potential(comp2), -2, atol=1e-03) - self.assertTrue( - test4, - f"_get_grand_potential: gets error for {comp2.reduced_formula}!", - ) + assert test4, f"_get_grand_potential: gets error for {comp2.reduced_formula}!" def test_get_energy(self): test1 = np.isclose(self.ir[0]._get_energy(0.5), -15, atol=1e-03) - self.assertTrue(test1, "_get_energy: phase diagram gets error!") + assert test1, "_get_energy: phase diagram gets error!" test2 = np.isclose(self.ir[3]._get_energy(0.6666666), -7.333333, atol=1e-03) - self.assertTrue(test2, "_get_energy: grand canonical phase diagram gets error!") + assert test2, "_get_energy: grand canonical phase diagram gets error!" test3 = np.isclose(self.ir[6]._get_energy(0.3333333), -3.333333, atol=1e-03) - self.assertTrue(test3, "_get_energy: convex hull energy gets error. ") + assert test3, "_get_energy: convex hull energy gets error. " test4 = np.isclose(self.ir[7]._get_energy(0.3333333), -4, atol=1e-03) - self.assertTrue(test4, "_get_energy: gets error. ") + assert test4, "_get_energy: gets error. " def test_get_reaction(self): - self.assertEqual( - str(self.ir[0]._get_reaction(0.5)), - "0.5 Mn + 0.5 O2 -> 0.5 MnO2", - "_get_reaction: reaction not involving chempots species gets error!", - ) - self.assertEqual( - str(self.ir[3]._get_reaction(0.666666)), - "0.5 Li2O + 0.5 Mn -> Li + 0.25 MnO2 + 0.25 Mn", - "_get_reaction: reaction involving chempots species gets error!", - ) + assert ( + str(self.ir[0]._get_reaction(0.5)) == "0.5 Mn + 0.5 O2 -> 0.5 MnO2" + ), "_get_reaction: reaction not involving chempots species gets error!" + assert ( + str(self.ir[3]._get_reaction(0.666666)) == "0.5 Li2O + 0.5 Mn -> Li + 0.25 MnO2 + 0.25 Mn" + ), "_get_reaction: reaction involving chempots species gets error!" def test_get_get_elmt_amt_in_rxt(self): rxt1 = Reaction( @@ -242,52 +228,40 @@ def test_get_get_elmt_amt_in_rxt(self): [Composition("LiMnO2")], ) test1 = np.isclose(self.ir[2]._get_elmt_amt_in_rxn(rxt1), 3) - self.assertTrue(test1, "_get_get_elmt_amt_in_rxt: gpd elements amounts gets error!") + assert test1, "_get_get_elmt_amt_in_rxt: gpd elements amounts gets error!" rxt2 = rxt1 rxt2.normalize_to(Composition("Li"), 0.5) test2 = np.isclose(self.ir[2]._get_elmt_amt_in_rxn(rxt2), 1.5) - self.assertTrue(test2, "_get_get_elmt_amt_in_rxt: gpd elements amounts gets error!") + assert test2, "_get_get_elmt_amt_in_rxt: gpd elements amounts gets error!" rxt3 = Reaction([Composition("O2"), Composition("Li")], [Composition("Li2O")]) # Li is not counted test3 = np.isclose(self.ir[2]._get_elmt_amt_in_rxn(rxt3), 1) - self.assertTrue(test3, "_get_get_elmt_amt_in_rxt: gpd elements amounts gets error!") + assert test3, "_get_get_elmt_amt_in_rxt: gpd elements amounts gets error!" # Li is counted test4 = np.isclose(self.ir[6]._get_elmt_amt_in_rxn(rxt3), 3) - self.assertTrue(test4, "_get_get_elmt_amt_in_rxt: pd elements amounts gets error!") + assert test4, "_get_get_elmt_amt_in_rxt: pd elements amounts gets error!" def test_convert(self): test_array = [(0.5, 1, 3), (0.4, 2, 3), (0, 1, 9), (1, 2, 7)] result = [InterfacialReactivity._convert(x, f1, f2) for x, f1, f2 in test_array] answer = [0.75, 0.5, 0, 1] - self.assertTrue( - np.allclose(result, answer), - f"_convert: conversion gets error! {answer} expected, but gets {result}", - ) + assert np.allclose(result, answer), f"_convert: conversion gets error! {answer} expected, but gets {result}" def test_reverse_convert(self): test_array = [(0.5, 1, 3), (0.4, 2, 3), (0, 1, 9), (1, 2, 7)] result = [InterfacialReactivity._reverse_convert(x, f1, f2) for x, f1, f2 in test_array] answer = [0.25, 0.3076923, 0, 1] - self.assertTrue( - np.allclose(result, answer), - f"_convert: conversion gets error! {answer} expected, but gets {result}", - ) + assert np.allclose(result, answer), f"_convert: conversion gets error! {answer} expected, but gets {result}" def test_products_property(self): test1 = sorted(self.ir[0].products) == sorted(["MnO2", "O2", "Mn"]) - self.assertTrue( - test1, - "decomposition products gets error for reaction not involving chempots species!", - ) + assert test1, "decomposition products gets error for reaction not involving chempots species!" test2 = sorted(self.ir[3].products) == sorted(["Li", "MnO2", "Mn", "Li2O"]) - self.assertTrue( - test2, - "decomposition products gets error for reaction involving chempots species!", - ) + assert test2, "decomposition products gets error for reaction involving chempots species!" def test_get_kinks(self): def test_get_kinks_helper( @@ -306,25 +280,24 @@ def test_get_kinks_helper( energy_per_rxt_kink = [i[4] for i in kinks] test1 = index == index_expect - self.assertTrue(test1, "get_kinks:index gets error!") + assert test1, "get_kinks:index gets error!" test2 = np.allclose(x_kink, x_kink_expect) - self.assertTrue(test2, "get_kinks:x kinks gets error!") + assert test2, "get_kinks:x kinks gets error!" test3 = np.allclose(energy_kink, energy_kink_expect) - self.assertTrue(test3, "get_kinks:energy kinks gets error!") + assert test3, "get_kinks:energy kinks gets error!" # Testing reaction strings are hard, # as species could be arranged in random order. test4 = len(react_kink) == len(react_kink_expect) - self.assertTrue( - test4, + assert test4, ( f"get_kinks: reaction kinks gets error for {ir.c1_original.reduced_formula} and " - f"{ir.c2_original.reduced_formula} reaction!", + f"{ir.c2_original.reduced_formula} reaction!" ) test5 = np.allclose(energy_per_rxt_kink, energy_per_rxt_kink_expect) - self.assertTrue(test5, "get_kinks: energy_per_rxt_kinks gets error!") + assert test5, "get_kinks: energy_per_rxt_kinks gets error!" test_get_kinks_helper( self.ir[0], @@ -378,7 +351,7 @@ def test_convexity_helper(ir): hull = ConvexHull(points) test2 = len(hull.vertices) == len(points) - self.assertTrue(test1 and test2, "Error: Generating non-convex plot!") + assert test1 and test2, "Error: Generating non-convex plot!" for ir in self.ir: test_convexity_helper(ir) @@ -387,30 +360,24 @@ def test_get_original_composition_ratio(self): # expected reaction1: 0.5 O2 + 0.5 Mn -> 0.5 MnO2 reaction1 = self.ir[0]._get_reaction(0.5) test1 = np.isclose(self.ir[0]._get_original_composition_ratio(reaction1), 0.5) - self.assertTrue( - test1, - "_get_original_composition_ratio: reaction not involving chempots species gets error!", - ) + assert test1, "_get_original_composition_ratio: reaction not involving chempots species gets error!" # expected reaction2: 0.5 Mn + 0.5 Li2O -> Li + 0.25 MnO2 + 0.25 Mn reaction2 = self.ir[3]._get_reaction(0.666666) test2 = np.isclose(self.ir[3]._get_original_composition_ratio(reaction2), 0.5) - self.assertTrue( - test2, - "_get_original_composition_ratio: reaction involving chempots species gets error!", - ) + assert test2, "_get_original_composition_ratio: reaction involving chempots species gets error!" def test_get_critical_original_kink_ratio(self): test1 = np.allclose(self.ir[0].get_critical_original_kink_ratio(), [0, 0.5, 1]) - self.assertTrue(test1, "get_critical_original_kink_ratio: gets error!") + assert test1, "get_critical_original_kink_ratio: gets error!" test2 = np.allclose(self.ir[10].get_critical_original_kink_ratio(), [0, 0.5, 1]) - self.assertTrue(test2, "get_critical_original_kink_ratio: gets error!") + assert test2, "get_critical_original_kink_ratio: gets error!" test3 = np.allclose(self.ir[11].get_critical_original_kink_ratio(), [0, 1]) - self.assertTrue(test3, "get_critical_original_kink_ratio: gets error!") + assert test3, "get_critical_original_kink_ratio: gets error!" test4 = np.allclose(self.ir[2].get_critical_original_kink_ratio(), [0, 0.5, 1]) - self.assertTrue(test4, "get_critical_original_kink_ratio: gets error!") + assert test4, "get_critical_original_kink_ratio: gets error!" test5 = np.allclose(self.ir[3].get_critical_original_kink_ratio(), [0, 0.66666, 1]) - self.assertTrue(test5, "get_critical_original_kink_ratio: gets error!") + assert test5, "get_critical_original_kink_ratio: gets error!" def test_labels(self): d_pymg = self.ir[0].labels @@ -420,25 +387,23 @@ def test_labels(self): 3: "x= 1.0 energy in eV/atom = 0.0 O2 -> O2", } - self.assertEqual( - d_pymg, - d_test, + assert d_pymg == d_test, ( "labels:label does not match for interfacial system " - f"with {self.ir[0].c1_original.reduced_formula} and {self.ir[0].c2_original.reduced_formula}.", + f"with {self.ir[0].c1_original.reduced_formula} and {self.ir[0].c2_original.reduced_formula}." ) def test_plot(self): for i in self.ir: fig = i.plot(backend="matplotlib") - self.assertTrue(fig, isinstance(fig, mpl_figure)) + assert fig, isinstance(fig, mpl_figure) fig = i.plot(backend="plotly") - self.assertTrue(isinstance(fig, plotly_figure)) + assert isinstance(fig, plotly_figure) def test_get_dataframe(self): for i in self.ir: df = i.get_dataframe() - self.assertTrue(isinstance(df, DataFrame)) + assert isinstance(df, DataFrame) def test_minimum(self): answer = [ @@ -452,10 +417,9 @@ def test_minimum(self): (0.3333333, -4.0), ] for i, j in zip(self.ir, answer): - self.assertTrue( - np.allclose(i.minimum, j), + assert np.allclose(i.minimum, j), ( f"minimum: the system with {i.c1_original.reduced_formula} and {i.c2_original.reduced_formula} " - f"gets error!{j} expected, but gets {i.minimum}", + f"gets error!{j} expected, but gets {i.minimum}" ) def test_get_no_mixing_energy(self): @@ -476,15 +440,13 @@ def energy_lst(lst): result_info = [i.get_no_mixing_energy() for i in self.ir if i.grand] print(result_info) for i, j in zip(result_info, answer): - self.assertTrue( - name_lst(i) == name_lst(j), - f"get_no_mixing_energy: names get error, {name_lst(j)} expected but gets {name_lst(i)}", - ) - self.assertTrue( - np.allclose(energy_lst(i), energy_lst(j)), + assert name_lst(i) == name_lst( + j + ), f"get_no_mixing_energy: names get error, {name_lst(j)} expected but gets {name_lst(i)}" + assert np.allclose(energy_lst(i), energy_lst(j)), ( "get_no_mixing_energy: " "no_mixing energies get error, " - f"{energy_lst(j)} expected but gets {energy_lst(i)}", + f"{energy_lst(j)} expected but gets {energy_lst(i)}" ) def test_get_chempot_correction(self): @@ -495,56 +457,48 @@ def test_get_chempot_correction(self): # test pressure effect. actual = InterfacialReactivity.get_chempot_correction("O", 298.15, 100e5) expect = 0.05916 - self.assertTrue( - np.isclose(actual, expect, atol=1e-2), - f"get_chempot_correction gets error, {expect} expected but gets {actual}", - ) + assert np.isclose( + actual, expect, atol=1e-2 + ), f"get_chempot_correction gets error, {expect} expected but gets {actual}" # test temperature effect. actual_2 = InterfacialReactivity.get_chempot_correction("O", 1000, 1e5) expect_2 = -0.82352 - self.assertTrue( - np.isclose(actual_2, expect_2, atol=1e-2), - f"get_chempot_correction gets error, {expect_2} expected but gets {actual_2}", - ) + assert np.isclose( + actual_2, expect_2, atol=1e-2 + ), f"get_chempot_correction gets error, {expect_2} expected but gets {actual_2}" actual_3 = InterfacialReactivity.get_chempot_correction("O", 500, 1e5) expect_3 = -0.223 - self.assertTrue( - np.isclose(actual_3, expect_3, atol=1e-2), - f"get_chempot_correction gets error, {expect_3} expected but gets {actual_3}", - ) + assert np.isclose( + actual_3, expect_3, atol=1e-2 + ), f"get_chempot_correction gets error, {expect_3} expected but gets {actual_3}" # test mixed effect. actual_4 = InterfacialReactivity.get_chempot_correction("O", 1000, 1e-25) expect_4 = -3.800 - self.assertTrue( - np.isclose(actual_4, expect_4, atol=1e-2), - f"get_chempot_correction gets error, {expect_4} expected but gets {actual_4}", - ) + assert np.isclose( + actual_4, expect_4, atol=1e-2 + ), f"get_chempot_correction gets error, {expect_4} expected but gets {actual_4}" actual_5 = InterfacialReactivity.get_chempot_correction("O", 1250, 1e-25) expect_5 = -4.86 - self.assertTrue( - np.isclose(actual_5, expect_5, atol=1e-2), - f"get_chempot_correction gets error, {expect_5} expected but gets {actual_5}", - ) + assert np.isclose( + actual_5, expect_5, atol=1e-2 + ), f"get_chempot_correction gets error, {expect_5} expected but gets {actual_5}" actual_6 = InterfacialReactivity.get_chempot_correction("O", 1500, 1e-25) expect_6 = -5.928 - self.assertTrue( - np.isclose(actual_6, expect_6, atol=1e-2), - f"get_chempot_correction gets error, {expect_6} expected but gets {actual_6}", - ) + assert np.isclose( + actual_6, expect_6, atol=1e-2 + ), f"get_chempot_correction gets error, {expect_6} expected but gets {actual_6}" actual_7 = InterfacialReactivity.get_chempot_correction("O", 1000, 1e-15) expect_7 = -2.808 - self.assertTrue( - np.isclose(actual_7, expect_7, atol=1e-2), - f"get_chempot_correction gets error, {expect_7} expected but gets {actual_7}", - ) + assert np.isclose( + actual_7, expect_7, atol=1e-2 + ), f"get_chempot_correction gets error, {expect_7} expected but gets {actual_7}" # test non-gas phase. actual_8 = InterfacialReactivity.get_chempot_correction("Li", 1000, 1e15) expect_8 = 0 - self.assertTrue( - np.isclose(actual_8, expect_8, atol=1e-5), - f"get_chempot_correction gets error, {expect_8} expected but gets {actual_8}", - ) + assert np.isclose( + actual_8, expect_8, atol=1e-5 + ), f"get_chempot_correction gets error, {expect_8} expected but gets {actual_8}" if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_local_env.py b/pymatgen/analysis/tests/test_local_env.py index 28dcaba1365..e6719d0bc25 100644 --- a/pymatgen/analysis/tests/test_local_env.py +++ b/pymatgen/analysis/tests/test_local_env.py @@ -11,6 +11,7 @@ import numpy as np import pytest +from pytest import approx from pymatgen.analysis.graphs import MoleculeGraph, StructureGraph from pymatgen.analysis.local_env import ( @@ -68,12 +69,12 @@ def setUp(self): def test_valences_ionic_structure(self): valence_dict = self._mgo_valrad_evaluator.valences for val in list(valence_dict.values()): - self.assertTrue(val in {2, -2}) + assert val in {2, -2} def test_radii_ionic_structure(self): radii_dict = self._mgo_valrad_evaluator.radii for rad in list(radii_dict.values()): - self.assertTrue(rad in {0.86, 1.26}) + assert rad in {0.86, 1.26} def tearDown(self): del self._mgo_uc @@ -89,17 +90,17 @@ def setUp(self): self.nn_sic = VoronoiNN() def test_get_voronoi_polyhedra(self): - self.assertEqual(len(self.nn.get_voronoi_polyhedra(self.s, 0).items()), 8) + assert len(self.nn.get_voronoi_polyhedra(self.s, 0).items()) == 8 def test_get_cn(self): site_0_coord_num = self.nn.get_cn(self.s, 0, use_weights=True, on_disorder="take_max_species") - self.assertAlmostEqual(site_0_coord_num, 5.809265748999465, 7) + assert site_0_coord_num == approx(5.809265748999465, 7) site_0_coord_num = self.nn_sic.get_cn(self.s_sic, 0, use_weights=True, on_disorder="take_max_species") - self.assertAlmostEqual(site_0_coord_num, 4.5381161643940668, 7) + assert site_0_coord_num == approx(4.5381161643940668, 7) def test_get_coordinated_sites(self): - self.assertEqual(len(self.nn.get_nn(self.s, 0)), 8) + assert len(self.nn.get_nn(self.s, 0)) == 8 def test_volume(self): self.nn.targets = None @@ -107,7 +108,7 @@ def test_volume(self): for n in range(len(self.s)): for nn in self.nn.get_voronoi_polyhedra(self.s, n).values(): volume += nn["volume"] - self.assertAlmostEqual(self.s.volume, volume) + assert self.s.volume == approx(volume) def test_solid_angle(self): self.nn.targets = None @@ -115,8 +116,8 @@ def test_solid_angle(self): angle = 0 for nn in self.nn.get_voronoi_polyhedra(self.s, n).values(): angle += nn["solid_angle"] - self.assertAlmostEqual(4 * np.pi, angle) - self.assertEqual(solid_angle([0, 0, 0], [[1, 0, 0], [-1, 0, 0], [0, 1, 0]]), pi) + assert 4 * np.pi == approx(angle) + assert solid_angle([0, 0, 0], [[1, 0, 0], [-1, 0, 0], [0, 1, 0]]) == pi def test_nn_shell(self): # First, make a SC lattice. Make my math easier @@ -125,11 +126,11 @@ def test_nn_shell(self): # Get the 1NN shell self.nn.targets = None nns = self.nn.get_nn_shell_info(s, 0, 1) - self.assertEqual(6, len(nns)) + assert 6 == len(nns) # Test the 2nd NN shell nns = self.nn.get_nn_shell_info(s, 0, 2) - self.assertEqual(18, len(nns)) + assert 18 == len(nns) self.assertArrayAlmostEqual([1] * 6, [x["weight"] for x in nns if max(np.abs(x["image"])) == 2]) self.assertArrayAlmostEqual([2] * 12, [x["weight"] for x in nns if max(np.abs(x["image"])) == 1]) @@ -151,19 +152,15 @@ def test_nn_shell(self): ) self.nn.weight = "area" nns = self.nn.get_nn_shell_info(cscl, 0, 1) - self.assertEqual(14, len(nns)) - self.assertEqual(6, np.isclose([x["weight"] for x in nns], 0.125 / 0.32476).sum()) # Square faces - self.assertEqual(8, np.isclose([x["weight"] for x in nns], 1).sum()) + assert 14 == len(nns) + assert 6 == np.isclose([x["weight"] for x in nns], 0.125 / 0.32476).sum() # Square faces + assert 8 == np.isclose([x["weight"] for x in nns], 1).sum() nns = self.nn.get_nn_shell_info(cscl, 0, 2) # Weight of getting back on to own site # Square-square hop: 6*5 options times (0.125/0.32476)^2 weight each # Hex-hex hop: 8*7 options times 1 weight each - self.assertAlmostEqual( - 60.4444, - np.sum([x["weight"] for x in nns if x["site_index"] == 0]), - places=3, - ) + assert 60.4444 == approx(np.sum([x["weight"] for x in nns if x["site_index"] == 0]), 3) def test_adj_neighbors(self): # Make a simple cubic structure @@ -175,10 +172,10 @@ def test_adj_neighbors(self): # Each neighbor has 4 adjacent neighbors, all orthogonal for nn_info in neighbors.values(): - self.assertEqual(4, len(nn_info["adj_neighbors"])) + assert 4 == len(nn_info["adj_neighbors"]) for adj_key in nn_info["adj_neighbors"]: - self.assertEqual(0, np.dot(nn_info["normal"], neighbors[adj_key]["normal"])) + assert 0 == np.dot(nn_info["normal"], neighbors[adj_key]["normal"]) def test_all_at_once(self): # Get all of the sites for LiFePO4 @@ -222,7 +219,7 @@ def test_Cs2O(self): # Compute the voronoi tessellation result = VoronoiNN().get_all_voronoi_polyhedra(strc) - self.assertEqual(3, len(result)) + assert 3 == len(result) def test_filtered(self): nn = VoronoiNN(weight="area") @@ -243,22 +240,18 @@ def test_filtered(self): # Run one test where you get the small neighbors nn.tol = little_weight * 0.99 nns = nn.get_nn_info(bcc, 0) - self.assertEqual(14, len(nns)) + assert 14 == len(nns) # Run a second test where we screen out little faces nn.tol = little_weight * 1.01 nns = nn.get_nn_info(bcc, 0) - self.assertEqual(8, len(nns)) + assert 8 == len(nns) # Make sure it works for the `get_all` operation all_nns = nn.get_all_nn_info(bcc * [2, 2, 2]) - self.assertEqual( - [ - 8, - ] - * 16, - [len(x) for x in all_nns], - ) + assert [ + 8, + ] * 16 == [len(x) for x in all_nns] def tearDown(self): del self.s @@ -278,21 +271,21 @@ def test_get_nn(self): for site_idx, site in enumerate(s): if site.specie == Element("Li"): - self.assertEqual(self.jmol.get_cn(s, site_idx), 0) + assert self.jmol.get_cn(s, site_idx) == 0 nsites_checked += 1 elif site.specie == Element("Fe"): - self.assertEqual(self.jmol.get_cn(s, site_idx), 6) + assert self.jmol.get_cn(s, site_idx) == 6 nsites_checked += 1 elif site.specie == Element("P"): - self.assertEqual(self.jmol.get_cn(s, site_idx), 4) + assert self.jmol.get_cn(s, site_idx) == 4 nsites_checked += 1 - self.assertEqual(nsites_checked, 12) + assert nsites_checked == 12 # Test a user override that would cause Li to show up as 2-coordinated - self.assertEqual(self.jmol_update.get_cn(s, 0), 2) + assert self.jmol_update.get_cn(s, 0) == 2 # Verify get_nn function works - self.assertEqual(len(self.jmol_update.get_nn(s, 0)), 2) + assert len(self.jmol_update.get_nn(s, 0)) == 2 def tearDown(self): del self.jmol @@ -304,10 +297,10 @@ def test_get_nn(self): inn = IsayevNN() s = self.get_structure("LiFePO4") - self.assertEqual(inn.get_cn(s, 0), 2) - self.assertEqual(inn.get_cn(s, 5), 6) - self.assertEqual(inn.get_cn(s, 10), 4) - self.assertEqual(len(inn.get_nn(s, 0)), 2) + assert inn.get_cn(s, 0) == 2 + assert inn.get_cn(s, 5) == 6 + assert inn.get_cn(s, 10) == 4 + assert len(inn.get_nn(s, 0)) == 2 class OpenBabelNNTest(PymatgenTest): @@ -319,17 +312,14 @@ def setUp(self): def test_nn_orders(self): strategy = OpenBabelNN() acetylene = strategy.get_nn_info(self.acetylene, 0) - self.assertEqual(acetylene[0]["weight"], 3) - self.assertEqual(acetylene[1]["weight"], 1) + assert acetylene[0]["weight"] == 3 + assert acetylene[1]["weight"] == 1 # Currently, benzene bonds register either as double or single, # not aromatic # Instead of searching for aromatic bonds, we check that bonds are # detected in the same way from both sides - self.assertEqual( - strategy.get_nn_info(self.benzene, 0)[0]["weight"], - strategy.get_nn_info(self.benzene, 1)[0]["weight"], - ) + assert strategy.get_nn_info(self.benzene, 0)[0]["weight"] == strategy.get_nn_info(self.benzene, 1)[0]["weight"] def test_nn_length(self): strategy = OpenBabelNN(order=False) @@ -339,10 +329,10 @@ def test_nn_length(self): c_bonds = [b for b in benzene_bonds if str(b["site"].specie) == "C"] h_bonds = [b for b in benzene_bonds if str(b["site"].specie) == "H"] - self.assertAlmostEqual(c_bonds[0]["weight"], 1.41, 2) - self.assertAlmostEqual(h_bonds[0]["weight"], 1.02, 2) + assert c_bonds[0]["weight"] == approx(1.41, 2) + assert h_bonds[0]["weight"] == approx(1.02, 2) - self.assertAlmostEqual(strategy.get_nn_info(self.acetylene, 0)[0]["weight"], 1.19, 2) + assert strategy.get_nn_info(self.acetylene, 0)[0]["weight"] == approx(1.19, 2) def tearDown(self): del self.benzene @@ -358,11 +348,11 @@ def test_nn_orders(self): strategy = CovalentBondNN() acetylene = strategy.get_nn_info(self.acetylene, 0) - self.assertEqual(acetylene[0]["weight"], 3) - self.assertEqual(acetylene[1]["weight"], 1) + assert acetylene[0]["weight"] == 3 + assert acetylene[1]["weight"] == 1 benzene = strategy.get_nn_info(self.benzene, 0) - self.assertAlmostEqual(benzene[0]["weight"], 1.6596, places=4) + assert benzene[0]["weight"] == approx(1.6596, 4) def test_nn_length(self): strategy = CovalentBondNN(order=False) @@ -372,20 +362,20 @@ def test_nn_length(self): c_bonds = [b for b in benzene_bonds if str(b["site"].specie) == "C"] h_bonds = [b for b in benzene_bonds if str(b["site"].specie) == "H"] - self.assertAlmostEqual(c_bonds[0]["weight"], 1.41, 2) - self.assertAlmostEqual(h_bonds[0]["weight"], 1.02, 2) + assert c_bonds[0]["weight"] == approx(1.41, 2) + assert h_bonds[0]["weight"] == approx(1.02, 2) acetylene = strategy.get_nn_info(self.acetylene, 0) - self.assertAlmostEqual(acetylene[0]["weight"], 1.19, places=2) + assert acetylene[0]["weight"] == approx(1.19, 2) def test_bonded_structure(self): strategy = CovalentBondNN() benzene = strategy.get_bonded_structure(self.benzene) - self.assertEqual(len(benzene.find_rings()), 1) + assert len(benzene.find_rings()) == 1 acetylene = strategy.get_bonded_structure(self.acetylene) - self.assertEqual(len(acetylene.graph.nodes), 4) + assert len(acetylene.graph.nodes) == 4 def tearDown(self): del self.benzene @@ -431,71 +421,71 @@ def setUp(self): self.lifepo4.add_oxidation_state_by_guess() def test_all_nn_classes(self): - self.assertEqual(MinimumDistanceNN(cutoff=5, get_all_sites=True).get_cn(self.cscl, 0), 14) - self.assertEqual(MinimumDistanceNN().get_cn(self.diamond, 0), 4) - self.assertEqual(MinimumDistanceNN().get_cn(self.nacl, 0), 6) - self.assertEqual(MinimumDistanceNN().get_cn(self.lifepo4, 0), 6) - self.assertEqual(MinimumDistanceNN(tol=0.01).get_cn(self.cscl, 0), 8) - self.assertEqual(MinimumDistanceNN(tol=0.1).get_cn(self.mos2, 0), 6) + assert MinimumDistanceNN(cutoff=5, get_all_sites=True).get_cn(self.cscl, 0) == 14 + assert MinimumDistanceNN().get_cn(self.diamond, 0) == 4 + assert MinimumDistanceNN().get_cn(self.nacl, 0) == 6 + assert MinimumDistanceNN().get_cn(self.lifepo4, 0) == 6 + assert MinimumDistanceNN(tol=0.01).get_cn(self.cscl, 0) == 8 + assert MinimumDistanceNN(tol=0.1).get_cn(self.mos2, 0) == 6 for image in MinimumDistanceNN(tol=0.1).get_nn_images(self.mos2, 0): - self.assertTrue(image in [(0, 0, 0), (0, 1, 0), (-1, 0, 0), (0, 0, 0), (0, 1, 0), (-1, 0, 0)]) + assert image in [(0, 0, 0), (0, 1, 0), (-1, 0, 0), (0, 0, 0), (0, 1, 0), (-1, 0, 0)] okeeffe = MinimumOKeeffeNN(tol=0.01) - self.assertEqual(okeeffe.get_cn(self.diamond, 0), 4) - self.assertEqual(okeeffe.get_cn(self.nacl, 0), 6) - self.assertEqual(okeeffe.get_cn(self.cscl, 0), 8) - self.assertEqual(okeeffe.get_cn(self.lifepo4, 0), 2) + assert okeeffe.get_cn(self.diamond, 0) == 4 + assert okeeffe.get_cn(self.nacl, 0) == 6 + assert okeeffe.get_cn(self.cscl, 0) == 8 + assert okeeffe.get_cn(self.lifepo4, 0) == 2 virenn = MinimumVIRENN(tol=0.01) - self.assertEqual(virenn.get_cn(self.diamond, 0), 4) - self.assertEqual(virenn.get_cn(self.nacl, 0), 6) - self.assertEqual(virenn.get_cn(self.cscl, 0), 8) - self.assertEqual(virenn.get_cn(self.lifepo4, 0), 2) + assert virenn.get_cn(self.diamond, 0) == 4 + assert virenn.get_cn(self.nacl, 0) == 6 + assert virenn.get_cn(self.cscl, 0) == 8 + assert virenn.get_cn(self.lifepo4, 0) == 2 brunner_recip = BrunnerNN_reciprocal(tol=0.01) - self.assertEqual(brunner_recip.get_cn(self.diamond, 0), 4) - self.assertEqual(brunner_recip.get_cn(self.nacl, 0), 6) - self.assertEqual(brunner_recip.get_cn(self.cscl, 0), 14) - self.assertEqual(brunner_recip.get_cn(self.lifepo4, 0), 6) + assert brunner_recip.get_cn(self.diamond, 0) == 4 + assert brunner_recip.get_cn(self.nacl, 0) == 6 + assert brunner_recip.get_cn(self.cscl, 0) == 14 + assert brunner_recip.get_cn(self.lifepo4, 0) == 6 brunner_rel = BrunnerNN_relative(tol=0.01) - self.assertEqual(brunner_rel.get_cn(self.diamond, 0), 4) - self.assertEqual(brunner_rel.get_cn(self.nacl, 0), 6) - self.assertEqual(brunner_rel.get_cn(self.cscl, 0), 14) - self.assertEqual(brunner_rel.get_cn(self.lifepo4, 0), 6) + assert brunner_rel.get_cn(self.diamond, 0) == 4 + assert brunner_rel.get_cn(self.nacl, 0) == 6 + assert brunner_rel.get_cn(self.cscl, 0) == 14 + assert brunner_rel.get_cn(self.lifepo4, 0) == 6 brunner_real = BrunnerNN_real(tol=0.01) - self.assertEqual(brunner_real.get_cn(self.diamond, 0), 4) - self.assertEqual(brunner_real.get_cn(self.nacl, 0), 6) - self.assertEqual(brunner_real.get_cn(self.cscl, 0), 14) - self.assertEqual(brunner_real.get_cn(self.lifepo4, 0), 30) + assert brunner_real.get_cn(self.diamond, 0) == 4 + assert brunner_real.get_cn(self.nacl, 0) == 6 + assert brunner_real.get_cn(self.cscl, 0) == 14 + assert brunner_real.get_cn(self.lifepo4, 0) == 30 econn = EconNN() - self.assertEqual(econn.get_cn(self.diamond, 0), 4) - self.assertEqual(econn.get_cn(self.nacl, 0), 6) - self.assertEqual(econn.get_cn(self.cscl, 0), 14) - self.assertEqual(econn.get_cn(self.lifepo4, 0), 6) + assert econn.get_cn(self.diamond, 0) == 4 + assert econn.get_cn(self.nacl, 0) == 6 + assert econn.get_cn(self.cscl, 0) == 14 + assert econn.get_cn(self.lifepo4, 0) == 6 voroinn = VoronoiNN(tol=0.5) - self.assertEqual(voroinn.get_cn(self.diamond, 0), 4) - self.assertEqual(voroinn.get_cn(self.nacl, 0), 6) - self.assertEqual(voroinn.get_cn(self.cscl, 0), 8) - self.assertEqual(voroinn.get_cn(self.lifepo4, 0), 6) + assert voroinn.get_cn(self.diamond, 0) == 4 + assert voroinn.get_cn(self.nacl, 0) == 6 + assert voroinn.get_cn(self.cscl, 0) == 8 + assert voroinn.get_cn(self.lifepo4, 0) == 6 crystalnn = CrystalNN() - self.assertEqual(crystalnn.get_cn(self.diamond, 0), 4) - self.assertEqual(crystalnn.get_cn(self.nacl, 0), 6) - self.assertEqual(crystalnn.get_cn(self.cscl, 0), 8) - self.assertEqual(crystalnn.get_cn(self.lifepo4, 0), 6) + assert crystalnn.get_cn(self.diamond, 0) == 4 + assert crystalnn.get_cn(self.nacl, 0) == 6 + assert crystalnn.get_cn(self.cscl, 0) == 8 + assert crystalnn.get_cn(self.lifepo4, 0) == 6 def test_get_local_order_params(self): nn = MinimumDistanceNN() ops = nn.get_local_order_parameters(self.diamond, 0) - self.assertAlmostEqual(ops["tetrahedral"], 0.9999934389036574) + assert ops["tetrahedral"] == approx(0.9999934389036574) ops = nn.get_local_order_parameters(self.nacl, 0) - self.assertAlmostEqual(ops["octahedral"], 0.9999995266669) + assert ops["octahedral"] == approx(0.9999995266669) class MotifIdentificationTest(PymatgenTest): @@ -573,36 +563,27 @@ def setUp(self): def test_site_is_of_motif_type(self): for i in range(self.diamond.num_sites): - self.assertEqual(site_is_of_motif_type(self.diamond, i), "tetrahedral") + assert site_is_of_motif_type(self.diamond, i) == "tetrahedral" for i in range(self.nacl.num_sites): - self.assertEqual(site_is_of_motif_type(self.nacl, i), "octahedral") + assert site_is_of_motif_type(self.nacl, i) == "octahedral" for i in range(self.cscl.num_sites): - self.assertEqual(site_is_of_motif_type(self.cscl, i), "bcc") - self.assertEqual(site_is_of_motif_type(self.square_pyramid, 0), "square pyramidal") + assert site_is_of_motif_type(self.cscl, i) == "bcc" + assert site_is_of_motif_type(self.square_pyramid, 0) == "square pyramidal" for i in range(1, self.square_pyramid.num_sites): - self.assertEqual(site_is_of_motif_type(self.square_pyramid, i), "unrecognized") - self.assertEqual(site_is_of_motif_type(self.trigonal_bipyramid, 0), "trigonal bipyramidal") + assert site_is_of_motif_type(self.square_pyramid, i) == "unrecognized" + assert site_is_of_motif_type(self.trigonal_bipyramid, 0) == "trigonal bipyramidal" for i in range(1, self.trigonal_bipyramid.num_sites): - self.assertEqual(site_is_of_motif_type(self.trigonal_bipyramid, i), "unrecognized") + assert site_is_of_motif_type(self.trigonal_bipyramid, i) == "unrecognized" def test_get_neighbors_of_site_with_index(self): - self.assertEqual(len(get_neighbors_of_site_with_index(self.diamond, 0)), 4) - self.assertEqual(len(get_neighbors_of_site_with_index(self.nacl, 0)), 6) - self.assertEqual(len(get_neighbors_of_site_with_index(self.cscl, 0)), 8) - self.assertEqual(len(get_neighbors_of_site_with_index(self.diamond, 0, delta=0.01)), 4) - self.assertEqual(len(get_neighbors_of_site_with_index(self.diamond, 0, cutoff=6)), 4) - self.assertEqual( - len(get_neighbors_of_site_with_index(self.diamond, 0, approach="voronoi")), - 4, - ) - self.assertEqual( - len(get_neighbors_of_site_with_index(self.diamond, 0, approach="min_OKeeffe")), - 4, - ) - self.assertEqual( - len(get_neighbors_of_site_with_index(self.diamond, 0, approach="min_VIRE")), - 4, - ) + assert len(get_neighbors_of_site_with_index(self.diamond, 0)) == 4 + assert len(get_neighbors_of_site_with_index(self.nacl, 0)) == 6 + assert len(get_neighbors_of_site_with_index(self.cscl, 0)) == 8 + assert len(get_neighbors_of_site_with_index(self.diamond, 0, delta=0.01)) == 4 + assert len(get_neighbors_of_site_with_index(self.diamond, 0, cutoff=6)) == 4 + assert len(get_neighbors_of_site_with_index(self.diamond, 0, approach="voronoi")) == 4 + assert len(get_neighbors_of_site_with_index(self.diamond, 0, approach="min_OKeeffe")) == 4 + assert len(get_neighbors_of_site_with_index(self.diamond, 0, approach="min_VIRE")) == 4 def tearDown(self): del self.silicon @@ -634,8 +615,8 @@ def set_nn_info(self): # Critic2NN has external dependency, is tested separately if "Critic2" not in str(subclass): nn_info = subclass().get_nn_info(self.diamond, 0) - self.assertEqual(nn_info[0]["site_index"], 1) - self.assertEqual(nn_info[0]["image"][0], 1) + assert nn_info[0]["site_index"] == 1 + assert nn_info[0]["image"][0] == 1 def test_on_disorder_options(self): assert get_args(on_disorder_options) == ( @@ -1013,13 +994,13 @@ def setUp(self): ) def test_init(self): - self.assertIsNotNone(LocalStructOrderParams(["cn"], parameters=None, cutoff=0.99)) + assert LocalStructOrderParams(["cn"], parameters=None, cutoff=0.99) is not None parameters = [{"norm": 2}] lostops = LocalStructOrderParams(["cn"], parameters=parameters) tmp = lostops.get_parameters(0) parameters[0]["norm"] = 3 - self.assertEqual(tmp, lostops.get_parameters(0)) + assert tmp == lostops.get_parameters(0) def test_get_order_parameters(self): # Set up everything. @@ -1079,169 +1060,169 @@ def test_get_order_parameters(self): # Single bond. op_vals = ops_101.get_order_parameters(self.single_bond, 0) - self.assertAlmostEqual(int(op_vals[13] * 1000), 1000) + assert int(op_vals[13] * 1000) == approx(1000) op_vals = ops_501.get_order_parameters(self.single_bond, 0) - self.assertAlmostEqual(int(op_vals[13] * 1000), 799) + assert int(op_vals[13] * 1000) == approx(799) op_vals = ops_101.get_order_parameters(self.linear, 0) - self.assertAlmostEqual(int(op_vals[13] * 1000), 0) + assert int(op_vals[13] * 1000) == approx(0) # Linear motif. op_vals = ops_101.get_order_parameters(self.linear, 0) - self.assertAlmostEqual(int(op_vals[1] * 1000), 1000) + assert int(op_vals[1] * 1000) == approx(1000) # 45 degrees-bent motif. op_vals = ops_101.get_order_parameters(self.bent45, 0) - self.assertAlmostEqual(int(op_vals[2] * 1000), 1000) + assert int(op_vals[2] * 1000) == approx(1000) # T-shape motif. op_vals = ops_101.get_order_parameters(self.T_shape, 0, indices_neighs=[1, 2, 3]) - self.assertAlmostEqual(int(op_vals[23] * 1000), 1000) + assert int(op_vals[23] * 1000) == approx(1000) # Cubic structure. op_vals = ops_099.get_order_parameters(self.cubic, 0) - self.assertAlmostEqual(op_vals[0], 0.0) - self.assertIsNone(op_vals[3]) - self.assertIsNone(op_vals[4]) - self.assertIsNone(op_vals[5]) - self.assertIsNone(op_vals[6]) - self.assertIsNone(op_vals[7]) - self.assertIsNone(op_vals[8]) + assert op_vals[0] == approx(0.0) + assert op_vals[3] is None + assert op_vals[4] is None + assert op_vals[5] is None + assert op_vals[6] is None + assert op_vals[7] is None + assert op_vals[8] is None op_vals = ops_101.get_order_parameters(self.cubic, 0) - self.assertAlmostEqual(op_vals[0], 6.0) - self.assertAlmostEqual(int(op_vals[3] * 1000), 23) - self.assertAlmostEqual(int(op_vals[4] * 1000), 1000) - self.assertAlmostEqual(int(op_vals[5] * 1000), 333) - self.assertAlmostEqual(int(op_vals[6] * 1000), 0) - self.assertAlmostEqual(int(op_vals[7] * 1000), 763) - self.assertAlmostEqual(int(op_vals[8] * 1000), 353) - self.assertAlmostEqual(int(op_vals[28] * 1000), 1000) + assert op_vals[0] == approx(6.0) + assert int(op_vals[3] * 1000) == approx(23) + assert int(op_vals[4] * 1000) == approx(1000) + assert int(op_vals[5] * 1000) == approx(333) + assert int(op_vals[6] * 1000) == approx(0) + assert int(op_vals[7] * 1000) == approx(763) + assert int(op_vals[8] * 1000) == approx(353) + assert int(op_vals[28] * 1000) == approx(1000) # Bcc structure. op_vals = ops_087.get_order_parameters(self.bcc, 0) - self.assertAlmostEqual(op_vals[0], 8.0) - self.assertAlmostEqual(int(op_vals[3] * 1000), 200) - self.assertAlmostEqual(int(op_vals[4] * 1000), 145) - self.assertAlmostEqual(int(op_vals[5] * 1000 + 0.5), 1000) - self.assertAlmostEqual(int(op_vals[6] * 1000), 0) - self.assertAlmostEqual(int(op_vals[7] * 1000), 509) - self.assertAlmostEqual(int(op_vals[8] * 1000), 628) + assert op_vals[0] == approx(8.0) + assert int(op_vals[3] * 1000) == approx(200) + assert int(op_vals[4] * 1000) == approx(145) + assert int(op_vals[5] * 1000 + 0.5) == approx(1000) + assert int(op_vals[6] * 1000) == approx(0) + assert int(op_vals[7] * 1000) == approx(509) + assert int(op_vals[8] * 1000) == approx(628) # Fcc structure. op_vals = ops_071.get_order_parameters(self.fcc, 0) - self.assertAlmostEqual(op_vals[0], 12.0) - self.assertAlmostEqual(int(op_vals[3] * 1000), 36) - self.assertAlmostEqual(int(op_vals[4] * 1000), 78) - self.assertAlmostEqual(int(op_vals[5] * 1000), -2) - self.assertAlmostEqual(int(op_vals[6] * 1000), 0) - self.assertAlmostEqual(int(op_vals[7] * 1000), 190) - self.assertAlmostEqual(int(op_vals[8] * 1000), 574) + assert op_vals[0] == approx(12.0) + assert int(op_vals[3] * 1000) == approx(36) + assert int(op_vals[4] * 1000) == approx(78) + assert int(op_vals[5] * 1000) == approx(-2) + assert int(op_vals[6] * 1000) == approx(0) + assert int(op_vals[7] * 1000) == approx(190) + assert int(op_vals[8] * 1000) == approx(574) # Hcp structure. op_vals = ops_101.get_order_parameters(self.hcp, 0) - self.assertAlmostEqual(op_vals[0], 12.0) - self.assertAlmostEqual(int(op_vals[3] * 1000), 33) - self.assertAlmostEqual(int(op_vals[4] * 1000), 82) + assert op_vals[0] == approx(12.0) + assert int(op_vals[3] * 1000) == approx(33) + assert int(op_vals[4] * 1000) == approx(82) # self.assertAlmostEqual(int(op_vals[5] * 1000), -26) - self.assertAlmostEqual(int(op_vals[6] * 1000), 0) - self.assertAlmostEqual(int(op_vals[7] * 1000), 97) - self.assertAlmostEqual(int(op_vals[8] * 1000), 484) + assert int(op_vals[6] * 1000) == approx(0) + assert int(op_vals[7] * 1000) == approx(97) + assert int(op_vals[8] * 1000) == approx(484) # Diamond structure. op_vals = ops_044.get_order_parameters(self.diamond, 0) - self.assertAlmostEqual(op_vals[0], 4.0) - self.assertAlmostEqual(int(op_vals[3] * 1000), 1000) - self.assertAlmostEqual(int(op_vals[4] * 1000), 37) - self.assertAlmostEqual(op_vals[5], 0.75) - self.assertAlmostEqual(int(op_vals[6] * 1000), 0) - self.assertAlmostEqual(int(op_vals[7] * 1000), 509) - self.assertAlmostEqual(int(op_vals[8] * 1000), 628) - self.assertAlmostEqual(int(op_vals[27] * 1000), 1000) + assert op_vals[0] == approx(4.0) + assert int(op_vals[3] * 1000) == approx(1000) + assert int(op_vals[4] * 1000) == approx(37) + assert op_vals[5] == approx(0.75) + assert int(op_vals[6] * 1000) == approx(0) + assert int(op_vals[7] * 1000) == approx(509) + assert int(op_vals[8] * 1000) == approx(628) + assert int(op_vals[27] * 1000) == approx(1000) # Trigonal off-plane molecule. op_vals = ops_044.get_order_parameters(self.trigonal_off_plane, 0) - self.assertAlmostEqual(op_vals[0], 3.0) - self.assertAlmostEqual(int(op_vals[3] * 1000), 1000) - self.assertAlmostEqual(int(op_vals[33] * 1000), 1000) + assert op_vals[0] == approx(3.0) + assert int(op_vals[3] * 1000) == approx(1000) + assert int(op_vals[33] * 1000) == approx(1000) # Trigonal-planar motif. op_vals = ops_101.get_order_parameters(self.trigonal_planar, 0) - self.assertEqual(int(op_vals[0] + 0.5), 3) - self.assertAlmostEqual(int(op_vals[14] * 1000 + 0.5), 1000) - self.assertAlmostEqual(int(op_vals[29] * 1000 + 0.5), 1000) + assert int(op_vals[0] + 0.5) == 3 + assert int(op_vals[14] * 1000 + 0.5) == approx(1000) + assert int(op_vals[29] * 1000 + 0.5) == approx(1000) # Regular triangle motif. op_vals = ops_101.get_order_parameters(self.regular_triangle, 0) - self.assertAlmostEqual(int(op_vals[9] * 1000), 999) + assert int(op_vals[9] * 1000) == approx(999) # Square-planar motif. op_vals = ops_101.get_order_parameters(self.square_planar, 0) - self.assertAlmostEqual(int(op_vals[15] * 1000 + 0.5), 1000) - self.assertAlmostEqual(int(op_vals[30] * 1000 + 0.5), 1000) + assert int(op_vals[15] * 1000 + 0.5) == approx(1000) + assert int(op_vals[30] * 1000 + 0.5) == approx(1000) # Square motif. op_vals = ops_101.get_order_parameters(self.square, 0) - self.assertAlmostEqual(int(op_vals[10] * 1000), 1000) + assert int(op_vals[10] * 1000) == approx(1000) # Pentagonal planar. op_vals = ops_101.get_order_parameters(self.pentagonal_planar.sites, 0, indices_neighs=[1, 2, 3, 4, 5]) - self.assertAlmostEqual(int(op_vals[12] * 1000 + 0.5), 126) - self.assertAlmostEqual(int(op_vals[16] * 1000 + 0.5), 1000) - self.assertAlmostEqual(int(op_vals[31] * 1000 + 0.5), 1000) + assert int(op_vals[12] * 1000 + 0.5) == approx(126) + assert int(op_vals[16] * 1000 + 0.5) == approx(1000) + assert int(op_vals[31] * 1000 + 0.5) == approx(1000) # Trigonal pyramid motif. op_vals = ops_101.get_order_parameters(self.trigonal_pyramid, 0, indices_neighs=[1, 2, 3, 4]) - self.assertAlmostEqual(int(op_vals[18] * 1000 + 0.5), 1000) + assert int(op_vals[18] * 1000 + 0.5) == approx(1000) # Square pyramid motif. op_vals = ops_101.get_order_parameters(self.square_pyramid, 0) - self.assertAlmostEqual(int(op_vals[11] * 1000 + 0.5), 1000) - self.assertAlmostEqual(int(op_vals[12] * 1000 + 0.5), 667) - self.assertAlmostEqual(int(op_vals[17] * 1000 + 0.5), 1000) + assert int(op_vals[11] * 1000 + 0.5) == approx(1000) + assert int(op_vals[12] * 1000 + 0.5) == approx(667) + assert int(op_vals[17] * 1000 + 0.5) == approx(1000) # Pentagonal pyramid motif. op_vals = ops_101.get_order_parameters(self.pentagonal_pyramid, 0, indices_neighs=[1, 2, 3, 4, 5, 6]) - self.assertAlmostEqual(int(op_vals[19] * 1000 + 0.5), 1000) + assert int(op_vals[19] * 1000 + 0.5) == approx(1000) # Hexagonal pyramid motif. op_vals = ops_101.get_order_parameters(self.hexagonal_pyramid, 0, indices_neighs=[1, 2, 3, 4, 5, 6, 7]) - self.assertAlmostEqual(int(op_vals[20] * 1000 + 0.5), 1000) + assert int(op_vals[20] * 1000 + 0.5) == approx(1000) # Trigonal bipyramidal. op_vals = ops_101.get_order_parameters(self.trigonal_bipyramidal.sites, 0, indices_neighs=[1, 2, 3, 4, 5]) - self.assertAlmostEqual(int(op_vals[12] * 1000 + 0.5), 1000) + assert int(op_vals[12] * 1000 + 0.5) == approx(1000) # Pentagonal bipyramidal. op_vals = ops_101.get_order_parameters(self.pentagonal_bipyramid.sites, 0, indices_neighs=[1, 2, 3, 4, 5, 6, 7]) - self.assertAlmostEqual(int(op_vals[21] * 1000 + 0.5), 1000) + assert int(op_vals[21] * 1000 + 0.5) == approx(1000) # Hexagonal bipyramid motif. op_vals = ops_101.get_order_parameters(self.hexagonal_bipyramid, 0, indices_neighs=[1, 2, 3, 4, 5, 6, 7, 8]) - self.assertAlmostEqual(int(op_vals[22] * 1000 + 0.5), 1000) + assert int(op_vals[22] * 1000 + 0.5) == approx(1000) # Cuboctahedral motif. op_vals = ops_101.get_order_parameters(self.cuboctahedron, 0, indices_neighs=[i for i in range(1, 13)]) - self.assertAlmostEqual(int(op_vals[24] * 1000 + 0.5), 1000) - self.assertAlmostEqual(int(op_vals[32] * 1000 + 0.5), 1000) + assert int(op_vals[24] * 1000 + 0.5) == approx(1000) + assert int(op_vals[32] * 1000 + 0.5) == approx(1000) # See-saw motif. op_vals = ops_101.get_order_parameters(self.see_saw_rect, 0, indices_neighs=[i for i in range(1, 5)]) - self.assertAlmostEqual(int(op_vals[25] * 1000 + 0.5), 1000) + assert int(op_vals[25] * 1000 + 0.5) == approx(1000) # Hexagonal planar motif. op_vals = ops_101.get_order_parameters(self.hexagonal_planar, 0, indices_neighs=[1, 2, 3, 4, 5, 6]) - self.assertAlmostEqual(int(op_vals[26] * 1000 + 0.5), 1000) + assert int(op_vals[26] * 1000 + 0.5) == approx(1000) # Square face capped trigonal prism. op_vals = ops_101.get_order_parameters( self.sq_face_capped_trig_pris, 0, indices_neighs=[i for i in range(1, 8)] ) - self.assertAlmostEqual(int(op_vals[34] * 1000 + 0.5), 1000) + assert int(op_vals[34] * 1000 + 0.5) == approx(1000) # Test providing explicit neighbor lists. op_vals = ops_101.get_order_parameters(self.bcc, 0, indices_neighs=[1]) - self.assertIsNotNone(op_vals[0]) - self.assertIsNone(op_vals[3]) - with self.assertRaises(ValueError): + assert op_vals[0] is not None + assert op_vals[3] is None + with pytest.raises(ValueError): ops_101.get_order_parameters(self.bcc, 0, indices_neighs=[2]) def tearDown(self): @@ -1288,11 +1269,11 @@ def tearDown(self): warnings.filters = self.prev_warnings def test_sanity(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): cnn = CrystalNN() cnn.get_cn(self.lifepo4, 0, use_weights=True) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): cnn = CrystalNN(weighted_cn=True) cnn.get_cn(self.lifepo4, 0, use_weights=False) @@ -1303,7 +1284,7 @@ def test_discrete_cn(self): for idx, _ in enumerate(self.lifepo4): cn_array.append(cnn.get_cn(self.lifepo4, idx)) - self.assertSequenceEqual(cn_array, expected_array) + assert cn_array == expected_array def test_weighted_cn(self): cnn = CrystalNN(weighted_cn=True) @@ -1341,24 +1322,24 @@ def test_weighted_cn_no_oxid(self): def test_fixed_length(self): cnn = CrystalNN(fingerprint_length=30) nndata = cnn.get_nn_data(self.lifepo4, 0) - self.assertEqual(len(nndata.cn_weights), 30) - self.assertEqual(len(nndata.cn_nninfo), 30) + assert len(nndata.cn_weights) == 30 + assert len(nndata.cn_nninfo) == 30 def test_cation_anion(self): cnn = CrystalNN(weighted_cn=True, cation_anion=True) - self.assertAlmostEqual(cnn.get_cn(self.lifepo4, 0, use_weights=True), 5.8630, 2) + assert cnn.get_cn(self.lifepo4, 0, use_weights=True) == approx(5.8630, 2) def test_x_diff_weight(self): cnn = CrystalNN(weighted_cn=True, x_diff_weight=0) - self.assertAlmostEqual(cnn.get_cn(self.lifepo4, 0, use_weights=True), 5.8630, 2) + assert cnn.get_cn(self.lifepo4, 0, use_weights=True) == approx(5.8630, 2) def test_noble_gas_material(self): cnn = CrystalNN() - self.assertEqual(cnn.get_cn(self.he_bcc, 0, use_weights=False), 0) + assert cnn.get_cn(self.he_bcc, 0, use_weights=False) == 0 cnn = CrystalNN(distance_cutoffs=(1.25, 5)) - self.assertEqual(cnn.get_cn(self.he_bcc, 0, use_weights=False), 8) + assert cnn.get_cn(self.he_bcc, 0, use_weights=False) == 8 def test_shifted_sites(self): cnn = CrystalNN() @@ -1371,10 +1352,7 @@ def test_shifted_sites(self): struct_shifted = Structure([7, 0, 0, 0, 7, 0, 0, 0, 7], ["I"] * len(sites_shifted), sites_shifted) bonded_struct_shifted = cnn.get_bonded_structure(struct_shifted) - self.assertEqual( - len(bonded_struct.get_connected_sites(0)), - len(bonded_struct_shifted.get_connected_sites(0)), - ) + assert len(bonded_struct.get_connected_sites(0)) == len(bonded_struct_shifted.get_connected_sites(0)) def test_get_cn(self): cnn = CrystalNN() @@ -1386,8 +1364,10 @@ def test_get_cn(self): assert site_0_coord_num == 8 assert site_0_coord_num == site_0_coord_num_strict_majority - self.assertRaises(ValueError, cnn.get_cn, self.disordered_struct, 0, on_disorder="take_majority_strict") - self.assertRaises(ValueError, cnn.get_cn, self.disordered_struct, 0, on_disorder="error") + with pytest.raises(ValueError): + cnn.get_cn(self.disordered_struct, 0, on_disorder="take_majority_strict") + with pytest.raises(ValueError): + cnn.get_cn(self.disordered_struct, 0, on_disorder="error") def test_get_bonded_structure(self): cnn = CrystalNN() @@ -1404,12 +1384,13 @@ def test_get_bonded_structure(self): assert len(structure_graph) == 2 assert structure_graph == structure_graph_strict_majority == structure_graph_drop_majority - self.assertRaises( - ValueError, cnn.get_bonded_structure, self.disordered_struct, 0, on_disorder="take_majority_strict" - ) - self.assertRaises(ValueError, cnn.get_bonded_structure, self.disordered_struct, 0, on_disorder="error") + with pytest.raises(ValueError): + cnn.get_bonded_structure(self.disordered_struct, 0, on_disorder="take_majority_strict") + with pytest.raises(ValueError): + cnn.get_bonded_structure(self.disordered_struct, 0, on_disorder="error") - self.assertRaises(ValueError, cnn.get_bonded_structure, self.disordered_struct, 0, on_disorder="error") + with pytest.raises(ValueError): + cnn.get_bonded_structure(self.disordered_struct, 0, on_disorder="error") class CutOffDictNNTest(PymatgenTest): @@ -1428,17 +1409,18 @@ def tearDown(self): def test_cn(self): nn = CutOffDictNN({("C", "C"): 2}) - self.assertEqual(nn.get_cn(self.diamond, 0), 4) + assert nn.get_cn(self.diamond, 0) == 4 nn_null = CutOffDictNN() - self.assertEqual(nn_null.get_cn(self.diamond, 0), 0) + assert nn_null.get_cn(self.diamond, 0) == 0 def test_from_preset(self): nn = CutOffDictNN.from_preset("vesta_2019") - self.assertEqual(nn.get_cn(self.diamond, 0), 4) + assert nn.get_cn(self.diamond, 0) == 4 # test error thrown on unknown preset - self.assertRaises(ValueError, CutOffDictNN.from_preset, "test") + with pytest.raises(ValueError): + CutOffDictNN.from_preset("test") @unittest.skipIf(not which("critic2"), "critic2 executable not present") @@ -1497,45 +1479,45 @@ def setUp(self): self.water_cluster_Mg = MoleculeGraph.with_empty_graph(charged_Mg_cluster) def test_metal_edge_extender(self): - self.assertEqual(len(self.LiEC_graph.graph.edges), 11) + assert len(self.LiEC_graph.graph.edges) == 11 extended_mol_graph = metal_edge_extender(self.LiEC_graph) - self.assertEqual(len(extended_mol_graph.graph.edges), 12) + assert len(extended_mol_graph.graph.edges) == 12 def test_custom_metals(self): extended_mol_graph = metal_edge_extender(self.LiEC_graph, metals={"K"}) - self.assertEqual(len(extended_mol_graph.graph.edges), 11) + assert len(extended_mol_graph.graph.edges) == 11 # empty metals should exit cleanly with no change to graph mol_graph = metal_edge_extender(self.water_cluster_K, metals={}, cutoff=2.5) - self.assertEqual(len(mol_graph.graph.edges), 0) + assert len(mol_graph.graph.edges) == 0 mol_graph = metal_edge_extender(self.water_cluster_K, metals={"K"}, cutoff=2.5) - self.assertEqual(len(mol_graph.graph.edges), 4) + assert len(mol_graph.graph.edges) == 4 extended_graph = metal_edge_extender(self.water_cluster_K, metals={"K"}, cutoff=4.5) - self.assertEqual(len(extended_graph.graph.edges), 7) + assert len(extended_graph.graph.edges) == 7 # if None, should auto-detect Li extended_mol_graph = metal_edge_extender(self.LiEC_graph, metals=None) - self.assertEqual(len(extended_mol_graph.graph.edges), 12) + assert len(extended_mol_graph.graph.edges) == 12 def test_custom_coordinators(self): # leave out Oxygen, graph should not change extended_mol_graph = metal_edge_extender(self.LiEC_graph, coordinators={"N", "F", "S", "Cl"}) - self.assertEqual(len(extended_mol_graph.graph.edges), 11) + assert len(extended_mol_graph.graph.edges) == 11 # empty coordinators should exit cleanly with no change extended_mol_graph = metal_edge_extender(self.LiEC_graph, coordinators={}) - self.assertEqual(len(extended_mol_graph.graph.edges), 11) + assert len(extended_mol_graph.graph.edges) == 11 def test_custom_cutoff(self): short_mol_graph = metal_edge_extender(self.LiEC_graph, cutoff=0.5) - self.assertEqual(len(short_mol_graph.graph.edges), 11) + assert len(short_mol_graph.graph.edges) == 11 # with a cutoff of 1.5, no edges should be found. # test that the 2nd pass analysis (auto increasing cutoff to 2.5) picks # up the six coordination bonds short_mol_graph = metal_edge_extender(self.water_cluster_Mg, cutoff=1.5) - self.assertEqual(len(short_mol_graph.graph.edges), 6) + assert len(short_mol_graph.graph.edges) == 6 if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_molecule_matcher.py b/pymatgen/analysis/tests/test_molecule_matcher.py index 39cc0032006..9ce556f7180 100644 --- a/pymatgen/analysis/tests/test_molecule_matcher.py +++ b/pymatgen/analysis/tests/test_molecule_matcher.py @@ -7,28 +7,28 @@ import unittest import numpy as np - -try: - import openbabel as ob - - from pymatgen.analysis.molecule_matcher import ( - InchiMolAtomMapper, - IsomorphismMolAtomMapper, - MoleculeMatcher, - ) -except (ImportError, RuntimeError): - ob = None +import pytest +from pytest import approx from pymatgen.analysis.molecule_matcher import ( BruteForceOrderMatcher, GeneticOrderMatcher, HungarianOrderMatcher, + InchiMolAtomMapper, + IsomorphismMolAtomMapper, KabschMatcher, + MoleculeMatcher, ) from pymatgen.core.operations import SymmOp from pymatgen.core.structure import Lattice, Molecule, Structure from pymatgen.util.testing import PymatgenTest +try: + import openbabel as ob + +except (ImportError, RuntimeError): + ob = None + test_dir = os.path.join(PymatgenTest.TEST_FILES_DIR, "molecules", "molecule_matcher") @@ -164,7 +164,7 @@ def test_get_rmsd(self): mm = MoleculeMatcher() mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "t4.xyz")) - self.assertEqual(f"{mm.get_rmsd(mol1, mol2):7.3}", "0.00488") + assert f"{mm.get_rmsd(mol1, mol2):7.3}" == "0.00488" def test_group_molecules(self): mm = MoleculeMatcher(tolerance=0.001) @@ -175,18 +175,18 @@ def test_group_molecules(self): filename_groups = [[filename_list[mol_list.index(m)] for m in g] for g in mol_groups] with open(os.path.join(test_dir, "grouped_mol_list.txt")) as f: grouped_text = f.read().strip() - self.assertEqual(str(filename_groups), grouped_text) + assert str(filename_groups) == grouped_text def test_to_and_from_dict(self): mm = MoleculeMatcher(tolerance=0.5, mapper=InchiMolAtomMapper(angle_tolerance=50.0)) d = mm.as_dict() mm2 = MoleculeMatcher.from_dict(d) - self.assertEqual(d, mm2.as_dict()) + assert d == mm2.as_dict() mm = MoleculeMatcher(tolerance=0.5, mapper=IsomorphismMolAtomMapper()) d = mm.as_dict() mm2 = MoleculeMatcher.from_dict(d) - self.assertEqual(d, mm2.as_dict()) + assert d == mm2.as_dict() def fit_with_mapper(self, mapper): coords = [ @@ -201,72 +201,72 @@ def fit_with_mapper(self, mapper): rotcoords = [op.operate(c) for c in coords] mol2 = Molecule(["C", "H", "H", "H", "H"], rotcoords) mm = MoleculeMatcher(mapper=mapper) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "benzene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "benzene2.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "benzene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "t2.xyz")) - self.assertFalse(mm.fit(mol1, mol2)) + assert not mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "c1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "c2.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "t4.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "j1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "j2.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "ethene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "ethene2.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "toluene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "toluene2.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "cyclohexane1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "cyclohexane2.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "oxygen1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "oxygen2.xyz")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) mm = MoleculeMatcher(tolerance=0.001, mapper=mapper) mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "t4.xyz")) - self.assertFalse(mm.fit(mol1, mol2)) + assert not mm.fit(mol1, mol2) def test_strange_inchi(self): mm = MoleculeMatcher(tolerance=0.05, mapper=InchiMolAtomMapper()) mol1 = Molecule.from_file(os.path.join(test_dir, "k1.sdf")) mol2 = Molecule.from_file(os.path.join(test_dir, "k2.sdf")) - self.assertTrue(mm.fit(mol1, mol2)) + assert mm.fit(mol1, mol2) def test_thiane(self): mm = MoleculeMatcher(tolerance=0.05, mapper=InchiMolAtomMapper()) mol1 = Molecule.from_file(os.path.join(test_dir, "thiane1.sdf")) mol2 = Molecule.from_file(os.path.join(test_dir, "thiane2.sdf")) - self.assertFalse(mm.fit(mol1, mol2)) + assert not mm.fit(mol1, mol2) def test_thiane_ethynyl(self): mm = MoleculeMatcher(tolerance=0.05, mapper=InchiMolAtomMapper()) mol1 = Molecule.from_file(os.path.join(test_dir, "thiane_ethynyl1.sdf")) mol2 = Molecule.from_file(os.path.join(test_dir, "thiane_ethynyl2.sdf")) - self.assertFalse(mm.fit(mol1, mol2)) + assert not mm.fit(mol1, mol2) def test_cdi_23(self): mm = MoleculeMatcher(tolerance=0.05, mapper=InchiMolAtomMapper()) mol1 = Molecule.from_file(os.path.join(test_dir, "cdi_23_1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "cdi_23_2.xyz")) - self.assertFalse(mm.fit(mol1, mol2)) + assert not mm.fit(mol1, mol2) class KabschMatcherTest(unittest.TestCase): @@ -277,7 +277,7 @@ def test_get_rmsd(self): mm = KabschMatcher(mol1) _, _, rmsd = mm.match(mol2) - self.assertAlmostEqual(rmsd, 0.0028172956033732936, places=6) + assert rmsd == approx(0.0028172956033732936, 6) def test_to_and_from_dict(self): mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) @@ -286,7 +286,7 @@ def test_to_and_from_dict(self): d_source = mm_source.as_dict() mm_target = KabschMatcher.from_dict(d_source) - self.assertDictEqual(d_source, mm_target.as_dict()) + assert d_source == mm_target.as_dict() def test_rotated_molecule(self): @@ -306,7 +306,7 @@ def test_rotated_molecule(self): mm = KabschMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_mismatched_atom_composition(self): @@ -315,7 +315,7 @@ def test_mismatched_atom_composition(self): mm = KabschMatcher(mol1) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = mm.fit(mol2) @@ -326,7 +326,7 @@ def test_missmatched_atom_order(self): mm = KabschMatcher(mol1) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = mm.fit(mol2) mol1 = Molecule.from_file(os.path.join(test_dir, "c1.xyz")) @@ -334,7 +334,7 @@ def test_missmatched_atom_order(self): mm = KabschMatcher(mol1) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = mm.fit(mol2) def test_fit(self): @@ -344,14 +344,14 @@ def test_fit(self): mm = KabschMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0028172956033732936, places=6) + assert rmsd == approx(0.0028172956033732936, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "oxygen1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "oxygen2.xyz")) mm = KabschMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) class HungarianOrderMatcherTest(unittest.TestCase): @@ -362,7 +362,7 @@ def test_get_rmsd(self): mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.002825344731118855, places=6) + assert rmsd == approx(0.002825344731118855, 6) def test_to_and_from_dict(self): mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) @@ -371,7 +371,7 @@ def test_to_and_from_dict(self): d_source = mm_source.as_dict() mm_target = HungarianOrderMatcher.from_dict(d_source) - self.assertDictEqual(d_source, mm_target.as_dict()) + assert d_source == mm_target.as_dict() def test_rotated_molecule(self): @@ -391,7 +391,7 @@ def test_rotated_molecule(self): mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_mismatched_atom_composition(self): @@ -399,7 +399,7 @@ def test_mismatched_atom_composition(self): mol2 = Molecule.from_file(os.path.join(test_dir, "t2.xyz")) mm = HungarianOrderMatcher(mol1) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = mm.fit(mol2) def test_fit(self): @@ -410,56 +410,56 @@ def test_fit(self): mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 1.4171601659148593e-05, places=6) + assert rmsd == approx(1.4171601659148593e-05, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "c1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "c2.xyz")) mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 9.479012116064961e-05, places=6) + assert rmsd == approx(9.479012116064961e-05, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "t4.xyz")) mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.002825344731118855, places=6) + assert rmsd == approx(0.002825344731118855, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "j1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "j2.xyz")) mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 9.28245597473488e-05, places=6) + assert rmsd == approx(9.28245597473488e-05, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "ethene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "ethene2.xyz")) mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.00021150729609276233, places=6) + assert rmsd == approx(0.00021150729609276233, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "toluene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "toluene2.xyz")) mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0001445787263551832, places=6) + assert rmsd == approx(0.0001445787263551832, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "cyclohexane1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "cyclohexane2.xyz")) mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.00012447269440740117, places=6) + assert rmsd == approx(0.00012447269440740117, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "oxygen1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "oxygen2.xyz")) mm = HungarianOrderMatcher(mol1) _, rmsd = mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) class GeneticOrderMatcherTest(unittest.TestCase): @@ -470,7 +470,7 @@ def test_get_rmsd(self): mm = GeneticOrderMatcher(mol1, threshold=0.3) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 0.0028172956033734615, places=6) + assert rmsd == approx(0.0028172956033734615, 6) def test_to_and_from_dict(self): mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) @@ -479,7 +479,7 @@ def test_to_and_from_dict(self): d_source = mm_source.as_dict() mm_target = GeneticOrderMatcher.from_dict(d_source) - self.assertDictEqual(d_source, mm_target.as_dict()) + assert d_source == mm_target.as_dict() def test_rotated_molecule(self): @@ -499,7 +499,7 @@ def test_rotated_molecule(self): mm = GeneticOrderMatcher(mol1, threshold=0.3) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_mismatched_atom_composition(self): @@ -507,7 +507,7 @@ def test_mismatched_atom_composition(self): mol2 = Molecule.from_file(os.path.join(test_dir, "t2.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.3) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = mm.fit(mol2)[0] def test_fit(self): @@ -518,56 +518,56 @@ def test_fit(self): mm = GeneticOrderMatcher(mol1, threshold=0.01) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 7.061017534055039e-05, places=6) + assert rmsd == approx(7.061017534055039e-05, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "c1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "c2.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.01) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 9.459575146593829e-05, places=6) + assert rmsd == approx(9.459575146593829e-05, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "t3.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "t4.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.01) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 0.0028172956033734615, places=6) + assert rmsd == approx(0.0028172956033734615, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "j1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "j2.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.01) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 9.28245597473488e-05, places=6) + assert rmsd == approx(9.28245597473488e-05, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "ethene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "ethene2.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.01) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 0.00019757961816426042, places=6) + assert rmsd == approx(0.00019757961816426042, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "toluene1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "toluene2.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.1) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 0.0001398867874149986, places=6) + assert rmsd == approx(0.0001398867874149986, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "cyclohexane1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "cyclohexane2.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.01) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 0.00012190586696474853, places=6) + assert rmsd == approx(0.00012190586696474853, 6) mol1 = Molecule.from_file(os.path.join(test_dir, "oxygen1.xyz")) mol2 = Molecule.from_file(os.path.join(test_dir, "oxygen2.xyz")) mm = GeneticOrderMatcher(mol1, threshold=0.01) _, rmsd = mm.fit(mol2)[0] - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) class KabschMatcherSiTest(unittest.TestCase): @@ -579,29 +579,29 @@ def setUpClass(cls): def test_to_and_from_dict(self): d = self.mm.as_dict() mm = KabschMatcher.from_dict(d) - self.assertDictEqual(d, mm.as_dict()) + assert d == mm.as_dict() def test_missmatched_atoms(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = self.mm.fit(mol2) def test_rotated_molecule(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_rotated.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_perturbed_atom_position(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_perturbed.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.2232223954240079, places=6) + assert rmsd == approx(0.2232223954240079, 6) def test_permuted_atoms_order(self): # This test shows very poor rmsd result, because the `KabschMatcher` # is not capable to handle arbitrary atom's order mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_permuted.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 2.7962454578966454, places=6) + assert rmsd == approx(2.7962454578966454, 6) class BruteForceOrderMatcherSiTest(unittest.TestCase): @@ -613,13 +613,13 @@ def setUpClass(cls): def test_to_and_from_dict(self): d = self.mm.as_dict() mm = BruteForceOrderMatcher.from_dict(d) - self.assertDictEqual(d, mm.as_dict()) + assert d == mm.as_dict() def test_random_match(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_2.xyz")) # ValueError: The number of all possible permuataions (20922789888000) is not feasible to run this method! - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = self.mm.fit(mol2) @@ -632,34 +632,34 @@ def setUpClass(cls): def test_to_and_from_dict(self): d = self.mm.as_dict() mm = HungarianOrderMatcher.from_dict(d) - self.assertDictEqual(d, mm.as_dict()) + assert d == mm.as_dict() def test_missmatched_atoms(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_rotated.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = self.mm.fit(mol2) def test_rotated_molecule(self): # TODO: Checking the cause of the large deviation mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_rotated.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 1.025066171481399, places=6) + assert rmsd == approx(1.025066171481399, 6) def test_perturbed_atom_position(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_perturbed.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.2232223954240077, places=6) + assert rmsd == approx(0.2232223954240077, 6) def test_permuted_atoms_order(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_permuted.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_random_match(self): # TODO: Checking the cause of the large deviation mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_2.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 1.0177241485450828, places=6) + assert rmsd == approx(1.0177241485450828, 6) class GeneticOrderMatcherSiTest(unittest.TestCase): @@ -671,32 +671,32 @@ def setUpClass(cls): def test_to_and_from_dict(self): d = self.mm.as_dict() mm = GeneticOrderMatcher.from_dict(d) - self.assertDictEqual(d, mm.as_dict()) + assert d == mm.as_dict() def test_missmatched_atoms(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.mm.fit(mol2) def test_rotated_molecule(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_rotated.xyz")) res = self.mm.fit(mol2) - self.assertAlmostEqual(res[0][-1], 0.0, places=6) + assert res[0][-1] == approx(0.0, 6) def test_perturbed_atom_position(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_perturbed.xyz")) res = self.mm.fit(mol2) - self.assertAlmostEqual(res[0][-1], 0.2232223954240079, places=6) + assert res[0][-1] == approx(0.2232223954240079, 6) def test_permuted_atoms_order(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_permuted.xyz")) res = self.mm.fit(mol2) - self.assertAlmostEqual(res[0][-1], 0.0, places=6) + assert res[0][-1] == approx(0.0, 6) def test_random_match(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_2.xyz")) res = self.mm.fit(mol2) - self.assertAlmostEqual(res[0][-1], 0.22163169511782, places=6) + assert res[0][-1] == approx(0.22163169511782, 6) class KabschMatcherSi2OTest(unittest.TestCase): @@ -708,24 +708,24 @@ def setUpClass(cls): def test_missmatched_atoms(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_rotated.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = self.mm.fit(mol2) def test_rotated_molecule(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_rotated.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_perturbed_atom_position(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_perturbed.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.24340452336622473, places=6) + assert rmsd == approx(0.24340452336622473, 6) def test_permuted_atoms_order(self): # This task should fail, because `KabschMatcher` is not capable # to handle arbitrary atom's order mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_permuted.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = self.mm.fit(mol2) @@ -737,28 +737,28 @@ def setUpClass(cls): def test_missmatched_atoms(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_rotated.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = self.mm.fit(mol2) def test_rotated_molecule(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_rotated.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_perturbed_atom_position(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_perturbed.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.2434045087608993, places=6) + assert rmsd == approx(0.2434045087608993, 6) def test_permuted_atoms_order(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_permuted.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_random_match(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_2.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.23051587697194997, places=6) + assert rmsd == approx(0.23051587697194997, 6) class HungarianOrderMatcherSi2OTest(unittest.TestCase): @@ -770,28 +770,28 @@ def setUpClass(cls): def test_missmatched_atoms(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster_rotated.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): _, rmsd = self.mm.fit(mol2) def test_rotated_molecule(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_rotated.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_perturbed_atom_position(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_perturbed.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.24474957657894614, places=6) + assert rmsd == approx(0.24474957657894614, 6) def test_permuted_atoms_order(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_permuted.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.0, places=6) + assert rmsd == approx(0.0, 6) def test_random_match(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_2.xyz")) _, rmsd = self.mm.fit(mol2) - self.assertAlmostEqual(rmsd, 0.23231038877573124, places=6) + assert rmsd == approx(0.23231038877573124, 6) class GeneticOrderMatcherSi2OTest(unittest.TestCase): @@ -802,32 +802,32 @@ def setUpClass(cls): def test_missmatched_atoms(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si_cluster.xyz")) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): self.mm.fit(mol2) def test_rotated_molecule(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_rotated.xyz")) res = self.mm.fit(mol2) - self.assertAlmostEqual(res[0][1], 0.0, places=6) + assert res[0][1] == approx(0.0, 6) def test_perturbed_atom_position(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_perturbed.xyz")) res = self.mm.fit(mol2) - self.assertEqual(len(res), 3) - self.assertAlmostEqual(res[0][1], 0.24340452336622473, places=6) + assert len(res) == 3 + assert res[0][1] == approx(0.24340452336622473, 6) def test_permuted_atoms_order(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_permuted.xyz")) res = self.mm.fit(mol2) - self.assertEqual(len(res), 3) - self.assertAlmostEqual(res[0][1], 0.0, places=6) + assert len(res) == 3 + assert res[0][1] == approx(0.0, 6) def test_random_match(self): mol2 = Molecule.from_file(os.path.join(test_dir, "Si2O_cluster_2.xyz")) res = self.mm.match(mol2) - self.assertEqual(len(res), 3) - self.assertEqual(res[0][0], [5, 0, 4, 1, 3, 2]) - self.assertAlmostEqual(res[0][-1], 0.2305159973457393, places=6) + assert len(res) == 3 + assert res[0][0] == [5, 0, 4, 1, 3, 2] + assert res[0][-1] == approx(0.2305159973457393, 6) if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_nmr.py b/pymatgen/analysis/tests/test_nmr.py index 4730224a8ba..412dfb87ae0 100644 --- a/pymatgen/analysis/tests/test_nmr.py +++ b/pymatgen/analysis/tests/test_nmr.py @@ -3,6 +3,7 @@ import unittest import numpy as np +from pytest import approx from pymatgen.analysis.nmr import ChemicalShielding, ElectricFieldGradient from pymatgen.util.testing import PymatgenTest @@ -11,10 +12,10 @@ class TestChemicalShieldingNotation(PymatgenTest): def test_construction(self): cs = ChemicalShielding(np.arange(9).reshape((3, 3))) - self.assertEqual(cs.shape, (3, 3)) + assert cs.shape == (3, 3) cs = ChemicalShielding([1, 2, 3]) - self.assertEqual(cs.shape, (3, 3)) + assert cs.shape == (3, 3) self.assertArrayEqual(np.diag(cs), [1, 2, 3]) def test_principal_axis_system(self): @@ -31,28 +32,28 @@ def test_principal_axis_system(self): def test_notations(self): cs = ChemicalShielding.from_maryland_notation(195.0788, 68.1733, 0.8337) hae1 = cs.haeberlen_values - self.assertAlmostEqual(hae1.sigma_iso, 195.0788, places=5) - self.assertAlmostEqual(hae1.delta_sigma_iso, -65.33899505250002, places=5) - self.assertAlmostEqual(hae1.zeta, -43.559330035000016, places=5) - self.assertAlmostEqual(hae1.eta, 0.13013537835511454, places=5) + assert hae1.sigma_iso == approx(195.0788, 5) + assert hae1.delta_sigma_iso == approx(-65.33899505250002, 5) + assert hae1.zeta == approx(-43.559330035000016, 5) + assert hae1.eta == approx(0.13013537835511454, 5) meh1 = cs.mehring_values - self.assertAlmostEqual(meh1.sigma_iso, 195.0788, places=5) - self.assertAlmostEqual(meh1.sigma_11, 151.51946996499998, places=5) - self.assertAlmostEqual(meh1.sigma_22, 214.02416007, places=5) - self.assertAlmostEqual(meh1.sigma_33, 219.69276996500002, places=5) + assert meh1.sigma_iso == approx(195.0788, 5) + assert meh1.sigma_11 == approx(151.51946996499998, 5) + assert meh1.sigma_22 == approx(214.02416007, 5) + assert meh1.sigma_33 == approx(219.69276996500002, 5) mary1 = cs.maryland_values - self.assertAlmostEqual(mary1.sigma_iso, 195.0788, places=5) - self.assertAlmostEqual(mary1.omega, 68.1733, places=5) - self.assertAlmostEqual(mary1.kappa, 0.8337, places=5) + assert mary1.sigma_iso == approx(195.0788, 5) + assert mary1.omega == approx(68.1733, 5) + assert mary1.kappa == approx(0.8337, 5) class TestElectricFieldGradient(PymatgenTest): def test_construction(self): efg = ElectricFieldGradient(np.arange(9).reshape((3, 3))) - self.assertEqual(efg.shape, (3, 3)) + assert efg.shape == (3, 3) efg = ElectricFieldGradient([1, 2, 3]) - self.assertEqual(efg.shape, (3, 3)) + assert efg.shape == (3, 3) def test_principal_axis_system(self): efg = ElectricFieldGradient([1, 2, 3]) @@ -68,11 +69,11 @@ def test_principal_axis_system(self): def test_Attributes(self): efg = ElectricFieldGradient([[11.11, 1.371, 2.652], [1.371, 3.635, -3.572], [2.652, -3.572, -14.746]]) - self.assertAlmostEqual(efg.V_yy, 11.516, places=3) - self.assertAlmostEqual(efg.V_xx, 4.204, places=3) - self.assertAlmostEqual(efg.V_zz, -15.721, places=3) - self.assertAlmostEqual(efg.asymmetry, 0.465, places=3) - self.assertAlmostEqual(efg.coupling_constant("Al"), 5.573, places=3) + assert efg.V_yy == approx(11.516, 3) + assert efg.V_xx == approx(4.204, 3) + assert efg.V_zz == approx(-15.721, 3) + assert efg.asymmetry == approx(0.465, 3) + assert efg.coupling_constant("Al") == approx(5.573, 3) if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_path_finder.py b/pymatgen/analysis/tests/test_path_finder.py index 045ad2fb3ec..9e16aed2ad0 100644 --- a/pymatgen/analysis/tests/test_path_finder.py +++ b/pymatgen/analysis/tests/test_path_finder.py @@ -43,12 +43,12 @@ def test_image_num(self): for i, image in enumerate(pf.images): if i % 3 == 0: images.append(image) - self.assertEqual(len(images), 9) + assert len(images) == 9 moving_site = relax_sites[0] dists = [s1.sites[moving_site].distance(s2.sites[moving_site]) for s1, s2 in zip(pf.images[:-1], pf.images[1:])] # check that all the small distances are about equal - self.assertTrue(abs(min(dists) - max(dists)) / mean(dists) < 0.02) + assert abs(min(dists) - max(dists)) / mean(dists) < 0.02 if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_piezo.py b/pymatgen/analysis/tests/test_piezo.py index ba6bdad73e1..9aac337595e 100644 --- a/pymatgen/analysis/tests/test_piezo.py +++ b/pymatgen/analysis/tests/test_piezo.py @@ -10,6 +10,7 @@ import unittest import numpy as np +import pytest from pymatgen.analysis.piezo import PiezoTensor from pymatgen.util.testing import PymatgenTest @@ -49,20 +50,23 @@ def test_new(self): pt = PiezoTensor(self.full_tensor_array) self.assertArrayAlmostEqual(pt, self.full_tensor_array) bad_dim_array = np.zeros((3, 3)) - self.assertRaises(ValueError, PiezoTensor, bad_dim_array) + with pytest.raises(ValueError): + PiezoTensor(bad_dim_array) def test_from_voigt(self): bad_voigt = np.zeros((3, 7)) pt = PiezoTensor.from_voigt(self.voigt_matrix) self.assertArrayEqual(pt, self.full_tensor_array) - self.assertRaises(ValueError, PiezoTensor.from_voigt, bad_voigt) + with pytest.raises(ValueError): + PiezoTensor.from_voigt(bad_voigt) self.assertArrayEqual(self.voigt_matrix, pt.voigt) def test_from_vasp_voigt(self): bad_voigt = np.zeros((3, 7)) pt = PiezoTensor.from_vasp_voigt(self.vasp_matrix) self.assertArrayEqual(pt, self.full_tensor_array) - self.assertRaises(ValueError, PiezoTensor.from_voigt, bad_voigt) + with pytest.raises(ValueError): + PiezoTensor.from_voigt(bad_voigt) self.assertArrayEqual(self.voigt_matrix, pt.voigt) diff --git a/pymatgen/analysis/tests/test_pourbaix_diagram.py b/pymatgen/analysis/tests/test_pourbaix_diagram.py index a0bc07fd486..22452bb16de 100644 --- a/pymatgen/analysis/tests/test_pourbaix_diagram.py +++ b/pymatgen/analysis/tests/test_pourbaix_diagram.py @@ -14,6 +14,7 @@ import pytest from monty.serialization import dumpfn, loadfn from monty.tempfile import ScratchDir +from pytest import approx from pymatgen.analysis.pourbaix_diagram import ( IonEntry, @@ -45,36 +46,32 @@ def setUp(self): self.PxIon.concentration = 1e-4 def test_pourbaix_entry(self): - self.assertEqual(self.PxIon.entry.energy, 25, "Wrong Energy!") - self.assertEqual(self.PxIon.entry.name, "MnO4[-1]", "Wrong Entry!") - self.assertEqual(self.PxSol.entry.energy, 49, "Wrong Energy!") - self.assertEqual(self.PxSol.entry.name, "Mn2O3", "Wrong Entry!") + assert self.PxIon.entry.energy == 25, "Wrong Energy!" + assert self.PxIon.entry.name == "MnO4[-1]", "Wrong Entry!" + assert self.PxSol.entry.energy == 49, "Wrong Energy!" + assert self.PxSol.entry.name == "Mn2O3", "Wrong Entry!" # self.assertEqual(self.PxIon.energy, 25, "Wrong Energy!") # self.assertEqual(self.PxSol.energy, 49, "Wrong Energy!") - self.assertEqual(self.PxIon.concentration, 1e-4, "Wrong concentration!") + assert self.PxIon.concentration == 1e-4, "Wrong concentration!" def test_calc_coeff_terms(self): - self.assertEqual(self.PxIon.npH, -8, "Wrong npH!") - self.assertEqual(self.PxIon.nPhi, -7, "Wrong nPhi!") - self.assertEqual(self.PxIon.nH2O, 4, "Wrong nH2O!") + assert self.PxIon.npH == -8, "Wrong npH!" + assert self.PxIon.nPhi == -7, "Wrong nPhi!" + assert self.PxIon.nH2O == 4, "Wrong nH2O!" - self.assertEqual(self.PxSol.npH, -6, "Wrong npH!") - self.assertEqual(self.PxSol.nPhi, -6, "Wrong nPhi!") - self.assertEqual(self.PxSol.nH2O, 3, "Wrong nH2O!") + assert self.PxSol.npH == -6, "Wrong npH!" + assert self.PxSol.nPhi == -6, "Wrong nPhi!" + assert self.PxSol.nH2O == 3, "Wrong nH2O!" def test_to_from_dict(self): d = self.PxIon.as_dict() ion_entry = self.PxIon.from_dict(d) - self.assertEqual(ion_entry.entry.name, "MnO4[-1]", "Wrong Entry!") + assert ion_entry.entry.name == "MnO4[-1]", "Wrong Entry!" d = self.PxSol.as_dict() sol_entry = self.PxSol.from_dict(d) - self.assertEqual(sol_entry.name, "Mn2O3(s)", "Wrong Entry!") - self.assertEqual( - sol_entry.energy, - self.PxSol.energy, - "as_dict and from_dict energies unequal", - ) + assert sol_entry.name == "Mn2O3(s)", "Wrong Entry!" + assert sol_entry.energy == self.PxSol.energy, "as_dict and from_dict energies unequal" # Ensure computed entry data persists entry = ComputedEntry("TiO2", energy=-20, data={"test": "test"}) @@ -82,8 +79,8 @@ def test_to_from_dict(self): with ScratchDir("."): dumpfn(pbx_entry, "pbx_entry.json") reloaded = loadfn("pbx_entry.json") - self.assertIsInstance(reloaded.entry, ComputedEntry) - self.assertIsNotNone(reloaded.entry.data) + assert isinstance(reloaded.entry, ComputedEntry) + assert reloaded.entry.data is not None def test_energy_functions(self): # TODO: test these for values @@ -96,21 +93,18 @@ def test_multi_entry(self): # TODO: More robust multientry test m_entry = MultiEntry([self.PxSol, self.PxIon]) for attr in ["energy", "composition", "nPhi"]: - self.assertEqual( - getattr(m_entry, attr), - getattr(self.PxSol, attr) + getattr(self.PxIon, attr), - ) + assert getattr(m_entry, attr) == getattr(self.PxSol, attr) + getattr(self.PxIon, attr) # As dict, from dict m_entry_dict = m_entry.as_dict() m_entry_new = MultiEntry.from_dict(m_entry_dict) - self.assertEqual(m_entry_new.energy, m_entry.energy) + assert m_entry_new.energy == m_entry.energy def test_get_elt_fraction(self): entry = ComputedEntry("Mn2Fe3O3", 49) pbentry = PourbaixEntry(entry) - self.assertAlmostEqual(pbentry.get_element_fraction("Fe"), 0.6) - self.assertAlmostEqual(pbentry.get_element_fraction("Mn"), 0.4) + assert pbentry.get_element_fraction("Fe") == approx(0.6) + assert pbentry.get_element_fraction("Mn") == approx(0.4) class PourbaixDiagramTest(unittest.TestCase): @@ -123,33 +117,42 @@ def setUpClass(cls): cls.pbx_nofilter = PourbaixDiagram(cls.test_data["Zn"], filter_solids=False) def test_pourbaix_diagram(self): - self.assertEqual( - {e.name for e in self.pbx.stable_entries}, - {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, - "List of stable entries does not match", - ) - - self.assertEqual( - {e.name for e in self.pbx_nofilter.stable_entries}, - {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)", "ZnO2(s)", "ZnH(s)"}, - "List of stable entries for unfiltered pbx does not match", - ) + assert {e.name for e in self.pbx.stable_entries} == { + "ZnO(s)", + "Zn[2+]", + "ZnHO2[-]", + "ZnO2[2-]", + "Zn(s)", + }, "List of stable entries does not match" + + assert {e.name for e in self.pbx_nofilter.stable_entries} == { + "ZnO(s)", + "Zn[2+]", + "ZnHO2[-]", + "ZnO2[2-]", + "Zn(s)", + "ZnO2(s)", + "ZnH(s)", + }, "List of stable entries for unfiltered pbx does not match" pbx_lowconc = PourbaixDiagram(self.test_data["Zn"], conc_dict={"Zn": 1e-8}, filter_solids=True) - self.assertEqual( - {e.name for e in pbx_lowconc.stable_entries}, - {"Zn(HO)2(aq)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, - ) + assert {e.name for e in pbx_lowconc.stable_entries} == { + "Zn(HO)2(aq)", + "Zn[2+]", + "ZnHO2[-]", + "ZnO2[2-]", + "Zn(s)", + } def test_properties(self): - self.assertEqual(len(self.pbx.unstable_entries), 2) + assert len(self.pbx.unstable_entries) == 2 def test_multicomponent(self): # Assure no ions get filtered at high concentration ag_n = [e for e in self.test_data["Ag-Te-N"] if "Te" not in e.composition] highconc = PourbaixDiagram(ag_n, filter_solids=True, conc_dict={"Ag": 1e-5, "N": 1}) entry_sets = [set(e.entry_id) for e in highconc.stable_entries] - self.assertIn({"mp-124", "ion-17"}, entry_sets) + assert {"mp-124", "ion-17"} in entry_sets # Binary system pd_binary = PourbaixDiagram( @@ -158,15 +161,15 @@ def test_multicomponent(self): comp_dict={"Ag": 0.5, "Te": 0.5}, conc_dict={"Ag": 1e-8, "Te": 1e-8}, ) - self.assertEqual(len(pd_binary.stable_entries), 30) + assert len(pd_binary.stable_entries) == 30 test_entry = pd_binary.find_stable_entry(8, 2) - self.assertTrue("mp-499" in test_entry.entry_id) + assert "mp-499" in test_entry.entry_id # Find a specific multientry to test - self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0) + assert pd_binary.get_decomposition_energy(test_entry, 8, 2) == 0 pd_ternary = PourbaixDiagram(self.test_data["Ag-Te-N"], filter_solids=True) - self.assertEqual(len(pd_ternary.stable_entries), 49) + assert len(pd_ternary.stable_entries) == 49 # Fetch a solid entry and a ground state entry mixture ag_te_n = self.test_data["Ag-Te-N"][-1] @@ -174,39 +177,36 @@ def test_multicomponent(self): [self.test_data["Ag-Te-N"][i] for i in [4, 18, 30]], weights=[1 / 3, 1 / 3, 1 / 3], ) - self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag_te_n, 2, -1), 2.767822855765) - self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag_te_n, 10, -2), 3.756840056890625) - self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1), 0) + assert pd_ternary.get_decomposition_energy(ag_te_n, 2, -1) == approx(2.767822855765) + assert pd_ternary.get_decomposition_energy(ag_te_n, 10, -2) == approx(3.756840056890625) + assert pd_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1) == approx(0) # Test invocation of Pourbaix diagram from ternary data new_ternary = PourbaixDiagram(pd_ternary.all_entries) - self.assertEqual(len(new_ternary.stable_entries), 49) - self.assertAlmostEqual(new_ternary.get_decomposition_energy(ag_te_n, 2, -1), 2.767822855765) - self.assertAlmostEqual(new_ternary.get_decomposition_energy(ag_te_n, 10, -2), 3.756840056890625) - self.assertAlmostEqual(new_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1), 0) + assert len(new_ternary.stable_entries) == 49 + assert new_ternary.get_decomposition_energy(ag_te_n, 2, -1) == approx(2.767822855765) + assert new_ternary.get_decomposition_energy(ag_te_n, 10, -2) == approx(3.756840056890625) + assert new_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1) == approx(0) def test_get_pourbaix_domains(self): domains = PourbaixDiagram.get_pourbaix_domains(self.test_data["Zn"]) - self.assertEqual(len(domains[0]), 7) + assert len(domains[0]) == 7 def test_get_decomposition(self): # Test a stable entry to ensure that it's zero in the stable region entry = self.test_data["Zn"][12] # Should correspond to mp-2133 - self.assertAlmostEqual( - self.pbx.get_decomposition_energy(entry, 10, 1), - 0.0, - 5, - "Decomposition energy of ZnO is not 0.", - ) + assert self.pbx.get_decomposition_energy(entry, 10, 1) == approx( + 0.0, 5 + ), "Decomposition energy of ZnO is not 0." # Test an unstable entry to ensure that it's never zero entry = self.test_data["Zn"][11] ph, v = np.meshgrid(np.linspace(0, 14), np.linspace(-2, 4)) result = self.pbx_nofilter.get_decomposition_energy(entry, ph, v) - self.assertTrue((result >= 0).all(), "Unstable energy has hull energy of 0 or less") + assert (result >= 0).all(), "Unstable energy has hull energy of 0 or less" # Test an unstable hydride to ensure HER correction works - self.assertAlmostEqual(self.pbx.get_decomposition_energy(entry, -3, -2), 3.6979147983333) + assert self.pbx.get_decomposition_energy(entry, -3, -2) == approx(3.6979147983333) # Test a list of pHs self.pbx.get_decomposition_energy(entry, np.linspace(0, 2, 5), 2) @@ -226,49 +226,55 @@ def test_get_decomposition(self): filter_solids=True, comp_dict={"Na": 1, "Sn": 12, "C": 24}, ) - self.assertAlmostEqual(pbx.get_decomposition_energy(custom_ion_entry, 5, 2), 2.1209002582, 1) + assert pbx.get_decomposition_energy(custom_ion_entry, 5, 2) == approx(2.1209002582, 1) def test_get_stable_entry(self): entry = self.pbx.get_stable_entry(0, 0) - self.assertEqual(entry.entry_id, "ion-0") + assert entry.entry_id == "ion-0" def test_multielement_parallel(self): # Simple test to ensure that multiprocessing is working test_entries = self.test_data["Ag-Te-N"] nproc = multiprocessing.cpu_count() pbx = PourbaixDiagram(test_entries, filter_solids=True, nproc=nproc) - self.assertEqual(len(pbx.stable_entries), 49) + assert len(pbx.stable_entries) == 49 def test_solid_filter(self): entries = self.test_data["Zn"] pbx = PourbaixDiagram(entries, filter_solids=False) oxidized_phase = pbx.find_stable_entry(10, 2) - self.assertEqual(oxidized_phase.name, "ZnO2(s)") + assert oxidized_phase.name == "ZnO2(s)" entries = self.test_data["Zn"] pbx = PourbaixDiagram(entries, filter_solids=True) oxidized_phase = pbx.find_stable_entry(10, 2) - self.assertEqual(oxidized_phase.name, "ZnO(s)") + assert oxidized_phase.name == "ZnO(s)" def test_serialization(self): d = self.pbx.as_dict() new = PourbaixDiagram.from_dict(d) - self.assertEqual( - {e.name for e in new.stable_entries}, - {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)"}, - "List of stable entries does not match", - ) + assert {e.name for e in new.stable_entries} == { + "ZnO(s)", + "Zn[2+]", + "ZnHO2[-]", + "ZnO2[2-]", + "Zn(s)", + }, "List of stable entries does not match" # Test with unstable solid entries included (filter_solids=False), this should result in the # previously filtered entries being included with pytest.warns(DeprecationWarning, match="The include_unprocessed_entries kwarg is deprecated!"): d = self.pbx_nofilter.as_dict(include_unprocessed_entries=True) new = PourbaixDiagram.from_dict(d) - self.assertEqual( - {e.name for e in new.stable_entries}, - {"ZnO(s)", "Zn[2+]", "ZnHO2[-]", "ZnO2[2-]", "Zn(s)", "ZnO2(s)", "ZnH(s)"}, - "List of stable entries for unfiltered pbx does not match", - ) + assert {e.name for e in new.stable_entries} == { + "ZnO(s)", + "Zn[2+]", + "ZnHO2[-]", + "ZnO2[2-]", + "Zn(s)", + "ZnO2(s)", + "ZnH(s)", + }, "List of stable entries for unfiltered pbx does not match" pd_binary = PourbaixDiagram( self.test_data["Ag-Te"], @@ -277,7 +283,7 @@ def test_serialization(self): conc_dict={"Ag": 1e-8, "Te": 1e-8}, ) new_binary = PourbaixDiagram.from_dict(pd_binary.as_dict()) - self.assertEqual(len(pd_binary.stable_entries), len(new_binary.stable_entries)) + assert len(pd_binary.stable_entries) == len(new_binary.stable_entries) class PourbaixPlotterTest(unittest.TestCase): diff --git a/pymatgen/analysis/tests/test_prototypes.py b/pymatgen/analysis/tests/test_prototypes.py index 27c653a0396..0e2b6645b99 100644 --- a/pymatgen/analysis/tests/test_prototypes.py +++ b/pymatgen/analysis/tests/test_prototypes.py @@ -15,38 +15,29 @@ def test_prototype_matching(self): struct = self.get_structure("Sn") prototype = af.get_prototypes(struct)[0] - self.assertDictEqual( - prototype["tags"], - { - "aflow": "A_cF8_227_a", - "mineral": "diamond", - "pearson": "cF8", - "strukturbericht": "A4", - }, - ) + assert prototype["tags"] == { + "aflow": "A_cF8_227_a", + "mineral": "diamond", + "pearson": "cF8", + "strukturbericht": "A4", + } struct = self.get_structure("CsCl") prototype = af.get_prototypes(struct)[0] - self.assertDictEqual( - prototype["tags"], - { - "aflow": "AB_cP2_221_b_a", - "mineral": "", - "pearson": "cP2", - "strukturbericht": "B2", - }, - ) + assert prototype["tags"] == { + "aflow": "AB_cP2_221_b_a", + "mineral": "", + "pearson": "cP2", + "strukturbericht": "B2", + } struct = self.get_structure("Li2O") prototype = af.get_prototypes(struct)[0] - self.assertDictEqual( - prototype["tags"], - { - "aflow": "AB2_cF12_225_a_c", - "mineral": "Fluorite", - "pearson": "cF12", - "strukturbericht": "C1", - }, - ) + assert prototype["tags"] == { + "aflow": "AB2_cF12_225_a_c", + "mineral": "Fluorite", + "pearson": "cF12", + "strukturbericht": "C1", + } diff --git a/pymatgen/analysis/tests/test_reaction_calculator.py b/pymatgen/analysis/tests/test_reaction_calculator.py index 137243483bb..ac90489ceec 100644 --- a/pymatgen/analysis/tests/test_reaction_calculator.py +++ b/pymatgen/analysis/tests/test_reaction_calculator.py @@ -10,6 +10,8 @@ from math import isnan import numpy as np +import pytest +from pytest import approx from pymatgen.analysis.reaction_calculator import ( BalancedReaction, @@ -32,52 +34,52 @@ def test_init(self): reactants = [Composition("Fe"), Composition("O2")] products = [Composition("Fe2O3")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "2 Fe + 1.5 O2 -> Fe2O3") - self.assertEqual(rxn.normalized_repr, "4 Fe + 3 O2 -> 2 Fe2O3") + assert str(rxn) == "2 Fe + 1.5 O2 -> Fe2O3" + assert rxn.normalized_repr == "4 Fe + 3 O2 -> 2 Fe2O3" d = rxn.as_dict() rxn = Reaction.from_dict(d) repr, factor = rxn.normalized_repr_and_factor() - self.assertEqual(repr, "4 Fe + 3 O2 -> 2 Fe2O3") - self.assertAlmostEqual(factor, 2) + assert repr == "4 Fe + 3 O2 -> 2 Fe2O3" + assert factor == approx(2) reactants = [Composition("FePO4"), Composition("Mn")] products = [Composition("FePO4"), Composition("Xe")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "FePO4 -> FePO4") + assert str(rxn) == "FePO4 -> FePO4" products = [Composition("Ti2 O4"), Composition("O1")] reactants = [Composition("Ti1 O2")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "2 TiO2 -> 2 TiO2") + assert str(rxn) == "2 TiO2 -> 2 TiO2" reactants = [Composition("FePO4"), Composition("Li")] products = [Composition("LiFePO4")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "FePO4 + Li -> LiFePO4") + assert str(rxn) == "FePO4 + Li -> LiFePO4" reactants = [Composition("MgO")] products = [Composition("MgO")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "MgO -> MgO") + assert str(rxn) == "MgO -> MgO" reactants = [Composition("Mg")] products = [Composition("Mg")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "Mg -> Mg") + assert str(rxn) == "Mg -> Mg" reactants = [Composition("FePO4"), Composition("LiPO3")] products = [Composition("LiFeP2O7")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "FePO4 + LiPO3 -> LiFeP2O7") + assert str(rxn) == "FePO4 + LiPO3 -> LiFeP2O7" reactants = [Composition("Na"), Composition("K2O")] products = [Composition("Na2O"), Composition("K")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "2 Na + K2O -> Na2O + 2 K") + assert str(rxn) == "2 Na + K2O -> Na2O + 2 K" # Test for an old bug which has a problem when excess product is # defined. @@ -85,12 +87,12 @@ def test_init(self): reactants = [Composition("FePO4")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "FePO4 -> FePO4") + assert str(rxn) == "FePO4 -> FePO4" products = list(map(Composition, ["LiCrO2", "La8Ti8O12", "O2"])) reactants = [Composition("LiLa3Ti3CrO12")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "LiLa3Ti3CrO12 -> LiCrO2 + 1.5 La2Ti2O3 + 2.75 O2") + assert str(rxn) == "LiLa3Ti3CrO12 -> LiCrO2 + 1.5 La2Ti2O3 + 2.75 O2" def test_rank(self): reactants = [Composition("La2Zr2O7"), Composition("LiCoO2")] @@ -101,10 +103,7 @@ def test_rank(self): Composition("Li2O"), ] - self.assertEqual( - str(Reaction(reactants, products)), - "La2Zr2O7 + 6 LiCoO2 -> La2O3 + 3 Co2O3 + 2 Li2ZrO3 + Li2O", - ) + assert str(Reaction(reactants, products)) == "La2Zr2O7 + 6 LiCoO2 -> La2O3 + 3 Co2O3 + 2 Li2ZrO3 + Li2O" reactants = [Composition("La2O3"), Composition("Co2O3"), Composition("Li2ZrO3")] products = [ @@ -112,9 +111,8 @@ def test_rank(self): Composition("La2Zr2O7"), Composition("Li3CoO3"), ] - self.assertEqual( - str(Reaction(reactants, products)), - "La2O3 + 0.3333 Co2O3 + 2 Li2ZrO3 -> Li2O + La2Zr2O7 + 0.6667 Li3CoO3", + assert ( + str(Reaction(reactants, products)) == "La2O3 + 0.3333 Co2O3 + 2 Li2ZrO3 -> Li2O + La2Zr2O7 + 0.6667 Li3CoO3" ) reactants = [Composition("La2O3"), Composition("Co2O3"), Composition("Li2ZrO3")] @@ -124,9 +122,8 @@ def test_rank(self): Composition("La2Zr2O7"), Composition("Li3CoO3"), ] - self.assertEqual( - str(Reaction(reactants, products)), - "La2O3 + 0.3333 Co2O3 + 2 Li2ZrO3 -> Li2O + La2Zr2O7 + 0.6667 Li3CoO3", + assert ( + str(Reaction(reactants, products)) == "La2O3 + 0.3333 Co2O3 + 2 Li2ZrO3 -> Li2O + La2Zr2O7 + 0.6667 Li3CoO3" ) reactants = [Composition("La2O3"), Composition("Co2O3"), Composition("Li2ZrO3")] @@ -137,9 +134,8 @@ def test_rank(self): Composition("Li3CoO3"), Composition("XeNe"), ] - self.assertEqual( - str(Reaction(reactants, products)), - "La2O3 + 0.3333 Co2O3 + 2 Li2ZrO3 -> Li2O + La2Zr2O7 + 0.6667 Li3CoO3", + assert ( + str(Reaction(reactants, products)) == "La2O3 + 0.3333 Co2O3 + 2 Li2ZrO3 -> Li2O + La2Zr2O7 + 0.6667 Li3CoO3" ) reactants = [Composition("LiCoO2")] @@ -150,35 +146,33 @@ def test_rank(self): Composition("Li1F1"), Composition("Co1F3"), ] - self.assertEqual( - str(Reaction(reactants, products)), - "1.667 LiCoO2 + 0.3333 CoF3 -> Co2O3 + 0.3333 Li2O + LiF", - ) + assert str(Reaction(reactants, products)) == "1.667 LiCoO2 + 0.3333 CoF3 -> Co2O3 + 0.3333 Li2O + LiF" # this test can fail because of numerical rank calculation issues reactants = [Composition("LiCoO2"), Composition("Li2O1")] products = [Composition("ZrF4"), Composition("Co2O3")] - self.assertEqual(str(Reaction(reactants, products)), "2 LiCoO2 -> Li2O + Co2O3") + assert str(Reaction(reactants, products)) == "2 LiCoO2 -> Li2O + Co2O3" def test_singular_case(self): rxn = Reaction( [Composition("XeMn"), Composition("Li")], [Composition("S"), Composition("LiS2"), Composition("FeCl")], ) - self.assertEqual(str(rxn), "Li + 2 S -> LiS2") + assert str(rxn) == "Li + 2 S -> LiS2" def test_overdetermined(self): - self.assertRaises(ReactionError, Reaction, [Composition("Li")], [Composition("LiO2")]) + with pytest.raises(ReactionError): + Reaction([Composition("Li")], [Composition("LiO2")]) def test_scientific_notation(self): products = [Composition("FePO3.9999"), Composition("O2")] reactants = [Composition("FePO4")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "FePO4 -> Fe1P1O3.9999 + 5e-05 O2") - self.assertEqual(rxn, Reaction.from_string(str(rxn))) + assert str(rxn) == "FePO4 -> Fe1P1O3.9999 + 5e-05 O2" + assert rxn == Reaction.from_string(str(rxn)) rxn2 = Reaction.from_string("FePO4 + 20 CO -> 1e1 O2 + Fe1P1O4 + 20 C") - self.assertEqual(str(rxn2), "20 CO -> 20 C + 10 O2") + assert str(rxn2) == "20 CO -> 20 C + 10 O2" def test_equals(self): reactants = [Composition("Fe"), Composition("O2")] @@ -187,14 +181,14 @@ def test_equals(self): reactants = [Composition("O2"), Composition("Fe")] products = [Composition("Fe2O3")] rxn2 = Reaction(reactants, products) - self.assertTrue(rxn == rxn2) + assert rxn == rxn2 def test_normalize_to(self): products = [Composition("Fe"), Composition("O2")] reactants = [Composition("Fe2O3")] rxn = Reaction(reactants, products) rxn.normalize_to(Composition("Fe"), 3) - self.assertEqual(str(rxn), "1.5 Fe2O3 -> 3 Fe + 2.25 O2") + assert str(rxn) == "1.5 Fe2O3 -> 3 Fe + 2.25 O2" def test_calculate_energy(self): reactants = [Composition("MgO"), Composition("Al2O3")] @@ -205,9 +199,9 @@ def test_calculate_energy(self): Composition("MgAl2O4"): -0.5, } rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "MgO + Al2O3 -> MgAl2O4") - self.assertEqual(rxn.normalized_repr, "MgO + Al2O3 -> MgAl2O4") - self.assertAlmostEqual(rxn.calculate_energy(energies), -0.2, 5) + assert str(rxn) == "MgO + Al2O3 -> MgAl2O4" + assert rxn.normalized_repr == "MgO + Al2O3 -> MgAl2O4" + assert rxn.calculate_energy(energies) == approx(-0.2, 5) def test_as_entry(self): reactants = [Composition("MgO"), Composition("Al2O3")] @@ -219,8 +213,8 @@ def test_as_entry(self): } rxn = Reaction(reactants, products) entry = rxn.as_entry(energies) - self.assertEqual(entry.name, "MgO + Al2O3 -> MgAl2O4") - self.assertAlmostEqual(entry.energy, -0.2, 5) + assert entry.name == "MgO + Al2O3 -> MgAl2O4" + assert entry.energy == approx(-0.2, 5) products = [Composition("Fe"), Composition("O2")] reactants = [Composition("Fe2O3")] @@ -231,8 +225,8 @@ def test_as_entry(self): Composition("Fe2O3"): 0.5, } entry = rxn.as_entry(energies) - self.assertEqual(entry.composition, Composition("Fe1.0 O1.5")) - self.assertAlmostEqual(entry.energy, -0.25, 5) + assert entry.composition == Composition("Fe1.0 O1.5") + assert entry.energy == approx(-0.25, 5) def test_products_reactants(self): reactants = [ @@ -249,11 +243,11 @@ def test_products_reactants(self): } rxn = Reaction(reactants, products) - self.assertIn(Composition("O2"), rxn.products, "O not in products!") - self.assertIn(Composition("Li3Fe2(PO4)3"), rxn.reactants, "Li3Fe2(PO4)4 not in reactants!") - self.assertEqual(str(rxn), "0.3333 Li3Fe2(PO4)3 + 0.1667 Fe2O3 -> 0.25 O2 + LiFePO4") - self.assertEqual(rxn.normalized_repr, "4 Li3Fe2(PO4)3 + 2 Fe2O3 -> 3 O2 + 12 LiFePO4") - self.assertAlmostEqual(rxn.calculate_energy(energies), -0.48333333, 5) + assert Composition("O2") in rxn.products, "O not in products!" + assert Composition("Li3Fe2(PO4)3") in rxn.reactants, "Li3Fe2(PO4)4 not in reactants!" + assert str(rxn) == "0.3333 Li3Fe2(PO4)3 + 0.1667 Fe2O3 -> 0.25 O2 + LiFePO4" + assert rxn.normalized_repr == "4 Li3Fe2(PO4)3 + 2 Fe2O3 -> 3 O2 + 12 LiFePO4" + assert rxn.calculate_energy(energies) == approx(-0.48333333, 5) def test_to_from_dict(self): reactants = [Composition("Fe"), Composition("O2")] @@ -261,13 +255,13 @@ def test_to_from_dict(self): rxn = Reaction(reactants, products) d = rxn.as_dict() rxn = Reaction.from_dict(d) - self.assertEqual(rxn.normalized_repr, "4 Fe + 3 O2 -> 2 Fe2O3") + assert rxn.normalized_repr == "4 Fe + 3 O2 -> 2 Fe2O3" def test_underdetermined(self): reactants = [Composition("Fe"), Composition("O2")] products = [Composition("Fe"), Composition("O2")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "Fe + O2 -> Fe + O2") + assert str(rxn) == "Fe + O2 -> Fe + O2" reactants = [ Composition("Fe"), @@ -278,7 +272,7 @@ def test_underdetermined(self): ] products = [Composition("FeO2"), Composition("NaCl"), Composition("Li2Cl2")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "Fe + O2 + Na + 2 Li + 1.5 Cl2 -> FeO2 + NaCl + 2 LiCl") + assert str(rxn) == "Fe + O2 + Na + 2 Li + 1.5 Cl2 -> FeO2 + NaCl + 2 LiCl" reactants = [ Composition("Fe"), @@ -295,18 +289,18 @@ def test_underdetermined(self): ] rxn = Reaction(reactants, products) # this can't normalize to 1 LiCl + 1 Na2O (not enough O), so chooses LiCl and FeCl - self.assertEqual(str(rxn), "Fe + Na + 0.5 Li2O + Cl2 -> LiCl + 0.5 Na2O + FeCl") + assert str(rxn) == "Fe + Na + 0.5 Li2O + Cl2 -> LiCl + 0.5 Na2O + FeCl" def test_underdetermined_reactants(self): reactants = [Composition("Li"), Composition("Cl"), Composition("Cl")] products = [Composition("LiCl")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "Li + 0.25 Cl2 + 0.25 Cl2 -> LiCl") + assert str(rxn) == "Li + 0.25 Cl2 + 0.25 Cl2 -> LiCl" reactants = [Composition("LiMnCl3"), Composition("LiCl"), Composition("MnCl2")] products = [Composition("Li2MnCl4")] rxn = Reaction(reactants, products) - self.assertEqual(str(rxn), "LiMnCl3 + 3 LiCl + MnCl2 -> 2 Li2MnCl4") + assert str(rxn) == "LiMnCl3 + 3 LiCl + MnCl2 -> 2 Li2MnCl4" class BalancedReactionTest(unittest.TestCase): @@ -320,12 +314,13 @@ def test_init(self): rct = {Composition("K2SO4"): 3, Composition("Na2S"): 1, Composition("Li"): 24} prod = {Composition("KNaS"): 2, Composition("K2S"): 2, Composition("Li2O"): 12} rxn = BalancedReaction(rct, prod) - self.assertIsNotNone(str(rxn)) + assert str(rxn) is not None # Test unbalanced exception rct = {Composition("K2SO4"): 1, Composition("Na2S"): 1, Composition("Li"): 24} prod = {Composition("KNaS"): 2, Composition("K2S"): 2, Composition("Li2O"): 12} - self.assertRaises(ReactionError, BalancedReaction, rct, prod) + with pytest.raises(ReactionError): + BalancedReaction(rct, prod) def test_to_from_dict(self): rct = {Composition("K2SO4"): 3, Composition("Na2S"): 1, Composition("Li"): 24} @@ -334,11 +329,11 @@ def test_to_from_dict(self): d = rxn.as_dict() new_rxn = BalancedReaction.from_dict(d) for comp in new_rxn.all_comp: - self.assertEqual(new_rxn.get_coeff(comp), rxn.get_coeff(comp)) + assert new_rxn.get_coeff(comp) == rxn.get_coeff(comp) def test_from_string(self): rxn = BalancedReaction({Composition("Li"): 4, Composition("O2"): 1}, {Composition("Li2O"): 2}) - self.assertEqual(rxn, BalancedReaction.from_string("4 Li + O2 -> 2Li2O")) + assert rxn == BalancedReaction.from_string("4 Li + O2 -> 2Li2O") rxn = BalancedReaction( {Composition("Li(NiO2)3"): 1}, @@ -349,10 +344,7 @@ def test_from_string(self): }, ) - self.assertEqual( - rxn, - BalancedReaction.from_string("1.000 Li(NiO2)3 -> 0.500 O2 + 1.000 Li(NiO2)2 + 1.000 NiO"), - ) + assert rxn == BalancedReaction.from_string("1.000 Li(NiO2)3 -> 0.500 O2 + 1.000 Li(NiO2)2 + 1.000 NiO") def test_remove_spectator_species(self): rxn = BalancedReaction( @@ -360,7 +352,7 @@ def test_remove_spectator_species(self): {Composition("Li2O"): 2, Composition("Na"): 1}, ) - self.assertTrue(Composition("Na") not in rxn.all_comp) + assert Composition("Na") not in rxn.all_comp class ComputedReactionTest(unittest.TestCase): @@ -404,7 +396,7 @@ def setUp(self): self.rxn = ComputedReaction(rcts, prods) def test_calculated_reaction_energy(self): - self.assertAlmostEqual(self.rxn.calculated_reaction_energy, -5.60748821935) + assert self.rxn.calculated_reaction_energy == approx(-5.60748821935) def test_calculated_reaction_energy_uncertainty(self): d = [ @@ -470,12 +462,12 @@ def test_calculated_reaction_energy_uncertainty(self): prods = list(filter(lambda e: e.composition.reduced_formula == "Li2O2", entries)) rxn_with_uncertainty = ComputedReaction(rcts, prods) - self.assertAlmostEqual(rxn_with_uncertainty.calculated_reaction_energy_uncertainty, 0.5 * 0.0744) + assert rxn_with_uncertainty.calculated_reaction_energy_uncertainty == approx(0.5 * 0.0744) def test_calculated_reaction_energy_uncertainty_for_no_uncertainty(self): # test that reaction_energy_uncertainty property doesn't cause errors # when products/reactants have no uncertainties - self.assertAlmostEqual(self.rxn.calculated_reaction_energy_uncertainty, 0) + assert self.rxn.calculated_reaction_energy_uncertainty == approx(0) def test_calculated_reaction_energy_uncertainty_for_nan(self): # test that reaction_energy_uncertainty property is nan when the uncertainty @@ -543,21 +535,21 @@ def test_calculated_reaction_energy_uncertainty_for_nan(self): prods = list(filter(lambda e: e.composition.reduced_formula == "Li2O2", entries)) rxn_with_uncertainty = ComputedReaction(rcts, prods) - self.assertTrue(isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty)) + assert isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty) def test_init(self): - self.assertEqual(str(self.rxn), "2 Li + O2 -> Li2O2") + assert str(self.rxn) == "2 Li + O2 -> Li2O2" def test_to_from_dict(self): d = self.rxn.as_dict() new_rxn = ComputedReaction.from_dict(d) - self.assertEqual(str(new_rxn), "2 Li + O2 -> Li2O2") + assert str(new_rxn) == "2 Li + O2 -> Li2O2" def test_all_entries(self): for c, e in zip(self.rxn.coeffs, self.rxn.all_entries): if c > 0: - self.assertEqual(e.composition.reduced_formula, "Li2O2") - self.assertAlmostEqual(e.energy, -959.64693323) + assert e.composition.reduced_formula == "Li2O2" + assert e.energy == approx(-959.64693323) if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_structure_analyzer.py b/pymatgen/analysis/tests/test_structure_analyzer.py index 459743f68fc..40402401d23 100644 --- a/pymatgen/analysis/tests/test_structure_analyzer.py +++ b/pymatgen/analysis/tests/test_structure_analyzer.py @@ -8,6 +8,7 @@ import unittest import numpy as np +from pytest import approx from pymatgen.analysis.structure_analyzer import ( RelaxationAnalyzer, @@ -38,19 +39,11 @@ def setUp(self): def test_analyze(self): # Check for the Voronoi index of site i in Structure single_structure = self.va.analyze(self.s, n=5) - self.assertIn( - single_structure.view(), - np.array([4, 3, 3, 4, 2, 2, 1, 0]).view(), - "Cannot find the right polyhedron.", - ) + assert single_structure.view() in np.array([4, 3, 3, 4, 2, 2, 1, 0]).view(), "Cannot find the right polyhedron." # Check for the presence of a Voronoi index and its frequency in # a ensemble (list) of Structures ensemble = self.va.analyze_structures(self.ss, step_freq=2, most_frequent_polyhedra=10) - self.assertIn( - ("[1 3 4 7 1 0 0 0]", 3), - ensemble, - "Cannot find the right polyhedron in ensemble.", - ) + assert ("[1 3 4 7 1 0 0 0]", 3) in ensemble, "Cannot find the right polyhedron in ensemble." class RelaxationAnalyzerTest(unittest.TestCase): @@ -63,18 +56,18 @@ def setUp(self): def test_vol_and_para_changes(self): for v in self.analyzer.get_percentage_lattice_parameter_changes().values(): - self.assertAlmostEqual(-0.0092040921155279731, v) + assert -0.0092040921155279731 == approx(v) latt_change = v vol_change = self.analyzer.get_percentage_volume_change() - self.assertAlmostEqual(-0.0273589101391, vol_change) + assert -0.0273589101391 == approx(vol_change) # This is a simple cubic cell, so the latt and vol change are simply # Related. So let's test that. - self.assertAlmostEqual((1 + latt_change) ** 3 - 1, vol_change) + assert (1 + latt_change) ** 3 - 1 == approx(vol_change) def test_get_percentage_bond_dist_changes(self): for v in self.analyzer.get_percentage_bond_dist_changes().values(): for v2 in v.values(): - self.assertAlmostEqual(-0.009204092115527862, v2) + assert -0.009204092115527862 == approx(v2) class VoronoiConnectivityTest(PymatgenTest): @@ -82,27 +75,24 @@ def test_connectivity_array(self): vc = VoronoiConnectivity(self.get_structure("LiFePO4")) ca = vc.connectivity_array expected = np.array([0, 1.96338392, 0, 0.04594495]) - self.assertTrue(np.allclose(ca[15, :4, ca.shape[2] // 2], expected)) + assert np.allclose(ca[15, :4, ca.shape[2] // 2], expected) expected = np.array([0, 0, 0]) - self.assertTrue(np.allclose(ca[1, -3:, 51], expected)) + assert np.allclose(ca[1, -3:, 51], expected) site = vc.get_sitej(27, 51) - self.assertEqual(site.specie, Element("O")) + assert site.specie == Element("O") expected = np.array([-0.29158, 0.74889, 0.95684]) - self.assertTrue(np.allclose(site.frac_coords, expected)) + assert np.allclose(site.frac_coords, expected) class MiscFunctionTest(PymatgenTest): def test_average_coordination_number(self): xdatcar = Xdatcar(os.path.join(PymatgenTest.TEST_FILES_DIR, "XDATCAR.MD")) coordination_numbers = average_coordination_number(xdatcar.structures, freq=1) - self.assertAlmostEqual( - coordination_numbers["Fe"], - 4.771903318390836, - 5, - "Coordination number not calculated properly.", - ) + assert coordination_numbers["Fe"] == approx( + 4.771903318390836, 5 + ), "Coordination number not calculated properly." def test_solid_angle(self): center = [2.294508207929496, 4.4078057081404, 2.299997773791287] @@ -113,20 +103,15 @@ def test_solid_angle(self): [3.874524708023352, 4.425301459451914, 2.771990305592935], [2.055778446743566, 4.437449313863041, 4.061046832034642], ] - self.assertAlmostEqual( - solid_angle(center, coords), - 1.83570965938, - 7, - "Wrong result returned by solid_angle", - ) + assert solid_angle(center, coords) == approx(1.83570965938, 7), "Wrong result returned by solid_angle" def test_contains_peroxide(self): for f in ["LiFePO4", "NaFePO4", "Li3V2(PO4)3", "Li2O"]: - self.assertFalse(contains_peroxide(self.get_structure(f))) + assert not contains_peroxide(self.get_structure(f)) for f in ["Li2O2", "K2O2"]: - self.assertTrue(contains_peroxide(self.get_structure(f))) + assert contains_peroxide(self.get_structure(f)) def test_oxide_type(self): el_li = Element("Li") @@ -141,7 +126,7 @@ def test_oxide_type(self): coords.append([0.132568, 0.414910, 0.000000]) coords.append([0.867432, 0.585090, 0.000000]) struct = Structure(latt, elts, coords) - self.assertEqual(oxide_type(struct, 1.1), "superoxide") + assert oxide_type(struct, 1.1) == "superoxide" el_li = Element("Li") el_o = Element("O") @@ -154,7 +139,7 @@ def test_oxide_type(self): [0.099587, 0.874790, 0.224797], ] struct = Structure(latt, elts, coords) - self.assertEqual(oxide_type(struct, 1.1), "ozonide") + assert oxide_type(struct, 1.1) == "ozonide" latt = Lattice.from_parameters(3.159597, 3.159572, 7.685205, 89.999884, 89.999674, 60.000510) el_li = Element("Li") @@ -171,7 +156,7 @@ def test_oxide_type(self): [0.666665, 0.666684, 0.149189], ] struct = Structure(latt, elts, coords) - self.assertEqual(oxide_type(struct, 1.1), "peroxide") + assert oxide_type(struct, 1.1) == "peroxide" el_li = Element("Li") el_o = Element("O") @@ -187,7 +172,7 @@ def test_oxide_type(self): [0.500000, 0.000000, 0.807328], ] struct = Structure(latt, elts, coords) - self.assertEqual(oxide_type(struct, 1.1), "hydroxide") + assert oxide_type(struct, 1.1) == "hydroxide" el_li = Element("Li") el_n = Element("N") @@ -203,7 +188,7 @@ def test_oxide_type(self): [0.500000, 0.000000, 0.807328], ] struct = Structure(latt, elts, coords) - self.assertEqual(oxide_type(struct, 1.1), "None") + assert oxide_type(struct, 1.1) == "None" el_o = Element("O") latt = Lattice.from_parameters(4.389828, 5.369789, 5.369789, 70.786622, 69.244828, 69.244828) @@ -219,7 +204,7 @@ def test_oxide_type(self): [0.867359, 0.851778, 0.851778], ] struct = Structure(latt, elts, coords) - self.assertEqual(oxide_type(struct, 1.1), "None") + assert oxide_type(struct, 1.1) == "None" def test_sulfide_type(self): # NaS2 -> polysulfide @@ -232,14 +217,14 @@ def test_sulfide_type(self): [0.14700, 0.11600, 0.40000], ] struct = Structure.from_spacegroup(122, latt, species, coords) - self.assertEqual(sulfide_type(struct), "polysulfide") + assert sulfide_type(struct) == "polysulfide" # NaCl type NaS -> sulfide latt = Lattice.cubic(5.75) species = ["Na", "S"] coords = [[0.00000, 0.00000, 0.00000], [0.50000, 0.50000, 0.50000]] struct = Structure.from_spacegroup(225, latt, species, coords) - self.assertEqual(sulfide_type(struct), "sulfide") + assert sulfide_type(struct) == "sulfide" # Na2S2O3 -> None (sulfate) latt = Lattice.monoclinic(6.40100, 8.10000, 8.47400, 96.8800) @@ -254,7 +239,7 @@ def test_sulfide_type(self): [0.16248, -0.08546, 0.11608], ] struct = Structure.from_spacegroup(14, latt, species, coords) - self.assertEqual(sulfide_type(struct), None) + assert sulfide_type(struct) is None # Na3PS3O -> sulfide latt = Lattice.orthorhombic(9.51050, 11.54630, 5.93230) @@ -268,11 +253,11 @@ def test_sulfide_type(self): [0.50000, 0.30300, 0.61140], ] struct = Structure.from_spacegroup(36, latt, species, coords) - self.assertEqual(sulfide_type(struct), "sulfide") + assert sulfide_type(struct) == "sulfide" # test for unphysical cells struct.scale_lattice(struct.volume * 10) - self.assertEqual(sulfide_type(struct), "sulfide") + assert sulfide_type(struct) == "sulfide" if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_structure_matcher.py b/pymatgen/analysis/tests/test_structure_matcher.py index dcdf71032fc..a12f1b47699 100644 --- a/pymatgen/analysis/tests/test_structure_matcher.py +++ b/pymatgen/analysis/tests/test_structure_matcher.py @@ -9,7 +9,9 @@ import unittest import numpy as np +import pytest from monty.json import MontyDecoder +from pytest import approx from pymatgen.analysis.structure_matcher import ( ElementComparator, @@ -42,19 +44,20 @@ def test_ignore_species(self): s1 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "LiFePO4.cif")) s2 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "POSCAR")) m = StructureMatcher(ignored_species=["Li"], primitive_cell=False, attempt_supercell=True) - self.assertTrue(m.fit(s1, s2)) - self.assertTrue(m.fit_anonymous(s1, s2)) + assert m.fit(s1, s2) + assert m.fit_anonymous(s1, s2) groups = m.group_structures([s1, s2]) - self.assertEqual(len(groups), 1) + assert len(groups) == 1 s2.make_supercell((2, 1, 1)) ss1 = m.get_s2_like_s1(s2, s1, include_ignored_species=True) - self.assertAlmostEqual(ss1.lattice.a, 20.820740000000001) - self.assertEqual(ss1.composition.reduced_formula, "LiFePO4") + assert ss1.lattice.a == approx(20.820740000000001) + assert ss1.composition.reduced_formula == "LiFePO4" - self.assertEqual( - {k.symbol: v.symbol for k, v in m.get_best_electronegativity_anonymous_mapping(s1, s2).items()}, - {"Fe": "Fe", "P": "P", "O": "O"}, - ) + assert {k.symbol: v.symbol for k, v in m.get_best_electronegativity_anonymous_mapping(s1, s2).items()} == { + "Fe": "Fe", + "P": "P", + "O": "O", + } def test_get_supercell_size(self): l = Lattice.cubic(1) @@ -63,23 +66,24 @@ def test_get_supercell_size(self): s2 = Structure(l2, ["Cu", "Cu", "Ag"], [[0] * 3] * 3) sm = StructureMatcher(supercell_size="volume") - self.assertEqual(sm._get_supercell_size(s1, s2), (1, True)) - self.assertEqual(sm._get_supercell_size(s2, s1), (1, True)) + assert sm._get_supercell_size(s1, s2) == (1, True) + assert sm._get_supercell_size(s2, s1) == (1, True) sm = StructureMatcher(supercell_size="num_sites") - self.assertEqual(sm._get_supercell_size(s1, s2), (2, False)) - self.assertEqual(sm._get_supercell_size(s2, s1), (2, True)) + assert sm._get_supercell_size(s1, s2) == (2, False) + assert sm._get_supercell_size(s2, s1) == (2, True) sm = StructureMatcher(supercell_size="Ag") - self.assertEqual(sm._get_supercell_size(s1, s2), (2, False)) - self.assertEqual(sm._get_supercell_size(s2, s1), (2, True)) + assert sm._get_supercell_size(s1, s2) == (2, False) + assert sm._get_supercell_size(s2, s1) == (2, True) sm = StructureMatcher(supercell_size=["Ag", "Cu"]) - self.assertEqual(sm._get_supercell_size(s1, s2), (1, True)) - self.assertEqual(sm._get_supercell_size(s2, s1), (1, True)) + assert sm._get_supercell_size(s1, s2) == (1, True) + assert sm._get_supercell_size(s2, s1) == (1, True) sm = StructureMatcher(supercell_size="wfieoh") - self.assertRaises(ValueError, sm._get_supercell_size, s1, s2) + with pytest.raises(ValueError): + sm._get_supercell_size(s1, s2) def test_cmp_fstruct(self): sm = StructureMatcher() @@ -90,12 +94,14 @@ def test_cmp_fstruct(self): mask = np.array([[False, False]]) mask2 = np.array([[True, False]]) - self.assertRaises(ValueError, sm._cmp_fstruct, s2, s1, frac_tol, mask.T) - self.assertRaises(ValueError, sm._cmp_fstruct, s1, s2, frac_tol, mask.T) + with pytest.raises(ValueError): + sm._cmp_fstruct(s2, s1, frac_tol, mask.T) + with pytest.raises(ValueError): + sm._cmp_fstruct(s1, s2, frac_tol, mask.T) - self.assertTrue(sm._cmp_fstruct(s1, s2, frac_tol, mask)) - self.assertFalse(sm._cmp_fstruct(s1, s2, frac_tol / 2, mask)) - self.assertFalse(sm._cmp_fstruct(s1, s2, frac_tol, mask2)) + assert sm._cmp_fstruct(s1, s2, frac_tol, mask) + assert not sm._cmp_fstruct(s1, s2, frac_tol / 2, mask) + assert not sm._cmp_fstruct(s1, s2, frac_tol, mask2) def test_cart_dists(self): sm = StructureMatcher() @@ -113,30 +119,32 @@ def test_cart_dists(self): n1 = (len(s1) / l.volume) ** (1 / 3) n2 = (len(s2) / l.volume) ** (1 / 3) - self.assertRaises(ValueError, sm._cart_dists, s2, s1, l, mask.T, n2) - self.assertRaises(ValueError, sm._cart_dists, s1, s2, l, mask.T, n1) + with pytest.raises(ValueError): + sm._cart_dists(s2, s1, l, mask.T, n2) + with pytest.raises(ValueError): + sm._cart_dists(s1, s2, l, mask.T, n1) d, ft, s = sm._cart_dists(s1, s2, l, mask, n1) - self.assertTrue(np.allclose(d, [0])) - self.assertTrue(np.allclose(ft, [-0.01, -0.02, -0.03])) - self.assertTrue(np.allclose(s, [1])) + assert np.allclose(d, [0]) + assert np.allclose(ft, [-0.01, -0.02, -0.03]) + assert np.allclose(s, [1]) # check that masking best value works d, ft, s = sm._cart_dists(s1, s2, l, mask2, n1) - self.assertTrue(np.allclose(d, [0])) - self.assertTrue(np.allclose(ft, [0.02, 0.03, 0.04])) - self.assertTrue(np.allclose(s, [0])) + assert np.allclose(d, [0]) + assert np.allclose(ft, [0.02, 0.03, 0.04]) + assert np.allclose(s, [0]) # check that averaging of translation is done properly d, ft, s = sm._cart_dists(s1, s3, l, mask3, n1) - self.assertTrue(np.allclose(d, [0.08093341] * 2)) - self.assertTrue(np.allclose(ft, [0.01, 0.025, 0.035])) - self.assertTrue(np.allclose(s, [1, 0])) + assert np.allclose(d, [0.08093341] * 2) + assert np.allclose(ft, [0.01, 0.025, 0.035]) + assert np.allclose(s, [1, 0]) # check distances are large when mask allows no 'real' mapping d, ft, s = sm._cart_dists(s1, s4, l, mask4, n1) - self.assertTrue(np.min(d) > 1e8) - self.assertTrue(np.min(ft) > 1e8) + assert np.min(d) > 1e8 + assert np.min(ft) > 1e8 def test_get_mask(self): sm = StructureMatcher(comparator=ElementComparator()) @@ -150,9 +158,9 @@ def test_get_mask(self): [True, True, False, True], ] m, inds, i = sm._get_mask(s1, s2, 1, True) - self.assertTrue(np.all(m == result)) - self.assertTrue(i == 2) - self.assertEqual(inds, [2]) + assert np.all(m == result) + assert i == 2 + assert inds == [2] # test supercell with match result = [ @@ -161,9 +169,9 @@ def test_get_mask(self): [1, 1, 1, 1, 0, 0, 1, 1], ] m, inds, i = sm._get_mask(s1, s2, 2, True) - self.assertTrue(np.all(m == result)) - self.assertTrue(i == 2) - self.assertTrue(np.allclose(inds, np.array([4]))) + assert np.all(m == result) + assert i == 2 + assert np.allclose(inds, np.array([4])) # test supercell without match result = [ @@ -173,9 +181,9 @@ def test_get_mask(self): [0, 0, 0, 0, 1, 1], ] m, inds, i = sm._get_mask(s2, s1, 2, True) - self.assertTrue(np.all(m == result)) - self.assertTrue(i == 0) - self.assertTrue(np.allclose(inds, np.array([]))) + assert np.all(m == result) + assert i == 0 + assert np.allclose(inds, np.array([])) # test s2_supercell result = [ @@ -189,9 +197,9 @@ def test_get_mask(self): [0, 0, 1], ] m, inds, i = sm._get_mask(s2, s1, 2, False) - self.assertTrue(np.all(m == result)) - self.assertTrue(i == 0) - self.assertTrue(np.allclose(inds, np.array([]))) + assert np.all(m == result) + assert i == 0 + assert np.allclose(inds, np.array([])) # test for multiple translation indices s1 = Structure(l, ["Cu", "Ag", "Cu", "Ag", "Ag"], [[0] * 3] * 5) @@ -199,9 +207,9 @@ def test_get_mask(self): result = [[1, 0, 1, 0, 0], [0, 1, 0, 1, 1], [1, 0, 1, 0, 0]] m, inds, i = sm._get_mask(s1, s2, 1, True) - self.assertTrue(np.all(m == result)) - self.assertTrue(i == 1) - self.assertTrue(np.allclose(inds, [0, 2])) + assert np.all(m == result) + assert i == 1 + assert np.allclose(inds, [0, 2]) def test_get_supercells(self): sm = StructureMatcher(comparator=ElementComparator()) @@ -211,17 +219,17 @@ def test_get_supercells(self): s2 = Structure(l2, ["Cu", "Cu", "Ag"], [[0] * 3] * 3) scs = list(sm._get_supercells(s1, s2, 8, False)) for x in scs: - self.assertAlmostEqual(abs(np.linalg.det(x[3])), 8) - self.assertEqual(len(x[0]), 4) - self.assertEqual(len(x[1]), 24) - self.assertEqual(len(scs), 48) + assert abs(np.linalg.det(x[3])) == approx(8) + assert len(x[0]) == 4 + assert len(x[1]) == 24 + assert len(scs) == 48 scs = list(sm._get_supercells(s2, s1, 8, True)) for x in scs: - self.assertAlmostEqual(abs(np.linalg.det(x[3])), 8) - self.assertEqual(len(x[0]), 24) - self.assertEqual(len(x[1]), 4) - self.assertEqual(len(scs), 48) + assert abs(np.linalg.det(x[3])) == approx(8) + assert len(x[0]) == 24 + assert len(x[1]) == 4 + assert len(scs) == 48 def test_fit(self): """ @@ -233,30 +241,30 @@ def test_fit(self): """ sm = StructureMatcher() - self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) + assert sm.fit(self.struct_list[0], self.struct_list[1]) # Test rotational/translational invariance op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False, np.array([0.4, 0.7, 0.9])) self.struct_list[1].apply_operation(op) - self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) + assert sm.fit(self.struct_list[0], self.struct_list[1]) # Test failure under large atomic translation self.struct_list[1].translate_sites([0], [0.4, 0.4, 0.2], frac_coords=True) - self.assertFalse(sm.fit(self.struct_list[0], self.struct_list[1])) + assert not sm.fit(self.struct_list[0], self.struct_list[1]) self.struct_list[1].translate_sites([0], [-0.4, -0.4, -0.2], frac_coords=True) # random.shuffle(editor._sites) - self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) + assert sm.fit(self.struct_list[0], self.struct_list[1]) # Test FrameworkComporator sm2 = StructureMatcher(comparator=FrameworkComparator()) lfp = self.get_structure("LiFePO4") nfp = self.get_structure("NaFePO4") - self.assertTrue(sm2.fit(lfp, nfp)) - self.assertFalse(sm.fit(lfp, nfp)) + assert sm2.fit(lfp, nfp) + assert not sm.fit(lfp, nfp) # Test anonymous fit. - self.assertEqual(sm.fit_anonymous(lfp, nfp), True) - self.assertAlmostEqual(sm.get_rms_anonymous(lfp, nfp)[0], 0.060895871160262717) + assert sm.fit_anonymous(lfp, nfp) is True + assert sm.get_rms_anonymous(lfp, nfp)[0] == approx(0.060895871160262717) # Test partial occupancies. s1 = Structure( @@ -269,16 +277,16 @@ def test_fit(self): [{"Fe": 0.25}, {"Fe": 0.5}, {"Fe": 0.5}, {"Fe": 0.75}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]], ) - self.assertFalse(sm.fit(s1, s2)) - self.assertFalse(sm.fit(s2, s1)) + assert not sm.fit(s1, s2) + assert not sm.fit(s2, s1) s2 = Structure( Lattice.cubic(3), [{"Mn": 0.5}, {"Mn": 0.5}, {"Mn": 0.5}, {"Mn": 0.5}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]], ) - self.assertEqual(sm.fit_anonymous(s1, s2), True) + assert sm.fit_anonymous(s1, s2) is True - self.assertAlmostEqual(sm.get_rms_anonymous(s1, s2)[0], 0) + assert sm.get_rms_anonymous(s1, s2)[0] == approx(0) # test symmetric sm_coarse = sm = StructureMatcher( @@ -290,35 +298,35 @@ def test_fit(self): s1 = Structure.from_file(PymatgenTest.TEST_FILES_DIR / "fit_symm_s1.vasp") s2 = Structure.from_file(PymatgenTest.TEST_FILES_DIR / "fit_symm_s2.vasp") - self.assertEqual(sm_coarse.fit(s1, s2), True) - self.assertEqual(sm_coarse.fit(s2, s1), False) - self.assertEqual(sm_coarse.fit(s1, s2, symmetric=True), False) - self.assertEqual(sm_coarse.fit(s2, s1, symmetric=True), False) + assert sm_coarse.fit(s1, s2) is True + assert sm_coarse.fit(s2, s1) is False + assert sm_coarse.fit(s1, s2, symmetric=True) is False + assert sm_coarse.fit(s2, s1, symmetric=True) is False def test_oxi(self): """Test oxidation state removal matching""" sm = StructureMatcher() - self.assertFalse(sm.fit(self.oxi_structs[0], self.oxi_structs[1])) + assert not sm.fit(self.oxi_structs[0], self.oxi_structs[1]) sm = StructureMatcher(comparator=ElementComparator()) - self.assertTrue(sm.fit(self.oxi_structs[0], self.oxi_structs[1])) + assert sm.fit(self.oxi_structs[0], self.oxi_structs[1]) def test_primitive(self): """Test primitive cell reduction""" sm = StructureMatcher(primitive_cell=True) self.struct_list[1].make_supercell([[2, 0, 0], [0, 3, 0], [0, 0, 1]]) - self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) + assert sm.fit(self.struct_list[0], self.struct_list[1]) def test_class(self): # Tests entire class as single working unit sm = StructureMatcher() # Test group_structures and find_indices out = sm.group_structures(self.struct_list) - self.assertEqual(list(map(len, out)), [4, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1]) - self.assertEqual(sum(map(len, out)), len(self.struct_list)) + assert list(map(len, out)) == [4, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1] + assert sum(map(len, out)) == len(self.struct_list) for s in self.struct_list[::2]: s.replace_species({"Ti": "Zr", "O": "Ti"}) out = sm.group_structures(self.struct_list, anonymous=True) - self.assertEqual(list(map(len, out)), [4, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1]) + assert list(map(len, out)) == [4, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1] def test_mix(self): structures = [ @@ -333,15 +341,15 @@ def test_mix(self): for g in groups: formula = g[0].composition.reduced_formula if formula in ["Li2O", "LiFePO4"]: - self.assertEqual(len(g), 2) + assert len(g) == 2 else: - self.assertEqual(len(g), 1) + assert len(g) == 1 def test_left_handed_lattice(self): """Ensure Left handed lattices are accepted""" sm = StructureMatcher() s = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Li3GaPCO7.json")) - self.assertTrue(sm.fit(s, s)) + assert sm.fit(s, s) def test_as_dict_and_from_dict(self): sm = StructureMatcher( @@ -354,25 +362,25 @@ def test_as_dict_and_from_dict(self): ) d = sm.as_dict() sm2 = StructureMatcher.from_dict(d) - self.assertEqual(sm2.as_dict(), d) + assert sm2.as_dict() == d def test_no_scaling(self): sm = StructureMatcher(ltol=0.1, stol=0.1, angle_tol=2, scale=False, comparator=ElementComparator()) - self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) + assert sm.fit(self.struct_list[0], self.struct_list[1]) - self.assertTrue(sm.get_rms_dist(self.struct_list[0], self.struct_list[1])[0] < 0.0008) + assert sm.get_rms_dist(self.struct_list[0], self.struct_list[1])[0] < 0.0008 def test_supercell_fit(self): sm = StructureMatcher(attempt_supercell=False) s1 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Al3F9.json")) s2 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Al3F9_distorted.json")) - self.assertFalse(sm.fit(s1, s2)) + assert not sm.fit(s1, s2) sm = StructureMatcher(attempt_supercell=True) - self.assertTrue(sm.fit(s1, s2)) - self.assertTrue(sm.fit(s2, s1)) + assert sm.fit(s1, s2) + assert sm.fit(s2, s1) def test_get_lattices(self): sm = StructureMatcher( @@ -389,13 +397,13 @@ def test_get_lattices(self): s2 = Structure(l2, [], []) lattices = list(sm._get_lattices(s=s1, target_lattice=s2.lattice)) - self.assertEqual(len(lattices), 16) + assert len(lattices) == 16 l3 = Lattice.from_parameters(1.1, 2, 20, 89, 91, 90) s3 = Structure(l3, [], []) lattices = list(sm._get_lattices(s=s1, target_lattice=s3.lattice)) - self.assertEqual(len(lattices), 0) + assert len(lattices) == 0 def test_find_match1(self): sm = StructureMatcher( @@ -416,10 +424,10 @@ def test_find_match1(self): s2.make_supercell(scale_matrix) fc = s2.frac_coords + match[3] fc -= np.round(fc) - self.assertAlmostEqual(np.sum(fc), 0.9) - self.assertAlmostEqual(np.sum(fc[:, :2]), 0.1) + assert np.sum(fc) == approx(0.9) + assert np.sum(fc[:, :2]) == approx(0.1) cart_dist = np.sum(match[1] * (l.volume / 3) ** (1 / 3)) - self.assertAlmostEqual(cart_dist, 0.15) + assert cart_dist == approx(0.15) def test_find_match2(self): sm = StructureMatcher( @@ -441,8 +449,8 @@ def test_find_match2(self): s2.make_supercell(scale_matrix) s2.translate_sites(range(len(s2)), match[3]) - self.assertAlmostEqual(np.sum(s2.frac_coords) % 1, 0.3) - self.assertAlmostEqual(np.sum(s2.frac_coords[:, :2]) % 1, 0) + assert np.sum(s2.frac_coords) % 1 == approx(0.3) + assert np.sum(s2.frac_coords[:, :2]) % 1 == approx(0) def test_supercell_subsets(self): sm = StructureMatcher( @@ -476,52 +484,52 @@ def test_supercell_subsets(self): # test when s1 is exact supercell of s2 result = sm.get_s2_like_s1(s1, s2) for a, b in zip(s1, result): - self.assertTrue(a.distance(b) < 0.08) - self.assertEqual(a.species, b.species) + assert a.distance(b) < 0.08 + assert a.species == b.species - self.assertTrue(sm.fit(s1, s2)) - self.assertTrue(sm.fit(s2, s1)) - self.assertTrue(sm_no_s.fit(s1, s2)) - self.assertTrue(sm_no_s.fit(s2, s1)) + assert sm.fit(s1, s2) + assert sm.fit(s2, s1) + assert sm_no_s.fit(s1, s2) + assert sm_no_s.fit(s2, s1) rms = (0.048604032430991401, 0.059527539448807391) - self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms)) - self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms)) + assert np.allclose(sm.get_rms_dist(s1, s2), rms) + assert np.allclose(sm.get_rms_dist(s2, s1), rms) # test when the supercell is a subset of s2 subset_supercell = s1.copy() del subset_supercell[0] result = sm.get_s2_like_s1(subset_supercell, s2) - self.assertEqual(len(result), 6) + assert len(result) == 6 for a, b in zip(subset_supercell, result): - self.assertTrue(a.distance(b) < 0.08) - self.assertEqual(a.species, b.species) + assert a.distance(b) < 0.08 + assert a.species == b.species - self.assertTrue(sm.fit(subset_supercell, s2)) - self.assertTrue(sm.fit(s2, subset_supercell)) - self.assertFalse(sm_no_s.fit(subset_supercell, s2)) - self.assertFalse(sm_no_s.fit(s2, subset_supercell)) + assert sm.fit(subset_supercell, s2) + assert sm.fit(s2, subset_supercell) + assert not sm_no_s.fit(subset_supercell, s2) + assert not sm_no_s.fit(s2, subset_supercell) rms = (0.053243049896333279, 0.059527539448807336) - self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2), rms)) - self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell), rms)) + assert np.allclose(sm.get_rms_dist(subset_supercell, s2), rms) + assert np.allclose(sm.get_rms_dist(s2, subset_supercell), rms) # test when s2 (once made a supercell) is a subset of s1 s2_missing_site = s2.copy() del s2_missing_site[1] result = sm.get_s2_like_s1(s1, s2_missing_site) for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result): - self.assertTrue(a.distance(b) < 0.08) - self.assertEqual(a.species, b.species) + assert a.distance(b) < 0.08 + assert a.species == b.species - self.assertTrue(sm.fit(s1, s2_missing_site)) - self.assertTrue(sm.fit(s2_missing_site, s1)) - self.assertFalse(sm_no_s.fit(s1, s2_missing_site)) - self.assertFalse(sm_no_s.fit(s2_missing_site, s1)) + assert sm.fit(s1, s2_missing_site) + assert sm.fit(s2_missing_site, s1) + assert not sm_no_s.fit(s1, s2_missing_site) + assert not sm_no_s.fit(s2_missing_site, s1) rms = (0.029763769724403633, 0.029763769724403987) - self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms)) - self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms)) + assert np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms) + assert np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms) def test_get_s2_large_s2(self): sm = StructureMatcher( @@ -545,7 +553,7 @@ def test_get_s2_large_s2(self): result = sm.get_s2_like_s1(s1, s2) for x, y in zip(s1, result): - self.assertLess(x.distance(y), 0.08) + assert x.distance(y) < 0.08 def test_get_mapping(self): sm = StructureMatcher( @@ -568,16 +576,17 @@ def test_get_mapping(self): s2.make_supercell([2, 1, 1]) # equal sizes for i, x in enumerate(sm.get_mapping(s1, s2)): - self.assertEqual(s1[x].species, s2[i].species) + assert s1[x].species == s2[i].species del s1[0] # s1 is subset of s2 for i, x in enumerate(sm.get_mapping(s2, s1)): - self.assertEqual(s1[i].species, s2[x].species) + assert s1[i].species == s2[x].species # s2 is smaller than s1 del s2[0] del s2[1] - self.assertRaises(ValueError, sm.get_mapping, s2, s1) + with pytest.raises(ValueError): + sm.get_mapping(s2, s1) def test_get_supercell_matrix(self): sm = StructureMatcher( @@ -595,14 +604,14 @@ def test_get_supercell_matrix(self): s1.make_supercell([2, 1, 1]) s2 = Structure(l, ["Si", "Si", "Ag"], [[0, 0.1, 0], [0, 0.1, -0.95], [-0.7, 0.5, 0.375]]) result = sm.get_supercell_matrix(s1, s2) - self.assertTrue((result == [[-2, 0, 0], [0, 1, 0], [0, 0, 1]]).all()) + assert (result == [[-2, 0, 0], [0, 1, 0], [0, 0, 1]]).all() s1 = Structure(l, ["Si", "Si", "Ag"], [[0, 0, 0.1], [0, 0, 0.2], [0.7, 0.4, 0.5]]) s1.make_supercell([[1, -1, 0], [0, 0, -1], [0, 1, 0]]) s2 = Structure(l, ["Si", "Si", "Ag"], [[0, 0.1, 0], [0, 0.1, -0.95], [-0.7, 0.5, 0.375]]) result = sm.get_supercell_matrix(s1, s2) - self.assertTrue((result == [[-1, -1, 0], [0, 0, -1], [0, 1, 0]]).all()) + assert (result == [[-1, -1, 0], [0, 0, -1], [0, 1, 0]]).all() # test when the supercell is a subset sm = StructureMatcher( @@ -616,7 +625,7 @@ def test_get_supercell_matrix(self): ) del s1[0] result = sm.get_supercell_matrix(s1, s2) - self.assertTrue((result == [[-1, -1, 0], [0, 0, -1], [0, 1, 0]]).all()) + assert (result == [[-1, -1, 0], [0, 0, -1], [0, 1, 0]]).all() def test_subset(self): sm = StructureMatcher( @@ -633,24 +642,24 @@ def test_subset(self): s2 = Structure(l, ["Si", "Ag"], [[0, 0.1, 0], [-0.7, 0.5, 0.4]]) result = sm.get_s2_like_s1(s1, s2) - self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0, 0, 0.1])), 1) - self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0.7, 0.4, 0.5])), 1) + assert len(find_in_coord_list_pbc(result.frac_coords, [0, 0, 0.1])) == 1 + assert len(find_in_coord_list_pbc(result.frac_coords, [0.7, 0.4, 0.5])) == 1 # test with fewer species in s2 s1 = Structure(l, ["Si", "Ag", "Si"], [[0, 0, 0.1], [0, 0, 0.2], [0.7, 0.4, 0.5]]) s2 = Structure(l, ["Si", "Si"], [[0, 0.1, 0], [-0.7, 0.5, 0.4]]) result = sm.get_s2_like_s1(s1, s2) mindists = np.min(s1.lattice.get_all_distances(s1.frac_coords, result.frac_coords), axis=0) - self.assertLess(np.max(mindists), 1e-6) + assert np.max(mindists) < 1e-6 - self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0, 0, 0.1])), 1) - self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0.7, 0.4, 0.5])), 1) + assert len(find_in_coord_list_pbc(result.frac_coords, [0, 0, 0.1])) == 1 + assert len(find_in_coord_list_pbc(result.frac_coords, [0.7, 0.4, 0.5])) == 1 # test with not enough sites in s1 # test with fewer species in s2 s1 = Structure(l, ["Si", "Ag", "Cl"], [[0, 0, 0.1], [0, 0, 0.2], [0.7, 0.4, 0.5]]) s2 = Structure(l, ["Si", "Si"], [[0, 0.1, 0], [-0.7, 0.5, 0.4]]) - self.assertEqual(sm.get_s2_like_s1(s1, s2), None) + assert sm.get_s2_like_s1(s1, s2) is None def test_out_of_cell_s2_like_s1(self): l = Lattice.cubic(5) @@ -658,7 +667,7 @@ def test_out_of_cell_s2_like_s1(self): s2 = Structure(l, ["Si", "Ag", "Si"], [[0, 0, 0.98], [0, 0, 0.99], [0.7, 0.4, 0.5]]) new_s2 = StructureMatcher(primitive_cell=False).get_s2_like_s1(s1, s2) dists = np.sum((s1.cart_coords - new_s2.cart_coords) ** 2, axis=-1) ** 0.5 - self.assertLess(np.max(dists), 0.1) + assert np.max(dists) < 0.1 def test_disordered_primitive_to_ordered_supercell(self): sm_atoms = StructureMatcher( @@ -691,11 +700,12 @@ def test_disordered_primitive_to_ordered_supercell(self): supercell = Structure(ls, ["Na", "Cl"], scoords) supercell.make_supercell([[-1, 1, 0], [0, 1, 1], [1, 0, 0]]) - self.assertFalse(sm_sites.fit(prim, supercell)) - self.assertTrue(sm_atoms.fit(prim, supercell)) + assert not sm_sites.fit(prim, supercell) + assert sm_atoms.fit(prim, supercell) - self.assertRaises(ValueError, sm_atoms.get_s2_like_s1, prim, supercell) - self.assertEqual(len(sm_atoms.get_s2_like_s1(supercell, prim)), 4) + with pytest.raises(ValueError): + sm_atoms.get_s2_like_s1(prim, supercell) + assert len(sm_atoms.get_s2_like_s1(supercell, prim)) == 4 def test_ordered_primitive_to_disordered_supercell(self): sm_atoms = StructureMatcher( @@ -727,8 +737,8 @@ def test_ordered_primitive_to_disordered_supercell(self): s1 = Structure(lp, ["Na", "Cl"], pcoords) s2 = Structure(ls, [{"Na": 0.5}, {"Na": 0.5}, {"Cl": 0.5}, {"Cl": 0.5}], scoords) - self.assertTrue(sm_sites.fit(s1, s2)) - self.assertFalse(sm_atoms.fit(s1, s2)) + assert sm_sites.fit(s1, s2) + assert not sm_atoms.fit(s1, s2) def test_disordered_to_disordered(self): sm_atoms = StructureMatcher( @@ -746,7 +756,7 @@ def test_disordered_to_disordered(self): s1 = Structure(lp, [{"Na": 0.5, "Cl": 0.5}, {"Na": 0.5, "Cl": 0.5}], coords) s2 = Structure(lp, [{"Na": 0.5, "Cl": 0.5}, {"Na": 0.5, "Br": 0.5}], coords) - self.assertFalse(sm_atoms.fit(s1, s2)) + assert not sm_atoms.fit(s1, s2) def test_occupancy_comparator(self): @@ -768,31 +778,28 @@ def test_occupancy_comparator(self): comparator=OccupancyComparator(), ) - self.assertTrue(sm_sites.fit(s1, s2)) - self.assertFalse(sm_sites.fit(s1, s3)) + assert sm_sites.fit(s1, s2) + assert not sm_sites.fit(s1, s3) def test_electronegativity(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5) s1 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Na2Fe2PAsO4S4.json")) s2 = Structure.from_file(os.path.join(PymatgenTest.TEST_FILES_DIR, "Na2Fe2PNO4Se4.json")) - self.assertEqual( - sm.get_best_electronegativity_anonymous_mapping(s1, s2), - { - Element("S"): Element("Se"), - Element("As"): Element("N"), - Element("Fe"): Element("Fe"), - Element("Na"): Element("Na"), - Element("P"): Element("P"), - Element("O"): Element("O"), - }, - ) - self.assertEqual(len(sm.get_all_anonymous_mappings(s1, s2)), 2) + assert sm.get_best_electronegativity_anonymous_mapping(s1, s2) == { + Element("S"): Element("Se"), + Element("As"): Element("N"), + Element("Fe"): Element("Fe"), + Element("Na"): Element("Na"), + Element("P"): Element("P"), + Element("O"): Element("O"), + } + assert len(sm.get_all_anonymous_mappings(s1, s2)) == 2 # test include_dist dists = {Element("N"): 0, Element("P"): 0.0010725064} for mapping, d in sm.get_all_anonymous_mappings(s1, s2, include_dist=True): - self.assertAlmostEqual(dists[mapping[Element("As")]], d) + assert dists[mapping[Element("As")]] == approx(d) def test_rms_vs_minimax(self): # This tests that structures with adjusted RMS less than stol, but minimax @@ -806,9 +813,9 @@ def test_rms_vs_minimax(self): s2 = Structure(l, sp, [[0.5, 0, 0], [0, 0, 0], [0, 0, 0.6]]) self.assertArrayAlmostEqual(sm.get_rms_dist(s1, s2), (0.32**0.5 / 2, 0.4)) - self.assertEqual(sm.fit(s1, s2), False) - self.assertEqual(sm.fit_anonymous(s1, s2), False) - self.assertEqual(sm.get_mapping(s1, s2), None) + assert sm.fit(s1, s2) is False + assert sm.fit_anonymous(s1, s2) is False + assert sm.get_mapping(s1, s2) is None if __name__ == "__main__": diff --git a/pymatgen/analysis/tests/test_surface_analysis.py b/pymatgen/analysis/tests/test_surface_analysis.py index d58955b22f8..b80b2f70a19 100644 --- a/pymatgen/analysis/tests/test_surface_analysis.py +++ b/pymatgen/analysis/tests/test_surface_analysis.py @@ -5,6 +5,7 @@ import unittest import warnings +from pytest import approx from sympy import Number, Symbol from pymatgen.analysis.surface_analysis import ( @@ -44,7 +45,7 @@ def setUp(self): # Load objects for Cu test self.Cu_entry_dict = get_entry_dict(os.path.join(get_path(""), "Cu_entries.txt")) - self.assertEqual(len(self.Cu_entry_dict), 13) + assert len(self.Cu_entry_dict) == 13 self.Cu_ucell_entry = ComputedStructureEntry.from_dict(self.ucell_entries["Cu"]) # Load dummy MgO slab entries @@ -62,25 +63,24 @@ def test_properties(self): for clean in self.metals_O_entry_dict[el][hkl]: for ads in self.metals_O_entry_dict[el][hkl][clean]: ml = ads.get_unit_primitive_area - self.assertAlmostEqual(ml, 4, 2) - self.assertAlmostEqual(ads.get_monolayer, 1 / 4, 2) + assert ml == approx(4, 2) + assert ads.get_monolayer == approx(1 / 4, 2) Nads = ads.Nads_in_slab - self.assertEqual(Nads, 1) - self.assertEqual(ads.Nsurfs_ads_in_slab, 1) + assert Nads == 1 + assert ads.Nsurfs_ads_in_slab == 1 # Determine the correct binding energy with open(os.path.join(get_path(""), "isolated_O_entry.txt")) as isolated_O_entry: isolated_O_entry = json.loads(isolated_O_entry.read()) O = ComputedStructureEntry.from_dict(isolated_O_entry) gbind = (ads.energy - ml * clean.energy) / Nads - O.energy_per_atom - self.assertEqual(gbind, ads.gibbs_binding_energy()) + assert gbind == ads.gibbs_binding_energy() # Determine the correction Gibbs adsorption energy eads = Nads * gbind - self.assertEqual(eads, ads.gibbs_binding_energy(eads=True)) + assert eads == ads.gibbs_binding_energy(eads=True) se = ads.surface_energy(el_ucell) - self.assertAlmostEqual( - se.as_coefficients_dict()[Symbol("delu_O")], - (-1 / 2) * ads.surface_area ** (-1), + assert se.as_coefficients_dict()[Symbol("delu_O")] == approx( + (-1 / 2) * ads.surface_area ** (-1) ) def test_create_slab_label(self): @@ -91,18 +91,18 @@ def test_create_slab_label(self): for clean in self.metals_O_entry_dict[el][hkl]: label = clean.create_slab_label comp = str(clean.composition.reduced_composition) - self.assertEqual(f"{hkl} {comp}", label) + assert f"{hkl} {comp}" == label for ads in self.metals_O_entry_dict[el][hkl][clean]: label = ads.create_slab_label - self.assertEqual(label, f"{hkl} {comp}+O, 0.250 ML") + assert label == f"{hkl} {comp}+O, 0.250 ML" def test_surface_energy(self): # For a non-stoichiometric case, the chemical potentials do not # cancel out, they serve as a reservoir for any missing atoms for slab_entry in self.MgO_slab_entry_dict[(1, 1, 1)]: se = slab_entry.surface_energy(self.MgO_ucell_entry, ref_entries=[self.Mg_ucell_entry]) - self.assertEqual(tuple(se.as_coefficients_dict()), (Number(1), Symbol("delu_Mg"))) + assert tuple(se.as_coefficients_dict()) == (Number(1), Symbol("delu_Mg")) # For the case of a clean, stoichiometric slab, the surface energy # should be constant (i.e. surface energy is a constant). @@ -119,7 +119,7 @@ def test_surface_energy(self): # The (111) facet should be the most stable clean111_entry = list(self.Cu_entry_dict[(1, 1, 1)])[0] se_Cu111 = clean111_entry.surface_energy(self.Cu_ucell_entry) - self.assertEqual(min(all_se), se_Cu111) + assert min(all_se) == se_Cu111 def test_cleaned_up_slab(self): # The cleaned up slab should have the same reduced formula as a clean slab @@ -128,10 +128,7 @@ def test_cleaned_up_slab(self): for clean in self.metals_O_entry_dict[el][hkl]: for ads in self.metals_O_entry_dict[el][hkl][clean]: s = ads.cleaned_up_slab - self.assertEqual( - s.composition.reduced_composition, - clean.composition.reduced_composition, - ) + assert s.composition.reduced_composition == clean.composition.reduced_composition class SurfaceEnergyPlotterTest(PymatgenTest): @@ -165,20 +162,20 @@ def test_get_stable_entry_at_u(self): # Test that the surface energy is clean for specific range of chempot entry1, gamma1 = plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -7}) entry2, gamma2 = plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -6}) - self.assertEqual(gamma1, gamma2) - self.assertEqual(entry1.label, entry2.label) + assert gamma1 == gamma2 + assert entry1.label == entry2.label # Now test that for a high chempot, adsorption # occurs and gamma is not equal to clean gamma entry3, gamma3 = plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -1}) - self.assertNotEqual(entry3.label, entry2.label) - self.assertNotEqual(gamma3, gamma2) + assert entry3.label != entry2.label + assert gamma3 != gamma2 # For any chempot greater than -6, surface energy should vary # but the configuration should remain the same entry4, gamma4 = plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): 0}) - self.assertEqual(entry3.label, entry4.label) - self.assertNotEqual(gamma3, gamma4) + assert entry3.label == entry4.label + assert gamma3 != gamma4 def test_wulff_from_chempot(self): @@ -197,9 +194,9 @@ def test_wulff_from_chempot(self): ] for hkl in area_frac_dict: if hkl in facets_hkl: - self.assertNotEqual(area_frac_dict[hkl], 0) + assert area_frac_dict[hkl] != 0 else: - self.assertEqual(area_frac_dict[hkl], 0) + assert area_frac_dict[hkl] == 0 for analyzer in self.Oads_analyzer_dict.values(): # Test WulffShape for adsorbed surfaces @@ -211,11 +208,11 @@ def test_wulff_from_chempot(self): # for Ni when adsorption comes into play wulff_neg7 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-7) wulff_neg6 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-6) - self.assertEqual(wulff_neg7.weighted_surface_energy, wulff_neg6.weighted_surface_energy) + assert wulff_neg7.weighted_surface_energy == wulff_neg6.weighted_surface_energy wulff_neg55 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-5.5) - self.assertNotEqual(wulff_neg55.weighted_surface_energy, wulff_neg6.weighted_surface_energy) + assert wulff_neg55.weighted_surface_energy != wulff_neg6.weighted_surface_energy wulff_neg525 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-5.25) - self.assertNotEqual(wulff_neg55.weighted_surface_energy, wulff_neg525.weighted_surface_energy) + assert wulff_neg55.weighted_surface_energy != wulff_neg525.weighted_surface_energy def test_color_palette_dict(self): @@ -235,7 +232,7 @@ def test_get_surface_equilibrium(self): clean111_entry = list(self.Cu_entry_dict[(1, 1, 1)])[0] clean100_entry = list(self.Cu_entry_dict[(1, 0, 0)])[0] soln = self.Cu_analyzer.get_surface_equilibrium([clean111_entry, clean100_entry]) - self.assertFalse(soln) + assert not soln # For adsorbed system, we should find one intercept Pt_entries = self.metals_O_entry_dict["Pt"] @@ -244,13 +241,13 @@ def test_get_surface_equilibrium(self): Pt_analyzer = self.Oads_analyzer_dict["Pt"] soln = Pt_analyzer.get_surface_equilibrium([clean, ads]) - self.assertNotEqual(list(soln.values())[0], list(soln.values())[1]) + assert list(soln.values())[0] != list(soln.values())[1] # Check if the number of parameters for adsorption are correct - self.assertEqual((Symbol("delu_O"), Symbol("gamma")), tuple(soln)) + assert (Symbol("delu_O"), Symbol("gamma")) == tuple(soln) # Adsorbed systems have a b2=(-1*Nads) / (Nsurfs * Aads) se = ads.surface_energy(Pt_analyzer.ucell_entry, Pt_analyzer.ref_entries) - self.assertAlmostEqual(se.as_coefficients_dict()[Symbol("delu_O")], -1 / (2 * ads.surface_area)) + assert se.as_coefficients_dict()[Symbol("delu_O")] == approx(-1 / (2 * ads.surface_area)) def test_stable_u_range_dict(self): analyzer = list(self.Oads_analyzer_dict.values())[-1] @@ -259,7 +256,7 @@ def test_stable_u_range_dict(self): all_u = [] for entry in stable_u_range: all_u.extend(stable_u_range[entry]) - self.assertGreater(len(all_u), 1) + assert len(all_u) > 1 def test_entry_dict_from_list(self): @@ -272,7 +269,7 @@ def test_entry_dict_from_list(self): all_Pt_slab_entries.extend(Pt_entries[hkl][clean]) a = SurfaceEnergyPlotter(all_Pt_slab_entries, self.Pt_analyzer.ucell_entry) - self.assertEqual(type(a).__name__, "SurfaceEnergyPlotter") + assert type(a).__name__ == "SurfaceEnergyPlotter" # def test_monolayer_vs_BE(self): # for el in self.Oads_analyzer_dict: @@ -322,10 +319,10 @@ def setUp(self): def test_shift(self): wf_analyzer_shift = WorkFunctionAnalyzer.from_files(shift=-0.25, blength=3.7, **self.kwargs) - self.assertAlmostEqual(self.wf_analyzer.ave_bulk_p, wf_analyzer_shift.ave_bulk_p, places=0) + assert self.wf_analyzer.ave_bulk_p == approx(wf_analyzer_shift.ave_bulk_p, 0) def test_is_converged(self): - self.assertTrue(self.wf_analyzer.is_converged()) + assert self.wf_analyzer.is_converged() class NanoscaleStabilityTest(PymatgenTest): @@ -356,7 +353,7 @@ def test_stability_at_r(self): fcc_wulff = self.La_fcc_analyzer.wulff_from_chempot() bulk = self.La_fcc_analyzer.ucell_entry gfcc, rfcc = self.nanoscale_stability.wulff_gform_and_r(fcc_wulff, bulk, r + 10, from_sphere_area=True) - self.assertGreater(gfcc, ghcp) + assert gfcc > ghcp # fcc phase of La particle should be the stable # polymorph below the equilibrium radius @@ -366,7 +363,7 @@ def test_stability_at_r(self): fcc_wulff = self.La_fcc_analyzer.wulff_from_chempot() bulk = self.La_fcc_analyzer.ucell_entry gfcc, rfcc = self.nanoscale_stability.wulff_gform_and_r(fcc_wulff, bulk, r - 10, from_sphere_area=True) - self.assertLess(gfcc, ghcp) + assert gfcc < ghcp def test_scaled_wulff(self): # Ensure for a given radius, the effective radius @@ -375,9 +372,9 @@ def test_scaled_wulff(self): fcc_wulff = self.La_fcc_analyzer.wulff_from_chempot() w1 = self.nanoscale_stability.scaled_wulff(hcp_wulff, 10) w2 = self.nanoscale_stability.scaled_wulff(fcc_wulff, 10) - self.assertAlmostEqual(w1.effective_radius, w2.effective_radius) - self.assertAlmostEqual(w1.effective_radius, 10) - self.assertAlmostEqual(10, w2.effective_radius) + assert w1.effective_radius == approx(w2.effective_radius) + assert w1.effective_radius == approx(10) + assert 10 == approx(w2.effective_radius) def get_entry_dict(filename): diff --git a/pymatgen/analysis/tests/test_transition_state.py b/pymatgen/analysis/tests/test_transition_state.py index 1c4324a0512..abab8c7fb54 100644 --- a/pymatgen/analysis/tests/test_transition_state.py +++ b/pymatgen/analysis/tests/test_transition_state.py @@ -47,12 +47,12 @@ def runTest(self): self.assertArrayAlmostEqual(neb_analysis1.r, neb_analysis1_from_dict.r) self.assertArrayAlmostEqual(neb_analysis1.energies, neb_analysis1_from_dict.energies) self.assertArrayAlmostEqual(neb_analysis1.forces, neb_analysis1_from_dict.forces) - self.assertEqual(neb_analysis1.structures, neb_analysis1_from_dict.structures) + assert neb_analysis1.structures == neb_analysis1_from_dict.structures self.assertArrayAlmostEqual(neb_analysis1.r, neb_analysis1_from_json_data.r) self.assertArrayAlmostEqual(neb_analysis1.energies, neb_analysis1_from_json_data.energies) self.assertArrayAlmostEqual(neb_analysis1.forces, neb_analysis1_from_json_data.forces) - self.assertEqual(neb_analysis1.structures, neb_analysis1_from_json_data.structures) + assert neb_analysis1.structures == neb_analysis1_from_json_data.structures self.assertArrayAlmostEqual(neb_analysis1.get_extrema()[1][0], (0.50023335723480078, 325.20043063935128)) diff --git a/pymatgen/analysis/tests/test_wulff.py b/pymatgen/analysis/tests/test_wulff.py index 86ef25157c6..52db3747ed1 100644 --- a/pymatgen/analysis/tests/test_wulff.py +++ b/pymatgen/analysis/tests/test_wulff.py @@ -4,6 +4,8 @@ import os import unittest +from pytest import approx + from pymatgen.analysis.wulff import WulffShape from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure @@ -119,7 +121,7 @@ def consistency_tests(self): fractional_areas = self.wulff_Ir.area_fraction_dict miller_list = [hkl for hkl in fractional_areas] area_list = [fractional_areas[hkl] for hkl in fractional_areas] - self.assertEqual(miller_list[area_list.index(max(area_list))], (1, 1, 1)) + assert miller_list[area_list.index(max(area_list))] == (1, 1, 1) # Overall weighted surface energy of fcc Nb should be # equal to the energy of the (310) surface, ie. fcc Nb @@ -129,14 +131,11 @@ def consistency_tests(self): Nb_area_fraction_dict = self.wulff_Nb.area_fraction_dict for hkl in Nb_area_fraction_dict: if hkl == (3, 1, 0): - self.assertEqual(Nb_area_fraction_dict[hkl], 1) + assert Nb_area_fraction_dict[hkl] == 1 else: - self.assertEqual(Nb_area_fraction_dict[hkl], 0) + assert Nb_area_fraction_dict[hkl] == 0 - self.assertEqual( - self.wulff_Nb.miller_energy_dict[(3, 1, 0)], - self.wulff_Nb.weighted_surface_energy, - ) + assert self.wulff_Nb.miller_energy_dict[(3, 1, 0)] == self.wulff_Nb.weighted_surface_energy def symmetry_test(self): @@ -148,18 +147,18 @@ def symmetry_test(self): check_symmetry_Nb = self.symm_check(self.ucell_Nb, self.wulff_Nb.wulff_pt_list) check_symmetry_Ir = self.symm_check(self.ucell_Ir, self.wulff_Ir.wulff_pt_list) check_symmetry_Ti = self.symm_check(self.ucell_Ti, self.wulff_Ti.wulff_pt_list) - self.assertTrue(check_symmetry_Nb) - self.assertTrue(check_symmetry_Ir) - self.assertTrue(check_symmetry_Ti) + assert check_symmetry_Nb + assert check_symmetry_Ir + assert check_symmetry_Ti def test_get_azimuth_elev(self): # Test out the viewing of the Wulff shape from Miller indices. azim, elev = self.wulff_Ir._get_azimuth_elev((0, 0, 1)) - self.assertEqual(azim, 0) - self.assertEqual(elev, 90) + assert azim == 0 + assert elev == 90 azim, elev = self.wulff_Ir._get_azimuth_elev((1, 1, 1)) - self.assertAlmostEqual(azim, 45) + assert azim == approx(45) def test_properties(self): @@ -173,12 +172,9 @@ def test_properties(self): } for mp_id, wulff in wulff_shapes.items(): properties = self.surface_properties[mp_id] - self.assertEqual( - round(wulff.weighted_surface_energy, 3), - round(properties["weighted_surface_energy"], 3), - ) - self.assertEqual(round(wulff.shape_factor, 3), round(properties["shape_factor"], 3)) - self.assertEqual(round(wulff.anisotropy, 3), round(properties["surface_anisotropy"], 3)) + assert round(wulff.weighted_surface_energy, 3) == round(properties["weighted_surface_energy"], 3) + assert round(wulff.shape_factor, 3) == round(properties["shape_factor"], 3) + assert round(wulff.anisotropy, 3) == round(properties["surface_anisotropy"], 3) def test_corner_and_edges(self): diff --git a/pymatgen/analysis/tests/test_xps.py b/pymatgen/analysis/tests/test_xps.py index ea3856118d1..9eb03e77f06 100644 --- a/pymatgen/analysis/tests/test_xps.py +++ b/pymatgen/analysis/tests/test_xps.py @@ -12,9 +12,9 @@ def test_from_dos(self): v = Vasprun(PymatgenTest.TEST_FILES_DIR / "vasprun.xml.LiF") dos = v.complete_dos xps = XPS.from_dos(dos) - self.assertEqual(len(xps), 301) + assert len(xps) == 301 xps.smear(0.3) - self.assertEqual(len(xps), 301) + assert len(xps) == 301 if __name__ == "__main__": diff --git a/pymatgen/analysis/topological/tests/test_spillage.py b/pymatgen/analysis/topological/tests/test_spillage.py index 77b7bd0b28b..2d8eb246b22 100644 --- a/pymatgen/analysis/topological/tests/test_spillage.py +++ b/pymatgen/analysis/topological/tests/test_spillage.py @@ -4,6 +4,8 @@ import unittest import warnings +from pytest import approx + from pymatgen.analysis.topological.spillage import SOCSpillage from pymatgen.util.testing import PymatgenTest @@ -23,7 +25,7 @@ def test_spillage_from_vasprun(self): # JVASP-1044 gamma_max = SOCSpillage(wf_noso=wf_noso, wf_so=wf_so).overlap_so_spinpol() - self.assertAlmostEqual(gamma_max, 1.3634111271008775, places=5) + assert gamma_max == approx(1.3634111271008775, 5) if __name__ == "__main__": diff --git a/pymatgen/analysis/xas/tests/test_spectrum.py b/pymatgen/analysis/xas/tests/test_spectrum.py index 91145d0d2c8..97ef36e55e1 100644 --- a/pymatgen/analysis/xas/tests/test_spectrum.py +++ b/pymatgen/analysis/xas/tests/test_spectrum.py @@ -9,7 +9,9 @@ import warnings import numpy as np +import pytest from monty.json import MontyDecoder +from pytest import approx from pymatgen.analysis.xas.spectrum import XAS, site_weighted_spectrum from pymatgen.core import Element @@ -41,22 +43,22 @@ def setUp(self): self.site2_xanes = XAS.from_dict(site2_xanes_dict) def test_e0(self): - self.assertAlmostEqual(7728.565, self.k_xanes.e0) + assert 7728.565 == approx(self.k_xanes.e0) def test_k(self): - self.assertEqual(len(self.k_xanes.x), len(self.k_xanes.k)) - self.assertAlmostEqual(self.k_xanes.e0, self.k_xanes.x[self.k_xanes.k.index(0)]) + assert len(self.k_xanes.x) == len(self.k_xanes.k) + assert self.k_xanes.e0 == approx(self.k_xanes.x[self.k_xanes.k.index(0)]) def test_normalization(self): self.k_xanes.normalize(mode="sum") - self.assertAlmostEqual(1.0, np.sum(self.k_xanes.y)) + assert 1.0 == approx(np.sum(self.k_xanes.y)) def test_add_mul(self): scaled_spect = self.k_xanes + self.k_xanes scaled_spect2 = self.k_xanes * 3 - self.assertTrue(np.allclose(scaled_spect.y, 2 * self.k_xanes.y)) - self.assertTrue(np.allclose(scaled_spect2.y, 3 * self.k_xanes.y)) - self.assertAlmostEqual(0.274302, self.k_xanes.get_interpolated_value(7720.422), 3) + assert np.allclose(scaled_spect.y, 2 * self.k_xanes.y) + assert np.allclose(scaled_spect2.y, 3 * self.k_xanes.y) + assert 0.274302 == approx(self.k_xanes.get_interpolated_value(7720.422), 3) def test_to_from_dict(self): s = XAS.from_dict(self.k_xanes.as_dict()) @@ -67,37 +69,36 @@ def test_attributes(self): self.assertArrayEqual(self.k_xanes.intensity, self.k_xanes.y) def test_str(self): - self.assertIsNotNone(str(self.k_xanes)) + assert str(self.k_xanes) is not None def test_validate(self): y_zeros = np.zeros(len(self.k_xanes.x)) - self.assertRaises( - ValueError, - XAS, - self.k_xanes.x, - y_zeros, - self.k_xanes.structure, - self.k_xanes.absorbing_element, - ) + with pytest.raises(ValueError): + XAS( + self.k_xanes.x, + y_zeros, + self.k_xanes.structure, + self.k_xanes.absorbing_element, + ) def test_stitch_xafs(self): - self.assertRaises(ValueError, XAS.stitch, self.k_xanes, self.k_exafs, mode="invalid") + with pytest.raises(ValueError): + XAS.stitch(self.k_xanes, self.k_exafs, mode="invalid") xafs = XAS.stitch(self.k_xanes, self.k_exafs, mode="XAFS") - self.assertIsInstance(xafs, XAS) - self.assertEqual("XAFS", xafs.spectrum_type) - self.assertEqual(len(xafs.x), 500) - self.assertAlmostEqual(min(xafs.x), min(self.k_xanes.x), 2) - self.assertAlmostEqual(max(xafs.y), max(self.k_xanes.y), 2) - self.assertAlmostEqual( - xafs.x[np.argmax(np.gradient(xafs.y) / np.gradient(xafs.x))], - self.k_xanes.e0, - 2, - ) - self.assertRaises(ValueError, XAS.stitch, self.k_xanes, self.l2_xanes, mode="XAFS") + assert isinstance(xafs, XAS) + assert "XAFS" == xafs.spectrum_type + assert len(xafs.x) == 500 + assert min(xafs.x) == approx(min(self.k_xanes.x), 2) + assert max(xafs.y) == approx(max(self.k_xanes.y), 2) + assert xafs.x[np.argmax(np.gradient(xafs.y) / np.gradient(xafs.x))] == approx(self.k_xanes.e0, 2) + with pytest.raises(ValueError): + XAS.stitch(self.k_xanes, self.l2_xanes, mode="XAFS") self.k_xanes.x = np.zeros(100) - self.assertRaises(ValueError, XAS.stitch, self.k_xanes, self.k_exafs) + with pytest.raises(ValueError): + XAS.stitch(self.k_xanes, self.k_exafs) self.k_xanes.absorbing_element = Element("Pt") - self.assertRaises(ValueError, XAS.stitch, self.k_xanes, self.k_exafs, mode="XAFS") + with pytest.raises(ValueError): + XAS.stitch(self.k_xanes, self.k_exafs, mode="XAFS") def test_stitch_l23(self): self.l2_xanes.y[0] = 0.1 @@ -105,38 +106,35 @@ def test_stitch_l23(self): warnings.simplefilter("always") XAS.stitch(self.l2_xanes, self.l3_xanes, 100, mode="L23") # self.assertEqual(len(w), 6) - self.assertIs(w[-1].category, UserWarning) - self.assertIn("jump", str(w[-1].message)) + assert w[-1].category is UserWarning + assert "jump" in str(w[-1].message) self.l2_xanes = XAS.from_dict(l2_xanes_dict) l23 = XAS.stitch(self.l2_xanes, self.l3_xanes, 100, mode="L23") - self.assertIsInstance(l23, XAS) - self.assertEqual("L23", l23.edge) - self.assertAlmostEqual(min(l23.x), min(self.l3_xanes.x), 3) - self.assertAlmostEqual(max(l23.x), max(self.l3_xanes.x), 3) - self.assertTrue(np.greater_equal(l23.y, self.l2_xanes.y).all()) - self.assertEqual(len(l23.x), 100) + assert isinstance(l23, XAS) + assert "L23" == l23.edge + assert min(l23.x) == approx(min(self.l3_xanes.x), 3) + assert max(l23.x) == approx(max(self.l3_xanes.x), 3) + assert np.greater_equal(l23.y, self.l2_xanes.y).all() + assert len(l23.x) == 100 self.l2_xanes.spectrum_type = "EXAFS" - self.assertRaises(ValueError, XAS.stitch, self.l2_xanes, self.l3_xanes, mode="L23") + with pytest.raises(ValueError): + XAS.stitch(self.l2_xanes, self.l3_xanes, mode="L23") self.l2_xanes.absorbing_element = Element("Pt") - self.assertRaises(ValueError, XAS.stitch, self.l2_xanes, self.l3_xanes, mode="L23") - self.assertRaises(ValueError, XAS.stitch, self.k_xanes, self.l3_xanes, mode="L23") + with pytest.raises(ValueError): + XAS.stitch(self.l2_xanes, self.l3_xanes, mode="L23") + with pytest.raises(ValueError): + XAS.stitch(self.k_xanes, self.l3_xanes, mode="L23") def test_site_weighted_spectrum(self): weighted_spectrum = site_weighted_spectrum([self.site1_xanes, self.site2_xanes]) - self.assertIsInstance(weighted_spectrum, XAS) - self.assertTrue(len(weighted_spectrum.x), 500) + assert isinstance(weighted_spectrum, XAS) + assert len(weighted_spectrum.x), 500 # The site multiplicities for site1 and site2 are 4 and 2, respectively. - self.assertAlmostEqual( - weighted_spectrum.y[0], - (4 * self.site1_xanes.y[0] + 2 * self.site2_xanes.y[0]) / 6, - 2, - ) - self.assertEqual( - min(weighted_spectrum.x), - max(min(self.site1_xanes.x), min(self.site2_xanes.x)), - ) + assert weighted_spectrum.y[0] == approx((4 * self.site1_xanes.y[0] + 2 * self.site2_xanes.y[0]) / 6, 2) + assert min(weighted_spectrum.x) == max(min(self.site1_xanes.x), min(self.site2_xanes.x)) self.site2_xanes.absorbing_index = self.site1_xanes.absorbing_index - self.assertRaises(ValueError, site_weighted_spectrum, [self.site1_xanes, self.site2_xanes]) + with pytest.raises(ValueError): + site_weighted_spectrum([self.site1_xanes, self.site2_xanes]) if __name__ == "__main__": diff --git a/pymatgen/io/vasp/outputs.py b/pymatgen/io/vasp/outputs.py index 10fa330fbec..2ddd27fb40d 100644 --- a/pymatgen/io/vasp/outputs.py +++ b/pymatgen/io/vasp/outputs.py @@ -4991,7 +4991,7 @@ def get_parchg( a pymatgen.io.vasp.outputs.Chgcar object """ if phase and not np.all(self.kpoints[kpoint] == 0.0): - warnings.warn("phase == True should only be used for the Gamma kpoint! I hope you know what you're doing!") + warnings.warn("phase is True should only be used for the Gamma kpoint! I hope you know what you're doing!") # scaling of ng for the fft grid, need to restore value at the end temp_ng = self.ng diff --git a/pymatgen/io/vasp/sets.py b/pymatgen/io/vasp/sets.py index f05ab386479..6f80e0779f1 100644 --- a/pymatgen/io/vasp/sets.py +++ b/pymatgen/io/vasp/sets.py @@ -3056,7 +3056,7 @@ def get_valid_magmom_struct(structure, inplace=True, spin_mode="auto"): - none: Remove all the magmom information Returns: - New structure if inplace == False + New structure if inplace is False """ default_values = {"s": 1.0, "v": [1.0, 1.0, 1.0], "n": None} if spin_mode[0].lower() == "a":