Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix lll_reduce for slab generation #3927

Merged
merged 49 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
70628b4
refactory
jinlhr542 Jul 17, 2024
d0e7820
Revert "refactory"
jinlhr542 Jul 17, 2024
6ef29c5
Update surface and interface generating functions
jinlhr542 Jul 17, 2024
765ddc1
1
jinlhr542 Jul 17, 2024
8df0bd5
pre-commit auto-fixes
pre-commit-ci[bot] Jul 17, 2024
363cd2d
Revert "pre-commit auto-fixes"
jinlhr542 Jul 17, 2024
9e0b208
pre-commit auto-fixes
pre-commit-ci[bot] Jul 17, 2024
6dd38e6
spelling
jinlhr542 Jul 17, 2024
0c453e9
Merge branch 'master' of https://github.com/jinlhr542/pymatgen
jinlhr542 Jul 17, 2024
7dea8e0
Allowing not to filter out symmetrical slabs when determining termina…
jinlhr542 Aug 7, 2024
da1c4e7
pre-commit auto-fixes
pre-commit-ci[bot] Aug 7, 2024
b729f2b
fixing too long sentence
jinlhr542 Aug 7, 2024
3ed4f7b
Update surface.py
jinlhr542 Aug 7, 2024
1d04f88
pre-commit auto-fixes
pre-commit-ci[bot] Aug 7, 2024
4cadfdd
add test into test_surface.py
jinlhr542 Aug 8, 2024
d19e480
Merge branch 'master' into master
jinlhr542 Aug 8, 2024
0a95c3c
pre-commit auto-fixes
pre-commit-ci[bot] Aug 8, 2024
10907e3
Update test_surface.py
jinlhr542 Aug 8, 2024
5fad587
Merge branch 'master' of https://github.com/jinlhr542/pymatgen
jinlhr542 Aug 8, 2024
508c48b
pre-commit auto-fixes
pre-commit-ci[bot] Aug 8, 2024
b2b1c78
Update test_surface.py
jinlhr542 Aug 8, 2024
12f0b39
Update test_coherent_interface.py
jinlhr542 Aug 8, 2024
db9dddc
Update test_coherent_interface.py
jinlhr542 Aug 8, 2024
f9bb6fc
pre-commit auto-fixes
pre-commit-ci[bot] Aug 8, 2024
83d0b4a
Update test_coherent_interface.py
jinlhr542 Aug 8, 2024
dcfeb00
Merge branch 'master' of https://github.com/jinlhr542/pymatgen
jinlhr542 Aug 8, 2024
445c9ef
pre-commit auto-fixes
pre-commit-ci[bot] Aug 8, 2024
45917a7
Update test_coherent_interface.py
jinlhr542 Aug 8, 2024
e5000e8
Merge branch 'master' of https://github.com/jinlhr542/pymatgen
jinlhr542 Aug 8, 2024
bcfbacd
pre-commit auto-fixes
pre-commit-ci[bot] Aug 8, 2024
fab9b91
Update test_coherent_interface.py
jinlhr542 Aug 9, 2024
a9c951e
Update test_coherent_interface.py
jinlhr542 Aug 9, 2024
9343cb5
pre-commit auto-fixes
pre-commit-ci[bot] Aug 9, 2024
fddb185
Update src/pymatgen/core/surface.py
jinlhr542 Sep 5, 2024
22b3660
minor document
jinlhr542 Sep 5, 2024
fcc0e56
pre-commit auto-fixes
pre-commit-ci[bot] Sep 5, 2024
3a7e7fc
minor documentation correction
jinlhr542 Sep 5, 2024
92900f6
Merge branch 'master' of https://github.com/jinlhr542/pymatgen
jinlhr542 Sep 5, 2024
5120509
minor correction
jinlhr542 Sep 5, 2024
32f7b13
minor correction
jinlhr542 Sep 5, 2024
e2bf731
minor correction
jinlhr542 Sep 5, 2024
5e4199d
minor correction
jinlhr542 Sep 5, 2024
7e379bc
rename t_index into t_idx
jinlhr542 Sep 9, 2024
e024635
pre-commit auto-fixes
pre-commit-ci[bot] Sep 9, 2024
fc8ef07
fix mypy errors and tweak doc strings
janosh Sep 9, 2024
fc585e1
Merge branch 'master' into master
jinlhr542 Sep 11, 2024
e90bf50
pre-commit auto-fixes
pre-commit-ci[bot] Sep 11, 2024
9704888
update conflict files
jinlhr542 Sep 11, 2024
c793a61
Merge branch 'master' of https://github.com/jinlhr542/pymatgen
jinlhr542 Sep 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions src/pymatgen/analysis/interfaces/coherent_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,32 @@ def __init__(
film_miller: Tuple3Ints,
substrate_miller: Tuple3Ints,
zslgen: ZSLGenerator | None = None,
termination_ftol: float = 0.25,
label_index: bool = False, # necessary to add index to termination
filter_out_sym_slabs: bool = True,
):
"""
Args:
substrate_structure: structure of substrate
film_structure: structure of film
film_miller: miller index of the film layer
substrate_miller: miller index for the substrate layer
zslgen: BiDirectionalZSL if you want custom lattice matching tolerances for coherency.
substrate_structure (Structure): substrate structure
film_structure (Structure): film structure
film_miller (tuple[int, int, int]): miller index for the film layer
substrate_miller (tuple[int, int, int]): miller index for the substrate layer
zslgen (ZSLGenerator | None): BiDirectionalZSL if you want custom lattice matching tolerances for coherency.
termination_ftol (float): tolerance to distinguish different terminating atomic planes.
label_index (bool): If True add an extra index at the beginning of the termination label.
filter_out_sym_slabs (bool): If True filter out identical slabs with different terminations.
This might need to be set as False to find more non-identical terminations because slab
identity separately does not mean combinational identity.
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you clarify/rephrase "identity separately does not mean combinational identity". combinational is a rare word. do you mean identity of slab + termination?

Copy link
Contributor Author

@jinlhr542 jinlhr542 Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically, for two combinations (A1, B1) and (A2, B2). 'A1 equal to A2' and 'B1 equal to B2' does not always make (A1, B2) equal to (A2, B2). When making two interfaces if the two set of slabs are related by certain symmetry operations respectively, these operations need to be compatible to make a 'combinational' identity thus identical interfaces.

# Bulk structures
self.substrate_structure = substrate_structure
self.film_structure = film_structure
self.film_miller = film_miller
self.substrate_miller = substrate_miller
self.zslgen = zslgen or ZSLGenerator(bidirectional=True)

self.termination_ftol = termination_ftol
self.label_index = label_index
self.filter_out_sym_slabs = filter_out_sym_slabs
self._find_matches()
self._find_terminations()

Expand Down Expand Up @@ -131,14 +141,24 @@ def _find_terminations(self):
reorient_lattice=False, # This is necessary to not screw up the lattice
)

film_slabs = film_sg.get_slabs()
sub_slabs = sub_sg.get_slabs()

film_slabs = film_sg.get_slabs(ftol=self.termination_ftol, filter_out_sym_slabs=self.filter_out_sym_slabs)
sub_slabs = sub_sg.get_slabs(ftol=self.termination_ftol, filter_out_sym_slabs=self.filter_out_sym_slabs)
film_shifts = [slab.shift for slab in film_slabs]
film_terminations = [label_termination(slab) for slab in film_slabs]

if self.label_index:
film_terminations = [
label_termination(slab, self.termination_ftol, t_idx) for t_idx, slab in enumerate(film_slabs, start=1)
]
else:
film_terminations = [label_termination(slab, self.termination_ftol) for slab in film_slabs]

sub_shifts = [slab.shift for slab in sub_slabs]
sub_terminations = [label_termination(slab) for slab in sub_slabs]
if self.label_index:
sub_terminations = [
label_termination(slab, self.termination_ftol, t_idx) for t_idx, slab in enumerate(sub_slabs, start=1)
]
else:
sub_terminations = [label_termination(slab, self.termination_ftol) for slab in sub_slabs]

self._terminations = {
(film_label, sub_label): (film_shift, sub_shift)
Expand Down
18 changes: 14 additions & 4 deletions src/pymatgen/core/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -2843,8 +2843,14 @@ def from_slabs(
return iface


def label_termination(slab: Structure) -> str:
"""Label the slab surface termination."""
def label_termination(slab: Structure, ftol: float = 0.25, t_idx: int | None = None) -> str:
"""Label the slab surface termination.

