Source code for exponential fit of Green function

Source code for exponential fit of Green function#

\(\phantom{\xi}\)

Listing 2 Content of file fit_green_tau.py containing the source code used to fit the time-displaced Green function with an exponential decay for determining the single particle gap. See also Section 2.7.2.1 for a derivation of the exponential decay and Appendix A.2.2.10 demonstrates how to apply the fitting function.#
  1"""Fit time-displaced Green function exponentially to determine
  2single particle gap."""
  3# pylint: disable=invalid-name
  4
  5import numpy as np
  6from scipy.optimize import curve_fit
  7
  8
  9def fit_green_tau(taus, G, dG, plot=False):
 10    """Fit time-displaced Green function exponentially to determine
 11    single particle gap.
 12
 13    Parameters
 14    ----------
 15    taus : array-like object
 16        Tau values.
 17    G : array-like object
 18        Green function values
 19    dG : array-like object
 20        Standard errors of Green functions
 21    plot : bool, default=False
 22        Plot G(tau).
 23
 24    Returns
 25    -------
 26    gap, dgap : floats
 27        Single particle gap and standard error.
 28    """
 29    N_tau = len(G)
 30
 31    def func1(x, a, b):
 32        return a*np.exp(-b*x)
 33
 34    def func2(x, a, b):
 35        return a*np.exp(b*(x-taus[-1]))
 36
 37    if plot:
 38        import matplotlib.pyplot as plt
 39        fig, (ax, ax2) = plt.subplots(1, 2, figsize=(14, 5))
 40        ax.errorbar(x=taus, y=G, yerr=dG)
 41        ax.set_yscale('log')
 42
 43    W = N_tau//2
 44    for i in range(N_tau//2):
 45        if G[i]-dG[i] < 1E-7:
 46            W = i
 47            # print(i)
 48            break
 49    i1 = W//2 - 5
 50    i2 = i1+10
 51    popt, pcov, i1_out, i2_out, chi_squared, reduced_chi_squared, accepted = \
 52        try_fit(func1, taus, G, dG, i1, i2)
 53    gap1  = popt[1]
 54    dgap1 = np.sqrt(pcov[1,1])
 55    if plot:
 56        # print('left')
 57        ax.errorbar(
 58            x=taus[i1_out:i2_out],
 59            y=G[i1_out:i2_out],
 60            yerr=dG[i1_out:i2_out]
 61            )
 62        title = f'{accepted} {i1_out} {i2_out} {i2_out-i1_out} {reduced_chi_squared}'
 63        ax2.errorbar(x=[1], y=[gap1], yerr=[dgap1])
 64
 65    W = N_tau//2
 66    for i in range(N_tau//2):
 67        if G[-i]-dG[-i] < 1E-7:
 68            W = i
 69            # print(i)
 70            break
 71    i2 = N_tau-W//2+5
 72    i1 = i2-10
 73    out = try_fit(func2, taus, G, dG, i1, i2)
 74    # print(out)
 75    if out is not None:
 76        popt, pcov, i1_out, i2_out, chi_squared, reduced_chi_squared, accepted = out
 77        gap2  = popt[1]
 78        dgap2 = np.sqrt(pcov[1,1])
 79    gap  = np.mean([gap1, gap2])
 80    dgap = (np.max([gap1+dgap1, gap2+dgap2]) - np.min([gap1-dgap1, gap2-dgap2]))/2
 81    if plot:
 82        # print('right')
 83        ax.errorbar(x=taus[i1_out:i2_out],
 84                    y=G[i1_out:i2_out],
 85                    yerr=dG[i1_out:i2_out])
 86        ax.set_title(f'{title}\n{accepted} {i1_out} {i2_out} ' +
 87                     f'{i2_out-i1_out} {reduced_chi_squared}')
 88        ax2.errorbar(x=[2], y=[gap2], yerr=[dgap2])
 89        ax2.errorbar(x=[3], y=[gap], yerr=[dgap])
 90        plt.show()
 91        plt.close()
 92
 93    return gap, dgap
 94
 95
 96def try_fit(func, x, y, dy, i1, i2, popt=None):
 97    """Fit function func to data x[i1:i2], y[i1:i2], dy[i1:i2] and alternately
 98    decrease i1 and increase i2 until some standards for the quality of fit are
 99    no more fulfilled."""
100    this_side = 1
101    side1_fin = False
102    side2_fin = False
103    accepted = False
104
105    while True:
106        if i1 < 0 or i2 >= len(y):
107            return popt, pcov, i1, i2, chi_squared, reduced_chi_squared, accepted
108        try:
109            popt, pcov = curve_fit(func, x[i1:i2], y[i1:i2], sigma=dy[i1:i2],
110                                   absolute_sigma=True, p0=popt)
111        except RuntimeError as curve_fit_failed:
112            try:
113                return popt, pcov, i1, i2, chi_squared, reduced_chi_squared, accepted
114            except NameError:
115                raise Exception("curve_fit did not converge") from curve_fit_failed
116
117        # ================== Estimate quality of fit ==================
118        chi_squared = np.sum(((func(x[i1:i2], *popt)-y[i1:i2])/dy[i1:i2])**2)
119        reduced_chi_squared = (chi_squared)/(len(x[i1:i2])-len(popt))
120
121        n_ges  = 0
122        n_in_h = 0
123        n_in   = 0
124        n_in_4 = 0
125        n_end1 = 0
126        n_end2 = 0
127
128        for i in range(i1, i2):
129            n_ges = n_ges + 1
130            if abs( func(x[i], *popt) - y[i] ) < 0.7 * dy[i]:
131                n_in_h = n_in_h + 1
132            if abs( func(x[i], *popt) - y[i] ) < 4 * dy[i]:
133                n_in_4 = n_in_4 + 1
134            if abs( func(x[i], *popt) - y[i] ) < dy[i]:
135                n_in = n_in + 1
136
137        for i in range(i1,i1+3):
138            if abs( func(x[i], *popt) - y[i] ) < 2*dy[i]:
139                n_end1 = n_end1 + 1
140
141        for i in range(i2-3,i2):
142            if abs( func(x[i], *popt) - y[i] ) < 2*dy[i]:
143                n_end2 = n_end2 + 1
144
145        ratio_h = n_in_h / n_ges
146        ratio   = n_in / n_ges
147        ratio_4 = n_in_4 / n_ges
148
149        # line = "Fitting from {} to {}: {:5.2f} {:5.2f} "
150        #        "{} {:5.2f} {:5.2f} {:5.2f} {} {}"
151        # line = line.format(i1, i2, chi_squared, reduced_chi_squared,
152        #                    n_ges, ratio_h, ratio, ratio_4, n_end1, n_end2)
153        # print(line)
154
155        # ====== Set minimal standard for quality of fit ======
156        success = ((ratio > 0.68) and (ratio_4 > 0.99)
157                   and (n_end1 > 1) and (n_end2 > 1))
158
159        if not success:
160            # print('rejected!')
161            if this_side == 1:
162                side1_fin = True
163                i1 = i1 + 1
164            elif this_side == 2:
165                side2_fin = True
166                i2 = i2 - 1
167        else:
168            # print('accepted')
169            accepted = True
170
171        if i1 == 0:
172            side1_fin = True
173        if i2 == len(x)-1:
174            side2_fin = True
175
176        if this_side == 1 and side2_fin is False:
177            i2 = i2+1
178            this_side = 2
179        elif this_side == 2 and side1_fin is False:
180            i1 = i1-1
181            this_side = 1
182        elif side2_fin is False:
183            i2 = i2+2
184            this_side = 2
185        elif side1_fin is False:
186            i1 = i1-1
187            this_side = 1
188        else:
189            return popt, pcov, i1, i2, chi_squared, reduced_chi_squared, accepted