def getf_excitation(photspec, norm_fac, dt, xe, n, method, cross_check=False): if ((method == 'old') or (method == 'helium') or (method == 'ion')): # All photons between 11.2eV and 13.6eV are deposited into excitation # partial binning if not cross_check: tot_excite_eng = (photspec.toteng( bound_type='eng', bound_arr=np.array([phys.lya_eng, phys.rydberg]))[0]) else: tot_excite_eng = np.dot( photspec.N[(photspec.eng >= 10.2) & (photspec.eng <= 13.6)], photspec.eng[(photspec.eng >= 10.2) & (photspec.eng <= 13.6)]) f_excite_HI = tot_excite_eng * norm_fac else: # Only photons in the 10.2eV bin participate in 1s->2p excitation. # 1s->2s transition handled more carefully. # Convenient variables kappa = kappa_DM(photspec, xe) # Added this line since rate_2p1s_times_x1s function was removed. rate_2p1s_times_x1s = (8 * np.pi * phys.hubble(photspec.rs) / (3 * (phys.nH * photspec.rs**3 * (phys.c / phys.lya_freq)**3))) f_excite_HI = ( kappa * (3 * rate_2p1s_times_x1s * phys.nH + phys.width_2s1s_H * n[0]) * phys.lya_eng * (norm_fac / phys.nB / photspec.rs**3 * dt)) return f_excite_HI
def Gam_pi(x_be, T_DM, rs, alphaD, m_be, m_bp, xi): B_D = get_BD(alphaD, m_be, m_bp) T_D = xi * phys.TCMB(rs) m_D = m_be + m_bp - B_D mu_D = m_be * m_bp / m_D n_D = phys.rho_DM / m_D * rs**3 denom = (3 / 2 * T_DM * n_D * (1 + x_be)) # Calculate x_2s using Hyrec's steady stateassumption # Lya_D = 3 / 4 * B_D dark_sobolev = 2**7 * alphaD**3 * B_D * n_D * phys.c**3 * (1 - x_be) / ( 3**7 * 2 * np.pi * phys.hbar * phys.hubble(rs) * Lya_D) R_D = 2**9 * alphaD**3 * B_D / 3**8 * ( 1 - np.exp(-dark_sobolev)) / dark_sobolev Lam = (alphaD / phys.alpha)**6 * (B_D / phys.rydberg) * phys.width_2s1s_H alpha_B = dark_alpha_recomb(T_DM, alphaD, m_be, m_bp, xi) beta_e = dark_beta_ion(T_D, alphaD, m_be, m_bp, xi) x_2 = (n_D * x_be**2 * alpha_B + (3 * R_D + Lam) * (1 - x_be) * np.exp(-B_D / T_D)) / (beta_e + 3 / 4 * R_D + 1 / 4 * Lam) x_2s = 1 / 4 * x_2 pre = alphaD**3 * T_D**2 / (3 * np.pi) * np.exp(-B_D / (4 * T_D)) convert = phys.hbar return pre * x_2s * n_D * F_pi(T_D / B_D) / denom * convert
def kappa_DM(photspec, xe): """ Compute kappa_DM of the modified tla. Parameters ---------- photspec : Spectrum object spectrum of photons. Assumed to be in dNdE mode. spec.toteng() should return Energy per baryon. Returns ------- kappa_DM : float The added photoionization rate due to products of DM. """ eng = photspec.eng rs = photspec.rs rate_2p1s_times_x1s = (8 * np.pi * phys.hubble(rs) / (3 * (phys.nH * rs**3 * (phys.c / phys.lya_freq)**3))) x1s_times_R_Lya = rate_2p1s_times_x1s Lambda = phys.width_2s1s_H # The bin number containing 10.2eV lya_index = spectools.get_indx(eng, phys.lya_eng) # The bins between 10.2eV and 13.6eV exc_bounds = spectools.get_bounds_between(eng, phys.lya_eng, phys.rydberg) # Effect on 2p state due to DM products kappa_2p = (photspec.dNdE[lya_index] * phys.nB * rs**3 * np.pi**2 * (phys.hbar * phys.c)**3 / phys.lya_eng**2) # Effect on 2s state kappa_2s = get_kappa_2s(photspec) return (kappa_2p * 3 * x1s_times_R_Lya / 4 + kappa_2s * (1 - xe) * Lambda / 4) / (3 * x1s_times_R_Lya / 4 + (1 - xe) * Lambda / 4)
def dark_peebles_C(x_be, rs, alphaD, m_be, m_bp, xi): B_D = get_BD(alphaD, m_be, m_bp) m_D = m_be + m_bp - B_D n_D = phys.rho_DM / m_D * rs**3 #n_D = phys.nH*rs**3 T_D = xi * phys.TCMB(rs) beta = dark_beta_ion(T_D, alphaD, m_be, m_bp, xi) Lya_D = 3 / 4 * B_D dark_sobolev = 2**7 * alphaD**3 * B_D * n_D * phys.c**3 * (1 - x_be) / ( 3**7 * 2 * np.pi * phys.hbar * phys.hubble(rs) * Lya_D) R_D_fac = 3 / 4 * 2**9 * alphaD**3 * B_D / 3**8 * ( 1 - np.exp(-dark_sobolev)) / dark_sobolev Lam_2s_1s_fac = 1 / 4 * (alphaD / phys.alpha)**6 * ( B_D / phys.rydberg) * phys.width_2s1s_H ssum = R_D_fac + Lam_2s_1s_fac return ssum / (ssum + beta)
def get_normalized_spec(spec, dE_dVdt, rs): """ Normalizes the spectrum to per baryon per dlnz, given dE/(dV dt). Parameters ---------- spec : Spectrum Input spectrum to be normalized. dE_dVdt : float The injection dE/(dV dt) in eV cm^-3 s^-1. rs : float The redshift (1+z). Returns ------- Spectrum The normalized spectrum (per baryon per dlnz). """ dE_dNBdlnz = dE_dVdt / (phys.nB * rs**3) / phys.hubble(rs) return spec / spec.toteng() * dEdNBdlnz
def norm_fac(rs): # Normalization to convert from per injection event to # per baryon per dlnz step. return rate_func_N(rs) * (dlnz * coarsen_factor / phys.hubble(rs) / (phys.nB * rs**3))
def evolve(in_spec_elec=None, in_spec_phot=None, rate_func_N=None, rate_func_eng=None, DM_process=None, mDM=None, sigmav=None, lifetime=None, primary=None, struct_boost=None, start_rs=None, end_rs=4, helium_TLA=False, reion_switch=False, reion_rs=None, photoion_rate_func=None, photoheat_rate_func=None, xe_reion_func=None, init_cond=None, coarsen_factor=1, backreaction=True, compute_fs_method='no_He', mxstep=1000, rtol=1e-4, use_tqdm=True, cross_check=False): """ Main function computing histories and spectra. Parameters ----------- in_spec_elec : :class:`.Spectrum`, optional Spectrum per injection event into electrons. *in_spec_elec.rs* of the :class:`.Spectrum` must be the initial redshift. in_spec_phot : :class:`.Spectrum`, optional Spectrum per injection event into photons. *in_spec_phot.rs* of the :class:`.Spectrum` must be the initial redshift. rate_func_N : function, optional Function returning number of injection events per volume per time, with redshift :math:`(1+z)` as an input. rate_func_eng : function, optional Function returning energy injected per volume per time, with redshift :math:`(1+z)` as an input. DM_process : {'swave', 'decay'}, optional Dark matter process to use. sigmav : float, optional Thermally averaged cross section for dark matter annihilation. lifetime : float, optional Decay lifetime for dark matter decay. primary : string, optional Primary channel of annihilation/decay. See :func:`.get_pppc_spec` for complete list. Use *'elec_delta'* or *'phot_delta'* for delta function injections of a pair of photons/an electron-positron pair. struct_boost : function, optional Energy injection boost factor due to structure formation. start_rs : float, optional Starting redshift :math:`(1+z)` to evolve from. Default is :math:`(1+z)` = 3000. Specify only for use with *DM_process*. Otherwise, initialize *in_spec_elec.rs* and/or *in_spec_phot.rs* directly. end_rs : float, optional Final redshift :math:`(1+z)` to evolve to. Default is 1+z = 4. reion_switch : bool Reionization model included if *True*, default is *False*. helium_TLA : bool If *True*, the TLA is solved with helium. Default is *False*. reion_rs : float, optional Redshift :math:`(1+z)` at which reionization effects turn on. photoion_rate_func : tuple of functions, optional Functions take redshift :math:`1+z` as input, return the photoionization rate in s\ :sup:`-1` of HI, HeI and HeII respectively. If not specified, defaults to :func:`.photoion_rate`. photoheat_rate_func : tuple of functions, optional Functions take redshift :math:`1+z` as input, return the photoheating rate in s\ :sup:`-1` of HI, HeI and HeII respectively. If not specified, defaults to :func:`.photoheat_rate`. xe_reion_func : function, optional Specifies a fixed ionization history after reion_rs. init_cond : tuple of floats Specifies the initial (xH, xHe, Tm). Defaults to :func:`.Tm_std`, :func:`.xHII_std` and :func:`.xHeII_std` at the *start_rs*. coarsen_factor : int Coarsening to apply to the transfer function matrix. Default is 1. backreaction : bool If *False*, uses the baseline TLA solution to calculate :math:`f_c(z)`. Default is True. compute_fs_method : {'no_He', 'He_recomb', 'He'} mxstep : int, optional The maximum number of steps allowed for each integration point. See *scipy.integrate.odeint()* for more information. Default is *1000*. rtol : float, optional The relative error of the solution. See *scipy.integrate.odeint()* for more information. Default is *1e-4*. use_tqdm : bool, optional Uses tqdm if *True*. Default is *True*. cross_check : bool, optional If *True*, compare against 1604.02457 by using original MEDEA files, turning off partial binning, etc. Default is *False*. Examples -------- 1. *Dark matter annihilation* -- dark matter mass of 50 GeV, annihilation cross section :math:`2 \\times 10^{-26}` cm\ :sup:`3` s\ :sup:`-1`, annihilating to :math:`b \\bar{b}`, solved without backreaction, a coarsening factor of 32 and the default structure formation boost: :: import darkhistory.physics as phys out = evolve( DM_process='swave', mDM=50e9, sigmav=2e-26, primary='b', start_rs=3000., backreaction=False, struct_boost=phys.struct_boost_func() ) 2. *Dark matter decay* -- dark matter mass of 100 GeV, decay lifetime :math:`3 \\times 10^{25}` s, decaying to a pair of :math:`e^+e^-`, solved with backreaction, a coarsening factor of 16: :: out = evolve( DM_process='decay', mDM=1e8, lifetime=3e25, primary='elec_delta', start_rs=3000., backreaction=True ) See Also --------- :func:`.get_pppc_spec` :func:`.struct_boost_func` :func:`.photoion_rate`, :func:`.photoheat_rate` :func:`.Tm_std`, :func:`.xHII_std` and :func:`.xHeII_std` """ ######################################################################### ######################################################################### # Input # ######################################################################### ######################################################################### ##################################### # Initialization for DM_process # ##################################### # Load data. binning = load_data('binning') photeng = binning['phot'] eleceng = binning['elec'] dep_tf_data = load_data('dep_tf') highengphot_tf_interp = dep_tf_data['highengphot'] lowengphot_tf_interp = dep_tf_data['lowengphot'] lowengelec_tf_interp = dep_tf_data['lowengelec'] highengdep_interp = dep_tf_data['highengdep'] ics_tf_data = load_data('ics_tf') ics_thomson_ref_tf = ics_tf_data['thomson'] ics_rel_ref_tf = ics_tf_data['rel'] engloss_ref_tf = ics_tf_data['engloss'] # Handle the case where a DM process is specified. if DM_process == 'swave': if sigmav is None or start_rs is None: raise ValueError('sigmav and start_rs must be specified.') # Get input spectra from PPPC. in_spec_elec = pppc.get_pppc_spec(mDM, eleceng, primary, 'elec') in_spec_phot = pppc.get_pppc_spec(mDM, photeng, primary, 'phot') # Initialize the input spectrum redshift. in_spec_elec.rs = start_rs in_spec_phot.rs = start_rs # Convert to type 'N'. in_spec_elec.switch_spec_type('N') in_spec_phot.switch_spec_type('N') # If struct_boost is none, just set to 1. if struct_boost is None: def struct_boost(rs): return 1. # Define the rate functions. def rate_func_N(rs): return (phys.inj_rate('swave', rs, mDM=mDM, sigmav=sigmav) * struct_boost(rs) / (2 * mDM)) def rate_func_eng(rs): return (phys.inj_rate('swave', rs, mDM=mDM, sigmav=sigmav) * struct_boost(rs)) if DM_process == 'decay': if lifetime is None or start_rs is None: raise ValueError('lifetime and start_rs must be specified.') # The decay rate is insensitive to structure formation def struct_boost(rs): return 1 # Get spectra from PPPC. in_spec_elec = pppc.get_pppc_spec(mDM, eleceng, primary, 'elec', decay=True) in_spec_phot = pppc.get_pppc_spec(mDM, photeng, primary, 'phot', decay=True) # Initialize the input spectrum redshift. in_spec_elec.rs = start_rs in_spec_phot.rs = start_rs # Convert to type 'N'. in_spec_elec.switch_spec_type('N') in_spec_phot.switch_spec_type('N') # Define the rate functions. def rate_func_N(rs): return (phys.inj_rate('decay', rs, mDM=mDM, lifetime=lifetime) / mDM) def rate_func_eng(rs): return phys.inj_rate('decay', rs, mDM=mDM, lifetime=lifetime) ##################################### # Input Checks # ##################################### if (not np.array_equal(in_spec_elec.eng, eleceng) or not np.array_equal(in_spec_phot.eng, photeng)): raise ValueError( 'in_spec_elec and in_spec_phot must use config.photeng and config.eleceng respectively as abscissa.' ) if (highengphot_tf_interp.dlnz != lowengphot_tf_interp.dlnz or highengphot_tf_interp.dlnz != lowengelec_tf_interp.dlnz or lowengphot_tf_interp.dlnz != lowengelec_tf_interp.dlnz): raise ValueError( 'TransferFuncInterp objects must all have the same dlnz.') if in_spec_elec.rs != in_spec_phot.rs: raise ValueError('Input spectra must have the same rs.') if cross_check: print( 'cross_check has been set to True -- No longer using all MEDEA files and no longer using partial-binning.' ) ##################################### # Initialization # ##################################### # Initialize start_rs for arbitrary injection. start_rs = in_spec_elec.rs # Initialize the initial x and Tm. if init_cond is None: # Default to baseline xH_init = phys.xHII_std(start_rs) xHe_init = phys.xHeII_std(start_rs) Tm_init = phys.Tm_std(start_rs) else: # User-specified. xH_init = init_cond[0] xHe_init = init_cond[1] Tm_init = init_cond[2] # Initialize redshift/timestep related quantities. # Default step in the transfer function. Note highengphot_tf_interp.dlnz # contains 3 different regimes, and we start with the first. dlnz = highengphot_tf_interp.dlnz[-1] # The current redshift. rs = start_rs # The timestep between evaluations of transfer functions, including # coarsening. dt = dlnz * coarsen_factor / phys.hubble(rs) # tqdm set-up. if use_tqdm: from tqdm import tqdm_notebook as tqdm pbar = tqdm(total=np.ceil((np.log(rs) - np.log(end_rs)) / dlnz / coarsen_factor)) def norm_fac(rs): # Normalization to convert from per injection event to # per baryon per dlnz step. return rate_func_N(rs) * (dlnz * coarsen_factor / phys.hubble(rs) / (phys.nB * rs**3)) def rate_func_eng_unclustered(rs): # The rate excluding structure formation for s-wave annihilation. # This is the correct normalization for f_c(z). if struct_boost is not None: return rate_func_eng(rs) / struct_boost(rs) else: return rate_func_eng(rs) # If there are no electrons, we get a speed up by ignoring them. elec_processes = False if in_spec_elec.totN() > 0: elec_processes = True if elec_processes: ##################################### # High-Energy Electrons # ##################################### # Get the data necessary to compute the electron cooling results. # coll_ion_sec_elec_specs is \bar{N} for collisional ionization, # and coll_exc_sec_elec_specs \bar{N} for collisional excitation. # Heating and others are evaluated in get_elec_cooling_tf # itself. # Contains information that makes converting an energy loss spectrum # to a scattered electron spectrum fast. (coll_ion_sec_elec_specs, coll_exc_sec_elec_specs, ics_engloss_data) = get_elec_cooling_data(eleceng, photeng) ######################################################################### ######################################################################### # Pre-Loop Preliminaries # ######################################################################### ######################################################################### # Initialize the arrays that will contain x and Tm results. x_arr = np.array([[xH_init, xHe_init]]) Tm_arr = np.array([Tm_init]) # Initialize Spectra objects to contain all of the output spectra. out_highengphot_specs = Spectra([], spec_type='N') out_lowengphot_specs = Spectra([], spec_type='N') out_lowengelec_specs = Spectra([], spec_type='N') # Define these methods for speed. append_highengphot_spec = out_highengphot_specs.append append_lowengphot_spec = out_lowengphot_specs.append append_lowengelec_spec = out_lowengelec_specs.append # Initialize arrays to store f values. f_low = np.empty((0, 5)) f_high = np.empty((0, 5)) # Initialize array to store high-energy energy deposition rate. highengdep_grid = np.empty((0, 4)) # Object to help us interpolate over MEDEA results. MEDEA_interp = make_interpolator(interp_type='2D', cross_check=cross_check) ######################################################################### ######################################################################### # LOOP! LOOP! LOOP! LOOP! # ######################################################################### ######################################################################### while rs > end_rs: # Update tqdm. if use_tqdm: pbar.update(1) ############################# # First Step Special Cases # ############################# if rs == start_rs: # Initialize the electron and photon arrays. # These will carry the spectra produced by applying the # transfer function at rs to high-energy photons. highengphot_spec_at_rs = in_spec_phot * 0 lowengphot_spec_at_rs = in_spec_phot * 0 lowengelec_spec_at_rs = in_spec_elec * 0 highengdep_at_rs = np.zeros(4) ##################################################################### ##################################################################### # Electron Cooling # ##################################################################### ##################################################################### # Get the transfer functions corresponding to electron cooling. # These are \bar{T}_\gamma, \bar{T}_e and \bar{R}_c. if elec_processes: if backreaction: xHII_elec_cooling = x_arr[-1, 0] xHeII_elec_cooling = x_arr[-1, 1] else: xHII_elec_cooling = phys.xHII_std(rs) xHeII_elec_cooling = phys.xHeII_std(rs) (ics_sec_phot_tf, elec_processes_lowengelec_tf, deposited_ion_arr, deposited_exc_arr, deposited_heat_arr, continuum_loss, deposited_ICS_arr) = get_elec_cooling_tf( eleceng, photeng, rs, xHII_elec_cooling, xHeII=xHeII_elec_cooling, raw_thomson_tf=ics_thomson_ref_tf, raw_rel_tf=ics_rel_ref_tf, raw_engloss_tf=engloss_ref_tf, coll_ion_sec_elec_specs=coll_ion_sec_elec_specs, coll_exc_sec_elec_specs=coll_exc_sec_elec_specs, ics_engloss_data=ics_engloss_data) # Apply the transfer function to the input electron spectrum. # Low energy electrons from electron cooling, per injection event. elec_processes_lowengelec_spec = ( elec_processes_lowengelec_tf.sum_specs(in_spec_elec)) # Add this to lowengelec_at_rs. lowengelec_spec_at_rs += (elec_processes_lowengelec_spec * norm_fac(rs)) # High-energy deposition into ionization, # *per baryon in this step*. deposited_ion = np.dot(deposited_ion_arr, in_spec_elec.N * norm_fac(rs)) # High-energy deposition into excitation, # *per baryon in this step*. deposited_exc = np.dot(deposited_exc_arr, in_spec_elec.N * norm_fac(rs)) # High-energy deposition into heating, # *per baryon in this step*. deposited_heat = np.dot(deposited_heat_arr, in_spec_elec.N * norm_fac(rs)) # High-energy deposition numerical error, # *per baryon in this step*. deposited_ICS = np.dot(deposited_ICS_arr, in_spec_elec.N * norm_fac(rs)) ####################################### # Photons from Injected Electrons # ####################################### # ICS secondary photon spectrum after electron cooling, # per injection event. ics_phot_spec = ics_sec_phot_tf.sum_specs(in_spec_elec) # Get the spectrum from positron annihilation, per injection event. # Only half of in_spec_elec is positrons! positronium_phot_spec = pos.weighted_photon_spec(photeng) * ( in_spec_elec.totN() / 2) positronium_phot_spec.switch_spec_type('N') # Add injected photons + photons from injected electrons # to the photon spectrum that got propagated forward. if elec_processes: highengphot_spec_at_rs += (in_spec_phot + ics_phot_spec + positronium_phot_spec) * norm_fac(rs) else: highengphot_spec_at_rs += in_spec_phot * norm_fac(rs) # Set the redshift correctly. highengphot_spec_at_rs.rs = rs ##################################################################### ##################################################################### # Save the Spectra! # ##################################################################### ##################################################################### # At this point, highengphot_at_rs, lowengphot_at_rs and # lowengelec_at_rs have been computed for this redshift. append_highengphot_spec(highengphot_spec_at_rs) append_lowengphot_spec(lowengphot_spec_at_rs) append_lowengelec_spec(lowengelec_spec_at_rs) ##################################################################### ##################################################################### # Compute f_c(z) # ##################################################################### ##################################################################### if elec_processes: # High-energy deposition from input electrons. highengdep_at_rs += np.array([ deposited_ion / dt, deposited_exc / dt, deposited_heat / dt, deposited_ICS / dt ]) # Values of (xHI, xHeI, xHeII) to use for computing f. if backreaction: # Use the previous values with backreaction. x_vec_for_f = np.array( [1. - x_arr[-1, 0], phys.chi - x_arr[-1, 1], x_arr[-1, 1]]) else: # Use baseline values if no backreaction. x_vec_for_f = np.array([ 1. - phys.xHII_std(rs), phys.chi - phys.xHeII_std(rs), phys.xHeII_std(rs) ]) f_raw = compute_fs(MEDEA_interp, lowengelec_spec_at_rs, lowengphot_spec_at_rs, x_vec_for_f, rate_func_eng_unclustered(rs), dt, highengdep_at_rs, method=compute_fs_method, cross_check=cross_check) # Save the f_c(z) values. f_low = np.concatenate((f_low, [f_raw[0]])) f_high = np.concatenate((f_high, [f_raw[1]])) # print(f_low, f_high) # Save CMB upscattered rate and high-energy deposition rate. highengdep_grid = np.concatenate((highengdep_grid, [highengdep_at_rs])) # Compute f for TLA: sum of low and high. f_H_ion = f_raw[0][0] + f_raw[1][0] f_exc = f_raw[0][2] + f_raw[1][2] f_heat = f_raw[0][3] + f_raw[1][3] if compute_fs_method == 'old': # The old method neglects helium. f_He_ion = 0. else: f_He_ion = f_raw[0][1] + f_raw[1][1] ##################################################################### ##################################################################### # ********* AFTER THIS, COMPUTE QUANTITIES FOR NEXT STEP ********* # ##################################################################### ##################################################################### # Define the next redshift step. next_rs = np.exp(np.log(rs) - dlnz * coarsen_factor) ##################################################################### ##################################################################### # TLA Integration # ##################################################################### ##################################################################### # Initial conditions for the TLA, (Tm, xHII, xHeII, xHeIII). # This is simply the last set of these variables. init_cond_TLA = np.array([Tm_arr[-1], x_arr[-1, 0], x_arr[-1, 1], 0]) # Solve the TLA for x, Tm for the *next* step. new_vals = tla.get_history(np.array([rs, next_rs]), init_cond=init_cond_TLA, f_H_ion=f_H_ion, f_H_exc=f_exc, f_heating=f_heat, injection_rate=rate_func_eng_unclustered, reion_switch=reion_switch, reion_rs=reion_rs, photoion_rate_func=photoion_rate_func, photoheat_rate_func=photoheat_rate_func, xe_reion_func=xe_reion_func, helium_TLA=helium_TLA, f_He_ion=f_He_ion, mxstep=mxstep, rtol=rtol) ##################################################################### ##################################################################### # Photon Cooling Transfer Functions # ##################################################################### ##################################################################### # Get the transfer functions for this step. if not backreaction: # Interpolate using the baseline solution. xHII_to_interp = phys.xHII_std(rs) xHeII_to_interp = phys.xHeII_std(rs) else: # Interpolate using the current xHII, xHeII values. xHII_to_interp = x_arr[-1, 0] xHeII_to_interp = x_arr[-1, 1] highengphot_tf, lowengphot_tf, lowengelec_tf, highengdep_arr = (get_tf( rs, xHII_to_interp, xHeII_to_interp, dlnz, coarsen_factor=coarsen_factor)) # Get the spectra for the next step by applying the # transfer functions. highengdep_at_rs = np.dot(np.swapaxes(highengdep_arr, 0, 1), out_highengphot_specs[-1].N) highengphot_spec_at_rs = highengphot_tf.sum_specs( out_highengphot_specs[-1]) lowengphot_spec_at_rs = lowengphot_tf.sum_specs( out_highengphot_specs[-1]) lowengelec_spec_at_rs = lowengelec_tf.sum_specs( out_highengphot_specs[-1]) highengphot_spec_at_rs.rs = next_rs lowengphot_spec_at_rs.rs = next_rs lowengelec_spec_at_rs.rs = next_rs if next_rs > end_rs: # Only save if next_rs < end_rs, since these are the x, Tm # values for the next redshift. # Save the x, Tm data for the next step in x_arr and Tm_arr. Tm_arr = np.append(Tm_arr, new_vals[-1, 0]) if helium_TLA: # Append the calculated xHe to x_arr. x_arr = np.append(x_arr, [[new_vals[-1, 1], new_vals[-1, 2]]], axis=0) else: # Append the baseline solution value. x_arr = np.append(x_arr, [[new_vals[-1, 1], phys.xHeII_std(next_rs)]], axis=0) # Re-define existing variables. rs = next_rs dt = dlnz * coarsen_factor / phys.hubble(rs) ######################################################################### ######################################################################### # END OF LOOP! END OF LOOP! # ######################################################################### ######################################################################### if use_tqdm: pbar.close() f_to_return = (f_low, f_high) # Some processing to get the data into presentable shape. f_low_dict = { 'H ion': f_low[:, 0], 'He ion': f_low[:, 1], 'exc': f_low[:, 2], 'heat': f_low[:, 3], 'cont': f_low[:, 4] } f_high_dict = { 'H ion': f_high[:, 0], 'He ion': f_high[:, 1], 'exc': f_high[:, 2], 'heat': f_high[:, 3], 'cont': f_high[:, 4] } f = {'low': f_low_dict, 'high': f_high_dict} data = { 'rs': out_highengphot_specs.rs, 'x': x_arr, 'Tm': Tm_arr, 'highengphot': out_highengphot_specs, 'lowengphot': out_lowengphot_specs, 'lowengelec': out_lowengelec_specs, 'f': f } return data