MaterSim / PyXtal

A code to generate atomic structure with symmetry
MIT License
247 stars 65 forks source link

Profiling #32

Closed qzhu2017 closed 5 years ago

qzhu2017 commented 6 years ago

I am still not very happy about the efficiency. By running the tests on molecular_3D. It takes a lot of time for some space groups. We need to figure out which parts of the code need to be optimized.

scottfredericks commented 6 years ago

@qzhu2017 I have attached the output from running profile.run("a = molecular_crystal(164, ['H2O'], [12], 2.5)") I will look through and see which functions are taking the most time. There is quite a bit listed in the output.

profile.txt

qzhu2017 commented 6 years ago

@scottfredericks, I think the following functions needs to be optimized. Please

    3602    4.661    0.001  383.575    0.106 crystal.py:1898(check_wyckoff_position)

As I said, the most import recipe is to get ride of these for loops. http://www.physics.unlv.edu/~qzhu/PyXtal/html/_modules/pyxtal/crystal.html#check_wyckoff_position

I suggest you create a test script to only test the performance of this code. For the improvement, you need to think about how to get rid of these complex for loops. Please also talk to David. Maybe he knows how to improve it.

qzhu2017 commented 5 years ago

@scottfredericks I am starting to use this code for some production runs. However, the generation of structures are slow. We have to fix this issue asap.

from pyxtal.molecular_crystal import molecular_crystal
from random import randint

for i in range(20):
    run = True
    while run:
        sg = randint(4,191)
        print(sg)
        rand_crystal = molecular_crystal(sg, ['H2O'], [2], 2.0)
        if rand_crystal.valid:
            run = False
        print(rand_crystal.struct)

Even with such a simple script. You will see that the generation of structures gets stuck occasionally.

qzhu2017 commented 5 years ago

@scottfredericks please have a look at this issue. We need to discuss it on Monday during the group meeting.

scottfredericks commented 5 years ago

@qzhu2017 I will try to optimize the core functionality over the weekend.

scottfredericks commented 5 years ago

@qzhu2017 You are right, check_wyckoff_position seems to cost much more time than even the distance checking functions. site_symm and find_generating_point are also quite slow, but I think the list comparisons form the biggest expense.

Within check_wyckoff_position, I think we can use frozen sets to compare the unorganized lists of symmetry operations, since the SymmOp objects in question will either be identical or completely different. However, because the eq function for the SymmOp class uses np.allclose, the comparison is quite slow; we need to call np.allclose for each element pair.

I am trying to find a workaround, but the hash function for SymmOp is "return 7", and the SymmOp's affine_matrix (ndarray) has no hash functions. We could use the index of the SymmOp within the general position, but this would still require using np.allclose for the comparison. So, I am struggling to figure out how to compare the two sets quickly, but I still have a few ideas.

scottfredericks commented 5 years ago

@qzhu2017 I have implemented a hash on the affine matrices of the SymmOp's. Running profile on rand_crystal = molecular_crystal(10, ['H2O'], [2], 2.0) I've gotten the time down from about 450 seconds to about 95 seconds. I am not sure if there is a way to use apply_along_axis for this specific situation, but I will ask David tomorrow how to implement apply_along_axis tomorrow.

scottfredericks commented 5 years ago

@qzhu2017 It seems the new implementation broke generation for some space groups. I have re-uploaded with the old method, with the change that exact_translation is no longer a variable.

qzhu2017 commented 5 years ago

@scottfredericks don't forget to check the other places which may slow down the calculation. After that, we can close this issue.

scottfredericks commented 5 years ago

@qzhu2017 It seems the reason for incorrect symmetry is due to the hashing function (in check_wyckoff_position) being incorrect (I think it was using the "return 7" value passed by pymatgen). I have a simple fix, but it slows down the code again. I will upload this fix, then work on optimizing it. I spent most of yesterday thinking about our method, and I think there is a lot of room for improvement. I'll work on implementing it today.

scottfredericks commented 5 years ago

@qzhu2017 I have finally managed to vectorize the code for check_wyckoff_position. We use the squared Euclidean distance between the original set of points, and the points generated by the Wyckoff operators. We require that there be at least one unique mapping between the points and the generated points; this is done by checking the distance matrix. If there is at least one 0 in each row, and a zero in each column, then we say a mapping exists, and the point is valid as a generating point.

There are still some groups which are slow to generate. But, overall the code is significantly faster. I will next work on optimizing the regular distance functions.

qzhu2017 commented 5 years ago

@scottfredericks

excellent! There still exist a few space groups which takes quite long time. For atomic_3D, 167, 202, 224 seem to take a long time. But you removed it in the previous test. So it is much better now.

For molecular_3D, It looks like there a many space groups take long time. Not sure why it is.

    67  |   67  |   67  |   38.38 s ~~~
    74  |   74  |   74  |   27.11 s ~~~
    129 |   129 |   129 |   123.11 s ~~~~
    134 |   134 |   134 |   77.59 s ~~~~
    137 |   137 |   137 |   77.72 s ~~~~

These are not in the list of skipped space groups. We need to do another profiling to understand the processes better.

scottfredericks commented 5 years ago

@qzhu2017 I have updated the methods for find_short_dist and check_distance. Note that this required a change in the way the indices work for the lists sites_tmp, coorinates_tmp, final_coor, etc, within the atomic crystal classes. Previously, we had separate lists of points for each Wyckoff position. Now, we simply store all points in a single np array. Likewise, for the species, we just stored one specie per WP, but now we store the specie of every point in the coordinate list separately. Also, check_distance now takes a list of species for specie2, instead of a single specie. These changes have not yet been implemented for molecular crystals.

I have yet to update the following functions and their implementations:

Also, I need to update the molecular versions of these. I will try to have this done by the end of the week, then move onto other issues.

Also, I've noticed that using a squared Euclidean metric with cdist is for some reason not any faster than a standard Euclidean metric. I expected that taking the square root would make 'euclidean' slower than 'squeuclidean', but this doesn't seem to be the case.

qzhu2017 commented 5 years ago

@scottfredericks I just ran some test, the code gained quite a big improvement so far. However, I am not sure about the following results.

Couldn't generate crystal after max attempts.
~~~~ Error: Could not generate space group 191 after 14.82 s ~~~
    192 |   192 |   192 |   0.65 s
    193 |   193 |   193 |   10.64 s ~~~
