Skip to content

Commit ddd559c

Browse files
authored
Merge pull request #13 from DerWeh/fcc
Add face-centered cubic (fcc) lattice Gf and DOS
2 parents f02a91b + aab2535 commit ddd559c

File tree

4 files changed

+405
-1
lines changed

4 files changed

+405
-1
lines changed

gftool/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@
9898
gf_z as bcc_gf_z,
9999
hilbert_transform as bcc_hilbert_transform)
100100

101+
# Body-centered cubic lattice
102+
from .lattice.fcc import (dos as fcc_dos,
103+
dos_moment as fcc_dos_moment,
104+
gf_z as fcc_gf_z,
105+
hilbert_transform as fcc_hilbert_transform)
106+
101107
# Fermi statistics
102108
from .statistics import (fermi_fct, fermi_fct_d1, fermi_fct_inv,
103109
matsubara_frequencies, pade_frequencies)
@@ -125,6 +131,7 @@
125131
assert all((triangular_dos, triangular_dos_moment, triangular_gf_z, triangular_hilbert_transform))
126132
assert all((sc_dos, sc_dos_moment, sc_gf_z, sc_hilbert_transform))
127133
assert all((bcc_dos, bcc_dos_moment, bcc_gf_z, bcc_hilbert_transform))
134+
assert all((fcc_dos, fcc_dos_moment, fcc_gf_z, fcc_hilbert_transform))
128135
assert all((fermi_fct, fermi_fct_d1, fermi_fct_inv, matsubara_frequencies, pade_frequencies))
129136
assert all((bose_fct, matsubara_frequencies_b))
130137
assert all((pole_gf_z, pole_gf_d1_z, pole_gf_tau, pole_gf_ret_t, pole_gf_moments))

