Skip to content

Commit

Permalink
update and prepare for v0.6.3
Browse files Browse the repository at this point in the history
  • Loading branch information
qzhu2017 committed Jan 17, 2024
1 parent f521572 commit 425d883
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 62 deletions.
4 changes: 2 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
author = "Qiang Zhu, Scott Fredericks, Kevin Parrish"

# The short X.Y version
version = "0.6.2"
version = "0.6.3"
# The full version, including alpha/beta/rc tags
release = "0.6.2"
release = "0.6.3"


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ab-initio generation of random crystal structures. It has the following features
- Structural manipulation via symmetry relation (both subgroup and supergroup)
- Geometry optimization from built-in and external optimization methods

The current version is ``0.6.2`` at `GitHub <https://github.com/qzhu2017/PyXtal>`_.
The current version is ``0.6.3`` at `GitHub <https://github.com/qzhu2017/PyXtal>`_.
It is available for use under the MIT license. Expect updates upon request by
`Qiang Zhu\'s group <https://qzhu2017.github.io>`_ at the
University of North Carolina at Charlotte.
Expand Down
84 changes: 42 additions & 42 deletions pyxtal/descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
Module for crystal packing descriptor from energy decomposition
"""
import numpy as np
import pyshtools as pysh
from scipy.stats import qmc
from scipy.spatial.transform import Rotation
from scipy.optimize import minimize
from scipy.special import sph_harm

def _qlm(dists, l=4):
'''
Calculates the vector associated with an atomic site and
Calculates the vector associated with an atomic site and
one of its neighbors
Args:
Expand Down Expand Up @@ -56,7 +55,7 @@ def correlation(coef1, coef2, angle=None, s=0):
"""
Compute the correlation between to sph coefs
Args:
Args:
coef1: sph coefficients 1
coef2: sph coefficients 2
angle: [alpha, beta, gamma]
Expand All @@ -65,7 +64,7 @@ def correlation(coef1, coef2, angle=None, s=0):
Return:
distance scaled in [0, 1]
"""
if angle is not None:
if angle is not None:
coef2 = coef2.rotate(angle[0], angle[1], angle[2], degrees=True)
power = np.sqrt(coef1.spectrum()[s:].sum()*coef2.spectrum()[s:].sum())
cross = coef1.cross_spectrum(coef2)[s:]
Expand All @@ -75,7 +74,7 @@ def correlation_opt(coef1, coef2, angle, s=0):
"""
Compute the correlation between two sph coefs
Args:
Args:
coef1: sph coefficients 1
coef2: sph coefficients 2
angle: [alpha, beta, gamma]
Expand All @@ -88,15 +87,15 @@ def fun(x0, coef1, coef2, s):
coef = coef2.rotate(x0[0], x0[1], x0[2], degrees=True)
return -correlation(coef1, coef, s=s)

