├── .gitignore ├── LICENSE ├── README.md ├── examples ├── NiO │ ├── CAS-CCSD │ │ └── run_dmft.py │ ├── nio_dft.py │ ├── nio_gw.py │ ├── nio_set_ham.py │ ├── run_dft │ ├── run_dmft │ ├── run_dmft.py │ ├── run_gw │ └── run_ham ├── Si │ ├── CAS-CCSD │ │ └── run_dmft.py │ ├── run_dmft │ ├── run_dmft.py │ ├── run_gw │ ├── run_ham │ ├── si_gw.py │ └── si_set_ham.py ├── SrMoO3 │ ├── README.md │ ├── run_dmft.py │ ├── srmoo3_gw.py │ ├── srmoo3_interpolate.py │ ├── srmoo3_lda.py │ └── srmoo3_set_ham.py └── interpolation │ ├── diamond_gw_interpolate.py │ ├── diamond_gw_interpolate_iao.py │ └── diamond_hf_interpolate.py └── fcdmft ├── __init__.py ├── dmft ├── __init__.py ├── dmft_solver.py ├── gwdmft.py └── run_dmft.py ├── gw ├── __init__.py ├── mol │ ├── __init__.py │ ├── gw_ac.py │ ├── gw_dc.py │ ├── gw_gf.py │ ├── ugw_ac.py │ ├── ugw_dc.py │ └── ugw_gf.py └── pbc │ ├── __init__.py │ ├── krgw_ac.py │ ├── krgw_gf.py │ ├── kugw_ac.py │ └── kugw_gf.py ├── rpa ├── __init__.py ├── mol │ ├── __init__.py │ ├── rpa.py │ └── urpa.py └── pbc │ ├── __init__.py │ ├── krpa.py │ ├── kurpa.py │ ├── rpa.py │ └── urpa.py ├── solver ├── casno.py ├── ccgf.py ├── gfdmrg.py ├── gfdmrg_sz.py ├── gmres.py ├── mpiccgf.py ├── mpiuccgf.py ├── scf_mu.py ├── ucc_eri.py └── uccgf.py └── utils ├── cholesky.py ├── interpolate.py └── write.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 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/ 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fcdmft 2 | ====== 3 | 4 | Ab initio full cell dynamical mean-field theory (DMFT) and GW+DMFT for solids based on PySCF 5 | 6 | Authors: Tianyu Zhu (tianyu.zhu@yale.edu), Huanchen Zhai, Zhihao Cui, Linqing Peng, Garnet Chan 7 | 8 | Installation 9 | ------------ 10 | 11 | * Prerequisites 12 | - PySCF 1.7 or higher, and all dependencies 13 | - libdmet (by Zhi-Hao Cui, https://github.com/gkclab/libdmet_preview) 14 | - block2 (optional, by Huanchen Zhai, https://github.com/block-hczhai/block2-preview) 15 | - CheMPS2 (optional) 16 | 17 | * You need to set environment variable `PYTHONPATH` to export fcdmft to Python. 18 | E.g. if fcdmft is installed in `/opt`, your `PYTHONPATH` should be 19 | 20 | export PYTHONPATH=/opt/fcdmft:$PYTHONPATH 21 | 22 | Features 23 | -------- 24 | 25 | * Full cell G0W0+DMFT and HF+DMFT (mixed MPI and OpenMP parallelization) 26 | 27 | * Hamiltonian-based impurity solvers 28 | * Coupled-cluster Green's function 29 | * Quantum chemistry dynamical DMRG (from block2) 30 | * DMRG-MRCI Green's function (from block2) 31 | * FCI/ED Green's function (from CheMPS2, for test only) 32 | 33 | * Molecular and periodic G0W0 34 | 35 | * Molecular and periodic RPA 36 | 37 | * CAS-CI treatment of the impurity problem 38 | 39 | QuickStart 40 | ---------- 41 | 42 | You can find Python scripts for running DMFT calculations in `/fcdmft/examples`. 43 | For example, in `/fcdmft/examples/Si`, the steps to run a full cell GW+DMFT 44 | calculation are: 45 | 46 | 1. Perform DFT and GW calculations by running `si_gw.py` 47 | (Note: For large systems, GW should be performed separately using multiple nodes, 48 | i.e. MPI, see `/fcdmft/examples/NiO`); 49 | 50 | 2. Derive impurity Hamiltonian and GW double counting term by running `si_set_ham.py`; 51 | 52 | 3. Perform GW+DMFT calculation by running `run_dmft.py` (serial or MPI/OpenMP). 53 | All DMFT parameters should be set in `run_dmft.py`. In this example, CCSD-GF is used 54 | as impurity solver. See `run_dmft` for sample Slurm submission script. 55 | 56 | 4. (Optional) One may use a CAS-CI treatment when solving the impurity problem. See 57 | `CAS-CCSD/run_dmft.py` for setting CAS-related parameters. 58 | 59 | References 60 | ---------- 61 | 62 | Please cite the following papers in publications utilizing the fcdmft package: 63 | 64 | * T. Zhu and G. K.-L. Chan, Phys. Rev. X 11, 021006 (2021) 65 | 66 | * T. Zhu, Z.-H. Cui, and G. K.-L. Chan, J. Chem. Theory Comput. 16, 141-153 (2020) 67 | 68 | * T. Zhu, C. A. Jimenez-Hoyos, J. McClain, T. C. Berkelbach, and G. K.-L. Chan, Phys. Rev. B 100, 115154 (2019) 69 | 70 | Cite the following paper if GW code is used: 71 | 72 | * T. Zhu and G. K.-L. Chan, J. Chem. Theory Comput. 17, 727-741 (2021) 73 | 74 | Cite the following paper if libdmet package is used: 75 | 76 | * Z.-H. Cui, T. Zhu, and G. K.-L. Chan, J. Chem. Theory Comput. 16, 119-129 (2020) 77 | -------------------------------------------------------------------------------- /examples/NiO/nio_dft.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pyscf.pbc import df, dft, gto 3 | import numpy as np 4 | import os, h5py 5 | from pyscf.pbc.lib import chkfile 6 | from pyscf import lib 7 | 8 | einsum = lib.einsum 9 | 10 | def at(a0): 11 | a = np.zeros((3,3)) # generators of rhombohedral cell 12 | a[0,:] = ( 1.00, 0.50, 0.50) # 13 | a[1,:] = ( 0.50, 1.00, 0.50) # 14 | a[2,:] = ( 0.50, 0.50, 1.00) 15 | a *= a0 16 | 17 | g = np.zeros((4,3)) # ions in the cell, in crystal coordinates 18 | g[0,:] = ( 0.00, 0.00, 0.00) # Ni 19 | g[1,:] = ( 0.50, 0.50, 0.50) # Ni 20 | g[2,:] = ( 0.25, 0.25, 0.25) # O 21 | g[3,:] = ( 0.75, 0.75, 0.75) # O 22 | 23 | pos=[] 24 | for mu in range(4): 25 | v=[0.00,0.00,0.00] 26 | for nu in range(3): 27 | v=v[:]+g[mu,nu]*a[nu,:] # actual coordinates of the ions 28 | z='Ni' 29 | if(mu>1): z='O' 30 | pos.append([z,v]) 31 | 32 | return a,pos 33 | 34 | a0 = 4.17 35 | vec, posion = at(a0) 36 | cell = gto.Cell() 37 | cell.build(unit = 'angstrom', 38 | a = vec, 39 | atom = posion, 40 | dimension = 3, 41 | max_memory = 64000, 42 | verbose = 5, 43 | basis='gth-dzvp-molopt-sr', 44 | pseudo='gth-pbe', 45 | precision=1e-12) 46 | 47 | kmesh = [4,4,4] 48 | kpts = cell.make_kpts(kmesh,scaled_center=[0,0,0],wrap_around=True) 49 | gdf = df.GDF(cell, kpts) 50 | gdf.auxbasis = df.aug_etb(cell, beta=2.3) 51 | gdf_fname = 'gdf_ints_444.h5' 52 | gdf._cderi_to_save = gdf_fname 53 | gdf.mesh = np.asarray([25, 25, 25]) 54 | if not os.path.isfile(gdf_fname): 55 | gdf.build() 56 | 57 | chkfname = 'nio_444.chk' 58 | if os.path.isfile(chkfname): 59 | kmf = dft.KUKS(cell, kpts).density_fit() 60 | kmf.xc = 'pbe' 61 | kmf.with_df = gdf 62 | kmf.with_df._cderi = gdf_fname 63 | kmf.conv_tol = 1e-12 64 | data = chkfile.load(chkfname, 'scf') 65 | kmf.__dict__.update(data) 66 | else: 67 | kmf = dft.KUKS(cell, kpts).density_fit() 68 | kmf.xc = 'pbe' 69 | kmf.with_df = gdf 70 | kmf.with_df._cderi = gdf_fname 71 | kmf.conv_tol = 1e-12 72 | kmf.chkfile = chkfname 73 | 74 | aoind = cell.aoslice_by_atom() 75 | dm = kmf.get_init_guess() 76 | dm[0,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] = 2. * dm[0,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] 77 | dm[0,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] = 0. * dm[0,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] 78 | dm[1,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] = 0. * dm[1,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] 79 | dm[1,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] = 2. * dm[1,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] 80 | kmf.kernel(dm) 81 | 82 | -------------------------------------------------------------------------------- /examples/NiO/nio_gw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pyscf.pbc import df, dft, gto 3 | import numpy as np 4 | import os, h5py 5 | from pyscf.pbc.lib import chkfile 6 | from pyscf import lib 7 | from fcdmft.gw.pbc import kugw_gf 8 | from fcdmft.utils import write 9 | from mpi4py import MPI 10 | 11 | rank = MPI.COMM_WORLD.Get_rank() 12 | size = MPI.COMM_WORLD.Get_size() 13 | comm = MPI.COMM_WORLD 14 | 15 | einsum = lib.einsum 16 | 17 | def at(a0): 18 | a = np.zeros((3,3)) # generators of rhombohedral cell 19 | a[0,:] = ( 1.00, 0.50, 0.50) # 20 | a[1,:] = ( 0.50, 1.00, 0.50) # 21 | a[2,:] = ( 0.50, 0.50, 1.00) 22 | a *= a0 23 | 24 | g = np.zeros((4,3)) # ions in the cell, in crystal coordinates 25 | g[0,:] = ( 0.00, 0.00, 0.00) # Ni 26 | g[1,:] = ( 0.50, 0.50, 0.50) # Ni 27 | g[2,:] = ( 0.25, 0.25, 0.25) # O 28 | g[3,:] = ( 0.75, 0.75, 0.75) # O 29 | 30 | pos=[] 31 | for mu in range(4): 32 | v=[0.00,0.00,0.00] 33 | for nu in range(3): 34 | v=v[:]+g[mu,nu]*a[nu,:] # actual coordinates of the ions 35 | z='Ni' 36 | if(mu>1): z='O' 37 | pos.append([z,v]) 38 | 39 | return a,pos 40 | 41 | a0 = 4.17 42 | vec, posion = at(a0) 43 | cell = gto.Cell() 44 | cell.build(unit = 'angstrom', 45 | a = vec, 46 | atom = posion, 47 | dimension = 3, 48 | max_memory = 32000, 49 | verbose = 5, 50 | basis='gth-dzvp-molopt-sr', 51 | pseudo='gth-pbe', 52 | precision=1e-12) 53 | 54 | kmesh = [4,4,4] 55 | kpts = cell.make_kpts(kmesh,scaled_center=[0,0,0],wrap_around=True) 56 | gdf = df.GDF(cell, kpts) 57 | gdf.auxbasis = df.aug_etb(cell, beta=2.3) 58 | gdf_fname = 'gdf_ints_444.h5' 59 | gdf._cderi_to_save = gdf_fname 60 | gdf.mesh = np.asarray([25, 25, 25]) 61 | if not os.path.isfile(gdf_fname): 62 | gdf.build() 63 | 64 | chkfname = 'nio_444.chk' 65 | if os.path.isfile(chkfname): 66 | kmf = dft.KUKS(cell, kpts).density_fit() 67 | kmf.xc = 'pbe' 68 | kmf.with_df = gdf 69 | kmf.with_df._cderi = gdf_fname 70 | kmf.conv_tol = 1e-12 71 | data = chkfile.load(chkfname, 'scf') 72 | kmf.__dict__.update(data) 73 | else: 74 | kmf = dft.KUKS(cell, kpts).density_fit() 75 | kmf.xc = 'pbe' 76 | kmf.with_df = gdf 77 | kmf.with_df._cderi = gdf_fname 78 | kmf.conv_tol = 1e-12 79 | kmf.chkfile = chkfname 80 | 81 | aoind = cell.aoslice_by_atom() 82 | dm = kmf.get_init_guess() 83 | dm[0,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] = 2. * dm[0,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] 84 | dm[0,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] = 0. * dm[0,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] 85 | dm[1,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] = 0. * dm[1,:,aoind[0][2]:aoind[0][3], aoind[0][2]:aoind[0][3]] 86 | dm[1,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] = 2. * dm[1,:,aoind[1][2]:aoind[1][3], aoind[1][2]:aoind[1][3]] 87 | kmf.kernel(dm) 88 | 89 | gw = kugw_gf.KUGWGF(kmf) 90 | gw.ac = 'pade' 91 | Ha2ev = 27.211386 92 | gw.eta = 0.2/Ha2ev 93 | gw.fullsigma = True 94 | gw.fc = True 95 | omega = np.linspace(6./Ha2ev,24./Ha2ev,91) 96 | # writefile must >= 1 for GW+DMFT calc 97 | gf, gf0, sigma = gw.kernel(omega=omega, writefile=1) 98 | nkpts = gw.nkpts 99 | gf = 1./nkpts * np.sum(gf, axis=1) 100 | gf0 = 1./nkpts * np.sum(gf0, axis=1) 101 | 102 | if rank == 0: 103 | outdir = 'GW_DOS' 104 | if not os.path.isdir(outdir): 105 | os.mkdir(outdir) 106 | write.write_gf_to_dos(outdir+'/nio_gw_dos', omega, gf) 107 | write.write_gf_to_dos(outdir+'/nio_pbe_dos', omega, gf0) 108 | 109 | mo_energy = gw.mo_energy 110 | nocca, noccb = gw.nocc 111 | homo = -99.; lumo = 99. 112 | for k in range(nkpts): 113 | if homo < mo_energy[0][k][nocca-1]: 114 | homo = mo_energy[0][k][nocca-1] 115 | if lumo > mo_energy[1][k][nocca]: 116 | lumo = mo_energy[1][k][nocca] 117 | if rank == 0: 118 | print ('VBM, CBM, Gap', homo*Ha2ev, lumo*Ha2ev, (lumo-homo)*Ha2ev) 119 | -------------------------------------------------------------------------------- /examples/NiO/nio_set_ham.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | import h5py, os 4 | from libdmet_solid.system import lattice 5 | from libdmet_solid.basis_transform import make_basis 6 | from libdmet_solid.basis_transform import eri_transform 7 | import libdmet_solid.utils.logger as log 8 | 9 | from pyscf.pbc import df, dft, scf, gto 10 | from pyscf.pbc.lib import chkfile 11 | from pyscf import lib 12 | from pyscf import gto as gto_mol 13 | from pyscf import scf as scf_mol 14 | 15 | from fcdmft.gw.mol import ugw_dc 16 | from fcdmft.utils import cholesky 17 | 18 | log.verbose = 'DEBUG1' 19 | 20 | einsum = lib.einsum 21 | 22 | # NOTE: lattice system setup by user 23 | def at(a0): 24 | a = np.zeros((3,3)) # generators of rhombohedral cell 25 | a[0,:] = ( 1.00, 0.50, 0.50) # 26 | a[1,:] = ( 0.50, 1.00, 0.50) # 27 | a[2,:] = ( 0.50, 0.50, 1.00) 28 | a *= a0 29 | 30 | g = np.zeros((4,3)) # ions in the cell, in crystal coordinates 31 | g[0,:] = ( 0.00, 0.00, 0.00) # Ni 32 | g[1,:] = ( 0.50, 0.50, 0.50) # Ni 33 | g[2,:] = ( 0.25, 0.25, 0.25) # O 34 | g[3,:] = ( 0.75, 0.75, 0.75) # O 35 | 36 | pos=[] 37 | for mu in range(4): 38 | v=[0.00,0.00,0.00] 39 | for nu in range(3): 40 | v=v[:]+g[mu,nu]*a[nu,:] # actual coordinates of the ions 41 | z='Ni' 42 | if(mu>1): z='O' 43 | pos.append([z,v]) 44 | 45 | return a,pos 46 | 47 | a0 = 4.17 48 | vec, posion = at(a0) 49 | cell = gto.Cell() 50 | cell.build(unit = 'angstrom', 51 | a = vec, 52 | atom = posion, 53 | dimension = 3, 54 | max_memory = 128000, 55 | verbose = 5, 56 | basis='gth-dzvp-molopt-sr', 57 | pseudo='gth-pbe', 58 | precision=1e-12) 59 | 60 | kmesh = [4,4,4] 61 | Lat = lattice.Lattice(cell, kmesh) 62 | kpts = Lat.kpts 63 | nao = Lat.nao 64 | nkpts = Lat.nkpts 65 | 66 | gdf = df.GDF(cell, kpts) 67 | gdf.auxbasis = df.aug_etb(cell, beta=2.3) 68 | gdf_fname = 'gdf_ints_444.h5' 69 | gdf._cderi_to_save = gdf_fname 70 | gdf.mesh = np.asarray([25, 25, 25]) 71 | if not os.path.isfile(gdf_fname): 72 | gdf.build() 73 | 74 | # NOTE: set up KRKS by user 75 | # obtain spin-restricted IAO basis (currently required by gw_dc) 76 | chkfname = 'nio_444_rks.chk' 77 | if os.path.isfile(chkfname): 78 | krmf = dft.KRKS(cell, kpts).density_fit() 79 | krmf = scf.addons.smearing_(krmf, sigma=5e-3, method="fermi") 80 | krmf.xc = 'pbe' 81 | krmf.with_df = gdf 82 | krmf.with_df._cderi = gdf_fname 83 | krmf.conv_tol = 1e-12 84 | data = chkfile.load(chkfname, 'scf') 85 | krmf.__dict__.update(data) 86 | else: 87 | krmf = dft.KRKS(cell, kpts).density_fit() 88 | krmf = scf.addons.smearing_(krmf, sigma=5e-3, method="fermi") 89 | krmf.xc = 'pbe' 90 | krmf.with_df = gdf 91 | krmf.with_df._cderi = gdf_fname 92 | krmf.conv_tol = 1e-12 93 | krmf.chkfile = chkfname 94 | krmf.kernel() 95 | 96 | # read KUKS 97 | chkfname = 'nio_444.chk' 98 | if os.path.isfile(chkfname): 99 | kmf = dft.KUKS(cell, kpts).density_fit() 100 | kmf.xc = 'pbe' 101 | kmf.with_df = gdf 102 | kmf.with_df._cderi = gdf_fname 103 | kmf.conv_tol = 1e-12 104 | data = chkfile.load(chkfname, 'scf') 105 | kmf.__dict__.update(data) 106 | 107 | # set spin 108 | mo_energy = np.asarray(kmf.mo_energy) 109 | mo_coeff = np.asarray(kmf.mo_coeff) 110 | if len(mo_energy.shape) == 2: 111 | spin = 1 112 | mo_energy = mo_energy[np.newaxis, ...] 113 | mo_coeff = mo_coeff[np.newaxis, ...] 114 | else: 115 | spin = 2 116 | 117 | # NOTE: choose IAO basis and use krmf by user 118 | # C_ao_lo: transformation matrix from AO to LO (IAO) basis 119 | MINAO = {'Ni':'gth-szv-molopt-sr', 'O':'gth-szv-molopt-sr'} 120 | C_ao_iao, C_ao_iao_val, C_ao_iao_virt = make_basis.get_C_ao_lo_iao(Lat, krmf, minao=MINAO, full_return=True) 121 | C_ao_lo = np.zeros((spin,nkpts,nao,nao),dtype=np.complex128) 122 | for s in range(spin): 123 | C_ao_lo[s] = C_ao_iao 124 | 125 | # C_mo_lo: transformation matrix from MO to LO (IAO) basis 126 | S_ao_ao = kmf.get_ovlp() 127 | C_mo_lo = np.zeros((spin,nkpts,nao,nao),dtype=np.complex128) 128 | for s in range(spin): 129 | for ki in range(nkpts): 130 | C_mo_lo[s][ki] = np.dot(np.dot(mo_coeff[s][ki].T.conj(), S_ao_ao[ki]), C_ao_lo[s][ki]) 131 | fn = 'C_mo_lo.h5' 132 | feri = h5py.File(fn, 'w') 133 | feri['C_ao_lo'] = np.asarray(C_ao_lo) 134 | feri['C_mo_lo'] = np.asarray(C_mo_lo) 135 | feri.close() 136 | 137 | # get DFT density matrix in IAO basis 138 | DM_ao = np.asarray(kmf.make_rdm1()) 139 | if len(DM_ao.shape) == 3: 140 | DM_ao = DM_ao[np.newaxis, ...] 141 | DM_lo = np.zeros((spin,nkpts,nao,nao),dtype=DM_ao.dtype) 142 | for s in range(spin): 143 | for ki in range(nkpts): 144 | Cinv = np.dot(C_ao_lo[s][ki].T.conj(),S_ao_ao[ki]) 145 | DM_lo[s][ki] = np.dot(np.dot(Cinv, DM_ao[s][ki]), Cinv.T.conj()) 146 | 147 | for s in range(spin): 148 | nelec_lo = np.trace(DM_lo[s].sum(axis=0)/nkpts) 149 | print ('Nelec imp', nelec_lo.real) 150 | fn = 'DM_iao_k.h5' 151 | feri = h5py.File(fn, 'w') 152 | feri['DM'] = np.asarray(DM_lo) 153 | feri.close() 154 | 155 | # get 4-index ERI 156 | eri = eri_transform.get_unit_eri_fast(cell, gdf, C_ao_lo=C_ao_lo, feri=gdf_fname) 157 | fn = 'eri_imp111_iao.h5' 158 | feri = h5py.File(fn, 'w') 159 | feri['eri'] = np.asarray(eri.real) 160 | feri.close() 161 | 162 | # get one-electron integrals 163 | hcore_ao = np.asarray(kmf.get_hcore()) 164 | JK_ao = np.asarray(kmf.get_veff()) 165 | if len(JK_ao.shape) == 3: 166 | JK_ao = JK_ao[np.newaxis, ...] 167 | hcore_lo = np.zeros((spin,nkpts,nao,nao),dtype=hcore_ao.dtype) 168 | JK_lo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 169 | for s in range(spin): 170 | for ki in range(nkpts): 171 | hcore_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), hcore_ao[ki]), C_ao_lo[s,ki]) 172 | JK_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), JK_ao[s,ki]), C_ao_lo[s,ki]) 173 | 174 | fn = 'hcore_JK_iao_k_dft.h5' 175 | feri = h5py.File(fn, 'w') 176 | feri['hcore'] = np.asarray(hcore_lo) 177 | feri['JK'] = np.asarray(JK_lo) 178 | feri.close() 179 | assert(np.max(np.abs(hcore_lo.sum(axis=1).imag/nkpts))<1e-6) 180 | assert(np.max(np.abs(JK_lo.sum(axis=1).imag/nkpts))<1e-6) 181 | 182 | # get HF JK term using DFT density 183 | kmf_hf = scf.KUHF(cell, kpts, exxdiv=None) 184 | kmf_hf.with_df = gdf 185 | kmf_hf.with_df._cderi = gdf_fname 186 | kmf_hf.max_cycle = 0 187 | JK_ao = np.asarray(kmf_hf.get_veff(dm_kpts=DM_ao)) 188 | if len(JK_ao.shape) == 3: 189 | JK_ao = JK_ao[np.newaxis, ...] 190 | 191 | # NOTE: choose finite size correction by user 192 | # set gw_fc to True if finite size correction is used in kgw 193 | gw_fc = True 194 | if gw_fc: 195 | # finite size correction to exchange 196 | vk_corr = -2./np.pi * (6.*np.pi**2/cell.vol/nkpts)**(1./3.) 197 | nocca = (cell.nelectron+cell.spin) // 2 198 | noccb = cell.nelectron - nocca 199 | JK_mo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 200 | for s in range(spin): 201 | if s == 0: 202 | nocc = nocca 203 | else: 204 | nocc = noccb 205 | for ki in range(nkpts): 206 | JK_mo[s,ki] = np.dot(np.dot(mo_coeff[s][ki].T.conj(), JK_ao[s,ki]), mo_coeff[s][ki]) 207 | for i in range(nocc): 208 | JK_mo[s,ki][i,i] = JK_mo[s,ki][i,i] + vk_corr 209 | 210 | JK_lo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 211 | for s in range(spin): 212 | for ki in range(nkpts): 213 | JK_lo[s,ki] = np.dot(np.dot(C_mo_lo[s,ki].T.conj(), JK_mo[s,ki]), C_mo_lo[s,ki]) 214 | else: 215 | JK_lo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 216 | for s in range(spin): 217 | for ki in range(nkpts): 218 | JK_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), JK_ao[s,ki]), C_ao_lo[s,ki]) 219 | 220 | fn = 'hcore_JK_iao_k_hf.h5' 221 | feri = h5py.File(fn, 'w') 222 | feri['JK'] = np.asarray(JK_lo) 223 | feri.close() 224 | 225 | # Cholesky decomposition for generating 3-index density-fitted ERI (required by gw_dc) 226 | if not os.path.isfile('cderi.h5'): 227 | try: 228 | cd = cholesky.cholesky(eri[0], tau=1e-7, dimQ=50) 229 | except: 230 | cd = cholesky.cholesky(eri[0], tau=1e-6, dimQ=50) 231 | cderi = cd.kernel() 232 | cderi = cderi.reshape(-1,nao,nao) 233 | print ('3-index ERI', cderi.shape) 234 | fn = 'cderi.h5' 235 | feri = h5py.File(fn, 'w') 236 | feri['cderi'] = np.asarray(cderi) 237 | feri.close() 238 | else: 239 | fn = 'cderi.h5' 240 | feri = h5py.File(fn, 'r') 241 | cderi = np.asarray(feri['cderi']) 242 | feri.close() 243 | 244 | # Compute GW double counting self-energy 245 | naux, nimp, nimp = cderi.shape 246 | nocca = (cell.nelectron+cell.spin)//2 247 | noccb = cell.nelectron - nocca 248 | homo = -99.; lumo = 99. 249 | for k in range(nkpts): 250 | if homo < max(mo_energy[0,k][nocca-1],mo_energy[1,k][noccb-1]): 251 | homo = max(mo_energy[0,k][nocca-1],mo_energy[1,k][noccb-1]) 252 | if lumo > min(mo_energy[0,k][nocca],mo_energy[1,k][noccb]): 253 | lumo = min(mo_energy[0,k][nocca],mo_energy[1,k][noccb]) 254 | ef = (homo+lumo) / 2. 255 | 256 | # NOTE: check analytic continuation stability (sigma) by user 257 | mol = gto_mol.M() 258 | mol.verbose = 5 259 | mol.max_memory = cell.max_memory 260 | mf = scf_mol.UHF(mol) 261 | gw = ugw_dc.UGWGF(mf) 262 | gw.nmo = (nimp, nimp) 263 | gw.nocc = (nocca, noccb) 264 | gw.eta = 0.2/27.211386 265 | gw.ac = 'pade' 266 | gw.ef = ef 267 | gw.fullsigma = True 268 | omega = np.linspace(6./27.211386,24./27.211386,91) 269 | sigma_lo = gw.kernel(Lpq=cderi, omega=omega, kmf=kmf, C_mo_lo=C_mo_lo, nw=100, nt=2000) 270 | print('### local GW self-energy (trace) on real axis ###') 271 | print('# freq imag real #') 272 | for i in range(len(omega)): 273 | print (omega[i], np.trace(sigma_lo[0,:,:,i].imag), np.trace(sigma_lo[0,:,:,i].real)) 274 | -------------------------------------------------------------------------------- /examples/NiO/run_dft: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH --partition=smallmem,parallel 3 | #SBATCH -N 1 4 | #SBATCH --ntasks-per-node=1 5 | #SBATCH -c 28 6 | #SBATCH -t 05-00:00:00 7 | #SBATCH --mem=64000 8 | #SBATCH --output=nio_dft.out 9 | 10 | export SLURM_MPI_TYPE=pmi2 11 | export OMP_NUM_THREADS=28 12 | 13 | srun python -u nio_dft.py 14 | 15 | -------------------------------------------------------------------------------- /examples/NiO/run_dmft: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #SBATCH --partition=smallmem,parallel 3 | #SBATCH -N 2 4 | #SBATCH --ntasks-per-node=4 5 | #SBATCH --cpus-per-task=7 6 | #SBATCH --mem=126000 7 | #SBATCH -t 01-00:00:00 8 | #SBATCH --output=run_dmft.out 9 | 10 | srun hostname 11 | MKL_NUM_THREADS=7 OMP_NUM_THREADS=7 mpirun -np 8 python -u run_dmft.py 12 | -------------------------------------------------------------------------------- /examples/NiO/run_gw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #SBATCH --partition=smallmem,serial,parallel 3 | #SBATCH -N 4 4 | #SBATCH --ntasks-per-node=4 5 | #SBATCH --cpus-per-task=7 6 | #SBATCH --mem=126000 7 | #SBATCH -t 02-00:00:00 8 | #SBATCH --output=nio_gw.out 9 | 10 | srun hostname 11 | MKL_NUM_THREADS=7 OMP_NUM_THREADS=7 mpirun -np 16 python -u nio_gw.py 12 | -------------------------------------------------------------------------------- /examples/NiO/run_ham: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH --partition=smallmem,parallel 3 | #SBATCH -N 1 4 | #SBATCH --ntasks-per-node=1 5 | #SBATCH -c 28 6 | #SBATCH -t 05-00:00:00 7 | #SBATCH --mem=126000 8 | #SBATCH --output=nio_set_ham.out 9 | 10 | export SLURM_MPI_TYPE=pmi2 11 | export OMP_NUM_THREADS=28 12 | 13 | srun python -u nio_set_ham.py 14 | 15 | -------------------------------------------------------------------------------- /examples/Si/run_dmft: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #SBATCH --partition=smallmem,serial,parallel 3 | #SBATCH -N 4 4 | #SBATCH --ntasks-per-node=4 5 | #SBATCH --cpus-per-task=7 6 | #SBATCH --mem=126000 7 | #SBATCH -t 05-00:00:00 8 | #SBATCH --output=run_dmft.out 9 | 10 | srun hostname 11 | MKL_NUM_THREADS=7 OMP_NUM_THREADS=7 mpirun -np 16 python -u run_dmft.py 12 | -------------------------------------------------------------------------------- /examples/Si/run_dmft.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Main routine to set up DMFT parameters and run DMFT 3 | ''' 4 | 5 | try: 6 | import block2 7 | from block2.su2 import MPICommunicator 8 | dmrg_ = True 9 | except: 10 | dmrg_ = False 11 | pass 12 | import numpy as np 13 | import scipy, os, h5py 14 | from fcdmft.utils import write 15 | from fcdmft.dmft import gwdmft 16 | from mpi4py import MPI 17 | 18 | rank = MPI.COMM_WORLD.Get_rank() 19 | size = MPI.COMM_WORLD.Get_size() 20 | comm = MPI.COMM_WORLD 21 | 22 | def dmft_abinitio(): 23 | ''' 24 | List of DMFT parameters 25 | 26 | gw_dmft : choose to run GW+DMFT (True) or HF+DMFT (False) 27 | opt_mu : whether to optimize chemical potential during DMFT cycles 28 | solver_type : choose impurity solver ('cc', 'ucc', 'dmrg', 'dmrgsz', 'fci') 29 | disc_type : choose bath discretization method ('opt', 'direct', 'linear', 'gauss', 'log') 30 | max_memory : maximum memory for DMFT calculation (per MPI process for CC, per node for DMRG) 31 | dmft_max_cycle : maximum number of DMFT iterations (set to 0 for one-shot DMFT) 32 | chkfile : chkfile for saving DMFT self-consistent quantities (hyb and self-energy) 33 | diag_only : choose to only fit diagonal hybridization (optional) 34 | orb_fit : special orbitals (e.g. 3d/4f) with x5 weight in bath optimization (optional) 35 | delta : broadening for discretizing hybridization (often 0.02-0.1 Ha) 36 | nbath : number of bath energies (can be any integer) 37 | nb_per_e : number of bath orbitals per bath energy (should be no greater than nval-ncore) 38 | total bath number = nb_per_e * nbath 39 | mu : initial chemical potential 40 | nval : number of valence (plus core) impurity orbitals (only ncore:nval orbs coupled to bath) 41 | ncore : number of core impurity orbitals 42 | nelectron : electron number per cell 43 | gmres_tol : GMRES/GCROTMK convergence criteria for solvers in production run (often 1e-3) 44 | wl0, wh0: (optional) real-axis frequency range [wl0+mu, wh0+mu] for bath discretization 45 | in DMFT self-consistent iterations (defualt: -0.4, 0.4) 46 | wl, wh : real-axis frequnecy range [wl, wh] for production run 47 | eta : spectral broadening for production run (often 0.1-0.4 eV) 48 | twist_average : whether to use twist-averaged HF/GW for lattice GF and hybridization 49 | band_interpolation : whether to use interpolated HF/GW for lattice GF and hybridization 50 | ''' 51 | # DMFT self-consistent loop parameters 52 | gw_dmft = True 53 | opt_mu = False 54 | solver_type = 'cc' 55 | disc_type = 'opt' 56 | max_memory = 32000 57 | dmft_max_cycle = 10 58 | chkfile = 'DMFT_chk.h5' 59 | diag_only = False 60 | orb_fit = None 61 | twist_average = False 62 | band_interpolate = False 63 | 64 | delta = 0.1 65 | mu = 0.267 66 | nbath = 12 67 | nb_per_e = 8 68 | wl0 = -0.4 69 | wh0 = 0.4 70 | 71 | nval = 8 72 | ncore = 0 73 | nelectron = 8 74 | 75 | # DMFT production run parameters 76 | Ha2eV = 27.211386 77 | wl = 2./Ha2eV 78 | wh = 13./Ha2eV 79 | eta = 0.1/Ha2eV 80 | gmres_tol = 1e-3 81 | # final self-energy on imag or real axis 82 | run_imagfreq = False 83 | 84 | ''' 85 | load_mf : bool 86 | Load DMFT imp+bath mean-field object from saved file. if True, dmft_max_cycle = 0. 87 | save_mf : bool 88 | Save DMFT imp+bath mean-field object to saved file at the end of DMFT cycles. 89 | load_mf and save_mf cannot be True at the same time. 90 | ''' 91 | load_mf = False 92 | save_mf = True 93 | 94 | ''' 95 | specific parameters for CAS treatment of impurity problem: 96 | cas : use CASCI or not (default: False) 97 | casno : natural orbital method for CASCI 98 | (choices: 'gw': GW@HF, 'cc': CCSD, 'ci': CISD, 'hf' : HF) 99 | composite : whether to use GW or CCSD Green's function as the low-level GF 100 | for impurity problem; if False, use HF Green's function as low-level GF 101 | thresh : float 102 | Threshold on NO occupation numbers. Default is 1e-4. 103 | nvir_act : int 104 | Number of virtual NOs to keep. Default is None. If present, overrides `thresh`. 105 | nocc_act : int 106 | Number of occupied NOs to keep. Default is None. If present, overrides `thresh` and `vno_only`. 107 | save_gf : bool 108 | Save CAS Green's function. Default is False. 109 | read_gf : bool 110 | Read saved CAS Green's function and skip solving CAS problem. Default is False. 111 | thresh2 : float 112 | Threshold on NO occupation numbers for a larger CAS problem solved 113 | by a cheaper impurity solver. Default is None. 114 | load_cas : bool 115 | Load DMFT CAS problem mean-field object from saved file (only support CCSD-NO based CAS). 116 | ''' 117 | cas = False 118 | casno = 'gw' 119 | composite = False 120 | thresh = None 121 | nvir_act = None 122 | nocc_act = None 123 | save_gf = False 124 | read_gf = False 125 | thresh2 = None 126 | 127 | # spin-unrestricted 128 | nvir_act_a = None 129 | nocc_act_a = None 130 | nvir_act_b = None 131 | nocc_act_b = None 132 | 133 | load_cas = False 134 | 135 | # specific parameters for DMRG solvers (see fcdmft/solver/gfdmrg.py for detailed comments) 136 | gs_n_steps = 20 137 | gf_n_steps = 6 138 | gs_tol = 1E-10 139 | gf_tol = 1E-3 140 | gs_bond_dims = [400] * 5 + [800] * 5 + [1500] * 5 + [2000] * 5 141 | gs_noises = [1E-3] * 7 + [1E-4] * 5 + [1e-7] * 5 + [0] 142 | gf_bond_dims = [200] * 2 + [500] * 4 143 | gf_noises = [1E-4] * 1 + [1E-5] * 1 + [1E-7] * 1 + [0] 144 | dmrg_gmres_tol = 1E-7 145 | dmrg_verbose = 2 146 | reorder_method = 'gaopt' 147 | dmrg_local = True 148 | n_off_diag_cg = -2 149 | extra_nw = 5 150 | extra_dw = 0.1/Ha2eV 151 | # if extra_delta is not None, approx. DMRG treatment of freq will be used 152 | extra_delta = None 153 | # at lease one of 'load_dir' and 'save_dir' should be None 154 | load_dir = None 155 | save_dir = './gs_mps' 156 | 157 | # DMRG-MRCI parameters 158 | dyn_corr_method = None 159 | nocc_act_low = None # number of lowest occ orbs treated by MRCI 160 | nvir_act_high = None # number of highest vir orbs treated by MRCI 161 | 162 | ### Finishing parameter settings ### 163 | 164 | # read hcore 165 | fn = 'hcore_JK_iao_k_dft.h5' 166 | feri = h5py.File(fn, 'r') 167 | hcore_k = np.asarray(feri['hcore']) 168 | feri.close() 169 | 170 | # read HF-JK matrix 171 | fn = 'hcore_JK_iao_k_hf.h5' 172 | feri = h5py.File(fn, 'r') 173 | JK_k = np.asarray(feri['JK']) 174 | feri.close() 175 | 176 | # read density matrix 177 | fn = 'DM_iao_k.h5' 178 | feri = h5py.File(fn, 'r') 179 | DM_k = np.asarray(feri['DM']) 180 | feri.close() 181 | 182 | # read 4-index ERI 183 | fn = 'eri_imp111_iao.h5' 184 | feri = h5py.File(fn, 'r') 185 | eri = np.asarray(feri['eri']) 186 | feri.close() 187 | eri_new = eri 188 | if eri_new.shape[0] == 3: 189 | eri_new = np.zeros_like(eri) 190 | eri_new[0] = eri[0] 191 | eri_new[1] = eri[2] 192 | eri_new[2] = eri[1] 193 | del eri 194 | 195 | # Read interpolated Fock for more accurate hybridization 196 | if band_interpolate: 197 | fn = 'hcore_JK_iao_k_dft_band.h5' 198 | feri = h5py.File(fn, 'r') 199 | hcore_k_band = np.asarray(feri['hcore']) 200 | JK_k_dft_band = np.asarray(feri['JK']) 201 | feri.close() 202 | if hcore_k_band.ndim == 3: 203 | hcore_k_band = hcore_k_band[np.newaxis, ...] 204 | JK_k_dft_band = JK_k_dft_band[np.newaxis, ...] 205 | 206 | # Read twist average Fock for more accurate hybridization 207 | if twist_average: 208 | from pyscf import lib 209 | mesh = [0,1] 210 | center_list = lib.cartesian_prod((mesh, mesh, mesh))[1:] 211 | spin, nkpts, nao, nao = hcore_k.shape 212 | hcore_k_TA = np.zeros((len(center_list), spin, nkpts, nao, nao), dtype=np.complex) 213 | JK_k_TA = np.zeros((len(center_list), spin, nkpts, nao, nao), dtype=np.complex) 214 | for i in range(len(center_list)): 215 | center = center_list[i] 216 | 217 | # read hcore 218 | fn = 'hcore_JK_iao_k_dft_%d_%d_%d.h5'%(center[0],center[1],center[2]) 219 | feri = h5py.File(fn, 'r') 220 | hcore_k_TA[i] = np.asarray(feri['hcore']) 221 | feri.close() 222 | 223 | # read HF-JK matrix 224 | fn = 'hcore_JK_iao_k_hf_%d_%d_%d.h5'%(center[0],center[1],center[2]) 225 | feri = h5py.File(fn, 'r') 226 | JK_k_TA[i] = np.asarray(feri['JK']) 227 | feri.close() 228 | 229 | hcore_k_TA = hcore_k_TA.transpose(1,0,2,3,4).reshape(spin, len(center_list)*nkpts, nao, nao) 230 | JK_k_TA = JK_k_TA.transpose(1,0,2,3,4).reshape(spin, len(center_list)*nkpts, nao, nao) 231 | 232 | assert (not (band_interpolate and twist_average)) 233 | 234 | # run self-consistent DMFT 235 | mydmft = gwdmft.DMFT(hcore_k, JK_k, DM_k, eri_new, nval, ncore, nbath, 236 | nb_per_e, disc_type=disc_type, solver_type=solver_type) 237 | mydmft.gw_dmft = gw_dmft 238 | mydmft.verbose = 5 239 | mydmft.diis = True 240 | mydmft.gmres_tol = gmres_tol 241 | mydmft.max_memory = max_memory 242 | mydmft.chkfile = chkfile 243 | mydmft.diag_only = diag_only 244 | mydmft.orb_fit = orb_fit 245 | mydmft.twist_average = twist_average 246 | mydmft.band_interpolate = band_interpolate 247 | if twist_average: 248 | mydmft.center_list = center_list 249 | mydmft.hcore_k_band = hcore_k_TA 250 | mydmft.JK_k_band = JK_k_TA 251 | if band_interpolate: 252 | mydmft.hcore_k_band = hcore_k_band 253 | mydmft.JK_k_dft_band = JK_k_dft_band 254 | 255 | assert (not (load_mf and save_mf)) 256 | if load_mf: 257 | dmft_max_cycle = 0 258 | mydmft.max_cycle = dmft_max_cycle 259 | mydmft.run_imagfreq = run_imagfreq 260 | if solver_type == 'dmrg' or solver_type == 'dmrgsz': 261 | if not dmrg_: 262 | raise ImportError 263 | mydmft.load_mf = load_mf 264 | mydmft.save_mf = save_mf 265 | 266 | if cas: 267 | mydmft.cas = cas 268 | mydmft.casno = casno 269 | mydmft.composite = composite 270 | mydmft.thresh = thresh 271 | mydmft.thresh2 = thresh2 272 | mydmft.nvir_act = nvir_act 273 | mydmft.nocc_act = nocc_act 274 | mydmft.save_gf = save_gf 275 | mydmft.read_gf = read_gf 276 | if casno == 'gw': 277 | assert(gw_dmft) 278 | if eri_new.shape[0] == 3: 279 | mydmft.nvir_act_a = nvir_act_a 280 | mydmft.nocc_act_a = nocc_act_a 281 | mydmft.nvir_act_b = nvir_act_b 282 | mydmft.nocc_act_b = nocc_act_b 283 | mydmft.load_cas = load_cas 284 | 285 | if solver_type == 'dmrg' or solver_type == 'dmrgsz': 286 | mydmft.gs_n_steps = gs_n_steps 287 | mydmft.gf_n_steps = gf_n_steps 288 | mydmft.gs_tol = gs_tol 289 | mydmft.gf_tol = gf_tol 290 | mydmft.gs_bond_dims = gs_bond_dims 291 | mydmft.gs_noises = gs_noises 292 | mydmft.gf_bond_dims = gf_bond_dims 293 | mydmft.gf_noises = gf_noises 294 | mydmft.dmrg_gmres_tol = dmrg_gmres_tol 295 | mydmft.dmrg_verbose = dmrg_verbose 296 | mydmft.reorder_method = reorder_method 297 | mydmft.n_off_diag_cg = n_off_diag_cg 298 | mydmft.load_dir = load_dir 299 | mydmft.save_dir = save_dir 300 | mydmft.dmrg_local = dmrg_local 301 | mydmft.dyn_corr_method = dyn_corr_method 302 | mydmft.nvir_act_high = nvir_act_high 303 | mydmft.nocc_act_low = nocc_act_low 304 | if nocc_act_low is not None: 305 | assert (nocc_act_low <= nocc_act) 306 | if nvir_act_high is not None: 307 | assert (nvir_act_high <= nvir_act) 308 | 309 | mydmft.kernel(mu0=mu, wl=wl0, wh=wh0, delta=delta, occupancy=nelectron, opt_mu=opt_mu) 310 | occupancy = np.trace(mydmft.get_rdm_imp()) 311 | if rank == 0: 312 | print ('At mu =', mydmft.mu, ', occupancy =', occupancy) 313 | 314 | mydmft.verbose = 5 315 | mydmft._scf.mol.verbose = 5 316 | spin = mydmft.spin 317 | 318 | if not mydmft.run_imagfreq: 319 | # extra_freqs and extra_delta only for DMRG solver 320 | if extra_delta is not None and (mydmft.solver_type=='dmrg' or mydmft.solver_type=='dmrgsz'): 321 | nw = int(round((wh-wl)/(extra_dw * extra_nw)))+1 322 | freqs = np.linspace(wl, wh, nw) 323 | extra_freqs = [] 324 | for i in range(len(freqs)): 325 | freqs_tmp = [] 326 | if extra_nw % 2 == 0: 327 | for w in range(-extra_nw // 2, extra_nw // 2): 328 | freqs_tmp.append(freqs[i] + extra_dw * w) 329 | else: 330 | for w in range(-(extra_nw-1) // 2, (extra_nw+1) // 2): 331 | freqs_tmp.append(freqs[i] + extra_dw * w) 332 | extra_freqs.append(np.array(freqs_tmp)) 333 | mydmft.extra_freqs = extra_freqs 334 | mydmft.extra_delta = extra_delta 335 | freqs_comp = np.array(extra_freqs).reshape(-1) 336 | else: 337 | nw = int(round((wh-wl)/eta))+1 338 | freqs = np.linspace(wl, wh, nw) 339 | freqs_comp = freqs 340 | 341 | # Get impurity DOS (production run) 342 | #ldos = mydmft.get_ldos_imp(freqs, eta) 343 | 344 | # Get lattice DOS (production run) 345 | ldos, ldos_gw = mydmft.get_ldos_latt(freqs, eta) 346 | spin = mydmft.spin 347 | 348 | filename = 'mu-%0.3f_n-%0.2f_%d-%d_eta-%.2f_d-%.2f_%s'%( 349 | mu,occupancy,nval,nbath,eta*Ha2eV,delta,solver_type) 350 | if rank == 0: 351 | write.write_dos(filename, freqs_comp, ldos, occupancy=occupancy) 352 | 353 | if mydmft.gw_dmft: 354 | filename = 'mu-%0.3f_n-%0.2f_%d-%d_eta-%.2f_d-%.2f_gw'%( 355 | mu,occupancy,nval,nbath,eta*Ha2eV,delta) 356 | else: 357 | filename = 'mu-%0.3f_n-%0.2f_%d-%d_eta-%.2f_d-%.2f_hf'%( 358 | mu,occupancy,nval,nbath,eta*Ha2eV,delta) 359 | if rank == 0: 360 | write.write_dos(filename, freqs_comp, ldos_gw, occupancy=occupancy) 361 | 362 | else: 363 | nimp = eri_new.shape[-1] 364 | omega_ns = np.linspace(0./27.211386, 10.0/27.211386, 21)[1:] 365 | if solver_type == 'dmrg' or solver_type == 'dmrgsz': 366 | # TODO: check DMRG load_dir is correct 367 | if mydmft.load_dir is None: 368 | mydmft.load_dir = mydmft.save_dir 369 | mydmft.save_dir = None 370 | assert(mydmft.load_dir is not None) 371 | sigma = np.zeros((spin,nimp,nimp,len(omega_ns)),dtype=complex) 372 | for iw in range(len(omega_ns)): 373 | sigma[:,:,:,iw] = mydmft.get_sigma_imp(np.array([mu]), omega_ns[iw], 374 | save_gf=save_gf, read_gf=read_gf)[:,:nimp,:nimp,0] 375 | else: 376 | sigma = mydmft.get_sigma_imp(mu+1j*omega_ns, 0.0, 377 | save_gf=save_gf, read_gf=read_gf)[:,:nimp,:nimp] 378 | 379 | tmpdir = 'dmft_dos' 380 | if rank == 0: 381 | if not os.path.isdir(tmpdir): 382 | os.mkdir(tmpdir) 383 | for i in range(nimp): 384 | write.write_sigma_elem(tmpdir+'/dmft_sigma_imag_orb-%d'%(i), omega_ns, sigma[:,i,i,:]) 385 | 386 | if __name__ == '__main__': 387 | dmft_abinitio() 388 | -------------------------------------------------------------------------------- /examples/Si/run_gw: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH --partition=smallmem,parallel 3 | #SBATCH -N 1 4 | #SBATCH --ntasks-per-node=1 5 | #SBATCH -c 28 6 | #SBATCH -t 05-00:00:00 7 | #SBATCH --mem=32000 8 | #SBATCH --output=si_gw.out 9 | 10 | export SLURM_MPI_TYPE=pmi2 11 | export OMP_NUM_THREADS=28 12 | 13 | srun python -u si_gw.py 14 | 15 | -------------------------------------------------------------------------------- /examples/Si/run_ham: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH --partition=smallmem,parallel 3 | #SBATCH -N 1 4 | #SBATCH --ntasks-per-node=1 5 | #SBATCH -c 28 6 | #SBATCH -t 05-00:00:00 7 | #SBATCH --mem=32000 8 | #SBATCH --output=si_set_ham.out 9 | 10 | export SLURM_MPI_TYPE=pmi2 11 | export OMP_NUM_THREADS=28 12 | 13 | srun python -u si_set_ham.py 14 | 15 | -------------------------------------------------------------------------------- /examples/Si/si_gw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pyscf.pbc import df, dft, gto 3 | import numpy as np 4 | import os, h5py 5 | from pyscf.pbc.lib import chkfile 6 | from pyscf import lib 7 | from fcdmft.gw.pbc import krgw_gf 8 | from fcdmft.utils import write 9 | 10 | einsum = lib.einsum 11 | 12 | cell = gto.Cell() 13 | cell.build(unit = 'angstrom', 14 | a = ''' 15 | 0.000000 2.715000 2.715000 16 | 2.715000 0.000000 2.715000 17 | 2.715000 2.715000 0.000000 18 | ''', 19 | atom = 'Si 2.03625 2.03625 2.03625; Si 3.39375 3.39375 3.39375', 20 | dimension = 3, 21 | max_memory = 32000, 22 | verbose = 5, 23 | basis='gth-dzvp', 24 | pseudo='gth-pbe', 25 | precision=1e-12) 26 | 27 | kmesh = [4,4,4] 28 | kpts = cell.make_kpts(kmesh,scaled_center=[0,0,0]) 29 | gdf = df.GDF(cell, kpts) 30 | gdf.auxbasis = df.aug_etb(cell, beta=2.0) 31 | gdf_fname = 'gdf_ints_444.h5' 32 | gdf._cderi_to_save = gdf_fname 33 | if not os.path.isfile(gdf_fname): 34 | gdf.build() 35 | 36 | chkfname = 'si_444.chk' 37 | if os.path.isfile(chkfname): 38 | kmf = dft.KRKS(cell, kpts).density_fit() 39 | kmf.xc = 'pbe' 40 | kmf.with_df = gdf 41 | kmf.with_df._cderi = gdf_fname 42 | kmf.conv_tol = 1e-12 43 | data = chkfile.load(chkfname, 'scf') 44 | kmf.__dict__.update(data) 45 | else: 46 | kmf = dft.KRKS(cell, kpts).density_fit() 47 | kmf.xc = 'pbe' 48 | kmf.with_df = gdf 49 | kmf.with_df._cderi = gdf_fname 50 | kmf.conv_tol = 1e-12 51 | kmf.chkfile = chkfname 52 | kmf.kernel() 53 | 54 | gw = krgw_gf.KRGWGF(kmf) 55 | gw.ac = 'pade' 56 | Ha2ev = 27.211386 57 | gw.eta = 0.1/Ha2ev 58 | gw.fullsigma = True 59 | gw.fc = True 60 | omega = np.linspace(0.,18./Ha2ev,181) 61 | # writefile must >= 1 for GW+DMFT calc 62 | gf, gf0, sigma = gw.kernel(omega=omega, writefile=1) 63 | 64 | gf = gf[np.newaxis, ...] 65 | gf0 = gf0[np.newaxis, ...] 66 | nkpts = gw.nkpts 67 | gf = 1./nkpts * np.sum(gf, axis=1) 68 | gf0 = 1./nkpts * np.sum(gf0, axis=1) 69 | 70 | outdir = 'GW_DOS' 71 | if not os.path.isdir(outdir): 72 | os.mkdir(outdir) 73 | write.write_gf_to_dos(outdir+'/si_gw_dos', omega, gf) 74 | write.write_gf_to_dos(outdir+'/si_pbe_dos', omega, gf0) 75 | 76 | mo_energy = gw.mo_energy 77 | nocc = gw.nocc 78 | homo = -99.; lumo = 99. 79 | for k in range(nkpts): 80 | if homo < mo_energy[k][nocc-1]: 81 | homo = mo_energy[k][nocc-1] 82 | if lumo > mo_energy[k][nocc]: 83 | lumo = mo_energy[k][nocc] 84 | print ('VBM, CBM, Gap', homo*Ha2ev, lumo*Ha2ev, (lumo-homo)*Ha2ev) 85 | -------------------------------------------------------------------------------- /examples/Si/si_set_ham.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | import h5py, os 4 | from libdmet_solid.system import lattice 5 | from libdmet_solid.basis_transform import make_basis 6 | from libdmet_solid.basis_transform import eri_transform 7 | import libdmet_solid.utils.logger as log 8 | 9 | from pyscf.pbc import df, dft, scf, gto 10 | from pyscf.pbc.lib import chkfile 11 | from pyscf import lib 12 | from pyscf import gto as gto_mol 13 | from pyscf import scf as scf_mol 14 | 15 | from fcdmft.gw.mol import gw_dc 16 | from fcdmft.utils import cholesky 17 | 18 | log.verbose = 'DEBUG1' 19 | 20 | einsum = lib.einsum 21 | 22 | # NOTE: lattice system setup by user 23 | cell = gto.Cell() 24 | cell.build(unit = 'angstrom', 25 | a = ''' 26 | 0.000000 2.715000 2.715000 27 | 2.715000 0.000000 2.715000 28 | 2.715000 2.715000 0.000000 29 | ''', 30 | atom = 'Si 2.03625 2.03625 2.03625; Si 3.39375 3.39375 3.39375', 31 | dimension = 3, 32 | max_memory = 32000, 33 | verbose = 5, 34 | basis='gth-dzvp', 35 | pseudo='gth-pbe', 36 | precision=1e-12) 37 | 38 | kmesh = [4,4,4] 39 | Lat = lattice.Lattice(cell, kmesh) 40 | kpts = Lat.kpts 41 | nao = Lat.nao 42 | nkpts = Lat.nkpts 43 | 44 | gdf = df.GDF(cell, kpts) 45 | gdf.auxbasis = df.aug_etb(cell, beta=2.0) 46 | gdf_fname = 'gdf_ints_444.h5' 47 | gdf._cderi_to_save = gdf_fname 48 | if not os.path.isfile(gdf_fname): 49 | gdf.build() 50 | 51 | chkfname = 'si_444.chk' 52 | if os.path.isfile(chkfname): 53 | kmf = dft.KRKS(cell, kpts).density_fit() 54 | kmf.xc = 'pbe' 55 | kmf.with_df = gdf 56 | kmf.with_df._cderi = gdf_fname 57 | kmf.conv_tol = 1e-12 58 | data = chkfile.load(chkfname, 'scf') 59 | kmf.__dict__.update(data) 60 | else: 61 | kmf = dft.KRKS(cell, kpts).density_fit() 62 | kmf.xc = 'pbe' 63 | kmf.with_df = gdf 64 | kmf.with_df._cderi = gdf_fname 65 | kmf.conv_tol = 1e-12 66 | kmf.chkfile = chkfname 67 | kmf.kernel() 68 | 69 | # set spin 70 | mo_energy = np.asarray(kmf.mo_energy) 71 | mo_coeff = np.asarray(kmf.mo_coeff) 72 | if len(mo_energy.shape) == 2: 73 | spin = 1 74 | mo_energy = mo_energy[np.newaxis, ...] 75 | mo_coeff = mo_coeff[np.newaxis, ...] 76 | else: 77 | spin = 2 78 | 79 | # NOTE: choose IAO basis by user 80 | # C_ao_lo: transformation matrix from AO to LO (IAO) basis 81 | MINAO = {'Si':'gth-szv'} 82 | C_ao_iao, C_ao_iao_val, C_ao_iao_virt = make_basis.get_C_ao_lo_iao(Lat, kmf, minao=MINAO, full_return=True) 83 | C_ao_lo = np.zeros((spin,nkpts,nao,nao),dtype=np.complex128) 84 | for s in range(spin): 85 | C_ao_lo[s] = C_ao_iao 86 | 87 | # C_mo_lo: transformation matrix from MO to LO (IAO) basis 88 | S_ao_ao = kmf.get_ovlp() 89 | C_mo_lo = np.zeros((spin,nkpts,nao,nao),dtype=np.complex128) 90 | for s in range(spin): 91 | for ki in range(nkpts): 92 | C_mo_lo[s][ki] = np.dot(np.dot(mo_coeff[s][ki].T.conj(), S_ao_ao[ki]), C_ao_lo[s][ki]) 93 | fn = 'C_mo_lo.h5' 94 | feri = h5py.File(fn, 'w') 95 | feri['C_ao_lo'] = np.asarray(C_ao_lo) 96 | feri['C_mo_lo'] = np.asarray(C_mo_lo) 97 | feri.close() 98 | 99 | # get DFT density matrix in IAO basis 100 | DM_ao = np.asarray(kmf.make_rdm1()) 101 | if len(DM_ao.shape) == 3: 102 | DM_ao = DM_ao[np.newaxis, ...] 103 | DM_lo = np.zeros((spin,nkpts,nao,nao),dtype=DM_ao.dtype) 104 | for s in range(spin): 105 | for ki in range(nkpts): 106 | Cinv = np.dot(C_ao_lo[s][ki].T.conj(),S_ao_ao[ki]) 107 | DM_lo[s][ki] = np.dot(np.dot(Cinv, DM_ao[s][ki]), Cinv.T.conj()) 108 | 109 | for s in range(spin): 110 | nelec_lo = np.trace(DM_lo[s].sum(axis=0)/nkpts) 111 | print ('Nelec imp', nelec_lo.real) 112 | fn = 'DM_iao_k.h5' 113 | feri = h5py.File(fn, 'w') 114 | feri['DM'] = np.asarray(DM_lo) 115 | feri.close() 116 | 117 | # get 4-index ERI 118 | eri = eri_transform.get_unit_eri_fast(cell, gdf, C_ao_lo=C_ao_lo, feri=gdf_fname) 119 | fn = 'eri_imp111_iao.h5' 120 | feri = h5py.File(fn, 'w') 121 | feri['eri'] = np.asarray(eri.real) 122 | feri.close() 123 | 124 | # get one-electron integrals 125 | hcore_ao = np.asarray(kmf.get_hcore()) 126 | JK_ao = np.asarray(kmf.get_veff()) 127 | if len(JK_ao.shape) == 3: 128 | JK_ao = JK_ao[np.newaxis, ...] 129 | hcore_lo = np.zeros((spin,nkpts,nao,nao),dtype=hcore_ao.dtype) 130 | JK_lo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 131 | for s in range(spin): 132 | for ki in range(nkpts): 133 | hcore_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), hcore_ao[ki]), C_ao_lo[s,ki]) 134 | JK_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), JK_ao[s,ki]), C_ao_lo[s,ki]) 135 | 136 | fn = 'hcore_JK_iao_k_dft.h5' 137 | feri = h5py.File(fn, 'w') 138 | feri['hcore'] = np.asarray(hcore_lo) 139 | feri['JK'] = np.asarray(JK_lo) 140 | feri.close() 141 | assert(np.max(np.abs(hcore_lo.sum(axis=1).imag/nkpts))<1e-6) 142 | assert(np.max(np.abs(JK_lo.sum(axis=1).imag/nkpts))<1e-6) 143 | 144 | # get HF JK term using DFT density 145 | kmf_hf = scf.KRHF(cell, kpts, exxdiv=None) 146 | kmf_hf.with_df = gdf 147 | kmf_hf.with_df._cderi = gdf_fname 148 | kmf_hf.max_cycle = 0 149 | JK_ao = np.asarray(kmf_hf.get_veff(dm_kpts=DM_ao[0])) 150 | if len(JK_ao.shape) == 3: 151 | JK_ao = JK_ao[np.newaxis, ...] 152 | 153 | # NOTE: choose finite size correction by user 154 | # set gw_fc to True if finite size correction is used in kgw 155 | gw_fc = True 156 | if gw_fc: 157 | # finite size correction to exchange 158 | vk_corr = -2./np.pi * (6.*np.pi**2/cell.vol/nkpts)**(1./3.) 159 | nocc = cell.nelectron // 2 160 | JK_mo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 161 | for s in range(spin): 162 | for ki in range(nkpts): 163 | JK_mo[s,ki] = np.dot(np.dot(mo_coeff[s,ki].T.conj(), JK_ao[s,ki]), mo_coeff[s,ki]) 164 | for i in range(nocc): 165 | JK_mo[s,ki][i,i] = JK_mo[s,ki][i,i] + vk_corr 166 | 167 | JK_lo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 168 | for s in range(spin): 169 | for ki in range(nkpts): 170 | JK_lo[s,ki] = np.dot(np.dot(C_mo_lo[s,ki].T.conj(), JK_mo[s,ki]), C_mo_lo[s,ki]) 171 | else: 172 | JK_lo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 173 | for s in range(spin): 174 | for ki in range(nkpts): 175 | JK_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), JK_ao[s,ki]), C_ao_lo[s,ki]) 176 | 177 | fn = 'hcore_JK_iao_k_hf.h5' 178 | feri = h5py.File(fn, 'w') 179 | feri['JK'] = np.asarray(JK_lo) 180 | feri.close() 181 | 182 | # Cholesky decomposition for generating 3-index density-fitted ERI (required by gw_dc) 183 | if not os.path.isfile('cderi.h5'): 184 | try: 185 | cd = cholesky.cholesky(eri[0], tau=1e-8, dimQ=50) 186 | except: 187 | cd = cholesky.cholesky(eri[0], tau=1e-6, dimQ=50) 188 | cderi = cd.kernel() 189 | cderi = cderi.reshape(-1,nao,nao) 190 | print ('3-index ERI', cderi.shape) 191 | fn = 'cderi.h5' 192 | feri = h5py.File(fn, 'w') 193 | feri['cderi'] = np.asarray(cderi) 194 | feri.close() 195 | else: 196 | fn = 'cderi.h5' 197 | feri = h5py.File(fn, 'r') 198 | cderi = np.asarray(feri['cderi']) 199 | feri.close() 200 | 201 | # Compute GW double counting self-energy 202 | naux, nimp, nimp = cderi.shape 203 | nocc = cell.nelectron // 2 204 | homo = -99.; lumo = 99. 205 | for k in range(nkpts): 206 | if homo < mo_energy[0,k][nocc-1]: 207 | homo = mo_energy[0,k][nocc-1] 208 | if lumo > mo_energy[0,k][nocc]: 209 | lumo = mo_energy[0,k][nocc] 210 | ef = 0.5 * (homo + lumo) 211 | 212 | # NOTE: check analytic continuation stability (sigma) by user 213 | mol = gto_mol.M() 214 | mol.verbose = 5 215 | mol.max_memory = cell.max_memory 216 | mf = scf_mol.RHF(mol) 217 | gw = gw_dc.GWGF(mf) 218 | gw.nmo = nimp 219 | gw.nocc = nocc 220 | gw.eta = 0.1/27.211386 221 | gw.ac = 'pade' 222 | gw.ef = ef 223 | gw.fullsigma = True 224 | omega = np.linspace(0,18./27.211386,181) 225 | sigma_lo = gw.kernel(Lpq=cderi, omega=omega, kmf=kmf, C_mo_lo=C_mo_lo, nw=100, nt=2000) 226 | print('### local GW self-energy (trace) on real axis ###') 227 | print('# freq imag real #') 228 | for i in range(len(omega)): 229 | print (omega[i], np.trace(sigma_lo[:,:,i].imag), np.trace(sigma_lo[:,:,i].real)) 230 | -------------------------------------------------------------------------------- /examples/SrMoO3/README.md: -------------------------------------------------------------------------------- 1 | This folder contains scripts for running GW+DMFT for SrMoO3, a moderately correlated metal. 2 | Band interpolation is used for getting large k-mesh GW bands, which is used for computing 3 | DMFT lattie GF and hybridization function. 4 | 5 | Steps 6 | ----- 7 | 8 | 1. Run `srmoo3_lda.py` (serial run) at 5x5x5 k-mesh to get LDA results and GDF integrals. 9 | 2. Run `srmoo3_gw.py` (parallel run, normally 2-3 nodes, each node 4 MPI processes), to get GW results at 5x5x5 k-mesh. 10 | 3. Run `srmoo3_set_ham.py` (serial run) to get local basis and impurity Hamiltonian for DMFT. 11 | 4. Run `srmoo3_interpolate.py` (serial run) to get LDA bands at 12x12x12 k-mesh and interpolate GW results to 12x12x12 k-mesh. Steps 3 and 4 can be run simultaneously. 12 | 5. Run `run_dmft.py` to perform GW+DMFT calculation using local basis, impurity Hamiltonian, and interpolated GW bands from Steps 3 and 4. 13 | -------------------------------------------------------------------------------- /examples/SrMoO3/srmoo3_gw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pyscf.pbc import df, dft, scf, cc, gto 3 | import numpy as np 4 | import h5py 5 | from libdmet_solid.system import lattice 6 | from pyscf.pbc.lib import chkfile 7 | import os 8 | from pyscf import lib 9 | import libdmet_solid.utils.logger as log 10 | from fcdmft.gw.pbc import krgw_gf 11 | from mpi4py import MPI 12 | 13 | rank = MPI.COMM_WORLD.Get_rank() 14 | size = MPI.COMM_WORLD.Get_size() 15 | comm = MPI.COMM_WORLD 16 | 17 | log.verbose = 'DEBUG1' 18 | 19 | einsum = lib.einsum 20 | 21 | cell = gto.Cell() 22 | cell.build(unit = 'angstrom', 23 | a = ''' 24 | 3.97600 0.00000 0.00000 25 | 0.00000 3.97600 0.00000 26 | 0.00000 0.00000 3.97600 27 | ''', 28 | atom = ''' 29 | Mo 1.98800 1.98800 1.98800 30 | Sr 0.00000 0.00000 0.00000 31 | O 1.98800 1.98800 0.00000 32 | O 1.98800 0.00000 1.98800 33 | O 0.00000 1.98800 1.98800 34 | ''', 35 | dimension = 3, 36 | max_memory = 40000, 37 | verbose = 5, 38 | pseudo = 'gth-pade', 39 | basis='gth-dzvp-molopt-sr', 40 | precision=1e-12) 41 | 42 | kmesh = [5,5,5] 43 | kpts = cell.make_kpts(kmesh,scaled_center=[0,0,0]) 44 | Lat = lattice.Lattice(cell, kmesh) 45 | nao = Lat.nao 46 | Lat.kpts = kpts 47 | nkpts = Lat.nkpts 48 | 49 | gdf_fname = 'gdf_ints_555.h5' 50 | gdf = df.GDF(cell, kpts) 51 | gdf._cderi_to_save = gdf_fname 52 | gdf.auxbasis = df.aug_etb(cell, beta=2.3, use_lval=True, l_val_set={'Mo':2,'O':1,'Sr':1}) 53 | if not os.path.isfile(gdf_fname): 54 | gdf.build() 55 | 56 | chkfname = 'srmoo3_555.chk' 57 | if os.path.isfile(chkfname): 58 | kmf = dft.KRKS(cell, kpts) 59 | kmf.xc = 'lda' 60 | kmf.exxdiv = None 61 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 62 | kmf.with_df = gdf 63 | kmf.with_df._cderi = gdf_fname 64 | data = chkfile.load(chkfname, 'scf') 65 | kmf.__dict__.update(data) 66 | else: 67 | kmf = dft.KRKS(cell, kpts) 68 | kmf.xc = 'lda' 69 | kmf.exxdiv = None 70 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 71 | kmf.with_df = gdf 72 | kmf.with_df._cderi = gdf_fname 73 | kmf.conv_tol = 1e-12 74 | kmf.chkfile = chkfname 75 | kmf.diis_space = 15 76 | kmf.max_cycle = 50 77 | kmf.kernel() 78 | 79 | gw = krgw_gf.KRGWGF(kmf) 80 | nmo = len(kmf.mo_energy[0]) 81 | gw.ac = 'pade' 82 | gw.eta = 0.1/27.211386 83 | gw.fullsigma = True 84 | gw.ef = 0.560479331479 85 | omega = np.linspace(6./27.211386,24./27.211386,181) 86 | gf, gf0, sigma = gw.kernel(omega=omega, orbs=range(0,nmo), writefile=1) 87 | 88 | if rank == 0: 89 | for i in range(len(omega)): 90 | print (omega[i],(-np.trace(gf0[:,:,:,i].imag.sum(axis=0)/nkpts))/np.pi, \ 91 | (-np.trace(gf[:,:,:,i].imag.sum(axis=0)/nkpts))/np.pi) 92 | print ('------------------') 93 | for i in range(len(omega)): 94 | print (omega[i],(-np.trace(gf0[1,:,:,i].imag))/np.pi, \ 95 | (-np.trace(gf[1,:,:,i].imag))/np.pi) 96 | 97 | -------------------------------------------------------------------------------- /examples/SrMoO3/srmoo3_interpolate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pyscf.pbc import df, dft, scf, cc, gto 3 | import numpy as np 4 | import h5py 5 | from libdmet_solid.system import lattice 6 | from libdmet_solid.basis_transform import make_basis 7 | from libdmet_solid.basis_transform import eri_transform 8 | from pyscf.pbc.lib import chkfile 9 | import os 10 | from pyscf import lib 11 | import libdmet_solid.utils.logger as log 12 | from fcdmft.utils import interpolate 13 | 14 | log.verbose = 'DEBUG1' 15 | 16 | einsum = lib.einsum 17 | 18 | def get_kgw_sigma_diff(freqs, eta): 19 | from fcdmft.gw.pbc import krgw_gf 20 | ''' 21 | Get k-point GW-AC self-energy in MO basis 22 | sigma = v_hf + sigma_c - v_xc 23 | ''' 24 | fn = 'ac_coeff.h5' 25 | feri = h5py.File(fn, 'r') 26 | coeff = np.asarray(feri['coeff']) 27 | ef = np.asarray(feri['fermi']) 28 | omega_fit = np.asarray(feri['omega_fit']) 29 | feri.close() 30 | 31 | fn = 'vxc.h5' 32 | feri = h5py.File(fn, 'r') 33 | vk = np.array(feri['vk']) 34 | v_mf = np.array(feri['v_mf']) 35 | feri.close() 36 | 37 | nkpts, nao, nao = vk.shape 38 | nw = len(freqs) 39 | sigma = np.zeros([nkpts,nao,nao,nw], dtype=np.complex) 40 | for k in range(nkpts): 41 | for p in range(nao): 42 | for q in range(nao): 43 | sigma[k,p,q] = krgw_gf.pade_thiele(freqs-ef+1j*eta, omega_fit, coeff[k,:,p,q]) 44 | sigma[k,p,q] += vk[k,p,q] - v_mf[k,p,q] 45 | 46 | return sigma 47 | 48 | def get_gf(hcore, sigma, freqs, delta): 49 | nw = len(freqs) 50 | nao = hcore.shape[0] 51 | gf = np.zeros([nao, nao, nw], np.complex128) 52 | for iw, w in enumerate(freqs): 53 | gf[:,:,iw] = np.linalg.inv((w+1j*delta)*np.eye(nao)-hcore-sigma[:,:,iw]) 54 | return gf 55 | 56 | def get_gf0(hcore, freqs, delta): 57 | nw = len(freqs) 58 | nao = hcore.shape[0] 59 | gf = np.zeros([nao, nao, nw], np.complex128) 60 | for iw, w in enumerate(freqs): 61 | gf[:,:,iw] = np.linalg.inv((w+1j*delta)*np.eye(nao)-hcore) 62 | return gf 63 | 64 | cell = gto.Cell() 65 | cell.build(unit = 'angstrom', 66 | a = ''' 67 | 3.97600 0.00000 0.00000 68 | 0.00000 3.97600 0.00000 69 | 0.00000 0.00000 3.97600 70 | ''', 71 | atom = ''' 72 | Mo 1.98800 1.98800 1.98800 73 | Sr 0.00000 0.00000 0.00000 74 | O 1.98800 1.98800 0.00000 75 | O 1.98800 0.00000 1.98800 76 | O 0.00000 1.98800 1.98800 77 | ''', 78 | dimension = 3, 79 | max_memory = 140000, 80 | verbose = 5, 81 | pseudo = 'gth-pade', 82 | basis='gth-dzvp-molopt-sr', 83 | precision=1e-12) 84 | 85 | # save cell object (required for using band_interpolate in DMFT) 86 | scf.chkfile.save_cell(cell, 'cell.chk') 87 | 88 | kmesh = [5,5,5] 89 | Lat = lattice.Lattice(cell, kmesh) 90 | kpts = Lat.kpts 91 | nao = Lat.nao 92 | nkpts = Lat.nkpts 93 | 94 | gdf = df.GDF(cell, kpts) 95 | gdf_fname = 'gdf_ints_555.h5' 96 | gdf._cderi_to_save = gdf_fname 97 | gdf.auxbasis = df.aug_etb(cell, beta=2.3, use_lval=True, l_val_set={'Mo':2,'O':1,'Sr':1}) 98 | if not os.path.isfile(gdf_fname): 99 | gdf.build() 100 | 101 | chkfname = 'srmoo3_555.chk' 102 | # save kmf object (required for using band_interpolate in DMFT) 103 | os.system('cp %s kmf.chk'%(chkfname)) 104 | if os.path.isfile(chkfname): 105 | kmf = dft.KRKS(cell, kpts) 106 | kmf.xc = 'lda' 107 | kmf.exxdiv = None 108 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 109 | kmf.with_df = gdf 110 | kmf.with_df._cderi = gdf_fname 111 | data = chkfile.load(chkfname, 'scf') 112 | kmf.__dict__.update(data) 113 | else: 114 | kmf = dft.KRKS(cell, kpts) 115 | kmf.xc = 'lda' 116 | kmf.exxdiv = None 117 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 118 | kmf.with_df = gdf 119 | kmf.with_df._cderi = gdf_fname 120 | kmf.conv_tol = 1e-12 121 | kmf.chkfile = chkfname 122 | kmf.diis_space = 15 123 | kmf.max_cycle = 50 124 | kmf.kernel() 125 | 126 | from libdmet_solid.lo.iao import reference_mol, get_labels, get_idx_each 127 | import copy 128 | ### Set up IAO Basis ### 129 | # set IAO (core+val) 130 | minao = 'gth-szv-molopt-sr' 131 | pmol = reference_mol(cell, minao=minao) 132 | basis = pmol._basis 133 | 134 | # set valence IAO 135 | basis_val = {} 136 | minao_val = 'gth-szv-molopt-sr-val' 137 | pmol_val = pmol.copy() 138 | pmol_val.basis = minao_val 139 | pmol_val.build() 140 | basis_val["Sr"] = copy.deepcopy(pmol_val._basis["Sr"]) 141 | basis_val["Mo"] = copy.deepcopy(pmol_val._basis["Mo"]) 142 | basis_val["O"] = copy.deepcopy(pmol_val._basis["O"]) 143 | 144 | pmol_val = pmol.copy() 145 | pmol_val.basis = basis_val 146 | pmol_val.build() 147 | 148 | val_labels = pmol_val.ao_labels() 149 | for i in range(len(val_labels)): 150 | val_labels[i] = val_labels[i].replace("Mo 4s", "Mo 5s") 151 | val_labels[i] = val_labels[i].replace("Sr 4s", "Sr 5s") 152 | pmol_val.ao_labels = lambda *args: val_labels 153 | 154 | # set core IAO 155 | basis_core = {} 156 | minao_core = 'gth-szv-molopt-sr-core' 157 | pmol_core = pmol.copy() 158 | pmol_core.basis = minao_core 159 | pmol_core.build() 160 | basis_core["Sr"] = copy.deepcopy(pmol_core._basis["Sr"]) 161 | basis_core["Mo"] = copy.deepcopy(pmol_core._basis["Mo"]) 162 | basis_core["O"] = copy.deepcopy(pmol_core._basis["O"]) 163 | 164 | pmol_core = pmol.copy() 165 | pmol_core.basis = basis_core 166 | pmol_core.build() 167 | core_labels = pmol_core.ao_labels() 168 | 169 | ncore = len(pmol_core.ao_labels()) 170 | nval = pmol_val.nao_nr() 171 | nvirt = cell.nao_nr() - ncore - nval 172 | Lat.set_val_virt_core(nval, nvirt, ncore) 173 | 174 | # construct IAO and PAO. 175 | C_ao_iao, C_ao_iao_val, C_ao_iao_virt, C_ao_iao_core \ 176 | = make_basis.get_C_ao_lo_iao(Lat, kmf, minao=minao, full_return=True, \ 177 | pmol_val=pmol_val, pmol_core=pmol_core, tol=1e-9) 178 | C_ao_lo = C_ao_iao[:,:,ncore:] 179 | nlo = C_ao_lo.shape[-1] 180 | 181 | ### Band Interpolation ### 182 | # get DFT bands at kpts_L, this kpts_L will be used for computing lattice GF/hyb in DMFT 183 | # Note: Running DFT (LDA/GGA) only needs diagonal k-block of GDF (j_only=True) 184 | kmesh_L = [12,12,12] 185 | kpts_L = cell.make_kpts(kmesh_L,scaled_center=[0,0,0]) 186 | gdf2 = df.GDF(cell, kpts) 187 | gdf2_fname = 'gdf_ints_555_band.h5' 188 | gdf2._cderi_to_save = gdf2_fname 189 | gdf2.auxbasis = gdf.auxbasis 190 | gdf2.kpts_band = kpts_L 191 | if not os.path.isfile(gdf2_fname): 192 | gdf2._j_only = True 193 | gdf2.build(j_only=True, kpts_band=kpts_L) 194 | 195 | kmf2 = dft.KRKS(cell, kpts).density_fit() 196 | kmf2.xc = 'lda' 197 | kmf2.exxdiv = None 198 | kmf2 = scf.addons.smearing_(kmf2, sigma=5e-3, method="fermi") 199 | kmf2.with_df = gdf2 200 | kmf2.with_df._cderi = gdf2_fname 201 | kmf2.mo_energy = kmf.mo_energy 202 | kmf2.mo_occ = kmf.mo_occ 203 | kmf2.mo_coeff = kmf.mo_coeff 204 | 205 | mo_energy_band, mo_coeff_band, hcore_band, veff_band = interpolate.get_bands(kmf2, kpts_L) 206 | 207 | # set up a new mean-field object at kpts_L 208 | kmf3 = dft.KRKS(cell, kpts_L).density_fit() 209 | kmf3.xc = 'lda' 210 | kmf3.exxdiv = None 211 | kmf3 = scf.addons.smearing_(kmf3, sigma=5e-3, method="fermi") 212 | kmf3.mo_energy = mo_energy_band 213 | kmf3.mo_coeff = mo_coeff_band 214 | kmf3.mo_occ = kmf3.get_occ(mo_energy_kpts=mo_energy_band, mo_coeff_kpts=mo_coeff_band) 215 | 216 | # IAO+PAO orbitals at kpts_L 217 | Lat2 = lattice.Lattice(cell, kmesh) 218 | Lat2.kpts = kpts_L 219 | Lat2.set_val_virt_core(nval, nvirt, ncore) 220 | C_ao_iao, C_ao_iao_val, C_ao_iao_virt, C_ao_iao_core \ 221 | = make_basis.get_C_ao_lo_iao(Lat2, kmf3, minao=minao, full_return=True, \ 222 | pmol_val=pmol_val, pmol_core=pmol_core, tol=1e-9) 223 | C_ao_lo_L = C_ao_iao[:,:,ncore:] 224 | 225 | # Make sure two sets of wannier orbitals have same order and phase 226 | ovlp = np.dot(C_ao_lo[0].T.conj(), kmf.get_ovlp()[0]).dot(C_ao_lo_L[0]) 227 | C_ao_lo_L_ordered = np.zeros_like(C_ao_lo_L) 228 | for i in range(ovlp.shape[0]): 229 | idx = np.argmax(np.abs(ovlp[i])) 230 | C_ao_lo_L_ordered[:,:,i] = C_ao_lo_L[:,:,idx] 231 | if (ovlp[i,idx].real < 0): 232 | C_ao_lo_L_ordered[:,:,i] = -C_ao_lo_L_ordered[:,:,i] 233 | C_ao_lo_L = C_ao_lo_L_ordered 234 | 235 | # Get DFT Fock at kpts_L in LO basis 236 | fock_band = hcore_band + veff_band 237 | hcore_dft_band = np.zeros((len(kpts_L),nlo,nlo),dtype=np.complex) 238 | veff_dft_band = np.zeros((len(kpts_L),nlo,nlo),dtype=np.complex) 239 | for k in range(len(kpts_L)): 240 | hcore_dft_band[k] = np.dot(C_ao_lo_L[k].T.conj(), hcore_band[k]).dot(C_ao_lo_L[k]) 241 | veff_dft_band[k] = np.dot(C_ao_lo_L[k].T.conj(), veff_band[k]).dot(C_ao_lo_L[k]) 242 | fock_dft_band = hcore_dft_band + veff_dft_band 243 | 244 | # Save DFT get_bands results 245 | fn = 'hcore_JK_iao_k_dft_band.h5' 246 | feri = h5py.File(fn, 'w') 247 | feri['hcore'] = np.asarray(hcore_dft_band) 248 | feri['JK'] = np.asarray(veff_dft_band) 249 | feri['kpts'] = np.asarray(kpts_L) 250 | feri['C_ao_lo'] = np.asarray(C_ao_lo_L) 251 | feri.close() 252 | 253 | # Interpolate GW self-energy (this part will be rerun in DMFT, can be skipped here) 254 | freqs = np.linspace(0.2, 0.9, 141) 255 | delta = 0.005 256 | sigma = get_kgw_sigma_diff(freqs, delta) 257 | sigma_band_lo = interpolate.interpolate_selfenergy(kmf, kpts_L, sigma, C_ao_lo=C_ao_lo) 258 | 259 | nkpts_band = len(kpts_L) 260 | gf = np.zeros((nlo, nlo, len(freqs)), dtype=np.complex) 261 | for k in range(nkpts_band): 262 | gf += 1./nkpts_band * get_gf(fock_dft_band[k], sigma_band_lo[k], freqs, delta) 263 | 264 | print ('GW t2g, eg') 265 | for i in range(len(freqs)): 266 | print (freqs[i], -gf[1,1,i].imag/np.pi, -gf[3,3,i].imag/np.pi) 267 | 268 | print ('GW total') 269 | for i in range(len(freqs)): 270 | print (freqs[i], -np.trace(gf[:,:,i]).imag/np.pi) 271 | 272 | nkpts_band = len(kpts_L) 273 | gf = np.zeros((nlo, nlo, len(freqs)), dtype=np.complex) 274 | for k in range(nkpts_band): 275 | gf += 1./nkpts_band * get_gf0(fock_dft_band[k], freqs, delta) 276 | 277 | print ('DFT t2g, eg') 278 | for i in range(len(freqs)): 279 | print (freqs[i], -gf[1,1,i].imag/np.pi, -gf[3,3,i].imag/np.pi) 280 | 281 | print ('DFT total') 282 | for i in range(len(freqs)): 283 | print (freqs[i], -np.trace(gf[:,:,i]).imag/np.pi) 284 | 285 | -------------------------------------------------------------------------------- /examples/SrMoO3/srmoo3_lda.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pyscf.pbc import df, dft, scf, cc, gto 3 | import numpy as np 4 | import h5py 5 | from libdmet_solid.system import lattice 6 | from libdmet_solid.basis_transform import make_basis 7 | from libdmet_solid.basis_transform import eri_transform 8 | from pyscf.pbc.lib import chkfile 9 | import os 10 | from pyscf import lib 11 | import libdmet_solid.utils.logger as log 12 | 13 | log.verbose = 'DEBUG1' 14 | 15 | einsum = lib.einsum 16 | 17 | cell = gto.Cell() 18 | cell.build(unit = 'angstrom', 19 | a = ''' 20 | 3.97600 0.00000 0.00000 21 | 0.00000 3.97600 0.00000 22 | 0.00000 0.00000 3.97600 23 | ''', 24 | atom = ''' 25 | Mo 1.98800 1.98800 1.98800 26 | Sr 0.00000 0.00000 0.00000 27 | O 1.98800 1.98800 0.00000 28 | O 1.98800 0.00000 1.98800 29 | O 0.00000 1.98800 1.98800 30 | ''', 31 | dimension = 3, 32 | max_memory = 150000, 33 | verbose = 5, 34 | pseudo = 'gth-pade', 35 | basis='gth-dzvp-molopt-sr', 36 | precision=1e-12) 37 | 38 | kmesh = [5,5,5] 39 | Lat = lattice.Lattice(cell, kmesh) 40 | kpts = Lat.kpts 41 | nao = Lat.nao 42 | nkpts = Lat.nkpts 43 | 44 | gdf = df.GDF(cell, kpts) 45 | gdf._cderi_to_save = 'gdf_ints_555.h5' 46 | gdf.auxbasis = df.aug_etb(cell, beta=2.3, use_lval=True, l_val_set={'Mo':2,'O':1,'Sr':1}) 47 | gdf.build() 48 | 49 | chkfname = 'srmoo3_555.chk' 50 | if os.path.isfile(chkfname): 51 | kmf = dft.KRKS(cell, kpts) 52 | kmf.xc = 'lda' 53 | kmf.exxdiv = None 54 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 55 | kmf.with_df = gdf 56 | kmf.with_df._cderi = 'gdf_ints_555.h5' 57 | data = chkfile.load(chkfname, 'scf') 58 | kmf.__dict__.update(data) 59 | else: 60 | kmf = dft.KRKS(cell, kpts) 61 | kmf.xc = 'lda' 62 | kmf.exxdiv = None 63 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 64 | kmf.with_df = gdf 65 | kmf.with_df._cderi = 'gdf_ints_555.h5' 66 | kmf.conv_tol = 1e-12 67 | kmf.chkfile = chkfname 68 | kmf.diis_space = 15 69 | kmf.max_cycle = 50 70 | kmf.kernel() 71 | 72 | nmo = len(kmf.mo_energy[0]) 73 | 74 | import copy 75 | mo_occ_old = copy.deepcopy(kmf.mo_occ) 76 | homo = -99; lumo = 99 77 | for k in range(nkpts): 78 | for i in range(nmo): 79 | if kmf.mo_occ[k][i] > 1.0: 80 | kmf.mo_occ[k][i] = 2. 81 | if kmf.mo_energy[k][i] > homo: 82 | homo = kmf.mo_energy[k][i] 83 | else: 84 | kmf.mo_occ[k][i] = 0. 85 | if kmf.mo_energy[k][i] < lumo: 86 | lumo = kmf.mo_energy[k][i] 87 | 88 | print (homo, lumo) 89 | 90 | -------------------------------------------------------------------------------- /examples/SrMoO3/srmoo3_set_ham.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | import h5py, os 4 | from libdmet_solid.system import lattice 5 | from libdmet_solid.basis_transform import make_basis 6 | from libdmet_solid.basis_transform import eri_transform 7 | import libdmet_solid.utils.logger as log 8 | 9 | from pyscf.pbc import df, dft, scf, gto 10 | from pyscf.pbc.lib import chkfile 11 | from pyscf import lib 12 | from pyscf import gto as gto_mol 13 | from pyscf import scf as scf_mol 14 | 15 | from fcdmft.gw.mol import gw_dc 16 | from fcdmft.utils import cholesky 17 | 18 | import copy 19 | from libdmet_solid.utils.misc import kdot, mdot 20 | from libdmet_solid.lo.iao import get_idx, get_idx_each_atom, \ 21 | get_idx_each_orbital 22 | from libdmet_solid.lo.iao import reference_mol, get_labels, get_idx_each 23 | 24 | log.verbose = 'DEBUG1' 25 | 26 | einsum = lib.einsum 27 | 28 | # NOTE: lattice system setup by user 29 | cell = gto.Cell() 30 | cell.build(unit = 'angstrom', 31 | a = ''' 32 | 3.97600 0.00000 0.00000 33 | 0.00000 3.97600 0.00000 34 | 0.00000 0.00000 3.97600 35 | ''', 36 | atom = ''' 37 | Mo 1.98800 1.98800 1.98800 38 | Sr 0.00000 0.00000 0.00000 39 | O 1.98800 1.98800 0.00000 40 | O 1.98800 0.00000 1.98800 41 | O 0.00000 1.98800 1.98800 42 | ''', 43 | dimension = 3, 44 | max_memory = 100000, 45 | verbose = 5, 46 | pseudo = 'gth-pade', 47 | basis='gth-dzvp-molopt-sr', 48 | precision=1e-12) 49 | 50 | kmesh = [5,5,5] 51 | Lat = lattice.Lattice(cell, kmesh) 52 | kpts = Lat.kpts 53 | nao = Lat.nao 54 | nkpts = Lat.nkpts 55 | 56 | gdf = df.GDF(cell, kpts) 57 | gdf_fname = 'gdf_ints_555.h5' 58 | gdf._cderi_to_save = gdf_fname 59 | gdf.auxbasis = df.aug_etb(cell, beta=2.3, use_lval=True, l_val_set={'Mo':2,'O':1,'Sr':1}) 60 | if not os.path.isfile(gdf_fname): 61 | gdf.build() 62 | 63 | chkfname = 'srmoo3_555.chk' 64 | if os.path.isfile(chkfname): 65 | kmf = dft.KRKS(cell, kpts) 66 | kmf.xc = 'lda' 67 | kmf.exxdiv = None 68 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 69 | kmf.with_df = gdf 70 | kmf.with_df._cderi = gdf_fname 71 | data = chkfile.load(chkfname, 'scf') 72 | kmf.__dict__.update(data) 73 | else: 74 | kmf = dft.KRKS(cell, kpts) 75 | kmf.xc = 'lda' 76 | kmf.exxdiv = None 77 | kmf = scf.addons.smearing_(kmf, sigma=5e-3, method="fermi") 78 | kmf.with_df = gdf 79 | kmf.with_df._cderi = gdf_fname 80 | kmf.conv_tol = 1e-12 81 | kmf.chkfile = chkfname 82 | kmf.diis_space = 15 83 | kmf.max_cycle = 50 84 | kmf.kernel() 85 | 86 | # set spin 87 | mo_energy = np.asarray(kmf.mo_energy) 88 | mo_coeff = np.asarray(kmf.mo_coeff) 89 | if len(mo_energy.shape) == 2: 90 | spin = 1 91 | mo_energy = mo_energy[np.newaxis, ...] 92 | mo_coeff = mo_coeff[np.newaxis, ...] 93 | else: 94 | spin = 2 95 | 96 | # NOTE: choose IAO basis by user 97 | # set IAO (core+val) 98 | minao = 'gth-szv-molopt-sr' 99 | pmol = reference_mol(cell, minao=minao) 100 | basis = pmol._basis 101 | 102 | # set valence IAO 103 | basis_val = {} 104 | minao_val = 'gth-szv-molopt-sr-val' 105 | pmol_val = pmol.copy() 106 | pmol_val.basis = minao_val 107 | pmol_val.build() 108 | basis_val["Sr"] = copy.deepcopy(pmol_val._basis["Sr"]) 109 | basis_val["Mo"] = copy.deepcopy(pmol_val._basis["Mo"]) 110 | basis_val["O"] = copy.deepcopy(pmol_val._basis["O"]) 111 | 112 | pmol_val = pmol.copy() 113 | pmol_val.basis = basis_val 114 | pmol_val.build() 115 | 116 | val_labels = pmol_val.ao_labels() 117 | for i in range(len(val_labels)): 118 | val_labels[i] = val_labels[i].replace("Mo 4s", "Mo 5s") 119 | val_labels[i] = val_labels[i].replace("Sr 4s", "Sr 5s") 120 | pmol_val.ao_labels = lambda *args: val_labels 121 | 122 | # set core IAO 123 | basis_core = {} 124 | minao_core = 'gth-szv-molopt-sr-core' 125 | pmol_core = pmol.copy() 126 | pmol_core.basis = minao_core 127 | pmol_core.build() 128 | basis_core["Sr"] = copy.deepcopy(pmol_core._basis["Sr"]) 129 | basis_core["Mo"] = copy.deepcopy(pmol_core._basis["Mo"]) 130 | basis_core["O"] = copy.deepcopy(pmol_core._basis["O"]) 131 | 132 | pmol_core = pmol.copy() 133 | pmol_core.basis = basis_core 134 | pmol_core.build() 135 | core_labels = pmol_core.ao_labels() 136 | 137 | ncore = len(pmol_core.ao_labels()) 138 | nval = pmol_val.nao_nr() 139 | nvirt = cell.nao_nr() - ncore - nval 140 | Lat.set_val_virt_core(nval, nvirt, ncore) 141 | 142 | # First construct IAO and PAO. 143 | C_ao_iao, C_ao_iao_val, C_ao_iao_virt, C_ao_iao_core \ 144 | = make_basis.get_C_ao_lo_iao(Lat, kmf, minao=minao, full_return=True, \ 145 | pmol_val=pmol_val, pmol_core=pmol_core, tol=1e-9) 146 | 147 | # C_ao_lo: transformation matrix from AO to LO (IAO) basis 148 | nlo = nao - ncore 149 | C_ao_lo = np.zeros((spin,nkpts,nao,nlo),dtype=np.complex128) 150 | for s in range(spin): 151 | C_ao_lo[s] = C_ao_iao[:,:,ncore:] 152 | 153 | # C_mo_lo: transformation matrix from MO to LO (IAO) basis 154 | S_ao_ao = kmf.get_ovlp() 155 | C_mo_lo = np.zeros((spin,nkpts,nao,nlo),dtype=np.complex128) 156 | for s in range(spin): 157 | for ki in range(nkpts): 158 | C_mo_lo[s][ki] = np.dot(np.dot(mo_coeff[s][ki].T.conj(), S_ao_ao[ki]), C_ao_lo[s][ki]) 159 | fn = 'C_mo_lo.h5' 160 | feri = h5py.File(fn, 'w') 161 | feri['C_ao_lo'] = np.asarray(C_ao_lo) 162 | feri['C_mo_lo'] = np.asarray(C_mo_lo) 163 | feri.close() 164 | 165 | # get DFT density matrix in IAO basis 166 | DM_ao = np.asarray(kmf.make_rdm1()) 167 | if len(DM_ao.shape) == 3: 168 | DM_ao = DM_ao[np.newaxis, ...] 169 | DM_lo = np.zeros((spin,nkpts,nlo,nlo),dtype=DM_ao.dtype) 170 | for s in range(spin): 171 | for ki in range(nkpts): 172 | Cinv = np.dot(C_ao_lo[s][ki].T.conj(),S_ao_ao[ki]) 173 | DM_lo[s][ki] = np.dot(np.dot(Cinv, DM_ao[s][ki]), Cinv.T.conj()) 174 | 175 | for s in range(spin): 176 | nelec_lo = np.trace(DM_lo[s].sum(axis=0)/nkpts) 177 | print ('Nelec imp', nelec_lo.real) 178 | fn = 'DM_iao_k.h5' 179 | feri = h5py.File(fn, 'w') 180 | feri['DM'] = np.asarray(DM_lo) 181 | feri.close() 182 | 183 | # get 4-index ERI 184 | eri = eri_transform.get_unit_eri_fast(cell, gdf, C_ao_lo=C_ao_lo, feri=gdf_fname) 185 | fn = 'eri_imp111_iao.h5' 186 | feri = h5py.File(fn, 'w') 187 | feri['eri'] = np.asarray(eri.real) 188 | feri.close() 189 | 190 | # get one-electron integrals 191 | hcore_ao = np.asarray(kmf.get_hcore()) 192 | JK_ao = np.asarray(kmf.get_veff()) 193 | if len(JK_ao.shape) == 3: 194 | JK_ao = JK_ao[np.newaxis, ...] 195 | hcore_lo = np.zeros((spin,nkpts,nlo,nlo),dtype=hcore_ao.dtype) 196 | JK_lo = np.zeros((spin,nkpts,nlo,nlo),dtype=JK_ao.dtype) 197 | for s in range(spin): 198 | for ki in range(nkpts): 199 | hcore_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), hcore_ao[ki]), C_ao_lo[s,ki]) 200 | JK_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), JK_ao[s,ki]), C_ao_lo[s,ki]) 201 | 202 | fn = 'hcore_JK_iao_k_dft.h5' 203 | feri = h5py.File(fn, 'w') 204 | feri['hcore'] = np.asarray(hcore_lo) 205 | feri['JK'] = np.asarray(JK_lo) 206 | feri.close() 207 | assert(np.max(np.abs(hcore_lo.sum(axis=1).imag/nkpts))<1e-6) 208 | assert(np.max(np.abs(JK_lo.sum(axis=1).imag/nkpts))<1e-6) 209 | 210 | # get HF JK term using DFT density 211 | kmf_hf = scf.KRHF(cell, kpts, exxdiv=None) 212 | kmf_hf.with_df = gdf 213 | kmf_hf.with_df._cderi = gdf_fname 214 | kmf_hf.max_cycle = 0 215 | JK_ao = np.asarray(kmf_hf.get_veff(dm_kpts=DM_ao[0])) 216 | if len(JK_ao.shape) == 3: 217 | JK_ao = JK_ao[np.newaxis, ...] 218 | 219 | # NOTE: choose finite size correction by user 220 | # set gw_fc to True if finite size correction is used in kgw 221 | gw_fc = False 222 | if gw_fc: 223 | # finite size correction to exchange 224 | vk_corr = -2./np.pi * (6.*np.pi**2/cell.vol/nkpts)**(1./3.) 225 | nocc = cell.nelectron // 2 226 | JK_mo = np.zeros((spin,nkpts,nao,nao),dtype=JK_ao.dtype) 227 | for s in range(spin): 228 | for ki in range(nkpts): 229 | JK_mo[s,ki] = np.dot(np.dot(mo_coeff[s,ki].T.conj(), JK_ao[s,ki]), mo_coeff[s,ki]) 230 | for i in range(nocc): 231 | JK_mo[s,ki][i,i] = JK_mo[s,ki][i,i] + vk_corr 232 | 233 | JK_lo = np.zeros((spin,nkpts,nlo,nlo),dtype=JK_ao.dtype) 234 | for s in range(spin): 235 | for ki in range(nkpts): 236 | JK_lo[s,ki] = np.dot(np.dot(C_mo_lo[s,ki].T.conj(), JK_mo[s,ki]), C_mo_lo[s,ki]) 237 | else: 238 | JK_lo = np.zeros((spin,nkpts,nlo,nlo),dtype=JK_ao.dtype) 239 | for s in range(spin): 240 | for ki in range(nkpts): 241 | JK_lo[s,ki] = np.dot(np.dot(C_ao_lo[s,ki].T.conj(), JK_ao[s,ki]), C_ao_lo[s,ki]) 242 | 243 | fn = 'hcore_JK_iao_k_hf.h5' 244 | feri = h5py.File(fn, 'w') 245 | feri['JK'] = np.asarray(JK_lo) 246 | feri.close() 247 | 248 | # Cholesky decomposition for generating 3-index density-fitted ERI (required by gw_dc) 249 | if not os.path.isfile('cderi.h5'): 250 | try: 251 | cd = cholesky.cholesky(eri[0], tau=1e-7, dimQ=50) 252 | except: 253 | cd = cholesky.cholesky(eri[0], tau=1e-6, dimQ=50) 254 | cderi = cd.kernel() 255 | cderi = cderi.reshape(-1,nlo,nlo) 256 | print ('3-index ERI', cderi.shape) 257 | fn = 'cderi.h5' 258 | feri = h5py.File(fn, 'w') 259 | feri['cderi'] = np.asarray(cderi) 260 | feri.close() 261 | else: 262 | fn = 'cderi.h5' 263 | feri = h5py.File(fn, 'r') 264 | cderi = np.asarray(feri['cderi']) 265 | feri.close() 266 | 267 | # Compute GW double counting self-energy 268 | naux, nimp, nimp = cderi.shape 269 | nocc = cell.nelectron // 2 270 | # NOTE: set ef by user for metals 271 | ef = 0.560479331479 272 | 273 | # NOTE: check analytic continuation stability (sigma) by user 274 | mol = gto_mol.M() 275 | mol.verbose = 5 276 | mf = scf_mol.RHF(mol) 277 | mf.max_memory = kmf.max_memory 278 | gw = gw_dc.GWGF(mf) 279 | gw.nmo = nimp 280 | gw.nocc = nocc 281 | gw.eta = 0.1/27.211386 282 | gw.ac = 'pade' 283 | gw.ef = ef 284 | gw.fullsigma = True 285 | omega = np.linspace(7./27.211386,23./27.211386,161) 286 | sigma_lo = gw.kernel(Lpq=cderi, omega=omega, kmf=kmf, C_mo_lo=C_mo_lo, nw=100, nt=20000) 287 | print('### local GW self-energy (trace) on real axis ###') 288 | print('# freq imag real #') 289 | for i in range(len(omega)): 290 | print (omega[i], np.trace(sigma_lo[:,:,i].imag), np.trace(sigma_lo[:,:,i].real)) 291 | -------------------------------------------------------------------------------- /examples/interpolation/diamond_gw_interpolate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pyscf.pbc import df, gto, dft, scf 3 | from pyscf.pbc.lib import chkfile 4 | import os 5 | import matplotlib.pyplot as plt 6 | from ase.lattice import bulk 7 | from ase.dft.kpoints import sc_special_points as special_points, get_bandpath 8 | from fcdmft.utils import interpolate 9 | 10 | ''' 11 | GW Wannier band interpolation: 12 | Step 1: Get LDA bands at larger k-mesh (6x6x6) from get_bands (non-SCF diagonalization) 13 | Step 2: Interpolate LDA bands at random kpts_band using 6x6x6 results -> fock_dft_band 14 | Step 3: Interpolate GW self-energy at kpts_band using 4x4x4 results -> 15 | sigma_band_lo, then get GW bands 16 | 17 | Note: Currently a k-mesh must be used for constructing Wannier orbitals 18 | ''' 19 | 20 | cell = gto.Cell() 21 | cell.build(unit = 'angstrom', 22 | a = np.array([[0.000000, 1.783500, 1.783500], 23 | [1.783500, 0.000000, 1.783500], 24 | [1.783500, 1.783500, 0.000000]]), 25 | atom = 'C 1.337625 1.337625 1.337625; C 2.229375 2.229375 2.229375', 26 | dimension = 3, 27 | max_memory = 32000, 28 | verbose = 5, 29 | pseudo = 'gth-pade', 30 | basis='gth-dzv', 31 | precision=1e-10) 32 | 33 | kmesh = [4,4,4] 34 | kpts = cell.make_kpts(kmesh,scaled_center=[0,0,0]) 35 | gdf = df.GDF(cell, kpts) 36 | gdf_fname = 'gdf_ints_444.h5' 37 | gdf._cderi_to_save = gdf_fname 38 | if not os.path.isfile(gdf_fname): 39 | gdf.build() 40 | 41 | chkfname = 'diamond_444.chk' 42 | if os.path.isfile(chkfname): 43 | kmf = dft.KRKS(cell, kpts) 44 | kmf.with_df = gdf 45 | kmf.with_df._cderi = gdf_fname 46 | data = chkfile.load(chkfname, 'scf') 47 | kmf.__dict__.update(data) 48 | else: 49 | kmf = dft.KRKS(cell, kpts) 50 | kmf.with_df = gdf 51 | kmf.with_df._cderi = gdf_fname 52 | kmf.conv_tol = 1e-12 53 | kmf.chkfile = chkfname 54 | kmf.kernel() 55 | 56 | # GW@LDA 57 | from fcdmft.gw.pbc import krgw_gf 58 | gw = krgw_gf.KRGWGF(kmf) 59 | gw.eta = 1e-2 60 | gw.fullsigma = True 61 | gw.fc = True 62 | omega = np.linspace(-12./27.211386, 34./27.211386, 231) 63 | gf, gf0, sigma = gw.kernel(omega=omega, writefile=0) 64 | 65 | from libdmet_solid.basis_transform import make_basis 66 | from libdmet_solid.lo import pywannier90 67 | # valence Wannier orbitals 68 | num_wann = 8 69 | keywords = \ 70 | ''' 71 | num_iter = 1000 72 | begin projections 73 | C:sp3 74 | end projections 75 | exclude_bands : 9-%s 76 | num_cg_steps = 100 77 | precond = T 78 | '''%(kmf.cell.nao_nr()) 79 | 80 | w90 = pywannier90.W90(kmf, kmesh, num_wann, other_keywords = keywords) 81 | #w90.use_atomic = True 82 | #w90.use_bloch_phases = True 83 | #w90.use_scdm = True 84 | #w90.guiding_centres = False 85 | w90.kernel() 86 | 87 | C_ao_mo = np.asarray(w90.mo_coeff)[:, :, w90.band_included_list] 88 | C_mo_lo = make_basis.tile_u_matrix(np.array(w90.U_matrix.transpose(2, 0, 1), \ 89 | order='C'), u_virt=None, u_core=None) 90 | C_ao_lo = make_basis.multiply_basis(C_ao_mo, C_mo_lo) 91 | nbands = C_ao_lo.shape[-1] 92 | 93 | # set up band_kpts 94 | points = special_points['fcc'] 95 | G = points['G'] 96 | X = points['X'] 97 | W = points['W'] 98 | K = points['K'] 99 | L = points['L'] 100 | band_kpts, kpath, sp_points = get_bandpath([L, G, X, W, K, G], cell.a, npoints=100) 101 | band_kpts = cell.get_abs_kpts(band_kpts) 102 | 103 | # get DFT bands for a large k-mesh 104 | kmesh_L = [6,6,6] 105 | kpts_L = cell.make_kpts(kmesh_L,scaled_center=[0,0,0]) 106 | gdf2 = df.GDF(cell, kpts) 107 | gdf2_fname = 'gdf_ints_large.h5' 108 | gdf2._cderi_to_save = gdf2_fname 109 | gdf2.auxbasis = gdf.auxbasis 110 | gdf2.kpts_band = kpts_L 111 | if not os.path.isfile(gdf2_fname): 112 | gdf2._j_only = True 113 | gdf2.build(j_only=True, kpts_band=kpts_L) 114 | 115 | kmf2 = dft.KRKS(cell, kpts).density_fit() 116 | kmf2.xc = kmf.xc 117 | kmf2.exxdiv = kmf.exxdiv 118 | if hasattr(kmf, 'sigma'): 119 | kmf2 = scf.addons.smearing_(kmf2, sigma=kmf.sigma, method="fermi") 120 | kmf2.with_df = gdf2 121 | kmf2.with_df._cderi = gdf2_fname 122 | kmf2.mo_energy = kmf.mo_energy 123 | kmf2.mo_occ = kmf.mo_occ 124 | kmf2.mo_coeff = kmf.mo_coeff 125 | 126 | mo_energy_L, mo_coeff_L, hcore_L, veff_L = interpolate.get_bands(kmf2, kpts_L) 127 | 128 | # set up a new mean-field object at large k-mesh 129 | kmf3 = dft.KRKS(cell, kpts_L).density_fit() 130 | kmf3.xc = kmf.xc 131 | kmf3.exxdiv = kmf.exxdiv 132 | if hasattr(kmf, 'sigma'): 133 | kmf3 = scf.addons.smearing_(kmf3, sigma=kmf.sigma, method="fermi") 134 | kmf3.mo_energy = mo_energy_L 135 | kmf3.mo_coeff = mo_coeff_L 136 | kmf3.mo_occ = kmf3.get_occ(mo_energy_kpts=mo_energy_L, mo_coeff_kpts=mo_coeff_L) 137 | 138 | # Wannier basis for large k-mesh 139 | w90_L = pywannier90.W90(kmf3, kmesh_L, num_wann, other_keywords = keywords) 140 | w90_L.kernel() 141 | C_ao_mo = np.asarray(w90_L.mo_coeff)[:, :, w90_L.band_included_list] 142 | C_mo_lo = make_basis.tile_u_matrix(np.array(w90_L.U_matrix.transpose(2, 0, 1), \ 143 | order='C'), u_virt=None, u_core=None) 144 | C_ao_lo_L = make_basis.multiply_basis(C_ao_mo, C_mo_lo) 145 | 146 | # Make sure two sets of wannier orbitals have same order and phase 147 | # Search for Gamma point in kpts_L 148 | idx_G = None 149 | for i in range(len(kpts_L)): 150 | if np.linalg.norm(kpts_L[i]-kpts[0]) < 1e-8: 151 | idx_G = i 152 | break 153 | assert (idx_G is not None) 154 | ovlp = np.dot(C_ao_lo[0].T.conj(), kmf.get_ovlp()[0]).dot(C_ao_lo_L[idx_G]) 155 | C_ao_lo_L_ordered = np.zeros_like(C_ao_lo_L) 156 | for i in range(ovlp.shape[0]): 157 | idx = np.argmax(np.abs(ovlp[i])) 158 | C_ao_lo_L_ordered[:,:,i] = C_ao_lo_L[:,:,idx] 159 | if (ovlp[i,idx].real < 0): 160 | C_ao_lo_L_ordered[:,:,i] = -C_ao_lo_L_ordered[:,:,i] 161 | C_ao_lo_L = C_ao_lo_L_ordered 162 | 163 | # Get DFT Fock at band_kpts through interpolation 164 | mo_energy_band, mo_coeff_band, fock_dft_band = interpolate.interpolate_mf(kmf3, band_kpts, C_ao_lo=C_ao_lo_L, 165 | veff=veff_L, hcore=hcore_L, w90=w90_L) 166 | 167 | # Interpolate GW self-energy 168 | sigma_band_lo = interpolate.interpolate_selfenergy(kmf, band_kpts, sigma, C_ao_lo=C_ao_lo, w90=w90) 169 | 170 | # Get GW bands at band_kpts through interpolating mo diff 171 | e_kn_2 = interpolate.interpolate_hf_diff(kmf, band_kpts, fock_dft_band, mo_interpolate=True, 172 | mo_energy_hf=gw.mo_energy, C_ao_lo=C_ao_lo, w90=w90)[0] 173 | vbmax = -99 174 | for en in e_kn_2: 175 | vb_k = en[cell.nelectron//2-1] 176 | if vb_k > vbmax: 177 | vbmax = vb_k 178 | e_kn_2 = [en - vbmax for en in e_kn_2] 179 | 180 | # plot 181 | nkpts_band, nlo, nlo, nw = sigma_band_lo.shape 182 | delta = 0.2/27.211386 183 | dens = np.zeros((nkpts_band, nw)) 184 | for iw in range(nw): 185 | for k in range(nkpts_band): 186 | gf_band = np.linalg.inv((omega[iw]+1j*delta)*np.eye(nbands)-fock_dft_band[k]-sigma_band_lo[k,:,:,iw]) 187 | dens[k,iw] = -1./np.pi*np.trace(gf_band.imag)/27.211386 188 | dens = dens.T 189 | 190 | omega_new = omega 191 | for iw in range(len(omega_new)): 192 | omega_new[iw] -= gw.mo_energy[0][3] 193 | 194 | space_max = 0. 195 | for i in range(1,nkpts_band): 196 | if kpath[i]-kpath[i-1] > space_max: 197 | space_max = kpath[i]-kpath[i-1] 198 | k_max = (nkpts_band-1)*space_max 199 | kpath_new = np.linspace(0,k_max,nkpts_band) 200 | 201 | sp_points_new = np.zeros_like(sp_points) 202 | for i in range(len(sp_points)): 203 | for j in range(nkpts_band): 204 | if sp_points[i] == kpath[j]: 205 | sp_points_new[i] = kpath_new[j] 206 | 207 | xi = kpath_new 208 | yi = omega_new*27.211386 209 | fig = plt.subplots(figsize=(3.375,1.8)) 210 | ax = plt.subplot(111) 211 | interpolation_method = 'gaussian' 212 | extent = (xi[0],xi[-1],yi[0],yi[-1]) 213 | plt.imshow(dens, cmap='viridis', aspect=0.11, extent=extent, origin='lower', interpolation=interpolation_method) 214 | cbar = plt.colorbar() 215 | cbar.set_label('DOS [1/eV]', size=6) 216 | cbar.ax.tick_params(labelsize=6) 217 | 218 | emin = yi[0] 219 | emax = yi[-1] 220 | ax.axis(xmin=0, xmax=sp_points_new[-1], ymin=emin, ymax=emax) 221 | plt.xticks(sp_points_new, ['%s' % n for n in ['L', r'$\Gamma$', 'X', 'W', 'K', r'$\Gamma$']]) 222 | 223 | for p in sp_points_new: 224 | ax.plot([p, p], [emin, emax], '-', color='k', linewidth=0.6) 225 | ax.plot([0, sp_points_new[-1]], [0, 0], '--', color='k',linewidth=0.6) 226 | 227 | au2ev = 27.211386 228 | for n in range(nbands): 229 | ax.plot(kpath_new, [e[n]*au2ev for e in e_kn_2], '--',dashes=([2.2,1.4]),alpha=0.6,linewidth=0.6,color='white') 230 | 231 | ax.set_ylabel('Energy(eV)',fontsize=9) 232 | ax.yaxis.labelpad = -1 233 | 234 | major_yticks=np.arange(-25,20.1,5.0) 235 | #minor_yticks=np.arange(-25,20.1,1.0) 236 | ax.set_yticks(major_yticks) 237 | #ax.set_yticks(minor_yticks,minor=True) 238 | for axis in ['top','bottom','left','right']: 239 | ax.spines[axis].set_linewidth(0.5) 240 | ax.tick_params(axis='both',which='major',labelsize=8) 241 | ax.tick_params(axis='both',which='major',length=2.4,width=0.4,pad=2) 242 | ax.tick_params(axis='both',which='minor',length=1.5,width=0.4) 243 | 244 | plt.subplots_adjust(left=0.08,right=0.99,bottom=0.10,top=0.95) 245 | plt.savefig('diamond_444_GW.png', dpi=600) 246 | -------------------------------------------------------------------------------- /examples/interpolation/diamond_gw_interpolate_iao.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pyscf.pbc import df, gto, dft, scf 3 | from pyscf.pbc.lib import chkfile 4 | import os 5 | import matplotlib.pyplot as plt 6 | from ase.lattice import bulk 7 | from ase.dft.kpoints import sc_special_points as special_points, get_bandpath 8 | from fcdmft.utils import interpolate 9 | 10 | ''' 11 | GW IAO+PAO band interpolation: 12 | Step 1: Get LDA bands at band_kpts from get_bands (non-SCF diagonalization) 13 | Step 2: Interpolate GW self-energy at kpts_band using 4x4x4 results -> 14 | sigma_band_lo, then get GW bands 15 | ''' 16 | 17 | cell = gto.Cell() 18 | cell.build(unit = 'angstrom', 19 | a = np.array([[0.000000, 1.783500, 1.783500], 20 | [1.783500, 0.000000, 1.783500], 21 | [1.783500, 1.783500, 0.000000]]), 22 | atom = 'C 1.337625 1.337625 1.337625; C 2.229375 2.229375 2.229375', 23 | dimension = 3, 24 | max_memory = 32000, 25 | verbose = 5, 26 | pseudo = 'gth-pade', 27 | basis='gth-dzv', 28 | precision=1e-10) 29 | 30 | kmesh = [4,4,4] 31 | kpts = cell.make_kpts(kmesh,scaled_center=[0,0,0]) 32 | gdf = df.GDF(cell, kpts) 33 | gdf_fname = 'gdf_ints_444.h5' 34 | gdf._cderi_to_save = gdf_fname 35 | if not os.path.isfile(gdf_fname): 36 | gdf.build() 37 | 38 | chkfname = 'diamond_444.chk' 39 | if os.path.isfile(chkfname): 40 | kmf = dft.KRKS(cell, kpts) 41 | kmf.with_df = gdf 42 | kmf.with_df._cderi = gdf_fname 43 | data = chkfile.load(chkfname, 'scf') 44 | kmf.__dict__.update(data) 45 | else: 46 | kmf = dft.KRKS(cell, kpts) 47 | kmf.with_df = gdf 48 | kmf.with_df._cderi = gdf_fname 49 | kmf.conv_tol = 1e-12 50 | kmf.chkfile = chkfname 51 | kmf.kernel() 52 | 53 | # GW@LDA 54 | from fcdmft.gw.pbc import krgw_gf 55 | gw = krgw_gf.KRGWGF(kmf) 56 | gw.eta = 1e-2 57 | gw.fullsigma = True 58 | gw.fc = True 59 | omega = np.linspace(-12./27.211386, 44./27.211386, 281) 60 | gf, gf0, sigma = gw.kernel(omega=omega, writefile=0) 61 | 62 | from libdmet_solid.system import lattice 63 | from libdmet_solid.basis_transform import make_basis 64 | # IAO+PAO orbitals 65 | Lat = lattice.Lattice(cell, kmesh) 66 | MINAO = {'C':'gth-szv'} 67 | C_ao_iao, C_ao_iao_val, C_ao_iao_virt = make_basis.get_C_ao_lo_iao(Lat, kmf, minao=MINAO, full_return=True) 68 | C_ao_lo = C_ao_iao 69 | nbands = C_ao_lo.shape[-1] 70 | 71 | # set up band_kpts 72 | points = special_points['fcc'] 73 | G = points['G'] 74 | X = points['X'] 75 | W = points['W'] 76 | K = points['K'] 77 | L = points['L'] 78 | band_kpts, kpath, sp_points = get_bandpath([L, G, X, W, K, G], cell.a, npoints=100) 79 | band_kpts = cell.get_abs_kpts(band_kpts) 80 | 81 | # get DFT bands at band_kpts 82 | gdf2 = df.GDF(cell, kpts) 83 | gdf2_fname = 'gdf_ints_444_band.h5' 84 | gdf2._cderi_to_save = gdf2_fname 85 | gdf2.auxbasis = gdf.auxbasis 86 | gdf2.kpts_band = band_kpts 87 | if not os.path.isfile(gdf2_fname): 88 | gdf2._j_only = True 89 | gdf2.build(j_only=True, kpts_band=band_kpts) 90 | 91 | kmf2 = dft.KRKS(cell, kpts).density_fit() 92 | kmf2.xc = kmf.xc 93 | kmf2.exxdiv = kmf.exxdiv 94 | if hasattr(kmf, 'sigma'): 95 | kmf2 = scf.addons.smearing_(kmf2, sigma=kmf.sigma, method="fermi") 96 | kmf2.with_df = gdf2 97 | kmf2.with_df._cderi = gdf2_fname 98 | kmf2.mo_energy = kmf.mo_energy 99 | kmf2.mo_occ = kmf.mo_occ 100 | kmf2.mo_coeff = kmf.mo_coeff 101 | 102 | mo_energy_band, mo_coeff_band, hcore_band, veff_band = interpolate.get_bands(kmf2, band_kpts) 103 | 104 | # set up a new mean-field object at band_kpts 105 | kmf3 = dft.KRKS(cell, band_kpts).density_fit() 106 | kmf3.xc = kmf.xc 107 | kmf3.exxdiv = kmf.exxdiv 108 | if hasattr(kmf, 'sigma'): 109 | kmf3 = scf.addons.smearing_(kmf3, sigma=kmf.sigma, method="fermi") 110 | kmf3.mo_energy = mo_energy_band 111 | kmf3.mo_coeff = mo_coeff_band 112 | kmf3.mo_occ = kmf3.get_occ(mo_energy_kpts=mo_energy_band, mo_coeff_kpts=mo_coeff_band) 113 | 114 | # IAO+PAO orbitals at band_kpts 115 | Lat2 = lattice.Lattice(cell, kmesh) 116 | Lat2.kpts = band_kpts 117 | MINAO = {'C':'gth-szv'} 118 | C_ao_iao, C_ao_iao_val, C_ao_iao_virt = make_basis.get_C_ao_lo_iao(Lat2, kmf3, minao=MINAO, full_return=True) 119 | C_ao_lo_L = C_ao_iao 120 | 121 | # Make sure two sets of wannier orbitals have same order and phase 122 | # Search for Gamma point in band_kpts 123 | idx_G = None 124 | for i in range(len(band_kpts)): 125 | if np.linalg.norm(band_kpts[i]-kpts[0]) < 1e-8: 126 | idx_G = i 127 | break 128 | assert (idx_G is not None) 129 | ovlp = np.dot(C_ao_lo[0].T.conj(), kmf.get_ovlp()[0]).dot(C_ao_lo_L[idx_G]) 130 | C_ao_lo_L_ordered = np.zeros_like(C_ao_lo_L) 131 | for i in range(ovlp.shape[0]): 132 | idx = np.argmax(np.abs(ovlp[i])) 133 | C_ao_lo_L_ordered[:,:,i] = C_ao_lo_L[:,:,idx] 134 | if (ovlp[i,idx].real < 0): 135 | C_ao_lo_L_ordered[:,:,i] = -C_ao_lo_L_ordered[:,:,i] 136 | C_ao_lo_L = C_ao_lo_L_ordered 137 | 138 | # Get DFT Fock at band_kpts in LO basis 139 | fock_band = hcore_band + veff_band 140 | fock_dft_band = np.zeros((len(band_kpts),nbands,nbands),dtype=np.complex) 141 | for k in range(len(band_kpts)): 142 | fock_dft_band[k] = np.dot(C_ao_lo_L[k].T.conj(), fock_band[k]).dot(C_ao_lo_L[k]) 143 | 144 | # Interpolate GW self-energy 145 | sigma_band_lo = interpolate.interpolate_selfenergy(kmf, band_kpts, sigma, C_ao_lo=C_ao_lo) 146 | 147 | # Get GW bands at band_kpts through interpolating mo diff 148 | e_kn_2 = interpolate.interpolate_hf_diff(kmf, band_kpts, fock_dft_band, mo_interpolate=True, 149 | mo_energy_hf=gw.mo_energy, C_ao_lo=C_ao_lo)[0] 150 | vbmax = -99 151 | for en in e_kn_2: 152 | vb_k = en[cell.nelectron//2-1] 153 | if vb_k > vbmax: 154 | vbmax = vb_k 155 | e_kn_2 = [en - vbmax for en in e_kn_2] 156 | 157 | # plot 158 | nkpts_band, nlo, nlo, nw = sigma_band_lo.shape 159 | delta = 0.2/27.211386 160 | dens = np.zeros((nkpts_band, nw)) 161 | for iw in range(nw): 162 | for k in range(nkpts_band): 163 | gf_band = np.linalg.inv((omega[iw]+1j*delta)*np.eye(nbands)-fock_dft_band[k]-sigma_band_lo[k,:,:,iw]) 164 | dens[k,iw] = -1./np.pi*np.trace(gf_band.imag)/27.211386 165 | dens = dens.T 166 | 167 | omega_new = omega 168 | for iw in range(len(omega_new)): 169 | omega_new[iw] -= gw.mo_energy[0][3] 170 | 171 | space_max = 0. 172 | for i in range(1,nkpts_band): 173 | if kpath[i]-kpath[i-1] > space_max: 174 | space_max = kpath[i]-kpath[i-1] 175 | k_max = (nkpts_band-1)*space_max 176 | kpath_new = np.linspace(0,k_max,nkpts_band) 177 | 178 | sp_points_new = np.zeros_like(sp_points) 179 | for i in range(len(sp_points)): 180 | for j in range(nkpts_band): 181 | if sp_points[i] == kpath[j]: 182 | sp_points_new[i] = kpath_new[j] 183 | 184 | xi = kpath_new 185 | yi = omega_new*27.211386 186 | fig = plt.subplots(figsize=(3.375,1.8)) 187 | ax = plt.subplot(111) 188 | interpolation_method = 'gaussian' 189 | extent = (xi[0],xi[-1],yi[0],yi[-1]) 190 | plt.imshow(dens, cmap='viridis', aspect=0.09, extent=extent, origin='lower', interpolation=interpolation_method) 191 | cbar = plt.colorbar() 192 | cbar.set_label('DOS [1/eV]', size=6) 193 | cbar.ax.tick_params(labelsize=6) 194 | 195 | emin = yi[0] 196 | emax = yi[-1] 197 | ax.axis(xmin=0, xmax=sp_points_new[-1], ymin=emin, ymax=emax) 198 | plt.xticks(sp_points_new, ['%s' % n for n in ['L', r'$\Gamma$', 'X', 'W', 'K', r'$\Gamma$']]) 199 | 200 | for p in sp_points_new: 201 | ax.plot([p, p], [emin, emax], '-', color='k', linewidth=0.6) 202 | ax.plot([0, sp_points_new[-1]], [0, 0], '--', color='k',linewidth=0.6) 203 | 204 | au2ev = 27.211386 205 | for n in range(nbands): 206 | ax.plot(kpath_new, [e[n]*au2ev for e in e_kn_2], '--',dashes=([2.2,1.4]),alpha=0.6,linewidth=0.6,color='white') 207 | 208 | ax.set_ylabel('Energy(eV)',fontsize=9) 209 | ax.yaxis.labelpad = -1 210 | 211 | major_yticks=np.arange(-25,30.1,5.0) 212 | #minor_yticks=np.arange(-25,20.1,1.0) 213 | ax.set_yticks(major_yticks) 214 | #ax.set_yticks(minor_yticks,minor=True) 215 | for axis in ['top','bottom','left','right']: 216 | ax.spines[axis].set_linewidth(0.5) 217 | ax.tick_params(axis='both',which='major',labelsize=8) 218 | ax.tick_params(axis='both',which='major',length=2.4,width=0.4,pad=2) 219 | ax.tick_params(axis='both',which='minor',length=1.5,width=0.4) 220 | 221 | plt.subplots_adjust(left=0.08,right=0.99,bottom=0.10,top=0.95) 222 | plt.savefig('diamond_444_GW_iao.png', dpi=600) 223 | -------------------------------------------------------------------------------- /examples/interpolation/diamond_hf_interpolate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pyscf.pbc import df, gto, dft, scf 3 | from pyscf.pbc.lib import chkfile 4 | import os 5 | import matplotlib.pyplot as plt 6 | from ase.lattice import bulk 7 | from ase.dft.kpoints import sc_special_points as special_points, get_bandpath 8 | from fcdmft.utils import interpolate 9 | 10 | ''' 11 | HF Wannier band interpolation: 12 | Step 1: Get LDA bands at larger k-mesh (6x6x6) from get_bands (non-SCF diagonalization) 13 | Step 2: Interpolate LDA bands at random kpts_band using 6x6x6 results -> fock_dft_band 14 | Step 3: Interpolate difference between HF and LDA (deltaFock) at kpts_band using 4x4x4 results -> 15 | dfock_band_lo, then get HF bands: fock_dft_band + dfock_band_lo 16 | 17 | Note: Currently a k-mesh must be used for constructing Wannier orbitals 18 | ''' 19 | 20 | cell = gto.Cell() 21 | cell.build(unit = 'angstrom', 22 | a = np.array([[0.000000, 1.783500, 1.783500], 23 | [1.783500, 0.000000, 1.783500], 24 | [1.783500, 1.783500, 0.000000]]), 25 | atom = 'C 1.337625 1.337625 1.337625; C 2.229375 2.229375 2.229375', 26 | dimension = 3, 27 | max_memory = 32000, 28 | verbose = 4, 29 | pseudo = 'gth-pade', 30 | basis='gth-dzv', 31 | precision=1e-10) 32 | 33 | kmesh = [4,4,4] 34 | kpts = cell.make_kpts(kmesh,scaled_center=[0,0,0]) 35 | gdf = df.GDF(cell, kpts) 36 | gdf_fname = 'gdf_ints_444.h5' 37 | gdf._cderi_to_save = gdf_fname 38 | if not os.path.isfile(gdf_fname): 39 | gdf.build() 40 | 41 | chkfname = 'diamond_444.chk' 42 | if os.path.isfile(chkfname): 43 | kmf = dft.KRKS(cell, kpts) 44 | kmf.with_df = gdf 45 | kmf.with_df._cderi = gdf_fname 46 | data = chkfile.load(chkfname, 'scf') 47 | kmf.__dict__.update(data) 48 | else: 49 | kmf = dft.KRKS(cell, kpts) 50 | kmf.with_df = gdf 51 | kmf.with_df._cderi = gdf_fname 52 | kmf.conv_tol = 1e-12 53 | kmf.chkfile = chkfname 54 | kmf.kernel() 55 | 56 | from libdmet_solid.basis_transform import make_basis 57 | from libdmet_solid.lo import pywannier90 58 | # valence Wannier orbitals 59 | num_wann = 8 60 | keywords = \ 61 | ''' 62 | num_iter = 1000 63 | begin projections 64 | C:sp3 65 | end projections 66 | exclude_bands : 9-%s 67 | num_cg_steps = 100 68 | precond = T 69 | '''%(kmf.cell.nao_nr()) 70 | 71 | w90 = pywannier90.W90(kmf, kmesh, num_wann, other_keywords = keywords) 72 | #w90.use_atomic = True 73 | #w90.use_bloch_phases = True 74 | #w90.use_scdm = True 75 | #w90.guiding_centres = False 76 | w90.kernel() 77 | 78 | C_ao_mo = np.asarray(w90.mo_coeff)[:, :, w90.band_included_list] 79 | C_mo_lo = make_basis.tile_u_matrix(np.array(w90.U_matrix.transpose(2, 0, 1), \ 80 | order='C'), u_virt=None, u_core=None) 81 | C_ao_lo = make_basis.multiply_basis(C_ao_mo, C_mo_lo) 82 | nbands = C_ao_lo.shape[-1] 83 | 84 | # set up band_kpts 85 | points = special_points['fcc'] 86 | G = points['G'] 87 | X = points['X'] 88 | W = points['W'] 89 | K = points['K'] 90 | L = points['L'] 91 | band_kpts, kpath, sp_points = get_bandpath([L, G, X, W, K, G], cell.a, npoints=50) 92 | band_kpts = cell.get_abs_kpts(band_kpts) 93 | 94 | # get DFT bands for a large k-mesh 95 | kmesh_L = [6,6,6] 96 | kpts_L = cell.make_kpts(kmesh_L,scaled_center=[0,0,0]) 97 | gdf2 = df.GDF(cell, kpts) 98 | gdf2_fname = 'gdf_ints_large.h5' 99 | gdf2._cderi_to_save = gdf2_fname 100 | gdf2.auxbasis = gdf.auxbasis 101 | gdf2.kpts_band = kpts_L 102 | if not os.path.isfile(gdf2_fname): 103 | gdf2._j_only = True 104 | gdf2.build(j_only=True, kpts_band=kpts_L) 105 | 106 | kmf2 = dft.KRKS(cell, kpts).density_fit() 107 | kmf2.xc = kmf.xc 108 | kmf2.exxdiv = kmf.exxdiv 109 | if hasattr(kmf, 'sigma'): 110 | kmf2 = scf.addons.smearing_(kmf2, sigma=kmf.sigma, method="fermi") 111 | kmf2.with_df = gdf2 112 | kmf2.with_df._cderi = gdf2_fname 113 | kmf2.mo_energy = kmf.mo_energy 114 | kmf2.mo_occ = kmf.mo_occ 115 | kmf2.mo_coeff = kmf.mo_coeff 116 | 117 | mo_energy_L, mo_coeff_L, hcore_L, veff_L = interpolate.get_bands(kmf2, kpts_L) 118 | 119 | # set up a new mean-field object at large k-mesh 120 | kmf3 = dft.KRKS(cell, kpts_L).density_fit() 121 | kmf3.xc = kmf.xc 122 | kmf3.exxdiv = kmf.exxdiv 123 | if hasattr(kmf, 'sigma'): 124 | kmf3 = scf.addons.smearing_(kmf3, sigma=kmf.sigma, method="fermi") 125 | kmf3.mo_energy = mo_energy_L 126 | kmf3.mo_coeff = mo_coeff_L 127 | kmf3.mo_occ = kmf3.get_occ(mo_energy_kpts=mo_energy_L, mo_coeff_kpts=mo_coeff_L) 128 | 129 | # Wannier basis for large k-mesh 130 | w90_L = pywannier90.W90(kmf3, kmesh_L, num_wann, other_keywords = keywords) 131 | w90_L.kernel() 132 | C_ao_mo = np.asarray(w90_L.mo_coeff)[:, :, w90_L.band_included_list] 133 | C_mo_lo = make_basis.tile_u_matrix(np.array(w90_L.U_matrix.transpose(2, 0, 1), \ 134 | order='C'), u_virt=None, u_core=None) 135 | C_ao_lo_L = make_basis.multiply_basis(C_ao_mo, C_mo_lo) 136 | 137 | # Make sure two sets of wannier orbitals have same order and phase 138 | # Search for Gamma point in kpts_L 139 | idx_G = None 140 | for i in range(len(kpts_L)): 141 | if np.linalg.norm(kpts_L[i]-kpts[0]) < 1e-8: 142 | idx_G = i 143 | break 144 | assert (idx_G is not None) 145 | ovlp = np.dot(C_ao_lo[0].T.conj(), kmf.get_ovlp()[0]).dot(C_ao_lo_L[idx_G]) 146 | C_ao_lo_L_ordered = np.zeros_like(C_ao_lo_L) 147 | for i in range(ovlp.shape[0]): 148 | idx = np.argmax(np.abs(ovlp[i])) 149 | C_ao_lo_L_ordered[:,:,i] = C_ao_lo_L[:,:,idx] 150 | if (ovlp[i,idx].real < 0): 151 | C_ao_lo_L_ordered[:,:,i] = -C_ao_lo_L_ordered[:,:,i] 152 | C_ao_lo_L = C_ao_lo_L_ordered 153 | 154 | # Get DFT Fock at band_kpts through interpolation 155 | mo_energy_band, mo_coeff_band, fock_dft_band = interpolate.interpolate_mf(kmf3, band_kpts, C_ao_lo=C_ao_lo_L, 156 | veff=veff_L, hcore=hcore_L, w90=w90_L) 157 | 158 | # Get HF bands at band_kpts through interpolating veff diff 159 | e_kn = interpolate.interpolate_hf_diff(kmf, band_kpts, fock_dft_band, C_ao_lo=C_ao_lo, w90=w90)[0] 160 | vbmax = -99 161 | for en in e_kn: 162 | vb_k = en[cell.nelectron//2-1] 163 | if vb_k > vbmax: 164 | vbmax = vb_k 165 | e_kn = [en - vbmax for en in e_kn] 166 | 167 | # Get HF bands at band_kpts through interpolating mo diff 168 | e_kn_2 = interpolate.interpolate_hf_diff(kmf, band_kpts, fock_dft_band, mo_interpolate=True, C_ao_lo=C_ao_lo, w90=w90)[0] 169 | vbmax = -99 170 | for en in e_kn_2: 171 | vb_k = en[cell.nelectron//2-1] 172 | if vb_k > vbmax: 173 | vbmax = vb_k 174 | e_kn_2 = [en - vbmax for en in e_kn_2] 175 | 176 | au2ev = 27.21139 177 | emin = -1.2*au2ev 178 | emax = 1.2*au2ev 179 | 180 | plt.figure(figsize=(5, 6)) 181 | for n in range(nbands): 182 | plt.plot(kpath, [e[n]*au2ev for e in e_kn_2], color='orange', alpha=0.7) 183 | plt.plot(kpath, [e[n]*au2ev for e in e_kn], color='#4169E1') 184 | for p in sp_points: 185 | plt.plot([p, p], [emin, emax], 'k-') 186 | plt.plot([0, sp_points[-1]], [0, 0], 'k-') 187 | plt.xticks(sp_points, ['$%s$' % n for n in ['L', r'\Gamma', 'X', 'W', 'K', r'\Gamma']]) 188 | plt.axis(xmin=0, xmax=sp_points[-1], ymin=emin, ymax=emax) 189 | plt.xlabel('k-vector') 190 | plt.savefig('diamond_444_hf.png',dpi=600) 191 | -------------------------------------------------------------------------------- /fcdmft/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/__init__.py -------------------------------------------------------------------------------- /fcdmft/dmft/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/dmft/__init__.py -------------------------------------------------------------------------------- /fcdmft/gw/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/gw/__init__.py -------------------------------------------------------------------------------- /fcdmft/gw/mol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/gw/mol/__init__.py -------------------------------------------------------------------------------- /fcdmft/gw/mol/gw_dc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2014-2021 The PySCF Developers. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Author: Tianyu Zhu 17 | # 18 | 19 | """ 20 | Spin-restricted G0W0 double counting self-energy term in GW+DMFT 21 | 22 | Method: 23 | T. Zhu and G.K.-L. Chan, Phys. Rev. X 11, 021006 (2021) 24 | T. Zhu and G.K.-L. Chan, J. Chem. Theory. Comput. 17, 727-741 (2021) 25 | Compute polarizability on imaginary time, then transform to imag frequency 26 | to compute self-energy, then analytically continued to real frequency 27 | 28 | Other useful references: 29 | J. Phys. Chem. Lett. 9, 306 (2018) 30 | Phys. Rev. B 94, 165109 (2016) 31 | """ 32 | 33 | import time, h5py 34 | from functools import reduce 35 | import numpy 36 | import numpy as np 37 | 38 | from pyscf import lib 39 | from pyscf.lib import logger 40 | from pyscf.ao2mo import _ao2mo 41 | from pyscf import gto, df, dft, scf 42 | from pyscf.mp.mp2 import get_nocc, get_nmo, get_frozen_mask 43 | from pyscf import __config__ 44 | from fcdmft.gw.mol.gw_ac import _get_scaled_legendre_roots, \ 45 | two_pole, pade_thiele, GWAC, AC_twopole_full, AC_pade_thiele_full 46 | 47 | einsum = lib.einsum 48 | 49 | def kernel(gw, gfomega, Lpq=None, kmf=None, C_mo_lo=None, orbs=None, 50 | nw=None, nt=None, verbose=logger.NOTE, small_mem=False): 51 | ''' 52 | Returns: 53 | sigma : GW double counting self-energy on real axis 54 | ''' 55 | mf = gw._scf 56 | assert(gw.frozen == 0) 57 | 58 | if orbs is None: 59 | orbs = range(gw.nmo) 60 | norbs = len(orbs) 61 | 62 | nmo = gw.nmo 63 | nocc = gw.nocc 64 | assert(gw.ef) 65 | ef = gw.ef 66 | 67 | # Imaginary frequency grids 68 | freqs, wts_w = _get_scaled_legendre_roots(nw) 69 | 70 | # Imaginary time grids 71 | time, wts_t = _get_scaled_legendre_roots(nt, x0=1.0) 72 | 73 | eta = gw.eta 74 | nomega = len(gfomega) 75 | sigma = np.zeros((nmo,nmo,nomega),dtype=np.complex128) 76 | 77 | # Compute full self-energy on imaginary axis i*[0,iw_cutoff] 78 | sigmaI, omega = get_sigma_full(gw, orbs, Lpq, kmf, C_mo_lo, 79 | freqs, wts_w, time, wts_t, iw_cutoff=5., small_mem=small_mem) 80 | fn = 'gw_dc_sigmaI.h5' 81 | feri = h5py.File(fn, 'w') 82 | feri['sigmaI'] = np.asarray(sigmaI) 83 | feri['omega'] = np.asarray(omega) 84 | feri.close() 85 | 86 | # Analytic continuation 87 | if gw.ac == 'twopole': 88 | coeff = AC_twopole_full(sigmaI, omega, orbs, nocc) 89 | elif gw.ac == 'pade': 90 | coeff, omega_fit = AC_pade_thiele_full(sigmaI, omega, npts=18, step_ratio=2.5/3.0) 91 | 92 | # Compute real-axis self-energy 93 | for p in orbs: 94 | for q in orbs: 95 | if gw.ac == 'twopole': 96 | sigma[p,q] = two_pole(gfomega-ef+1j*eta, coeff[:,p-orbs[0],q-orbs[0]]) 97 | elif gw.ac == 'pade': 98 | sigma[p,q] = pade_thiele(gfomega-ef+1j*eta, omega_fit, coeff[:,p-orbs[0],q-orbs[0]]) 99 | 100 | fn = 'imp_ac_coeff.h5' 101 | feri = h5py.File(fn, 'w') 102 | feri['coeff'] = np.asarray(coeff) 103 | feri['fermi'] = np.asarray(ef) 104 | feri['omega_fit'] = np.asarray(omega_fit) 105 | feri.close() 106 | 107 | return sigma 108 | 109 | def get_sigma_full(gw, orbs, Lpq, kmf, C_mo_lo, freqs, wts_w, time, wts_t, iw_cutoff=None, small_mem=False): 110 | ''' 111 | Compute GW correlation self-energy (all elements) in LO basis 112 | on imaginary axis 113 | ''' 114 | nmo = gw.nmo 115 | nw = len(freqs) 116 | naux = Lpq.shape[0] 117 | norbs = len(orbs) 118 | nocc = gw.nocc 119 | ef = gw.ef 120 | 121 | print('### computing polarization in imag time and freq domain ###') 122 | tchunk = 100 123 | n_tchunk = len(time) // tchunk 124 | tlist = []; wtslist = [] 125 | for i in range(n_tchunk): 126 | tlist.append(time[i*tchunk:(i+1)*tchunk]) 127 | wtslist.append(wts_t[i*tchunk:(i+1)*tchunk]) 128 | if len(time) % tchunk != 0: 129 | tlist.append(time[n_tchunk*tchunk:]) 130 | wtslist.append(wts_t[n_tchunk*tchunk:]) 131 | n_tchunk += 1 132 | 133 | # Integration on numerical grids 134 | if iw_cutoff is not None: 135 | nw_sigma = sum(iw < iw_cutoff for iw in freqs) + 1 136 | else: 137 | nw_sigma = nw + 1 138 | 139 | omega = np.zeros((nw_sigma),dtype=np.complex128) 140 | omega[0] = 1j*0. 141 | omega[1:] = 1j*freqs[:(nw_sigma-1)] 142 | 143 | # Compute time-domain density response kernel and transform to freq domain 144 | Pi = np.zeros((naux,naux,nw),dtype=np.complex128) 145 | for i in range(n_tchunk): 146 | Pi_t = get_response_t(kmf, nocc, C_mo_lo, tlist[i], Lpq, ef) 147 | for w in range(nw): 148 | Pi[:,:,w] += CT_t_to_w(Pi_t, tlist[i], wtslist[i], freqs[w]) 149 | 150 | print('### computing self-energy in imag freq domain ###') 151 | sigma = np.zeros((norbs,norbs,nw_sigma),dtype=np.complex128) 152 | if small_mem: 153 | for w in range(nw): 154 | Pi_inv = np.linalg.inv(np.eye(naux)-Pi[:,:,w])-np.eye(naux) 155 | Qnu = einsum('Pnu,PQ->Qnu',Lpq[:,orbs,:],Pi_inv) 156 | g0_pos = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega+1j*freqs[w]) 157 | g0_neg = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega-1j*freqs[w]) 158 | for w2 in range(nw_sigma): 159 | Qnv = -wts_w[w] * einsum('Qnu,uv->Qnv',Qnu,g0_pos[:,:,w2]+g0_neg[:,:,w2]) / 2. / np.pi 160 | sigma[:,:,w2] += einsum('Qnv,Qvl->nl',Qnv,Lpq[:,:,orbs]) 161 | else: 162 | Qnvw = np.zeros((naux,norbs,nmo,nw_sigma),dtype=np.complex128) 163 | for w in range(nw): 164 | Pi_inv = np.linalg.inv(np.eye(naux)-Pi[:,:,w])-np.eye(naux) 165 | Qnu = einsum('Pnu,PQ->Qnu',Lpq[:,orbs,:],Pi_inv) 166 | g0_pos = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega+1j*freqs[w]) 167 | g0_neg = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega-1j*freqs[w]) 168 | Qnvw += -wts_w[w] * einsum('Qnu,uvw->Qnvw',Qnu,g0_pos+g0_neg) / 2. / np.pi 169 | sigma = einsum('Qnvw,Qvl->nlw',Qnvw,Lpq[:,:,orbs]) 170 | 171 | return sigma, omega 172 | 173 | def get_response_t(kmf, nocc, C_mo_lo, time, Lpq, mu): 174 | """ 175 | Compute polarization in time domain 176 | """ 177 | mo_occ = [x/2. for x in kmf.mo_occ] 178 | nkpts = len(mo_occ) 179 | nmo = len(kmf.mo_energy[0]) 180 | # Approximate metal as gapped system 181 | # TODO: exact treatment of metal 182 | for k in range(nkpts): 183 | for i in range(nmo): 184 | if mo_occ[k][i] > 0.5: 185 | mo_occ[k][i] = 1.0 186 | else: 187 | mo_occ[k][i] = 0.0 188 | 189 | nt = len(time) 190 | naux = Lpq.shape[0] 191 | PQ = np.zeros((naux,naux,nt)) 192 | 193 | for t in range(nt): 194 | g0_pos = get_g0_t_from_kmf(kmf, mo_occ, C_mo_lo, mu, time[t]) 195 | g0_neg = get_g0_t_from_kmf(kmf, mo_occ, C_mo_lo, mu, -time[t]) 196 | Pkj = einsum('Pij,ki->Pkj',Lpq,g0_pos) 197 | Qkj = einsum('Qkl,lj->Qkj',Lpq,g0_neg) 198 | PQ[:,:,t] = 2. * einsum('Pkj,Qkj->PQ',Pkj,Qkj) 199 | return PQ 200 | 201 | def get_g0_t_from_kmf(kmf, mo_occ, C_mo_lo, mu, time): 202 | """ 203 | Get impurity imaginary-time mean-field Green's function in LO basis 204 | """ 205 | mo_energy = np.asarray(kmf.mo_energy) 206 | nlo = C_mo_lo.shape[-1] 207 | nkpts, nmo = mo_energy.shape 208 | g0 = np.zeros((nkpts,nmo,nmo)) 209 | for k in range(nkpts): 210 | if time < 0.: 211 | idx = np.where(mo_occ[k] > 0.99)[0] 212 | g0[k,idx,idx] = np.exp(-(mo_energy[k][idx]-mu)*time) * mo_occ[k][idx] 213 | else: 214 | idx = np.where(mo_occ[k] < 0.01)[0] 215 | g0[k,idx,idx] = -np.exp(-(mo_energy[k][idx]-mu)*time) * (1.-mo_occ[k][idx]) 216 | 217 | g0_lo = np.zeros((nkpts,nlo,nlo),dtype=np.complex128) 218 | for k in range(nkpts): 219 | g0_lo[k,:,:] = np.dot(np.dot(C_mo_lo[0,k].T.conj(), g0[k,:,:]), C_mo_lo[0,k]) 220 | 221 | g0_imp = g0_lo.sum(axis=0)/nkpts 222 | 223 | return g0_imp.real 224 | 225 | def get_g0_w_from_kmf(kmf, C_mo_lo, mu, freqs): 226 | """ 227 | Get impurity imaginary-freq mean-field Green's function in LO basis 228 | """ 229 | mo_energy = np.asarray(kmf.mo_energy) 230 | nlo = C_mo_lo.shape[-1] 231 | nkpts, nmo = mo_energy.shape 232 | nw = len(freqs) 233 | g0 = np.zeros((nkpts,nmo,nmo,nw),np.complex128) 234 | for iw in range(nw): 235 | for k in range(nkpts): 236 | g0[k,:,:,iw] = np.diag(1./(freqs[iw]+mu-mo_energy[k])) 237 | 238 | g0_lo = np.zeros((nkpts,nlo,nlo,nw),np.complex128) 239 | for iw in range(nw): 240 | for k in range(nkpts): 241 | g0_lo[k,:,:,iw] = reduce(numpy.dot, (C_mo_lo[0,k].T.conj(), g0[k,:,:,iw], C_mo_lo[0,k])) 242 | 243 | g0_imp = g0_lo.sum(axis=0)/nkpts 244 | 245 | return g0_imp 246 | 247 | def CT_t_to_w(Gt, time, wts, omega): 248 | """ 249 | Cosine transform of even function from time to frequency 250 | """ 251 | cos_tw = einsum('t,t->t',np.cos(time*omega),wts) 252 | Gw = 2. * einsum('PQt,t->PQ',Gt,cos_tw) 253 | return Gw 254 | 255 | 256 | class GWGF(GWAC): 257 | 258 | eta = getattr(__config__, 'gw_dc_GWGF_eta', 5e-3) 259 | # Analytic continuation: pade or twopole 260 | ac = getattr(__config__, 'gw_dc_GWGF_ac', 'pade') 261 | 262 | def __init__(self, mf, frozen=0): 263 | GWAC.__init__(self, mf, frozen=0) 264 | #TODO: implement frozen orbs 265 | if frozen > 0: 266 | raise NotImplementedError 267 | self.frozen = frozen 268 | self.ef = None 269 | 270 | keys = set(('eta','ac')) 271 | self._keys = set(self.__dict__.keys()).union(keys) 272 | 273 | def dump_flags(self): 274 | log = logger.Logger(self.stdout, self.verbose) 275 | log.info('') 276 | log.info('******** %s ********', self.__class__) 277 | log.info('method = %s', self.__class__.__name__) 278 | nocc = self.nocc 279 | nvir = self.nmo - nocc 280 | log.info('GW nocc = %d, nvir = %d', nocc, nvir) 281 | if self.frozen > 0: 282 | log.info('frozen orbitals = %d', self.frozen) 283 | logger.info(self, 'analytic continuation method = %s', self.ac) 284 | return self 285 | 286 | get_nocc = get_nocc 287 | get_nmo = get_nmo 288 | get_frozen_mask = get_frozen_mask 289 | 290 | def kernel(self, omega, Lpq=None, kmf=None, C_mo_lo=None, orbs=None, nw=100, nt=2000, small_mem=False): 291 | """ 292 | Args: 293 | omega : 1D array (nomega), real freq 294 | kmf : PBC mean-field class 295 | Lpq : 3D array (naux, nlo, nlo), 3-index ERI 296 | C_mo_lo: 4D array (spin, nkpts, nmo, nlo), transformation matrix MO -> LO 297 | orbs: list, orbital indices 298 | nw: interger, grid number 299 | 300 | Returns: 301 | self.sigma : 3D array (nlo, nlo, nomega), GW self-energy (double counting) 302 | """ 303 | cput0 = (time.process_time(), time.perf_counter()) 304 | self.dump_flags() 305 | 306 | naux, nao, nao = Lpq.shape 307 | mem_incore = (2*nao**2*naux*nw + naux**2*100) * 8 / 1e6 308 | mem_now = lib.current_memory()[0] 309 | if (mem_incore + mem_now > 0.95 * self.max_memory): 310 | mem_incore_small = (nao**2*naux + naux**2*100) * 16 / 1e6 311 | if (mem_incore_small + mem_now < 0.95 * self.max_memory): 312 | small_mem = True 313 | else: 314 | logger.warn(self, 'Memory may not be enough, even with the small memory option!') 315 | raise NotImplementedError 316 | 317 | self.sigma = kernel(self, omega, Lpq=Lpq, kmf=kmf, 318 | C_mo_lo=C_mo_lo, orbs=orbs, nw=nw, nt=nt, verbose=self.verbose, small_mem=small_mem) 319 | 320 | logger.timer(self, 'GWGF', *cput0) 321 | return self.sigma 322 | -------------------------------------------------------------------------------- /fcdmft/gw/mol/ugw_dc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2014-2021 The PySCF Developers. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Author: Tianyu Zhu 17 | # 18 | 19 | """ 20 | Spin-unrestricted G0W0 double counting self-energy term in GW+DMFT 21 | 22 | Method: 23 | T. Zhu and G.K.-L. Chan, Phys. Rev. X 11, 021006 (2021) 24 | T. Zhu and G.K.-L. Chan, J. Chem. Theory. Comput. 17, 727-741 (2021) 25 | Compute polarizability on imaginary time, then transform to imag frequency 26 | to compute self-energy, then analytically continued to real frequency 27 | 28 | Other useful references: 29 | J. Phys. Chem. Lett. 9, 306 (2018) 30 | Phys. Rev. B 94, 165109 (2016) 31 | """ 32 | 33 | import time, h5py 34 | from functools import reduce 35 | import numpy 36 | import numpy as np 37 | 38 | from pyscf import lib 39 | from pyscf.lib import logger 40 | from pyscf.ao2mo import _ao2mo 41 | from pyscf import gto, df, dft, scf 42 | from pyscf.mp.ump2 import get_nocc, get_nmo, get_frozen_mask 43 | from pyscf import __config__ 44 | from fcdmft.gw.mol.gw_ac import _get_scaled_legendre_roots, \ 45 | two_pole, pade_thiele, AC_twopole_full, AC_pade_thiele_full 46 | from fcdmft.gw.mol.ugw_ac import UGWAC 47 | from fcdmft.gw.mol.gw_dc import CT_t_to_w 48 | 49 | einsum = lib.einsum 50 | 51 | def kernel(gw, gfomega, Lpq=None, kmf=None, C_mo_lo=None, orbs=None, 52 | nw=None, nt=None, verbose=logger.NOTE, small_mem=False): 53 | ''' 54 | Returns: 55 | sigma : GW double counting self-energy on real axis 56 | ''' 57 | mf = gw._scf 58 | assert(gw.frozen == 0) 59 | 60 | nocca, noccb = gw.nocc 61 | nmoa, nmob = gw.nmo 62 | nvira = nmoa - nocca 63 | nvirb = nmob - noccb 64 | assert(gw.ef) 65 | ef = gw.ef 66 | 67 | if orbs is None: 68 | orbs = range(nmoa) 69 | 70 | # Imaginary frequency grids 71 | freqs, wts_w = _get_scaled_legendre_roots(nw) 72 | 73 | # Imaginary time grids 74 | time, wts_t = _get_scaled_legendre_roots(nt, x0=1.0) 75 | 76 | eta = gw.eta 77 | nomega = len(gfomega) 78 | sigma = np.zeros((2,nmoa,nmoa,nomega),dtype=np.complex128) 79 | 80 | # Compute full self-energy on imaginary axis i*[0,iw_cutoff] 81 | sigmaI,omega = get_sigma_full(gw, orbs, Lpq, kmf, C_mo_lo, 82 | freqs, wts_w, time, wts_t, iw_cutoff=5., small_mem=small_mem) 83 | fn = 'gw_dc_sigmaI.h5' 84 | feri = h5py.File(fn, 'w') 85 | feri['sigmaI'] = np.asarray(sigmaI) 86 | feri['omega'] = np.asarray(omega) 87 | feri.close() 88 | 89 | # Analytic continuation 90 | if gw.ac == 'twopole': 91 | coeff_a = AC_twopole_full(sigmaI[0], omega, orbs, nocca) 92 | coeff_b = AC_twopole_full(sigmaI[1], omega, orbs, noccb) 93 | elif gw.ac == 'pade': 94 | coeff_a, omega_fit_a = AC_pade_thiele_full(sigmaI[0], omega, npts=18, step_ratio=2.5/3.0) 95 | coeff_b, omega_fit_b = AC_pade_thiele_full(sigmaI[1], omega, npts=18, step_ratio=2.5/3.0) 96 | omega_fit = omega_fit_a 97 | coeff = np.asarray((coeff_a,coeff_b)) 98 | 99 | # Compute real-axis self-energy 100 | for s in range(2): 101 | for p in orbs: 102 | for q in orbs: 103 | if gw.ac == 'twopole': 104 | sigma[s,p,q] = two_pole(gfomega-ef+1j*eta, coeff[s,:,p-orbs[0],q-orbs[0]]) 105 | elif gw.ac == 'pade': 106 | sigma[s,p,q] = pade_thiele(gfomega-ef+1j*eta, omega_fit, coeff[s,:,p-orbs[0],q-orbs[0]]) 107 | 108 | fn = 'imp_ac_coeff.h5' 109 | feri = h5py.File(fn, 'w') 110 | feri['coeff'] = np.asarray(coeff) 111 | feri['fermi'] = np.asarray(ef) 112 | feri['omega_fit'] = np.asarray(omega_fit) 113 | feri.close() 114 | 115 | return sigma 116 | 117 | def get_sigma_full(gw, orbs, Lpq, kmf, C_mo_lo, freqs, wts_w, time, wts_t, iw_cutoff=None, small_mem=False): 118 | ''' 119 | Compute GW correlation self-energy (all elements) in AO basis 120 | on imaginary axis 121 | ''' 122 | nocca, noccb = gw.nocc 123 | nmoa, nmob = gw.nmo 124 | nw = len(freqs) 125 | naux = Lpq.shape[0] 126 | norbs = len(orbs) 127 | ef = gw.ef 128 | 129 | print('### computing polarization in imag time and freq domain ###') 130 | tchunk = 100 131 | n_tchunk = len(time) // tchunk 132 | tlist = []; wtslist = [] 133 | for i in range(n_tchunk): 134 | tlist.append(time[i*tchunk:(i+1)*tchunk]) 135 | wtslist.append(wts_t[i*tchunk:(i+1)*tchunk]) 136 | if len(time) % tchunk != 0: 137 | tlist.append(time[n_tchunk*tchunk:]) 138 | wtslist.append(wts_t[n_tchunk*tchunk:]) 139 | n_tchunk += 1 140 | 141 | # Integration on numerical grids 142 | if iw_cutoff is not None: 143 | nw_sigma = sum(iw < iw_cutoff for iw in freqs) + 1 144 | else: 145 | nw_sigma = nw + 1 146 | 147 | omega = np.zeros((nw_sigma),dtype=np.complex128) 148 | omega[0] = 1j*0. 149 | omega[1:] = 1j*freqs[:(nw_sigma-1)] 150 | 151 | # Compute time-domain density response kernel and transform to freq domain 152 | Pi = np.zeros((naux,naux,nw),dtype=np.complex128) 153 | for i in range(n_tchunk): 154 | Pi_t = get_response_t(kmf, gw.nocc, C_mo_lo, tlist[i], Lpq, ef) 155 | for w in range(nw): 156 | Pi[:,:,w] += CT_t_to_w(Pi_t, tlist[i], wtslist[i], freqs[w]) 157 | 158 | print('### computing self-energy in imag freq domain ###') 159 | sigma = np.zeros((2,norbs,norbs,nw_sigma),dtype=np.complex128) 160 | if small_mem: 161 | for w in range(nw): 162 | Pi_inv = np.linalg.inv(np.eye(naux)-Pi[:,:,w])-np.eye(naux) 163 | Qnu = einsum('Pnu,PQ->Qnu',Lpq[:,orbs,:],Pi_inv) 164 | g0_pos = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega+1j*freqs[w]) 165 | g0_neg = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega-1j*freqs[w]) 166 | for w2 in range(nw_sigma): 167 | Qnv = -wts_w[w] * einsum('Qnu,suv->sQnv',Qnu,g0_pos[:,:,:,w2]+g0_neg[:,:,:,w2])/2./np.pi 168 | sigma[:,:,:,w2] += einsum('sQnv,Qvl->snl',Qnv,Lpq[:,:,orbs]) 169 | else: 170 | Qnvw = np.zeros((2,naux,norbs,nmoa,nw_sigma),dtype=np.complex128) 171 | for w in range(nw): 172 | Pi_inv = np.linalg.inv(np.eye(naux)-Pi[:,:,w])-np.eye(naux) 173 | Qnu = einsum('Pnu,PQ->Qnu',Lpq[:,orbs,:],Pi_inv) 174 | g0_pos = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega+1j*freqs[w]) 175 | g0_neg = get_g0_w_from_kmf(kmf, C_mo_lo, ef, omega-1j*freqs[w]) 176 | Qnvw[0] += -wts_w[w] * einsum('Qnu,uvw->Qnvw',Qnu,g0_pos[0]+g0_neg[0])/2./np.pi 177 | Qnvw[1] += -wts_w[w] * einsum('Qnu,uvw->Qnvw',Qnu,g0_pos[1]+g0_neg[1])/2./np.pi 178 | sigma[0] = einsum('Qnvw,Qvl->nlw',Qnvw[0],Lpq[:,:,orbs]) 179 | sigma[1] = einsum('Qnvw,Qvl->nlw',Qnvw[1],Lpq[:,:,orbs]) 180 | 181 | return sigma, omega 182 | 183 | def get_response_t(kmf, nocc, C_mo_lo, time, Lpq, mu): 184 | """ 185 | Compute polarization in time domain 186 | """ 187 | nt = len(time) 188 | naux = Lpq.shape[0] 189 | PQ = np.zeros((naux,naux,nt)) 190 | for t in range(nt): 191 | g0_pos = get_g0_t_from_kmf(kmf, nocc, C_mo_lo, mu, time[t]) 192 | g0_neg = get_g0_t_from_kmf(kmf, nocc, C_mo_lo, mu, -time[t]) 193 | Pkj_a = einsum('Pij,ki->Pkj',Lpq,g0_pos[0]).reshape(naux,-1) 194 | Pkj_b = einsum('Pij,ki->Pkj',Lpq,g0_pos[1]).reshape(naux,-1) 195 | Qkj_a = einsum('Qkl,lj->Qkj',Lpq,g0_neg[0]).reshape(naux,-1) 196 | Qkj_b = einsum('Qkl,lj->Qkj',Lpq,g0_neg[1]).reshape(naux,-1) 197 | PQ[:,:,t] = np.dot(Pkj_a,Qkj_a.transpose()) + np.dot(Pkj_b,Qkj_b.transpose()) 198 | return PQ 199 | 200 | def get_g0_t_from_kmf(kmf, nocc, C_mo_lo, mu, time): 201 | """ 202 | Get impurity imaginary-time mean-field Green's function in IAO basis 203 | """ 204 | nocca, noccb = nocc 205 | mo_energy = np.asarray(kmf.mo_energy) 206 | nlo = C_mo_lo.shape[-1] 207 | spin, nkpts, nmo = mo_energy.shape 208 | g0 = np.zeros((spin,nkpts,nmo,nmo)) 209 | for s in range(spin): 210 | noccs = nocc[s] 211 | if time < 0.: 212 | for k in range(nkpts): 213 | g0[s,k,:noccs,:noccs] = np.diag(np.exp(-(mo_energy[s][k,:noccs]-mu)*time)) 214 | else: 215 | for k in range(nkpts): 216 | g0[s,k,noccs:,noccs:] = -np.diag(np.exp(-(mo_energy[s][k,noccs:]-mu)*time)) 217 | 218 | g0_lo = np.zeros((spin,nkpts,nlo,nlo),dtype=np.complex128) 219 | for s in range(spin): 220 | for k in range(nkpts): 221 | g0_lo[s,k,:,:] = reduce(numpy.dot, (C_mo_lo[s,k].T.conj(), g0[s,k,:,:], C_mo_lo[s,k])) 222 | 223 | g0_imp = g0_lo.sum(axis=1)/nkpts 224 | 225 | return g0_imp.real 226 | 227 | def get_g0_w_from_kmf(kmf, C_mo_lo, mu, freqs): 228 | """ 229 | Get impurity imaginary-freq mean-field Green's function in IAO basis 230 | """ 231 | mo_energy = np.asarray(kmf.mo_energy) 232 | spin, nkpts, nmo = mo_energy.shape 233 | nlo = C_mo_lo.shape[-1] 234 | nw = len(freqs) 235 | g0 = np.zeros((spin,nkpts,nmo,nmo,nw),np.complex128) 236 | for s in range(spin): 237 | for iw in range(nw): 238 | for k in range(nkpts): 239 | g0[s,k,:,:,iw] = np.diag(1./(freqs[iw]+mu-mo_energy[s,k])) 240 | 241 | g0_lo = np.zeros((spin,nkpts,nlo,nlo,nw),np.complex128) 242 | for s in range(spin): 243 | for iw in range(nw): 244 | for k in range(nkpts): 245 | g0_lo[s,k,:,:,iw] = reduce(numpy.dot, (C_mo_lo[s,k].T.conj(), g0[s,k,:,:,iw], C_mo_lo[s,k])) 246 | 247 | g0_imp = g0_lo.sum(axis=1)/nkpts 248 | 249 | return g0_imp 250 | 251 | 252 | class UGWGF(UGWAC): 253 | 254 | eta = getattr(__config__, 'ugw_dc_UGWGF_eta', 5e-3) 255 | # Analytic continuation: pade or twopole 256 | ac = getattr(__config__, 'ugw_dc_UGWGF_ac', 'pade') 257 | 258 | def __init__(self, mf, frozen=0): 259 | UGWAC.__init__(self, mf, frozen=0) 260 | #TODO: implement frozen orbs 261 | if frozen > 0: 262 | raise NotImplementedError 263 | self.frozen = frozen 264 | self.ef = None 265 | 266 | keys = set(('eta','ac')) 267 | self._keys = set(self.__dict__.keys()).union(keys) 268 | 269 | def dump_flags(self): 270 | log = logger.Logger(self.stdout, self.verbose) 271 | log.info('') 272 | log.info('******** %s ********', self.__class__) 273 | log.info('method = %s', self.__class__.__name__) 274 | nocca, noccb = self.nocc 275 | nmoa, nmob = self.nmo 276 | nvira = nmoa - nocca 277 | nvirb = nmob - noccb 278 | log.info('GW (nocca, noccb) = (%d, %d), (nvira, nvirb) = (%d, %d)', 279 | nocca, noccb, nvira, nvirb) 280 | if self.frozen > 0: 281 | log.info('frozen orbitals = %s', str(self.frozen)) 282 | logger.info(self, 'analytic continuation method = %s', self.ac) 283 | return self 284 | 285 | get_nocc = get_nocc 286 | get_nmo = get_nmo 287 | get_frozen_mask = get_frozen_mask 288 | 289 | def kernel(self, omega, Lpq=None, kmf=None, C_mo_lo=None, orbs=None, nw=100, nt=2000, small_mem=False): 290 | """ 291 | Args: 292 | omega : 1D array (nomega), real freq 293 | kmf : PBC mean-field class 294 | Lpq : 4D array (2, naux, nlo, nlo), 3-index ERI 295 | C_mo_lo: 4D array (spin, nkpts, nmo, nlo), transformation matrix MO -> LO 296 | orbs: list, orbital indices 297 | nw: interger, grid number 298 | 299 | Returns: 300 | self.sigma : 4D array (2, nlo, nlo, nomega), GW self-energy (double counting) 301 | """ 302 | cput0 = (time.process_time(), time.perf_counter()) 303 | self.dump_flags() 304 | 305 | naux, nao, nao = Lpq.shape 306 | mem_incore = (3*nao**2*naux*nw + naux**2*100) * 16 / 1e6 307 | mem_now = lib.current_memory()[0] 308 | if (mem_incore + mem_now > 0.95 * self.max_memory): 309 | mem_incore_small = (2*nao**2*naux + naux**2*100) * 16 / 1e6 310 | if (mem_incore_small + mem_now < 0.95 * self.max_memory): 311 | small_mem = True 312 | else: 313 | logger.warn(self, 'Memory may not be enough, even with the small memory option!') 314 | raise NotImplementedError 315 | 316 | self.sigma = kernel(self, omega, Lpq=Lpq, kmf=kmf, 317 | C_mo_lo=C_mo_lo, orbs=orbs, nw=nw, nt=nt, verbose=self.verbose, small_mem=small_mem) 318 | 319 | logger.timer(self, 'UGWGF', *cput0) 320 | return self.sigma 321 | -------------------------------------------------------------------------------- /fcdmft/gw/pbc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/gw/pbc/__init__.py -------------------------------------------------------------------------------- /fcdmft/rpa/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/rpa/__init__.py -------------------------------------------------------------------------------- /fcdmft/rpa/mol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/rpa/mol/__init__.py -------------------------------------------------------------------------------- /fcdmft/rpa/mol/rpa.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2014-2021 The PySCF Developers. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Author: Tianyu Zhu 17 | # 18 | 19 | """ 20 | Spin-restricted random phase approximation (direct RPA/dRPA in chemistry) 21 | with N^4 scaling 22 | 23 | Method: 24 | Main routines are based on GW-AC method descirbed in: 25 | T. Zhu and G.K.-L. Chan, J. Chem. Theory. Comput. 17, 727-741 (2021) 26 | X. Ren et al., New J. Phys. 14, 053020 (2012) 27 | """ 28 | 29 | import time, h5py 30 | from functools import reduce 31 | import numpy as np 32 | 33 | from pyscf import lib 34 | from pyscf.lib import logger 35 | from pyscf.ao2mo import _ao2mo 36 | from pyscf import df, dft, scf 37 | from pyscf.mp.mp2 import get_nocc, get_nmo, get_frozen_mask 38 | from pyscf import __config__ 39 | 40 | einsum = lib.einsum 41 | 42 | # **************************************************************************** 43 | # core routines, kernel, rpa_ecorr, rho_response 44 | # **************************************************************************** 45 | 46 | def kernel(rpa, mo_energy, mo_coeff, Lpq=None, nw=None, verbose=logger.NOTE): 47 | """ 48 | RPA correlation and total energy 49 | 50 | Args: 51 | Lpq : density fitting 3-center integral in MO basis. 52 | nw : number of frequency point on imaginary axis. 53 | vhf_df : using density fitting integral to compute HF exchange. 54 | 55 | Returns: 56 | e_tot : RPA total energy 57 | e_hf : EXX energy 58 | e_corr : RPA correlation energy 59 | """ 60 | mf = rpa._scf 61 | # only support frozen core 62 | if rpa.frozen is not None: 63 | assert isinstance(rpa.frozen, int) 64 | assert rpa.frozen < rpa.nocc 65 | 66 | if Lpq is None: 67 | Lpq = rpa.ao2mo(mo_coeff) 68 | 69 | # Grids for integration on imaginary axis 70 | freqs, wts = _get_scaled_legendre_roots(nw) 71 | 72 | # Compute HF exchange energy (EXX) 73 | dm = mf.make_rdm1() 74 | rhf = scf.RHF(rpa.mol) 75 | e_hf = rhf.energy_elec(dm=dm)[0] 76 | e_hf += mf.energy_nuc() 77 | 78 | # Compute RPA correlation energy 79 | e_corr = get_rpa_ecorr(rpa, Lpq, freqs, wts) 80 | 81 | # Compute totol energy 82 | e_tot = e_hf + e_corr 83 | 84 | logger.debug(rpa, ' RPA total energy = %s', e_tot) 85 | logger.debug(rpa, ' EXX energy = %s, RPA corr energy = %s', e_hf, e_corr) 86 | 87 | return e_tot, e_hf, e_corr 88 | 89 | def get_rpa_ecorr(rpa, Lpq, freqs, wts): 90 | """ 91 | Compute RPA correlation energy 92 | """ 93 | mo_energy = _mo_energy_without_core(rpa, rpa._scf.mo_energy) 94 | nocc = rpa.nocc 95 | nw = len(freqs) 96 | naux = Lpq.shape[0] 97 | 98 | if (mo_energy[nocc] - mo_energy[nocc-1]) < 1e-3: 99 | logger.warn(rpa, 'Current RPA code not well-defined for degeneracy!') 100 | 101 | e_corr = 0. 102 | for w in range(nw): 103 | Pi = get_rho_response(freqs[w], mo_energy, Lpq[:, :nocc, nocc:]) 104 | ec_w = np.log(np.linalg.det(np.eye(naux) - Pi)) 105 | ec_w += np.trace(Pi) 106 | e_corr += 1./(2.*np.pi) * ec_w * wts[w] 107 | 108 | return e_corr 109 | 110 | def get_rho_response(omega, mo_energy, Lpq): 111 | """ 112 | Compute density response function in auxiliary basis at freq iw. 113 | """ 114 | naux, nocc, nvir = Lpq.shape 115 | eia = mo_energy[:nocc, None] - mo_energy[None, nocc:] 116 | eia = eia / (omega**2 + eia * eia) 117 | # Response from both spin-up and spin-down density 118 | Pia = Lpq * (eia * 4.0) 119 | Pi = einsum('Pia, Qia -> PQ', Pia, Lpq) 120 | return Pi 121 | 122 | # **************************************************************************** 123 | # frequency integral quadrature, legendre, clenshaw_curtis 124 | # **************************************************************************** 125 | 126 | def _get_scaled_legendre_roots(nw, x0=0.5): 127 | """ 128 | Scale nw Legendre roots, which lie in the 129 | interval [-1, 1], so that they lie in [0, inf) 130 | Ref: www.cond-mat.de/events/correl19/manuscripts/ren.pdf 131 | 132 | Returns: 133 | freqs : 1D array 134 | wts : 1D array 135 | """ 136 | freqs, wts = np.polynomial.legendre.leggauss(nw) 137 | freqs_new = x0 * (1.0 + freqs) / (1.0 - freqs) 138 | wts = wts * 2.0 * x0 / (1.0 - freqs)**2 139 | return freqs_new, wts 140 | 141 | def _get_clenshaw_curtis_roots(nw): 142 | """ 143 | Clenshaw-Curtis qaudrature on [0,inf) 144 | Ref: J. Chem. Phys. 132, 234114 (2010) 145 | Returns: 146 | freqs : 1D array 147 | wts : 1D array 148 | """ 149 | freqs = np.zeros(nw) 150 | wts = np.zeros(nw) 151 | a = 0.2 152 | for w in range(nw): 153 | t = (w + 1.0) / nw * np.pi * 0.5 154 | freqs[w] = a / np.tan(t) 155 | if w != nw - 1: 156 | wts[w] = a*np.pi * 0.5 / nw / (np.sin(t)**2) 157 | else: 158 | wts[w] = a*np.pi * 0.25 / nw / (np.sin(t)**2) 159 | return freqs[::-1], wts[::-1] 160 | 161 | def _mo_energy_without_core(rpa, mo_energy): 162 | return mo_energy[get_frozen_mask(rpa)] 163 | 164 | def _mo_without_core(rpa, mo): 165 | return mo[:,get_frozen_mask(rpa)] 166 | 167 | def as_scanner(rpa): 168 | '''Generating a scanner/solver for RPA PES.''' 169 | if isinstance(rpa, lib.SinglePointScanner): 170 | return rpa 171 | 172 | logger.info(rpa, 'Set %s as a scanner', rpa.__class__) 173 | 174 | class RPA_Scanner(rpa.__class__, lib.SinglePointScanner): 175 | def __init__(self, rpa): 176 | self.__dict__.update(rpa.__dict__) 177 | self._scf = rpa._scf.as_scanner() 178 | def __call__(self, mol_or_geom, **kwargs): 179 | if isinstance(mol_or_geom, gto.Mole): 180 | mol = mol_or_geom 181 | else: 182 | mol = self.mol.set_geom_(mol_or_geom, inplace=False) 183 | 184 | self.reset(mol) 185 | 186 | mf_scanner = self._scf 187 | mf_scanner(mol) 188 | self.mo_coeff = mf_scanner.mo_coeff 189 | self.mo_occ = mf_scanner.mo_occ 190 | self.kernel(**kwargs) 191 | return self.e_tot 192 | return RPA_Scanner(rpa) 193 | 194 | 195 | class RPA(lib.StreamObject): 196 | 197 | def __init__(self, mf, frozen=None, auxbasis=None): 198 | self.mol = mf.mol 199 | self._scf = mf 200 | self.verbose = self.mol.verbose 201 | self.stdout = self.mol.stdout 202 | self.max_memory = mf.max_memory 203 | self.frozen = frozen 204 | 205 | # DF-RPA must use density fitting integrals 206 | if getattr(mf, 'with_df', None): 207 | self.with_df = mf.with_df 208 | else: 209 | self.with_df = df.DF(mf.mol) 210 | if auxbasis: 211 | self.with_df.auxbasis = auxbasis 212 | else: 213 | try: 214 | self.with_df.auxbasis = df.make_auxbasis(mf.mol, mp2fit=True) 215 | except: 216 | self.with_df.auxbasis = df.make_auxbasis(mf.mol, mp2fit=False) 217 | self._keys.update(['with_df']) 218 | 219 | ################################################## 220 | # don't modify the following attributes, they are not input options 221 | self._nocc = None 222 | self._nmo = None 223 | self.mo_energy = mf.mo_energy 224 | self.mo_coeff = mf.mo_coeff 225 | self.mo_occ = mf.mo_occ 226 | self.e_corr = None 227 | self.e_hf = None 228 | self.e_tot = None 229 | 230 | def dump_flags(self, verbose=None): 231 | log = logger.Logger(self.stdout, self.verbose) 232 | log.info('') 233 | log.info('******** %s ********', self.__class__) 234 | log.info('method = %s', self.__class__.__name__) 235 | nocc = self.nocc 236 | nvir = self.nmo - nocc 237 | log.info('RPA nocc = %d, nvir = %d', nocc, nvir) 238 | if self.frozen is not None: 239 | log.info('frozen orbitals = %d', self.frozen) 240 | return self 241 | 242 | @property 243 | def nocc(self): 244 | return self.get_nocc() 245 | @nocc.setter 246 | def nocc(self, n): 247 | self._nocc = n 248 | 249 | @property 250 | def nmo(self): 251 | return self.get_nmo() 252 | @nmo.setter 253 | def nmo(self, n): 254 | self._nmo = n 255 | 256 | get_nocc = get_nocc 257 | get_nmo = get_nmo 258 | get_frozen_mask = get_frozen_mask 259 | 260 | as_scanner = as_scanner 261 | 262 | def kernel(self, mo_energy=None, mo_coeff=None, Lpq=None, nw=40): 263 | """ 264 | Args: 265 | mo_energy : 1D array (nmo), mean-field mo energy 266 | mo_coeff : 2D array (nmo, nmo), mean-field mo coefficient 267 | Lpq : 3D array (naux, nmo, nmo), 3-index ERI 268 | nw: interger, grid number 269 | 270 | Returns: 271 | self.e_tot : RPA total eenrgy 272 | self.e_hf : EXX energy 273 | self.e_corr : RPA correlation energy 274 | """ 275 | if mo_coeff is None: 276 | mo_coeff = _mo_without_core(self, self._scf.mo_coeff) 277 | if mo_energy is None: 278 | mo_energy = _mo_energy_without_core(self, self._scf.mo_energy) 279 | 280 | cput0 = (time.process_time(), time.perf_counter()) 281 | self.dump_flags() 282 | self.e_tot, self.e_hf, self.e_corr = \ 283 | kernel(self, mo_energy, mo_coeff, Lpq=Lpq, nw=nw, verbose=self.verbose) 284 | 285 | logger.timer(self, 'RPA', *cput0) 286 | return self.e_corr 287 | 288 | def ao2mo(self, mo_coeff=None): 289 | if mo_coeff is None: 290 | mo_coeff = self.mo_coeff 291 | nmo = self.nmo 292 | nao = self.mo_coeff.shape[0] 293 | naux = self.with_df.get_naoaux() 294 | mem_incore = (2 * nmo**2*naux) * 8 / 1e6 295 | mem_now = lib.current_memory()[0] 296 | 297 | mo = np.asarray(mo_coeff, order='F') 298 | ijslice = (0, nmo, 0, nmo) 299 | Lpq = None 300 | if (mem_incore + mem_now < 0.99 * self.max_memory) or self.mol.incore_anyway: 301 | Lpq = _ao2mo.nr_e2(self.with_df._cderi, mo, ijslice, aosym='s2', out=Lpq) 302 | return Lpq.reshape(naux, nmo, nmo) 303 | else: 304 | logger.warn(self, 'Memory may not be enough!') 305 | raise NotImplementedError 306 | 307 | if __name__ == '__main__': 308 | from pyscf import gto, dft, scf 309 | mol = gto.Mole() 310 | mol.verbose = 4 311 | mol.atom = [ 312 | [8 , (0. , 0. , 0.)], 313 | [1 , (0. , -0.7571 , 0.5861)], 314 | [1 , (0. , 0.7571 , 0.5861)]] 315 | mol.basis = 'def2-svp' 316 | mol.build() 317 | 318 | mf = dft.RKS(mol) 319 | mf.xc = 'pbe' 320 | mf.kernel() 321 | 322 | rpa = RPA(mf) 323 | rpa.kernel() 324 | print ('RPA e_tot, e_hf, e_corr = ', rpa.e_tot, rpa.e_hf, rpa.e_corr) 325 | assert(abs(rpa.e_corr- -0.30783004035780076) < 1e-6) 326 | assert(abs(rpa.e_tot- -76.26428191794182) < 1e-6) 327 | -------------------------------------------------------------------------------- /fcdmft/rpa/mol/urpa.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2014-2021 The PySCF Developers. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Author: Tianyu Zhu 17 | # 18 | 19 | """ 20 | Spin-unrestricted random phase approximation (direct RPA/dRPA in chemistry) 21 | with N^4 scaling 22 | 23 | Method: 24 | Main routines are based on GW-AC method descirbed in: 25 | T. Zhu and G.K.-L. Chan, J. Chem. Theory. Comput. 17, 727-741 (2021) 26 | X. Ren et al., New J. Phys. 14, 053020 (2012) 27 | """ 28 | 29 | import time, h5py 30 | from functools import reduce 31 | import numpy as np 32 | 33 | from pyscf import lib 34 | from pyscf.lib import logger 35 | from pyscf.ao2mo import _ao2mo 36 | from pyscf import df, dft, scf 37 | from pyscf.mp.ump2 import get_nocc, get_nmo, get_frozen_mask 38 | from pyscf import __config__ 39 | from fcdmft.rpa.mol.rpa import RPA, _get_scaled_legendre_roots 40 | 41 | einsum = lib.einsum 42 | 43 | # **************************************************************************** 44 | # core routines, kernel, rpa_ecorr, rho_response 45 | # **************************************************************************** 46 | 47 | def kernel(rpa, mo_energy, mo_coeff, Lpq=None, nw=None, verbose=logger.NOTE): 48 | """ 49 | RPA correlation and total energy 50 | 51 | Args: 52 | Lpq : density fitting 3-center integral in MO basis. 53 | nw : number of frequency point on imaginary axis. 54 | vhf_df : using density fitting integral to compute HF exchange. 55 | 56 | Returns: 57 | e_tot : RPA total energy 58 | e_hf : EXX energy 59 | e_corr : RPA correlation energy 60 | """ 61 | mf = rpa._scf 62 | # only support frozen core 63 | if rpa.frozen is not None: 64 | assert isinstance(rpa.frozen, int) 65 | assert (rpa.frozen < rpa.nocc[0] and rpa.frozen < rpa.nocc[1]) 66 | 67 | if Lpq is None: 68 | Lpq = rpa.ao2mo(mo_coeff) 69 | 70 | # Grids for integration on imaginary axis 71 | freqs, wts = _get_scaled_legendre_roots(nw) 72 | 73 | # Compute HF exchange energy (EXX) 74 | dm = mf.make_rdm1() 75 | uhf = scf.UHF(rpa.mol) 76 | e_hf = uhf.energy_elec(dm=dm)[0] 77 | e_hf += mf.energy_nuc() 78 | 79 | # Compute RPA correlation energy 80 | e_corr = get_rpa_ecorr(rpa, Lpq, freqs, wts) 81 | 82 | # Compute totol energy 83 | e_tot = e_hf + e_corr 84 | 85 | logger.debug(rpa, ' RPA total energy = %s', e_tot) 86 | logger.debug(rpa, ' EXX energy = %s, RPA corr energy = %s', e_hf, e_corr) 87 | 88 | return e_tot, e_hf, e_corr 89 | 90 | def get_rpa_ecorr(rpa, Lpq, freqs, wts): 91 | """ 92 | Compute RPA correlation energy 93 | """ 94 | mo_energy = _mo_energy_without_core(rpa, rpa._scf.mo_energy) 95 | nocca, noccb = rpa.nocc 96 | nw = len(freqs) 97 | naux = Lpq[0].shape[0] 98 | 99 | homo = max(mo_energy[0][nocca-1], mo_energy[1][noccb-1]) 100 | lumo = min(mo_energy[0][nocca], mo_energy[1][noccb]) 101 | if (lumo-homo) < 1e-3: 102 | logger.warn(rpa, 'Current RPA code not well-defined for degeneracy!') 103 | 104 | e_corr = 0. 105 | for w in range(nw): 106 | Pi = get_rho_response(freqs[w], mo_energy, Lpq[0,:,:nocca,nocca:], Lpq[1,:,:noccb,noccb:]) 107 | ec_w = np.log(np.linalg.det(np.eye(naux) - Pi)) 108 | ec_w += np.trace(Pi) 109 | e_corr += 1./(2.*np.pi) * ec_w * wts[w] 110 | 111 | return e_corr 112 | 113 | def get_rho_response(omega, mo_energy, Lpqa, Lpqb): 114 | ''' 115 | Compute density response function in auxiliary basis at freq iw 116 | ''' 117 | naux, nocca, nvira = Lpqa.shape 118 | naux, noccb, nvirb = Lpqb.shape 119 | eia_a = mo_energy[0,:nocca,None] - mo_energy[0,None,nocca:] 120 | eia_b = mo_energy[1,:noccb,None] - mo_energy[1,None,noccb:] 121 | eia_a = eia_a / (omega**2 + eia_a*eia_a) 122 | eia_b = eia_b / (omega**2 + eia_b*eia_b) 123 | Pia_a = Lpqa * (eia_a * 2.0) 124 | Pia_b = Lpqb * (eia_b * 2.0) 125 | # Response from both spin-up and spin-down density 126 | Pi = einsum('Pia, Qia -> PQ', Pia_a, Lpqa) + einsum('Pia, Qia -> PQ', Pia_b, Lpqb) 127 | return Pi 128 | 129 | def _mo_energy_without_core(rpa, mo_energy): 130 | moidx = get_frozen_mask(rpa) 131 | mo_energy = (mo_energy[0][moidx[0]], mo_energy[1][moidx[1]]) 132 | return np.asarray(mo_energy) 133 | 134 | def _mo_without_core(rpa, mo): 135 | moidx = get_frozen_mask(rpa) 136 | mo = (mo[0][:,moidx[0]], mo[1][:,moidx[1]]) 137 | return np.asarray(mo) 138 | 139 | def as_scanner(rpa): 140 | '''Generating a scanner/solver for RPA PES.''' 141 | if isinstance(rpa, lib.SinglePointScanner): 142 | return rpa 143 | 144 | logger.info(rpa, 'Set %s as a scanner', rpa.__class__) 145 | 146 | class RPA_Scanner(rpa.__class__, lib.SinglePointScanner): 147 | def __init__(self, rpa): 148 | self.__dict__.update(rpa.__dict__) 149 | self._scf = rpa._scf.as_scanner() 150 | def __call__(self, mol_or_geom, **kwargs): 151 | if isinstance(mol_or_geom, gto.Mole): 152 | mol = mol_or_geom 153 | else: 154 | mol = self.mol.set_geom_(mol_or_geom, inplace=False) 155 | 156 | self.reset(mol) 157 | 158 | mf_scanner = self._scf 159 | mf_scanner(mol) 160 | self.mo_coeff = mf_scanner.mo_coeff 161 | self.mo_occ = mf_scanner.mo_occ 162 | self.kernel(**kwargs) 163 | return self.e_tot 164 | return RPA_Scanner(rpa) 165 | 166 | 167 | class URPA(RPA): 168 | 169 | def dump_flags(self, verbose=None): 170 | log = logger.Logger(self.stdout, self.verbose) 171 | log.info('') 172 | log.info('******** %s ********', self.__class__) 173 | log.info('method = %s', self.__class__.__name__) 174 | nocca, noccb = self.nocc 175 | nmoa, nmob = self.nmo 176 | nvira = nmoa - nocca 177 | nvirb = nmob - noccb 178 | log.info('RPA (nocca, noccb) = (%d, %d), (nvira, nvirb) = (%d, %d)', 179 | nocca, noccb, nvira, nvirb) 180 | if self.frozen is not None: 181 | log.info('frozen orbitals = %s', str(self.frozen)) 182 | return self 183 | 184 | get_nocc = get_nocc 185 | get_nmo = get_nmo 186 | get_frozen_mask = get_frozen_mask 187 | 188 | as_scanner = as_scanner 189 | 190 | def kernel(self, mo_energy=None, mo_coeff=None, Lpq=None, nw=40): 191 | """ 192 | Args: 193 | mo_energy : 2D array (2, nmo), mean-field mo energy 194 | mo_coeff : 3D array (2, nmo, nmo), mean-field mo coefficient 195 | Lpq : 4D array (2, naux, nmo, nmo), 3-index ERI 196 | nw: interger, grid number 197 | 198 | Returns: 199 | self.e_tot : RPA total eenrgy 200 | self.e_hf : EXX energy 201 | self.e_corr : RPA correlation energy 202 | """ 203 | if mo_coeff is None: 204 | mo_coeff = _mo_without_core(self, self._scf.mo_coeff) 205 | if mo_energy is None: 206 | mo_energy = _mo_energy_without_core(self, self._scf.mo_energy) 207 | 208 | cput0 = (time.process_time(), time.perf_counter()) 209 | self.dump_flags() 210 | self.e_tot, self.e_hf, self.e_corr = \ 211 | kernel(self, mo_energy, mo_coeff, Lpq=Lpq, nw=nw, verbose=self.verbose) 212 | 213 | logger.timer(self, 'RPA', *cput0) 214 | return self.e_corr 215 | 216 | def ao2mo(self, mo_coeff=None): 217 | nmoa, nmob = self.nmo 218 | nao = self.mo_coeff[0].shape[0] 219 | naux = self.with_df.get_naoaux() 220 | mem_incore = (nmoa**2*naux + nmob**2*naux + nao**2*naux) * 8/1e6 221 | mem_now = lib.current_memory()[0] 222 | 223 | moa = np.asarray(mo_coeff[0], order='F') 224 | mob = np.asarray(mo_coeff[1], order='F') 225 | ijslicea = (0, nmoa, 0, nmoa) 226 | ijsliceb = (0, nmob, 0, nmob) 227 | Lpqa = None 228 | Lpqb = None 229 | if (mem_incore + mem_now < 0.99*self.max_memory) or self.mol.incore_anyway: 230 | Lpqa = _ao2mo.nr_e2(self.with_df._cderi, moa, ijslicea, aosym='s2', out=Lpqa) 231 | Lpqb = _ao2mo.nr_e2(self.with_df._cderi, mob, ijsliceb, aosym='s2', out=Lpqb) 232 | return np.asarray((Lpqa.reshape(naux,nmoa,nmoa),Lpqb.reshape(naux,nmob,nmob))) 233 | else: 234 | logger.warn(self, 'Memory may not be enough!') 235 | raise NotImplementedError 236 | 237 | 238 | if __name__ == '__main__': 239 | from pyscf import gto, dft, scf 240 | mol = gto.Mole() 241 | mol.verbose = 5 242 | mol.atom = 'F 0 0 0' 243 | mol.basis = 'def2-svp' 244 | mol.spin = 1 245 | mol.build() 246 | 247 | mf = dft.UKS(mol) 248 | mf.xc = 'pbe0' 249 | mf.kernel() 250 | 251 | rpa = URPA(mf) 252 | rpa.kernel() 253 | print ('RPA e_tot, e_hf, e_corr = ', rpa.e_tot, rpa.e_hf, rpa.e_corr) 254 | assert(abs(rpa.e_corr- -0.20980646878974454) < 1e-6) 255 | assert(abs(rpa.e_tot- -99.49292565821425) < 1e-6) 256 | -------------------------------------------------------------------------------- /fcdmft/rpa/pbc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhuGroup-Yale/fcdmft/98d93f863bc1ad9dc9d17982de4f5e89581a811c/fcdmft/rpa/pbc/__init__.py -------------------------------------------------------------------------------- /fcdmft/rpa/pbc/rpa.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2014-2021 The PySCF Developers. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Author: Tianyu Zhu 17 | # 18 | 19 | """ 20 | Periodic spin-restricted random phase approximation 21 | (direct RPA/dRPA in chemistry) with N^4 scaling (Gamma only) 22 | 23 | Method: 24 | Main routines are based on GW-AC method descirbed in: 25 | T. Zhu and G.K.-L. Chan, J. Chem. Theory. Comput. 17, 727-741 (2021) 26 | X. Ren et al., New J. Phys. 14, 053020 (2012) 27 | """ 28 | 29 | import time, h5py, os 30 | from functools import reduce 31 | import numpy as np 32 | 33 | from pyscf import lib 34 | from pyscf.lib import logger 35 | from pyscf.ao2mo import _ao2mo 36 | from pyscf.pbc import df, dft, scf 37 | from pyscf.mp.mp2 import get_nocc, get_nmo, get_frozen_mask 38 | from pyscf import __config__ 39 | from fcdmft.rpa.mol.rpa import RPA, get_rpa_ecorr, _get_scaled_legendre_roots, \ 40 | get_rho_response, _mo_energy_without_core, _mo_without_core 41 | 42 | einsum = lib.einsum 43 | 44 | def kernel(rpa, mo_energy, mo_coeff, Lpq=None, nw=None, verbose=logger.NOTE): 45 | """ 46 | RPA correlation and total energy 47 | 48 | Args: 49 | Lpq : density fitting 3-center integral in MO basis. 50 | nw : number of frequency point on imaginary axis. 51 | vhf_df : using density fitting integral to compute HF exchange. 52 | 53 | Returns: 54 | e_tot : RPA total energy 55 | e_hf : EXX energy 56 | e_corr : RPA correlation energy 57 | """ 58 | mf = rpa._scf 59 | assert(rpa.frozen is None) 60 | 61 | if Lpq is None: 62 | Lpq = rpa.ao2mo(mo_coeff) 63 | 64 | # Grids for integration on imaginary axis 65 | freqs, wts = _get_scaled_legendre_roots(nw) 66 | 67 | # Compute HF exchange energy (EXX) 68 | dm = mf.make_rdm1() 69 | rhf = scf.RHF(rpa.mol, exxdiv=mf.exxdiv) 70 | rhf.with_df = mf.with_df 71 | rhf.with_df._cderi = mf.with_df._cderi 72 | e_hf = rhf.energy_elec(dm=dm)[0] 73 | e_hf += mf.energy_nuc() 74 | 75 | # Compute RPA correlation energy 76 | e_corr = get_rpa_ecorr(rpa, Lpq, freqs, wts) 77 | 78 | # Compute totol energy 79 | e_tot = e_hf + e_corr 80 | 81 | logger.debug(rpa, ' RPA total energy = %s', e_tot) 82 | logger.debug(rpa, ' EXX energy = %s, RPA corr energy = %s', e_hf, e_corr) 83 | 84 | return e_tot, e_hf, e_corr 85 | 86 | 87 | class RPA(RPA): 88 | 89 | @property 90 | def nocc(self): 91 | return self.get_nocc() 92 | @nocc.setter 93 | def nocc(self, n): 94 | self._nocc = n 95 | 96 | @property 97 | def nmo(self): 98 | return self.get_nmo() 99 | @nmo.setter 100 | def nmo(self, n): 101 | self._nmo = n 102 | 103 | get_nocc = get_nocc 104 | get_nmo = get_nmo 105 | get_frozen_mask = get_frozen_mask 106 | 107 | def kernel(self, mo_energy=None, mo_coeff=None, Lpq=None, nw=40): 108 | """ 109 | Args: 110 | mo_energy : 1D array (nmo), mean-field mo energy 111 | mo_coeff : 2D array (nmo, nmo), mean-field mo coefficient 112 | Lpq : 3D array (naux, nmo, nmo), 3-index ERI 113 | nw: interger, grid number 114 | 115 | Returns: 116 | self.e_tot : RPA total eenrgy 117 | self.e_hf : EXX energy 118 | self.e_corr : RPA correlation energy 119 | """ 120 | if mo_coeff is None: 121 | mo_coeff = _mo_without_core(self, self._scf.mo_coeff) 122 | if mo_energy is None: 123 | mo_energy = _mo_energy_without_core(self, self._scf.mo_energy) 124 | 125 | cput0 = (time.process_time(), time.perf_counter()) 126 | self.dump_flags() 127 | self.e_tot, self.e_hf, self.e_corr = \ 128 | kernel(self, mo_energy, mo_coeff, Lpq=Lpq, nw=nw, verbose=self.verbose) 129 | 130 | logger.timer(self, 'RPA', *cput0) 131 | return self.e_corr 132 | 133 | def ao2mo(self, mo_coeff=None): 134 | nmo = self.nmo 135 | nao = self.mo_coeff.shape[0] 136 | naux = self.with_df.get_naoaux() 137 | kpts = self._scf.with_df.kpts 138 | mem_incore = (2 * nmo**2*naux) * 8 /1e6 139 | mem_now = lib.current_memory()[0] 140 | 141 | mo = np.asarray(mo_coeff, order='F') 142 | ijslice = (0, nmo, 0, nmo) 143 | Lpq = None 144 | 145 | eri_3d_kpts = [] 146 | for i, kpti in enumerate(kpts): 147 | eri_3d_kpts.append([]) 148 | for j, kptj in enumerate(kpts): 149 | eri_3d = [] 150 | for LpqR, LpqI, sign in self._scf.with_df.sr_loop([kpti,kptj], max_memory=mem_now, compact=False): 151 | eri_3d.append(LpqR+LpqI*1j) 152 | eri_3d = np.vstack(eri_3d).reshape(-1,nao,nao) 153 | eri_3d_kpts[i].append(eri_3d) 154 | 155 | if (mem_incore+mem_now < self.max_memory) or self.mol.incore_anyway: 156 | tao = [] 157 | ao_loc = None 158 | Lpq = _ao2mo.r_e2(np.array(eri_3d_kpts[0][0]), mo, ijslice, tao, ao_loc, out=Lpq) 159 | return Lpq.real.reshape(naux,nmo,nmo) 160 | else: 161 | logger.warn(self, 'Memory not enough!') 162 | raise NotImplementedError 163 | 164 | if __name__ == '__main__': 165 | from pyscf.pbc import gto, scf, dft, df, tools 166 | from pyscf.pbc.lib import chkfile 167 | 168 | ucell = gto.Cell() 169 | ucell.build(unit = 'angstrom', 170 | a = ''' 171 | 0.000000 1.783500 1.783500 172 | 1.783500 0.000000 1.783500 173 | 1.783500 1.783500 0.000000 174 | ''', 175 | atom = 'C 1.337625 1.337625 1.337625; C 2.229375 2.229375 2.229375', 176 | dimension = 3, 177 | max_memory = 64000, 178 | verbose = 5, 179 | pseudo = 'gth-pbe', 180 | basis='gth-dzv', 181 | precision=1e-12) 182 | 183 | kmesh = [3,1,1] 184 | cell = tools.super_cell(ucell, kmesh) 185 | cell.verbose = 5 186 | 187 | gdf = df.GDF(cell) 188 | gdf_fname = 'gdf_ints.h5' 189 | gdf._cderi_to_save = gdf_fname 190 | if not os.path.isfile(gdf_fname): 191 | gdf.build() 192 | 193 | chkfname = 'diamond_hf.chk' 194 | if os.path.isfile(chkfname): 195 | kmf = scf.RHF(cell).density_fit() 196 | kmf.with_df = gdf 197 | kmf.with_df._cderi = gdf_fname 198 | kmf.conv_tol = 1e-12 199 | data = chkfile.load(chkfname, 'scf') 200 | kmf.__dict__.update(data) 201 | else: 202 | kmf = scf.RHF(cell).density_fit() 203 | kmf.with_df = gdf 204 | kmf.with_df._cderi = gdf_fname 205 | kmf.conv_tol = 1e-12 206 | kmf.chkfile = chkfname 207 | kmf.kernel() 208 | 209 | rpa = RPA(kmf) 210 | rpa.kernel() 211 | assert(abs(rpa.e_corr- -0.5558316165999143) < 1e-6) 212 | assert(abs(rpa.e_tot- -32.08317615664809) < 1e-6) 213 | -------------------------------------------------------------------------------- /fcdmft/rpa/pbc/urpa.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2014-2021 The PySCF Developers. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Author: Tianyu Zhu 17 | # 18 | 19 | """ 20 | Periodic spin-unrestricted random phase approximation 21 | (direct RPA/dRPA in chemistry) with N^4 scaling (Gamma only) 22 | 23 | Method: 24 | Main routines are based on GW-AC method descirbed in: 25 | T. Zhu and G.K.-L. Chan, J. Chem. Theory. Comput. 17, 727-741 (2021) 26 | X. Ren et al., New J. Phys. 14, 053020 (2012) 27 | """ 28 | 29 | import time, h5py, os 30 | from functools import reduce 31 | import numpy as np 32 | 33 | from pyscf import lib 34 | from pyscf.lib import logger 35 | from pyscf.ao2mo import _ao2mo 36 | from pyscf import df, dft, scf 37 | from pyscf.mp.ump2 import get_nocc, get_nmo, get_frozen_mask 38 | from pyscf import __config__ 39 | from fcdmft.rpa.mol.urpa import URPA, get_rpa_ecorr, \ 40 | get_rho_response, _mo_energy_without_core, _mo_without_core 41 | from fcdmft.rpa.mol.rpa import _get_scaled_legendre_roots 42 | 43 | einsum = lib.einsum 44 | 45 | def kernel(rpa, mo_energy, mo_coeff, Lpq=None, nw=None, verbose=logger.NOTE): 46 | """ 47 | RPA correlation and total energy 48 | 49 | Args: 50 | Lpq : density fitting 3-center integral in MO basis. 51 | nw : number of frequency point on imaginary axis. 52 | vhf_df : using density fitting integral to compute HF exchange. 53 | 54 | Returns: 55 | e_tot : RPA total energy 56 | e_hf : EXX energy 57 | e_corr : RPA correlation energy 58 | """ 59 | mf = rpa._scf 60 | assert(rpa.frozen is None) 61 | 62 | if Lpq is None: 63 | Lpq = rpa.ao2mo(mo_coeff) 64 | 65 | # Grids for integration on imaginary axis 66 | freqs, wts = _get_scaled_legendre_roots(nw) 67 | 68 | # Compute HF exchange energy (EXX) 69 | dm = mf.make_rdm1() 70 | uhf = scf.UHF(rpa.mol, exxdiv=mf.exxdiv) 71 | uhf.with_df = mf.with_df 72 | uhf.with_df._cderi = mf.with_df._cderi 73 | e_hf = uhf.energy_elec(dm=dm)[0] 74 | e_hf += mf.energy_nuc() 75 | 76 | # Compute RPA correlation energy 77 | e_corr = get_rpa_ecorr(rpa, Lpq, freqs, wts) 78 | 79 | # Compute totol energy 80 | e_tot = e_hf + e_corr 81 | 82 | logger.debug(rpa, ' RPA total energy = %s', e_tot) 83 | logger.debug(rpa, ' EXX energy = %s, RPA corr energy = %s', e_hf, e_corr) 84 | 85 | return e_tot, e_hf, e_corr 86 | 87 | class URPA(URPA): 88 | 89 | get_nocc = get_nocc 90 | get_nmo = get_nmo 91 | get_frozen_mask = get_frozen_mask 92 | 93 | def kernel(self, mo_energy=None, mo_coeff=None, Lpq=None, nw=40): 94 | """ 95 | Args: 96 | mo_energy : 2D array (2, nmo), mean-field mo energy 97 | mo_coeff : 3D array (2, nmo, nmo), mean-field mo coefficient 98 | Lpq : 4D array (2, naux, nmo, nmo), 3-index ERI 99 | nw: interger, grid number 100 | 101 | Returns: 102 | self.e_tot : RPA total eenrgy 103 | self.e_hf : EXX energy 104 | self.e_corr : RPA correlation energy 105 | """ 106 | if mo_coeff is None: 107 | mo_coeff = _mo_without_core(self, self._scf.mo_coeff) 108 | if mo_energy is None: 109 | mo_energy = _mo_energy_without_core(self, self._scf.mo_energy) 110 | 111 | cput0 = (time.process_time(), time.perf_counter()) 112 | self.dump_flags() 113 | self.e_tot, self.e_hf, self.e_corr = \ 114 | kernel(self, mo_energy, mo_coeff, Lpq=Lpq, nw=nw, verbose=self.verbose) 115 | 116 | logger.timer(self, 'RPA', *cput0) 117 | return self.e_corr 118 | 119 | def ao2mo(self, mo_coeff=None): 120 | nmoa, nmob = self.nmo 121 | nao = self.mo_coeff[0].shape[0] 122 | naux = self.with_df.get_naoaux() 123 | kpts = self._scf.with_df.kpts 124 | mem_incore = (nmoa**2*naux + nmob**2*naux + nao**2*naux) * 8/1e6 125 | mem_now = lib.current_memory()[0] 126 | 127 | moa = np.asarray(mo_coeff[0], order='F') 128 | mob = np.asarray(mo_coeff[1], order='F') 129 | ijslicea = (0, nmoa, 0, nmoa) 130 | ijsliceb = (0, nmob, 0, nmob) 131 | Lpqa = None 132 | Lpqb = None 133 | 134 | eri_3d_kpts = [] 135 | for i, kpti in enumerate(kpts): 136 | eri_3d_kpts.append([]) 137 | for j, kptj in enumerate(kpts): 138 | eri_3d = [] 139 | for LpqR, LpqI, sign in self._scf.with_df.sr_loop([kpti,kptj], max_memory=mem_now, compact=False): 140 | eri_3d.append(LpqR+LpqI*1j) 141 | eri_3d = np.vstack(eri_3d).reshape(-1,nao,nao) 142 | eri_3d_kpts[i].append(eri_3d) 143 | 144 | if (mem_incore+mem_now < self.max_memory) or self.mol.incore_anyway: 145 | tao = [] 146 | ao_loc = None 147 | Lpqa = _ao2mo.r_e2(np.array(eri_3d_kpts[0][0]), moa, ijslicea, tao, ao_loc, out=Lpqa) 148 | tao = [] 149 | ao_loc = None 150 | Lpqb = _ao2mo.r_e2(np.array(eri_3d_kpts[0][0]), mob, ijsliceb, tao, ao_loc, out=Lpqb) 151 | return np.asarray((Lpqa.real.reshape(naux,nmoa,nmoa),Lpqb.real.reshape(naux,nmob,nmob))) 152 | else: 153 | logger.warn(self, 'Memory not enough!') 154 | raise NotImplementedError 155 | 156 | if __name__ == '__main__': 157 | from pyscf.pbc import gto, scf, dft, df, tools 158 | from pyscf.pbc.lib import chkfile 159 | 160 | ucell = gto.Cell() 161 | ucell.build( 162 | unit = 'B', 163 | a = [[ 0., 6.74027466, 6.74027466], 164 | [ 6.74027466, 0., 6.74027466], 165 | [ 6.74027466, 6.74027466, 0. ]], 166 | atom = '''H 0 0 0 167 | H 1.68506866 1.68506866 1.68506866 168 | H 3.37013733 3.37013733 3.37013733''', 169 | basis = 'gth-dzvp', 170 | pseudo = 'gth-pade', 171 | verbose = 5, 172 | charge = 0, 173 | spin = 1) 174 | 175 | kmesh = [3,1,1] 176 | cell = tools.super_cell(ucell, kmesh) 177 | cell.verbose = 5 178 | cell.spin = ucell.spin * 3 179 | 180 | gdf = df.GDF(cell) 181 | gdf_fname = 'gdf_ints.h5' 182 | gdf._cderi_to_save = gdf_fname 183 | if not os.path.isfile(gdf_fname): 184 | gdf.build() 185 | 186 | chkfname = 'h3_hf.chk' 187 | if os.path.isfile(chkfname): 188 | kmf = scf.UHF(cell).density_fit() 189 | kmf.with_df = gdf 190 | kmf.with_df._cderi = gdf_fname 191 | kmf.conv_tol = 1e-12 192 | data = chkfile.load(chkfname, 'scf') 193 | kmf.__dict__.update(data) 194 | else: 195 | kmf = scf.UHF(cell).density_fit() 196 | kmf.with_df = gdf 197 | kmf.with_df._cderi = gdf_fname 198 | kmf.conv_tol = 1e-12 199 | kmf.chkfile = chkfname 200 | kmf.kernel() 201 | 202 | rpa = URPA(kmf) 203 | rpa.kernel() 204 | assert(abs(rpa.e_corr- -0.12865054702442688) < 1e-6) 205 | assert(abs(rpa.e_tot- -4.767926645428772) < 1e-6) 206 | -------------------------------------------------------------------------------- /fcdmft/solver/ccgf.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | 4 | import numpy as np 5 | import scipy 6 | from fcdmft.solver import gmres 7 | 8 | import pyscf 9 | import pyscf.cc 10 | from pyscf.lib import logger 11 | from pyscf.cc.eom_rccsd import amplitudes_to_vector_ip, amplitudes_to_vector_ea 12 | 13 | ''' 14 | CCSD Green's function 15 | ''' 16 | 17 | 18 | def greens_b_singles_ea_rhf(t1, p): 19 | nocc, nvir = t1.shape 20 | ds_type = t1.dtype 21 | if p < nocc: 22 | return -t1[p,:] 23 | else: 24 | p = p-nocc 25 | result = np.zeros((nvir,), dtype=ds_type) 26 | result[p] = 1.0 27 | return result 28 | 29 | 30 | def greens_b_doubles_ea_rhf(t2, p): 31 | nocc, _, nvir, _ = t2.shape 32 | ds_type = t2.dtype 33 | if p < nocc: 34 | return -t2[p,:,:,:] 35 | else: 36 | return np.zeros((nocc,nvir,nvir), dtype=ds_type) 37 | 38 | 39 | def greens_b_vector_ea_rhf(cc, p): 40 | return amplitudes_to_vector_ea( 41 | greens_b_singles_ea_rhf(cc.t1, p), 42 | greens_b_doubles_ea_rhf(cc.t2, p), 43 | ) 44 | 45 | 46 | def greens_e_singles_ea_rhf(t1, t2, l1, l2, p): 47 | nocc, nvir = t1.shape 48 | ds_type = t1.dtype 49 | if p < nocc: 50 | return l1[p, :] 51 | else: 52 | p = p-nocc 53 | result = np.zeros((nvir,), dtype=ds_type) 54 | result[p] = -1.0 55 | result += np.einsum('ia,i->a', l1, t1[:,p]) 56 | result += 2*np.einsum('klca,klc->a', l2, t2[:,:,:,p]) 57 | result -= np.einsum('klca,lkc->a', l2, t2[:,:,:,p]) 58 | return result 59 | 60 | 61 | def greens_e_doubles_ea_rhf(t1, l1, l2, p): 62 | nocc, nvir = t1.shape 63 | ds_type = t1.dtype 64 | if p < nocc: 65 | return 2*l2[p,:,:,:] - l2[:,p,:,:] 66 | else: 67 | p = p-nocc 68 | result = np.zeros((nocc,nvir,nvir), dtype=ds_type) 69 | result[:,p,:] += -2*l1 70 | result[:,:,p] += l1 71 | result += 2*np.einsum('k,jkba->jab', t1[:,p], l2) 72 | result -= np.einsum('k,jkab->jab', t1[:,p], l2) 73 | return result 74 | 75 | 76 | def greens_e_vector_ea_rhf(cc, p): 77 | return amplitudes_to_vector_ea( 78 | greens_e_singles_ea_rhf(cc.t1, cc.t2, cc.l1, cc.l2, p), 79 | greens_e_doubles_ea_rhf(cc.t1, cc.l1, cc.l2, p), 80 | ) 81 | 82 | 83 | def greens_b_singles_ip_rhf(t1, p): 84 | nocc, nvir = t1.shape 85 | ds_type = t1.dtype 86 | if p < nocc: 87 | result = np.zeros((nocc,), dtype=ds_type) 88 | result[p] = 1.0 89 | return result 90 | else: 91 | p = p-nocc 92 | return t1[:,p] 93 | 94 | 95 | def greens_b_doubles_ip_rhf(t2, p): 96 | nocc, _, nvir, _ = t2.shape 97 | ds_type = t2.dtype 98 | if p < nocc: 99 | return np.zeros((nocc,nocc,nvir), dtype=ds_type) 100 | else: 101 | p = p-nocc 102 | return t2[:,:,p,:] 103 | 104 | 105 | def greens_b_vector_ip_rhf(cc, p): 106 | return amplitudes_to_vector_ip( 107 | greens_b_singles_ip_rhf(cc.t1, p), 108 | greens_b_doubles_ip_rhf(cc.t2, p), 109 | ) 110 | 111 | 112 | def greens_e_singles_ip_rhf(t1, t2, l1, l2, p): 113 | nocc, nvir = t1.shape 114 | ds_type = t1.dtype 115 | if p < nocc: 116 | result = np.zeros((nocc,), dtype=ds_type) 117 | result[p] = -1.0 118 | result += np.einsum('ia,a->i', l1, t1[p,:]) 119 | result += 2*np.einsum('ilcd,lcd->i', l2, t2[p,:,:,:]) 120 | result -= np.einsum('ilcd,ldc->i', l2, t2[p,:,:,:]) 121 | return result 122 | else: 123 | p = p-nocc 124 | return -l1[:,p] 125 | 126 | 127 | def greens_e_doubles_ip_rhf(t1, l1, l2, p): 128 | nocc, nvir = t1.shape 129 | ds_type = t1.dtype 130 | if p < nocc: 131 | result = np.zeros((nocc, nocc, nvir), dtype=ds_type) 132 | result[p, :, :] += -2*l1 133 | result[:, p, :] += l1 134 | result += 2*np.einsum('c,ijcb->ijb', t1[p,:], l2) 135 | result -= np.einsum('c,jicb->ijb', t1[p,:], l2) 136 | return result 137 | else: 138 | p = p-nocc 139 | return -2*l2[:,:,p,:] + l2[:,:,:,p] 140 | 141 | 142 | def greens_e_vector_ip_rhf(cc, p): 143 | return amplitudes_to_vector_ip( 144 | greens_e_singles_ip_rhf(cc.t1, cc.t2, cc.l1, cc.l2, p), 145 | greens_e_doubles_ip_rhf(cc.t1, cc.l1, cc.l2, p), 146 | ) 147 | 148 | 149 | def greens_func_multiply(ham, vector, linear_part, **kwargs): 150 | return np.array(ham(vector, **kwargs) + linear_part * vector) 151 | 152 | 153 | def ip_shape(cc): 154 | nocc, nvir = cc.t1.shape 155 | return nocc + nocc*nocc*nvir 156 | 157 | 158 | def ea_shape(cc): 159 | nocc, nvir = cc.t1.shape 160 | return nvir + nocc*nvir*nvir 161 | 162 | 163 | class CCGF(object): 164 | def __init__(self, mycc, tol=1e-4, verbose=None): 165 | self._cc = mycc 166 | self.tol = tol 167 | if verbose: 168 | self.verbose = verbose 169 | else: 170 | self.verbose = self._cc.verbose 171 | self.stdout = sys.stdout 172 | 173 | def ipccsd_ao(self, ps, omega_list, mo_coeff, broadening, qs=None): 174 | ''' 175 | Compute IP-CCSD-GF in AO basis 176 | ''' 177 | eomip = pyscf.cc.eom_rccsd.EOMIP(self._cc) 178 | eomip_imds = eomip.make_imds() 179 | diag = eomip.get_diag() 180 | 181 | if qs is None: 182 | qs = ps 183 | nmo = mo_coeff.shape[1] 184 | e_vector_mo = np.zeros([nmo, ip_shape(self._cc)], dtype=np.complex128) 185 | for i in range(nmo): 186 | e_vector_mo[i,:] = greens_e_vector_ip_rhf(self._cc, i) 187 | e_vector_ao = np.einsum("pi,ix->px", mo_coeff[qs,:], e_vector_mo) 188 | b_vector_mo = np.zeros([ip_shape(self._cc), nmo], dtype=np.complex128) 189 | for i in range(nmo): 190 | b_vector_mo[:,i] = greens_b_vector_ip_rhf(self._cc, i) 191 | b_vector_ao = np.einsum("xi,ip->xp", b_vector_mo, mo_coeff.T[:,ps]) 192 | 193 | gf_ao = np.zeros((len(ps), len(qs), len(omega_list)), dtype=np.complex128) 194 | for ip, p in enumerate(ps): 195 | x0 = None 196 | for iomega in range(len(omega_list))[::-1]: 197 | curr_omega = omega_list[iomega] 198 | 199 | def matr_multiply(vector, args=None): 200 | return greens_func_multiply(eomip.matvec, vector, curr_omega - 1j * broadening, imds=eomip_imds) 201 | 202 | diag_w = diag + curr_omega-1j*broadening 203 | if x0 is None: 204 | x0 = b_vector_ao[:,ip]/diag_w 205 | solver = gmres.GMRES(matr_multiply, b_vector_ao[:,ip], x0, diag_w, tol=self.tol) 206 | cput1 = (time.process_time(), time.perf_counter()) 207 | sol = solver.solve().reshape(-1) 208 | cput1 = logger.timer(self, 'IPGF GMRES orbital p = %d/%d, freq w = %d/%d (%d iterations)'%( 209 | ip+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1) 210 | x0 = sol 211 | for iq, q in enumerate(qs): 212 | gf_ao[ip,iq,iomega] = -np.dot(e_vector_ao[iq,:], sol) 213 | return gf_ao 214 | 215 | def eaccsd_ao(self, ps, omega_list, mo_coeff, broadening, qs=None): 216 | ''' 217 | Compute EA-CCSD-GF in AO basis 218 | ''' 219 | eomea = pyscf.cc.eom_rccsd.EOMEA(self._cc) 220 | eomea_imds = eomea.make_imds() 221 | diag = eomea.get_diag() 222 | 223 | if qs is None: 224 | qs = ps 225 | nmo = mo_coeff.shape[1] 226 | e_vector_mo = np.zeros([nmo, ea_shape(self._cc)], dtype=np.complex128) 227 | for i in range(nmo): 228 | e_vector_mo[i,:] = greens_e_vector_ea_rhf(self._cc, i) 229 | e_vector_ao = np.einsum("pi,ix->px", mo_coeff[qs,:], e_vector_mo) 230 | b_vector_mo = np.zeros([ea_shape(self._cc), nmo], dtype=np.complex128) 231 | for i in range(nmo): 232 | b_vector_mo[:,i] = greens_b_vector_ea_rhf(self._cc, i) 233 | b_vector_ao = np.einsum("xi,ip->xp", b_vector_mo, mo_coeff.T[:,ps]) 234 | 235 | gf_ao = np.zeros((len(qs), len(ps), len(omega_list)), dtype=np.complex128) 236 | for iq, q in enumerate(ps): 237 | x0 = None 238 | for iomega in range(len(omega_list)): 239 | curr_omega = omega_list[iomega] 240 | 241 | def matr_multiply(vector, args=None): 242 | return greens_func_multiply(eomea.matvec, vector, -curr_omega - 1j * broadening, imds=eomea_imds) 243 | 244 | diag_w = diag + (-curr_omega-1j*broadening) 245 | if x0 is None: 246 | x0 = b_vector_ao[:,iq]/diag_w 247 | solver = gmres.GMRES(matr_multiply, b_vector_ao[:,iq], x0, diag_w, tol=self.tol) 248 | cput1 = (time.process_time(), time.perf_counter()) 249 | sol = solver.solve().reshape(-1) 250 | cput1 = logger.timer(self, 'EAGF GMRES orbital q = %d/%d, freq w = %d/%d (%d iterations)'%( 251 | iq+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1) 252 | x0 = sol 253 | for ip, p in enumerate(qs): 254 | gf_ao[ip,iq,iomega] = np.dot(e_vector_ao[ip,:], sol) 255 | return gf_ao 256 | 257 | def ipccsd_mo(self, ps, qs, omega_list, broadening): 258 | ''' 259 | Compute IP-CCSD-GF in MO basis 260 | ''' 261 | eomip = pyscf.cc.eom_rccsd.EOMIP(self._cc) 262 | eomip_imds = eomip.make_imds() 263 | diag = eomip.get_diag() 264 | e_vector = list() 265 | for q in qs: 266 | e_vector.append(greens_e_vector_ip_rhf(self._cc, q)) 267 | gfvals = np.zeros((len(ps), len(qs), len(omega_list)), dtype=complex) 268 | for ip, p in enumerate(ps): 269 | b_vector = greens_b_vector_ip_rhf(self._cc, p) 270 | for iomega in range(len(omega_list)): 271 | curr_omega = omega_list[iomega] 272 | 273 | def matr_multiply(vector, args=None): 274 | return greens_func_multiply(eomip.matvec, vector, curr_omega - 1j * broadening, imds=eomip_imds) 275 | 276 | diag_w = diag + curr_omega-1j*broadening 277 | x0 = b_vector/diag_w 278 | solver = gmres.GMRES(matr_multiply, b_vector, x0, diag_w, tol=self.tol) 279 | cput1 = (time.process_time(), time.perf_counter()) 280 | sol = solver.solve().reshape(-1) 281 | cput1 = logger.timer(self, 'IPGF GMRES orbital p = %d/%d, freq w = %d/%d (%d iterations)'%( 282 | ip+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1) 283 | x0 = sol 284 | for iq, q in enumerate(qs): 285 | gfvals[ip,iq,iomega] = -np.dot(e_vector[iq], sol) 286 | return gfvals 287 | 288 | def eaccsd_mo(self, ps, qs, omega_list, broadening): 289 | ''' 290 | Compute EA-CCSD-GF in MO basis 291 | ''' 292 | eomea = pyscf.cc.eom_rccsd.EOMEA(self._cc) 293 | eomea_imds = eomea.make_imds() 294 | diag = eomea.get_diag() 295 | e_vector = list() 296 | for p in ps: 297 | e_vector.append(greens_e_vector_ea_rhf(self._cc, p)) 298 | gfvals = np.zeros((len(ps), len(qs), len(omega_list)), dtype=complex) 299 | for iq, q in enumerate(qs): 300 | b_vector = greens_b_vector_ea_rhf(self._cc, q) 301 | for iomega in range(len(omega_list)): 302 | curr_omega = omega_list[iomega] 303 | 304 | def matr_multiply(vector, args=None): 305 | return greens_func_multiply(eomea.matvec, vector, -curr_omega - 1j * broadening, imds=eomea_imds) 306 | 307 | diag_w = diag + (-curr_omega-1j*broadening) 308 | x0 = b_vector/diag_w 309 | solver = gmres.GMRES(matr_multiply, b_vector, x0, diag_w, tol=self.tol) 310 | cput1 = (time.process_time(), time.perf_counter()) 311 | sol = solver.solve().reshape(-1) 312 | cput1 = logger.timer(self, 'EAGF GMRES orbital q = %d/%d, freq w = %d/%d (%d iterations)'%( 313 | iq+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1) 314 | x0 = sol 315 | for ip, p in enumerate(ps): 316 | gfvals[ip,iq,iomega] = np.dot(e_vector[ip], sol) 317 | return gfvals 318 | 319 | def get_gf(self, p, q, omega_list, broadening): 320 | return (self.ipccsd_mo(p, q, omega_list, broadening), 321 | self.eaccsd_mo(p, q, omega_list, broadening)) 322 | 323 | -------------------------------------------------------------------------------- /fcdmft/solver/gmres.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.sparse.linalg as spla 3 | 4 | ''' 5 | GMRES/GCROT(m,k) for solving Green's function linear equations 6 | ''' 7 | 8 | class GMRES(object): 9 | def __init__(self, A, b, x0, diag=None, tol=1e-3): 10 | n = max(b.shape) 11 | self.A = spla.LinearOperator((n,n), matvec=A) 12 | self.b = b.reshape(-1) 13 | self.x0 = x0.reshape(-1) 14 | self.tol = tol 15 | self.niter = 0 16 | if diag is None: 17 | self.M = None 18 | else: 19 | diag = diag.reshape(-1) 20 | Mx = lambda x: x/diag 21 | self.M = spla.LinearOperator((n,n), matvec=Mx) 22 | 23 | def solve(self): 24 | callback = None 25 | self.niter = 0 26 | def callback(rk): 27 | #print ("residual =", rk) 28 | self.niter += 1 29 | #self.x, self.info = spla.gmres(self.A, self.b, x0=self.x0, M=self.M, 30 | # maxiter=200, callback=callback, restart=40, tol=self.tol) 31 | self.x, self.info = spla.gcrotmk(self.A, self.b, x0=self.x0, M=self.M, 32 | maxiter=200, callback=callback, m=40, tol=self.tol) 33 | self.x = self.x.reshape(-1) 34 | if self.info > 0: 35 | print ("convergence to tolerance not achieved in", self.info, "iterations") 36 | return self.x 37 | 38 | 39 | def setA(size, plus): 40 | A = np.zeros(shape=(size,size),dtype=complex) 41 | diag = np.zeros(shape=(size),dtype=complex) 42 | fac = 1.0 43 | for i in xrange(size): 44 | A[i,i] = 1.0 * fac + 6.0 * 1j * fac + plus + 30.*np.random.random() 45 | diag[i] = A[i,i] 46 | if i+2 < size: 47 | A[i,i+2] = 1.0 * fac 48 | if i+3 < size: 49 | A[i,i+3] = 0.7 * fac 50 | if i+1 < size: 51 | A[i+1,i] = 3.0*1j * fac 52 | return A, diag 53 | 54 | 55 | def main(): 56 | size = 300 57 | A, diag = setA(size, 0.0+1j*0.0) 58 | b = np.random.rand(size) + 0j*np.random.rand(size) 59 | b /= np.linalg.norm(b) 60 | 61 | x0 = np.dot(np.linalg.inv(A),b) 62 | x0 += 1./1.*(np.random.rand(size) + 0j*np.random.rand(size)) 63 | condition_number = np.linalg.cond(A) 64 | res = np.linalg.norm(b-np.dot(A,x0)) 65 | finalx = np.dot(np.linalg.inv(A), b) 66 | print (" ::: Making A,b matrix :::") 67 | print (" - condition number = %12.8f" % condition_number) 68 | print (" - x0 residual = %12.8f" % np.real(res)) 69 | 70 | def multiplyA(vector, args=None): 71 | return np.dot(A,vector) 72 | 73 | gmin = GMRES(multiplyA, b, x0, diag) 74 | sol = gmin.solve() 75 | 76 | print ("|Ax-b| = ", np.linalg.norm(np.dot(A,sol) - b)) 77 | 78 | if __name__ == '__main__': 79 | main() 80 | -------------------------------------------------------------------------------- /fcdmft/solver/mpiccgf.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | 4 | import numpy as np 5 | import scipy 6 | from fcdmft.solver import gmres 7 | 8 | import pyscf 9 | import pyscf.cc 10 | from pyscf.lib import logger 11 | from pyscf.cc.eom_rccsd import amplitudes_to_vector_ip, amplitudes_to_vector_ea 12 | 13 | from mpi4py import MPI 14 | 15 | 16 | ''' 17 | MPI CCSD Green's function 18 | ''' 19 | 20 | rank = MPI.COMM_WORLD.Get_rank() 21 | size = MPI.COMM_WORLD.Get_size() 22 | comm = MPI.COMM_WORLD 23 | 24 | def greens_b_singles_ea_rhf(t1, p): 25 | nocc, nvir = t1.shape 26 | ds_type = t1.dtype 27 | if p < nocc: 28 | return -t1[p,:] 29 | else: 30 | p = p-nocc 31 | result = np.zeros((nvir,), dtype=ds_type) 32 | result[p] = 1.0 33 | return result 34 | 35 | 36 | def greens_b_doubles_ea_rhf(t2, p): 37 | nocc, _, nvir, _ = t2.shape 38 | ds_type = t2.dtype 39 | if p < nocc: 40 | return -t2[p,:,:,:] 41 | else: 42 | return np.zeros((nocc,nvir,nvir), dtype=ds_type) 43 | 44 | 45 | def greens_b_vector_ea_rhf(cc, p): 46 | return amplitudes_to_vector_ea( 47 | greens_b_singles_ea_rhf(cc.t1, p), 48 | greens_b_doubles_ea_rhf(cc.t2, p), 49 | ) 50 | 51 | 52 | def greens_e_singles_ea_rhf(t1, t2, l1, l2, p): 53 | nocc, nvir = t1.shape 54 | ds_type = t1.dtype 55 | if p < nocc: 56 | return l1[p, :] 57 | else: 58 | p = p-nocc 59 | result = np.zeros((nvir,), dtype=ds_type) 60 | result[p] = -1.0 61 | result += np.einsum('ia,i->a', l1, t1[:,p]) 62 | result += 2*np.einsum('klca,klc->a', l2, t2[:,:,:,p]) 63 | result -= np.einsum('klca,lkc->a', l2, t2[:,:,:,p]) 64 | return result 65 | 66 | 67 | def greens_e_doubles_ea_rhf(t1, l1, l2, p): 68 | nocc, nvir = t1.shape 69 | ds_type = t1.dtype 70 | if p < nocc: 71 | return 2*l2[p,:,:,:] - l2[:,p,:,:] 72 | else: 73 | p = p-nocc 74 | result = np.zeros((nocc,nvir,nvir), dtype=ds_type) 75 | result[:,p,:] += -2*l1 76 | result[:,:,p] += l1 77 | result += 2*np.einsum('k,jkba->jab', t1[:,p], l2) 78 | result -= np.einsum('k,jkab->jab', t1[:,p], l2) 79 | return result 80 | 81 | 82 | def greens_e_vector_ea_rhf(cc, p): 83 | return amplitudes_to_vector_ea( 84 | greens_e_singles_ea_rhf(cc.t1, cc.t2, cc.l1, cc.l2, p), 85 | greens_e_doubles_ea_rhf(cc.t1, cc.l1, cc.l2, p), 86 | ) 87 | 88 | 89 | def greens_b_singles_ip_rhf(t1, p): 90 | nocc, nvir = t1.shape 91 | ds_type = t1.dtype 92 | if p < nocc: 93 | result = np.zeros((nocc,), dtype=ds_type) 94 | result[p] = 1.0 95 | return result 96 | else: 97 | p = p-nocc 98 | return t1[:,p] 99 | 100 | 101 | def greens_b_doubles_ip_rhf(t2, p): 102 | nocc, _, nvir, _ = t2.shape 103 | ds_type = t2.dtype 104 | if p < nocc: 105 | return np.zeros((nocc,nocc,nvir), dtype=ds_type) 106 | else: 107 | p = p-nocc 108 | return t2[:,:,p,:] 109 | 110 | 111 | def greens_b_vector_ip_rhf(cc, p): 112 | return amplitudes_to_vector_ip( 113 | greens_b_singles_ip_rhf(cc.t1, p), 114 | greens_b_doubles_ip_rhf(cc.t2, p), 115 | ) 116 | 117 | 118 | def greens_e_singles_ip_rhf(t1, t2, l1, l2, p): 119 | nocc, nvir = t1.shape 120 | ds_type = t1.dtype 121 | if p < nocc: 122 | result = np.zeros((nocc,), dtype=ds_type) 123 | result[p] = -1.0 124 | result += np.einsum('ia,a->i', l1, t1[p,:]) 125 | result += 2*np.einsum('ilcd,lcd->i', l2, t2[p,:,:,:]) 126 | result -= np.einsum('ilcd,ldc->i', l2, t2[p,:,:,:]) 127 | return result 128 | else: 129 | p = p-nocc 130 | return -l1[:,p] 131 | 132 | 133 | def greens_e_doubles_ip_rhf(t1, l1, l2, p): 134 | nocc, nvir = t1.shape 135 | ds_type = t1.dtype 136 | if p < nocc: 137 | result = np.zeros((nocc, nocc, nvir), dtype=ds_type) 138 | result[p, :, :] += -2*l1 139 | result[:, p, :] += l1 140 | result += 2*np.einsum('c,ijcb->ijb', t1[p,:], l2) 141 | result -= np.einsum('c,jicb->ijb', t1[p,:], l2) 142 | return result 143 | else: 144 | p = p-nocc 145 | return -2*l2[:,:,p,:] + l2[:,:,:,p] 146 | 147 | 148 | def greens_e_vector_ip_rhf(cc, p): 149 | return amplitudes_to_vector_ip( 150 | greens_e_singles_ip_rhf(cc.t1, cc.t2, cc.l1, cc.l2, p), 151 | greens_e_doubles_ip_rhf(cc.t1, cc.l1, cc.l2, p), 152 | ) 153 | 154 | 155 | def greens_func_multiply(ham, vector, linear_part, **kwargs): 156 | return np.array(ham(vector, **kwargs) + linear_part * vector) 157 | 158 | 159 | def ip_shape(cc): 160 | nocc, nvir = cc.t1.shape 161 | return nocc + nocc*nocc*nvir 162 | 163 | 164 | def ea_shape(cc): 165 | nocc, nvir = cc.t1.shape 166 | return nvir + nocc*nvir*nvir 167 | 168 | 169 | class CCGF(object): 170 | def __init__(self, mycc, tol=1e-4, verbose=None): 171 | self._cc = mycc 172 | self.tol = tol 173 | if verbose: 174 | self.verbose = verbose 175 | else: 176 | self.verbose = self._cc.verbose 177 | self.stdout = sys.stdout 178 | 179 | def ipccsd_ao(self, ps, omega_list, mo_coeff, broadening): 180 | ''' 181 | Compute IP-CCSD-GF in AO basis (parallelize over orbitals) 182 | ''' 183 | eomip = pyscf.cc.eom_rccsd.EOMIP(self._cc) 184 | eomip_imds = eomip.make_imds() 185 | diag = eomip.get_diag() 186 | 187 | nmo = mo_coeff.shape[1] 188 | e_vector_mo = np.zeros([nmo, ip_shape(self._cc)], dtype=np.complex128) 189 | b_vector_mo = np.zeros([ip_shape(self._cc), nmo], dtype=np.complex128) 190 | for i in range(nmo): 191 | e_vector_mo[i,:] = greens_e_vector_ip_rhf(self._cc, i) 192 | e_vector_ao = np.einsum("pi,ix->px", mo_coeff[ps,:], e_vector_mo) 193 | for i in range(nmo): 194 | b_vector_mo[:,i] = greens_b_vector_ip_rhf(self._cc, i) 195 | b_vector_ao = np.einsum("xi,ip->xp", b_vector_mo, mo_coeff.T[:,ps]) 196 | comm.Barrier() 197 | 198 | segsize = len(ps) // size 199 | if rank >= size-(len(ps)-segsize*size): 200 | start = rank * segsize + rank-(size-(len(ps)-segsize*size)) 201 | stop = min(len(ps), start+segsize+1) 202 | else: 203 | start = rank * segsize 204 | stop = min(len(ps), start+segsize) 205 | 206 | gf_ao = np.zeros((stop-start, len(ps), len(omega_list)), dtype=np.complex128) 207 | for ip in range(start,stop): 208 | p = ps[ip] 209 | x0 = None 210 | for iomega in range(len(omega_list))[::-1]: 211 | curr_omega = omega_list[iomega] 212 | 213 | def matr_multiply(vector, args=None): 214 | return greens_func_multiply(eomip.matvec, vector, curr_omega - 1j * broadening, imds=eomip_imds) 215 | 216 | diag_w = diag + curr_omega-1j*broadening 217 | if x0 is None: 218 | x0 = b_vector_ao[:,ip]/diag_w 219 | solver = gmres.GMRES(matr_multiply, b_vector_ao[:,ip], x0, diag_w, tol=self.tol) 220 | cput1 = (time.process_time(), time.perf_counter()) 221 | sol = solver.solve().reshape(-1) 222 | cput1 = logger.timer(self, 'IPGF orbital p = %d/%d, freq w = %d/%d (%d iterations) @ Rank %d'%( 223 | ip+1,len(ps),iomega+1,len(omega_list),solver.niter,rank), *cput1) 224 | x0 = sol 225 | for iq, q in enumerate(ps): 226 | gf_ao[ip-start,iq,iomega] = -np.dot(e_vector_ao[iq,:], sol) 227 | comm.Barrier() 228 | gf_ao_gather = comm.gather(gf_ao) 229 | if rank == 0: 230 | gf_ao_gather = np.vstack(gf_ao_gather) 231 | comm.Barrier() 232 | gf_ao_gather = comm.bcast(gf_ao_gather,root=0) 233 | return gf_ao_gather 234 | 235 | def eaccsd_ao(self, ps, omega_list, mo_coeff, broadening): 236 | ''' 237 | Compute EA-CCSD-GF in AO basis (parallelize over orbitals) 238 | ''' 239 | eomea = pyscf.cc.eom_rccsd.EOMEA(self._cc) 240 | eomea_imds = eomea.make_imds() 241 | diag = eomea.get_diag() 242 | 243 | nmo = mo_coeff.shape[1] 244 | e_vector_mo = np.zeros([nmo, ea_shape(self._cc)], dtype=np.complex128) 245 | b_vector_mo = np.zeros([ea_shape(self._cc), nmo], dtype=np.complex128) 246 | for i in range(nmo): 247 | e_vector_mo[i,:] = greens_e_vector_ea_rhf(self._cc, i) 248 | e_vector_ao = np.einsum("pi,ix->px", mo_coeff[ps,:], e_vector_mo) 249 | for i in range(nmo): 250 | b_vector_mo[:,i] = greens_b_vector_ea_rhf(self._cc, i) 251 | b_vector_ao = np.einsum("xi,ip->xp", b_vector_mo, mo_coeff.T[:,ps]) 252 | comm.Barrier() 253 | 254 | segsize = len(ps) // size 255 | if rank < len(ps)-segsize*size: 256 | start = rank * segsize + rank 257 | stop = min(len(ps), start+segsize+1) 258 | else: 259 | start = rank * segsize + len(ps)-segsize*size 260 | stop = min(len(ps), start+segsize) 261 | 262 | gf_ao = np.zeros((len(ps), stop-start, len(omega_list)), dtype=np.complex128) 263 | for iq in range(start,stop): 264 | q = ps[iq] 265 | x0 = None 266 | for iomega in range(len(omega_list)): 267 | curr_omega = omega_list[iomega] 268 | 269 | def matr_multiply(vector, args=None): 270 | return greens_func_multiply(eomea.matvec, vector, -curr_omega - 1j * broadening, imds=eomea_imds) 271 | 272 | diag_w = diag + (-curr_omega-1j*broadening) 273 | if x0 is None: 274 | x0 = b_vector_ao[:,iq]/diag_w 275 | solver = gmres.GMRES(matr_multiply, b_vector_ao[:,iq], x0, diag_w, tol=self.tol) 276 | cput1 = (time.process_time(), time.perf_counter()) 277 | sol = solver.solve().reshape(-1) 278 | cput1 = logger.timer(self, 'EAGF orbital q = %d/%d, freq w = %d/%d (%d iterations) @ Rank %d'%( 279 | iq+1,len(ps),iomega+1,len(omega_list),solver.niter,rank), *cput1) 280 | x0 = sol 281 | for ip, p in enumerate(ps): 282 | gf_ao[ip,iq-start,iomega] = np.dot(e_vector_ao[ip,:], sol) 283 | comm.Barrier() 284 | gf_ao_gather = comm.gather(gf_ao.transpose(1,0,2)) 285 | if rank == 0: 286 | gf_ao_gather = np.vstack(gf_ao_gather).transpose(1,0,2) 287 | comm.Barrier() 288 | gf_ao_gather = comm.bcast(gf_ao_gather,root=0) 289 | return gf_ao_gather 290 | 291 | def ipccsd_mo(self, ps, qs, omega_list, broadening): 292 | ''' 293 | Compute IP-CCSD-GF in MO basis 294 | ''' 295 | eomip = pyscf.cc.eom_rccsd.EOMIP(self._cc) 296 | eomip_imds = eomip.make_imds() 297 | diag = eomip.get_diag() 298 | e_vector = list() 299 | for q in qs: 300 | e_vector.append(greens_e_vector_ip_rhf(self._cc, q)) 301 | 302 | segsize = len(ps) // size 303 | if rank < len(ps)-segsize*size: 304 | start = rank * segsize + rank 305 | stop = min(len(ps), start+segsize+1) 306 | else: 307 | start = rank * segsize + len(ps)-segsize*size 308 | stop = min(len(ps), start+segsize) 309 | 310 | gfvals = np.zeros((stop-start, len(qs), len(omega_list)), dtype=np.complex128) 311 | for ip in range(start,stop): 312 | p = ps[ip] 313 | b_vector = greens_b_vector_ip_rhf(self._cc, p) 314 | for iomega in range(len(omega_list)): 315 | curr_omega = omega_list[iomega] 316 | 317 | def matr_multiply(vector, args=None): 318 | return greens_func_multiply(eomip.matvec, vector, curr_omega - 1j * broadening, imds=eomip_imds) 319 | 320 | diag_w = diag + curr_omega-1j*broadening 321 | x0 = b_vector/diag_w 322 | solver = gmres.GMRES(matr_multiply, b_vector, x0, diag_w, tol=self.tol) 323 | cput1 = (time.process_time(), time.perf_counter()) 324 | sol = solver.solve().reshape(-1) 325 | cput1 = logger.timer(self, 'IPGF orbital p = %d/%d, freq w = %d/%d (%d iterations) @ Rank %d'%( 326 | ip+1,len(ps),iomega+1,len(omega_list),solver.niter,rank), *cput1) 327 | x0 = sol 328 | for iq, q in enumerate(qs): 329 | gfvals[ip-start,iq,iomega] = -np.dot(e_vector[iq], sol) 330 | comm.Barrier() 331 | gfvals_gather = comm.gather(gfvals) 332 | if rank == 0: 333 | gfvals_gather = np.vstack(gfvals_gather) 334 | comm.Barrier() 335 | gfvals_gather = comm.bcast(gfvals_gather,root=0) 336 | return gfvals_gather 337 | 338 | def eaccsd_mo(self, ps, qs, omega_list, broadening): 339 | ''' 340 | Compute EA-CCSD-GF in MO basis 341 | ''' 342 | eomea = pyscf.cc.eom_rccsd.EOMEA(self._cc) 343 | eomea_imds = eomea.make_imds() 344 | diag = eomea.get_diag() 345 | e_vector = list() 346 | for p in ps: 347 | e_vector.append(greens_e_vector_ea_rhf(self._cc, p)) 348 | 349 | segsize = len(qs) // size 350 | if rank < len(qs)-segsize*size: 351 | start = rank * segsize + rank 352 | stop = min(len(qs), start+segsize+1) 353 | else: 354 | start = rank * segsize + len(qs)-segsize*size 355 | stop = min(len(qs), start+segsize) 356 | 357 | gfvals = np.zeros((len(ps), stop-start, len(omega_list)), dtype=np.complex128) 358 | for iq in range(start,stop): 359 | q = qs[iq] 360 | b_vector = greens_b_vector_ea_rhf(self._cc, q) 361 | for iomega in range(len(omega_list)): 362 | curr_omega = omega_list[iomega] 363 | 364 | def matr_multiply(vector, args=None): 365 | return greens_func_multiply(eomea.matvec, vector, -curr_omega - 1j * broadening, imds=eomea_imds) 366 | 367 | diag_w = diag + (-curr_omega-1j*broadening) 368 | x0 = b_vector/diag_w 369 | solver = gmres.GMRES(matr_multiply, b_vector, x0, diag_w, tol=self.tol) 370 | cput1 = (time.process_time(), time.perf_counter()) 371 | sol = solver.solve().reshape(-1) 372 | cput1 = logger.timer(self, 'EAGF orbital q = %d/%d, freq w = %d/%d (%d iterations) @ Rank %d'%( 373 | iq+1,len(qs),iomega+1,len(omega_list),solver.niter,rank), *cput1) 374 | x0 = sol 375 | for ip, p in enumerate(ps): 376 | gfvals[ip,iq-start,iomega] = np.dot(e_vector[ip], sol) 377 | comm.Barrier() 378 | gfvals_gather = comm.gather(gfvals.transpose(1,0,2)) 379 | if rank == 0: 380 | gfvals_gather = np.vstack(gfvals_gather).transpose(1,0,2) 381 | comm.Barrier() 382 | gfvals_gather = comm.bcast(gfvals_gather,root=0) 383 | return gfvals_gather 384 | 385 | def get_gf(self, p, q, omega_list, broadening): 386 | return (self.ipccsd_mo(p, q, omega_list, broadening), 387 | self.eaccsd_mo(p, q, omega_list, broadening)) 388 | 389 | -------------------------------------------------------------------------------- /fcdmft/solver/scf_mu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import numpy 4 | from pyscf.lib import logger 5 | import pyscf.scf as scf 6 | import numpy as np 7 | from pyscf import ao2mo 8 | 9 | ''' 10 | HF with fluctuating occupantion and smearing 11 | ''' 12 | 13 | class RHF(scf.hf.RHF): 14 | __doc__ = scf.hf.SCF.__doc__ 15 | 16 | def __init__(self, mol, mu, smearing=None): 17 | self.mu = mu 18 | self.smearing = smearing 19 | scf.hf.SCF.__init__(self, mol) 20 | 21 | def get_occ(self, mo_energy=None, mo_coeff=None): 22 | mo_occ = numpy.zeros_like(mo_energy) 23 | if self.smearing: 24 | for n,e in enumerate(mo_energy): 25 | mo_occ[n] = 2./(numpy.exp((e-self.mu)/self.smearing)+1) 26 | else: 27 | mo_occ[mo_energy<=self.mu] = 2. 28 | nmo = mo_energy.size 29 | nocc = int(numpy.sum(mo_occ) // 2) 30 | if self.verbose >= logger.INFO and nocc < nmo: 31 | if mo_energy[nocc-1]+1e-3 > mo_energy[nocc]: 32 | logger.warn(self, 'HOMO %.15g == LUMO %.15g', 33 | mo_energy[nocc-1], mo_energy[nocc]) 34 | else: 35 | logger.info(self, ' nelec = %d', nocc*2) 36 | logger.info(self, ' HOMO = %.15g LUMO = %.15g', 37 | mo_energy[nocc-1], mo_energy[nocc]) 38 | 39 | if self.verbose >= logger.DEBUG: 40 | numpy.set_printoptions(threshold=nmo) 41 | logger.debug(self, ' mo_energy =\n%s', mo_energy) 42 | numpy.set_printoptions(threshold=1000) 43 | return mo_occ 44 | 45 | class UHF(scf.uhf.UHF): 46 | __doc__ = scf.uhf.UHF.__doc__ 47 | 48 | def __init__(self, mol, mu, smearing=None): 49 | self.mu = mu 50 | self.smearing = smearing 51 | scf.uhf.UHF.__init__(self, mol) 52 | self._keys = self._keys.union(['h1e', 'ovlp']) 53 | self.h1e = None 54 | self.ovlp = None 55 | 56 | def get_occ(self, mo_energy=None, mo_coeff=None): 57 | mo_occ = numpy.zeros_like(mo_energy) 58 | if self.smearing: 59 | for i in range(2): 60 | for n,e in enumerate(mo_energy[i]): 61 | mo_occ[i][n] = 1./(numpy.exp((e-self.mu)/self.smearing)+1) 62 | else: 63 | for i in range(2): 64 | mo_occ[i][mo_energy[i]<=self.mu] = 1. 65 | nmo = mo_energy[0].size 66 | nocca = int(numpy.sum(mo_occ[0])) 67 | noccb = int(numpy.sum(mo_occ[1])) 68 | 69 | if self.verbose >= logger.INFO and nocca < nmo and noccb > 0 and noccb < nmo: 70 | if mo_energy[0][nocca-1]+1e-3 > mo_energy[0][nocca]: 71 | logger.warn(self, 'alpha HOMO %.15g == LUMO %.15g', 72 | mo_energy[0][nocca-1], mo_energy[0][nocca]) 73 | else: 74 | logger.info(self, ' alpha nelec = %d', nocca) 75 | logger.info(self, ' alpha HOMO = %.15g LUMO = %.15g', 76 | mo_energy[0][nocca-1], mo_energy[0][nocca]) 77 | 78 | if mo_energy[1][noccb-1]+1e-3 > mo_energy[1][noccb]: 79 | logger.warn(self, 'beta HOMO %.15g == LUMO %.15g', 80 | mo_energy[1][noccb-1], mo_energy[1][noccb]) 81 | else: 82 | logger.info(self, ' beta nelec = %d', noccb) 83 | logger.info(self, ' beta HOMO = %.15g LUMO = %.15g', 84 | mo_energy[1][noccb-1], mo_energy[1][noccb]) 85 | 86 | if self.verbose >= logger.DEBUG: 87 | numpy.set_printoptions(threshold=nmo) 88 | logger.debug(self, ' mo_energy =\n%s', mo_energy) 89 | numpy.set_printoptions(threshold=1000) 90 | 91 | if mo_coeff is not None and self.verbose >= logger.DEBUG: 92 | ss, s = self.spin_square((mo_coeff[0][:,mo_occ[0]>0], 93 | mo_coeff[1][:,mo_occ[1]>0]), self.get_ovlp()) 94 | logger.debug(self, 'multiplicity = %.8g 2S+1 = %.8g', ss, s) 95 | return mo_occ 96 | 97 | def get_jk(self, mol=None, dm=None, hermi=1, with_j=True, with_k=True): 98 | '''Coulomb (J) and exchange (K) 99 | 100 | Args: 101 | dm : a list of 2D arrays or a list of 3D arrays 102 | (alpha_dm, beta_dm) or (alpha_dms, beta_dms) 103 | ''' 104 | if mol is None: mol = self.mol 105 | if dm is None: dm = self.make_rdm1() 106 | if self._eri is not None or mol.incore_anyway or self._is_mem_enough(): 107 | if self._eri is None: 108 | log.error("SCF eri is not initialized.") 109 | self._eri = mol.intor('int2e', aosym='s8') 110 | 111 | #vj, vk = hf.dot_eri_dm(self._eri, dm, hermi, with_j, with_k) 112 | vj, vk = _get_jk(dm, self._eri) 113 | else: 114 | log.error("Direct SCF not implemented") 115 | vj, vk = hf.SCF.get_jk(self, mol, dm, hermi, with_j, with_k) 116 | return vj, vk 117 | 118 | def energy_elec(self, dm=None, h1e=None, vhf=None): 119 | if dm is None: dm = self.make_rdm1() 120 | if h1e is None: 121 | h1e = self.get_hcore() 122 | if isinstance(dm, numpy.ndarray) and dm.ndim == 2: 123 | dm = numpy.array((dm*.5, dm*.5)) 124 | if vhf is None: 125 | vhf = self.get_veff(self.mol, dm) 126 | e1 = numpy.einsum('ij,ji', h1e[0], dm[0]) 127 | e1+= numpy.einsum('ij,ji', h1e[1], dm[1]) 128 | e_coul =(numpy.einsum('ij,ji', vhf[0], dm[0]) + 129 | numpy.einsum('ij,ji', vhf[1], dm[1])) * .5 130 | logger.debug(self, 'E1 = %s Ecoul = %s', e1, e_coul.real) 131 | return (e1+e_coul).real, e_coul 132 | 133 | def get_hcore(self, *args): 134 | return self.h1e 135 | 136 | def get_ovlp(self, *args): 137 | return self.ovlp 138 | 139 | class UHFNOMU(scf.uhf.UHF): 140 | __doc__ = scf.uhf.UHF.__doc__ 141 | 142 | def __init__(self, mol, smearing=None): 143 | scf.uhf.UHF.__init__(self, mol) 144 | self._keys = self._keys.union(['h1e', 'ovlp']) 145 | self.h1e = None 146 | self.ovlp = None 147 | 148 | def get_jk(self, mol=None, dm=None, hermi=1, with_j=True, with_k=True): 149 | '''Coulomb (J) and exchange (K) 150 | 151 | Args: 152 | dm : a list of 2D arrays or a list of 3D arrays 153 | (alpha_dm, beta_dm) or (alpha_dms, beta_dms) 154 | ''' 155 | if mol is None: mol = self.mol 156 | if dm is None: dm = self.make_rdm1() 157 | if self._eri is not None or mol.incore_anyway or self._is_mem_enough(): 158 | if self._eri is None: 159 | log.error("SCF eri is not initialized.") 160 | self._eri = mol.intor('int2e', aosym='s8') 161 | 162 | #vj, vk = hf.dot_eri_dm(self._eri, dm, hermi, with_j, with_k) 163 | vj, vk = _get_jk(dm, self._eri) 164 | else: 165 | log.error("Direct SCF not implemented") 166 | vj, vk = hf.SCF.get_jk(self, mol, dm, hermi, with_j, with_k) 167 | return vj, vk 168 | 169 | def energy_elec(self, dm=None, h1e=None, vhf=None): 170 | if dm is None: dm = self.make_rdm1() 171 | if h1e is None: 172 | h1e = self.get_hcore() 173 | if isinstance(dm, numpy.ndarray) and dm.ndim == 2: 174 | dm = numpy.array((dm*.5, dm*.5)) 175 | if vhf is None: 176 | vhf = self.get_veff(self.mol, dm) 177 | e1 = numpy.einsum('ij,ji', h1e[0], dm[0]) 178 | e1+= numpy.einsum('ij,ji', h1e[1], dm[1]) 179 | e_coul =(numpy.einsum('ij,ji', vhf[0], dm[0]) + 180 | numpy.einsum('ij,ji', vhf[1], dm[1])) * .5 181 | logger.debug(self, 'E1 = %s Ecoul = %s', e1, e_coul.real) 182 | return (e1+e_coul).real, e_coul 183 | 184 | def get_hcore(self, *args): 185 | return self.h1e 186 | 187 | def get_ovlp(self, *args): 188 | return self.ovlp 189 | 190 | def _get_jk(dm, eri): 191 | """ 192 | Get J and K potential from rdm and ERI. 193 | vj00 = np.tensordot(dm[0], eri[0], ((0,1), (0,1))) # J a from a 194 | vj11 = np.tensordot(dm[1], eri[1], ((0,1), (0,1))) # J b from b 195 | vj10 = np.tensordot(dm[0], eri[2], ((0,1), (0,1))) # J b from a 196 | vj01 = np.tensordot(dm[1], eri[2], ((1,0), (3,2))) # J a from b 197 | vk00 = np.tensordot(dm[0], eri[0], ((0,1), (0,3))) # K a from a 198 | vk11 = np.tensordot(dm[1], eri[1], ((0,1), (0,3))) # K b from b 199 | JK = np.asarray([vj00 + vj01 - vk00, vj11 + vj10 - vk11]) 200 | """ 201 | dm = np.asarray(dm, dtype=np.double) 202 | eri = np.asarray(eri, dtype=np.double) 203 | if len(dm.shape) == 2: 204 | dm = dm[np.newaxis, ...] 205 | if len(eri.shape) == 4: 206 | eri = eri[np.newaxis, ...] 207 | spin = dm.shape[0] 208 | norb = dm.shape[-1] 209 | if spin == 1: 210 | eri = ao2mo.restore(8, eri, norb) 211 | vj, vk = scf.hf.dot_eri_dm(eri, dm, hermi=1) 212 | else: 213 | eri_aa = ao2mo.restore(8, eri[0], norb) 214 | eri_bb = ao2mo.restore(8, eri[1], norb) 215 | eri_ab = ao2mo.restore(4, eri[2], norb) 216 | vj00, vk00 = scf.hf.dot_eri_dm(eri_aa, dm[0], hermi=1) 217 | vj11, vk11 = scf.hf.dot_eri_dm(eri_bb, dm[1], hermi=1) 218 | vj01, _ = scf.hf.dot_eri_dm(eri_ab, dm[1], hermi=1, with_j=True, with_k=False) 219 | # ZHC NOTE the transpose, since the dot_eri_dm uses the convention ijkl, kl -> ij 220 | vj10, _ = scf.hf.dot_eri_dm(eri_ab.T, dm[0], hermi=1, with_j=True, with_k=False) 221 | # ZHC NOTE explicit write down vj, without broadcast 222 | vj = np.asarray([[vj00, vj11], [vj01, vj10]]) 223 | vk = np.asarray([vk00, vk11]) 224 | return vj, vk 225 | 226 | def _get_veff(dm, eri): 227 | """ 228 | Get HF effective potential from rdm and ERI. 229 | """ 230 | dm = np.asarray(dm, dtype=np.double) 231 | if len(dm.shape) == 2: 232 | dm = dm[np.newaxis, ...] 233 | spin = dm.shape[0] 234 | vj, vk = _get_jk(dm, eri) 235 | if spin == 1: 236 | JK = vj - vk*0.5 237 | else: 238 | JK = vj[0] + vj[1] - vk 239 | return JK 240 | 241 | 242 | -------------------------------------------------------------------------------- /fcdmft/solver/ucc_eri.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | CC impurity solver. 4 | """ 5 | 6 | import numpy as np 7 | import scipy.linalg as la 8 | import h5py 9 | 10 | from pyscf import cc 11 | import time 12 | from pyscf import lib 13 | from pyscf import ao2mo 14 | from pyscf.cc.uccsd import _ChemistsERIs 15 | 16 | def _make_eris_incore(mycc, mo_coeff=None, ao2mofn=None): 17 | """ 18 | Hacked CC make eri function. NOTE the order. 19 | """ 20 | cput0 = (time.process_time(), time.perf_counter()) 21 | eris = _ChemistsERIs() 22 | eris._common_init_(mycc, mo_coeff) 23 | 24 | nocca, noccb = mycc.nocc 25 | nmoa, nmob = mycc.nmo 26 | nvira, nvirb = nmoa-nocca, nmob-noccb 27 | 28 | moa = eris.mo_coeff[0] 29 | mob = eris.mo_coeff[1] 30 | nmoa = moa.shape[1] 31 | nmob = mob.shape[1] 32 | 33 | if callable(ao2mofn): 34 | eri_aa = ao2mofn(moa).reshape([nmoa]*4) 35 | eri_bb = ao2mofn(mob).reshape([nmob]*4) 36 | eri_ab = ao2mofn((moa,moa,mob,mob)) 37 | else: 38 | # ZHC NOTE the order, aa, bb, ab 39 | #if len(self._scf._eri.shape) : 40 | # mycc._scf._eri = np.asarray((mycc._scf._eri, )*3) 41 | eri_aa = ao2mo.restore(1, ao2mo.full(mycc._scf._eri[0], moa), nmoa) 42 | eri_bb = ao2mo.restore(1, ao2mo.full(mycc._scf._eri[1], mob), nmob) 43 | eri_ab = ao2mo.general(mycc._scf._eri[2], (moa,moa,mob,mob), compact=False) 44 | eri_ba = eri_ab.reshape(nmoa,nmoa,nmob,nmob).transpose(2,3,0,1) 45 | 46 | eri_aa = eri_aa.reshape(nmoa,nmoa,nmoa,nmoa) 47 | eri_ab = eri_ab.reshape(nmoa,nmoa,nmob,nmob) 48 | eri_ba = eri_ba.reshape(nmob,nmob,nmoa,nmoa) 49 | eri_bb = eri_bb.reshape(nmob,nmob,nmob,nmob) 50 | eris.oooo = eri_aa[:nocca,:nocca,:nocca,:nocca].copy() 51 | eris.ovoo = eri_aa[:nocca,nocca:,:nocca,:nocca].copy() 52 | eris.ovov = eri_aa[:nocca,nocca:,:nocca,nocca:].copy() 53 | eris.oovv = eri_aa[:nocca,:nocca,nocca:,nocca:].copy() 54 | eris.ovvo = eri_aa[:nocca,nocca:,nocca:,:nocca].copy() 55 | eris.ovvv = eri_aa[:nocca,nocca:,nocca:,nocca:].copy() 56 | eris.vvvv = eri_aa[nocca:,nocca:,nocca:,nocca:].copy() 57 | eri_aa = None 58 | 59 | eris.OOOO = eri_bb[:noccb,:noccb,:noccb,:noccb].copy() 60 | eris.OVOO = eri_bb[:noccb,noccb:,:noccb,:noccb].copy() 61 | eris.OVOV = eri_bb[:noccb,noccb:,:noccb,noccb:].copy() 62 | eris.OOVV = eri_bb[:noccb,:noccb,noccb:,noccb:].copy() 63 | eris.OVVO = eri_bb[:noccb,noccb:,noccb:,:noccb].copy() 64 | eris.OVVV = eri_bb[:noccb,noccb:,noccb:,noccb:].copy() 65 | eris.VVVV = eri_bb[noccb:,noccb:,noccb:,noccb:].copy() 66 | eri_bb = None 67 | 68 | eris.ooOO = eri_ab[:nocca,:nocca,:noccb,:noccb].copy() 69 | eris.ovOO = eri_ab[:nocca,nocca:,:noccb,:noccb].copy() 70 | eris.ovOV = eri_ab[:nocca,nocca:,:noccb,noccb:].copy() 71 | eris.ooVV = eri_ab[:nocca,:nocca,noccb:,noccb:].copy() 72 | eris.ovVO = eri_ab[:nocca,nocca:,noccb:,:noccb].copy() 73 | eris.ovVV = eri_ab[:nocca,nocca:,noccb:,noccb:].copy() 74 | eris.vvVV = eri_ab[nocca:,nocca:,noccb:,noccb:].copy() 75 | 76 | #eris.OOoo = eri_ba[:noccb,:noccb,:nocca,:nocca].copy() 77 | eris.OVoo = eri_ba[:noccb,noccb:,:nocca,:nocca].copy() 78 | #eris.OVov = eri_ba[:noccb,noccb:,:nocca,nocca:].copy() 79 | eris.OOvv = eri_ba[:noccb,:noccb,nocca:,nocca:].copy() 80 | eris.OVvo = eri_ba[:noccb,noccb:,nocca:,:nocca].copy() 81 | eris.OVvv = eri_ba[:noccb,noccb:,nocca:,nocca:].copy() 82 | #eris.VVvv = eri_ba[noccb:,noccb:,nocca:,nocca:].copy() 83 | eri_ab = None 84 | 85 | if not callable(ao2mofn): 86 | ovvv = eris.ovvv.reshape(nocca*nvira,nvira,nvira) 87 | eris.ovvv = lib.pack_tril(ovvv).reshape(nocca,nvira,nvira*(nvira+1)//2) 88 | eris.vvvv = ao2mo.restore(4, eris.vvvv, nvira) 89 | 90 | OVVV = eris.OVVV.reshape(noccb*nvirb,nvirb,nvirb) 91 | eris.OVVV = lib.pack_tril(OVVV).reshape(noccb,nvirb,nvirb*(nvirb+1)//2) 92 | eris.VVVV = ao2mo.restore(4, eris.VVVV, nvirb) 93 | 94 | ovVV = eris.ovVV.reshape(nocca*nvira,nvirb,nvirb) 95 | eris.ovVV = lib.pack_tril(ovVV).reshape(nocca,nvira,nvirb*(nvirb+1)//2) 96 | vvVV = eris.vvVV.reshape(nvira**2,nvirb**2) 97 | idxa = np.tril_indices(nvira) 98 | idxb = np.tril_indices(nvirb) 99 | eris.vvVV = lib.take_2d(vvVV, idxa[0]*nvira+idxa[1], idxb[0]*nvirb+idxb[1]) 100 | 101 | OVvv = eris.OVvv.reshape(noccb*nvirb,nvira,nvira) 102 | eris.OVvv = lib.pack_tril(OVvv).reshape(noccb,nvirb,nvira*(nvira+1)//2) 103 | return eris 104 | 105 | cc.uccsd._make_eris_incore = _make_eris_incore 106 | -------------------------------------------------------------------------------- /fcdmft/utils/cholesky.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import math 3 | 4 | LINEAR_DEP_THR = 1e-12 5 | 6 | def mat_loop_2d(ni,nj, i0=0, istep=1, j0=0, jstep=1): 7 | 8 | '''Loop over the full 2d matrix 9 | ''' 10 | 11 | for i in range(i0, ni, istep): 12 | for j in range(j0, nj, jstep): 13 | yield i,j 14 | 15 | 16 | def get_eri_diag(eri): 17 | 18 | nao = eri.shape[0] 19 | out = np.empty([nao,nao], dtype=np.double) 20 | for mu in range(nao): 21 | for nu in range(nao): 22 | out[mu,nu] = eri[mu,nu,mu,nu] 23 | 24 | return out 25 | 26 | 27 | class AOPair(): 28 | 29 | def __init__(self,mu,nu): 30 | 31 | self.mu = mu 32 | self.nu = nu 33 | self.Bmask = 0 34 | self.Dmask = 0 35 | self.eri_diag = -999999.0 36 | self.eri_off_ioff = 0 37 | self.Lpq = None 38 | 39 | 40 | class AOPairs(): 41 | 42 | def __init__(self, nao): 43 | 44 | self.nao = nao 45 | self.n_aopairs = nao*nao 46 | self.eri_diag = None 47 | self.sorted_ind = None 48 | 49 | self.data = None 50 | 51 | def init_aopairs(self): 52 | 53 | ni = self.nao 54 | nj = self.nao 55 | 56 | self.data = np.empty([ni,nj], dtype = object) 57 | for i,j in mat_loop_2d(ni,nj): 58 | self.data[i,j] = AOPair(i,j) 59 | 60 | def get_eri_diag(self, eri): 61 | 62 | self.eri_diag = get_eri_diag(eri) 63 | for i, j in self.ijloop(): 64 | ao_pair = self.get_aopair(i,j) 65 | ao_pair.eri_diag = self.eri_diag[i,j] 66 | 67 | def print_eri_diag(self): 68 | for i, j in self.ijloop(): 69 | ao_pair = self.get_aopair(i,j) 70 | print(i,j, ao_pair.eri_diag) 71 | 72 | def get_aopair(self, i, j): 73 | 74 | return self.data[i,j] 75 | 76 | 77 | def ijloop(self): 78 | 79 | for i,j in mat_loop_2d(self.nao, self.nao): 80 | yield i,j 81 | 82 | def reorder_aopairs(self): 83 | 84 | max_diag_values = np.zeros([self.n_aopairs]) 85 | ind = 0 86 | for i,j in self.ijloop(): 87 | max_diag_values[ind] = self.eri_diag[i,j] 88 | ind += 1 89 | self.sorted_ind = np.argsort(-max_diag_values) 90 | 91 | def sorted_ijloop(self): 92 | 93 | if self.sorted_ind is None: 94 | self.reorder_aopairs() 95 | 96 | nj = self.nao 97 | for ind in self.sorted_ind: 98 | i = ind//nj 99 | j = ind % nj 100 | yield i,j 101 | 102 | def sorted_aopairs(self): 103 | 104 | for i, j in self.sorted_ijloop(): 105 | yield self.get_aopair(i,j) 106 | 107 | 108 | 109 | def make_eri_offdiag(self,eri, p_aopair_ind, q_aopair_ind): 110 | 111 | nij = p_aopair_ind.shape[0] 112 | nkl = q_aopair_ind.shape[0] 113 | out = np.empty([nij*nkl], dtype=np.double) 114 | ind = 0 115 | for ij in range(nij): 116 | i = p_aopair_ind[ij,0] 117 | j = p_aopair_ind[ij,1] 118 | for kl in range(nkl): 119 | k = q_aopair_ind[kl,0] 120 | l = q_aopair_ind[kl,1] 121 | out[ind] = eri[i,j,k,l] 122 | ind += 1 123 | 124 | return out, nij, nkl 125 | 126 | def make_eri_ao_aopair(self, eri, kl_ind): 127 | 128 | nao = self.nao 129 | nao_r = len(kl_ind) 130 | nao_l = nao * nao 131 | out = np.empty([nao_l, nao_r], dtype = np.double) 132 | ind = 0 133 | eri = eri.reshape(nao_l, nao, nao) 134 | for kl in kl_ind: 135 | k = kl[0] 136 | l = kl[1] 137 | out[:,ind] = eri[:,k,l] 138 | ind += 1 139 | 140 | return out, nao_l, nao_r 141 | 142 | 143 | class cholesky(): 144 | 145 | def __init__(self, eri, tau=1e-8, sigma=1e-2, dimQ=10): 146 | 147 | self.eri = eri 148 | self.tau = tau 149 | self.sigma = sigma 150 | self.dimQ = dimQ 151 | 152 | nao = self.eri.shape[0] 153 | self.ao_pairs = AOPairs(nao) 154 | self.ao_pairs.init_aopairs() 155 | 156 | def kernel(self): 157 | 158 | self.step1() 159 | return self.step2() 160 | 161 | def step1(self): 162 | 163 | self.ao_pairs.get_eri_diag(self.eri) 164 | 165 | it = 0 166 | while True: 167 | 168 | it += 1 169 | if it > 200: 170 | print("decomposition is likely to fail!!!!") 171 | exit() 172 | 173 | Dmax = 0.0 174 | D = [] 175 | Q = [] 176 | nq = 0 177 | ioff = 0 178 | self.ao_pairs.sorted_ind = None 179 | for ind, ao_pair in enumerate(self.ao_pairs.sorted_aopairs()): 180 | 181 | max_diag = ao_pair.eri_diag 182 | if max_diag < self.tau: 183 | ao_pair.Dmask = 0 184 | ao_pair.Lpq = None 185 | continue 186 | 187 | if ind == 0: Dmax = max_diag 188 | 189 | i = ao_pair.mu 190 | j = ao_pair.nu 191 | 192 | ao_pair.Dmask = 1 193 | D.append((i,j)) 194 | 195 | ao_pair.eri_off_ioff = ioff 196 | ioff += 1 197 | 198 | if nq < self.dimQ: 199 | if ao_pair.eri_diag > self.sigma * Dmax: 200 | ao_pair.Dmask = 2 201 | Q.append((i,j)) 202 | nq += 1 203 | 204 | if nq == 0: break 205 | p_aopair_ind = np.asarray(D) 206 | q_aopair_ind = np.asarray(Q) 207 | 208 | eri_offdiag, nao_ij, nao_kl = self.ao_pairs.make_eri_offdiag(self.eri, p_aopair_ind, q_aopair_ind) 209 | eri_offdiag = eri_offdiag.reshape((nao_ij, nao_kl)) 210 | 211 | tmp = [] 212 | for q in Q: 213 | i = q[0] 214 | j = q[1] 215 | ao_pair = self.ao_pairs.get_aopair(i,j) 216 | Dq = -999999.0 217 | if ao_pair.Dmask == 2: 218 | Dq = ao_pair.eri_diag 219 | tmp.append((i, j, 0, Dq)) 220 | 221 | tmp1=np.asarray(tmp)[:,-1] 222 | sorted_q = np.argsort(-tmp1) 223 | 224 | for p in D: 225 | ao_pair = self.ao_pairs.get_aopair(p[0],p[1]) 226 | if ao_pair.Lpq is None: continue 227 | ioff = ao_pair.eri_off_ioff 228 | size = 1 229 | 230 | LL = np.zeros([size, nao_kl], dtype=np.double) 231 | for ind, q in enumerate(tmp): 232 | if q[-1] < -9999: continue 233 | ao_pair_q = self.ao_pairs.get_aopair(q[0],q[1]) 234 | if ao_pair_q.Lpq is None: continue 235 | 236 | LL[:,ind:ind+1] = np.dot(ao_pair.Lpq, ao_pair_q.Lpq[q[2]:q[2]+1,:].T) 237 | eri_offdiag[ioff:ioff+size,:] -= LL 238 | 239 | 240 | Lpq = np.empty([nao_ij, nq], dtype=np.double) 241 | iq = 0 242 | for ind in sorted_q: 243 | q = tmp[ind] 244 | if q[-1] < -9999: break 245 | 246 | Mpq = eri_offdiag[:,ind] 247 | Mqq = -1.0 248 | ao_pair = self.ao_pairs.get_aopair(q[0],q[1]) 249 | ioff = ao_pair.eri_off_ioff 250 | q_left = ioff + q[2] 251 | Mqq = eri_offdiag[q_left,ind] 252 | 253 | Ltmp = np.zeros([nao_ij], dtype=np.double) 254 | for J in range(0,iq): 255 | Ltmp += Lpq[:,J] * Lpq[q_left, J] 256 | 257 | Lpq[:,iq] = (Mpq - Ltmp)/math.sqrt(Mqq) 258 | 259 | 260 | ao_pair.Dmask = 1 #remove q from Q 261 | ao_pair.Bmask = 1 262 | 263 | 264 | for p in D: 265 | ao_pair = self.ao_pairs.get_aopair(p[0],p[1]) 266 | ioff = ao_pair.eri_off_ioff 267 | size = 1 268 | ao_pair.eri_diag -= Lpq[ioff:ioff+size,iq]**2 269 | 270 | iq += 1 271 | 272 | for p in D: 273 | ao_pair = self.ao_pairs.get_aopair(p[0],p[1]) 274 | ioff = ao_pair.eri_off_ioff 275 | size = 1 276 | if ao_pair.Lpq is None: 277 | ao_pair.Lpq = Lpq[ioff:ioff+size,:].copy() 278 | else: 279 | ao_pair.Lpq = np.append(ao_pair.Lpq, Lpq[ioff:ioff+size,:], axis=1) 280 | 281 | 282 | def step2(self): 283 | 284 | aopair_ind = [] 285 | for i, j in self.ao_pairs.ijloop(): 286 | ao_pair = self.ao_pairs.get_aopair(i,j) 287 | if ao_pair.Bmask == 1: 288 | aopair_ind.append((i,j)) 289 | 290 | aopair_ind = np.asarray(aopair_ind) 291 | 292 | eri_S, nao_ij, nao_kl = self.ao_pairs.make_eri_offdiag(self.eri, aopair_ind, aopair_ind) 293 | eri_S = eri_S.reshape(nao_ij,nao_kl) 294 | eri_S_ao = eri_S 295 | 296 | try: 297 | L = np.linalg.cholesky(eri_S_ao) 298 | tag = 'cd' 299 | except np.linalg.LinAlgError: 300 | w, v = np.linalg.eigh(eri_S_ao) 301 | idx = w > LINEAR_DEP_THR 302 | L = v[:,idx]/np.sqrt(w[idx]) 303 | tag = 'eig' 304 | #print(w) 305 | 306 | if tag == 'cd': 307 | L = np.linalg.inv(L).T 308 | 309 | #print("L.shape = ", L.shape) 310 | #L = np.insert(L, insert_loc, 0.0, axis = 0) 311 | #print(L.shape) 312 | 313 | eri, nao_ij, nao_kl = self.ao_pairs.make_eri_ao_aopair(self.eri, aopair_ind) 314 | eri = eri.reshape(nao_ij, nao_kl) 315 | cderi = np.dot(eri,L).T 316 | 317 | return cderi 318 | 319 | -------------------------------------------------------------------------------- /fcdmft/utils/write.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def write_dos(filename, freqs, ldos, occupancy=None): 4 | spin = ldos.shape[0] 5 | if spin == 1: 6 | with open(filename+'.dat', 'w') as f: 7 | if occupancy: 8 | f.write('# n = %0.12g\n'%(occupancy)) 9 | for w,freq in enumerate(freqs): 10 | f.write('%0.12g %.12g\n'%(freq, ldos[0][w])) 11 | else: 12 | with open(filename+'.dat', 'w') as f: 13 | if occupancy: 14 | f.write('# n = %0.12g\n'%(occupancy)) 15 | for w,freq in enumerate(freqs): 16 | f.write('%0.12g %.12g %.12g %.12g\n'%(freq, ldos[0][w], ldos[1][w], 17 | 0.5*(ldos[0][w]+ldos[1][w]))) 18 | 19 | def write_gf_to_dos(filename, freqs, gf): 20 | ldos = -1./np.pi * np.trace(gf.imag,axis1=1,axis2=2) 21 | spin = ldos.shape[0] 22 | if spin == 1: 23 | with open(filename+'.dat', 'w') as f: 24 | for w,freq in enumerate(freqs): 25 | f.write('%0.12g %.12g\n'%(freq, ldos[0][w])) 26 | else: 27 | with open(filename+'.dat', 'w') as f: 28 | for w,freq in enumerate(freqs): 29 | f.write('%0.12g %.12g %.12g %.12g\n'%(freq, ldos[0][w], ldos[1][w], 30 | 0.5*(ldos[0][w]+ldos[1][w]))) 31 | 32 | def write_sigma(filename, freqs, sigma): 33 | sig = np.trace(sigma.imag,axis1=1,axis2=2) 34 | spin = sig.shape[0] 35 | if spin == 1: 36 | with open(filename+'.dat', 'w') as f: 37 | for w,freq in enumerate(freqs): 38 | f.write('%0.12g %.12g\n'%(freq, sig[0][w])) 39 | else: 40 | with open(filename+'.dat', 'w') as f: 41 | for w,freq in enumerate(freqs): 42 | f.write('%0.12g %.12g %.12g %.12g\n'%(freq, sig[0][w], sig[1][w], 43 | 0.5*(sig[0][w]+sig[1][w]))) 44 | 45 | def write_sigma_elem(filename, freqs, sigma): 46 | spin = sigma.shape[0] 47 | if spin == 1: 48 | with open(filename+'.dat', 'w') as f: 49 | for w,freq in enumerate(freqs): 50 | f.write('%0.12g %0.12g %.12g\n'%(freq, sigma[0][w].real, sigma[0][w].imag)) 51 | else: 52 | with open(filename+'.dat', 'w') as f: 53 | for w,freq in enumerate(freqs): 54 | f.write('%0.12g %0.12g %.12g %.12g %.12g\n'%(freq, sigma[0][w].real, sigma[0][w].imag, 55 | sigma[1][w].real, sigma[1][w].imag)) 56 | 57 | --------------------------------------------------------------------------------