Couldn't generate crystal after max attempts.
~~~~ Error: Could not generate space group 194 after 93.96 s ~~~~
    195 |   195 |   195 |   0.40 s
    196 |   196 |   196 |   3.88 s ~~
    197 |   197 |   197 |   0.44 s
    198 |   198 |   198 |   0.22 s
    199 |   199 |   199 |   1.85 s ~
Couldn't generate crystal after max attempts.
~~~~ Error: Could not generate space group 200 after 15.98 s ~~~
    201 |   201 |   201 |   0.48 s
    202 |   202 |   202 |   15.37 s ~~~
    203 |   203 |   203 |   4.46 s ~~
    204 |   204 |   204 |   11.2 s ~~~
    205 |   205 |   205 |   0.34 s
    206 |   206 |   206 |   1.07 s ~
    207 |   207 |   207 |   11.54 s ~~~
    208 |   208 |   208 |   0.85 s
    209 |   209 |   209 |   3.86 s ~~
Couldn't generate crystal after max attempts.
~~~~ Error: Could not generate space group 210 after 1367.73 s ~~~~
    211 |   211 |   211 |   5.34 s ~~
    212 |   212 |   212 |   1.95 s ~
    213 |   213 |   213 |   0.46 s
    214 |   214 |   214 |   69.09 s ~~~~
Couldn't generate crystal after max attempts.
~~~~ Error: Could not generate space group 215 after 14.75 s ~~~

For space group 210, it took 1367 s to complete the max attempts, while it took only 14.75 s for space group 215. I thought each cycle should take more or less same time cost. Why do they differ so much?

scottfredericks commented 5 years ago

@qzhu2017 Most of the improvements for atomic crystals haven't been implemented for molecular crystals yet, but I plan to implement them today. The main difficulty is working with lists of coordinates and species, since there are more of them for molecular crystals.

Also, I think we can simplify the merging of Wyckoff positions if we just consider one point in the position. Instead of generating all points and comparing distances, I think it should be possible to first just compare the distance from the generating point to a new point generated by plugging it into a different Wyckoff position. But, I need to think about it more. The space group P6 is a good example for looking at the possible difficulties.

scottfredericks commented 5 years ago

@qzhu2017 I've managed to implement a new distance check method for 3D molecular crystals. Rather than copy this code for 2D and 1D molecular crystals, I plan to modularize the code by having 2D and 1D crystals inherit the same generate_crystal method. Also, I need to clean up the code in a few spots.

Some space groups are still slow, especially 225-228, but this make some sense considering the size of the general positions. Still, I haven't seen any groups take longer than 40 seconds on my machine, which is an improvement. I haven't gotten to test how long failed generation takes for these groups yet.

qzhu2017 commented 5 years ago

@scottfredericks This is a big improvement. Btw, I just tried and found the following errors.

~~~ Error:
Traceback (most recent call last):
  File "pyxtal/test_cases/test_all.py", line 1017, in test_modules
    from pyxtal.operations import orientation
ImportError: cannot import name 'orientation' from 'pyxtal.operations' (/Users/qiangzhu/Desktop/github/PyXtal/pyxtal/operations.py)
~~~ Failed module ~~~
scottfredericks commented 5 years ago

@qzhu2017 That should be fixed now.

qzhu2017 commented 5 years ago

@scottfredericks super! I really appreciate your efforts in improving the code.

My test on molecular_3D went smoothly with a total time cost of 234.9 s. I am very happy about it.

However, some minor issues still exists. This test reports two failed cases which do not return the desired symmetry. Here are the structures. Structure-1, desired-109, detected-25 Remark: this structure has very close intermolecular contacts. It suggests that we may have some issue in distance check fucntion.

H32 O16 
1.0
10.094833 0.000000 0.000000
0.000000 10.094833 0.000000
0.000000 0.000000 6.260018
H O
32 16
direct
0.075630 0.000000 0.116764 H
0.924370 0.000000 0.116764 H
0.924370 0.500000 0.366764 H
0.075630 0.500000 0.366764 H
0.500000 0.575630 0.616764 H
0.500000 0.424370 0.616764 H
0.500000 0.924370 0.866764 H
0.500000 0.075630 0.866764 H
0.075630 0.000000 0.743135 H
0.924370 0.000000 0.743135 H
0.924370 0.500000 0.993135 H
0.075630 0.500000 0.993135 H
0.500000 0.575630 0.243135 H
0.500000 0.424370 0.243135 H
0.500000 0.924370 0.493135 H
0.500000 0.075630 0.493135 H
0.000000 0.924370 0.657396 H
0.000000 0.075630 0.657396 H
0.000000 0.575630 0.907396 H
0.000000 0.424370 0.907396 H
0.575630 0.500000 0.157396 H
0.424370 0.500000 0.157396 H
0.424370 0.000000 0.407396 H
0.575630 0.000000 0.407396 H
0.000000 0.924370 0.935775 H
0.000000 0.075630 0.935775 H
0.000000 0.575630 0.185775 H
0.000000 0.424370 0.185775 H
0.575630 0.500000 0.435775 H
0.424370 0.500000 0.435775 H
0.424370 0.000000 0.685775 H
0.575630 0.000000 0.685775 H
0.000000 0.000000 0.212084 O
0.000000 0.500000 0.462084 O
0.500000 0.500000 0.712084 O
0.500000 0.000000 0.962084 O
0.000000 0.000000 0.838454 O
0.000000 0.500000 0.088454 O
0.500000 0.500000 0.338454 O
0.500000 0.000000 0.588454 O
0.000000 0.000000 0.562077 O
0.000000 0.500000 0.812077 O
0.500000 0.500000 0.062077 O
0.500000 0.000000 0.312077 O
0.000000 0.000000 0.031095 O
0.000000 0.500000 0.281095 O
0.500000 0.500000 0.531095 O
0.500000 0.000000 0.781095 O

Structure-2---desired 80, detected 3 QZ: Remark: this structure looks good to me. I don't understand why it failed. Could you please check it? If it is indeed the error of spglib, we should contact the author.

