Skip to content

Commit 44da03f

Browse files
authored
Merge pull request #21 from lcmd-epfl/local-spahm
Local spahm
2 parents 5ca8026 + c052b48 commit 44da03f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2123
-4
lines changed

.gitignore

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,129 @@
1-
__pycache__
2-
*.pyc
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
37
*.so
4-
*.swp
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
pip-wheel-metadata/
24+
share/python-wheels/
25+
*.egg-info/
26+
.installed.cfg
27+
*.egg
28+
MANIFEST
29+
30+
# PyInstaller
31+
# Usually these files are written by a python script from a template
32+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
33+
*.manifest
34+
*.spec
35+
36+
# Installer logs
37+
pip-log.txt
38+
pip-delete-this-directory.txt
39+
40+
# Unit test / coverage reports
41+
htmlcov/
42+
.tox/
43+
.nox/
44+
.coverage
45+
.coverage.*
46+
.cache
47+
nosetests.xml
48+
coverage.xml
49+
*.cover
50+
*.py,cover
51+
.hypothesis/
52+
.pytest_cache/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
target/
76+
77+
# Jupyter Notebook
78+
.ipynb_checkpoints
79+
80+
# IPython
81+
profile_default/
82+
ipython_config.py
83+
84+
# pyenv
85+
.python-version
86+
87+
# pipenv
88+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
90+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
91+
# install all needed dependencies.
92+
#Pipfile.lock
93+
94+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
95+
__pypackages__/
96+
97+
# Celery stuff
98+
celerybeat-schedule
99+
celerybeat.pid
100+
101+
# SageMath parsed files
102+
*.sage.py
103+
104+
# Environments
105+
.env
106+
.venv
107+
env/
108+
venv/
109+
ENV/
110+
env.bak/
111+
venv.bak/
112+
113+
# Spyder project settings
114+
.spyderproject
115+
.spyproject
116+
117+
# Rope project settings
118+
.ropeproject
119+
120+
# mkdocs documentation
121+
/site
122+
123+
# mypy
124+
.mypy_cache/
125+
.dmypy.json
126+
dmypy.json
127+
128+
# Pyre type checker
129+
.pyre/

qstack/compound.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,13 @@ def fragment_partitioning(fragments, prop_atom_inp, normalize=True):
282282
return props_frag
283283
else:
284284
return props_frag[0]
285+
286+
287+
def make_atom(q, basis):
288+
mol = gto.Mole()
289+
mol.atom = q + " 0.0 0.0 0.0"
290+
mol.charge = 0
291+
mol.spin = data.elements.ELEMENTS_PROTON[q] % 2
292+
mol.basis = basis
293+
mol.build()
294+
return mol

qstack/math/matrix.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ def from_tril(mat_tril):
1515
mat[ind] = mat_tril
1616
mat = mat + mat.T - np.diag(np.diag(mat))
1717
return mat
18+
19+
def sqrtm(m, eps=1e-13):
20+
e, b = np.linalg.eigh(m)
21+
e[abs(e) < eps] = 0.0
22+
sm = b @ np.diag(np.sqrt(e)) @ b.T
23+
return (sm+sm.T)*0.5

