Example #1
0
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)
Example #3
0
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)
Example #4
0
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)
Example #5
0
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')
Example #6
0
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')
Example #7
0
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()
Example #8
0
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)
Example #10
0
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()