|
| 1 | +import numpy as np |
| 2 | +from scipy.special import erfc |
| 3 | +from scipy.stats import chisquare, sigmaclip |
| 4 | + |
| 5 | +from ._base import BaseFeature |
| 6 | + |
| 7 | + |
| 8 | +class RedDwarfFit(BaseFeature): |
| 9 | + """Red dwarf flares fitting function. |
| 10 | +
|
| 11 | + - Depends on: **time**, **magnitude** |
| 12 | + - Minimum number of observations: **2** |
| 13 | + - Number of features: **4** |
| 14 | +
|
| 15 | + Note, that the function is developed to be used with fluxes, not magnitudes. |
| 16 | +
|
| 17 | + Guadalupe Tovar Mendoza et al. 2022 [DOI:10.3847/1538-3881/ac6fe6](https://doi.org/10.3847/1538-3881/ac6fe6) |
| 18 | + """ |
| 19 | + |
| 20 | + def _eval(self, t, m, sigma=None): |
| 21 | + amplitude, fwhm, tpeak = RedDwarfFit._flare_params(t, m) |
| 22 | + model = self.fit(t, amplitude, fwhm, tpeak) |
| 23 | + chi2 = np.sum((m - model) ** 2) / (len(m) - 1) |
| 24 | + |
| 25 | + return np.array([amplitude, fwhm, tpeak, chi2]) |
| 26 | + |
| 27 | + @staticmethod |
| 28 | + def _flare_params(t, m): |
| 29 | + clipped = sigmaclip(m, low=3.0, high=3.0)[0] |
| 30 | + background = np.mean(clipped) |
| 31 | + clean_flux = m - background |
| 32 | + peak = np.argmax(clean_flux) |
| 33 | + amplitude = clean_flux[peak] |
| 34 | + tpeak = t[peak] |
| 35 | + |
| 36 | + left_idx = np.where(clean_flux[:peak] < 0.5 * clean_flux[peak])[0][-1] |
| 37 | + left_t = t[left_idx] + (t[left_idx + 1] - t[left_idx]) * (0.5 * clean_flux[peak] - clean_flux[left_idx]) / ( |
| 38 | + clean_flux[left_idx + 1] - clean_flux[left_idx] |
| 39 | + ) |
| 40 | + |
| 41 | + right_idx = (np.where(clean_flux[peak:] < 0.5 * clean_flux[peak]) + peak)[0][0] |
| 42 | + right_t = t[right_idx] + (t[right_idx + 1] - t[right_idx]) * ( |
| 43 | + 0.5 * clean_flux[peak] - clean_flux[right_idx] |
| 44 | + ) / (clean_flux[right_idx + 1] - clean_flux[right_idx]) |
| 45 | + |
| 46 | + fwhm = right_t - left_t |
| 47 | + |
| 48 | + return amplitude, fwhm, tpeak |
| 49 | + |
| 50 | + @staticmethod |
| 51 | + def fit(t, amplitude, fwhm, tpeak): |
| 52 | + A, B, C, D1, D2, f1 = [ |
| 53 | + 0.9687734504375167, |
| 54 | + -0.251299705922117, |
| 55 | + 0.22675974948468916, |
| 56 | + 0.15551880775110513, |
| 57 | + 1.2150539528490194, |
| 58 | + 0.12695865022878844, |
| 59 | + ] |
| 60 | + |
| 61 | + t_new = (t - tpeak) / fwhm |
| 62 | + f2 = 1 - f1 |
| 63 | + |
| 64 | + eq = ( |
| 65 | + (1 / 2) |
| 66 | + * np.sqrt(np.pi) |
| 67 | + * A |
| 68 | + * C |
| 69 | + * f1 |
| 70 | + * np.exp(-D1 * t_new + ((B / C) + (D1 * C / 2)) ** 2) |
| 71 | + * erfc(((B - t_new) / C) + (C * D1 / 2)) |
| 72 | + ) + ( |
| 73 | + (1 / 2) |
| 74 | + * np.sqrt(np.pi) |
| 75 | + * A |
| 76 | + * C |
| 77 | + * f2 |
| 78 | + * np.exp(-D2 * t_new + ((B / C) + (D2 * C / 2)) ** 2) |
| 79 | + * erfc(((B - t_new) / C) + (C * D2 / 2)) |
| 80 | + ) |
| 81 | + |
| 82 | + return eq * amplitude |
| 83 | + |
| 84 | + @property |
| 85 | + def size(self): |
| 86 | + return 4 |
| 87 | + |
| 88 | + |
| 89 | +__all__ = ("RedDwarfFit",) |
0 commit comments