Coverage for tcvx21/analysis/fit_exp_decay_m.py: 95%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Routines to fit exponential decay functions to observables
3"""
4import numpy as np
5import matplotlib.pyplot as plt
6from scipy.optimize import curve_fit
7from tcvx21 import Quantity
10def exponential_decay(x, amplitude, decay_rate):
11 """A curve given simple exponential decay"""
12 return amplitude * np.exp(-x/decay_rate)
15def linear_fit(x, intercept, gradient):
16 return gradient * x + intercept
18# K, A_log = np.polyfit(position, np.log(values), 1)
19# A = np.exp(A_log)
20# return A, K
23def fit_exponential_decay(position, values, errors=None, fit_range=(-np.inf, np.inf), plot='', fit_logarithm=False):
24 """
25 Fit an exponential decay rate to some observable
26 If you supply plot as a non-empty string, then this will be used to label a fit of the plot
27 fit_range should be given as (min, max), in cm
29 If supplying errors, use these in an absolute sense (i.e. don't rescale the errors to calculate a relative weighting
30 of the points -- use the errors as directly setting the uncertainty of the fit)
31 """
33 position = position.to('cm').magnitude
35 if errors is None:
36 errors = np.ones_like(position)
37 has_errors = False
38 elif fit_logarithm:
39 raise NotImplementedError("No implementation for fit_logarithm with errors")
40 else:
41 errors = errors.to(values.units).magnitude
42 has_errors = True
44 mask = np.logical_and.reduce((position > fit_range[0], position < fit_range[1],
45 ~np.isnan(values), ~np.isnan(errors)))
47 units = values.units
48 values = values.magnitude[mask]
49 position = position[mask]
50 errors = errors[mask]
52 if len(position) < 3:
53 return (None, None), (None, None)
55 if fit_logarithm:
56 popt, pcov = curve_fit(linear_fit, xdata=position, ydata=np.log(values))
57 perr = np.sqrt(np.diag(pcov))
59 amplitude = np.exp(popt[0])
60 amplitude_error = amplitude * perr[0]
61 decay_rate = -1/popt[1]
62 decay_rate_error = np.abs(decay_rate * (perr[1]/popt[1]))
64 else:
65 popt, pcov = curve_fit(exponential_decay, xdata=position, ydata=values, sigma=errors,
66 absolute_sigma = has_errors,
67 p0=([np.max(values), 1.0]))
68 perr = np.sqrt(np.diag(pcov))
69 amplitude, decay_rate = popt
70 amplitude_error, decay_rate_error = perr
72 if plot:
73 plt.plot(position, exponential_decay(position, amplitude, decay_rate))
74 if has_errors:
75 plt.errorbar(position, values, errors, label=plot)
76 else:
77 plt.plot(position, values, label=plot)
78 plt.gca().set_yscale("log", nonpositive='clip')
80 return Quantity((amplitude, amplitude_error), units), Quantity((decay_rate, decay_rate_error), 'cm')