dft_tools/python/block_structure.py

443 lines
16 KiB
Python

import copy
import numpy as np
from pytriqs.gf.local import GfImFreq, BlockGf
from ast import literal_eval
from warnings import warn
class BlockStructure(object):
""" Contains information about the Green function structure.
This class contains information about the structure of the solver
and sumk Green functions and the mapping between them.
Parameters
----------
gf_struct_sumk : list of list of tuple
gf_struct_sumk[ish][idx] = (block_name,list of indices in block)
for correlated shell ish; idx is just a counter in the list
gf_struct_solver : list of dict
gf_struct_solver[ish][block] = list of indices in that block
for *inequivalent* correlated shell ish
solver_to_sumk : list of dict
solver_to_sumk[ish][(from_block,from_idx)] = (to_block,to_idx)
maps from the solver block and index to the sumk block and index
for *inequivalent* correlated shell ish
sumk_to_solver : list of dict
sumk_to_solver[ish][(from_block,from_idx)] = (to_block,to_idx)
maps from the sumk block and index to the solver block and index
for *inequivalent* correlated shell ish
solver_to_sumk_block : list of dict
solver_to_sumk_block[ish][from_block] = to_block
maps from the solver block to the sumk block
for *inequivalent* correlated shell ish
"""
def __init__(self,gf_struct_sumk=None,
gf_struct_solver=None,
solver_to_sumk=None,
sumk_to_solver=None,
solver_to_sumk_block=None):
self.gf_struct_sumk = gf_struct_sumk
self.gf_struct_solver = gf_struct_solver
self.solver_to_sumk = solver_to_sumk
self.sumk_to_solver = sumk_to_solver
self.solver_to_sumk_block = solver_to_sumk_block
@classmethod
def full_structure(cls,gf_struct,corr_to_inequiv):
""" Construct structure that maps to itself.
This has the same structure for sumk and solver, and the
mapping solver_to_sumk and sumk_to_solver is one-to-one.
Parameters
----------
gf_struct : list of dict
gf_struct[ish][block] = list of indices in that block
for (inequivalent) correlated shell ish
corr_to_inequiv : list
gives the mapping from correlated shell csh to inequivalent
correlated shell icsh, so that corr_to_inequiv[csh]=icsh
e.g. SumkDFT.corr_to_inequiv
if None, each inequivalent correlated shell is supposed to
be correspond to just one correlated shell with the same
index; there is not default, None has to be set explicitly!
"""
solver_to_sumk = []
s2sblock = []
gs_sumk = []
for ish in range(len(gf_struct)):
so2su = {}
so2sublock = {}
gss = []
for block in gf_struct[ish]:
so2sublock[block]=block
for ind in gf_struct[ish][block]:
so2su[(block,ind)]=(block,ind)
gss.append((block,gf_struct[ish][block]))
solver_to_sumk.append(so2su)
s2sblock.append(so2sublock)
gs_sumk.append(gss)
# gf_struct_sumk is not given for each inequivalent correlated
# shell, but for every correlated shell!
if corr_to_inequiv is not None:
gs_sumk_all = [None]*len(corr_to_inequiv)
for i in range(len(corr_to_inequiv)):
gs_sumk_all[i] = gs_sumk[corr_to_inequiv[i]]
else:
gs_sumk_all = gs_sumk
return cls(gf_struct_solver=copy.deepcopy(gf_struct),
gf_struct_sumk = gs_sumk_all,
solver_to_sumk = copy.deepcopy(solver_to_sumk),
sumk_to_solver = solver_to_sumk,
solver_to_sumk_block = s2sblock)
def pick_gf_struct_solver(self,new_gf_struct):
""" Pick selected orbitals within blocks.
This throws away parts of the Green's function that (for some
reason - be sure that you know what you're doing) shouldn't be
included in the calculation.
To drop an entire block, just don't include it.
To drop a certain index within a block, just don't include it.
If it was before:
[{'up':[0,1],'down':[0,1],'left':[0,1]}]
to choose the 0th index of the up block and the 1st index of
the down block and drop the left block, the new_gf_struct would
have to be
[{'up':[0],'down':[1]}]
Note that the indices will be renamed to be a 0-based
sequence of integers, i.e. the new structure will actually
be [{'up':[0],'down':[0]}].
For dropped indices, sumk_to_solver will map to (None,None).
Parameters
----------
new_gf_struct : list of dict
formatted the same as gf_struct_solver:
new_gf_struct[ish][block]=list of indices in that block.
"""
for ish in range(len(self.gf_struct_solver)):
gf_struct = new_gf_struct[ish]
# create new solver_to_sumk
so2su={}
so2su_block = {}
for blk,idxs in gf_struct.items():
for i in range(len(idxs)):
so2su[(blk,i)]=self.solver_to_sumk[ish][(blk,idxs[i])]
so2su_block[blk]=so2su[(blk,i)][0]
self.solver_to_sumk[ish] = so2su
self.solver_to_sumk_block[ish] = so2su_block
# create new sumk_to_solver
for k,v in self.sumk_to_solver[ish].items():
blk,ind=v
if blk in gf_struct and ind in gf_struct[blk]:
new_ind = gf_struct[blk].index(ind)
self.sumk_to_solver[ish][k]=(blk,new_ind)
else:
self.sumk_to_solver[ish][k]=(None,None)
# reindexing gf_struct so that it starts with 0
for k in gf_struct:
gf_struct[k]=range(len(gf_struct[k]))
self.gf_struct_solver[ish]=gf_struct
def pick_gf_struct_sumk(self,new_gf_struct):
""" Pick selected orbitals within blocks.
This throws away parts of the Green's function that (for some
reason - be sure that you know what you're doing) shouldn't be
included in the calculation.
To drop an entire block, just don't include it.
To drop a certain index within a block, just don't include it.
If it was before:
[{'up':[0,1],'down':[0,1],'left':[0,1]}]
to choose the 0th index of the up block and the 1st index of
the down block and drop the left block, the new_gf_struct would
have to be
[{'up':[0],'down':[1]}]
Note that the indices will be renamed to be a 0-based
sequence of integers.
For dropped indices, sumk_to_solver will map to (None,None).
Parameters
----------
new_gf_struct : list of dict
formatted the same as gf_struct_solver:
new_gf_struct[ish][block]=list of indices in that block.
However, the indices are not according to the solver Gf
but the sumk Gf.
"""
gfs = []
# construct gfs, which is the equivalent of new_gf_struct
# but according to the solver Gf, by using the sumk_to_solver
# mapping
for ish in range(len(new_gf_struct)):
gfs.append({})
for block in new_gf_struct[ish].keys():
for ind in new_gf_struct[ish][block]:
ind_sol = self.sumk_to_solver[ish][(block,ind)]
if not ind_sol[0] in gfs[ish]:
gfs[ish][ind_sol[0]]=[]
gfs[ish][ind_sol[0]].append(ind_sol[1])
self.pick_gf_struct_solver(gfs)
def map_gf_struct_solver(self,mapping):
""" Map the Green function structure from one struct to another.
Parameters
----------
mapping : list of dict
the dict consists of elements
(from_block,from_index) : (to_block,to_index)
that maps from one structure to the other
"""
for ish in range(len(mapping)):
gf_struct = {}
so2su = {}
su2so = {}
so2su_block = {}
for frm,to in mapping[ish].iteritems():
if not to[0] in gf_struct:
gf_struct[to[0]]=[]
gf_struct[to[0]].append(to[1])
so2su[to]=self.solver_to_sumk[ish][frm]
su2so[self.solver_to_sumk[ish][frm]]=to
if to[0] in so2su_block:
if so2su_block[to[0]] != \
self.solver_to_sumk_block[ish][frm[0]]:
warn("solver block '{}' maps to more than one sumk block: '{}', '{}'".format(
to[0],so2su_block[to[0]],self.solver_to_sumk_block[ish][frm[0]]))
else:
so2su_block[to[0]]=\
self.solver_to_sumk_block[ish][frm[0]]
for k in self.sumk_to_solver[ish].keys():
if not k in su2so:
su2so[k] = (None,None)
self.gf_struct_solver[ish]=gf_struct
self.solver_to_sumk[ish]=so2su
self.sumk_to_solver[ish]=su2so
self.solver_to_sumk_block[ish]=so2su_block
def create_gf(self,ish=0,gf_function=GfImFreq,**kwargs):
""" Create a zero BlockGf having the gf_struct_solver structure.
When using GfImFreq as gf_function, typically you have to
supply beta as keyword argument.
Parameters
----------
ish : int
shell index
gf_function : constructor
function used to construct the Gf objects constituting the
individual blocks; default: GfImFreq
**kwargs :
options passed on to the Gf constructor for the individual
blocks
"""
names = self.gf_struct_solver[ish].keys()
blocks=[]
for n in names:
G = gf_function(indices=self.gf_struct_solver[ish][n],**kwargs)
blocks.append(G)
G = BlockGf(name_list = names, block_list = blocks)
return G
def convert_gf(self,G,G_struct,ish=0,show_warnings=True,**kwargs):
""" Convert BlockGf from its structure to this structure.
.. warning::
Elements that are zero in the new structure due to
the new block structure will be just ignored, thus
approximated to zero.
Parameters
----------
G : BlockGf
the Gf that should be converted
G_struct : GfStructure
the structure ofthat G
ish : int
shell index
show_warnings : bool
whether to show warnings when elements of the Green's
function get thrown away
**kwargs :
options passed to the constructor for the new Gf
"""
G_new = self.create_gf(ish=ish,**kwargs)
for block in G_struct.gf_struct_solver[ish].keys():
for i1 in G_struct.gf_struct_solver[ish][block]:
for i2 in G_struct.gf_struct_solver[ish][block]:
i1_sumk = G_struct.solver_to_sumk[ish][(block,i1)]
i2_sumk = G_struct.solver_to_sumk[ish][(block,i2)]
i1_sol = self.sumk_to_solver[ish][i1_sumk]
i2_sol = self.sumk_to_solver[ish][i2_sumk]
if i1_sol[0] is None or i2_sol[0] is None:
if show_warnings:
warn(('Element {},{} of block {} of G is not present '+
'in the new structure').format(i1,i2,block))
continue
if i1_sol[0]!=i2_sol[0]:
if show_warnings:
warn(('Element {},{} of block {} of G is approximated '+
'to zero to match the new structure.').format(
i1,i2,block))
continue
G_new[i1_sol[0]][i1_sol[1],i2_sol[1]] = \
G[block][i1,i2]
return G_new
def approximate_as_diagonal(self):
""" Create a structure for a GF with zero off-diagonal elements.
.. warning::
In general, this will throw away non-zero elements of the
Green's function. Be sure to verify whether this approximation
is justified.
"""
self.gf_struct_solver=[]
self.solver_to_sumk=[]
self.solver_to_sumk_block=[]
for ish in range(len(self.sumk_to_solver)):
self.gf_struct_solver.append({})
self.solver_to_sumk.append({})
self.solver_to_sumk_block.append({})
for frm,to in self.sumk_to_solver[ish].iteritems():
if to[0] is not None:
self.gf_struct_solver[ish][frm[0]+'_'+str(frm[1])]=[0]
self.sumk_to_solver[ish][frm]=(frm[0]+'_'+str(frm[1]),0)
self.solver_to_sumk[ish][(frm[0]+'_'+str(frm[1]),0)]=frm
self.solver_to_sumk_block[ish][frm[0]+'_'+str(frm[1])]=frm[0]
def __eq__(self,other):
def compare(one,two):
if type(one)!=type(two):
return False
if one is None and two is None:
return True
if isinstance(one,list) or isinstance(one,tuple):
if len(one) != len(two):
return False
for x,y in zip(one,two):
if not compare(x,y):
return False
return True
elif isinstance(one,int):
return one==two
elif isinstance(one,str):
return one==two
elif isinstance(one,dict):
if set(one.keys()) != set(two.keys()):
return False
for k in set(one.keys()).intersection(two.keys()):
if not compare(one[k],two[k]):
return False
return True
warn('Cannot compare {}'.format(type(one)))
return False
for prop in [ "gf_struct_sumk", "gf_struct_solver",
"solver_to_sumk", "sumk_to_solver", "solver_to_sumk_block"]:
if not compare(getattr(self,prop),getattr(other,prop)):
return False
return True
def copy(self):
return copy.deepcopy(self)
def __reduce_to_dict__(self):
""" Reduce to dict for HDF5 export."""
ret = {}
for element in [ "gf_struct_sumk", "gf_struct_solver",
"solver_to_sumk_block"]:
ret[element] = getattr(self,element)
def construct_mapping(mapping):
d = []
for ish in range(len(mapping)):
d.append({})
for k,v in mapping[ish].iteritems():
d[ish][repr(k)] = repr(v)
return d
ret['solver_to_sumk']=construct_mapping(self.solver_to_sumk)
ret['sumk_to_solver']=construct_mapping(self.sumk_to_solver)
return ret
@classmethod
def __factory_from_dict__(cls,name,D) :
""" Create from dict for HDF5 import."""
def reconstruct_mapping(mapping):
d = []
for ish in range(len(mapping)):
d.append({})
for k,v in mapping[ish].iteritems():
# literal_eval is a saje alternative to eval
d[ish][literal_eval(k)] = literal_eval(v)
return d
D['solver_to_sumk']=reconstruct_mapping(D['solver_to_sumk'])
D['sumk_to_solver']=reconstruct_mapping(D['sumk_to_solver'])
return cls(**D)
def __str__(self):
s=''
s+= "gf_struct_sumk "+str( self.gf_struct_sumk)+'\n'
s+= "gf_struct_solver "+str(self.gf_struct_solver)+'\n'
s+= "solver_to_sumk_block "+str(self.solver_to_sumk_block)+'\n'
for el in ['solver_to_sumk','sumk_to_solver']:
s+=el+'\n'
element=getattr(self,el)
for ish in range(len(element)):
s+=' shell '+str(ish)+'\n'
def keyfun(el):
return '{}_{:05d}'.format(el[0],el[1])
keys = sorted(element[ish].keys(),key=keyfun)
for k in keys:
s+=' '+str(k)+str(element[ish][k])+'\n'
return s
from pytriqs.archive.hdf_archive_schemes import register_class
register_class(BlockStructure)