MaterSim / PyXtal

A code to generate atomic structure with symmetry
MIT License
270 stars 68 forks source link

0D clusters #20

Closed qzhu2017 closed 5 years ago

qzhu2017 commented 6 years ago

Here is the link to crystallographic point groups http://www.cryst.ehu.es/cryst/get_point_genpos.html

However, we also need to take care of those non crystallographic point groups, https://www.staff.ncl.ac.uk/j.p.goss/symmetry/CC_Non-Crystallographic.html

I don't have good way to generalize the scheme so far.

qzhu2017 commented 6 years ago

I think we just need to create a table to store all the symmetry operations in this case.

qzhu2017 commented 6 years ago

@scottfredericks , I suggest you start this code asap.

scottfredericks commented 6 years ago

@qzhu2017 I have added Schoenflies symbol interpretation for point groups within the Group class. The work is not yet finished: right now we can only get the general position via the ops attribute. For point groups with infinite rotational symmetry, the operations do not correspond to the general position right now, since the general position actually only has 1 degree of freedom (all points lie on a line). But for most other point groups, I think we have the general position. The only exception is the icosohedral groups "I" and "Ih". For these groups, I cannot figure out which rotational axes to use for generating the complete symmetry operations. It should be a 2-fold, 3-fold, and 5-fold rotational axis, but when I try to use these as generators in generate_full_symmops, the method just keeps running without terminating. I am not sure if this is a numerical accuracy issue, or if I just have the wrong axes.

As a temporary fix, I am just using the symmetry of a C60 molecule. From rotating the molecule, it looks like the correct generating axes should be: (1,0,0) for the 2-fold axis, (1,1,1) for the 3-fold axis, (0.5, sqrt(3)/2, 0) for the 5-fold axis, but these don't work so far.

I don't yet have a way to get the symmetry and special Wyckoff position information yet, but I am working on it. I should be able to create a random_cluster class even without all of this functionality soon; I just need to define the Wyckoff_positions list for the point group objects; starting out we can just use the general position.

For determining the special Wyckoff positions of point groups, I think the following method may work: 1) Analyze all operations in the point group 2) Find symmetry elements corresponding to each operation (either a plane, line, point, or all of 3d space) 3) Check for intersections among these elements, and temporarily store unique ones Note: The vast majority of intersections will just be (0,0,0), so we can ignore axis-axis intersections 4) check if the symmetry element is symmetric under the point group. If it is, this symmetry element defines a Wyckoff position

For the crystallographic point groups, and for some common non-crystallographic point groups, we can store the Wyckoff positions the same way we do for other group types. For other point groups, we can generate Wyckoff information on the fly. It should also be possible to store the Wyckoff information by symbol instead of by number; this makes sense since as far as I know there are no international numbers for the point groups. I just need to refresh myself on pandas to figure out how to do this.

scottfredericks commented 6 years ago

@qzhu2017 I have fixed the Point Group implementation, and added a random_cluster class to crystal.py. Currently, generation seems to fail for most point groups. I think it is some combination of estimating the volume, and using the correct general position. I will continue to look into it.

Also, for choosing numbers for the point groups, I think the following table should be a good basis: http://symmetry.jacobs-university.de/ I suggest we just number the point groups from left to right, then from top to bottom. Any point groups not in this table can just be generated on the fly. This table is based on the Wikipedia article of the same name.

qzhu2017 commented 6 years ago

@scottfredericks I agree with your suggestion on the numbering of the point groups.

qzhu2017 commented 6 years ago

@scottfredericks I just checked your code. And I believe the reason why the generation fails is because of setting of lattice. You assigned a cubic box in the beginning and then scaled the coordinates based on the approximation of sphere. However, this will only work well for the point groups like Th, Oh, Ih. I would still suggest the use of lattice either tetragonal or cubic. This should be useful for generating structures with Cnv and Cnh groups.

qzhu2017 commented 6 years ago

@scottfredericks http://uspex-team.org/online_utilities/uspex_manual_release/EnglishVersion/uspex_manual_release_eng/sect0038.html Here is the table of point group used in USPEX.

qzhu2017 commented 6 years ago

@scottfredericks I just did some simple tests