qstack/math/wigner.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import sympy as sp
5+
from sympy import symbols, Symbol, simplify, expand, cancel, expand_trig, Ynm, Ynm_c, Matrix, poly, zeros
6+
from .xyz_integrals_sym import xyz as xyzint
7+
8+
9+
# variables
10+
x,y,z = symbols('x y z')
11+
x1,y1,z1 = symbols('x1 y1 z1')
12+
# coefficients
13+
xx,xy,xz = symbols('xx xy xz')
14+
yx,yy,yz = symbols('yx yy yz')
15+
zx,zy,zz = symbols('zx zy zz')
16+
17+
def real_Y_correct_phase(l, m, theta, phi):
18+
# returns real spherical harmonic in Condon--Shortley phase convention
19+
# (sympy's Znm uses some other convention)
20+
ym1 = Ynm (l, -abs(m), theta, phi)
21+
ym2 = Ynm_c(l, -abs(m), theta, phi)
22+
if m==0:
23+
return ym1
24+
elif m<0:
25+
return sp.I / sp.sqrt(2) * (ym1 - ym2)
26+
elif m>0:
27+
return 1 / sp.sqrt(2) * (ym1 + ym2)
28+
29+
def get_polynom_Y(l, m):
30+
# rewrites a real spherical harmonic as a polynom of x,y,z
31+
theta = Symbol("theta", real=True)
32+
phi = Symbol("phi", real=True)
33+
r = Symbol('r', nonnegative=True)
34+
expr = real_Y_correct_phase(l,m, theta, phi) * r**l
35+
expr = expand(expr, func=True)
36+
expr = expr.rewrite(sp.cos)#.simplify().trigsimp()
37+
expr = expand_trig(expr)
38+
expr = cancel(expr)
39+
expr = expr.subs({r: sp.sqrt(x*x+y*y+z*z), phi: sp.atan2(y,x), theta: sp.atan2(sp.sqrt(x*x+y*y),z)})
40+
if m!=0:
41+
expr = cancel(expr).simplify()
42+
expr = expr.subs({x*x+y*y: 1-z*z,
43+
3*x*x+3*y*y : 3-3*z*z })
44+
return expr
45+
46+
def xyzint_wrapper(knm, integrals_xyz_dict):
47+
k,n,m = knm
48+
if k%2 or n%2 or m%2:
49+
return 0
50+
else:
51+
knm = tuple(sorted([k//2, n//2, m//2], reverse=True))
52+
if not knm in integrals_xyz_dict.keys():
53+
integrals_xyz_dict[knm] = xyzint(*knm)
54+
return integrals_xyz_dict[knm]
55+
56+
def product_Y(Y1,Y2):
57+
# computes the product of two spherical harmonics
58+
# and returns coefficients and a list of powers
59+
prod = Y1 * Y2
60+
prod = prod.expand().cancel()
61+
prod = poly(prod, gens=[x,y,z])
62+
return Matrix(prod.coeffs()), prod.monoms()
63+
64+
65+
def print_wigner(D):
66+
for l,d in enumerate(D):
67+
for m1 in range(-l,l+1):
68+
for m2 in range(-l,l+1):
69+
print('D[%d][% d,% d] = '%(l,m1,m2), d[m1,m2])
70+
print()
71+
72+
def compute_wigner(lmax):
73+
74+
Y = [ [0]*(2*l+1) for l in range(lmax+1)]
75+
Y_rot = [ [0]*(2*l+1) for l in range(lmax+1)]
76+
for l in range(lmax+1):
77+
for m in range(-l,l+1):
78+
# spherical harmonic
79+
Y[l][m] = get_polynom_Y(l, m)
80+
# rotated spherical harmonic
81+
Y_rot[l][m] = Y[l][m].subs({x: x1, y:y1, z:z1}).subs({x1:xx*x+xy*y+xz*z, y1:yx*x+yy*y+yz*z, z1:zx*x+zy*y+zz*z})
82+
83+
84+
D = [zeros(2*l+1,2*l+1) for l in range(lmax+1)]
85+
integrals_xyz_dict = {}
86+
for l in range(lmax+1):
87+
for m1 in range(-l,l+1):
88+
for m2 in range(-l,l+1):
89+
coefs, pows = product_Y(Y[l][m2], Y_rot[l][m1])
90+
mono_integrals = [xyzint_wrapper(p,integrals_xyz_dict) for p in pows]
91+
D[l][m1,m2] = coefs.dot(mono_integrals).factor() .subs({zx**2+zy**2: 1-zz**2, xx**2+xy**2:1-xz**2, yx**2+yy**2:1-yz**2}).simplify()
92+
return D
93+
94+
95+
if __name__ == "__main__":
96+
if len(sys.argv)<2:
97+
lmax = 2
98+
else:
99+
lmax = int(sys.argv[1])
100+
101+
D = compute_wigner(lmax)
102+
print_wigner(D)
103+

qstack/math/xyz_integrals_float.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
5+
def xyz(n,m,k):
6+
# computes the integral of x^2k y^2n z^2m over a sphere
7+
k,n,m = sorted([k,n,m], reverse=True)
8+
# k>=n>=m
9+
if n==0:
10+
xyz = 2.0 * (1.0 - (2.0*k-1.0)/(2.0*k+1.0))
11+
else:
12+
xyz = (2*k-1) * I23(n,m,k)
13+
return xyz
14+
15+
def I23(n,m,k):
16+
I23 = 0.0
17+
for l in range(0, n+m+2):
18+
I23 = I23 + (-1)**l * trinomial( n+m+1, n+m+1-l, l) / (2.0*l+2.0*k-1.0)
19+
I23 = I23 / ( (2*n+1) * 2**(2*n+2*m) )
20+
for l in range(1, n+2):
21+
I23 = I23 * (2*n+3-2*l) / (2*m-1+2*l)
22+
return I23
23+
24+
def trinomial(k1,k2,k3):
25+
# (k1+k2+k3)! / (k1! * k2! * k3!)
26+
k1,k2,k3 = sorted([k1,k2,k3], reverse=True)
27+
trinom = 1.0
28+
for k in range(1,k2+1):
29+
trinom = trinom * (k+k1) / k
30+
for k in range(1,k3+1):
31+
trinom = trinom * (k+k1+k2) / k
32+
return trinom
33+
34+
if __name__ == "__main__":
35+
k,n,m = map(int, sys.argv[1:4])
36+
print("%.15f π"%xyz(k,n,m))
37+

qstack/math/xyz_integrals_sym.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import sympy
5+
6+
def xyz(n,m,k):
7+
# computes the integral of x^2k y^2n z^2m over a sphere
8+
k,n,m = sorted([k,n,m], reverse=True)
9+
# k>=n>=m
10+
if n==0:
11+
K = sympy.symbols('K')
12+
xyz = (2 * (1 - (2*K-1)/(2*K+1))).subs(K,k)
13+
else:
14+
xyz = (2*k-1) * I23(n,m,k)
15+
return xyz * sympy.pi
16+
17+
def I23(n,m,k):
18+
I23 = 0.0
19+
K = sympy.symbols('K')
20+
for l in range(0, n+m+2):
21+
I23 = I23 + (-1)**l * trinomial( n+m+1, n+m+1-l, l) / (2*l+2*K-1)
22+
I23 = I23.subs(K,k)
23+
I23 = I23 / ( (2*n+1) * 2**(2*n+2*m) )
24+
for l in range(1, n+2):
25+
I23 = I23 * (2*n+3-2*l) / (2*m-1+2*l)
26+
return I23
27+
28+
def trinomial(k1,k2,k3):
29+
# (k1+k2+k3)! / (k1! * k2! * k3!)
30+
k1,k2,k3 = sorted([k1,k2,k3])
31+
trinom = sympy.FallingFactorial(k1+k2+k3, k3) / (sympy.factorial(k1)*sympy.factorial(k2))
32+
return trinom
33+
34+
if __name__ == "__main__":
35+
k,n,m = map(int, sys.argv[1:4])
36+
x = xyz(k,n,m)
37+
print("%.15f ="%x, x)
38+

qstack/spahm/rho/01_bind.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import argparse
5+
import numpy as np
6+
7+
parser = argparse.ArgumentParser(description='This script generates a single file per atomic species binding all the feature-vector representations obtained using the single-structures representations generator script (1_DMbRep.py)')
8+
parser.add_argument('--X-dir', required = True, type = str, dest='XDir', help = 'The path to the directory containing all the single-structure X representations.')
9+
parser.add_argument('--species', default = ["C", "H", "N", "O", "S"], type = str, nargs='+', dest='Species', help = 'The species contained in the DatBase.')
10+
args = parser.parse_args()
11+
12+
species = list(sorted(args.Species))
13+
X_files = sorted(os.listdir(args.XDir))
14+
15+
X_atom = {}
16+
for e in species:
17+
X_atom[e] = []
18+
19+
for f in X_files :
20+
X = np.load(args.XDir+'/'+f, allow_pickle=True)
21+
for q,x in X :
22+
X_atom[q].append(x)
23+
print(f)
24+
25+
for e in X_atom.keys() :
26+
X_array = np.array(X_atom[e])
27+
name_out = 'X_' + e + '_' + args.XDir
28+
if name_out[-1] == '/': name_out = name_out[:-1]
29+
np.save(name_out, X_array)
30+
print('# Atoms for ', e, '=', X_array.shape)

0 commit comments

Comments
 (0)