3
0
mirror of https://github.com/triqs/dft_tools synced 2024-07-18 00:43:41 +02:00
dft_tools/python/triqs_dft_tools/converters/elktools/elk_converter_tools.py
AlynJ ad8c4e75fe
Elk converter (#151)
Adding Elk-TRIQS interface (first iteration) 

This interface reads in Elk's ground-state files / projectors generated by a specific Elk interface code version (https://github.com/AlynJ/Elk_interface-TRIQS). The interface can perform charge-self consistent DFT+DMFT calculations using the aforementioned Elk code version, including spin orbit-coupling. Hence, this is the first open source interfaced DFT code to triqs with FCSC support. 

The commit includes detailed documentation and tutorials on how to use this interface. Moreover, further new post-processing routines are added for Fermi surface plots and spectral functions (A(w)) from the elk inputs.

The interface was tested by A. James and A. Hampel. However, this is the first iteration of the interface and should be used with care. Please report all bugs.

List of changes:
---------------
- sumk.py: added cacluation of charge density correction for elk (dm_type='elk').
- sumk_dft_tools.py: added new post-processing functions to calculate the Fermi surface and A(w) from the Elk inputs.
- documentation and tutorial files amended for this interface.
- added python tests for the Elk converter.
2020-10-09 08:35:28 -04:00

297 lines
10 KiB
Python

##########################################################################
#
# TRIQS: a Toolbox for Research in Interacting Quantum Systems
#
# Copyright (C) 2019 by A. D. N. James, M. Zingl and M. Aichhorn
#
# TRIQS is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# TRIQS. If not, see <http://www.gnu.org/licenses/>.
#
##########################################################################
from types import *
import numpy
import os.path
from locale import atof
class ElkConverterTools:
"""
Conversion Tools required to help covert Elk outputs into the TRIQS format.
"""
def __init__(self):
pass
def rotaxang(self,rot):
"""
This routine determines the axis of rotation vector (v) and the angle of rotation (th).
If R corresponds to an improper rotation then only the proper part is used and the determinant
is set to -1. The rotation convention follows the "right-hand rule". See Elk's rotaxang
routine.
"""
eps=1E-8
v=numpy.zeros([3], numpy.float_)
# find the determinant
det=numpy.linalg.det(rot)
if (abs(det-1.0)<eps):
det=1.0
elif (abs(det+1.0)<eps):
det=-1.0
else:
raise "sym_converter : Invalid rotation matrix!"
# proper rotation matrix
rotp=det*rot
v[0]=(rotp[1,2]-rotp[2,1])/2.0
v[1]=(rotp[2,0]-rotp[0,2])/2.0
v[2]=(rotp[0,1]-rotp[1,0])/2.0
t1=numpy.sqrt(numpy.dot(v,v))
t2=(rotp[0,0]+rotp[1,1]+rotp[2,2]-1.0)/2.0
if (abs(abs(t2)-1.0)>eps):
# theta not equal to 0 or pi
th=-numpy.arctan2(t1,t2)
v[:]=v[:]/t1
else:
# special case of sin(th)=0
if(t2>eps):
# zero angle: axis arbitrary
th=0.0
v[:]=1.0/numpy.sqrt(3.0)
else:
# rotation by pi
th=numpy.pi
if((rotp[0,0]>=rotp[1,1])&(rotp[0,0]>=rotp[2,2])):
if(rotp[0,0]<(-1.0+eps)):
mpi.report(rotp[0,0],-1.0+eps)
raise "sym_converter : Invalid rotation matrix!"
v[0]=numpy.sqrt(abs(rotp[0,0]+1.0)/2.0)
v[1]=(rotp[1,0]+rotp[0,1])/(4.0*v[0])
v[2]=(rotp[2,0]+rotp[0,2])/(4.0*v[0])
elif((rotp[1,1]>=rotp[0,0])&(rotp[1,1]>=rotp[2,2])):
if(rotp[1,1]<(-1.0+eps)):
mpi.report(rotp[1,1],-1.0+eps)
raise "sym_converter : Invalid rotation matrix!"
v[1]=numpy.sqrt(abs(rotp[1,1]+1.0)/2.0)
v[2]=(rotp[2,1]+rotp[1,2])/(4.0*v[1])
v[0]=(rotp[0,1]+rotp[1,0])/(4.0*v[1])
else:
if(rotp[2,2]<(-1.0+eps)):
mpi.report(rotp[2,2],-1.0+eps)
raise "sym_converter : Invalid rotation matrix!"
v[2]=numpy.sqrt(abs(rotp[2,2]+1.0)/2.0)
v[0]=(rotp[0,2]+rotp[2,0])/(4.0*v[2])
v[1]=(rotp[1,2]+rotp[2,1])/(4.0*v[2])
# return theta and v
return v,th
def axangsu2(self,v,th):
"""
Calculate the rotation SU(2) matrix - see Elk's axangsu2 routine.
"""
su2=numpy.zeros([2,2], numpy.complex_)
t1=numpy.sqrt(numpy.dot(v,v))
if(t1<1E-8):
raise "sym_converter : zero length axis vector!"
# normalise the vector
t1=1.0/t1
x=v[0]*t1; y=v[1]*t1; z=v[2]*t1
#calculate the SU(2) matrix
cs=numpy.cos(0.5*th)
sn=numpy.sin(0.5*th)
su2[0,0]=cs-z*sn*1j
su2[0,1]=-y*sn-x*sn*1j
su2[1,0]=y*sn-x*sn*1j
su2[1,1]=cs+z*sn*1j
#return the SU(2) matrix
return su2
def v3frac(self,v,eps):
"""
This finds the fractional part of 3-vector v components. This uses the
same method as in Elk (version 6.2.8) r3fac subroutine.
"""
v[0]=v[0]-numpy.floor(v[0])
if(v[0] < 0): v[0]+=1
if((1-v[0]) < eps): v[0]=0
if(v[0] < eps): v[0]=0
v[1]=v[1]-numpy.floor(v[1])
if(v[1] < 0): v[1]+=1
if((1-v[1]) < eps): v[1]=0
if(v[1] < eps): v[1]=0
v[2]=v[2]-numpy.floor(v[2])
if(v[2] < 0): v[2]+=1
if((1-v[2]) < eps): v[2]=0
if(v[2] < eps): v[2]=0
return v
def gen_perm(self,nsym,ns,na,natmtot,symmat,tr,atpos,epslat=1E-6):
"""
Generate the atom permutations per symmetry.
"""
perm=[]
iea=[]
for isym in range(nsym):
iea.append(numpy.zeros([natmtot,ns], numpy.int_))
#loop over species
for js in range(ns):
#loop over species atoms
v=numpy.zeros([3,na[js]], numpy.float_)
v2=numpy.zeros(3, numpy.float_)
for ia in range(na[js]):
v[:,ia]=self.v3frac(atpos[js][ia][0:3],epslat)
for ia in range(na[js]):
v2[:]=numpy.matmul(symmat[isym][:,:],(atpos[js][ia][0:3]+tr[isym]))
v2[:]=self.v3frac(v2,epslat)
for ja in range(na[js]):
t1=sum(abs(v[:,ja]-v2[:])) #check
if(t1 < epslat):
iea[isym][ja,js]=ia
break
#put iea into perm format
for isym in range(nsym):
perm.append([])
ja=0
prv_atms=0
for js in range(ns):
for ia in range(na[js]):
perm[isym].append(iea[isym][ia,js]+prv_atms+1)
ja+=1
prv_atms+=na[js]
#output perm
return perm
def symlat_to_complex_harmonics(self,nsym,n_shells,symlat,shells):
"""
This calculates the Elk (crystal) symmetries in complex spherical harmonics
This follows the methodology used in Elk's rotzflm, ylmrot and ylmroty routines.
"""
#need SciPy routines to get Euler angles - need version 1.4+
#from scipy.spatial.transform import Rotation as R
symmat=[]
rot=numpy.identity(3, numpy.float_)
angi=numpy.zeros(3, numpy.float_)
#loop over symmetries
for isym in range(nsym):
symmat.append([])
for ish in range(n_shells):
l=shells[ish]['l']
symmat[isym].append(numpy.zeros([2*l+1, 2*l+1], numpy.complex_))
#get determinant
det=numpy.linalg.det(symlat[isym])
p=1
#p is -1 for improper symmetries
if(det<0.0): p=-1
rot[:,:]=p*symlat[isym][:,:]
#r=R.from_matrix(rot)
#get the y-convention Euler angles as used by Elk.
#ang=r.as_euler('zyz')
ang=self.zyz_euler(rot)
#Elk uses inverse rotations, i.e. the function is being rotated, not the spherical harmonics
angi[0]=-ang[2]
angi[1]=-ang[1]
angi[2]=-ang[0]
#calculate the symmetry in the complex spherical harmonic basis.
d = self.ylmrot(p,angi,l)
symmat[isym][ish][:,:] = d[:,:]
#return the complex spherical harmonic
return symmat
def zyz_euler(self,rot):
"""
This calculates the Euler angles of matrix rot in the y-convention.
See Elk's roteuler routine.
This will be made redundent when TRIQS uses scipy version 1.4+
"""
eps=1E-8
pi=numpy.pi
ang=numpy.zeros(3, numpy.float_)
#get the Euler angles
if((abs(rot[2,0])>eps) or (abs(rot[2,1])>eps)):
ang[0]=numpy.arctan2(rot[2,1],rot[2,0])
if(abs(rot[2,0])>abs(rot[2,1])):
ang[1]=numpy.arctan2(rot[2,0]/numpy.cos(ang[0]),rot[2,2])
else:
ang[1]=numpy.arctan2(rot[2,1]/numpy.sin(ang[0]),rot[2,2])
ang[2]=numpy.arctan2(rot[1,2],-rot[0,2])
else:
ang[0]=numpy.arctan2(rot[0,1],rot[0,0])
if(rot[2,2]>0.0):
ang[1]=0.0
ang[2]=0.0
else:
ang[1]=pi
ang[2]=pi
#return Euler angles
return ang
def ylmrot(self,p,angi,l):
"""
calculates the rotation matrix in complex spherical harmonics for l.
THIS HAS ONLY BEEN TESTED FOR l=2.
"""
d=numpy.identity(2*l+1, numpy.complex_)
# generate the rotation matrix about the y-axis
dy=self.ylmroty(angi[1],l)
# apply inversion to odd l values if required
if(p==-1):
if(l % 2.0 != 0):
dy*=-1
# rotation by alpha and gamma
for m1 in range(-l,l+1,1):
lm1=l+m1
for m2 in range(-l,l+1,1):
lm2=l+m2
t1=-m1*angi[0]-m2*angi[2]
d[lm1,lm2]=dy[lm1,lm2]*(numpy.cos(t1)+1j*numpy.sin(t1))
#return the rotation matrix
return d
def ylmroty(self,beta,l):
"""
returns the rotation matrix around the y-axis with angle beta.
This uses the same real matrix formual as in Elk - see Elk's manual for ylmroty description
"""
#import the factorial function - needed for later versions of scipy (needs testing)
from scipy import special as spec
#calculates the rotation matrix in complex spherical harmonics for l
dy=numpy.identity(2*l+1, numpy.float_)
#sine and cosine of beta
cb=numpy.cos(beta/2.0)
sb=numpy.sin(beta/2.0)
# generate the rotaion operator for m-components of input l
for m1 in range(-l,l+1,1):
for m2 in range(-l,l+1,1):
sm=0.0
minlm=numpy.amin([l+m1, l-m2]) + 1
for k in range(minlm):
if(((l+m1-k)>=0) and ((l-m2-k)>=0) and ((m2-m1+k)>=0)):
j=2*(l-k)+m1-m2
if(j==0):
t1=1.0
else:
t1=cb**j
j=2*k+m2-m1
if(j!=0):
t1=t1*sb**j
t2=t1/(spec.factorial(k)*spec.factorial(l+m1-k)*spec.factorial(l-m2-k)*spec.factorial(m2-m1+k))
if(k % 2.0 != 0):
t2=-t2
sm+=t2
t1=numpy.sqrt(spec.factorial(l+m1)*spec.factorial(l-m1)*spec.factorial(l+m2)*spec.factorial(l-m2))
dy[m1+l,m2+l]=t1*sm
#return y-rotation matrix
return dy