Hi Sergey,
For method 2, I'm assuming you do a shift-invert around each energy.
If you need the LDOS for every site, then this is a good method.
If you want just a few elements, you can calculate the Green's function as
a continued fraction from the Lanczos tridiagonalization.
https://www.cond-mat.de/events/correl11/manuscript/Koch.pdf
This is an iterative method like KPM but I find that this method converges
faster sometimes.
If you're not happy with the energy resolution of KPM, you may have the
same issue here.
I sometimes need many thousands of iterations.
Here is my implementation.
I don't have any leads in my system.
def lanczos(H, i, N):
"""Calculate Lanczos tridiagonalization for the starting vector |i>.
Algorithm from
https://www.cond-mat.de/events/correl11/manuscript/Koch.pdf
Parameters
----------
H : Sparse array
Hamiltonian.
i : int
Starting element.
N : int
Number of iterations.
Returns
-------
a, b : array(N)
Tridiagonal elements.
"""
b = np.zeros(N)
a = np.zeros(N)
v = np.zeros(H.shape[0], H.dtype)
v[i] = 1
Hv = H.dot(v)
a[0] = Hv[i].real # a_0 = v.H.v
w = Hv - a[0]*v # w = H |v_0> - a_0 |v_0>
for n in range(1, N):
b[n] = np.linalg.norm(w)
w /= b[n] # w = |v_n>
v *= -b[n] # v = -b_n |v_{n-1}>
v, w = w, v # swap(v, w)
w += H.dot(v) # w = H |v_n> - b_n |v_{n-1}>
a[n] = np.vdot(v, w).real # a_n = <v_n|H|v_n> - b_n <v_n|v_{n-1}>
w -= a[n]*v # w = b_{n+1} |v_{n+1}>
return a, b
def greens_function(z, a, b):
"""Calculate the Green's function G = (z-H)^{-1} from Lanczos
tridiagonalization.
Algorithm from
https://www.cond-mat.de/events/correl11/manuscript/Koch.pdf
Parameters
----------
z : array(nz)
Energies to evaluate.
For retarded Green's function, z = ω+iδ.
a, b : array(N)
Tridiagonal elements.
Returns
-------
G : array(nz)
Green's function.
"""
G = z - a[-1]
for n in range(len(a)-2, -1, -1):
G = z - a[n] - b[n+1]**2/G
return 1 / G
a, b = lanczos(H, 0, 500)
omega = np.linspace(-4, 4, 401)
G = greens_function(omega+0.1j, a, b)
ldos = -G.imag/np.pi
Thanks,
Eric
On Thu, Aug 20, 2020 at 10:48 AM Sergey Slizovskiy <[email protected]> wrote:
> Dear Colleagues, (sorry, this is a repost of the same question to a new
> thread)
>
> I have a taks to compute local density of states at a single point for a
> large homogeneous sample with narrow STS tip potential and in magnetic
> field. I have tried several methods in Kwant to do this:
>
> 1) Naive diagonalization of H for system with no leads and direct
> calculation of LDOS. It works, but not for large systems, so, I see strong
> finite-size effects.
>
> 2) Sparse Lanczos method with several eigenvalues/functions extracted
> near each point in the grid of energy values.
>
> 3) KPM method. I am not happy with the energy resolution I can get with
> it.
>
> 4) Take a smaller main sample (with the size of the tip potential), but
> avoid the finite-size effects by attaching a lot of leads pointing in ,
> e.g. 6 directions. Then, use the kwant ldos function to scan LDOS over
> a grid of energies. Use magnetic_gauge to implement B field in both sample
> and scattering region.
>
> I see the last possibility as the quickest, but it gives me something
> that seems wrong to me.
>
> Does kwant ldos function work correctly if all the leads are insulating
> for a given energy? Does it correctly catch the localized states in the
> scattering region?
>
> Thank you for any feedback,
>
> Sergey
>
>
> Below is a copy of a reply by Pablo Piskunow
>
> Dear Sergey,
>
> In terms of scaling, the most convenient is the KPM method, since it scales
> linearly with the
> inverse energy resolution (that is, the number of moments), and linearly with
> the system size.
>
> Could you clarify what is the issue with the approach 3)?
>
> I am not sure what is the energy resolution that you want to achieve. You can
> conveniently set
> this value when creating a `kwant.kpm.SpectralDensity` instance via the
> arguments
> `energy_resolution` or (exclusive) `num_moments`. The latter is related to
> the energy resoution
> by `\delta \pi / N`, where `\delta` is the full width of the spectrum, and
> `N` the size of your system.
> In the case of a 2D, that will be similar to `L^2`, where `L` is the linear
> size.
>
> Furthermore, you could progressively increase the energy resolution by adding
> moments:
> ```
> spectrum = kwant.kpm.SpectralDensity(...)
> spectrum.add_moments(...)
> ```
>
> If you want to resolve individual energy levels then the approach 1) and 3)
> will have the same scaling,
> however, the KPM method will not give you eigenvalues.
>
> Finally, since you want to get the LDOS at a single site or a small set of
> sites near the STS tip, you should
> use the `kwant.kpm.LocalVectors` vector factory, setting the `where` argument
> to the desired spot.
>
> ```
> def center(site):
> return site.tag == (0, 0)
> vector_factory = kwant.kpm.LocalVectors(syst, where=center)
> spectrum = kwant.kpm.SpectralDensity(syst, num_moments=100, num_vectors=None,
> vector_factory=vector_factory)
> ```
> Note that `num_vectors=None` will let the `kwant.kpm` module to figure out
> how many vectors does the vector factory produce.
> Otherwise, it should be at most the **total number of orbitals** defined by
> the `where` function, that is, sites times orbitals per site.
>
> As you can see, the `kwant.kpm` module is the most suited for this problem,
> specially when the sample is very large.
> I hope this helps.
>
> Best regards,
> Pablo
>
>
>
> n the meantime, you can create a `spectrum` instance, and evaluate the KPM
> expansion at any energy,
> for example by
>
> ```
> spectrum = kwant.kpm.SpectralDensity(...)
> energy_array = np.linspace(0, 1, 200)
> density_array = spectrum(energy_array)
> ```
>
>
> Another note, if the Hamiltonian is huge, you will save some overhead by
> passing the bounds of the spectrum explicitly:
> `bounds=(e_min, e_max)`. These values must be strictly below and above the
> edges of the spectrum, otherwise
> the KPM expansion diverges.
>
>
> ah, I forgot.
>
> The automatic energy points (`spectrum.energies`) are fixed once you fix
> the bounds of the spectrum.
> So if for a range of magnetic fields you pass the same bounds, then the
> energies will be at the same
> points and so the densities will be easier to plot or compare.
>
> The width of the spectrum might change for different values of the
> magnetic field; and even if not, there is
> small randomness associated with finding the bounds, that you can get rid
> of by passing a seed to the
> random number generator like `rng=0`. If you choose to provide the bounds
> explicitly, you can disregard
> this random number generator stuff.
>
>
>
>