def main(directory, psfex_file, image, prefix, output_file): print("\nExecuting Python script pipeline_fors2/9-psf.py, with:") print(f"\tmain data directory {directory}") print(f"\tPSFEx file {psfex_file}") print(f"\timage file {image}") print(f"\toutput file {output_file}") print(f"\tfilter prefix {prefix}") print() _, pix_scale = ff.get_pixel_scale(image) pix_scale = float(pix_scale) fwhm_pix = float( ff.get_header_attribute(file=psfex_file, attribute='PSF_FWHM', ext=1)) fwhm_deg = fwhm_pix * pix_scale fwhm_arcsec = fwhm_deg * 3600. param_dict = { f'{prefix}_pixel_scale': pix_scale, f'{prefix}_fwhm_pix': fwhm_pix, f'{prefix}_fwhm_deg': fwhm_deg, f'{prefix}_fwhm_arcsec': fwhm_arcsec, } p.add_params(file=directory + '/' + output_file, params=param_dict) p.add_params(file=directory + '/' + 'output_paths', params={f'{prefix}_psf_model': psfex_file}) p.yaml_to_json(directory + '/' + output_file)
def plot_gal_params( hdu: fits.HDUList, ras: Union[list, np.ndarray, float], decs: Union[list, np.ndarray, float], a: Union[list, np.ndarray, float], b: Union[list, np.ndarray, float], theta: Union[list, np.ndarray, float], colour: str = 'white', show_centre: bool = False, label: str = None, world: bool = True, world_axes: bool = True, **kwargs): """ :param hdu: :param ras: In degrees. :param decs: In degrees. :param a: In degrees. :param b: In degrees. :param theta: In degrees, apparently. :param colour: :param offset: :param show_centre: :return: """ # TODO: Rename parameters to reflect acceptance of pixel coordinates. _, pix_scale = ff.get_pixel_scale(hdu) n_y, n_x = hdu[0].data.shape header = hdu[0].header wcs_image = wcs.WCS(header=header) if world: xs, ys = wcs_image.all_world2pix(ras, decs, 0) else: xs = np.array(ras) ys = np.array(decs) theta = np.array(theta) # Convert to photutils angle format theta = u.world_angle_se_to_pu(theta, rot_angle=ff.get_rotation_angle(header)) a = u.dequantify(a) b = u.dequantify(b) for i, x in enumerate(xs): u.debug_print(2, "plotting.plot_gal_params(): x, ys[i] ==", x, ys[i]) if a[i] != 0 and b[i] != 0: if world_axes: ellipse = photutils.EllipticalAperture((x, ys[i]), a=a[i] / pix_scale, b=b[i] / pix_scale, theta=theta[i]) else: ellipse = photutils.EllipticalAperture((x, ys[i]), a=a[i], b=b[i], theta=theta[i]) ellipse.plot(**kwargs) line_label = None else: line_label = label if show_centre: plt.plot((0.0, n_x), (ys[i], ys[i]), c=colour, label=line_label) plt.plot((x, x), (0.0, n_y), c=colour)
def distance_bar(hdu: fits.hdu.HDUList, ang_size_distance: float, x: float, y: float, frame: int, colour: str = 'white', length: float = None, angle_length: float = None, spread: float = 1., reverse_y=False): """ Draw a projected distance bar on your plot. :param hdu: :param ang_size_distance: Cosmological angular size distance, in parsecs. :param length: In parsecs, the length of bar you want to show. One of :param angle_length: In arcsecs, the length of the bar you want to show. :param x: Position of bar in plot. :param y: Position of bar in plot. :param colour: Colour of bar and text. :return: """ if angle_length is length is None: raise ValueError('Either length or angle_length must be provided.') if angle_length is not None and length is not None: raise ValueError('Only one of length or angle_length can be provided.') # Get the angular pixel scale (degrees per pixel) _, pix_angle_scale = ff.get_pixel_scale(hdu) # Get the distance pixel scale (parsecs per pixel) pix_length_scale = ff.projected_pix_scale(hdu=hdu, ang_size_distance=ang_size_distance) if angle_length is None: # Get the length of the bar in pixels. pix_length = length / pix_length_scale # Use the angular pixel scale to get the angular length of the bar angle_length = pix_length * pix_angle_scale else: # Convert to degrees. angle_length /= 3600 # Get the length of the bar in pixels. pix_length = angle_length / pix_angle_scale # Use the distance pixel scale to get the projected length of the bar. length = pix_length * pix_length_scale angle_length *= 3600 print('Projected length:', length, 'pc') print('Angular size:', angle_length, 'arcsecs') if reverse_y: plt.plot((x, x + pix_length), (2 * frame - y, 2 * frame - y), c=colour) plt.text(x, 2 * frame - y - spread, f'{np.round(length / 1000, 1)} kpc', color=colour) plt.text(x, 2 * frame - y + 1.7 * spread, f'{int(angle_length)} arcsec', color=colour) else: plt.plot((x, x + pix_length), (y, y), c=colour) plt.text(x, y + spread, f'{np.round(length / 1000, 1)} kpc', color=colour) plt.text(x, y - 1.7 * spread, f'{int(angle_length)} arcsec', color=colour)
def plot_gal_params(hdu: fits.HDUList, ras: Union[list, np.ndarray, float], decs: Union[list, np.ndarray, float], a: Union[list, np.ndarray, float], b: Union[list, np.ndarray, float], theta: Union[list, np.ndarray, float], colour: str = 'white', show_centre: bool = False, label: str = None, world: bool = True, world_axes: bool = True, line_style='-'): """ :param hdu: :param ras: In degrees. :param decs: In degrees. :param a: In degrees. :param b: In degrees. :param theta: In degrees, apparently. :param colour: :param offset: :param show_centre: :return: """ # TODO: Rename parameters to reflect acceptance of pixel coordinates. _, pix_scale = ff.get_pixel_scale(hdu) n_y, n_x = hdu[0].data.shape header = hdu[0].header wcs_image = wcs.WCS(header=header) if world: xs, ys = wcs_image.all_world2pix(ras, decs, 0) else: xs = np.array(ras) ys = np.array(decs) theta = np.array(theta) theta = -theta + ff.get_rotation_angle(header) # Convert to radians theta = theta * np.pi / 180 for i, x in enumerate(xs): if a[i] != 0 and b[i] != 0: if world_axes: ellipse = photutils.EllipticalAperture((x, ys[i]), a=a[i] / pix_scale, b=b[i] / pix_scale, theta=theta[i]) else: ellipse = photutils.EllipticalAperture((x, ys[i]), a=a[i], b=b[i], theta=theta[i]) ellipse.plot(color=colour, label=label, ls=line_style) line_label = None else: line_label = label if show_centre: plt.plot((0.0, n_x), (ys[i], ys[i]), c=colour, label=line_label) plt.plot((x, x), (0.0, n_y), c=colour)
def main(directory, psfex_file, image, prefix): _, pix_scale = ff.get_pixel_scale(image) pix_scale = float(pix_scale) fwhm_pix = float(ff.get_header_attribute(file=psfex_file, attribute='PSF_FWHM', ext=1)) fwhm_deg = fwhm_pix * pix_scale fwhm_arcsec = fwhm_deg * 3600. param_dict = {f'{prefix}_pixel_scale': pix_scale, f'{prefix}_fwhm_pix': fwhm_pix, f'{prefix}_fwhm_deg': fwhm_deg, f'{prefix}_fwhm_arcsec': fwhm_arcsec, } p.add_params(file=directory + '/output_values', params=param_dict) p.yaml_to_json(directory + '/output_values')
def main(data_dir, data_title, origin, destination, all_synths): print("\nExecuting Python script pipeline_fors2/5-background_subtract.py, with:") print(f"\tepoch {data_title}") print(f"\torigin directory {origin}") print(f"\tdestination directory {destination}") print() methods = ["ESO backgrounds only", "SExtractor backgrounds only", "polynomial fit", "Gaussian fit", "median value"] if all_synths: frame = 56 method = "polynomial fit" degree = 5 do_mask = True local = True global_sub = False trim_image = False recorrect_subbed = True eso_back = False else: frame = 200 # frame_arcsec = 30 * units.arcsec # frame_deg = frame_arcsec.to(units.deg) eso_back = False _, method = u.select_option(message="Please select the background subtraction method.", options=methods, default="polynomial fit") degree = None if method == "polynomial fit": degree = u.user_input(message=f"Please enter the degree of {method} to use:", typ=int, default=3) elif method == "ESO backgrounds only": eso_back = True do_mask = False if method not in ["ESO backgrounds only", "SExtractor backgrounds only", "median value"]: do_mask = u.select_yn(message="Mask sources using SExtractor catalogue?", default=True) if method in ["polynomial fit", "Gaussian fit"]: local = u.select_yn(message="Use a local fit?", default=True) else: local = False global_sub = False trim_image = False recorrect_subbed = False if local: global_sub = u.select_yn(message="Subtract local fit from entire image?", default="n") if not global_sub: trim_image = u.select_yn(message="Trim images to subtracted region?", default="y") recorrect_subbed = u.select_yn(message="Re-normalise background of subtracted region?", default="y") # if not eso_back and method != "SExtractor backgrounds only": # eso_back = u.select_yn(message="Subtract ESO Reflex fitted backgrounds first?", default=False) outputs = p.object_output_params(data_title, instrument='FORS2') data_dir = u.check_trailing_slash(data_dir) destination = u.check_trailing_slash(destination) destination = data_dir + destination u.mkdir_check_nested(destination) origin = u.check_trailing_slash(origin) science_origin = data_dir + origin + "science/" print(science_origin) filters = outputs['filters'] frb_params = p.object_params_frb(obj=data_title[:-2]) epoch_params = p.object_params_fors2(obj=data_title) background_origin_eso = "" if eso_back: background_origin_eso = data_dir + "/" + origin + "/backgrounds/" if method == "SExtractor backgrounds only": background_origin = f"{data_dir}{origin}backgrounds_sextractor/" elif method == "polynomial fit": background_origin = f"{destination}backgrounds/" # f"{destination}backgrounds_{method.replace(' ', '')}_degree_{degree}_local_{local}_globalsub_{global_sub}/" else: background_origin = f"{destination}backgrounds/" # f"{destination}backgrounds_{method.replace(' ', '')}_local_{local}_globalsub_{global_sub}/" trimmed_path = "" if trim_image: trimmed_path = f"{data_dir}{origin}trimmed_to_background/" u.mkdir_check_nested(trimmed_path) ra = frb_params["burst_ra"] dec = frb_params["burst_dec"] if all_synths: ras = epoch_params["test_synths"]["ra"] decs = epoch_params["test_synths"]["dec"] else: ras = [ra] decs = [dec] for fil in filters: trimmed_path_fil = "" if trim_image: trimmed_path_fil = f"{trimmed_path}{fil}/" u.mkdir_check(trimmed_path_fil) background_fil_dir = f"{background_origin}{fil}/" u.mkdir_check_nested(background_fil_dir) science_destination_fil = f"{destination}science/{fil}/" u.mkdir_check_nested(science_destination_fil) files = os.listdir(science_origin + fil + "/") for file_name in files: if file_name.endswith('.fits'): new_file = file_name.replace("norm", "bg_sub") new_path = f"{science_destination_fil}/{new_file}" print("NEW_PATH:", new_path) science = science_origin + fil + "/" + file_name # First subtract ESO Reflex background images # frame = (frame_deg / f.get_pixel_scale(file=science, astropy_units=True)[1]).to(f.pix).value if eso_back: background_eso = background_origin_eso + fil + "/" + file_name.replace("SCIENCE_REDUCED", "PHOT_BACKGROUND_SCI") ff.subtract_file(file=science, sub_file=background_eso, output=new_path) science_image = new_path if method != "ESO backgrounds only": print(ra, dec) print("Science image:", science) science_image = fits.open(science) print("Science file:", science_image) wcs_this = WCS(header=science_image[0].header) if method == "SExtractor backgrounds only": background = background_origin + fil + "/" + file_name + "_back.fits" print("Background image:", background) else: if method == "median value": print(science_image[0].data.shape) _, background_value, _ = sigma_clipped_stats(science_image[0].data) background = deepcopy(science_image) background[0].data = np.full(shape=science_image[0].data.shape, fill_value=background_value) background_path = background_origin + fil + "/" + file_name.replace("SCIENCE_REDUCED", "PHOT_BACKGROUND_MEDIAN") # Next do background fitting. else: background = deepcopy(science_image) background[0].data = np.zeros(background[0].data.shape) background_path = background_origin + fil + "/" + file_name.replace("SCIENCE_REDUCED", "PHOT_BACKGROUND_FITTED") for i, ra in enumerate(ras): dec = decs[i] x, y = wcs_this.all_world2pix(ra, dec, 0) print(x, y) bottom, top, left, right = ff.subimage_edges(data=science_image[0].data, x=x, y=y, frame=frame) if do_mask: # Produce a pixel mask that roughly masks out the true sources in the image so that # they don't get fitted. mask_max = 10 _, pixel_scale = ff.get_pixel_scale(science_image) sextractor = Table.read( f"{data_dir}analysis/sextractor/4-divided_by_exp_time/{fil}/{file_name.replace('.fits', '_psf-fit.cat')}", format='ascii.sextractor') weights = np.ones(shape=science_image[0].data.shape) for obj in filter( lambda o: left < o["X_IMAGE"] < right and bottom < o["Y_IMAGE"] < top, sextractor): mask_rad = min(int(obj["A_WORLD"] * obj["KRON_RADIUS"] / pixel_scale), mask_max) x_prime = int(np.round(obj["X_IMAGE"])) y_prime = int(np.round(obj["Y_IMAGE"])) weights[y_prime - mask_rad:y_prime + mask_rad, x_prime - mask_rad:x_prime + mask_rad] = 0.0 plt.imshow(weights, origin="lower") plt.savefig( background_origin + fil + "/" + file_name.replace("norm.fits", "mask.png")) else: weights = None background_this = fit_background_fits(image=science_image, model_type=method[:method.find(" ")], deg=degree, local=local, global_sub=global_sub, centre_x=x, centre_y=y, frame=frame, weights=weights) background[0].data += background_this[0].data if recorrect_subbed: offset = get_median_background(image=science, ra=epoch_params["renormalise_centre_ra"], dec=epoch_params["renormalise_centre_dec"], frame=50, show=False, output=new_path[ :new_path.find("bg_sub")] + "renorm_patch_") print("RECORRECT_SUBBED:", recorrect_subbed) print("SUBTRACTING FROM BACKGROUND:", offset) print(bottom, top, left, right) print(background[0].data[bottom:top, left:right].shape) print(np.median(background[0].data[bottom:top, left:right])) background[0].data[bottom:top, left:right] -= offset print(np.median(background[0].data[bottom:top, left:right])) if trim_image: print("TRIMMED_PATH_FIL:", trimmed_path_fil) science_image = ff.trim_file(path=science_image, left=left, right=right, top=top, bottom=bottom, new_path=trimmed_path_fil + file_name.replace( "norm.fits", "trimmed_to_back.fits")) print("Science after trim:", science_image) background = ff.trim_file(path=background, left=left, right=right, top=top, bottom=bottom, new_path=background_path) print("Writing background to:") print(background_path) background.writeto(background_path, overwrite=True) print("SCIENCE:", science_image) print("BACKGROUND:", background) subbed = ff.subtract_file(file=science_image, sub_file=background, output=new_path) # # TODO: check if regions overlap # # plt.hist(subbed[0].data[int(y - frame + 1):int(y + frame - 1), # int(x - frame + 1):int(x + frame - 1)].flatten(), # bins=10) # plt.savefig(new_path[:new_path.find("bg_sub")] + "histplot.png") # plt.close() copyfile(data_dir + "/" + origin + "/" + data_title + ".log", destination + data_title + ".log") u.write_log(path=destination + data_title + ".log", action=f'Backgrounds subtracted using 4-background_subtract.py with method {method}\n')
def main(image, cat_path_1, cat_path_2, cat_name_1, cat_name_2, offset_x, offset_y, psf): if cat_name_1 == 'DES': ra_name_1 = 'RA' dec_name_1 = 'DEC' else: ra_name_1 = 'ra' dec_name_1 = 'dec' if cat_name_2 == 'DES': ra_name_2 = 'RA' dec_name_2 = 'DEC' else: ra_name_2 = 'ra' dec_name_2 = 'dec' if psf: names = p.sextractor_names_psf() else: names = p.sextractor_names() if cat_name_1 == 'SExtractor': cat_1 = table.Table(np.genfromtxt(cat_path_1, names=names)) else: cat_1 = table.Table() cat_1 = cat_1.read(cat_path_1, format='ascii.csv') if cat_name_2 == 'SExtractor': cat_2 = table.Table(np.genfromtxt(cat_path_2, names=names)) else: cat_2 = table.Table() cat_2 = cat_2.read(cat_path_2, format='ascii.csv') param_dict = {} image = fits.open(image) header = image[0].header data = image[0].data wcs_info = wcs.WCS(header=header) cat_1['x'], cat_1['y'] = wcs_info.all_world2pix(cat_1[ra_name_1], cat_1[dec_name_1], 0) cat_2['x'], cat_2['y'] = wcs_info.all_world2pix(cat_2[ra_name_2], cat_2[dec_name_2], 0) norm = pl.nice_norm(data) plt.subplot(projection=wcs_info) plt.imshow(data, norm=norm, origin='lower', cmap='viridis') plt.scatter(cat_1['x'], cat_1['y'], label=cat_name_1, c='violet') plt.scatter(cat_2['x'], cat_2['y'], label=cat_name_2, c='red') plt.legend() plt.show() scale_ra, scale_dec = ff.get_pixel_scale(image) ra_corrected = cat_2[ra_name_2] + offset_x * scale_ra dec_corrected = cat_2[dec_name_2] + offset_y * scale_ra x, y = wcs_info.all_world2pix(ra_corrected, dec_corrected, 0) norm = pl.nice_norm(data) plt.subplot(projection=wcs_info) plt.imshow(data, norm=norm, origin='lower', cmap='viridis') plt.scatter(cat_1['x'], cat_1['y'], label=cat_name_1, c='violet') plt.scatter(x, y, label=cat_name_2, c='red') plt.legend() plt.show() image.close()
def tweak(sextractor_path: str, destination: str, image_path: str, cat_path: str, cat_name: str, tolerance: float = 10., show: bool = False, stars_only: bool = False, manual: bool = False, offset_x: float = None, offset_y: float = None, offsets_world: bool = False, psf: bool = True, specific_star: bool = False, star_ra: float = None, star_dec: float = None): """ For tweaking the astrometric solution of a fits image using a catalogue; either matches as many stars as possible and uses the median offset, uses a single star at a specified position (specific_star=True) or a manual offset. :param sextractor_path: Path to SExtractor-generated catalogue. :param destination: Directory to write tweaked image to. :param image_path: Path to image FITS file :param cat_path: Path to file containing catalogue. :param cat_name: Name of catalogue used. :param tolerance: Tolerance, in pixels, within which matches will be accepted. :param show: Plot matches onscreen? :param stars_only: Only match using stars, determined using SExtractor's 'class_star' output. :param manual: Use manual offset? :param offset_x: Offset in x to use; only if 'manual' is set to True. :param offset_y: Offset in y to use; only if 'manual' is set to True. :param offsets_world: If True, interprets the offsets as being given in World Coordinates (RA and DEC) :param psf: Use the PSF-fitting position? :param specific_star: Use a specific star to tweak? This means, instead of finding the closest matches for many stars, alignment is attempted with a single star nearest the given position. :param star_ra: Right Ascension of star for single-star alignment. :param star_dec: Declination of star for single-star alignment. :return: None """ param_dict = {} print(image_path) image = fits.open(image_path) header = image[0].header data = image[0].data wcs_info = wcs.WCS(header=header) # Set the appropriate column names depending on the format of the catalogue to use. if cat_name == 'DES': ra_name = 'RA' dec_name = 'DEC' elif cat_name == 'Gaia' or cat_name == 'SDSS': ra_name = 'ra' dec_name = 'dec' else: if psf: ra_name = 'ra_psf' dec_name = 'dec_psf' else: ra_name = 'ra' dec_name = 'dec' if psf: names = p.sextractor_names_psf() sextractor_ra_name = 'ra_psf' sextractor_dec_name = 'dec_psf' else: names = p.sextractor_names() sextractor_ra_name = 'ra' sextractor_dec_name = 'dec' if show or not manual: if cat_name == 'SExtractor': cat = table.Table(np.genfromtxt(cat_path, names=names)) else: cat = table.Table() cat = cat.read(cat_path, format='ascii.csv') sextracted = table.Table(np.genfromtxt(sextractor_path, names=names)) if stars_only: sextracted = sextracted[sextracted['class_star'] > 0.9] cat['x'], cat['y'] = wcs_info.all_world2pix(cat[ra_name], cat[dec_name], 0) x, y = wcs_info.all_world2pix(sextracted[sextractor_ra_name], sextracted[sextractor_dec_name], 0) norm = ImageNormalize(data, interval=ZScaleInterval(), stretch=SqrtStretch()) if show: # plt.subplot(projection=wcs_info) plt.imshow(data, norm=norm, origin='lower', cmap='viridis') plt.scatter(x, y, label='SExtractor', c='violet') plt.scatter(cat['x'], cat['y'], label=cat_name, c='red') plt.legend() plt.title('Pre-Correction') plt.show() if manual: if offset_x is not None and offset_y is not None: if not offsets_world: scale_ra, scale_dec = ff.get_pixel_scale(image) offset_ra = -offset_x * abs(scale_ra) offset_dec = -offset_y * abs(scale_dec) print(offset_x, offset_y) else: offset_ra = offset_x offset_dec = offset_y else: print('Set offsets in epoch .yaml file') offset_ra = offset_dec = None elif specific_star: if star_ra is not None and star_dec is not None: print(star_ra, star_dec) star_cat, d = u.find_object(x=star_ra, y=star_dec, x_search=cat[ra_name], y_search=cat[dec_name]) print('MD:', d) star_cat = cat[star_cat] star_sex, d = u.find_object( x=star_ra, y=star_dec, x_search=sextracted[sextractor_ra_name], y_search=sextracted[sextractor_dec_name]) print('MD:', d) star_sex = sextracted[star_sex] offset_ra = (star_sex[sextractor_ra_name] - star_cat[ra_name]) offset_dec = (star_sex[sextractor_dec_name] - star_cat[dec_name]) else: raise ValueError( 'If doing specific_star, must specify star_ra and star_dec') else: match_ids, match_ids_cat = u.match_cat(x_match=sextracted['x'], y_match=sextracted['y'], x_cat=cat['x'], y_cat=cat['y'], tolerance=tolerance) sextracted = sextracted[match_ids] cat = cat[match_ids_cat] offsets_ra = sextracted[sextractor_ra_name] - cat[ra_name] offsets_dec = sextracted[sextractor_dec_name] - cat[dec_name] offset_ra = np.nanmedian(offsets_ra) offset_dec = np.nanmedian(offsets_dec) # TODO: This is quick and dirty and will only work if the image is approximately oriented along x=RA # and y=DEC (CROTA ~ 0) if offset_ra is not None and offset_dec is not None: param_dict['offset_ra'] = float(offset_ra) param_dict['offset_dec'] = float(offset_dec) image = offset_astrometry(hdu=image, offset_ra=offset_ra, offset_dec=offset_dec, output=destination) if show and not manual: wcs_info = wcs.WCS(header=header) cat['x'], cat['y'] = wcs_info.all_world2pix( cat[ra_name], cat[dec_name], 0) x, y = wcs_info.all_world2pix( sextracted[sextractor_ra_name] - offset_ra, sextracted[sextractor_dec_name] - offset_dec, 0) if show: plt.subplot(projection=wcs_info) plt.imshow(data, norm=norm, origin='lower', cmap='viridis') plt.scatter(x, y, label='SExtractor', c='violet') plt.scatter(cat['x'], cat['y'], label=cat_name, c='red') plt.legend() plt.title('Post-Correction') plt.show() image.close() return param_dict
def distance_bar(hdu: fits.hdu.HDUList, ang_size_distance: float, x: float, y: float, frame: int, length: float = None, angle_length: float = None, spread: float = 1., reverse_y=False, fancy: bool = False, x_bar: float = None, line_kwargs: dict = {}, text_kwargs: dict = {}, fancy_line_kwargs: dict = {"lw": 0.5, "color": "red"}): """ Draw a projected distance bar on your plot. :param hdu: :param ang_size_distance: Cosmological angular size distance, in parsecs. :param length: In parsecs, the length of bar you want to show. One of :param angle_length: In arcsecs, the length of the bar you want to show. :param x: Position of bar in plot. :param y: Position of bar in plot. :return: """ if angle_length is length is None: raise ValueError('Either length or angle_length must be provided.') if angle_length is not None and length is not None: raise ValueError('Only one of length or angle_length can be provided.') # Get the angular pixel scale (degrees per pixel) _, pix_angle_scale = ff.get_pixel_scale(hdu) # Get the distance pixel scale (parsecs per pixel) pix_length_scale = ff.projected_pix_scale(hdu=hdu, ang_size_distance=ang_size_distance) if angle_length is None: # Get the length of the bar in pixels. pix_length = length / pix_length_scale # Use the angular pixel scale to get the angular length of the bar angle_length = pix_length * pix_angle_scale else: # Convert to degrees. angle_length /= 3600 # Get the length of the bar in pixels. pix_length = angle_length / pix_angle_scale # Use the distance pixel scale to get the projected length of the bar. length = pix_length * pix_length_scale angle_length *= 3600 print('Projected length:', length, 'pc') print('Angular size:', angle_length, 'arcsecs') if "lw" in line_kwargs: lw = line_kwargs["lw"] else: lw = 1 if x_bar is None: x_bar = x if reverse_y: x_left = x_bar x_right = x_bar + pix_length y_bar = 2 * frame - y y_text_kpc = 2 * frame - y - spread y_text_arcsec = 2 * frame - y + 1.7 * spread else: x_left = x_bar + 0.5 x_right = x_bar + 0.5 + pix_length y_bar = y y_text_kpc = y + spread y_text_arcsec = y - 1.7 * spread plt.plot((x_left, x_right), (y_bar, y_bar), **line_kwargs) if fancy: # Large bar left endcap plt.plot((x_left, x_left), (y_bar + spread / 3, y_bar - spread / 3), **line_kwargs) # Large bar right endcap plt.plot((x_right, x_right), (y_bar + spread / 3, y_bar - spread / 3), **line_kwargs) # Small bar plt.plot((x_left, x_right), (y_bar, y_bar), **fancy_line_kwargs) # Small bar left endcap plt.plot((x_left, x_left), (y_bar + spread / 3, y_bar - spread / 3), **fancy_line_kwargs) # Small bar right endcap plt.plot((x_right, x_right), (y_bar + spread / 3, y_bar - spread / 3), **fancy_line_kwargs) plt.text(x, y_text_kpc, f'{np.round(length / 1000, 1)} kpc', **text_kwargs) plt.text(x, y_text_arcsec, f'{int(angle_length)} arcsec', **text_kwargs)
def main(field, subtraction_path, epoch, instrument): comparison_name = f'{field}_{epoch}' params = p.object_params_frb(field) ra_burst = params['burst_ra'] dec_burst = params['burst_dec'] burst_err_a = params['burst_err_a'] burst_err_b = params['burst_err_b'] burst_err_theta = params['burst_err_theta'] comparison_params = p.object_params_instrument(comparison_name, instrument) subtraction_path = f'{params["data_dir"]}subtraction/{subtraction_path}' filters = params['filters'] for f in filters: f_0 = f[0] destination_path_filter = f'{subtraction_path}{f}/' template_epoch = params['template_epoch'] comparison_extinction = comparison_params[f_0 + '_ext_up'] cat_generated_file = filter(lambda file: file[-15:] == '_comparison.csv', os.listdir(f'{destination_path_filter}')).__next__() comparison_image_file = filter(lambda file: file[-24:] == '_comparison_aligned.fits', os.listdir(f'{destination_path_filter}')).__next__() difference_image_file = filter(lambda file: file[-16:] == '_difference.fits', os.listdir(f'{destination_path_filter}')).__next__() cat_generated_path = f'{destination_path_filter}{cat_generated_file}' cat_sextractor_path = f'{destination_path_filter}difference.cat' comparison_image_path = f'{destination_path_filter}{comparison_image_file}' difference_image_path = f'{destination_path_filter}{difference_image_file}' cat_generated = table.Table.read(cat_generated_path, format='ascii.csv') cat_sextractor = table.Table(np.genfromtxt(cat_sextractor_path, names=p.sextractor_names())) difference_image = fits.open(difference_image_path) comparison_image = fits.open(comparison_image_path) comparison_header = comparison_image[0].header exp_time = comparison_header['EXPTIME'] comparison_zeropoint, _, airmass, _ = ph.select_zeropoint(comparison_name, f, instrument=instrument) plotting.plot_all_params(image=difference_image, cat=cat_sextractor, show=False) for obj in cat_generated: plt.scatter(obj['x_0'], obj['y_0'], c='white') plt.show() _, pix_scale = ff.get_pixel_scale(comparison_image) match_ids_sextractor, match_ids_generated = u.match_cat(x_match=cat_sextractor['ra'], y_match=cat_sextractor['dec'], x_cat=cat_generated['ra'], y_cat=cat_generated['dec'], tolerance=3 * pix_scale) matches_sextractor = cat_sextractor[match_ids_sextractor] matches_generated = cat_generated[match_ids_generated] plotting.plot_all_params(image=difference_image, cat=matches_sextractor, show=False) # plt.scatter(x_burst, y_burst, c='white') plotting.plot_gal_params(hdu=difference_image, ras=[ra_burst], decs=[dec_burst], a=[burst_err_a], b=[burst_err_b], theta=[burst_err_theta], colour='blue', show_centre=True) for obj in matches_generated: plt.scatter(obj['x_0'], obj['y_0'], c='white') plt.show() matches_sextractor['mag_sextractor'], _, _ = ph.magnitude_complete(flux=matches_sextractor['flux_aper'], exp_time=exp_time, airmass=airmass, zeropoint=comparison_zeropoint, ext=comparison_extinction) matches = table.hstack([matches_generated, matches_sextractor], table_names=['generated', 'sextracted']) matches['delta_mag'] = matches['mag_sextractor'] - matches['mag'] delta_mag = np.median(matches['delta_mag']) matches.write(f'{destination_path_filter}{field}_{epoch}-{template_epoch}_difference_matched_sources.csv', format='ascii.csv', overwrite=True) print(f'{len(matches)} matches / {len(cat_generated)} generated = ' f'{100 * len(matches) / len(cat_generated)} % ') print(f'Faintest recovered: {max(matches["mag"])} generated; ' f'{max(matches_sextractor["mag_sextractor"])} sextracted') print('Median delta mag:', delta_mag) plt.scatter(matches['mag'], matches['delta_mag']) plt.show() difference_image.close()