>>> from pyxtal.crystal import random_cluster
>>> random_cluster('C3', ['C'], [3], 2.0)
<pyxtal.crystal.random_cluster object at 0x10e9abda0>
>>> random_cluster('C6', ['C'], [6], 2.0)
<pyxtal.crystal.random_cluster object at 0x119c24080>
>>> random_cluster('C6', ['C'], [7], 2.0)
Error: the number is incompatible with the wyckoff sites choice
<pyxtal.crystal.random_cluster object at 0x10e9abe10>
>>> random_cluster('C6', ['C'], [1], 2.0)
Error: the number is incompatible with the wyckoff sites choice
<pyxtal.crystal.random_cluster object at 0x119c240f0>
>>> random_cluster('C6', ['C'], [3], 2.0)
Error: the number is incompatible with the wyckoff sites choice
<pyxtal.crystal.random_cluster object at 0x10e9abe10>
>>> random_cluster('Ih', ['C'], [12], 2.0)
Error: the number is incompatible with the wyckoff sites choice
<pyxtal.crystal.random_cluster object at 0x10e9406a0>

I don't under stand why it failed to generate C6 with 7 atoms and Ih with 12 atoms.

scottfredericks commented 6 years ago

@qzhu2017 Since we currently only have the general position, C6 must have multiples of 6 atoms. Once special positions are added, any number should be possible, since the z axis is a special position. For Ih, the general position has 120 operations.

qzhu2017 commented 6 years ago

@scottfredericks , In this case, shall we add 0D_generator.csv and 0D_symmetry.csv, We can include these crystallographic point groups first, and then add the noncrystallographic point groups later.

scottfredericks commented 6 years ago

@qzhu2017 It seems that for non-crystallographic groups, the issue is numerical tolerance. Because the actual coordinates might be much larger than 1, the numerical difference between the actual and generated points increases as well. As a result, check_wyckoff_position fails to recognize the general position.

Part of this is fixed by changing the lattice so that we can work with fractional coordinates. Still, this does not seem to be enough to make groups O and I/Ih work. Perhaps using values from a list will help to reduce the numerical difference. I will work on getting the Wyckoff position tables for the crystallographic point groups.

scottfredericks commented 6 years ago

@qzhu2017 I have implemented get_point, and get_point_symmetry, which import the Wyckoff positions and site symmetry for point groups. However, I've noticed that there seem to be some errors in the Wyckoff positions listed on Bilbao.

As an example, point group C2h(2/m) lists a Wyckoff position 1o, which consists of (0,0,z). First off, I am not sure where '1o' comes from. Second, (0,0,z) is not a real Wyckoff position, because if we apply inversion (-x,-y,-z), then we would generate a new point. I think it is supposed to be (0,0,0). Here is the page in question: http://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-point_wp-list?num=5

As a result, I am having issues getting the Wyckoff generators. I am not sure how many more of these errors there are. I will try to go through and find as many as I can.

qzhu2017 commented 6 years ago

https://it.iucr.org/Ab/ch10o1v0001/

@scottfredericks This article seems to give very detailed description about the site symmetry and the choices of special directions.

scottfredericks commented 6 years ago

@qzhu2017 With the changes to Bilbao, I have updated the Wyckoff position csv files for the crystallographic point groups. For these groups, it seems we now have no trouble generating clusters with stoichiometry equal to the size of the general position. Other stoichiometries are still slow; this should change with the introduction of spherical and cylindrical lattices.

qzhu2017 commented 6 years ago

@scottfredericks I just checked your code. In this case, it should be easier to deal it with molecule class in Pymatgen, instead of the structure class. In the later, you don't need to think about the PBC conditions.

qzhu2017 commented 6 years ago

@scottfredericks The current code is a bit function, it took some time even for generating 2 atoms with C2 symmetry. It should be much easier. I am not sure if this is because you imposed some unnecessary distance checks for 3D PBC conditions, or because you used some strange lattice.

scottfredericks commented 6 years ago

@qzhu2017 It seems I hadn't committed the implementation of numbers for point groups. It should work now. Also, I changed the way clusters are stored. Instead of .struct, we now use .molecule, which is a pymatgen Molecule object. It should be easier to view the clusters now. I have also added a to_file function for viewing the output structure. This works for crystals and clusters alike.

Try the following code:

from pyxtal.crystal import *
for x in range(1, 33): print(random_cluster(x, ['C'], [len(get_point(x)[0])], 1.0).molecule)

For me, this works. I will add this to test_all.py today. Next, I will focus on the lattice class and non-crystallographic point groups.

scottfredericks commented 6 years ago

@qzhu2017 I have created a Lattice class and implemented it for generating crystal structures. I still need to implement it for other functions, but this is just a matter of extracting the matrix from the class within these functions. Also, I need to:

  1. implement the Lattice class inside of the group class
  2. store the matrix within the Lattice class
  3. Store the lattice class object within the random_crystal classes
scottfredericks commented 6 years ago

@qzhu2017 I am trying to generate the special Wyckoff positions for the icosohedral group, but am still running into problems. It seems that using R3 and R5 do not correctly generate the operations needed for the special Wyckoff positions. I am using the following code:

