Skip to content

Commit

Permalink
add get_wyckoff_position_from_xyz #239
Browse files Browse the repository at this point in the history
  • Loading branch information
qzhu2017 committed Dec 18, 2023
1 parent e6afab8 commit 145c37c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 19 deletions.
61 changes: 42 additions & 19 deletions pyxtal/symmetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ class Group:
[['4b'], ['8e']],
[['4b'], ['8f']]],
[False, True, True, True, False, True, True, True],
[[[6], [4]], [[6], [3]], [[6], [2]], [[6], [1]],
[[[6], [4]], [[6], [3]], [[6], [2]], [[6], [1]],
[[5], [4]], [[5], [3]], [[5], [2]], [[5], [1]]]
)
Expand Down Expand Up @@ -362,7 +362,7 @@ def get_lattice_dof(self):
dof = 2
else:
dof = 1

return dof


Expand Down Expand Up @@ -462,7 +462,7 @@ def list_wyckoff_combinations(self, numIons, quick=False, max_wp=None, min_wp=No

if max_wp is not None:
N_max = max_wp - (len(numIons) - 1)
max_solutions[max_solutions > N_max] = N_max
max_solutions[max_solutions > N_max] = N_max

list_solutions = []
for i, numIon in enumerate(numIons):
Expand All @@ -488,9 +488,9 @@ def list_wyckoff_combinations(self, numIons, quick=False, max_wp=None, min_wp=No
#print(sub_solutions)#; import sys; sys.exit()
if len(sub_solutions) == 0:
return [], [], []

# Gather all solutions and remove very large number solutions
solutions = np.array(list(itertools.product(*list_solutions)))
solutions = np.array(list(itertools.product(*list_solutions)))
dim1 = solutions.shape[0]
dim2 = np.prod(solutions.shape[1:])
solutions = solutions.reshape([dim1, dim2])
Expand Down Expand Up @@ -560,6 +560,29 @@ def get_wyckoff_position(self, index):
index = index_from_letter(letter, self.wyckoffs, dim=self.dim)
return self.Wyckoff_positions[index]

def get_wyckoff_position_from_xyz(self, xyz, decimals=4):
"""
Returns a single Wyckoff_position object.
Args:
xyz: a trial [x, y, z] coordinate
Returns: a Wyckoff_position object
"""
xyz = np.round(np.array(xyz, dtype=float), decimals=decimals)
xyz -= np.floor(xyz)

for wp in self.Wyckoff_positions:
pos = wp.apply_ops(xyz)
pos -= np.floor(pos)
is_present = np.any(np.all(pos == xyz, axis=1))
if is_present:
if len(pos) == len(np.unique(pos, axis=0)):
return wp

print("Cannot find the suitable wp for the given input")
return None


def get_alternatives(self):
"""
Expand Down Expand Up @@ -1014,7 +1037,7 @@ def search_supergroup_paths(self, H, max_layer=5):

def path_to_subgroup(self, H):
"""
For a given a path, extract the
For a given a path, extract the
a list of (g_types, subgroup_id, spg_number, wp_list (optional))
"""
path_list = []
Expand Down Expand Up @@ -1381,7 +1404,7 @@ def from_group_and_index(group, index, dim=3, use_hall=False, style='pyxtal', wy
elif dim == 1:
PBC = [0, 0, 1]

if wyckoffs is None:
if wyckoffs is None:
wyckoffs = get_wyckoffs(number, dim=dim)

wpdict = {
Expand Down Expand Up @@ -1426,7 +1449,7 @@ def from_symops_wo_group(ops):
def from_symops(ops, G):
"""
search Wyckoff Position by symmetry operations
Args:
ops: a list of symmetry operations
Expand All @@ -1438,11 +1461,11 @@ def from_symops(ops, G):
"""
if isinstance(ops[0], str):
ops = [SymmOp.from_xyz_str(op) for op in ops]

for wp in G:
if wp.has_equivalent_ops(ops):
return wp

if isinstance(ops[0], str):
print(ops)
else:
Expand Down Expand Up @@ -1630,7 +1653,7 @@ def update_hall(self, hall_numbers=None):
self.P = wp2.P
self.P1 = wp2.P1
self.ops = wp2.ops

return True
return False

Expand Down Expand Up @@ -1899,7 +1922,7 @@ def is_standard_setting(self):
self.index = i
self.letter = letter_from_index(i, G_ops, dim=self.dim)
return True

return False

def has_equivalent_ops(self, wp2, tol=1e-3):
Expand Down Expand Up @@ -2198,7 +2221,7 @@ def search_generator_dist(self, pt, lattice=np.eye(3), group=None):
d.append(distance(p0, lattice, PBC=self.PBC))

else: # sites like (x, 0, 0)
if group is not None:
if group is not None:
ops = group[0].ops
else:
ops = get_wyckoffs(self.number, dim=self.dim)[0]
Expand Down Expand Up @@ -2226,7 +2249,7 @@ def search_generator(self, pos, ops=None, tol=1e-2):
pos1: the position that matchs the standard setting
"""

if ops is None:
if ops is None:
ops = get_wyckoffs(self.number, dim=self.dim)[0]

match = False
Expand Down Expand Up @@ -2260,7 +2283,7 @@ def search_all_generators(self, pos, ops=None, tol=1e-2):
Return:
pos1: the position that matchs the standard setting
"""
if ops is None:
if ops is None:
ops = get_wyckoffs(self.number, dim=self.dim)[0]

coords = []
Expand Down Expand Up @@ -2396,7 +2419,7 @@ def choose_wyckoff_mol(G, number, site, orientations, gen_site=True, dim=3):
Args:
G: a pyxtal.symmetry.Group object
number: the number of molecules still needed in the unit cell
orientations: the valid orientations for a given molecule.
orientations: the valid orientations for a given molecule.
gen_site: general WP only
Returns:
Expand All @@ -2411,7 +2434,7 @@ def choose_wyckoff_mol(G, number, site, orientations, gen_site=True, dim=3):
else:
hn = None
return Wyckoff_position.from_group_and_letter(number, site, dim, hn=hn)

elif gen_site or np.random.random() > 0.5: # choose from high to low
for j, wyckoff in enumerate(wyckoffs):
if len(wyckoff[0]) <= number:
Expand Down Expand Up @@ -2573,7 +2596,7 @@ def index_from_letter(letter, group, dim=3):
Args:
letter: The wyckoff letter
group: an unorganized Wyckoff position array or Group object (preferred)
dim: the periodicity dimension of the symmetry group.
dim: the periodicity dimension of the symmetry group.
Returns:
a single index specifying the location of the Wyckoff position.
Expand Down Expand Up @@ -3426,7 +3449,7 @@ def search_cloest_wp(G, wp, op, pos):
closest match is (0.11, 0.11, 0.2)
Args:
G: space group number
G: space group number
wp: Wyckoff object
op: symmetry operation belonging to wp
pos: initial xyz position
Expand Down
18 changes: 18 additions & 0 deletions pyxtal/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ def test_to_subgroup(self):
self.assertTrue(c.valid)


def test_get_wyckoff_position_from_xyz(self):
g = Group(5)
pos = [([0.0, 0.0, 0.0], '2a'),
([0.5, 0.5, 0.5], None),
([0.1, 0.0, 0.1], '4c'),
([0.5, 0.0, 0.0], None),
([0.0, 0.1, 0.0], '2a'),
([0.0, 0.5, 0.5], '2b'),
([0.1, 0.2, 0.3], '4c'),
]
for d in pos:
(p0, wp0) = d
wp = g.get_wyckoff_position_from_xyz(p0)
if wp is None:
self.assertTrue(wp0 is None)
else:
self.assertTrue(wp.get_label() == wp0)

class TestSupergroup(unittest.TestCase):

def test_supergroup(self):
Expand Down

0 comments on commit 145c37c

Please sign in to comment.