res = minimize(fun, angle, args=(coef1, coef2, s),
res = minimize(fun, angle, args=(coef1, coef2, s),
method='Nelder-Mead', options={'maxiter': 20})
return -res.fun, res.x

def correlation_go(coef1, coef2, M=6, s=0, d_cut=0.92):
"""
global optimization of two coefs based on quasi random sampling
Args:
Args:
coef1: sph coefficients 1
coef2: sph coefficients 2
M: 2^M sampling points
Expand Down Expand Up @@ -125,7 +124,7 @@ def correlation_go(coef1, coef2, M=6, s=0, d_cut=0.92):
def fibonacci_sphere(N=1000):
"""
Sampling the sphere grids
Args:
N: number of pts to generate
Expand Down Expand Up @@ -174,15 +173,15 @@ def xyz2sph(xyzs, radian=True):
xyzs: 3D xyz coordinates
radian: return in radian (otherwise degree)
"""
pts = np.zeros([len(xyzs), 2])
pts = np.zeros([len(xyzs), 2])
for i, r_vec in enumerate(xyzs):
r_mag = np.linalg.norm(r_vec)
theta0 = np.arccos(r_vec[2]/r_mag)
if abs((r_vec[2] / r_mag) - 1.0) < 10.**(-8.):
theta0 = 0.0
elif abs((r_vec[2] / r_mag) + 1.0) < 10.**(-8.):
theta0 = np.pi

if r_vec[0] < 0.:
phi0 = np.pi + np.arctan(r_vec[1] / r_vec[0])
elif 0. < r_vec[0] and r_vec[1] < 0.:
Expand All @@ -207,17 +206,17 @@ def expand_sph(pts, l_max, norm=4, csphase=-1):
Transform the grid points to spherical harmonics
Args:
pts: 3D array
pts: 3D array
(thetas, phis, vals) with a length of N
lmax: Integer
The maximum degree of spherical harmonic.
coeff_norm: integer
The normalization of SHExpandLSQ().
The normalization of SHExpandLSQ().
1 (default) = Geodesy 4-pi normalized harmonics;
2 = Schmidt semi-normalized harmonics;
3 = unnormalized harmonics;
2 = Schmidt semi-normalized harmonics;
3 = unnormalized harmonics;
4 = orthonormal harmonics.
csphase: Integer
Expand Down Expand Up @@ -275,6 +274,7 @@ def get_alignment(pts, degrees=True):

class spherical_image():

import pyshtools as pysh
"""
A class to handle the crystal packing descriptor from spherical image
Expand All @@ -287,7 +287,7 @@ class spherical_image():
N: number of grid points on the unit sphere
"""

def __init__(self, xtal, model='molecule', max_d=10,
def __init__(self, xtal, model='molecule', max_d=10,
factor=2.2, lmax=13, sigma=0.1, N=10000):

for i in range(len(xtal.mol_sites)):
Expand Down Expand Up @@ -328,23 +328,23 @@ def calculate_density(self, pt, xyzs):
t0, p0, h = _pt
x0, y0, z0 = np.sin(t0)*np.cos(p0), np.sin(t0)*np.sin(p0), np.cos(t0)
dst = np.linalg.norm(xyzs - np.array([x0, y0, z0]), axis=1)
vals += h*np.exp(-(dst**2/(2.0*self.sigma**2)))
vals += h*np.exp(-(dst**2/(2.0*self.sigma**2)))
return vals

def get_molecules(self):
'''
compute the spherical images from neighboring molecules
Returns:
pts: [N, 3] array, (theta, phi, eng)
'''
pts = []
for i, site in enumerate(self.xtal.mol_sites):
_, neighs, comps, _, engs = self.xtal.get_neighboring_molecules(i,
factor=self.factor,
max_d=self.max_d,
_, neighs, comps, _, engs = self.xtal.get_neighboring_molecules(i,
factor=self.factor,
max_d=self.max_d,
ignore_E=False)
xyz, _ = site._get_coords_and_species(absolute=True, first=True)
xyz, _ = site._get_coords_and_species(absolute=True, first=True)
center = site.molecule.get_center(xyz)
coords = np.zeros([len(neighs), 3])
for _i, xyz in enumerate(neighs):
Expand All @@ -355,25 +355,25 @@ def get_molecules(self):
pt[:, 2] = engs/np.sum(engs)
pts.append(pt)
return pts

def get_contacts(self):
'''
Compute the spherical images from the neighboring distances
Returns:
pts: [N, 3] array, (theta, phi, eng)
'''
pts = []
for i, site in enumerate(self.xtal.mol_sites):
engs, pairs, dists = self.xtal.get_neighboring_dists(i,
factor=self.factor,
engs, pairs, dists = self.xtal.get_neighboring_dists(i,
factor=self.factor,
max_d=self.max_d)
pt = np.zeros([len(pairs), 3])
pt[:, :2] = xyz2sph(pairs)
pt[:, 2] = engs/np.sum(engs)
pts.append(pt)
return pts

def plot_sph_images(self, lmax=None, figname=None, molecule=False):
"""
Plot the spherical images in both 3d and 2d
Expand All @@ -393,7 +393,7 @@ def plot_sph_images(self, lmax=None, figname=None, molecule=False):
shift = 0

fig = plt.figure(figsize=(9, 4*nrows))
gs = gridspec.GridSpec(nrows=nrows, ncols=2,
gs = gridspec.GridSpec(nrows=nrows, ncols=2,
wspace=0.15, width_ratios=[0.7, 1])
if molecule:
from rdkit import Chem
Expand Down Expand Up @@ -422,7 +422,7 @@ def plot_sph_images(self, lmax=None, figname=None, molecule=False):
coef = self.coefs[i]
grid = coef.expand(lmax=lmax)
grid.plot3d(0, 0, title="{:6.3f}".format(self.ds[i]), show=False, ax=ax1)
grid.plot(show=False, ax=ax2, tick_interval=[120, 90],
grid.plot(show=False, ax=ax2, tick_interval=[120, 90],
tick_labelsize=14, axes_labelsize=16)
ax2.set_xlim([1, 359])

Expand All @@ -435,9 +435,9 @@ def plot_real_image(self, id=0):
"""
Plot the real molecular contacts in the crystal
"""
return self.xtal.show_mol_cluster(id,
factor=self.factor,
max_d=self.max_d,
return self.xtal.show_mol_cluster(id,
factor=self.factor,
max_d=self.max_d,
ignore_E=False,
plot=False)

Expand All @@ -451,13 +451,13 @@ def align(self, M=6):
"""
coef0 = self.coefs[0]
angles = get_alignment(self.pts[0])
self.coefs[0] = self.coefs[0].rotate(angles[0], angles[1], angles[2])
self.coefs[0] = self.coefs[0].rotate(angles[0], angles[1], angles[2])

for i in range(1, len(self.coefs)):
coef1 = self.coefs[i]
d, angles = correlation_go(self.coefs[0], coef1, M=M)
self.coefs[i] = coef1.rotate(angles[0], angles[1], angles[2])
self.ds[i] = d
self.coefs[i] = coef1.rotate(angles[0], angles[1], angles[2])
self.ds[i] = d

def rotate(self, alpha=0, beta=0, gamma=0):
"""
Expand All @@ -474,7 +474,7 @@ def rotate(self, alpha=0, beta=0, gamma=0):
def get_similarity(self, sph2, M=6, cutoff=0.95):
"""
Compute the similarity matrix between two sphs
Args:
sph2: the 2nd sph class
M: number of power in quasi random sampling
Expand Down Expand Up @@ -508,14 +508,14 @@ def __init__(self, xtal, max_CN=14):
def get_neighbors(self):
'''
get neighboring molecules
Returns:
pts: [N, 3] array, (theta, phi, eng)
'''
pts = []
for i, site in enumerate(self.xtal.mol_sites):
_, neighs, comps, _, engs = self.xtal.get_neighboring_molecules(i)
xyz, _ = site._get_coords_and_species(absolute=True, first=True)
xyz, _ = site._get_coords_and_species(absolute=True, first=True)
center = site.molecule.get_center(xyz)
coords = np.zeros([len(neighs), 3])
#print(len(neighs))
Expand All @@ -524,10 +524,10 @@ def get_neighbors(self):
coords[_i, :] = self.xtal.molecules[comps[_i]].get_center(xyz) - center
pts.append(coords)
return pts

def get_parameters(self, ls=[4, 6]):
'''
Computes
Computes
Args:
center: center xyz coordinate
neighbors: a list of neighboring xyz coordinates
Expand All @@ -545,10 +545,10 @@ def get_parameters(self, ls=[4, 6]):
qs.append(np.sqrt((4 * np.pi)/(2*l+1) * dot))

return qs


if __name__ == '__main__':

from pkg_resources import resource_filename
from pyxtal import pyxtal

Expand Down
18 changes: 9 additions & 9 deletions pyxtal/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from pyxtal.XRD import Similarity
from pyxtal.operations import get_inverse
from pyxtal.supergroup import supergroups, supergroup
from pyxtal.descriptor import spherical_image
from pyxtal.util import generate_wp_lib

cif_path = resource_filename("pyxtal", "database/cifs/")
Expand Down Expand Up @@ -1203,14 +1202,15 @@ def test_trans(self):
#print(hn, i, (c1 or c2))
self.assertTrue(c1 or c2)

def test_image(self):
c1 = pyxtal(molecular=True)
for name in ['benzene', 'aspirin', 'naphthalene']:
c1.from_seed(seed=cif_path+name+".cif", molecules=[name])
for model in ['contact', 'molecule']:
sph = spherical_image(c1, model=model)
sph.align()
print(name, model)
#def test_image(self):
# from pyxtal.descriptor import spherical_image
# c1 = pyxtal(molecular=True)
# for name in ['benzene', 'aspirin', 'naphthalene']:
# c1.from_seed(seed=cif_path+name+".cif", molecules=[name])
# for model in ['contact', 'molecule']:
# sph = spherical_image(c1, model=model)
# sph.align()
# print(name, model)

if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion pyxtal/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.6.2"
__version__ = "0.6.3"
10 changes: 5 additions & 5 deletions pyxtal/wyckoff_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,9 @@ def split_k(self, wp1, wp2_lists):

all_g2_orbits = []
translations = self.translation_generator()
# the translation generator provides all the possible ways to translate
# the translation generator provides all the possible ways to translate
# the starting positions, then they are shifted
for translation in translations:
for translation in translations:
for gen in wp1_generators:#into the proper orientation
orbit = np.matmul(self.inv_R,gen)
orbit[np.abs(orbit)<1e-5] = 0
Expand Down Expand Up @@ -451,7 +451,7 @@ def check_orbits(self, g1_orbits, wp2, wp2_lists):
if len(g1_orbits) < len(wp2):
s2 = ""
for wp2 in wp2_lists:
s2 += wp2.get_label()
s2 += wp2.get_label()
s2 += ', '
# g, h = self.G.number, self.H.number
# print("Error between {:d}[{:s}] -> {:d}[{:s}]".format(g, s1, h, s2))
Expand All @@ -463,10 +463,10 @@ def check_orbits(self, g1_orbits, wp2, wp2_lists):
def __str__(self):
s = "Wycokff split from {:d} to {:d}\n".format(self.G.number, self.H.number)
for i, wp1 in enumerate(self.wp1_lists):
s += "\n{:s} -> ".format(wp1.get_label())
s += "\n{:s} -> ".format(wp1.get_label())

for j, wp2 in enumerate(self.wp2_lists[i]):
s += "{:s}\n".format(wp2.get_label())
s += "{:s}\n".format(wp2.get_label())
g1s = self.G1_orbits[i][j]
g2s = self.G2_orbits[i][j]
Hs = self.H_orbits[i][j]
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@
'ase>=3.18.0', #covered by pymatgen
'scipy>=1.7.3',
'importlib_metadata>=1.4',
'pyshtools>=4.10.3',
#'pyshtools>=4.10.3',
#"openbabel>=3.0.0",
],
extra_require={
'visualization': ["py3Dmol>=0.8.0"],
#'descriptor': ["pyshtools>=4.10.3"],
'descriptor': ["pyshtools>=4.10.3"],
},
python_requires=">=3.7, <=3.12", #add the restriction for now issue #189
license="MIT",
Expand Down

0 comments on commit 425d883

Please sign in to comment.