|
| 1 | +from functools import partial |
| 2 | + |
| 3 | +import numpy as np |
| 4 | +from numpy import exp, log |
| 5 | +from numpy.polynomial import Polynomial |
| 6 | + |
| 7 | + |
| 8 | +def reparam(h, omega_b, omega_cdm): |
| 9 | + Ob = omega_b / h**2 |
| 10 | + Om = omega_cdm / h**2 + Ob |
| 11 | + return Ob, Om |
| 12 | + |
| 13 | + |
| 14 | +def poly6(lna, lna_pivot=None, tilt=None, params=None, robu_flag=False): |
| 15 | + # the const and linear coeffs can be absorbed by the pivot and tilt of each curve |
| 16 | + c = np.array([0, 1, 1.25218906e-01, 3.53290242e-02, 2.20265427e-03, 7.48303918e-06]) |
| 17 | + poly = Polynomial(c) |
| 18 | + |
| 19 | + if params is not None and robu_flag is False: |
| 20 | + lna_pivot = pivot_sr(**params) |
| 21 | + tilt = tilt_sr(**params) |
| 22 | + |
| 23 | + if params is not None and robu_flag is True: |
| 24 | + lna_pivot = robust_pivot_sr(**params) |
| 25 | + tilt = robust_tilt_sr(**params) |
| 26 | + |
| 27 | + if lna_pivot is None or tilt is None: |
| 28 | + return poly(lna) |
| 29 | + |
| 30 | + return poly((lna - lna_pivot) * tilt) |
| 31 | + |
| 32 | + |
| 33 | +def gomppoly6(lna, lna_pivot=None, tilt=None, params=None, robu_flag=False): |
| 34 | + return exp(- exp(poly6(lna, lna_pivot=lna_pivot, tilt=tilt, params=params, robu_flag=robu_flag))) |
| 35 | + |
| 36 | + |
| 37 | +def pivot_sr(h, omega_b, omega_cdm, sigma8, n_s, zt, fit): |
| 38 | + Ob, Om = reparam(h, omega_b, omega_cdm) |
| 39 | + s8, ns = sigma8, n_s |
| 40 | + |
| 41 | + if fit == 'gomp1': # complexity 22 |
| 42 | + return (ns + 0.35580978 * log(0.11230898 * zt)) * (0.048352774 - s8) - Om - ns + (Ob / Om) ** h |
| 43 | + if fit == 'gomp1_raw': |
| 44 | + return ((((ns - (log(0.11230898 * zt) * -0.35580978)) * (0.048352774 - s8)) - (Om + ns)) + ((Ob / Om) ** h)) |
| 45 | + |
| 46 | + if fit == 'gomp2': # complexity 22 |
| 47 | + return (Ob / Om) ** Om - log((zt + Ob ** -0.49822742) ** s8 * h) ** 0.5721157 - ns ** 1.8340757 |
| 48 | + if fit == 'gomp2_raw': |
| 49 | + return ((((Ob / Om) ** Om) - (log(((zt + (Ob ** -0.49822742)) ** s8) * h) ** 0.5721157)) - (ns ** 1.8340757)) |
| 50 | + |
| 51 | + raise ValueError(f'fit = {fit} not recognized') |
| 52 | + |
| 53 | +def robust_pivot_sr(h, omega_b, omega_cdm, sigma8, n_s, zt, Tv, LX, fit): |
| 54 | + Ob, Om = reparam(h, omega_b, omega_cdm) |
| 55 | + s8, ns = sigma8, n_s |
| 56 | + if fit == 'robustgomp1': # complexity 34 |
| 57 | + return ((((Tv - np.log((((Tv ** (-40.065258 + LX)) + zt) + np.exp(s8)) ** s8)) / 2.5946999) - np.exp(ns - (Ob ** Om))) - ((h ** (Om ** 0.79247624)) / (0.8632819 ** ns))) |
| 58 | + |
| 59 | + if fit == 'robustgomp2': # complexity 27 |
| 60 | + return (((Ob / (Om * s8)) ** (s8 * h)) - (((((19.920519 + zt) + (0.08890569 ** (40.43043 - LX))) * s8) ** (ns / Tv)) + Om)) |
| 61 | + |
| 62 | + raise ValueError(f'fit = {fit} not recognized') |
| 63 | + |
| 64 | + |
| 65 | +def tilt_sr(h, omega_b, omega_cdm, sigma8, n_s, zt, fit): |
| 66 | + Ob, Om = reparam(h, omega_b, omega_cdm) |
| 67 | + s8, ns = sigma8, n_s |
| 68 | + |
| 69 | + if fit == 'gomp1': # complexity 25 |
| 70 | + return log(Ob) * (0.005659511 ** Om / 0.601493 - log(zt - (Om + ns * h) ** 15.051933) + h) + h / s8 |
| 71 | + if fit == 'gomp1_raw': |
| 72 | + return ((log(Ob) * (((0.005659511 ** Om) / 0.601493) - (log(zt - ((Om + (ns * h)) ** 15.051933)) - h))) + (h / s8)) |
| 73 | + |
| 74 | + if fit == 'gomp2': # complexity 11, 1e-5 difference in constants from those in csv |
| 75 | + return ((zt - Om ** -1.583228) / (Ob * h)) ** 0.31627414 |
| 76 | + if fit == 'gomp2_raw': |
| 77 | + return (((zt - (Om ** -1.583228)) / (Ob * h)) ** 0.31627414) |
| 78 | + |
| 79 | + raise ValueError(f'fit = {fit} not recognized') |
| 80 | + |
| 81 | +def robust_tilt_sr(h, omega_b, omega_cdm, sigma8, n_s, zt, Tv, LX, fit): |
| 82 | + Ob, Om = reparam(h, omega_b, omega_cdm) |
| 83 | + s8, ns = sigma8, n_s |
| 84 | + |
| 85 | + if fit == 'robustgomp1': # complexity 24 |
| 86 | + return (((zt / h) ** 0.4991587) - (((((Ob + -0.20859116) * LX) - -6.0811667) - (Om * (Tv - np.exp(ns ** 2.8035064)))) / s8)) |
| 87 | + |
| 88 | + if fit == 'robustgomp2': # complexity 26 |
| 89 | + return (((((0.12804393 - Ob) / h) * (zt + ((Tv - ((s8 / Om) + 1.0161631)) / Om))) - (ns**3.052106)) + np.exp(0.039550677 * LX)) |
| 90 | + |
| 91 | + raise ValueError(f'fit = {fit} not recognized') |
| 92 | + |
| 93 | + |
| 94 | +def tanh(z, z_reio): |
| 95 | + """Planck 2018 VI footnote 15""" |
| 96 | + y, y_reio = (1 + z) ** 1.5, (1 + z_reio) ** 1.5 |
| 97 | + delta_z = 0.5 |
| 98 | + delta_y = 1.5 * np.sqrt(1 + z_reio) * delta_z |
| 99 | + x_HI = 0.5 * (1 + np.tanh((y - y_reio) / delta_y)) |
| 100 | + return x_HI |
| 101 | + |
| 102 | + |
| 103 | +def xHI_like(_self=None, type='dw', fit='gomp1'): |
| 104 | + data = np.array([ |
| 105 | + [7.29, 0.49, -0.11, 0.11], # combined quasars daming wing |
| 106 | + [6.15, 0.20, -0.12, 0.14], # 2404.12585 damping wing |
| 107 | + [6.35, 0.29, -0.13, 0.14], |
| 108 | + [5.60, 0.19, -0.16, 0.11], # 2405.12273 damping wing |
| 109 | + [6.10, 0.21, -0.07, 0.17], # 2401.10328 damping wing |
| 110 | + [6.46, 0.21, -0.07, 0.33], |
| 111 | + [6.87, 0.37, -0.17, 0.17], |
| 112 | + [6.60, 0.08, -0.05, 0.08], # 2101.01205 luminosity function |
| 113 | + [7.00, 0.28, -0.05, 0.05], |
| 114 | + #[7.30, 0.83, -0.07, 0.06], # concerning interpolation of UV luminosity |
| 115 | + ]) |
| 116 | + if type == 'dwlf': # both damping wing and luminosity function |
| 117 | + pass |
| 118 | + elif type == 'dw': # only damping wing |
| 119 | + data = data[:-2] |
| 120 | + else: |
| 121 | + raise ValueError(f'{type=} not recognized') |
| 122 | + |
| 123 | + z, m, l, h = data.T |
| 124 | + lna = - log(1 + z) |
| 125 | + mean = m + (l + h) / 2 # symmetrized |
| 126 | + var = ((h - l) / 2) ** 2 # symmetrized |
| 127 | + |
| 128 | + if fit == 'tanh': |
| 129 | + z_reio = _self.provider.get_param('z_reio') |
| 130 | + xHI = tanh(z, z_reio) |
| 131 | + elif fit == 'robustgomp1' or fit == 'robustgomp2': |
| 132 | + params = dict( |
| 133 | + h=_self.provider.get_param('H0') / 100, |
| 134 | + omega_b=_self.provider.get_param('omega_b'), |
| 135 | + omega_cdm=_self.provider.get_param('omega_cdm'), |
| 136 | + sigma8=_self.provider.get_param('sigma8'), |
| 137 | + n_s=_self.provider.get_param('n_s'), |
| 138 | + zt=_self.provider.get_param('zt'), |
| 139 | + Tv=_self.provider.get_param('Tv'), |
| 140 | + LX=_self.provider.get_param('LX'), |
| 141 | + fit=fit, |
| 142 | + ) |
| 143 | + # aww, making a flag was a bad idea: wdm_flag, axion flag... |
| 144 | + # fine for now |
| 145 | + xHI = gomppoly6(lna, params=params, robu_flag=True) |
| 146 | + elif fit == 'reio_gomp_noSR': |
| 147 | + lna_pivot = _self.provider.get_param('alpha_gomp') |
| 148 | + tilt = _self.provider.get_param('beta_gomp') |
| 149 | + xHI = gomppoly6(lna, lna_pivot=lna_pivot, tilt=tilt, robu_flag=False) |
| 150 | + else: |
| 151 | + params = dict( |
| 152 | + h=_self.provider.get_param('H0') / 100, |
| 153 | + omega_b=_self.provider.get_param('omega_b'), |
| 154 | + omega_cdm=_self.provider.get_param('omega_cdm'), |
| 155 | + sigma8=_self.provider.get_param('sigma8'), |
| 156 | + n_s=_self.provider.get_param('n_s'), |
| 157 | + zt=_self.provider.get_param('zt'), |
| 158 | + fit=fit, |
| 159 | + ) |
| 160 | + xHI = gomppoly6(lna, params=params, robu_flag=False) |
| 161 | + |
| 162 | + half_neg_chi2 = -0.5 * ((xHI - mean) ** 2 / var).sum() |
| 163 | + return half_neg_chi2 |
| 164 | + |
| 165 | +tanh_dw = partial(xHI_like, type='dw', fit='tanh') |
| 166 | +tanh_dwlf = partial(xHI_like, type='dwlf', fit='tanh') |
| 167 | +gomp_1dw = partial(xHI_like, type='dw', fit='gomp1') |
| 168 | +gomp_1dwlf = partial(xHI_like, type='dwlf', fit='gomp1') |
| 169 | +gomp_2dw = partial(xHI_like, type='dw', fit='gomp2') |
| 170 | +gomp_2dwlf = partial(xHI_like, type='dwlf', fit='gomp2') |
| 171 | +robustgomp_1dw = partial(xHI_like, type='dw', fit='robustgomp1') |
| 172 | +robustgomp_2dw = partial(xHI_like, type='dw', fit='robustgomp2') |
| 173 | +freegomp_dw = partial(xHI_like, type='dw', fit='reio_gomp_noSR') |
| 174 | + |
| 175 | + |
| 176 | +if __name__ == '__main__': |
| 177 | + params = dict( |
| 178 | + h=0.67810, |
| 179 | + omega_b=0.02238280, |
| 180 | + omega_cdm=0.1201075, |
| 181 | + sigma8=0.8159, |
| 182 | + n_s=0.9660499, |
| 183 | + zt=24, |
| 184 | + ) |
| 185 | + #fits = ['gomp1', 'gomp1_raw', 'gomp2', 'gomp2_raw'] |
| 186 | + fits = ['gomp1', 'gomp2'] |
| 187 | + |
| 188 | + import matplotlib.pyplot as plt |
| 189 | + z = np.linspace(5, 16, num=111, endpoint=True) |
| 190 | + lna = - log(1 + z) |
| 191 | + for fit in fits: |
| 192 | + params['fit'] = fit |
| 193 | + plt.plot(z, gomppoly6(lna, params=params, robu_flag=False), label=fit, lw=1, alpha=.5) |
| 194 | + plt.plot(z, gomppoly6(lna, lna_pivot=-2.1038, tilt=7.49, robu_flag=False), label='free', lw=1, alpha=0.7) |
| 195 | + plt.plot(z, tanh(z, z_reio=7.67), label='tanh', lw=1, alpha=.5) |
| 196 | + plt.legend() |
| 197 | + plt.xlabel('$z$') |
| 198 | + plt.ylabel('$x_\mathrm{HI}$') |
| 199 | + plt.show() |
0 commit comments