H16 O8 
1.0
5.017367 0.000000 0.000000
0.000000 5.017367 0.000000
0.000000 0.000000 12.670451
H O
16 8
direct
0.910188 0.877167 0.068394 H
0.089812 0.122833 0.068394 H
0.089812 0.622833 0.318394 H
0.910188 0.377167 0.318394 H
0.622833 0.410188 0.568394 H
0.377167 0.589812 0.568394 H
0.377167 0.089812 0.818394 H
0.622833 0.910188 0.818394 H
0.136118 0.931983 0.742144 H
0.863882 0.068017 0.742144 H
0.863882 0.568017 0.992144 H
0.136118 0.431983 0.992144 H
0.568017 0.636118 0.242144 H
0.431983 0.363882 0.242144 H
0.431983 0.863882 0.492144 H
0.568017 0.136118 0.492144 H
0.000000 0.000000 0.021300 O
0.000000 0.500000 0.271300 O
0.500000 0.500000 0.521300 O
0.500000 0.000000 0.771300 O
0.000000 0.000000 0.695050 O
0.000000 0.500000 0.945050 O
0.500000 0.500000 0.195050 O
0.500000 0.000000 0.445050 O
scottfredericks commented 5 years ago

@qzhu2017 It seems I've misunderstood how to implement the covalent radius in distance checks. For hydrogen, the value is 0.31. Clearly, this is too small a distance for Hydrogens to be separated by; in water the O-H distance is about 0.93894 Angstroms (using mol_from_collection('H2O')). In H2, the distance is about 0.74 Angstroms.

Currently, the program takes the average of the covalent radii between two atoms as the tolerance. It looks like we should at least double or maybe triple this. I think that we can use a smaller tolerance for inter-molecular atomic distance checks, and a larger tolerance for the final distance check (which checks distances within the same molecule). In theory, if we use the right value for the first check, we shouldn't need to perform the final distance check at all. We can easily test this by printing a message when the final distance check detects something.

So, for perspective: The current value for H-O tolerance is 0.485 A, while the H-O distance in water is 0.93 A. The current value for H-H tolerance is 0.310 A, while the H-H distance in H2 is 0.74 A.

Given your knowledge, what do you think a reasonable choice would be?

qzhu2017 commented 5 years ago

@scottfredericks I see. Remember, this is the covalent radii. So estimation of bond length should be the sum of two radius. Suppose H has 0.31 A and O has 0.66 For H-O, the tolerance should be 0.31+0.66 = 0.97. We should not allow the intermolecular H-O contacts has distance smaller than 0.97. Similarly, the tolerance should be 0.312 for H-H, 0.662 for O-O.

scottfredericks commented 5 years ago

@qzhu2017 For space group 80 and 109 (and for 110), I got similar results, and realized it was only happening for space groups with screw axes. For these cases, I think we just need to use the Wyckoff generators instead of the Wyckoff positions for choosing molecular centers. I have implemented this change, and after testing these space groups 100 times each, I have not seen the issue come up again.

For the tolerance, I have updated to use the sum of both tolerances. For the final verify_distances, I use a factor of 0.75, since otherwise the bonds within a single molecule would fail the check. For some reason the crystal sometimes fails this final distance check. I am not sure why, so I will try to troubleshoot it.

qzhu2017 commented 5 years ago

@scottfredericks For molecular crystals, we only check the intermolecular distances, not the intramolecular distances. We don't want the atoms belonging two different molecules to form the bonds.

Say if you have two molecules, H2O-1 and H2O-2. We only check distance between H (in H2O-1) and H (in H2O-2), but not the distance between H and H in both H2O-1.

qzhu2017 commented 5 years ago

@scottfredericks , I think your implementation is wrong. Let me make it clear.

scottfredericks commented 5 years ago

For future reference and optimization, here is output for profiling molecular_crystal. This is using inter-atomic distances.

>>> profile.run("c = molecular_crystal(225, ['H2O'], [48], 1.0)")
         547152321 function calls (544548359 primitive calls) in 3925.418 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   243076    2.077    0.000    2.077    0.000 :0(__array_prepare__)
   166556    1.267    0.000    1.267    0.000 :0(__array_wrap__)
    79342    1.968    0.000    1.968    0.000 :0(__deepcopy__)
   185866    1.719    0.000    1.719    0.000 :0(__new__)
   181527    5.403    0.000    6.594    0.000 :0(__reduce_ex__)
     9041    0.039    0.000    0.039    0.000 :0(abs)
      104    0.001    0.000    0.001    0.000 :0(acos)
        9    0.000    0.000    0.000    0.000 :0(add)
   270328    0.611    0.000    0.806    0.000 :0(all)
    64415    0.442    0.000    0.813    0.000 :0(any)
  8580386   22.100    0.000   22.100    0.000 :0(append)
     2020    0.016    0.000    0.016    0.000 :0(argmin)
        1    0.000    0.000    0.000    0.000 :0(argsort)
 68154905  324.507    0.000  324.507    0.000 :0(array)
    77586    1.916    0.000    1.916    0.000 :0(astype)
     2296    0.007    0.000    0.007    0.000 :0(bit_length)
       16    0.000    0.000    0.000    0.000 :0(callable)
    64027   14.236    0.000   14.236    0.000 :0(cdist_euclidean_double_wrap)
        1    0.000    0.000    0.000    0.000 :0(cluster_dist)
     2165    0.041    0.000    0.041    0.000 :0(concatenate)
    61993    0.349    0.000    0.349    0.000 :0(copy)
   166557    2.441    0.000    2.441    0.000 :0(copyto)
      365    0.002    0.000    0.002    0.000 :0(cos)
  5512935   40.462    0.000   40.462    0.000 :0(dot)
    64043    0.469    0.000    0.469    0.000 :0(empty)
   166556    4.362    0.000    4.362    0.000 :0(empty_like)
        8    0.015    0.002    0.015    0.002 :0(eval)
        1    0.000    0.000 3925.418 3925.418 :0(exec)
    40812    0.144    0.000    0.144    0.000 :0(finditer)
     2165    0.017    0.000    0.017    0.000 :0(flatten)
       54    0.000    0.000    0.000    0.000 :0(format)
      262    0.008    0.000    0.008    0.000 :0(frompyfunc)
  4422431   59.494    0.000   59.501    0.000 :0(get)
        8    0.000    0.000    0.000    0.000 :0(get_value)
  1236436   12.203    0.000   13.609    0.000 :0(getattr)
    11308    0.044    0.000    0.044    0.000 :0(geterrobj)
     3882    0.013    0.000    0.013    0.000 :0(getrandbits)
    93816    0.204    0.000    0.204    0.000 :0(group)
   234889    5.949    0.000    7.464    0.000 :0(hasattr)
        8    0.000    0.000    0.000    0.000 :0(hash)
  3110407   40.984    0.000   40.984    0.000 :0(id)
        1    0.000    0.000    0.000    0.000 :0(inconsistent)
    64185    0.307    0.000    0.307    0.000 :0(index)
       42    0.000    0.000    0.000    0.000 :0(insert)
       16    0.000    0.000    0.000    0.000 :0(is_float)
  1577010   11.913    0.000   11.944    0.000 :0(isinstance)
        8    0.000    0.000    0.000    0.000 :0(isscalar)
 10954275   34.523    0.000   34.523    0.000 :0(issubclass)
   853009    5.680    0.000    5.680    0.000 :0(items)
     4216    0.011    0.000    0.011    0.000 :0(join)
    82103    0.617    0.000    0.617    0.000 :0(keys)
