Skip to content

Commit 910e037

Browse files
committed
Merge branch 'master' of github.com:materialsproject/pymatgen
2 parents 05fcb53 + 5049409 commit 910e037

File tree

2 files changed

+44
-26
lines changed

2 files changed

+44
-26
lines changed

src/pymatgen/analysis/local_env.py

+31-26
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from scipy.spatial import Voronoi
2525

2626
from pymatgen.analysis.bond_valence import BV_PARAMS, BVAnalyzer
27-
from pymatgen.analysis.graphs import MoleculeGraph, StructureGraph
27+
from pymatgen.analysis.graphs import StructureGraph
2828
from pymatgen.analysis.molecule_structure_comparator import CovalentRadius
2929
from pymatgen.core import Element, IStructure, PeriodicNeighbor, PeriodicSite, Site, Species, Structure
3030

@@ -38,6 +38,7 @@
3838

3939
from typing_extensions import Self
4040

41+
from pymatgen.analysis.graphs import MoleculeGraph
4142
from pymatgen.core.composition import SpeciesLike
4243
from pymatgen.util.typing import Tuple3Ints
4344

@@ -55,9 +56,11 @@
5556

5657
with open(f"{MODULE_DIR}/op_params.yaml", encoding="utf-8") as file:
5758
DEFAULT_OP_PARAMS: dict[str, dict[str | int, float] | None] = YAML().load(file)
59+
default_op_params = DEFAULT_OP_PARAMS # needed externally
5860

5961
with open(f"{MODULE_DIR}/cn_opt_params.yaml", encoding="utf-8") as file:
6062
CN_OPT_PARAMS: dict[int, dict[str, list[str | dict[str, float]]]] = YAML().load(file)
63+
cn_opt_params = CN_OPT_PARAMS # needed externally
6164

6265
with open(f"{MODULE_DIR}/ionic_radii.json", encoding="utf-8") as file:
6366
_ION_RADII = json.load(file)
@@ -110,9 +113,8 @@ def nearest_key(sorted_vals: list[int], skey: int) -> int:
110113
return sorted_vals[0]
111114
before = sorted_vals[idx - 1]
112115
after = sorted_vals[idx]
113-
if after - skey < skey - before:
114-
return after
115-
return before
116+
117+
return after if after - skey < skey - before else before
116118

117119
for idx, site in enumerate(self._structure):
118120
if isinstance(site.specie, Element):
@@ -214,27 +216,27 @@ def _handle_disorder(structure: Structure, on_disorder: on_disorder_options):
214216
"example since Fe and O have equal occupancy and Fe comes first). 'error' raises an error in case "
215217
f"of disordered structure. Offending {structure = }"
216218
)
217-
if on_disorder.startswith("take_"):
218-
# disordered structures raise AttributeError when passed to NearNeighbors.get_cn()
219-
# or NearNeighbors.get_bonded_structure() (and probably others too, see GH-2070).
220-
# As a workaround, we create a new structure with majority species on each site.
221-
structure = structure.copy() # make a copy so we don't mutate the original structure
222-
for idx, site in enumerate(structure):
223-
max_specie = max(site.species, key=site.species.get)
224-
max_val = site.species[max_specie]
225-
if max_val <= 0.5:
226-
if on_disorder == "take_majority_strict":
227-
raise ValueError(
228-
f"Site {idx} has no majority species, the max species is {max_specie} with occupancy {max_val}"
229-
)
230-
if on_disorder == "take_majority_drop":
231-
continue
232-
233-
# this is the take_max_species case
234-
site.species = max_specie # set site species in copied structure to max specie
235-
else:
219+
if not on_disorder.startswith("take_"):
236220
raise ValueError(f"Unexpected {on_disorder = }, should be one of {get_args(on_disorder_options)}")
237221

