Wannier localization

In [1]:
import pathlib

import numpy as np

from eminus import Atoms, read, SCF
from eminus.extras import view
from eminus.localizer import get_wannier, wannier_cost
from eminus.orbitals import FLO, SCDM, WO
from eminus.tools import orbital_center
In [2]:
# Run an initial calculation for methane
atoms = Atoms(*read("CH4.xyz"), ecut=15, center=True)
scf = SCF(atoms)
scf.run();
XYZ file comment: "Experimental geometry from CCCBDB: https://cccbdb.nist.gov/exp2x.asp?casno=74828&charge=0"
Start auto minimization...
Method  Iteration  Etot [Eh]    dEtot [Eh]   |Gradient|   
pccg           1   +18.061427   
pccg           2   +7.881450    -1.0180e+01  [+1.05e+04]  
pccg           3   +0.099586    -7.7819e+00  [+1.87e+03]  
pccg           4   -4.096798    -4.1964e+00  [+3.10e+02]  
pccg           5   -6.145812    -2.0490e+00  [+5.75e+01]  
pccg           6   -6.874551    -7.2874e-01  [+1.02e+01]  
pccg           7   -7.285784    -4.1123e-01  [+2.95e+00]  
pccg           8   -7.577908    -2.9212e-01  [+3.02e+00]  
pccg           9   -7.787117    -2.0921e-01  [+1.35e+00]  
pccg          10   -7.857628    -7.0510e-02  [+2.89e-01]  
pccg          11   -7.875693    -1.8065e-02  [+6.94e-02]  
pccg          12   -7.879606    -3.9137e-03  [+1.64e-02]  
pccg          13   -7.880591    -9.8423e-04  [+4.26e-03]  
pccg          14   -7.880813    -2.2256e-04  [+9.53e-04]  
pccg          15   -7.880863    -4.9429e-05  [+2.08e-04]  
pccg          16   -7.880875    -1.2336e-05  [+5.59e-05]  
pccg          17   -7.880877    -2.2595e-06  [+1.01e-05]  
pccg          18   -7.880878    -5.3147e-07  [+2.19e-06]  
pccg          19   -7.880878    -1.4534e-07  [+6.02e-07]  
pccg          20   -7.880878    -3.6440e-08  [+1.55e-07]  
SCF converged after 20 iterations.
Total SCF time: 5.91427 s
Etot = -7.880877999 Eh
In [3]:
# Calculate the SCDMs to have pre-localized orbitals
scdm = SCDM(scf)
In [4]:
# Do the Wannier localization
# The resulting orbitals are equivalent to Foster-Boys orbitals, but with periodic boundary conditions
wannier = get_wannier(atoms, scdm)
Wannier localizer converged after 15 iterations.
In [5]:
# Compare the initial SCDM spreads to the Wannier spreads
scdm_spreads = wannier_cost(atoms, scdm)
print(f"\nSCDM spreads = {scdm_spreads}")
print(f"SCDM spread = {np.sum(scdm_spreads)}")
Costs:
[2.60730048 2.66311286 2.71520219 2.76391029]

SCDM spreads = [[2.60730048 2.66311286 2.71520219 2.76391029]]
SCDM spread = 10.74952581703485
In [6]:
# The Wannier orbitals are a bit more localized, and all orbitals are evenly localized
wannier_spreads = wannier_cost(atoms, wannier)
print(f"Wannier spreads = {wannier_spreads}")
print(f"Wannier spread = {np.sum(wannier_spreads)}")
Costs:
[2.6820577  2.68206874 2.68206274 2.68211308]
Wannier spreads = [[2.6820577  2.68206874 2.68206274 2.68211308]]
Wannier spread = 10.728302260338978
In [7]:
# All of the above can be done with one function call, also save the orbitals
WO(scf, write_cubes=True);
Wannier localizer converged after 15 iterations.
Write CH4_WO_k0_0.cube...
Write CH4_WO_k0_1.cube...
Write CH4_WO_k0_2.cube...
Write CH4_WO_k0_3.cube...
In [8]:
# Display the orbitals from the cube files
view(pathlib.Path().glob("*.cube"));
interactive(children=(Dropdown(description='filename', options=('CH4_WO_k0_2.cube', 'CH4_WO_k0_1.cube', 'CH4_W…
In [9]:
# One could calculate the center of masses from the Wannier orbitals...
coms = orbital_center(atoms, wannier[0])
atoms.view(coms)
In [10]:
# ...and use them as an initial guess to create a set of FLOs
flo = FLO(scf, fods=coms)
flo_spreads = wannier_cost(atoms, flo)
print(f"FLO spreads = {flo_spreads}")
print(f"FLO spread = {np.sum(flo_spreads)}")
Costs:
[3.51558107 3.64121787 3.85319419 4.52338782]
FLO spreads = [[3.51558107 3.64121787 3.85319419 4.52338782]]
FLO spread = 15.533380945809824