qmcpack/utils/ncjf_gen.py

346 lines
10 KiB
Python
Executable File

#! /usr/bin/env python3
import re
import sys
import numpy as np
from copy import deepcopy
import xml.etree.ElementTree as et
## reads in a qmcpack particle file
## places a single atom-centered gaussian around each atom
def indent(elem, level=0):
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if elem.text:
t = re.search(r"(.+)\n",elem.text,flags=re.DOTALL)
if t is not None:
elem.text = t.group(1) + i
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
# main gaussian class
class gaussian:
def _D_to_B(self, A, D):
B = np.dot(A,D)
return B
def _B_to_D(self, A, B):
try:
D = np.linalg.solve(A,B)
except:
return np.nan
return D
# raise Exception("_B_to_D: conversion not implemented.")
def _K_to_C(self, A, D, K):
C = reduce(np.dot, [D,A,D]) + K
return C
def _C_to_K(self, A, B, C):
try:
D = np.linalg.solve(A,B)
except:
return np.nan
K = C - np.dot(D,B)
return K
#raise Exception("_C_to_K: conversion not implemented.")
# arguments:
# name
# A should be a 3x3 np.array
# B, D should be a 3 element np.array
# C, K should just be a floating point number
def __init__(self, name, A, B, C, D = None, K = None):
self.A = np.ndarray(buffer=deepcopy(A),shape=(3,3),dtype=np.float64)
self.D = None if D is None else np.array(deepcopy(D),dtype=np.float64)
self.K = None if K is None else np.float64(deepcopy(K))
self.B = None if B is None else np.array(deepcopy(B),dtype=np.float64)
self.C = None if C is None else np.float64(deepcopy(C))
if B is not None and C is not None:
self.D = self._B_to_D(self.A, self.B)
self.K = self._C_to_K(self.A, self.B, self.C)
elif D is not None and K is not None: # prefer to use D, K
self.B = self._D_to_B(self.A, self.D)
self.C = self._K_to_C(self.A, self.D, self.K)
# assign variables
self.name = deepcopy(name)
def __str__(self):
A = self.A
B = self.B
C = self.C
D = self.D
K = self.K
name = self.name
if B is not None and C is not None:
s = ("gaussian: name: {}\n"
" A: {} {} {}\n"
" {} {} {}\n"
" {} {} {}\n"
" B: {} {} {}\n"
" C: {}\n")
val_array = [ela for row in A for ela in row] + [elb for elb in B] + [C]
s = s.format(name, *val_array)
return s
elif D is not None and D is not np.nan and K is not None and K is not np.nan:
s = ("gaussian: name: {}\n"
" A: {} {} {}\n"
" {} {} {}\n"
" {} {} {}\n"
" D: {} {} {}\n"
" K: {}\n")
val_array = [ela for row in A for ela in row] + [elb for elb in D] + [K]
s = s.format(name, *val_array)
return s
else:
return "incomplete gaussian definition: A: {}, B: {}, C: {}, D: {}, K: {}".format(
A is not None, B is not None, C is not None, D is not None, K is not None)
# build and return xml tag structure
def xml_tag(self, var_list = ["A", "B", "C"], opt=True):
# serialize A, B
function_tag = et.Element("function",{"id":self.name})
if "A" in var_list:
A_serial = []
for i in range(3):
for j in range(3):
A_serial.append(self.A[i,j]) if j >= i else None
A_tag = et.Element("var",{ "name": "A", "opt": "(opt_a)" if opt else "False" } )
A_tag.text = " ".join( map(str, A_serial ) )
function_tag.append(A_tag)
if "B" in var_list:
B_tag = et.Element("var",{ "name": "B", "opt": "(opt_b)" if opt else "False" } )
B_tag.text = " ".join( map(str, list(self.B)) )
function_tag.append(B_tag)
if "C" in var_list:
C_tag = et.Element("var",{ "name": "C", "opt": "(opt_c)" if opt else "False" } )
C_tag.text = str(self.C)
function_tag.append(C_tag)
if "D" in var_list:
D_tag = et.Element("var",{ "name": "D", "opt": "(opt_d)" if opt else "False" } )
D_tag.text = " ".join( map(str, list(self.D)) )
function_tag.append(D_tag)
if "K" in var_list:
K_tag = et.Element("var",{ "name": "K", "opt": "(opt_k)" if opt else "False" } )
K_tag.text = str(self.K)
function_tag.append(K_tag)
return function_tag
def __deepcopy__(self,memo):
gaussian_copy = gaussian(self.name, self.A, self.B, self.C)
return gaussian_copy
# g*c or g1*g2
def __mul__(self, other):
# other is another gaussian
try:
A = self.A + other.A
B = self.B + other.B
C = self.C + other.C
D = self._B_to_D(self.A, self.B)
K = self._C_to_K(self.A, self.B, self.C)
return gaussian("none",A,B,C)
# other is a number
except:
A = self.A
B = self.B
C = self.C + np.log(other)
return gaussian("none",A,B,C)
# c*g
def __rmul__(self, other):
A = self.A
B = self.B
C = self.C + np.log(other)
return gaussian("none",A,B,C)
# g *= c or g1 *= g2
def __imul__(self, other):
# other is another gaussian
try:
self.A += other.A
self.B += other.B
self.C += other.C
self.D = self._B_to_D(self.A, self.B)
self.K = self._C_to_K(self.A, self.B, self.C)
# other is a number
except:
self.C += np.log(other)
return self
# g / c or g1 / g2
def __div__(self,other):
try:
A = self.A - other.A
B = self.B - other.B
C = self.C - other.C
self.D = self._B_to_D(self.A, self.B)
self.K = self._C_to_K(self.A, self.B, self.C)
return gaussian("none",A,B,C)
except:
A = self.A
B = self.B
C = self.C - np.log(other)
self.D = self._B_to_D(self.A, self.B)
self.K = self._C_to_K(self.A, self.B, self.C)
return gaussian("none",A,B,C)
# c / g
def __rdiv__(self,other):
A = self.A
B = self.B
C = self.C - np.log(other)
self.D = self._B_to_D(self.A, self.B)
self.K = self._C_to_K(self.A, self.B, self.C)
return gaussian("none",A,B,C)
# g /= c or g1 /= g2
def __idiv__(self,other):
# other is another gaussian
try:
self.A -= other.A
self.B -= other.B
self.C -= other.C
self.D = self._B_to_D(self.A, self.B)
self.K = self._C_to_K(self.A, self.B, self.C)
# other must be a number
except:
self.C -= np.log(other)
return self
def logval(self,r):
logval = reduce(np.dot,[r,self.A,r]) - 2*np.dot(self.B,r) + self.C
return logval
def gradlog(self,r):
gradlog = np.array(2*np.dot(self.A,r) - 2*self.B)
return gradlog
def val(self,r):
return np.exp(self.logval(r))
def gradval(self,r):
val = self.val(r)
gradlog = self.gradlog(r)
gradval = gradlog * val
return gradval
def hessval(self,r):
A = self.A
x,y,z = r
val = self.val(r)
gradval = self.gradval(r)
gradlog = self.gradlog(r)
gh = (2*A + np.outer(gradlog,gradlog))*val
return gh
# cf functions correspond to the counting function 1/1+g
def cf_val(self,r):
cf_val = 1/(1+self.val(r))
return cf_val
def cf_gradval(self,r):
val = self.val(r)
cf_gradval = -(2*np.dot(self.A,r) - 2*self.B)*val/(1 + val)**2
return cf_gradval
def cf_hessval(self,r):
val = self.val(r)
gradval = self.gradval(r)
cf_val = self.cf_val(r)
cf_gradval = self.cf_gradval(r)
gh = self.hessval(r)
cf_hessval = np.outer(gradval, gradval)*cf_val**2 - gh*cf_val
return cf_hessval
def atomic_coords(ptclfile):
ptcltree = et.parse(ptclfile)
ptclroot = ptcltree.getroot()
posnode = ptclroot.find(".//particleset[@name='ion0']/attrib[@name='position']")
posblock = posnode.text
pts = []
re_num = "(-?\d\.\d+e[+-]\d\d)"
re_pos = "{re_num}\s+{re_num}\s+{re_num}".format(re_num=re_num)
for pos in re.finditer(re_pos, posblock):
pts.append( np.array(pos.groups(), dtype=np.float64) )
return pts
def gaussian_generator(A, B, C, prefix="g"):
n = len(A)
g = []
if n == 0:
return g
for i, (A,B,C) in enumerate(zip(A,B,C)):
name = "{}{:0>{width}}".format(prefix, i, width=int(np.log10(n)))
g.append(gaussian(name,A=A,B=B,C=C))
return g
def build_cartesian_gaussians(pts):
alpha = 1
A = [-alpha*np.eye(3,3) for pt in pts]
B = [np.dot(Apt, pt) for Apt, pt in zip(A, pts)]
C = [reduce(np.dot, [pt, Apt, pt]) for Apt, pt in zip(A, pts)]
g = gaussian_generator(A, B, C)
return g
# writes an ncjf to the specified outfile
def write_ncjf(outpath, g_list, F, ncjf_name, gref = None):
ng = len(g_list)
cjf_tag = et.Element("ncjf")
jast_tag = et.Element("jastrow",{'type':"Counting",'name':ncjf_name, 'region':'normalized_gaussian'})
cjf_tag.append(jast_tag)
# build debug tag
#debug_tag = et.Element("debug", {'name':'debug', 'seqlen':'5', 'period':'10000'})
#jast_tag.append(debug_tag)
# build F matrix tag
F_tri = F[np.triu_indices(ng)]
F_tag = et.Element("var", {'name':"F", "opt":"(opt_F)"})
# pretty print F-matrix as an upper-triangle
F_str = ""
for i in range(ng):
F_str += "\n " + " "*15*i + "{:>15.5e}"*(ng-i) + " "
F_tag.text = F_str.format(*F_tri)
F_tag.text += "\n "
jast_tag.append(F_tag)
# build region tag
if gref is None:
gref = g_list[0]
region_tag = et.Element("region", {"reference_id":gref.name, "opt":"(opt_C)"})
jast_tag.append(region_tag)
# gaussian function tags
for gaussian in g_list:
gaussian_tag = gaussian.xml_tag(var_list=["A","B","C"], opt=(gref.name != gaussian.name))
region_tag.append(gaussian_tag)
# indent and write to file
indent(cjf_tag)
print " writing cjf tags to file: {}".format(outpath)
et.ElementTree(cjf_tag).write(outpath)
if __name__ == "__main__":
if len(sys.argv) < 3:
print "usage: ncjf_gen.py qmc.ptcl.xml cjf.xml"
sys.exit(0)
ptclfile = sys.argv[1]
cjffile = sys.argv[2]
acoords = atomic_coords(ptclfile)
g_list = build_cartesian_gaussians(acoords)
Fdim = len(g_list)
write_ncjf(cjffile, g_list, np.zeros((Fdim, Fdim)), "atom-centered")