Skip to content

Commit 41e610c

Browse files
committed
a preliminary support on partial information cconstraints
1 parent 145589f commit 41e610c

File tree

3 files changed

+113
-37
lines changed

3 files changed

+113
-37
lines changed

pyxtal/crystal.py

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,14 @@ def set_sites(self, sites):
139139
# Symmetry sites
140140
self.sites = {}
141141
for i, specie in enumerate(self.species):
142-
if sites is not None and sites[i] is not None:
142+
if sites is not None and sites[i] is not None and len(sites[i])>0:
143143
self._check_consistency(sites[i], self.numIons[i])
144-
self.sites[specie] = sites[i]
144+
if type(sites[i][0]) is dict:
145+
self.sites[specie] = []
146+
for item in sites[i][0].items():
147+
self.sites[specie].append({item[0]: item[1]})
148+
else:
149+
self.sites[specie] = sites[i]
145150
else:
146151
self.sites[specie] = None
147152

@@ -292,7 +297,6 @@ def _set_ion_wyckoffs(self, numIon, specie, cell, wyks):
292297
"""
293298
numIon_added = 0
294299
tol = self.tol_matrix.get_tol(specie, specie)
295-
tol_matrix = self.tol_matrix
296300
wyckoff_sites_tmp = []
297301

298302
# Now we start to add the specie to the wyckoff position
@@ -312,51 +316,66 @@ def _set_ion_wyckoffs(self, numIon, specie, cell, wyks):
312316
else: # Selecting the merging
313317
site = None
314318

315-
wp = choose_wyckoff(self.group, numIon - numIon_added, site, self.dim)
316-
if wp is not False:
317-
# Generate a list of coords from ops
318-
mult = wp.multiplicity # remember the original multiplicity
319-
pt = self.lattice.generate_point()
320-
# Merge coordinates if the atoms are close
321-
pt, wp, _ = wp.merge(pt, cell, tol)
322-
# For pure planar structure
323-
if self.dim == 2 and self.thickness is not None and self.thickness < 0.1:
324-
pt[-1] = 0.5
325-
326-
# If site the pre-assigned, do not accept merge
319+
new_site = None
320+
if type(site) is dict: #site with coordinates
321+
key = list(site.keys())[0]
322+
wp = choose_wyckoff(self.group, numIon-numIon_added, key, self.dim)
323+
new_site = atom_site(wp, site[key], specie)
324+
else:
325+
wp = choose_wyckoff(self.group, numIon-numIon_added, site, self.dim)
327326
if wp is not False:
328-
if site is not None and mult != wp.multiplicity:
329-
cycle += 1
330-
continue
331-
# Use a Wyckoff_site object for the current site
332-
new_site = atom_site(wp, pt, specie)
333-
334-
# Check current WP against existing WP's
335-
passed_wp_check = True
336-
for ws in wyckoff_sites_tmp + wyks:
337-
if not new_site.check_with_ws2(ws, cell, tol_matrix):
338-
passed_wp_check = False
339-
340-
if passed_wp_check:
341-
if sites_list is not None:
342-
sites_list.pop(0)
343-
wyckoff_sites_tmp.append(new_site)
344-
numIon_added += new_site.multiplicity
345-
346-
# Check if enough atoms have been added
347-
if numIon_added == numIon:
348-
return wyckoff_sites_tmp
327+
# Generate a list of coords from ops
328+
mult = wp.multiplicity # remember the original multiplicity
329+
pt = self.lattice.generate_point()
330+
# Merge coordinates if the atoms are close
331+
pt, wp, _ = wp.merge(pt, cell, tol)
332+
# For pure planar structure
333+
if self.dim == 2 and self.thickness is not None and self.thickness < 0.1:
334+
pt[-1] = 0.5
335+
336+
# If site the pre-assigned, do not accept merge
337+
if wp is not False:
338+
if site is not None and mult != wp.multiplicity:
339+
cycle += 1
340+
continue
341+
# Use a Wyckoff_site object for the current site
342+
new_site = atom_site(wp, pt, specie)
343+
344+
# Check current WP against existing WP's
345+
if self.check_wp(wyckoff_sites_tmp, wyks, cell, new_site):
346+
if sites_list is not None:
347+
sites_list.pop(0)
348+
wyckoff_sites_tmp.append(new_site)
349+
numIon_added += new_site.multiplicity
350+
351+
# Check if enough atoms have been added
352+
if numIon_added == numIon:
353+
return wyckoff_sites_tmp
349354

350355
cycle += 1
351356
self.numattempts += 1
352357

353358
return None
354359

360+
def check_wp(self, wyckoff_sites_tmp, wyks, cell, new_site):
361+
# Check current WP against existing WP's
362+
if new_site is None:
363+
return False
364+
365+
for ws in wyckoff_sites_tmp + wyks:
366+
if not new_site.check_with_ws2(ws, cell, self.tol_matrix):
367+
return False
368+
return True
369+
355370

356371
def _check_consistency(self, site, numIon):
357372
num = 0
358373
for s in site:
359-
num += int(s[:-1])
374+
if type(s) is dict:
375+
for key in s.keys():
376+
num += int(key[:-1])
377+
else:
378+
num += int(s[:-1])
360379
if numIon == num:
361380
return True
362381
else:
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# generated using pymatgen
2+
data_Al2SiO5
3+
_symmetry_space_group_name_H-M Pnnm
4+
_cell_length_a 7.87578500
5+
_cell_length_b 7.97942000
6+
_cell_length_c 5.61389800
7+
_cell_angle_alpha 90.00000000
8+
_cell_angle_beta 90.00000000
9+
_cell_angle_gamma 90.00000000
10+
_symmetry_Int_Tables_number 58
11+
_chemical_formula_structural Al2SiO5
12+
_chemical_formula_sum 'Al8 Si4 O20'
13+
_cell_volume 352.80090817
14+
_cell_formula_units_Z 4
15+
loop_
16+
_symmetry_equiv_pos_site_id
17+
_symmetry_equiv_pos_as_xyz
18+
1 'x, y, z'
19+
2 '-x, -y, -z'
20+
3 '-x, -y, z'
21+
4 'x, y, -z'
22+
5 'x+1/2, -y+1/2, -z+1/2'
23+
6 '-x+1/2, y+1/2, z+1/2'
24+
7 '-x+1/2, y+1/2, -z+1/2'
25+
8 'x+1/2, -y+1/2, z+1/2'
26+
loop_
27+
_atom_site_type_symbol
28+
_atom_site_label
29+
_atom_site_symmetry_multiplicity
30+
_atom_site_fract_x
31+
_atom_site_fract_y
32+
_atom_site_fract_z
33+
_atom_site_occupancy
34+
Al Al0 4 0.00000000 0.00000000 0.24176400 1
35+
Al Al1 4 0.12942900 0.63918100 0.00000000 1
36+
Si Si2 4 0.24575200 0.25224100 0.00000000 1
37+
O O3 8 0.23075400 0.13422900 0.23971600 1
38+
O O4 4 0.07587800 0.86359200 0.50000000 1
39+
O O5 4 0.07596900 0.86286100 0.00000000 1
40+
O O6 4 0.10253200 0.40066200 0.00000000 1

pyxtal/test_all.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,23 @@ def test_molecular(self):
927927
pmg_s2 = s2.to_pymatgen()
928928
self.assertTrue(sm.StructureMatcher().fit(pmg_s1, pmg_s2))
929929

930+
class TestPartial(unittest.TestCase):
931+
def test_Al2SiO5(self):
932+
cell = Lattice.from_para(7.8758, 7.9794, 5.6139, 90, 90, 90)
933+
spg = 58
934+
elements = ['Al', 'Si', 'O']
935+
composition = [8, 4, 20]
936+
sites = [[{"4e": [0.0000, 0.0000, 0.2418],
937+
"4g": [0.1294, 0.6392, 0.0000],
938+
}],
939+
[{"4g": [0.2458, 0.2522, 0.0000]}],
940+
[], #empty for oxygen
941+
]
942+
943+
s = pyxtal()
944+
s.from_random(3, spg, elements, composition, lattice=cell, sites=sites)
945+
self.assertTrue(s.valid)
946+
930947
class Test_operations(unittest.TestCase):
931948
def test_inverse(self):
932949
coord0 = [0.35, 0.1, 0.4]

0 commit comments

Comments
 (0)