R2 = SymmOp.from_xyz_string('-x,-y,z')
R3 = SymmOp.from_xyz_string('z,x,y')
tau = 0.5*(sqrt(5)+1)
m = aa2matrix([1., tau, 0.], 2*pi/5)
R5 = SymmOp.from_rotation_and_translation(m, [0,0,0])

pc = SymmOp.from_xyz_string('x,0,0')
pb = SymmOp.from_xyz_string('x,x,x')
pa = SymmOp.from_xyz_string('0,y,'+str(tau))

wd = generate_full_symmops([R2, R3, R5], .03)
wc = [op*pc for op in generate_full_symmops([R3,R5], .03)]
wb = [op*pb for op in generate_full_symmops([R2,R5], .03)]
wa = [op*pa for op in generate_full_symmops([R2,R3], .03)]
wo = [SymmOp.from_xyz_string('0,0,0')]

self.wyckoffs = [wd, wc, wb, wa, wo]

The general position is correct, but the Wyckoff positions c and b have the wrong multiplicities. I think they might not be groups generated by R3 and R5, but I am not sure what the correct generators are. I will keep searching; it might be possible to find generators just by searching subsets of the general position.

Also, I have managed to generate something with the same basic structure as c60, using the general position of I, provided the input point is of the form (0, y, z). This leads me to believe that c60 is indeed the special position 60d of Ih. I used the following code:

from pyxtal.symmetry import *
g = Group('I', dim=0)
p = np.array([0, rand(), rand()])*3.0
points = [op.operate(p) for op in g[0]]
Molecule(['C']*60, points).get_boxed_structure(10,10,10).to('c60.cif')

Eventually, I got two different structure prototypes. One is like normal c60, and the other is like an inverted form of c60, where the the pentagon sides line up with other pentagon sides, rather than the points lining up with points. I have attached these as "c60_1.cif" and "c60_2.cif" respectively.

c60.zip

Also, I ran into the same problem you mentioned. There were atoms inside of the sphere; this is because I was accidentally generating multiple random coordinates for a single Wyckoff position, when I should have just used one. In other words, I accidentally did this:

points = [op.operate([0,rand(),rand()]) for op in wp]

instead of this:

p = [0,rand(),rand()]
points = [op.operate(p) for op in wp]

I'm not sure if this is the same reason for the problem.

scottfredericks commented 6 years ago

@qzhu2017 I have incorporated the special positions for I and Ih. However, for some reason check_wyckoff_position is not working for these groups. Here is an example code:

>>> from pyxtal.crystal import *
>>> g = Group('Ih',dim=0)
>>> p = [rand(),rand(),rand()]
>>> points = [op.operate(p) for op in g[1]]
>>> check_wyckoff_position(points, g)
(False, None)

But, check_wyckoff_position should recognize the points as belonging to a Wyckoff position. I will troubleshoot this issue to see what the cause is. I think it might be the storage of site symmetry; I will check.

qzhu2017 commented 6 years ago

@scottfredericks , the results are cute! I like the structure which you generated. However, I am unable to reproduce the results by running the same code. Perhaps, you did not commit your code completely.

scottfredericks commented 6 years ago

@qzhu2017 I think the problem is solved now. There was an assumption within check_wyckoff_position that held for crystal groups, but not for point groups. Now, I am able to generate Buckminsterfullerene, as well as a few other interesting molecule types. I have made a test script called test_cases/c60.py for this purpose. Here are some example cif files: c60_out_new.zip In particular, I've noticed the structure in c60_2.cif multiple times; it seems to be the most common.

scottfredericks commented 6 years ago

@qzhu2017 After a few more tries, I managed to find a structure with 2 Wyckoff positions using a volume factor of 1: c60_9.cif.zip

qzhu2017 commented 6 years ago

@scottfredericks excellent. I like this script.

scottfredericks commented 6 years ago

@qzhu2017 I have implemented the remaining non-crystallographic groups (including special Wyckoff positions and symmetry). The "D" point groups were a bit complicated; the types of Wyckoff positions seem to depend strongly on the order of the group. Also, I noticed that the symmetry operations for some point groups were in hexagonal coordinates; I have changed things so that we convert into Euclidean coordinates now. The test_all script flags these structures as having incorrect symmetry. But, visually inspecting the output, they seem fine. I think this is just an issue with the way we check the symmetry in test_all.py.

Now I will work on the distance_matrix class and fixing inter-molecular distances.

qzhu2017 commented 6 years ago

@scottfredericks Excellent. Please update the documentation on the use of this function. Also, give the full list of point groups which are supported in PyXtal.