923473/844384    6.037    0.000    7.301    0.000 :0(len)
    70736    0.255    0.000    0.255    0.000 :0(lower)
    77299    0.935    0.000    0.935    0.000 :0(max)
   238653    1.610    0.000   16.024    0.000 :0(mean)
    78710    0.886    0.000    0.886    0.000 :0(min)
        1    0.000    0.000    0.000    0.000 :0(mst_single_linkage)
61368164/61201608  122.981    0.000  127.027    0.000 :0(next)
   166682    2.175    0.000    2.175    0.000 :0(normalize_axis_index)
    17736    0.034    0.000    0.034    0.000 :0(ord)
        1    0.000    0.000    0.000    0.000 :0(pdist_euclidean_double_wrap)
   306092    0.731    0.000    0.731    0.000 :0(pop)
       14    0.000    0.000    0.000    0.000 :0(promote_types)
     5014    0.056    0.000    0.056    0.000 :0(random_sample)
  4863755   24.577    0.000   24.577    0.000 :0(ravel)
 50810657  533.874    0.000  533.874    0.000 :0(reduce)
     2067    0.008    0.000    0.008    0.000 :0(remove)
      262    0.003    0.000    0.003    0.000 :0(repeat)
     6672    0.029    0.000    0.029    0.000 :0(replace)
        1    0.000    0.000    0.000    0.000 :0(reshape)
     2827    0.024    0.000    0.024    0.000 :0(result_type)
   239371    1.983    0.000    1.983    0.000 :0(round)
      579    0.001    0.000    0.001    0.000 :0(search)
     5654    0.027    0.000    0.027    0.000 :0(seterrobj)
        1    0.000    0.000    0.000    0.000 :0(setprofile)
      356    0.002    0.000    0.002    0.000 :0(sin)
     2165    0.030    0.000    0.030    0.000 :0(sort)
     3996    0.029    0.000    0.037    0.000 :0(sorted)
     6672    0.026    0.000    0.026    0.000 :0(split)
      189    0.001    0.000    0.001    0.000 :0(sqrt)
     6672    0.022    0.000    0.022    0.000 :0(strip)
    78485    0.455    0.000    1.669    0.000 :0(sum)
   334593    2.861    0.000    2.861    0.000 :0(transpose)
   181750    3.174    0.000    3.174    0.000 :0(update)
       20    0.000    0.000    0.000    0.000 :0(upper)
     8689    0.022    0.000    0.022    0.000 :0(values)
       16    0.000    0.000    0.000    0.000 :0(values_from_object)
        8    0.000    0.000    0.000    0.000 :0(view)
     3581    0.251    0.000    0.251    0.000 :0(where)
   575780    8.735    0.000    8.735    0.000 :0(zeros)
    26682    1.048    0.000    1.772    0.000 <frozen importlib._bootstrap>:989(_handle_fromlist)
      9/1    0.000    0.000 3925.417 3925.417 <string>:1(<module>)
      108    0.000    0.000    0.001    0.000 __init__.py:1284(debug)
      108    0.000    0.000    0.000    0.000 __init__.py:1528(getEffectiveLevel)
      108    0.000    0.000    0.001    0.000 __init__.py:1542(isEnabledFor)
    84006    1.601    0.000    2.314    0.000 _collections_abc.py:672(keys)
     1823    0.011    0.000    0.015    0.000 _collections_abc.py:676(items)
    85829    0.718    0.000    0.718    0.000 _collections_abc.py:698(__init__)
    76098    1.268    0.000    4.556    0.000 _collections_abc.py:701(__len__)
   168012    2.303    0.000    4.472    0.000 _collections_abc.py:719(__iter__)
     3646    0.025    0.000    0.091    0.000 _collections_abc.py:742(__iter__)
 50484092  157.543    0.000  687.198    0.000 _methods.py:28(_amin)
    78096    0.259    0.000    1.214    0.000 _methods.py:31(_sum)
      125    0.000    0.000    0.002    0.000 _methods.py:37(_any)
     9687    0.050    0.000    0.195    0.000 _methods.py:40(_all)
   238657    1.181    0.000    1.726    0.000 _methods.py:43(_count_reduce_items)
   238657    6.908    0.000   14.414    0.000 _methods.py:53(_mean)
     4278    0.012    0.000    0.012    0.000 _weakrefset.py:70(__contains__)
     2334    0.020    0.000    0.032    0.000 abc.py:178(__instancecheck__)
       54    0.002    0.000    0.125    0.002 analyzer.py:1005(_check_R2_axes_asym)
       54    0.004    0.000    0.111    0.002 analyzer.py:1016(_find_mirror)
       12    0.000    0.000    0.026    0.002 analyzer.py:1183(get_pointgroup)
      589    0.053    0.000    0.570    0.001 analyzer.py:1190(is_valid_op)
        1    0.000    0.000    0.006    0.006 analyzer.py:1212(_get_eq_sets)
        1    0.000    0.000    0.000    0.000 analyzer.py:1234(<listcomp>)
        1    0.000    0.000    0.002    0.002 analyzer.py:1237(get_clustered_indices)
       12    0.000    0.000    0.000    0.000 analyzer.py:1252(<setcomp>)
        2    0.000    0.000    0.000    0.000 analyzer.py:1257(<dictcomp>)
        1    0.000    0.000    0.001    0.001 analyzer.py:1272(_combine_eq_sets)
        2    0.000    0.000    0.000    0.000 analyzer.py:1295(all_equivalent_atoms_of_i)
        2    0.000    0.000    0.000    0.000 analyzer.py:1299(<dictcomp>)
        1    0.000    0.000    0.007    0.007 analyzer.py:1331(get_equivalent_atoms)
        1    0.000    0.000    0.008    0.008 analyzer.py:1354(symmetrize_molecule)
        1    0.000    0.000    0.002    0.002 analyzer.py:1454(cluster_sites)
        1    0.000    0.000    0.000    0.000 analyzer.py:1469(<listcomp>)
        1    0.000    0.000    0.000    0.000 analyzer.py:1475(<dictcomp>)
       13    0.005    0.000    0.027    0.002 analyzer.py:1494(generate_full_symmops)
       13    0.000    0.000    0.010    0.001 analyzer.py:1512(<listcomp>)
       13    0.000    0.000    0.001    0.000 analyzer.py:1530(<listcomp>)
       12    0.000    0.000    0.026    0.002 analyzer.py:1607(__init__)
       54    0.001    0.000    6.073    0.112 analyzer.py:841(__init__)
       54    0.041    0.001    5.237    0.097 analyzer.py:864(_analyze)
       54    0.001    0.000    0.239    0.004 analyzer.py:917(_proc_asym_top)
       54    0.001    0.000    0.113    0.002 analyzer.py:976(_proc_cyclic)
       54    0.000    0.000    0.000    0.000 analyzer.py:980(<lambda>)
     2165    0.032    0.000    0.226    0.000 arraysetops.py:113(unique)
     2165    0.084    0.000    0.182    0.000 arraysetops.py:256(_unique1d)
        8    0.000    0.000    0.000    0.000 base.py:1303(_convert_scalar_indexer)
        8    0.000    0.000    0.000    0.000 base.py:1638(__contains__)
        8    0.000    0.000    0.001    0.000 base.py:2454(get_value)
        8    0.000    0.000    0.000    0.000 base.py:3360(_maybe_cast_indexer)
        1    0.000    0.000    0.002    0.002 collection.py:49(__getitem__)
        1    0.000    0.000    0.002    0.002 collection.py:57(_read)
       16    0.000    0.000    0.000    0.000 common.py:435(_apply_if_callable)
      582    0.016    0.000    0.039    0.000 composition.py:102(__init__)
     3019    0.029    0.000    0.085    0.000 composition.py:146(__getitem__)
    78490    1.244    0.000    1.855    0.000 composition.py:155(__len__)
    77921    1.558    0.000    2.166    0.000 composition.py:158(__iter__)
     1196    0.033    0.000    0.170    0.000 composition.py:170(__eq__)
        3    0.000    0.000    0.000    0.000 composition.py:243(__hash__)
      390    0.005    0.000    0.738    0.002 composition.py:431(weight)
      390    0.005    0.000    0.728    0.002 composition.py:437(<listcomp>)
     1416    0.077    0.000    0.189    0.000 coord_utils.py:31(find_in_coord_list)
