diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index bbb2761e..a35c42a9 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -24,6 +24,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -e .[strict] + pip install -e .[dev] pre-commit install - name: Lint @@ -34,7 +35,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11"] env: PMG_MAPI_KEY: ${{ secrets.PMG_MAPI_KEY }} @@ -51,6 +52,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -e .[strict] + pip install -e .[tests] - name: Test run: pytest --cov=pymatgen --cov-report=xml diff --git a/pymatgen/analysis/defects/core.py b/pymatgen/analysis/defects/core.py index bd502a26..ca972be6 100644 --- a/pymatgen/analysis/defects/core.py +++ b/pymatgen/analysis/defects/core.py @@ -13,10 +13,11 @@ from pymatgen.core import Element, PeriodicSite, Species, Structure from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.symmetry.structure import SymmetrizedStructure -from pyrho.utils import get_plane_spacing from pymatgen.analysis.defects.supercells import get_sc_fromstruct +from .utils import get_plane_spacing + # TODO Possible redesign idea: ``DefectSite`` class defined with a defect object. # This makes some of the accounting logic a bit harder since we will probably # just have one concrete ``Defect`` class so you can write custom multiplicity functions @@ -414,7 +415,7 @@ def _guess_oxi_state(self) -> float: f"No common oxidation states found for {self.site.specie}." "Please specify the oxidation state manually." ) - sub_oxi = sub_states[0] + sub_oxi = min(sub_states, key=lambda x: abs(x - rm_oxi)) return sub_oxi - rm_oxi def __repr__(self) -> str: diff --git a/pymatgen/analysis/defects/finder.py b/pymatgen/analysis/defects/finder.py index a7ff97da..2f935eaf 100644 --- a/pymatgen/analysis/defects/finder.py +++ b/pymatgen/analysis/defects/finder.py @@ -228,7 +228,7 @@ def get_soap_vec(struct: Structure) -> NDArray: dummy_structure = struct.copy() for el in species_: dummy_structure.replace_species({str(el): DUMMY_SPECIES}) - soap_desc = SOAP(species=[DUMMY_SPECIES], rcut=5, nmax=8, lmax=6, periodic=True) + soap_desc = SOAP(species=[DUMMY_SPECIES], r_cut=5, n_max=8, l_max=6, periodic=True) vecs = soap_desc.create(adaptor.get_atoms(dummy_structure)) return vecs diff --git a/pymatgen/analysis/defects/utils.py b/pymatgen/analysis/defects/utils.py index 2ff4d4a3..37d48009 100644 --- a/pymatgen/analysis/defects/utils.py +++ b/pymatgen/analysis/defects/utils.py @@ -1043,3 +1043,41 @@ def group_docs( else: for itr, group in enumerate(sgroups): yield f"{h}:{itr}", group + + +def get_plane_spacing(lattice: npt.NDArray) -> list[float]: + """Get the cartesian spacing between periodic planes of a unit cell. + Copied from materialsproject/pyrho + + >>> get_plane_spacing([[1,0,0], [1,1,0], [0,0,2]]) + [0.7653..., 1.042..., 2.0] + + Args: + lattice: + List of lattice vectors in cartesian coordinates + + Returns: + List[float]: + List where the k-th element is is the spacing of planes generated by all + lattice vectors EXCEPT the k-th one + + """ + # get all pairwise projections i must be smaller than j + ndim = len(lattice) + idx_pairs = [*itertools.combinations(range(ndim), 2)] + latt_len = [np.linalg.norm(lattice[i]) for i in range(ndim)] + pproj = { + (i, j): np.dot(lattice[i], lattice[j]) / latt_len[i] / latt_len[j] + for i, j in idx_pairs + } + # get the spacing in each direction: + spacing = [] + for idir in range(ndim): + idir_proj = [ + np.array(lattice[j]) * pproj[tuple(sorted([idir, j]))] # type: ignore + for j in range(ndim) + if j != idir + ] + v_perp_subspace = lattice[idir] - sum(idir_proj) + spacing.append(np.linalg.norm(v_perp_subspace)) + return spacing diff --git a/pyproject.toml b/pyproject.toml index 447d814a..ade6874a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,11 @@ classifiers = [ "Topic :: Other/Nonlisted Topic", "Topic :: Scientific/Engineering", ] -dependencies = ["pymatgen>=2023.5.8", "scikit-image>=0.19.3", "mp-pyrho>=0.3.0", "numpy<=1.23.5"] +dependencies = [ + "pymatgen>=2023.5.8", + "scikit-image>=0.19.3", + "numpy" +] description = "Pymatgen extension for defects analysis" dynamic = ["version"] keywords = ["high-throughput", "automated", "dft", "defects"] @@ -25,7 +29,7 @@ readme = "README.rst" requires-python = '>=3.8' [project.optional-dependencies] -finder = ["dscribe>=1.2.1"] +finder = ["dscribe>=2.0.0"] dev = ["pre-commit>=2.12.1"] docs = [ "jupyter-book>=0.13.1", @@ -33,14 +37,10 @@ docs = [ pydefect = ["pydefect>=0.6.2"] strict = [ - "pymatgen>=2023.5.8", - "dscribe==1.2.2", - "scikit-image==0.20.0", - "pytest==7.2.2", - "pytest-cov==4.0.0", - "pre-commit==3.3.3", - "mp-pyrho==0.3.0", - "numpy==1.23.5" + "pymatgen==2023.7.20", + "dscribe==2.0.0", + "scikit-image==0.21.0", + "numpy==1.24.4", ] tests = ["pytest==7.2.2", "pytest-cov==4.0.0"] diff --git a/tests/test_utils.py b/tests/test_utils.py index 4e2e1170..68c1fdaa 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -12,6 +12,7 @@ get_avg_chg, get_local_extrema, get_localized_states, + get_plane_spacing, group_docs, ) @@ -158,3 +159,8 @@ def get_interstitial(fpos): res.append(defect_names) assert "|".join(sorted(res)) == "N_i|N_i,N_i|v_Ga,v_Ga|v_N,v_N" assert "|".join(sorted(g_names)) == "N_i:0|N_i:1|v_Ga|v_N" + + +def test_plane_spacing(gan_struct): + lattice = gan_struct.lattice.matrix + assert np.allclose(get_plane_spacing(lattice), [2.785, 2.785, 5.239], atol=0.001)