gftool/lattice/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@
1717
kagome
1818
sc
1919
bcc
20+
fcc
2021
2122
"""
22-
from . import (bcc, bethe, bethez, honeycomb, kagome, lieb, onedim,
23+
from . import (bcc, bethe, bethez, fcc, honeycomb, kagome, lieb, onedim,
2324
rectangular, sc, square, triangular)
2425

2526
# silence warnings of unused imports
2627
assert bcc
2728
assert bethe
2829
assert bethez
30+
assert fcc
2931
assert honeycomb
3032
assert kagome
3133
assert lieb

gftool/lattice/fcc.py

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
r"""3D face-centered cubic (fcc) lattice.
2+
3+
The dispersion of the 3D face-centered cubic lattice is given by
4+
5+
.. math::
6+
7+
ϵ_{k_x,k_y,k_z} = 4t [\cos(k_x/2)\cos(k_y/2) + \cos(k_x/2)\cos(k_z/2) + \cos(k_y/2) \cos(k_z/2)]
8+
9+
which takes values in :math:`ϵ_{k_x, k_y, k_z} ∈ [-4t, +12t] = [-0.5D, +1.5D]`.
10+
11+
:half_bandwidth: The half_bandwidth corresponds to a nearest neighbor hopping
12+
of `t=D/8`.
13+
14+
"""
15+
import numpy as np
16+
17+
from mpmath import mp
18+
19+
from gftool._util import _u_ellipk
20+
21+
22+
def _signed_sqrt(z):
23+
"""Square root with correct sign for fcc lattice."""
24+
# sign = np.where((z.real < 0) & (z.imag < 0), -1, 1)
25+
sign = np.where(z.real < 0, -1, 1)
26+
factor = np.where(sign == 1, 1, -1j)
27+
return factor * np.lib.scimath.sqrt(sign*z)
28+
29+
30+
def gf_z(z, half_bandwidth):
31+
r"""Local Green's function of the 3D face-centered cubic (fcc) lattice.
32+
33+
Note, that the spectrum is asymmetric and in :math:`[-D/2, 3D/2]`,
34+
where :math:`D` is the half-bandwidth.
35+
36+
Has a van Hove singularity at `z=-half_bandwidth/2` (divergence) and at
37+
`z=0` (continuous but not differentiable).
38+
39+
Implements equations (2.16), (2.17) and (2.11) from [morita1971]_.
40+
41+
Parameters
42+
----------
43+
z : complex np.ndarray or complex
44+
Green's function is evaluated at complex frequency `z`.
45+
half_bandwidth : float
46+
Half-bandwidth of the DOS of the face-centered cubic lattice.
47+
The `half_bandwidth` corresponds to the nearest neighbor hopping `t=D/8`.
48+
49+
Returns
50+
-------
51+
gf_z : complex np.ndarray or complex
52+
Value of the face-centered cubic lattice Green's function
53+
54+
References
55+
----------
56+
.. [morita1971] Morita, T., Horiguchi, T., 1971. Calculation of the Lattice
57+
Green’s Function for the bcc, fcc, and Rectangular Lattices. Journal of
58+
Mathematical Physics 12, 986–992. https://doi.org/10.1063/1.1665693
59+
60+
Examples
61+
--------
62+
>>> ww = np.linspace(-1.6, 1.6, num=501, dtype=complex)
63+
>>> gf_ww = gt.lattice.fcc.gf_z(ww, half_bandwidth=1)
64+
65+
>>> import matplotlib.pyplot as plt
66+
>>> _ = plt.axvline(-0.5, color='black', linewidth=0.8)
67+
>>> _ = plt.axvline(0, color='black', linewidth=0.8)
68+
>>> _ = plt.axhline(0, color='black', linewidth=0.8)
69+
>>> _ = plt.plot(ww.real, gf_ww.real, label=r"$\Re G$")
70+
>>> _ = plt.plot(ww.real, gf_ww.imag, '--', label=r"$\Im G$")
71+
>>> _ = plt.ylabel(r"$G*D$")
72+
>>> _ = plt.xlabel(r"$\omega/D$")
73+
>>> _ = plt.xlim(left=ww.real.min(), right=ww.real.max())
74+
>>> _ = plt.legend()
75+
>>> plt.show()
76+
77+
"""
78+
D = half_bandwidth / 2
79+
z = np.asarray(1 / D * z)
80+
retarded = z.imag > 0
81+
z = np.where(retarded, np.conj(z), z) # calculate advanced only, and use symmetry
82+
zp1 = z + 1
83+
zp1_pow = _signed_sqrt(zp1)**-3
84+
sum1 = 4 * _signed_sqrt(z) * zp1_pow
85+
sum2 = (z - 1) * _signed_sqrt(z - 3) * zp1_pow
86+
m_p = 0.5*(1 + sum1 - sum2) # eq. (2.11)
87+
m_m = 0.5*(1 - sum1 - sum2) # eq. (2.11)
88+
kii = np.asarray(_u_ellipk(m_p))
89+
kii[m_p.imag < 0] += 2j*_u_ellipk(1 - m_p[m_p.imag < 0]) # eq (2.17)
90+
gf = 4 / (np.pi**2 * D * zp1) * _u_ellipk(m_m) * kii # eq (2.16)
91+
return np.where(retarded, np.conj(gf), gf) # return to retarded by symmetry
92+
93+
94+
def hilbert_transform(xi, half_bandwidth):
95+
r"""Hilbert transform of non-interacting DOS of the face-centered cubic lattice.
96+
97+
The Hilbert transform is defined
98+
99+
.. math:: \tilde{D}(ξ) = ∫_{-∞}^{∞}dϵ \frac{DOS(ϵ)}{ξ − ϵ}
100+
101+
The lattice Hilbert transform is the same as the non-interacting Green's
102+
function.
103+
104+
Parameters
105+
----------
106+
xi : complex np.ndarray or complex
107+
Point at which the Hilbert transform is evaluated
108+
half_bandwidth : float
109+
half-bandwidth of the DOS of the 3D face-centered cubic lattice
110+
111+
Returns
112+
-------
113+
hilbert_transform : complex np.ndarray or complex
114+
Hilbert transform of `xi`.
115+
116+
Notes
117+
-----
118+
Relation between nearest neighbor hopping `t` and half-bandwidth `D`
119+
120+
.. math:: 8t = D
121+
122+
See Also
123+
--------
124+
gftool.lattice.fcc.gf_z
125+
126+
"""
127+
return gf_z(xi, half_bandwidth)
128+
129+
130+
def dos(eps, half_bandwidth):
131+
r"""DOS of non-interacting 3D face-centered cubic lattice.
132+
133+
Has a van Hove singularity at `z=-half_bandwidth/2` (divergence) and at
134+
`z=0` (continuous but not differentiable).
135+
136+
Parameters
137+
----------
138+
eps : float np.ndarray or float
139+
DOS is evaluated at points `eps`.
140+
half_bandwidth : float
141+
Half-bandwidth of the DOS, DOS(`eps` < -0.5*`half_bandwidth`) = 0,
142+
DOS(1.5*`half_bandwidth` < `eps`) = 0.
143+
The `half_bandwidth` corresponds to the nearest neighbor hopping `t=D/8`
144+
145+
Returns
146+
-------
147+
dos : float np.ndarray or float
148+
The value of the DOS.
149+
150+
See Also
151+
--------
152+
gftool.lattice.fcc.dos_mp : multi-precision version suitable for integration
153+
154+
References
155+
----------
156+
.. [morita1971] Morita, T., Horiguchi, T., 1971. Calculation of the Lattice
157+
Green’s Function for the bcc, fcc, and Rectangular Lattices. Journal of
158+
Mathematical Physics 12, 986–992. https://doi.org/10.1063/1.1665693
159+
160+
Examples
161+
--------
162+
>>> eps = np.linspace(-1.6, 1.6, num=501)
163+
>>> dos = gt.lattice.fcc.dos(eps, half_bandwidth=1)
164+
165+
>>> import matplotlib.pyplot as plt
166+
>>> _ = plt.axvline(0, color='black', linewidth=0.8)
167+
>>> _ = plt.axvline(-0.5, color='black', linewidth=0.8)
168+
>>> _ = plt.plot(eps, dos)
169+
>>> _ = plt.xlabel(r"$\epsilon/D$")
170+
>>> _ = plt.ylabel(r"DOS * $D$")
171+
>>> _ = plt.ylim(bottom=0)
172+
>>> _ = plt.xlim(left=eps.min(), right=eps.max())
173+
>>> plt.show()
174+
175+
"""
176+
eps = np.asarray(eps)
177+
singular = eps == -0.5*half_bandwidth
178+
finite = (-0.5*half_bandwidth < eps) & (eps < 1.5*half_bandwidth) & ~singular
179+
dos_ = np.zeros_like(eps)
180+
dos_[finite] = 1 / np.pi * gf_z(eps[finite], half_bandwidth=half_bandwidth).imag
181+
dos_[singular] = np.infty
182+
return abs(dos_) # at 0.5D wrong sign
183+
184+
185+
# ∫dϵ ϵ^m DOS(ϵ) for half-bandwidth D=1
186+
# from: integral of dos_mp with mp.workdps(100)
187+
# for m in range(0, 21, 1):
188+
# with mp.workdps(100):
189+
# print(mp.quad(lambda eps: eps**m * dos_mp(eps), [mp.mpf('-0.5'), 0, 1])
190+
# rational numbers obtained by mp.identify
191+
dos_moment_coefficients = {
192+
0: 1,
193+
1: 0,
194+
2: 3/16,
195+
3: 3/32,
196+
4: 135/1024,
197+
5: 135/1024,
198+
6: 0.1611328125,
199+
7: 0.1922607421875,
200+
8: 0.24070143699646,
201+
9: 0.305163860321045,
202+
10: 0.394462153315544,
203+
11: 0.516299419105052,
204+
12: 0.683690124191343,
205+
13: 0.913928582333027,
206+
14: 1.23181895411108,
207+
15: 1.67210463207448,
208+
16: 2.28395076888283,
209+
17: 3.13686893977359,
210+
18: 4.32941325997849,
211+
19: 6.00152324929046,
212+
20: 8.35226611969712,
213+
}
214+
215+
216+
def dos_moment(m, half_bandwidth):
217+
"""Calculate the `m` th moment of the face-centered cubic DOS.
218+
219+
The moments are defined as :math:`∫dϵ ϵ^m DOS(ϵ)`.
220+
221+
Parameters
222+
----------
223+
m : int
224+
The order of the moment.
225+
half_bandwidth : float
226+
Half-bandwidth of the DOS of the 3D face-centered cubic lattice.
227+
228+
Returns
229+
-------
230+
dos_moment : float
231+
The `m` th moment of the 3D face-centered cubic DOS.
232+
233+
Raises
234+
------
235+
NotImplementedError
236+
Currently only implemented for a few specific moments `m`.
237+
238+
See Also
239+
--------
240+
gftool.lattice.fcc.dos
241+
242+
"""
243+
try:
244+
return dos_moment_coefficients[m] * half_bandwidth**m
245+
except KeyError as keyerr:
246+
raise NotImplementedError('Calculation of arbitrary moments not implemented.') from keyerr
247+
248+
249+
def _signed_mp_sqrt(eps):
250+
"""Square root with correct sign for fcc lattice."""
251+
if eps >= 0:
252+
return mp.sqrt(eps)
253+
return -1j*mp.sqrt(-eps)
254+
255+
256+
def dos_mp(eps, half_bandwidth=1):
257+
r"""Multi-precision DOS of non-interacting 3D face-centered cubic lattice.
258+
259+
Has a van Hove singularity at `z=-half_bandwidth/2` (divergence) and at
260+
`z=0` (continuous but not differentiable).
261+
262+
This function is particularity suited to calculate integrals of the form
263+
:math:`∫dϵ DOS(ϵ)f(ϵ)`. If you have problems with the convergence,
264+
consider using :math:`∫dϵ DOS(ϵ)[f(ϵ)-f(-1/2)] + f(-1/2)` to avoid the
265+
singularity.
266+
267+
Parameters
268+
----------
269+
eps : mpmath.mpf or mpf_like
270+
DOS is evaluated at points `eps`.
271+
half_bandwidth : mpmath.mpf or mpf_like
272+
Half-bandwidth of the DOS, DOS(`eps` < -0.5*`half_bandwidth`) = 0,
273+
DOS(1.5*`half_bandwidth` < `eps`) = 0.
274+
The `half_bandwidth` corresponds to the nearest neighbor hopping `t=D/8`
275+
276+
Returns
277+
-------
278+
dos_mp : mpmath.mpf
279+
The value of the DOS.
280+
281+
See Also
282+
--------
283+
gftool.lattice.fcc.dos : vectorized version suitable for array evaluations
284+
285+
References
286+
----------
287+
.. [morita1971] Morita, T., Horiguchi, T., 1971. Calculation of the Lattice
288+
Green’s Function for the bcc, fcc, and Rectangular Lattices. Journal of
289+
Mathematical Physics 12, 986–992. https://doi.org/10.1063/1.1665693
290+
291+
Examples
292+
--------
293+
Calculate integrals:
294+
295+
>>> from mpmath import mp
296+
>>> unit = mp.quad(gt.lattice.fcc.dos_mp, [-0.5, 0, 1.5])
297+
>>> mp.identify(unit)
298+
'1'
299+
300+
>>> eps = np.linspace(-1.6, 1.6, num=501)
301+
>>> dos_mp = [gt.lattice.fcc.dos_mp(ee, half_bandwidth=1) for ee in eps]
302+
>>> dos_mp = np.array(dos_mp, dtype=np.float64)
303+
304+
>>> import matplotlib.pyplot as plt
305+
>>> _ = plt.axvline(0, color='black', linewidth=0.8)
306+
>>> _ = plt.axvline(-0.5, color='black', linewidth=0.8)
307+
>>> _ = plt.plot(eps, dos_mp)
308+
>>> _ = plt.xlabel(r"$\epsilon/D$")
309+
>>> _ = plt.ylabel(r"DOS * $D$")
310+
>>> _ = plt.ylim(bottom=0)
311+
>>> _ = plt.xlim(left=eps.min(), right=eps.max())
312+
>>> plt.show()
313+
314+
"""
315+
D = mp.mpf(half_bandwidth) * mp.mpf('1/2')
316+
eps = mp.mpf(eps) / D
317+
if 3 < eps < -1:
318+
return mp.mpf('0')
319+
if eps == -1:
320+
return mp.inf
321+
epsp1 = eps + 1
322+
epsp1_pow = _signed_mp_sqrt(epsp1)**-3
323+
sum1 = 4 * _signed_mp_sqrt(eps) * epsp1_pow
324+
sum2 = (eps - 1) * _signed_mp_sqrt(eps - 3) * epsp1_pow
325+
m_p = mp.mpf('0.5')*(1 + sum1 - sum2) # eq. (2.11)
326+
m_m = mp.mpf('0.5')*(1 - sum1 - sum2) # eq. (2.11)
327+
kii = mp.ellipk(m_p)
328+
if m_p.imag < 0:
329+
kii += 2j * mp.ellipk(1 - m_p) # eq (2.17)
330+
return abs(4 / (mp.pi**3 * D * epsp1) * (_u_ellipk(m_m) * kii).imag) # eq (2.16)

0 commit comments

Comments
 (0)