def noise_error_ps(nu_c, k, t, **kwargs): ''' Calculate the system noise error on the power spectrum, using the analytical expression in Mellema et al 2013 (equation 11). If no arguments are given, the noise is calculated for LOFAR-like parameters with delta k = k, and a bandwidth of 10 MHz. Parameters: * nu_c (float): the central observing frequency * k (float or array-like): the k mode(s) * t (float): the observing time in hours Valid kwargs: * Rmax (float): the radius of the array in meters * Aeff (float or function): the effective area. Can be a function of nu. * Nstat (int): the number of stations * Tsys (float or function): the system temperature. Can be a function of nu. * B (float): the bandwidth in MHz * epsilon (float): the width of the k bins in terms of k * multipole (int): if this is zero (default), the noise is calculated for the monopole (spherially-averaged). Otherwise it is calculated for the given multipole moment of the power spectrum. Returns: The system noise error in mK^2 ''' wavel = const.c / nu_c * 1.e-3 t = t * 60. * 60. #s Rmax = kwargs.get('Rmax', 1500.) Acore = Rmax**2 * np.pi #m^2 Aeff = kwargs.get('Aeff', lambda nu: 526. * (nu / 150.)**(-2)) if hasattr(Aeff, '__call__'): Aeff_val = Aeff(nu_c) else: Aeff_val = Aeff Nstat = kwargs.get('Nstat', 48) Acoll = Nstat * Aeff_val B = kwargs.get('B', 10.) Dc = cm.nu_to_cdist(nu_c) deltaDc = np.abs(Dc - cm.nu_to_cdist(nu_c + B)) OmegaFoV = wavel**2 / Aeff_val Tsys = kwargs.get('Tsys', lambda nu: (140. + 60. * (nu / 300.)**(-2.55)) * 1000.) if hasattr(Tsys, '__call__'): Tsys_val = Tsys(nu_c) else: Tsys_val = Tsys epsilon = kwargs.get('epsilon', 1) multipole = kwargs.get('multipole', 0) multipole_factor = np.sqrt(2 * multipole + 1) Delta_noise = (2. / np.pi) * k**(3. / 2.) * np.sqrt( Dc**2 * deltaDc * OmegaFoV) * (Tsys_val / np.sqrt(B * 1.e6 * t))**2 * ( Acore * Aeff_val / (Acoll**2)) / np.sqrt(epsilon) return Delta_noise * multipole_factor
def noise_error_ps(nu_c, k, t, **kwargs): ''' Calculate the system noise error on the power spectrum, using the analytical expression in Mellema et al 2013 (equation 11). If no arguments are given, the noise is calculated for LOFAR-like parameters with delta k = k, and a bandwidth of 10 MHz. Parameters: * nu_c (float): the central observing frequency * k (float or array-like): the k mode(s) * t (float): the observing time in hours Valid kwargs: * Rmax (float): the radius of the array in meters * Aeff (float or function): the effective area. Can be a function of nu. * Nstat (int): the number of stations * Tsys (float or function): the system temperature. Can be a function of nu. * B (float): the bandwidth in MHz * epsilon (float): the width of the k bins in terms of k * multipole (int): if this is zero (default), the noise is calculated for the monopole (spherially-averaged). Otherwise it is calculated for the given multipole moment of the power spectrum. Returns: The system noise error in mK^2 ''' wavel = const.c/nu_c*1.e-3 t = t*60.*60. #s Rmax = kwargs.get('Rmax', 1500.) Acore = Rmax**2*np.pi #m^2 Aeff = kwargs.get('Aeff', lambda nu: 526.*(nu/150.)**(-2)) if hasattr(Aeff, '__call__'): Aeff_val = Aeff(nu_c) else: Aeff_val = Aeff Nstat = kwargs.get('Nstat', 48) Acoll = Nstat*Aeff_val B = kwargs.get('B', 10.) Dc = cm.nu_to_cdist(nu_c) deltaDc = np.abs(Dc - cm.nu_to_cdist(nu_c+B)) OmegaFoV = wavel**2/Aeff_val Tsys = kwargs.get('Tsys', lambda nu: (140. + 60.*(nu/300.)**(-2.55))*1000.) if hasattr(Tsys, '__call__'): Tsys_val = Tsys(nu_c) else: Tsys_val = Tsys epsilon = kwargs.get('epsilon', 1) multipole = kwargs.get('multipole', 0) multipole_factor = np.sqrt(2*multipole+1) Delta_noise = (2./np.pi)*k**(3./2.)*np.sqrt(Dc**2*deltaDc*OmegaFoV)*(Tsys_val/np.sqrt(B*1.e6*t))**2*(Acore*Aeff_val/(Acoll**2))/np.sqrt(epsilon) return Delta_noise*multipole_factor
def observational_lightcone_to_physical(observational_lightcone, input_freqs, input_dtheta): ''' Interpolate a lightcone volume measured in observational (angle/frequency) units into physical (length) units. The output resolution will be set to the coarest one, as determined either by the angular or the frequency resolution. The lightcone must have the LoS as the last index, with frequencies decreasing along the LoS. Parameters: * observational_lightcone (numpy array): the input lightcone volume * input_freqs (numpy array): the frequency in MHz of each slice along the line of sight of the input * input_dheta (float): the angular size of a cell in arcmin Returns: * The output volume * The redshifts along the LoS of the output * The output cell size in Mpc ''' assert input_freqs[0] > input_freqs[-1] assert observational_lightcone.shape[0] == observational_lightcone.shape[1] #Determine new cell size - set either by frequency or angle. #The FoV size in Mpc is set by the lowest redshift dnu = input_freqs[0] - input_freqs[1] z_low = cm.nu_to_z(input_freqs[0]) fov_deg = observational_lightcone.shape[0] * input_dtheta / 60. fov_mpc = fov_deg / cm.angular_size_comoving(1., z_low) cell_size_perp = fov_mpc / observational_lightcone.shape[0] cell_size_par = cm.nu_to_cdist(input_freqs[-1]) - cm.nu_to_cdist( input_freqs[-2]) output_cell_size = max([cell_size_par, cell_size_perp]) hf.print_msg('Making physical lightcone with cell size %.2f Mpc' % output_cell_size) #Go through each slice along frequency axis. Cut off excess and #interpolate down to correct resolution n_cells_perp = int(fov_mpc / output_cell_size) output_volume_par = np.zeros( (n_cells_perp, n_cells_perp, observational_lightcone.shape[2])) for i in range(output_volume_par.shape[2]): z = cm.nu_to_z(input_freqs[i]) output_volume_par[:,:,i] = angular_slice_to_physical(observational_lightcone[:,:,i],\ z, slice_size_deg=fov_deg, output_cell_size=output_cell_size,\ output_size_mpc=fov_mpc, order=2) #Bin along frequency axis output_volume, output_redshifts = bin_lightcone_in_mpc(output_volume_par, \ input_freqs, output_cell_size) return output_volume, output_redshifts, output_cell_size
def bin_lightcone_in_mpc(lightcone, frequencies, cell_size_mpc): ''' Bin a lightcone in Mpc slices along the LoS ''' distances = cm.nu_to_cdist(frequencies) n_output_cells = (distances[-1] - distances[0]) / cell_size_mpc output_distances = np.arange(distances[0], distances[-1], cell_size_mpc) output_lightcone = np.zeros( (lightcone.shape[0], lightcone.shape[1], n_output_cells)) #Bin in Mpc by smoothing and indexing smooth_scale = np.round(len(frequencies) / n_output_cells) tophat3d = np.ones((1, 1, smooth_scale)) tophat3d /= np.sum(tophat3d) lightcone_smoothed = scipy.signal.fftconvolve(lightcone, tophat3d, mode='same') for i in range(output_lightcone.shape[2]): idx = hf.find_idx(distances, output_distances[i]) output_lightcone[:, :, i] = lightcone_smoothed[:, :, idx] output_redshifts = cm.cdist_to_z(output_distances) return output_lightcone, output_redshifts
def observational_lightcone_to_physical(observational_lightcone, input_freqs, input_dtheta): ''' Interpolate a lightcone volume measured in observational (angle/frequency) units into physical (length) units. The output resolution will be set to the coarest one, as determined either by the angular or the frequency resolution. The lightcone must have the LoS as the last index, with frequencies decreasing along the LoS. Parameters: * observational_lightcone (numpy array): the input lightcone volume * input_freqs (numpy array): the frequency in MHz of each slice along the line of sight of the input * input_dheta (float): the angular size of a cell in arcmin Returns: * The output volume * The redshifts along the LoS of the output * The output cell size in Mpc ''' assert input_freqs[0] > input_freqs[-1] assert observational_lightcone.shape[0] == observational_lightcone.shape[1] #Determine new cell size - set either by frequency or angle. #The FoV size in Mpc is set by the lowest redshift dnu = input_freqs[0]-input_freqs[1] z_low = cm.nu_to_z(input_freqs[0]) fov_deg = observational_lightcone.shape[0]*input_dtheta/60. fov_mpc = fov_deg/cm.angular_size_comoving(1., z_low) cell_size_perp = fov_mpc/observational_lightcone.shape[0] cell_size_par = cm.nu_to_cdist(input_freqs[-1])-cm.nu_to_cdist(input_freqs[-2]) output_cell_size = max([cell_size_par, cell_size_perp]) hf.print_msg('Making physical lightcone with cell size %.2f Mpc' % output_cell_size) #Go through each slice along frequency axis. Cut off excess and #interpolate down to correct resolution n_cells_perp = int(fov_mpc/output_cell_size) output_volume_par = np.zeros((n_cells_perp, n_cells_perp, observational_lightcone.shape[2])) for i in range(output_volume_par.shape[2]): z = cm.nu_to_z(input_freqs[i]) output_volume_par[:,:,i] = angular_slice_to_physical(observational_lightcone[:,:,i],\ z, slice_size_deg=fov_deg, output_cell_size=output_cell_size,\ output_size_mpc=fov_mpc, order=2) #Bin along frequency axis output_volume, output_redshifts = bin_lightcone_in_mpc(output_volume_par, \ input_freqs, output_cell_size) return output_volume, output_redshifts, output_cell_size
def bin_lightcone_in_frequency(lightcone, z_low, box_size_mpc, dnu): ''' Bin a lightcone in frequency bins. Parameters: * lightcone (numpy array): the lightcone in length units * z_low (float): the lowest redshift of the lightcone * box_size_mpc (float): the side of the lightcone in Mpc * dnu (float): the width of the frequency bins in MHz Returns: The lightcone, binned in frequencies with high frequencies first The frequencies along the line of sight in MHz ''' #Figure out dimensions and make output volume cell_size = box_size_mpc / lightcone.shape[0] distances = cm.z_to_cdist(z_low) + np.arange( lightcone.shape[2]) * cell_size input_redshifts = cm.cdist_to_z(distances) input_frequencies = cm.z_to_nu(input_redshifts) nu1 = input_frequencies[0] nu2 = input_frequencies[-1] output_frequencies = np.arange(nu1, nu2, -dnu) output_lightcone = np.zeros((lightcone.shape[0], lightcone.shape[1], \ len(output_frequencies))) #Bin in frequencies by smoothing and indexing max_cell_size = cm.nu_to_cdist(output_frequencies[-1]) - cm.nu_to_cdist( output_frequencies[-2]) smooth_scale = np.round(max_cell_size / cell_size) if smooth_scale < 1: smooth_scale = 1 hf.print_msg('Smooth along LoS with scale %f' % smooth_scale) tophat3d = np.ones((1, 1, smooth_scale)) tophat3d /= np.sum(tophat3d) lightcone_smoothed = scipy.signal.fftconvolve(lightcone, tophat3d, mode='same') for i in range(output_lightcone.shape[2]): nu = output_frequencies[i] idx = hf.find_idx(input_frequencies, nu) output_lightcone[:, :, i] = lightcone_smoothed[:, :, idx] return output_lightcone, output_frequencies
def get_lightcone_subvolume(lightcone, redshifts, central_z, \ depth_mhz=None, depth_mpc=None, odd_num_cells=True, \ subtract_mean=True, fov_Mpc=None): ''' Extract a subvolume from a lightcone, at a given central redshift, and with a given depth. The depth can be specified in Mpc or MHz. You must give exactly one of these parameters. Parameters: * ligthcone (numpy array): the lightcone * redshifts (numpy array): the redshifts along the LOS * central_z (float): the central redshift of the subvolume * depth_mhz (float): the depth of the subvolume in MHz * depth_mpc (float): the depth of the subvolume in Mpc * odd_num_cells (bool): if true, the depth of the box will always be an odd number of cells. This avoids problems with power spectrum calculations. * subtract_mean (bool): if true, subtract the mean of the signal (Default: True) * fov_Mpc (float): the FoV size in Mpc Returns: Tuple with (subvolume, dims) where dims is a tuple with the dimensions of the subvolume in Mpc ''' assert len(np.nonzero([depth_mhz, depth_mpc])) == 1 if fov_Mpc == None: fov_Mpc = conv.LB central_nu = cm.z_to_nu(central_z) if depth_mpc != None: #Depth is given in Mpc central_dist = cm.nu_to_cdist(central_nu) low_z = cm.cdist_to_z(central_dist-depth_mpc/2.) high_z = cm.cdist_to_z(central_dist+depth_mpc/2.) else: #Depth is given in MHz low_z = cm.nu_to_z(central_nu+depth_mhz/2.) high_z = cm.nu_to_z(central_nu-depth_mhz/2.) if low_z < redshifts.min(): raise Exception('Lowest z is outside range') if high_z > redshifts.max(): raise Exception('Highest z is outside range') low_n = int(find_idx(redshifts, low_z)) high_n = int(find_idx(redshifts, high_z)) if (high_n-low_n) % 2 == 0 and odd_num_cells: high_n += 1 subbox = lightcone[:,:,low_n:high_n] if subtract_mean: subbox = st.subtract_mean_signal(subbox, los_axis=2) box_depth = float(subbox.shape[2])/lightcone.shape[1]*fov_Mpc box_dims = (fov_Mpc, fov_Mpc, box_depth) return subbox, box_dims
def get_lightcone_subvolume(lightcone, redshifts, central_z, \ depth_mhz=None, depth_mpc=None, odd_num_cells=True, \ subtract_mean=True, fov_Mpc=None): ''' Extract a subvolume from a lightcone, at a given central redshift, and with a given depth. The depth can be specified in Mpc or MHz. You must give exactly one of these parameters. Parameters: * ligthcone (numpy array): the lightcone * redshifts (numpy array): the redshifts along the LOS * central_z (float): the central redshift of the subvolume * depth_mhz (float): the depth of the subvolume in MHz * depth_mpc (float): the depth of the subvolume in Mpc * odd_num_cells (bool): if true, the depth of the box will always be an odd number of cells. This avoids problems with power spectrum calculations. * subtract_mean (bool): if true, subtract the mean of the signal (Default: True) * fov_Mpc (float): the FoV size in Mpc Returns: Tuple with (subvolume, dims) where dims is a tuple with the dimensions of the subvolume in Mpc ''' assert len(np.nonzero([depth_mhz, depth_mpc])) == 1 if fov_Mpc == None: fov_Mpc = conv.LB central_nu = cm.z_to_nu(central_z) if depth_mpc != None: #Depth is given in Mpc central_dist = cm.nu_to_cdist(central_nu) low_z = cm.cdist_to_z(central_dist - depth_mpc / 2.) high_z = cm.cdist_to_z(central_dist + depth_mpc / 2.) else: #Depth is given in MHz low_z = cm.nu_to_z(central_nu + depth_mhz / 2.) high_z = cm.nu_to_z(central_nu - depth_mhz / 2.) if low_z < redshifts.min(): raise Exception('Lowest z is outside range') if high_z > redshifts.max(): raise Exception('Highest z is outside range') low_n = int(find_idx(redshifts, low_z)) high_n = int(find_idx(redshifts, high_z)) if (high_n - low_n) % 2 == 0 and odd_num_cells: high_n += 1 subbox = lightcone[:, :, low_n:high_n] if subtract_mean: subbox = st.subtract_mean_signal(subbox, los_axis=2) box_depth = float(subbox.shape[2]) / lightcone.shape[1] * fov_Mpc box_dims = (fov_Mpc, fov_Mpc, box_depth) return subbox, box_dims
def bin_lightcone_in_frequency(lightcone, z_low, box_size_mpc, dnu): ''' Bin a lightcone in frequency bins. Parameters: * lightcone (numpy array): the lightcone in length units * z_low (float): the lowest redshift of the lightcone * box_size_mpc (float): the side of the lightcone in Mpc * dnu (float): the width of the frequency bins in MHz Returns: The lightcone, binned in frequencies with high frequencies first The frequencies along the line of sight in MHz ''' #Figure out dimensions and make output volume cell_size = box_size_mpc/lightcone.shape[0] distances = cm.z_to_cdist(z_low) + np.arange(lightcone.shape[2])*cell_size input_redshifts = cm.cdist_to_z(distances) input_frequencies = cm.z_to_nu(input_redshifts) nu1 = input_frequencies[0] nu2 = input_frequencies[-1] output_frequencies = np.arange(nu1, nu2, -dnu) output_lightcone = np.zeros((lightcone.shape[0], lightcone.shape[1], \ len(output_frequencies))) #Bin in frequencies by smoothing and indexing max_cell_size = cm.nu_to_cdist(output_frequencies[-1])-cm.nu_to_cdist(output_frequencies[-2]) smooth_scale = np.round(max_cell_size/cell_size) if smooth_scale < 1: smooth_scale = 1 hf.print_msg('Smooth along LoS with scale %f' % smooth_scale) tophat3d = np.ones((1,1,smooth_scale)) tophat3d /= np.sum(tophat3d) lightcone_smoothed = fftconvolve(lightcone, tophat3d) for i in range(output_lightcone.shape[2]): nu = output_frequencies[i] idx = hf.find_idx(input_frequencies, nu) output_lightcone[:,:,i] = lightcone_smoothed[:,:,idx] return output_lightcone, output_frequencies
def bin_lightcone_in_mpc(lightcone, frequencies, cell_size_mpc): ''' Bin a lightcone in Mpc slices along the LoS ''' distances = cm.nu_to_cdist(frequencies) n_output_cells = (distances[-1]-distances[0])/cell_size_mpc output_distances = np.arange(distances[0], distances[-1], cell_size_mpc) output_lightcone = np.zeros((lightcone.shape[0], lightcone.shape[1], n_output_cells)) #Bin in Mpc by smoothing and indexing smooth_scale = np.round(len(frequencies)/n_output_cells) tophat3d = np.ones((1,1,smooth_scale)) tophat3d /= np.sum(tophat3d) lightcone_smoothed = fftconvolve(lightcone, tophat3d, mode='same') for i in range(output_lightcone.shape[2]): idx = hf.find_idx(distances, output_distances[i]) output_lightcone[:,:,i] = lightcone_smoothed[:,:,idx] output_redshifts = cm.cdist_to_z(output_distances) return output_lightcone, output_redshifts