1917933/26437  165.182    0.000  424.146    0.016 copy.py:132(deepcopy)
  1218003    9.423    0.000    9.423    0.000 copy.py:190(_deepcopy_atomic)
26397/26199    2.746    0.000  367.511    0.014 copy.py:210(_deepcopy_list)
335527/26244   37.820    0.000  398.527    0.015 copy.py:236(_deepcopy_dict)
   622793   16.000    0.000   25.945    0.000 copy.py:252(_keep_alive)
181527/26243   26.257    0.000  414.484    0.016 copy.py:268(_reconstruct)
   363054    6.627    0.000   33.822    0.000 copy.py:273(<genexpr>)
   181522    3.417    0.000    5.123    0.000 copyreg.py:87(__newobj__)
        1    0.000    0.000    0.000    0.000 copyreg.py:96(_slotnames)
        3    0.000    0.000    0.000    0.000 crystal.py:1027(generate_lattice)
    80756    3.418    0.000  158.134    0.002 crystal.py:111(filtered_coords)
   776256   28.872    0.000   28.872    0.000 crystal.py:127(filter_vector)
    27922    0.239    0.000  251.647    0.009 crystal.py:134(filtered_coords_euclidean)
        5    0.028    0.006    0.819    0.164 crystal.py:1432(get_wyckoffs)
  4863039  124.466    0.000  124.466    0.000 crystal.py:149(filter_vector_euclidean)
        1    0.037    0.037    0.906    0.906 crystal.py:1612(get_wyckoff_symmetry)
        2    0.015    0.007    0.700    0.350 crystal.py:1785(get_wyckoff_generators)
     1780    0.014    0.000    0.014    0.000 crystal.py:221(jk_from_i)
   240949   13.251    0.000   30.773    0.000 crystal.py:489(create_matrix)
     2296    0.196    0.000 2469.116    1.075 crystal.py:541(distance_matrix)
     2296    0.419    0.000    0.419    0.000 crystal.py:562(<listcomp>)
     2296    0.560    0.000   26.702    0.012 crystal.py:564(<listcomp>)
      131    0.001    0.000    0.001    0.000 crystal.py:567(dsquared)
    27791    0.576    0.000  648.512    0.023 crystal.py:570(distance_matrix_euclidean)
    52812    0.650    0.000    0.650    0.000 crystal.py:584(subtract)
    75718    3.564    0.000   12.131    0.000 crystal.py:594(get_tol)
      132    0.027    0.000   41.510    0.314 crystal.py:618(check_distance)
   238653    5.913    0.000   59.094    0.000 crystal.py:666(get_center)
        3    0.000    0.000    0.000    0.000 crystal.py:701(para2matrix)
        1    0.000    0.000    0.000    0.000 crystal.py:837(cellsize)
     2165    1.679    0.001 2444.259    1.129 crystal.py:861(find_short_dist)
     1479    2.596    0.002    4.582    0.003 crystal.py:909(connected_components)
