6
6
import math
7
7
from typing import TYPE_CHECKING
8
8
9
+ import numpy as np
10
+ from monty .dev import deprecated
9
11
from pymatgen .analysis .structure_matcher import ElementComparator , StructureMatcher
12
+ from pymatgen .core import Lattice
13
+ from pymatgen .util .coord_cython import is_coord_subset_pbc , pbc_shortest_vectors
14
+ from pyrho .charge_density import ChargeDensity
10
15
11
16
# from ase.build import find_optimal_cell_shape, get_deviation_from_optimal_cell_shape
12
17
# from pymatgen.io.ase import AseAtomsAdaptor
13
18
14
19
if TYPE_CHECKING :
20
+ import numpy .typing as npt
15
21
from numpy .typing import ArrayLike , NDArray
16
22
from pymatgen .core import Structure
23
+ from pymatgen .io .vasp .outputs import VolumetricData
17
24
18
25
__author__ = "Jimmy-Xuan Shen"
19
26
__copyright__ = "Copyright 2022, The Materials Project"
@@ -58,12 +65,13 @@ def get_sc_fromstruct(
58
65
return sc_mat
59
66
60
67
61
- def get_matched_structure_mapping (
68
+ def get_matched_structure_mapping_old (
62
69
uc_struct : Structure , sc_struct : Structure , sm : StructureMatcher | None = None
63
70
):
64
71
"""Get the mapping of the supercell to the unit cell.
65
72
66
- Get the mapping from the supercell defect structure onto the base structure,
73
+ Get the mapping from the supercell structure onto the base structure,
74
+ Note: this only works for structures that are exactly matched.
67
75
68
76
Args:
69
77
uc_struct: host structure, smaller cell
@@ -86,6 +94,38 @@ def get_matched_structure_mapping(
86
94
return sc_m , total_t
87
95
88
96
97
+ @deprecated (message = "This function was reworked in Feb 2024" )
98
+ def get_matched_structure_mapping (
99
+ uc_struct : Structure , sc_struct : Structure , sm : StructureMatcher | None = None
100
+ ):
101
+ """Get the mapping of the supercell to the unit cell.
102
+
103
+ Get the mapping from the supercell structure onto the base structure,
104
+ Note: this only works for structures that are exactly matched.
105
+
106
+ Args:
107
+ uc_struct: host structure, smaller cell
108
+ sc_struct: bigger cell
109
+ sm: StructureMatcher instance
110
+ Returns:
111
+ sc_m : supercell matrix to apply to s1 to get s2
112
+ total_t : translation to apply on s1 * sc_m to get s2
113
+ """
114
+ if sm is None :
115
+ sm = StructureMatcher (
116
+ primitive_cell = False , comparator = ElementComparator (), attempt_supercell = True
117
+ )
118
+ s1 , s2 = sm ._process_species ([sc_struct .copy (), uc_struct .copy ()])
119
+ trans = sm .get_transformation (s1 , s2 )
120
+ if trans is None :
121
+ return None
122
+ sc , t , mapping = trans
123
+ temp = s2 .copy ().make_supercell (sc )
124
+ ii , jj = 0 , mapping [0 ]
125
+ vec = np .round (sc_struct [ii ].frac_coords - temp [jj ].frac_coords )
126
+ return sc , t + vec
127
+
128
+
89
129
def _cubic_cell (
90
130
base_struct : Structure ,
91
131
min_atoms : int = 80 ,
@@ -159,3 +199,66 @@ def _ase_cubic(base_structure, min_atoms: int = 80, max_atoms: int = 240):
159
199
if min_dev [1 ] is None :
160
200
raise RuntimeError ("Could not find a cubic supercell" )
161
201
return min_dev [1 ]
202
+
203
+
204
+ def _avg_lat (l1 , l2 ):
205
+ """Get the average lattice from two lattices."""
206
+ params = (np .array (l1 .parameters ) + np .array (l2 .parameters )) / 2
207
+ return Lattice .from_parameters (* params )
208
+
209
+
210
+ def _lowest_dist (struct , ref_struct ):
211
+ """For each site, return the lowest distance to any site in the reference structure."""
212
+ avg_lat = _avg_lat (struct .lattice , ref_struct .lattice )
213
+ _ , d_2 = pbc_shortest_vectors (
214
+ avg_lat , struct .frac_coords , ref_struct .frac_coords , return_d2 = True
215
+ )
216
+ return np .min (d_2 , axis = 1 )
217
+
218
+
219
+ def get_closest_sc_mat (
220
+ uc_struct : Structure ,
221
+ sc_struct : Structure ,
222
+ sm : StructureMatcher | None = None ,
223
+ debug : bool = False ,
224
+ ):
225
+ """Get the best guess for the supercell matrix that created this defect cell.
226
+
227
+ Args:
228
+ uc_struct: unit cell structure, should be the host structure
229
+ sc_struct: supercell structure, should be the defect structure
230
+ sm: StructureMatcher instance, if None, one will be created with default settings
231
+ debug: bool, if True, return the full list of (distances, lattice, sc_mat) will
232
+ be returned
233
+
234
+ Returns:
235
+ sc_mat: supercell matrix to apply to s1 to get s2
236
+ dist: mean distance between the two structures
237
+ """
238
+ if sm is None :
239
+ sm = StructureMatcher (primitive_cell = False , comparator = ElementComparator ())
240
+
241
+ fu = int (np .round (sc_struct .lattice .volume / uc_struct .lattice .volume ))
242
+ candidate_lattices = tuple (
243
+ sm ._get_lattices (sc_struct .lattice , uc_struct , supercell_size = fu )
244
+ )
245
+
246
+ def _get_mean_dist (lattice , sc_mat ):
247
+ if (
248
+ np .dot (np .cross (lattice .matrix [0 ], lattice .matrix [1 ]), lattice .matrix [2 ])
249
+ < 0
250
+ ):
251
+ return float ("inf" )
252
+ sc2 = uc_struct * sc_mat
253
+ return np .mean (_lowest_dist (sc2 , sc_struct ))
254
+
255
+ _ , best_sc_mat = min (candidate_lattices , key = lambda x : _get_mean_dist (x [0 ], x [1 ]))
256
+ if debug :
257
+ return sorted (
258
+ [
259
+ (_get_mean_dist (lat_ , smat_ ), lat_ , smat_ )
260
+ for lat_ , smat_ in candidate_lattices
261
+ ],
262
+ key = lambda x : x [0 ],
263
+ )
264
+ return best_sc_mat
0 commit comments