Args:
slab (Slab): film or substrate slab to label termination for
ftol (float): tolerance for terminating position hierarchical clustering
t_idx (None | int): if not None, adding an extra index to the termination label output
"""
frac_coords = slab.frac_coords
n = len(frac_coords)

Expand All @@ -2867,7 +2873,7 @@ def label_termination(slab: Structure) -> str:

condensed_m = squareform(dist_matrix)
z = linkage(condensed_m)
clusters = fcluster(z, 0.25, criterion="distance")
clusters = fcluster(z, ftol, criterion="distance")

clustered_sites: dict[int, list[Site]] = {c: [] for c in clusters}
for idx, cluster in enumerate(clusters):
Expand All @@ -2880,7 +2886,11 @@ def label_termination(slab: Structure) -> str:

sp_symbol = SpacegroupAnalyzer(top_plane, symprec=0.1).get_space_group_symbol()
form = top_plane.reduced_formula
return f"{form}_{sp_symbol}_{len(top_plane)}"

if t_idx is None:
return f"{form}_{sp_symbol}_{len(top_plane)}"

return f"{t_idx}_{form}_{sp_symbol}_{len(top_plane)}"


def count_layers(struct: Structure, el: Element | None = None) -> int:
Expand Down
35 changes: 20 additions & 15 deletions src/pymatgen/core/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1134,10 +1134,10 @@ def get_slab(
if self.lll_reduce:
# Sanitize Slab (LLL reduction + site sorting + map frac_coords)
lll_slab = struct.copy(sanitize=True)
struct = lll_slab

# Apply reduction on the scaling factor
mapping = lll_slab.lattice.find_mapping(struct.lattice)
struct = lll_slab
if mapping is None:
raise RuntimeError("LLL reduction has failed")
scale_factor = np.dot(mapping[2], scale_factor)
Expand Down Expand Up @@ -1194,6 +1194,7 @@ def get_slabs(
symmetrize: bool = False,
repair: bool = False,
ztol: float = 0,
filter_out_sym_slabs: bool = True,
) -> list[Slab]:
"""Generate slabs with shift values calculated from the internal
gen_possible_terminations func. If the user decide to avoid breaking
Expand All @@ -1217,6 +1218,7 @@ def get_slabs(
can lead to many more possible slabs.
ztol (float): Fractional tolerance for determine overlapping z-ranges,
smaller ztol might result in more possible Slabs.
filter_out_sym_slabs (bool): If True filter out identical slabs with different terminations.

Returns:
list[Slab]: All possible Slabs of a particular surface,
Expand Down Expand Up @@ -1342,22 +1344,25 @@ def get_z_ranges(
slabs.append(self.repair_broken_bonds(slab=slab, bonds=bonds))

# Filter out surfaces that might be the same
matcher = StructureMatcher(ltol=tol, stol=tol, primitive_cell=False, scale=False)
if filter_out_sym_slabs:
matcher = StructureMatcher(ltol=tol, stol=tol, primitive_cell=False, scale=False)

final_slabs: list[Slab] = []
for group in matcher.group_structures(slabs):
# For each unique slab, symmetrize the
# surfaces by removing sites from the bottom
if symmetrize:
sym_slabs = self.nonstoichiometric_symmetrized_slab(group[0])
final_slabs.extend(sym_slabs)
else:
final_slabs.append(group[0])

final_slabs: list[Slab] = []
for group in matcher.group_structures(slabs):
# For each unique slab, symmetrize the
# surfaces by removing sites from the bottom
# Filter out similar surfaces generated by symmetrization
if symmetrize:
sym_slabs = self.nonstoichiometric_symmetrized_slab(group[0])
final_slabs.extend(sym_slabs)
else:
final_slabs.append(group[0])

# Filter out similar surfaces generated by symmetrization
if symmetrize:
matcher_sym = StructureMatcher(ltol=tol, stol=tol, primitive_cell=False, scale=False)
final_slabs = [group[0] for group in matcher_sym.group_structures(final_slabs)]
matcher_sym = StructureMatcher(ltol=tol, stol=tol, primitive_cell=False, scale=False)
final_slabs = [group[0] for group in matcher_sym.group_structures(final_slabs)]
else:
final_slabs = slabs

return cast(list[Slab], sorted(final_slabs, key=lambda slab: slab.energy))

Expand Down
33 changes: 33 additions & 0 deletions tests/analysis/interfaces/test_coherent_interface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import unittest

from numpy.testing import assert_allclose

from pymatgen.analysis.interfaces.coherent_interfaces import (
Expand All @@ -8,6 +10,9 @@
get_2d_transform,
get_rot_3d_for_2d,
)
from pymatgen.analysis.interfaces.substrate_analyzer import SubstrateAnalyzer
from pymatgen.core.lattice import Lattice
from pymatgen.core.structure import Structure
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from pymatgen.util.testing import PymatgenTest

Expand Down Expand Up @@ -44,3 +49,31 @@ def test_coherent_interface_builder(self):
# SP: this test is super fragile and the result fluctuates between 6, 30 and 42 for
# no apparent reason. The author should fix this.
assert len(list(builder.get_interfaces(termination=("O2_Pmmm_1", "Si_R-3m_1")))) >= 6


class TestCoherentInterfaceBuilder(unittest.TestCase):
def setUp(self):
# build substrate & film structure
basis = [[0, 0, 0], [0.25, 0.25, 0.25]]
self.substrate = Structure(Lattice.cubic(a=5.431), ["Si", "Si"], basis)
self.film = Structure(Lattice.cubic(a=5.658), ["Ge", "Ge"], basis)

def test_termination_searching(self):
sub_analyzer = SubstrateAnalyzer()
matches = list(sub_analyzer.calculate(substrate=self.substrate, film=self.film))
cib = CoherentInterfaceBuilder(
film_structure=self.film,
substrate_structure=self.substrate,
film_miller=matches[0].film_miller,
substrate_miller=matches[0].substrate_miller,
zslgen=sub_analyzer,
termination_ftol=1e-4,
label_index=True,
filter_out_sym_slabs=False,
)
assert cib.terminations == [
("1_Ge_P4/mmm_1", "1_Si_P4/mmm_1"),
("1_Ge_P4/mmm_1", "2_Si_P4/mmm_1"),
("2_Ge_P4/mmm_1", "1_Si_P4/mmm_1"),
("2_Ge_P4/mmm_1", "2_Si_P4/mmm_1"),
], "termination results wrong"
2 changes: 1 addition & 1 deletion tests/core/test_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ def test_previous_reconstructions(self):
assert any(len(match.group_structures([struct, slab])) == 1 for slab in slabs)


class MillerIndexFinderTests(PymatgenTest):
class TestMillerIndexFinder(PymatgenTest):
def setUp(self):
self.cscl = Structure.from_spacegroup("Pm-3m", Lattice.cubic(4.2), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]])
self.Fe = Structure.from_spacegroup("Im-3m", Lattice.cubic(2.82), ["Fe"], [[0, 0, 0]])
Expand Down