240672/238653    0.655    0.000    0.658    0.000 crystal.py:927(add_neighbors)
    64028    0.164    0.000    0.164    0.000 distance.py:125(_args_to_kwargs_xdist)
        1    0.000    0.000    0.000    0.000 distance.py:1388(pdist)
   128055    0.307    0.000    0.656    0.000 distance.py:164(_copy_array_if_base_present)
   128055    0.450    0.000    1.628    0.000 distance.py:184(_convert_to_type)
    64028    0.159    0.000    0.159    0.000 distance.py:188(_filter_deprecated_kwargs)
        2    0.000    0.000    0.000    0.000 distance.py:1958(is_valid_y)
        1    0.000    0.000    0.000    0.000 distance.py:2036(num_obs_y)
    64027    4.261    0.000   26.602    0.000 distance.py:2065(cdist)
    64027    0.912    0.000    2.846    0.000 distance.py:241(_validate_cdist_input)
        1    0.000    0.000    0.000    0.000 distance.py:283(_validate_pdist_input)
    76297    8.581    0.000    8.581    0.000 element.py:12(__init__)
    77580    1.932    0.000    3.180    0.000 enum.py:265(__call__)
    77580    1.248    0.000    1.248    0.000 enum.py:515(__new__)
    76032    0.652    0.000    0.652    0.000 enum.py:592(name)
        8    0.000    0.000    0.000    0.000 frame.py:1940(__getitem__)
        8    0.000    0.000    0.000    0.000 frame.py:1966(_getitem_column)
     2020    0.009    0.000    0.039    0.000 fromnumeric.py:1007(argmin)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:1778(sum)
      117    0.001    0.000    0.003    0.000 fromnumeric.py:1934(any)
     9452    0.104    0.000    0.441    0.000 fromnumeric.py:2021(all)
 50484092  295.609    0.000  982.807    0.000 fromnumeric.py:2323(amin)
   239371    0.968    0.000    4.504    0.000 fromnumeric.py:2771(around)
   239371    0.835    0.000    5.339    0.000 fromnumeric.py:2840(round_)
        4    0.000    0.000    0.000    0.000 fromnumeric.py:2854(mean)
      262    0.004    0.000    0.012    0.000 fromnumeric.py:37(_wrapit)
      262    0.001    0.000    0.018    0.000 fromnumeric.py:382(repeat)
   576205    6.226    0.000   13.292    0.000 fromnumeric.py:50(_wrapfunc)
   334551    3.854    0.000   13.565    0.000 fromnumeric.py:529(transpose)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:851(argsort)
   232677    3.928    0.000   10.468    0.000 function_base.py:1461(copy)
      262    0.016    0.000   13.206    0.050 function_base.py:2727(__call__)
      262    0.046    0.000    0.223    0.001 function_base.py:2757(_get_ufunc_and_otypes)
      262    0.006    0.000    0.056    0.000 function_base.py:2779(<listcomp>)
      524    0.006    0.000    0.006    0.000 function_base.py:2780(<genexpr>)
      262    0.008    0.000    0.008    0.000 function_base.py:2784(<listcomp>)
      262    0.008    0.000    0.020    0.000 function_base.py:2808(<listcomp>)
      262    0.840    0.003   13.190    0.050 function_base.py:2818(_vectorize_call)
      262    0.006    0.000    0.028    0.000 function_base.py:2828(<listcomp>)
        8    0.000    0.000    0.000    0.000 generic.py:1640(_get_item_cache)
        8    0.000    0.000    0.000    0.000 generic.py:7(_check)
        1    0.000    0.000    0.000    0.000 groups.py:36(get_symm_data)
        1    0.000    0.000    0.000    0.000 groups.py:487(sg_symbol_from_int_number)
        1    0.000    0.000    0.000    0.000 hierarchy.py:1214(_convert_to_double)
        1    0.000    0.000    0.000    0.000 hierarchy.py:1284(inconsistent)
        2    0.000    0.000    0.000    0.000 hierarchy.py:1537(is_valid_linkage)
        2    0.000    0.000    0.000    0.000 hierarchy.py:1618(_check_hierarchy_uses_cluster_before_formed)
        2    0.000    0.000    0.000    0.000 hierarchy.py:1626(_check_hierarchy_uses_cluster_more_than_once)
        1    0.000    0.000    0.000    0.000 hierarchy.py:1699(fcluster)
        1    0.000    0.000    0.001    0.001 hierarchy.py:1809(fclusterdata)
        3    0.000    0.000    0.000    0.000 hierarchy.py:206(_copy_array_if_base_present)
        3    0.000    0.000    0.000    0.000 hierarchy.py:218(_copy_arrays_if_base_present)
        3    0.000    0.000    0.000    0.000 hierarchy.py:225(<listcomp>)
        1    0.000    0.000    0.000    0.000 hierarchy.py:485(linkage)
   166556   10.815    0.000   51.826    0.000 index_tricks.py:566(__init__)
   166556    0.733    0.000    0.733    0.000 index_tricks.py:574(__iter__)
 61201608  219.133    0.000  339.340    0.000 index_tricks.py:585(__next__)
        8    0.000    0.000    0.000    0.000 internals.py:154(internal_values)
        8    0.000    0.000    0.000    0.000 internals.py:169(to_dense)
        8    0.000    0.000    0.000    0.000 internals.py:303(dtype)
       24    0.000    0.000    0.000    0.000 internals.py:4124(_block)
        8    0.000    0.000    0.000    0.000 internals.py:4194(dtype)
        8    0.000    0.000    0.000    0.000 internals.py:4221(internal_values)
        8    0.000    0.000    0.000    0.000 internals.py:4224(get_values)
      576    0.002    0.000    0.005    0.000 lattice.py:135(get_cartesian_coords)
        1    0.000    0.000    0.000    0.000 lattice.py:46(__init__)
        1    0.000    0.000    0.000    0.000 lattice.py:79(<listcomp>)
    76520    1.082    0.000    1.082    0.000 linalg.py:100(get_linalg_error_extobj)
      234    0.021    0.000    0.054    0.000 linalg.py:1021(eig)
    76520    2.487    0.000    5.463    0.000 linalg.py:105(_makearray)
  5018437   14.742    0.000   23.042    0.000 linalg.py:110(isComplexType)
       20    0.001    0.000    0.003    0.000 linalg.py:1162(eigh)
    77380    1.372    0.000    2.102    0.000 linalg.py:123(_realType)
       98    0.000    0.000    0.000    0.000 linalg.py:126(_complexType)
    77224    4.120    0.000    9.097    0.000 linalg.py:138(_commonType)
      704    0.021    0.000    0.057    0.000 linalg.py:1822(det)
    77224    0.937    0.000    0.937    0.000 linalg.py:197(_assertRankAtLeast2)
    77224    2.556    0.000    4.359    0.000 linalg.py:208(_assertNdSquareness)
  4863755  122.728    0.000  237.160    0.000 linalg.py:2103(norm)
      234    0.002    0.000    0.007    0.000 linalg.py:213(_assertFinite)
    76266   15.900    0.000   41.332    0.001 linalg.py:464(inv)
        1    0.000    0.000    0.000    0.000 molecular_crystal.py:153(get_box)
     2165    1.634    0.001  653.797    0.302 molecular_crystal.py:228(check_wyckoff_position_molecular)
    27660    0.183    0.000    0.931    0.000 molecular_crystal.py:262(<listcomp>)
      131    0.077    0.001    0.526    0.004 molecular_crystal.py:279(<listcomp>)
     2165    5.577    0.003 3168.037    1.463 molecular_crystal.py:302(merge_coordinate_molecular)
     2165    0.093    0.000    0.217    0.000 molecular_crystal.py:354(choose_wyckoff_molecular)
      131    0.001    0.000    0.001    0.000 molecular_crystal.py:403(__init__)
      132   15.933    0.121  695.066    5.266 molecular_crystal.py:423(_get_coords_and_species)
        1    0.001    0.001    7.475    7.475 molecular_crystal.py:488(get_coords_and_species)
      131    0.169    0.001  729.307    5.567 molecular_crystal.py:519(check_distances)
        1    0.001    0.001 3925.417 3925.417 molecular_crystal.py:575(__init__)
        1    0.000    0.000    0.000    0.000 molecular_crystal.py:660(Msgs)
        1    0.001    0.001    9.004    9.004 molecular_crystal.py:668(get_orientations)
        1    0.000    0.000    0.000    0.000 molecular_crystal.py:699(check_compatible)
        1    0.000    0.000    0.000    0.000 molecular_crystal.py:705(<listcomp>)
        1    0.229    0.229 3914.175 3914.175 molecular_crystal.py:750(generate_crystal)
     2165    0.901    0.000    6.220    0.003 molecular_crystal.py:833(<listcomp>)
      577    0.006    0.000    0.020    0.000 molecular_crystal.py:916(<genexpr>)
        1    0.000    0.000    0.000    0.000 molecular_crystal.py:94(estimate_volume_molecular)
        1    0.000    0.000    0.190    0.190 molecule.py:129(reoriented_molecule)
        2    0.000    0.000    0.033    0.017 molecule.py:143(reorient)
       12    0.000    0.000    1.187    0.099 molecule.py:189(get_symmetry)
    41/12    0.060    0.001    9.003    0.750 molecule.py:254(orientation_in_wyckoff_position)
       20    0.002    0.000    0.164    0.008 molecule.py:82(get_inertia_tensor)
        3    0.000    0.000    0.000    0.000 num.py:16(abs_cap)
        8    0.000    0.000    0.000    0.000 numeric.py:134(_convert_scalar_indexer)
       84    0.003    0.000    0.006    0.000 numeric.py:1459(normalize_axis_tuple)
        1    0.000    0.000    0.000    0.000 numeric.py:146(ones)
      168    0.001    0.000    0.002    0.000 numeric.py:1506(<genexpr>)
       42    0.003    0.000    0.011    0.000 numeric.py:1515(moveaxis)
       42    0.000    0.000    0.000    0.000 numeric.py:1577(<listcomp>)
       14    0.004    0.000    0.017    0.001 numeric.py:1591(cross)
    26682    1.285    0.000    5.327    0.000 numeric.py:2157(identity)
     1913    0.026    0.000    0.573    0.000 numeric.py:2189(allclose)
     2827    0.130    0.000    0.975    0.000 numeric.py:2260(isclose)
     2827    0.132    0.000    0.459    0.000 numeric.py:2319(within_tol)
     5654    0.097    0.000    0.226    0.000 numeric.py:2460(seterr)
     5654    0.058    0.000    0.078    0.000 numeric.py:2560(geterr)
     2827    0.016    0.000    0.024    0.000 numeric.py:2853(__init__)
     2827    0.022    0.000    0.138    0.000 numeric.py:2857(__enter__)
     2827    0.023    0.000    0.134    0.000 numeric.py:2862(__exit__)
  5236413   16.477    0.000   31.691    0.000 numeric.py:424(asarray)
 61459818  181.564    0.000  462.301    0.000 numeric.py:495(asanyarray)
   128055    0.384    0.000    1.178    0.000 numeric.py:547(ascontiguousarray)
   166556    4.663    0.000   13.993    0.000 numeric.py:83(zeros_like)
        6    0.000    0.000    0.000    0.000 numerictypes.py:577(obj2sctype)
        3    0.000    0.000    0.000    0.000 numerictypes.py:669(issubsctype)
   537846   10.601    0.000   23.686    0.000 operations.py:104(operate)
      563    0.017    0.000    0.476    0.001 operations.py:109(is_orthogonal)
      194    0.021    0.000    0.054    0.000 operations.py:128(aa2matrix)
      207    0.018    0.000    0.439    0.002 operations.py:175(matrix2aa)
     3143    0.012    0.000    0.012    0.000 operations.py:183(rotation_matrix)
      145    0.007    0.000    0.022    0.000 operations.py:197(__mul__)
       72    0.004    0.000    0.074    0.001 operations.py:205(inverse)
      162    0.009    0.000    0.033    0.000 operations.py:213(from_axis_angle_and_translation)
       14    0.002    0.000    0.067    0.005 operations.py:246(rotate_vector)
      108    0.007    0.000    0.026    0.000 operations.py:313(reflection)
      280    0.007    0.000    0.025    0.000 operations.py:336(get_order)
      207    0.012    0.000    0.707    0.003 operations.py:357(__init__)
     6672    0.705    0.000    1.652    0.000 operations.py:406(from_xyz_string)
    35847    0.646    0.000    1.121    0.000 operations.py:43(__init__)
      158    0.019    0.000    0.328    0.002 operations.py:43(angle)
      100    0.001    0.000    0.009    0.000 operations.py:439(is_conjugate)
      176    0.003    0.000    0.226    0.001 operations.py:516(__init__)
      436    0.004    0.000    0.034    0.000 operations.py:530(get_matrix)
      161    0.002    0.000    0.020    0.000 operations.py:560(get_op)
      131    0.001    0.000    0.125    0.001 operations.py:596(random_orientation)
    35416    2.717    0.000    6.932    0.000 operations.py:61(from_rotation_and_translation)
     4183    0.030    0.000    0.057    0.000 periodic_table.py:1256(get_el_sp)
       60    0.000    0.000    0.000    0.000 periodic_table.py:486(number)
    81184    0.675    0.000    0.675    0.000 periodic_table.py:549(__hash__)
        3    0.000    0.000    0.000    0.000 periodic_table.py:555(__str__)
    77001    2.111    0.000    5.286    0.000 periodic_table.py:769(__deepcopy__)
      579    0.003    0.000    0.009    0.000 periodic_table.py:951(from_string)
        1    0.000    0.000 3925.418 3925.418 profile:0(c = molecular_crystal(225, ['H2O'], [48], 1.0))
        0    0.000             0.000          profile:0(profiler)
     2296    0.028    0.000    0.048    0.000 random.py:222(_randbelow)
     2296    0.024    0.000    0.076    0.000 random.py:252(choice)
        8    0.000    0.000    0.000    0.000 range.py:446(__len__)
      579    0.003    0.000    0.006    0.000 re.py:179(search)
      780    0.007    0.000    0.014    0.000 re.py:224(finditer)
    13344    0.042    0.000    0.076    0.000 re.py:231(compile)
    14703    0.038    0.000    0.038    0.000 re.py:286(_compile)
        8    0.000    0.000    0.000    0.000 series.py:331(dtype)
        8    0.000    0.000    0.000    0.000 series.py:384(_values)
        8    0.000    0.000    0.000    0.000 series.py:389(get_values)
        8    0.000    0.000    0.001    0.000 series.py:598(__getitem__)
 61201608  236.923    0.000  576.263    0.000 shape_base.py:125(<genexpr>)
   166556  706.623    0.004 3196.549    0.019 shape_base.py:23(apply_along_axis)
        3    0.000    0.000    0.000    0.000 sites.py:121(species_string)
   155797    1.296    0.000    1.296    0.000 sites.py:133(species_and_occu)
    76095    4.538    0.000   15.778    0.000 sites.py:141(specie)
   232677    4.554    0.000   15.023    0.000 sites.py:156(coords)
        3    0.000    0.000    0.000    0.000 sites.py:171(x)
        3    0.000    0.000    0.000    0.000 sites.py:178(y)
        3    0.000    0.000    0.000    0.000 sites.py:185(z)
      576    0.005    0.000    0.088    0.000 sites.py:292(__init__)
   153363    5.842    0.000    8.963    0.000 sites.py:42(__init__)
   152781    5.033    0.000    7.972    0.000 sites.py:82(properties)
   152781    1.729    0.000    1.729    0.000 sites.py:87(<dictcomp>)
   231003    4.111    0.000    4.111    0.000 sites.py:89(__getattr__)
    64028    0.294    0.000    1.089    0.000 six.py:130(callable)
   192084    0.364    0.000    0.364    0.000 six.py:131(<genexpr>)
   166556    1.046    0.000    1.046    0.000 stride_tricks.py:20(__init__)
   166556    1.077    0.000    1.077    0.000 stride_tricks.py:25(_maybe_view_as_subclass)
   166556   12.616    0.000   22.804    0.000 stride_tricks.py:38(as_strided)
        1    0.000    0.000    0.000    0.000 structure.py:107(<listcomp>)
       76    0.001    0.000    0.003    0.000 structure.py:109(species_and_occu)
       76    0.001    0.000    0.001    0.000 structure.py:114(<listcomp>)
       76    0.003    0.000    0.009    0.000 structure.py:162(site_properties)
       78    0.009    0.000    0.031    0.000 structure.py:1623(__init__)
       76    0.013    0.000    0.953    0.013 structure.py:1702(center_of_mass)
    28749    0.222    0.000    0.222    0.000 structure.py:1715(sites)
    27003    0.639    0.000    0.853    0.000 structure.py:180(__iter__)
     1093    0.007    0.000    0.009    0.000 structure.py:183(__getitem__)
      653    0.015    0.000    0.024    0.000 structure.py:186(__len__)
      728    0.011    0.000    0.082    0.000 structure.py:200(cart_coords)
       76    0.004    0.000    1.007    0.013 structure.py:2049(get_centered_molecule)
      728    0.010    0.000    0.054    0.000 structure.py:206(<listcomp>)
        1    0.000    0.000    0.095    0.095 structure.py:2205(__init__)
       78    0.001    0.000    0.032    0.000 structure.py:2869(__init__)
    50851    1.901    0.000   58.548    0.001 structure.py:3190(apply_operation)
   152553   11.329    0.000   54.214    0.000 structure.py:3198(operate_site)
    50851    2.433    0.000   56.646    0.001 structure.py:3203(<listcomp>)
        1    0.005    0.005    0.095    0.095 structure.py:338(__init__)
        1    0.000    0.000    0.000    0.000 structure.py:98(species)
    62275    2.872    0.000    4.289    0.000 twodim_base.py:140(eye)
     1106    0.006    0.000    0.006    0.000 type_check.py:106(real)
    76032    1.532    0.000    2.184    0.000 types.py:135(__get__)
     8688    2.227    0.000    5.076    0.001 units.py:180(check_mappings)
   260640    1.104    0.000    1.602    0.000 units.py:183(<listcomp>)
     8688    0.233    0.000    5.512    0.001 units.py:196(__init__)
     7908    0.019    0.000    0.019    0.000 units.py:217(<dictcomp>)
     7908    0.016    0.000    0.016    0.000 units.py:245(__iter__)
     7908    0.013    0.000    0.013    0.000 units.py:248(__getitem__)
     3954    0.046    0.000    0.108    0.000 units.py:254(__repr__)
     3954    0.008    0.000    0.008    0.000 units.py:256(<lambda>)
     3954    0.011    0.000    0.011    0.000 units.py:257(<listcomp>)
     3954    0.017    0.000    0.125    0.000 units.py:261(__str__)
     4344    0.037    0.000    2.820    0.001 units.py:363(__new__)
     4344    0.043    0.000    2.910    0.001 units.py:369(__init__)
     2592    0.030    0.000    3.226    0.001 units.py:413(__mul__)
      390    0.006    0.000    0.704    0.002 units.py:421(__rmul__)
      972    0.010    0.000    1.187    0.001 units.py:449(__neg__)
      390    0.015    0.000    1.450    0.004 units.py:846(wrapped_f)
scottfredericks commented 5 years ago

@qzhu2017 There is now an option "-s" or "--summary" in test_all.py. This outputs a summary.txt file which contains all the information printed to the terminal during the test run. If any incorrect symmetries are found, this file will be output automatically.