222+
# disordered structures raise AttributeError when passed to NearNeighbors.get_cn()
223+
# or NearNeighbors.get_bonded_structure() (and probably others too, see GH-2070).
224+
# As a workaround, we create a new structure with majority species on each site.
225+
structure = structure.copy() # make a copy so we don't mutate the original structure
226+
for idx, site in enumerate(structure):
227+
max_specie = max(site.species, key=site.species.get)
228+
max_val = site.species[max_specie]
229+
if max_val <= 0.5:
230+
if on_disorder == "take_majority_strict":
231+
raise ValueError(
232+
f"Site {idx} has no majority species, the max species is {max_specie} with occupancy {max_val}"
233+
)
234+
if on_disorder == "take_majority_drop":
235+
continue
236+
237+
# this is the take_max_species case
238+
site.species = max_specie # set site species in copied structure to max specie
239+
238240
return structure
239241

240242

@@ -1525,6 +1527,9 @@ def get_bonded_structure(self, structure: Structure, decorate: bool = False) ->
15251527
Returns:
15261528
MoleculeGraph: object from pymatgen.analysis.graphs
15271529
"""
1530+
# requires optional dependency which is why it's not a top-level import
1531+
from pymatgen.analysis.graphs import MoleculeGraph
1532+
15281533
if decorate:
15291534
# Decorate all sites in the underlying structure
15301535
# with site properties that provides information on the
@@ -2508,7 +2513,7 @@ def get_q2(self, thetas=None, phis=None):
25082513
imag += pre_y_2_2[idx] * self._sin_n_p[2][idx]
25092514
acc += real * real + imag * imag
25102515

2511-
return math.sqrt(4 * math.pi * acc / (5 * float(n_nn * n_nn)))
2516+
return math.sqrt(4 * math.pi * acc / (5 * float(n_nn**2)))
25122517

25132518
def get_q4(self, thetas=None, phis=None):
25142519
"""
@@ -2617,7 +2622,7 @@ def get_q4(self, thetas=None, phis=None):
26172622
imag += pre_y_4_4[idx] * self._sin_n_p[4][idx]
26182623
acc += real * real + imag * imag
26192624

2620-
return math.sqrt(4 * math.pi * acc / (9 * float(n_nn * n_nn)))
2625+
return math.sqrt(4 * math.pi * acc / (9 * float(n_nn**2)))
26212626

26222627
def get_q6(self, thetas=None, phis=None):
26232628
"""
@@ -2788,7 +2793,7 @@ def get_q6(self, thetas=None, phis=None):
27882793
imag += pre_y_6_6[idx] * self._sin_n_p[6][idx]
27892794
acc += real * real + imag * imag
27902795

2791-
return math.sqrt(4 * math.pi * acc / (13 * float(n_nn * n_nn)))
2796+
return math.sqrt(4 * math.pi * acc / (13 * float(n_nn**2)))
27922797

27932798
def get_type(self, index):
27942799
"""Get type of order parameter at the index provided and

tests/analysis/test_local_env.py

+13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
from pymatgen.analysis.graphs import MoleculeGraph, StructureGraph
1212
from pymatgen.analysis.local_env import (
13+
CN_OPT_PARAMS,
14+
DEFAULT_OP_PARAMS,
1315
BrunnerNNReal,
1416
BrunnerNNReciprocal,
1517
BrunnerNNRelative,
@@ -28,6 +30,8 @@
2830
OpenBabelNN,
2931
ValenceIonicRadiusEvaluator,
3032
VoronoiNN,
33+
cn_opt_params,
34+
default_op_params,
3135
get_neighbors_of_site_with_index,
3236
metal_edge_extender,
3337
on_disorder_options,
@@ -41,6 +45,15 @@
4145
TEST_DIR = f"{TEST_FILES_DIR}/analysis/local_env/fragmenter_files"
4246

4347

48+
def test_opt_params():
49+
assert isinstance(default_op_params, dict)
50+
assert default_op_params == DEFAULT_OP_PARAMS
51+
52+
assert isinstance(cn_opt_params, dict)
53+
assert list(cn_opt_params) == [2, 3, 4, 5, 6, 7, 8, 12]
54+
assert cn_opt_params == CN_OPT_PARAMS
55+
56+
4457
class TestValenceIonicRadiusEvaluator(PymatgenTest):
4558
def setUp(self):
4659
"""Setup MgO rocksalt structure for testing Vacancy."""

0 commit comments

Comments
 (0)