Exemple #1
0
def ap_correction(image, syntax, df):

    import numpy as np
    from astropy.stats import sigma_clip
    import matplotlib.pyplot as plt
    from autophot.packages.functions import mag
    from autophot.packages.functions import set_size

    import logging
    logger = logging.getLogger(__name__)

    plt.ioff()

    ap_diff = mag(df['flux_inf_ap'] / df['flux_ap'], 0)
    ap_diff = ap_diff[~np.isnan(ap_diff)]

    corr_mask = np.array(
        ~sigma_clip(ap_diff, sigma=syntax['ap_corr_sigma']).mask)

    ap_corr = ap_diff[corr_mask]

    if syntax['ap_corr_plot']:

        fig = plt.figure(figsize=set_size(240, 1))

        ax1 = fig.add_subplot(111)
        ax1.hist([ap_diff, ap_corr],
                 weights=[
                     np.ones_like(ap_diff) / float(len(ap_diff)),
                     np.ones_like(ap_corr) / float(len(ap_corr))
                 ],
                 bins=30,
                 label=[
                     'w/o Clipping',
                     'w/ Clipping [$%d\sigma$]' % syntax['ap_corr_sigma']
                 ],
                 density=False)

        # ax1.hist(ap_corr,bins = 'auto',label = ,density = True)

        ax1.set_xlabel(
            r'$-2.5 log_{10}(\frac{\sum Infinite \ Aperture}{\sum Aperture})$')
        ax1.set_ylabel('Probability')
        ax1.legend(loc='best', frameon=False)

        fig.savefig(syntax['write_dir'] + 'APCOR.pdf',
                    format='pdf',
                    bbox_inches='tight')
        plt.close()

        # plt.show()

    logger.info('Aperture correction: %.3f +/- %.3f' %
                (np.nanmean(ap_corr), np.nanstd(ap_corr)))
    ap_corr = np.nanmean(ap_corr)
    return ap_corr
Exemple #2
0
def plot_PSF_model_steps(sources_dict, syntax, image, it=3):
    '''
    Plot out steps for PSF model for use in publication

    '''

    import matplotlib.pyplot as plt
    from matplotlib.gridspec import GridSpec
    import pathlib
    import os

    from autophot.packages.functions import array_correction, rebin, set_size

    plt.ioff()

    regriding_size = int(syntax['regrid_size'])

    save_loc = os.path.join(syntax['write_dir'], 'PSF_residual_shift_check')

    pathlib.Path(save_loc).mkdir(parents=True, exist_ok=True)

    keys = list(sources_dict.keys())

    fig = plt.figure(figsize=set_size(500, aspect=0.5))

    bbox_props = dict(boxstyle="round,pad=0.5", fc="none", ec="none", lw=0.1)

    ncols = 6
    nrows = 3

    for i in range(it):

        PSF_data = sources_dict[keys[i]]

        heights = [0.1, 1, 0.1]
        widths = [
            1,
            1,
            0.5,
            0.5,
            0.5,
            0.5,
        ]

        grid = GridSpec(nrows,
                        ncols,
                        wspace=0.5,
                        hspace=0.5,
                        height_ratios=heights,
                        width_ratios=widths)

        ax1 = fig.add_subplot(grid[1, 0])

        ax1.set_title('Bright, Isolated Source')

        close_up = PSF_data['close_up']

        ax1.imshow(close_up, aspect='auto', origin='lower')

        ax1.scatter(PSF_data['x_best'],
                    PSF_data['y_best'],
                    s=10,
                    marker='x',
                    color='red')
        ax1.scatter(close_up.shape[0] / 2,
                    close_up.shape[0] / 2,
                    marker='s',
                    facecolors='none',
                    s=10,
                    edgecolors='black',
                    label='Image center')

        # ax1.axvline(close_up.shape[0]/2,color = 'black',linestyle = ':')
        # ax1.axhline(close_up.shape[0]/2,color = 'black',label = 'Center of image',linestyle = ':')

        ax2 = fig.add_subplot(grid[1, 1])
        ax2.set_title('Subtract Model')

        residual = PSF_data['residual']

        ax2.imshow(residual, aspect='auto', origin='lower')
        ax2.scatter(PSF_data['x_best'],
                    PSF_data['y_best'],
                    s=10,
                    marker='x',
                    color='red')
        ax2.scatter(close_up.shape[0] / 2,
                    close_up.shape[0] / 2,
                    marker='s',
                    facecolors='none',
                    s=10,
                    edgecolors='black',
                    label='Image center')

        # ax2.axvline(close_up.shape[0]/2,color = 'black',linestyle = ':')
        # ax2.axhline(close_up.shape[0]/2,color = 'black',label = 'Center of image',linestyle = ':')

        ax3 = fig.add_subplot(grid[0:3, 2:4])

        ax3.set_title('Regrid')
        residual_regrid = PSF_data['regrid']

        ax3.imshow(residual_regrid, aspect='auto', origin='lower')

        ax3.scatter(array_correction(PSF_data['x_best'] * regriding_size),
                    array_correction(PSF_data['y_best'] * regriding_size),
                    marker='x',
                    color='red',
                    s=25,
                    label='Best Fit')

        ax3.scatter(residual_regrid.shape[0] / 2,
                    residual_regrid.shape[0] / 2,
                    marker='s',
                    facecolors='none',
                    s=25,
                    edgecolors='black',
                    label='Image center')

        ax3.annotate(
            'Regriding size = x%d' % regriding_size,
            xy=(0, 0.5),
            xycoords='axes fraction',
            xytext=(0.05, 0.05),
            bbox=bbox_props,
            # arrowprops=
            #     dict(facecolor='black', shrink=0.05),
            #     horizontalalignment='left',
            #     verticalalignment='center'
        )

        ax4 = fig.add_subplot(grid[0:3, 4:6])

        ax4.set_title('Roll')
        roll = rebin(PSF_data['roll'],
                     (int(2 * syntax['scale']), int(2 * syntax['scale'])))
        roll = PSF_data['roll']

        x_roll = PSF_data['x_roll']
        y_roll = PSF_data['y_roll']

        ax4.imshow(roll, aspect='auto', origin='lower')
        ax4.scatter(
            array_correction(x_roll + PSF_data['x_best'] * regriding_size),
            array_correction(y_roll + PSF_data['y_best'] * regriding_size),
            marker='x',
            color='red',
            s=25,
            label='Best Fit')
        ax4.scatter(roll.shape[0] / 2,
                    roll.shape[0] / 2,
                    marker='s',
                    facecolors='none',
                    edgecolors='black',
                    label='Image center',
                    s=25)

        # ax5 = fig.add_subplot(grid[1 , 6])

        # ax5.set_title('Step: 5')

        # roll_bin  = rebin(PSF_data['roll'],(2*syntax['scale'],2*syntax['scale']))

        # ax5.imshow(roll_bin,aspect = 'auto',origin = 'lower')

        for ax in fig.axes:
            ax.set_axis_off()

        from matplotlib.patches import ConnectionPatch

        xyA = (1.01, 0.5)  # in axes coordinates
        xyB = (-.01, 0.5)  # x in axes coordinates, y in data coordinates
        coordsA = ax1.transAxes
        coordsB = ax2.transAxes
        con = ConnectionPatch(xyA=xyA,
                              xyB=xyB,
                              coordsA=coordsA,
                              coordsB=coordsB,
                              arrowstyle="->")
        ax2.add_artist(con)

        xyA = (1.01, 1.01)  # in axes coordinates
        xyB = (-0.01, 0.99)  # x in axes coordinates, y in data coordinates
        coordsA = ax2.transAxes
        coordsB = ax3.transAxes
        con = ConnectionPatch(xyA=xyA,
                              xyB=xyB,
                              coordsA=coordsA,
                              coordsB=coordsB,
                              arrowstyle="-")
        ax3.add_artist(con)

        xyA = (1.01, 0.01)  # in axes coordinates
        xyB = (-0.01, +0.01)  # x in axes coordinates, y in data coordinates
        coordsA = ax2.transAxes
        coordsB = ax3.transAxes
        con = ConnectionPatch(xyA=xyA,
                              xyB=xyB,
                              coordsA=coordsA,
                              coordsB=coordsB,
                              arrowstyle="-")
        ax3.add_artist(con)

        xyA = (1.01, 0.5)  # in axes coordinates
        xyB = (-.01, 0.5)  # x in axes coordinates, y in data coordinates
        coordsA = ax3.transAxes
        coordsB = ax4.transAxes
        con = ConnectionPatch(xyA=xyA,
                              xyB=xyB,
                              coordsA=coordsA,
                              coordsB=coordsB,
                              arrowstyle="->")
        ax3.add_artist(con)

        # xyA = (1.0, 1.0)  # in axes coordinates
        # xyB = (-0.0, 1)  # x in axes coordinates, y in data coordinates
        # coordsA = ax3.transAxes
        # coordsB = ax4.transAxes
        # con = ConnectionPatch(xyA=xyA, xyB=xyB, coordsA=coordsA, coordsB=coordsB,
        #                       arrowstyle="-")
        # ax3.add_artist(con)

        # xyA = (1.0, 0.0)  # in axes coordinates
        # xyB = (-0.0, +0.00)  # x in axes coordinates, y in data coordinates
        # coordsA = ax3.transAxes
        # coordsB = ax4.transAxes
        # con = ConnectionPatch(xyA=xyA, xyB=xyB, coordsA=coordsA, coordsB=coordsB,
        #                       arrowstyle="-")
        # ax3.add_artist(con)

        # xyA = (1.01, 0.5)  # in axes coordinates
        # xyB = (-.01, 0.5)  # x in axes coordinates, y in data coordinates
        # coordsA = ax4.transAxes
        # coordsB = ax5.transAxes
        # con = ConnectionPatch(xyA=xyA, xyB=xyB, coordsA=coordsA, coordsB=coordsB,
        #                       arrowstyle="->",
        #                       )
        # ax5.add_artist(con)
        lines, labels = fig.axes[-1].get_legend_handles_labels()

        fig.legend(
            lines,
            labels,
            loc='lower left',
            frameon=False,
            bbox_to_anchor=(0.2, 0.15),
            ncol=2,
            prop={'size': 7},
            scatterpoints=1,
        )

        plt.savefig(os.path.join(save_loc, '%s_residual.pdf' % keys[i]),
                    # bbox_inches='tight'
                    )

        # plt.close()

    return
Exemple #3
0
def limiting_magnitude_prob(syntax, image, model=None, r_table=None):
    '''
    syntax - dict
        dDtionary of input paramters
    image - np.array
        Image of region of interest with target in center of image
    model - function
        - psf function from autophot
    '''
    try:

        from photutils import CircularAperture
        import matplotlib.pyplot as plt
        import numpy as np
        import matplotlib.gridspec as gridspec
        import random
        from scipy.optimize import curve_fit
        import warnings
        from photutils.datasets import make_noise_image
        # from autophot.packages.functions import mag
        from photutils import DAOStarFinder
        from astropy.stats import sigma_clipped_stats
        # from matplotlib.ticker import MultipleLocator
        from mpl_toolkits.axes_grid1 import make_axes_locatable
        from autophot.packages.rm_bkg import rm_bkg

        from astropy.visualization import ZScaleInterval

        import logging

        logger = logging.getLogger(__name__)

        limiting_mag_figure = plt.figure(figsize=set_size(240, aspect=1.5))

        gs = gridspec.GridSpec(2, 2, hspace=0.5, wspace=0.2)
        ax0 = limiting_mag_figure.add_subplot(gs[:, :-1])

        ax1 = limiting_mag_figure.add_subplot(gs[-1, -1])
        ax2 = limiting_mag_figure.add_subplot(gs[:-1, -1])

        # level for detection - Rule of thumb ~ 5 is a good detection level
        level = syntax['lim_SNR']

        logger.info('Limiting threshold: %d sigma' % level)

        image_no_surface, surface = rm_bkg(image, syntax, image.shape[0] / 2,
                                           image.shape[0] / 2)

        # =============================================================================
        # find and mask sources in close up
        # =============================================================================

        image_mean, image_median, image_std = sigma_clipped_stats(
            image,
            sigma=syntax['source_sigma_close_up'],
            maxiters=syntax['iters'])

        daofind = DAOStarFinder(fwhm=syntax['fwhm'],
                                threshold=syntax['bkg_level'] * image_std)

        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            # ignore no sources warning
            sources = daofind(image - image_median)

        if sources != None:
            positions = list(
                zip(np.array(sources['xcentroid']),
                    np.array(sources['ycentroid'])))

            positions.append((image.shape[0] / 2, image.shape[1] / 2))

        else:
            positions = [(image.shape[0] / 2, image.shape[1] / 2)]

        # "size" of source
        source_size = syntax['image_radius']

        pixel_number = int(np.ceil(np.pi * source_size**2))

        # Mask out target region
        mask_ap = CircularAperture(positions, r=source_size)

        mask = mask_ap.to_mask(method='center')

        mask_sumed = [i.to_image(image.shape) for i in mask]

        if len(mask_sumed) != 1:
            mask_sumed = sum(mask_sumed)
        else:
            mask_sumed = mask_sumed[0]

        mask_sumed[mask_sumed > 0] = 1

        logging.info('Number of pixels in star: %d' % pixel_number)

        # Mask out center region
        mask_image = (image_no_surface) * (1 - mask_sumed)

        vmin, vmax = (ZScaleInterval(nsamples=1500)).get_limits(mask_image)

        excluded_points = mask_image == 0
        exclud_x = excluded_points[0]
        exclud_y = excluded_points[1]

        exclud_zip = list(zip(exclud_x, exclud_y))

        included_points = np.where(mask_image != 0)

        includ_x = list(included_points[0])
        includ_y = list(included_points[1])

        includ_zip = list(zip(includ_x, includ_y))

        # ax2.scatter(exclud_y,exclud_x,color ='black',marker = 'X',alpha = 0.5  ,label = 'excluded_pixels',zorder = 1)
        ax2.scatter(includ_y,
                    includ_x,
                    color='red',
                    marker='x',
                    alpha=0.5,
                    label='included_pixels',
                    zorder=2)

        number_of_points = 300

        fake_points = {}

        if len(includ_zip) < pixel_number:
            includ_zip = includ_zip + exclud_zip

        for i in range(number_of_points):
            fake_points[i] = []
            # count = 0
            random_pixels = random.sample(includ_zip, pixel_number)
            xp_ran = [i[0] for i in random_pixels]
            yp_ran = [i[1] for i in random_pixels]

            fake_points[i].append([xp_ran, yp_ran])

        fake_sum = {}
        for i in range(number_of_points):

            fake_sum[i] = []

            for j in fake_points[i]:

                for k in range(len(j[0])):

                    fake_sum[i].append(image_no_surface[j[0][k]][j[1][k]])

        fake_mags = {}

        for f in fake_sum.keys():

            fake_mags[f] = np.sum(fake_sum[f])

# =============================================================================
#     Histogram
# =============================================================================

        hist, bins = np.histogram(list(fake_mags.values()),
                                  bins=len(list(fake_mags.values())),
                                  density=True)

        center = (bins[:-1] + bins[1:]) / 2

        sigma_guess = np.nanstd(list(fake_mags.values()))
        mean_guess = np.nanmean(list(fake_mags.values()))
        A_guess = np.nanmax(hist)

        def gauss(x, a, x0, sigma):
            return a * np.exp(-(x - x0)**2 / (2 * sigma**2))

        popt, pcov = curve_fit(gauss,
                               center,
                               hist,
                               p0=[A_guess, mean_guess, sigma_guess],
                               absolute_sigma=True)

        mean = popt[1]
        std = abs(popt[2])

        logging.info('Mean: %s - std: %s' % (round(mean, 3), round(std, 3)))

        if syntax['probable_detection_limit']:

            beta = float(syntax['probable_detection_limit_beta'])

            def detection_probability(n, sigma, beta):
                from scipy.special import erfinv
                '''

                Probabilistic upper limit computation base on:
                http://web.ipac.caltech.edu/staff/fmasci/home/mystats/UpperLimits_FM2011.pdf

                Assuming Gassauin nose distribution

                n: commonly used threshold value integer above some background level

                sigma: sigma value from noise distribution found from local area around source

                beta: Detection probability


                '''
                flux_upper_limit = (n +
                                    np.sqrt(2) * erfinv(2 * beta - 1)) * sigma

                return flux_upper_limit

            logging.info("Using Probable detection limit [b' = %d%% ]" %
                         (100 * beta))

            f_ul = mean + detection_probability(level, std, beta)

            logging.info("Flux Upper limit: %.3f" % f_ul)

        else:
            f_ul = abs(mean + level * std)
            logging.info('Detection at %s std: %.3f' % (level, f_ul))

        # =============================================================================
        # Plot histogram of background values
        # =============================================================================

        line_kwargs = dict(alpha=0.5, color='black', ls='--')

        # the histogram of the data
        n, bins, patches = ax0.hist(list(fake_mags.values()),
                                    density=True,
                                    bins=30,
                                    facecolor='blue',
                                    alpha=1,
                                    label='Pseudo-Flux\nDistribution')

        ax0.axvline(mean, **line_kwargs)
        ax0.axvline(mean + 1 * std, **line_kwargs)
        ax0.text(mean + 1 * std,
                 np.max(n),
                 r'$1\sigma$',
                 rotation=-90,
                 va='top')
        ax0.axvline(mean + 2 * std, **line_kwargs)
        ax0.text(mean + 2 * std,
                 np.max(n),
                 r'$2\sigma$',
                 rotation=-90,
                 va='top')

        if syntax['probable_detection_limit']:

            ax0.axvline(f_ul, **line_kwargs)
            ax0.text(f_ul,
                     np.max(n),
                     r"$\beta'$ = %d%%" % (100 * beta),
                     rotation=-90,
                     va='top')

        else:
            ax0.axvline(f_ul, **line_kwargs)
            ax0.text(mean + level * std,
                     np.max(n),
                     r'$' + str(level) + r'\sigma$',
                     rotation=-90,
                     va='top')

        x_fit = np.linspace(ax0.get_xlim()[0], ax0.get_xlim()[1], 250)

        ax0.plot(x_fit, gauss(x_fit, *popt), label='Gaussian Fit', color='red')

        ax0.ticklabel_format(axis='y', style='sci', scilimits=(-2, 0))
        ax0.yaxis.major.formatter._useMathText = True

        ax0.set_xlabel('Pseudo-Flux')
        ax0.set_ylabel('Normalised Probability')

        im2 = ax2.imshow(image - surface,
                         origin='lower',
                         aspect='auto',
                         interpolation='nearest')
        divider = make_axes_locatable(ax2)
        cax = divider.append_axes("right", size="5%", pad=0.05)
        cb = limiting_mag_figure.colorbar(im2, cax=cax)
        cb.ax.set_ylabel('Counts', rotation=270, labelpad=10)

        cb.formatter.set_powerlimits((0, 0))
        cb.ax.yaxis.set_offset_position('left')
        cb.update_ticks()

        ax2.set_title('Image - Surface')

        # =============================================================================
        # Convert counts to magnitudes
        # =============================================================================

        flux = f_ul / syntax['exp_time']

        mag_level = -2.5 * np.log10(flux)

        # =============================================================================
        # We now have an upper and lower estimate of the the limiting magnitude
        # =============================================================================
        '''
        Visual display of limiting case

        if PSF model is available use that

        else

        use a gaussian profile with the same number of counts
        '''
        fake_sources = np.zeros(image.shape)

        try:

            if syntax['c_counts']:
                pass

            model_label = 'PSF'

            def mag2image(m):
                '''
                Convert magnitude to height of PSF
                '''
                Amplitude = (syntax['exp_time'] /
                             (syntax['c_counts'] + syntax['r_counts'])) * (
                                 10**(m / -2.5))

                return Amplitude

            # PSF model that matches close-up shape around target
            def input_model(x, y, flux):
                return model(x, y, 0, flux, r_table, pad_shape=image.shape)

        except:
            '''
            if PSF model isn't available - use Gaussian instead

            '''
            logging.info('PSF model not available - Using Gaussian')
            model_label = 'Gaussian'

            sigma = syntax['fwhm'] / 2 * np.sqrt(2 * np.log(2))

            def mag2image(m):
                '''
                Convert magnitude to height of Gaussian
                '''

                #  Volumne/counts under 2d gaussian for a magnitude m
                volume = (10**(m / -2.5)) * syntax['exp_time']

                # https://en.wikipedia.org/wiki/Gaussian_function
                Amplitude = volume / (2 * np.pi * sigma**2)

                return Amplitude

            #  Set up grid

            def input_model(x, y, A):

                x = np.arange(0, image.shape[0])
                xx, yy = np.meshgrid(x, x)

                from autophot.packages.functions import gauss_2d, moffat_2d

                if syntax['use_moffat']:
                    model = moffat_2d(
                        (xx, yy), x, y, 0, A,
                        syntax['image_params']).reshape(image.shape)

                else:
                    model = gauss_2d(
                        (xx, yy), x, y, 0, A,
                        syntax['image_params']).reshape(image.shape)

                return model

        # =============================================================================
        #  What magnitude do you want this target to be?
        # =============================================================================

        mag2image = mag2image

        inject_source_mag = mag2image(mag_level)

        # =============================================================================
        # Random well-spaced points to plot
        # =============================================================================

        random_sources = sample_with_minimum_distance(
            n=[int(syntax['fwhm']),
               int(image.shape[0] - syntax['fwhm'])],
            k=syntax['inject_sources_random_number'],
            d=int(syntax['fwhm'] / 2))
        import math

        def PointsInCircum(r, n=100):
            return [(math.cos(2 * math.pi / n * x) * r + image.shape[1] / 2,
                     math.sin(2 * math.pi / n * x) * r + image.shape[0] / 2)
                    for x in range(0, n)]

        random_sources = PointsInCircum(2 * syntax['fwhm'], n=3)
        x = [abs(i[0]) for i in random_sources]
        y = [abs(i[1]) for i in random_sources]

        print(x)
        print(y)

        # =============================================================================
        # Inject sources
        # =============================================================================

        try:
            if syntax['inject_source_random']:

                for i in range(0, len(x)):

                    fake_source_i = input_model(x[i], y[i], inject_source_mag)

                    if syntax['inject_source_add_noise']:

                        nan_idx = np.isnan(fake_source_i)
                        fake_source_i[nan_idx] = 0
                        fake_source_i[fake_source_i < 0] = 0

                        fake_source_i = make_noise_image(
                            fake_source_i.shape,
                            distribution='poisson',
                            mean=fake_source_i,
                            random_state=np.random.randint(0, 1e3))
                        # fake_source_i[nan_idx] = np.nan1

                    fake_sources += fake_source_i
                    ax1.scatter(x[i],
                                y[i],
                                marker='o',
                                s=150,
                                facecolors='none',
                                edgecolors='r',
                                alpha=0.5)
                    ax1.annotate(str(i), (x[i], -.5 + y[i]),
                                 color='r',
                                 alpha=0.5,
                                 ha='center')

            if syntax['inject_source_on_target']:

                fake_source_on_target = input_model(image.shape[1] / 2,
                                                    image.shape[0] / 2,
                                                    inject_source_mag)

                if syntax['inject_source_add_noise']:
                    nan_idx = np.isnan(fake_source_on_target)
                    fake_source_on_target[nan_idx] = 1e-6
                    fake_source_on_target[fake_source_on_target < 0] = 0

                    fake_source_on_target = make_noise_image(
                        fake_source_on_target.shape,
                        distribution='poisson',
                        mean=fake_source_on_target,
                        random_state=np.random.randint(0, 1e3))

                fake_sources += fake_source_on_target

                ax1.scatter(image.shape[1] / 2,
                            image.shape[0] / 2,
                            marker='o',
                            s=150,
                            facecolors='none',
                            edgecolors='black',
                            alpha=0.5)
                ax1.annotate('On\nTarget',
                             (image.shape[1] / 2, -1 + image.shape[0] / 2),
                             color='black',
                             alpha=0.5,
                             ha='center')

            im1 = ax1.imshow(
                image - surface + fake_sources,
                # vmin = vmin,
                # vmax = vmax,
                aspect='auto',
                # norm = norm,
                origin='lower',
                interpolation='nearest')
            ax1.set_title(' Fake [%s] Sources ' % model_label)

        except Exception as e:
            logging.exception(e)
            im1 = ax1.imshow(
                image - surface,
                origin='lower',
                aspect='auto',
            )
            ax1.set_title('[ERROR] Fake Sources [%s]' % model_label)

        # plt.colorbar(im1,ax=ax1)
        divider = make_axes_locatable(ax1)
        cax = divider.append_axes("right", size="5%", pad=0.05)
        cb = limiting_mag_figure.colorbar(im1, cax=cax)
        cb.ax.set_ylabel('Counts', rotation=270, labelpad=10)
        # cb = fig.colorbar(im)
        cb.formatter.set_powerlimits((0, 0))
        cb.ax.yaxis.set_offset_position('left')
        cb.update_ticks()

        ax0.legend(loc='lower center',
                   bbox_to_anchor=(0.5, 1.02),
                   ncol=2,
                   frameon=False)

        limiting_mag_figure.savefig(
            syntax['write_dir'] + 'limiting_mag_porb.pdf',
            # box_extra_artists=([l]),
            bbox_inches='tight',
            format='pdf')
        plt.close('all')

    # master try/except
    except Exception as e:
        print('limit issue')
        logging.exception(e)

    syntax['maglim_mean'] = mean
    syntax['maglim_std'] = std

    return mag_level, syntax
Exemple #4
0
def plot_lightcurve(syntax,
                    sn_peak=None,
                    fwhm_limit=1,
                    pick_filter=[],
                    filter_spacing=0.2,
                    plot_error_lim=0.01,
                    show_plot=True,
                    vega2AB=False,
                    AB2vega=False):

    import numpy as np
    import pandas as pd
    import os, sys
    import matplotlib.pyplot as plt

    from autophot.packages.functions import set_size

    plt.ioff()

    def Vega2AB(df, AB2Vega=False):
        df_AB = df.copy()
        Vega2AB_dict = {
            # 'U':0.79,
            # 'B':-0.09,
            # 'V':0.02,
            # 'R':0.21,
            # 'I':0.45,
            # 'u':0.91,
            # 'g':-0.08,
            # 'r':0.16,
            # 'i':0.37,
            # 'z':0.54,
            'J': 0.929,
            'H': 1.394,
            'K': 1.859,
            #                    'S':-1.51,
            #                    'D':-1.69,
            #                    'A':-1.73,
        }
        if AB2Vega:
            for key in Vega2AB_dict:
                Vega2AB_dict[key] *= -1

        for f in list(Vega2AB_dict.keys()):
            try:
                df_AB[f] = df[f] + Vega2AB_dict[f]
                df_AB['lmag'][~np.isnan(df[f])] = df_AB['lmag'][
                    ~np.isnan(df[f])] + Vega2AB_dict[f]
            except:
                print('ERROR: %s' % f)
                pass
        return df_AB

    out_dir = syntax['fits_dir'] + '_' + syntax['outdir_name']

    output_file_loc = os.path.join(out_dir, syntax['outcsv_name'] + '.csv')

    if not os.path.exists(output_file_loc):
        print('Cannot find output file')
        return
    else:
        data = pd.read_csv(output_file_loc)

    if vega2AB:
        data = Vega2AB(data)

    if AB2vega:
        data = Vega2AB(data, Vega=True)

    markers = [
        'o', 's', 'v', '^', '<', '>', 'p', 'P', '*', 'h', 'H', '+', 'X', 'd',
        'D', '1', '2', '3', '4', '8'
    ]

    filters = ['K', 'H', 'J', 'z', 'I', 'i', 'R', 'r', 'V', 'g', 'B', 'U', 'u']

    cols = {
        'u': 'dodgerblue',
        'g': 'g',
        'r': 'r',
        'i': 'goldenrod',
        'z': 'k',
        'U': 'slateblue',
        'B': 'b',
        'V': 'yellowgreen',
        'R': 'crimson',
        'G': 'salmon',
        'I': 'chocolate',
        'J': 'darkred',
        'H': 'orangered',
        'K': 'saddlebrown'
    }

    # List of telescopes used
    telelst = list(set(data.telescope.values))

    if len(pick_filter) != 0:
        used_filters = pick_filter
    else:
        used_filters = [i for i in filters if i in list(data.columns)]

    tele_dict = dict(zip(telelst, markers))
    filter_dict = {i: cols[i] for i in used_filters}

    offset = {}
    min_sep = 1
    # filter_spacing = 0

    if sn_peak != None:
        if len(used_filters) > 1 and filter_spacing != 0:

            peak_range = [sn_peak - 50, sn_peak + 50]

            mid_filter = used_filters[len(used_filters) // 2]
            offset[mid_filter] = 0

            upper_filters = used_filters[len(used_filters) // 2:][1::]
            lower_filters = used_filters[:len(used_filters) // 2]

            used_filter_data = data[['mjd', mid_filter, mid_filter + '_err'
                                     ]][~np.isnan(data[mid_filter].values)]

            range_data_mid = used_filter_data[used_filter_data['mjd'].between(
                peak_range[0], peak_range[1])]

            shift_old = 0.

            for i in upper_filters:
                try:

                    used_filter_data = data[['mjd', i, i + '_err'
                                             ]][~np.isnan(data[i].values)]

                    range_data = used_filter_data[
                        used_filter_data['mjd'].between(
                            peak_range[0], peak_range[1])]

                    median = np.nanmin(range_data[i])

                    shift = min_sep

                    offset[i] = abs(shift) + abs(shift_old)

                    shift_old = abs(shift) + abs(shift_old)

                except Exception as e:
                    exc_type, exc_obj, exc_tb = sys.exc_info()
                    fname = os.path.split(
                        exc_tb.tb_frame.f_code.co_filename)[1]
                    print(exc_type, fname, exc_tb.tb_lineno, e, i)

            median_upper = np.nanmin(range_data_mid[mid_filter])
            shift_old = 0.

            for i in lower_filters[::-1]:
                try:

                    used_filter_data = data[['mjd', i, i + '_err'
                                             ]][~np.isnan(data[i].values)]

                    range_data = used_filter_data[
                        used_filter_data['mjd'].between(
                            peak_range[0], peak_range[1])]

                    median = np.nanmin(range_data[i])

                    shift = -min_sep

                    offset[i] = -abs(shift) - abs(shift_old)

                    median_upper = median - abs(shift) - abs(shift_old)

                    shift_old = -abs(shift) - abs(shift_old)
                except Exception as e:
                    exc_type, exc_obj, exc_tb = sys.exc_info()
                    fname = os.path.split(
                        exc_tb.tb_frame.f_code.co_filename)[1]
                    print(exc_type, fname, exc_tb.tb_lineno, e, i)
        else:
            for i in used_filters:
                offset[i] = 0
    else:
        for i in range(len(used_filters)):
            offset[used_filters[i]] = filter_spacing * i

    mjd = []
    mag = []
    mag_e = []
    color = []
    marker = []
    lmag = []
    SNR = []
    fname = []
    filters = []
    fwhm_source = []

    mjd_range = [-np.inf, np.inf]

    for f in used_filters:

        data_f = data[f][data['mjd'].between(mjd_range[0], mjd_range[1])]

        data_filters_idx = data_f[~np.isnan(data[f])].index
        data_filters = data.iloc[data_filters_idx]

        mjd += list(data_filters.mjd.values)
        mag += list(data_filters[f].values + offset[f])

        mag_e += list(data_filters[f + '_err'].values)

        fwhm_source += list(
            abs((data_filters['target_fwhm'] - data_filters['fwhm']).values))

        SNR += list(data_filters.SNR.values)

        lmag += list(data_filters.lmag.values + offset[f])
        color += list([filter_dict[f]] * len(data_filters))

        marker += [tele_dict[i] for i in data_filters.telescope]
        fname += list(data_filters.fname)
        filters += [f] * len(data_filters)

    # =============================================================================

    plt.close('lightucurve_AUTOPHOT')
    fig = plt.figure('lightucurve_AUTOPHOT',
                     figsize=set_size(504.0, aspect=0.75))

    ax1 = fig.add_subplot(111)
    subp = [ax1]

    for axes in subp:
        axes.invert_yaxis()

    n_plotted = 0

    for _x, _y, _y_e, _y_l, _s, _c, _m, _f, _fil, _fwhm in zip(
            mjd, mag, mag_e, lmag, SNR, color, marker, fname, filters,
            fwhm_source):

        for axes in subp:

            if _y_l < _y or np.isnan(_y) or _fwhm > fwhm_limit:

                # plot Limiting magnitude
                markers, caps, bars = axes.errorbar(_x,
                                                    _y_l,
                                                    yerr=[0.3],
                                                    marker=_m,
                                                    c=_c,
                                                    alpha=0.15,
                                                    lolims=True,
                                                    uplims=False,
                                                    capsize=0,
                                                    ls='None',
                                                    ecolor='black',
                                                    fillstyle='full',
                                                    markeredgecolor=_c)
                n_plotted += 1
                [bar.set_alpha(0.15) for bar in bars]
                [cap.set_alpha(0.15) for cap in caps]

            elif _y_e >= plot_error_lim:

                # Source with errors
                markers, caps, bars = axes.errorbar(
                    _x,
                    _y,
                    yerr=_y_e,
                    marker=_m,
                    c=_c,
                    capsize=1,
                    # fillstyle = 'none',
                    markeredgecolor=_c,
                    ecolor='black',
                    alpha=0.75)
                n_plotted += 1
                [bar.set_alpha(0.3) for bar in bars]
                [cap.set_alpha(0.3) for cap in caps]

            else:

                axes.scatter(_x,
                             _y,
                             marker=_m,
                             c=np.array([_c]),
                             edgecolor=_c,
                             alpha=0.75)
                n_plotted += 1

        print('\rPlotting light curve %d / %d' % (n_plotted, len(mag)), end='')

    print(' ... done')

    # Add upper phase axis
    if sn_peak != None:

        def to_phase(x, peak=sn_peak):
            return x - peak

        def to_mjd(x, peak=sn_peak):
            return x + peak

        ax1_1 = ax1.twiny()
        ax1_1.set_xlim(xmin=to_phase(ax1.get_xlim()[0]),
                       xmax=to_phase(ax1.get_xlim()[1]))
        ax1_1.set_xlabel('Days since maximum ')

    # Add legends
    f = lambda m, c, f: plt.plot(
        [], [], marker=m, color=c, fillstyle=f, markeredgecolor=c, ls="none")[0
                                                                              ]

    color_handles = [
        f("o", filter_dict[i], 'full') for i in list(used_filters)
    ]
    marker_handles = [f(tele_dict[i], "k", 'full') for i in tele_dict]
    filter_labels = [
        i + '{0:+0.1f}'.format(offset[i]) for i in list(used_filters)
    ]

    # Legend for filters
    first_legend = ax1.legend(color_handles,
                              filter_labels,
                              fancybox=True,
                              ncol=len(color_handles),
                              bbox_to_anchor=(-0.02, 1.1, 1, 0),
                              loc='lower center',
                              frameon=False)

    # Legend for telescopes
    second_legend = ax1.legend(marker_handles,
                               telelst,
                               fancybox=True,
                               ncol=len(marker_handles) // 2,
                               bbox_to_anchor=(0, -0.1, 1, 0),
                               loc='upper center',
                               frameon=False)

    ax1.add_artist(first_legend)
    ax1.add_artist(second_legend)

    ax1.set_xlabel('Modified Julian Date')
    ax1.set_ylabel('Observed Magnitude + constant')

    plt.savefig(
        os.path.join(syntax['fits_dir'], 'lightcurve.pdf'),
        # box_extra_artists=([first_legend,second_legend]),
        bbox_inches='tight')

    if not show_plot:
        plt.close('all')
    else:
        plt.ion()
        plt.show()

    plt.ion()

    return
Exemple #5
0
def fit(image,sources,residual_table,syntax,fwhm,
        return_psf_model = False,
        save_plot = False,show_plot = False,
        rm_bkg_val = True,hold_pos = False,
        return_fwhm = False,return_subtraction_image = False,
        fname = None,no_print = False

        ):



    '''

    Fitting of PSF model to source

    '''


    import numpy as np
    import pandas as pd
    import pathlib
    import lmfit
    import logging

    import matplotlib.pyplot as plt

    from autophot.packages.functions import gauss_2d,moffat_2d,moffat_fwhm,gauss_sigma2fwhm

    from matplotlib.gridspec import  GridSpec

    import os

    dir_path = os.path.dirname(os.path.realpath(__file__))
    plt.style.use(os.path.join(dir_path,'autophot.mplstyle'))


    logger = logging.getLogger(__name__)


    fitting_radius = int(np.ceil(1.3*fwhm))
    regriding_size = int(syntax['regrid_size'])

    sources = sources
    residual_table = residual_table

    def build_psf(xc, yc, sky, H, r_table, slice_scale = None,pad_shape = None):

        '''
        Slice_scale = only return "slice scaled" image
        '''

        try:

            psf_shape = r_table.shape

            if pad_shape != None:
                #  Need to make PSF fitting bigger
                top =    int((pad_shape[0] - r_table.shape[0])/2)
                bottom=  int((pad_shape[0] - r_table.shape[0])/2)
                left =   int((pad_shape[1] - r_table.shape[1])/2)
                right =  int((pad_shape[1] - r_table.shape[1])/2)

                # print((top, bottom), (left, right))

                psf_shape = pad_shape

                r_table = np.pad(r_table, [(top, bottom), (left, right)], mode='constant', constant_values=0)

            x_rebin = np.arange(0,psf_shape[0])
            y_rebin = np.arange(0,psf_shape[1])

            xx_rebin,yy_rebin = np.meshgrid(x_rebin,y_rebin)

            # sigma  = fwhm/(2*np.sqrt(2*np.log(2)))
            if syntax['use_moffat']:
                core = moffat_2d((xx_rebin,yy_rebin),xc,yc,sky,H,syntax['image_params']).reshape(psf_shape)

            else:
                core = gauss_2d((xx_rebin,yy_rebin),xc,yc,sky,H,syntax['image_params']).reshape(psf_shape)

            residual_rebinned = np.repeat(np.repeat(r_table, regriding_size, axis=0), regriding_size, axis=1)

            x_roll = scale_roll(xc,int(r_table.shape[1]/2),regriding_size)
            y_roll = scale_roll(yc,int(r_table.shape[0]/2),regriding_size)

            residual_roll = np.roll(np.roll(residual_rebinned,y_roll,axis=0),x_roll,axis = 1)

            residual = rebin(residual_roll,psf_shape)

            psf =  (sky  + (H* residual )) + core

            if np.isnan(np.min(psf)):
                logger.info(sky,H,np.min(residual),np.min(core))

            psf[np.isnan(psf)] = 0

            if slice_scale != None:
                psf = psf[int ( 0.5 * r_table.shape[1] - slice_scale): int(0.5*r_table.shape[1] + slice_scale),
                          int ( 0.5 * r_table.shape[0] - slice_scale): int(0.5*r_table.shape[0] + slice_scale)]

        except Exception as e:
            logger.exception(e)
            psf = np.nan

        return psf


    if return_psf_model:

        shape = int(2*syntax['scale']),int(2*syntax['scale'])
        x_slice = np.arange(0,shape[0])
        xx_sl,yy_sl= np.meshgrid(x_slice,x_slice)

        if syntax['use_moffat']:
            PSF_model = moffat_2d((xx_sl,yy_sl),shape[1]/2,shape[1]/2,0,1,dict(alpha=syntax['image_params']['alpha'],beta=syntax['image_params']['beta'])).reshape(shape)
        else:
            PSF_model= gauss_2d((xx_sl,yy_sl),shape[1]/2,shape[1]/2,0,1,dict(sigma=syntax['image_params']['sigma'])).reshape(shape)

        return PSF_model


    psf_params = []

    x = np.arange(0,2*syntax['scale'])

    xx,yy= np.meshgrid(x,x)

    if hold_pos:
        dx = 1e-6
        dy = 1e-6
    else:
        dx = syntax['dx']
        dy = syntax['dy']


    lower_x_bound = syntax['scale']
    lower_y_bound = syntax['scale']
    upper_x_bound = syntax['scale']
    upper_y_bound = syntax['scale']


    if return_subtraction_image:

        from astropy.visualization.mpl_normalize import ImageNormalize

        from astropy.visualization import  ZScaleInterval, SquaredStretch


        norm = ImageNormalize( stretch = SquaredStretch())

        vmin,vmax = (ZScaleInterval(nsamples = 1500)).get_limits(image)

    '''
    Known issue - for poor images, some sources may be too close to boundary, remove this
    '''
    if not return_fwhm and not no_print:
        logger.info('Image cutout size: (%.f,%.f) (%.f,%.f)' % ((lower_x_bound,upper_x_bound,lower_y_bound,upper_y_bound)))

    sources = sources[sources.x_pix < image.shape[1] - upper_x_bound]
    sources = sources[sources.x_pix > lower_x_bound]
    sources = sources[sources.y_pix < image.shape[0] - upper_y_bound]
    sources = sources[sources.y_pix > lower_y_bound]

    if not no_print:
        logger.info('Fitting PSF to %d sources' % len(sources))

    for n  in range(len(sources.index)):
        if not return_fwhm and not no_print:
            print('\rFitting PSF to source: %d / %d' % (n+1,len(sources)), end = '')

        try:

            idx = list(sources.index)[n]


            source_base =   image[int(sources.y_pix[idx]-lower_y_bound): int(sources.y_pix[idx] + upper_y_bound),
                                  int(sources.x_pix[idx]-lower_x_bound): int(sources.x_pix[idx] + upper_x_bound)]

            if source_base.shape != (int(2*syntax['scale']),int(2*syntax['scale'])):
                print('not right shape')

                bkg_median = np.nan
                H = np.nan
                H_psf_err = np.nan
                x_fitted = np.nan
                y_fitted = np.nan
                psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))
                continue

            xc = syntax['scale']
            yc = syntax['scale']

            xc_global =  sources.x_pix[idx]
            yc_global =  sources.y_pix[idx]

            if not rm_bkg_val:
                source_bkg_free = source_base
                bkg_median = 0
            else:

                try:
                    source_bkg_free,bkg_surface = rm_bkg(source_base,syntax,source_base.shape[1]/2,source_base.shape[0]/2)
                    bkg_median = np.nanmedian(bkg_surface)

                except Exception as e:
                    bkg_median = np.nan
                    H = np.nan
                    H_psf_err = np.nan
                    x_fitted = np.nan
                    y_fitted = np.nan
                    psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))
                    logger.exception(e)
                    continue


            source = source_bkg_free[int(0.5*source_bkg_free.shape[1] - fitting_radius):int(0.5*source_bkg_free.shape[1] + fitting_radius) ,
                                     int(0.5*source_bkg_free.shape[0] - fitting_radius):int(0.5*source_bkg_free.shape[0] + fitting_radius) ]

            if source.shape != (int(2*fitting_radius),int(2*fitting_radius)):
                bkg_median = np.nan
                H = np.nan
                H_psf_err = np.nan
                x_fitted = np.nan
                y_fitted = np.nan
                psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))
                continue

            if np.sum(np.isnan(source)) == len(source):
                bkg_median = np.nan
                H = np.nan
                H_psf_err = np.nan
                x_fitted = np.nan
                y_fitted = np.nan
                psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))

                continue


            if hold_pos:
                dx = 1e-6
                dy = 1e-6
            else:
                dx = syntax['dx']
                dy = syntax['dy']
            # if not return_fwhm:
            #     dx = syntax['dx']
            #     dy = syntax['catalogdy']


            x_slice = np.arange(0,2*fitting_radius)
            xx_sl,yy_sl= np.meshgrid(x_slice,x_slice)

            if return_fwhm :
                if not no_print:
                    logger.info('Fitting gaussian to source to get FWHM')

                pars = lmfit.Parameters()
                pars.add('A',value = np.nanmax(source),min = 0)
                pars.add('x0',value = source.shape[1]/2,min = 0.5*source.shape[1] - dx,max = 0.5*source.shape[1] + dx)
                pars.add('y0',value = source.shape[0]/2,min = 0.5*source.shape[0] - dy,max = 0.5*source.shape[0] + dy)

                # print(pars)

                if syntax['use_moffat']:
                    pars.add('alpha',value = syntax['image_params']['alpha'],
                             min = 0,max = 25)

                    pars.add('beta',value = syntax['image_params']['beta'],
                             min = 0,
                             vary = syntax['vary_moff_beta']  )

                else:
                    pars.add('sigma',value = syntax['image_params']['sigma'],
                             min = 0,
                             max = gauss_fwhm2sigma(syntax['max_fit_fwhm']) )

                if syntax['use_moffat']:
                    fitting_model_fwhm = moffat_fwhm
                    def residual(p):
                        p = p.valuesdict()
                        return (source  - moffat_2d((xx_sl,yy_sl),p['x0'],p['y0'],0,p['A'],dict(alpha=p['alpha'],beta=p['beta'])).reshape(source.shape)).flatten()
                else:
                    fitting_model_fwhm = gauss_sigma2fwhm
                    def residual(p):
                        p = p.valuesdict()
                        return (source - gauss_2d((xx_sl,yy_sl),p['x0'],p['y0'],0,p['A'],dict(sigma=p['sigma'])).reshape(source.shape)).flatten()

                mini = lmfit.Minimizer(residual, pars,nan_policy = 'omit')
                result = mini.minimize(method = 'least_squares')

                xc = result.params['x0'].value
                yc = result.params['y0'].value

                if syntax['use_moffat']:
                    target_PSF_FWHM = fitting_model_fwhm(dict(alpha=result.params['alpha'],beta=result.params['beta']))
                else:
                    target_PSF_FWHM = fitting_model_fwhm(dict(sigma=result.params['sigma']))


                if not no_print:
                    logger.info('Target FWHM: %.3f' % target_PSF_FWHM)

                xc_global = xc - 0.5*source.shape[1] + sources.x_pix[idx]
                yc_global = yc - 0.5*source.shape[0] + sources.y_pix[idx]


                #  shift image and increase shize of image by shift

                # xc_global = sources.x_pix[idx]
                # yc_global = sources.y_pix[idx]

                source_base =   image[int(yc_global-lower_y_bound): int(yc_global + upper_y_bound),
                                      int(xc_global-lower_x_bound): int(xc_global + upper_x_bound)]

                source_bkg_free,bkg_surface = rm_bkg(source_base,syntax,source_bkg_free.shape[0]/2,source_bkg_free.shape[1]/2)

                bkg_median = np.nanmedian(bkg_surface)

                source = source_bkg_free[int(source_bkg_free.shape[0]/2 - fitting_radius):int(source_bkg_free.shape[0]/2 + fitting_radius) ,
                                         int(source_bkg_free.shape[1]/2 - fitting_radius):int(source_bkg_free.shape[1]/2 + fitting_radius) ]

# =============================================================================
#
# =============================================================================

            try:

                '''
                Fit and subtract PSF model
                '''

                if hold_pos:
                    dx = 1e-6
                    dy = 1e-6
                else:
                    dx = syntax['dx']
                    dy = syntax['dy']

                pars = lmfit.Parameters()

                pars.add('A', value = np.nanmax(source)*0.75,min = 0)

                pars.add('x0',value = 0.5*residual_table.shape[1],
                         min = 0.5*residual_table.shape[1]-dx,
                         max = 0.5*residual_table.shape[1]+dx)

                pars.add('y0',value = 0.5*residual_table.shape[0],
                         min = 0.5*residual_table.shape[0]-dy,
                         max = 0.5*residual_table.shape[0]+dy)


                def residual(p):
                    p = p.valuesdict()
                    res = ((source - build_psf(p['x0'],p['y0'],0,p['A'],residual_table,slice_scale = source.shape[0]/2)))
                    return res.flatten()


                mini = lmfit.Minimizer(residual,
                                       pars,
                                       nan_policy = 'omit',
                                       scale_covar=True)

                result = mini.minimize(method = 'least_squares')

                xc = result.params['x0'].value

                yc = result.params['y0'].value

                H = result.params['A'].value

                H_psf_err = result.params['A'].stderr

                x_fitted = xc -0.5*residual_table.shape[1] + xc_global
                y_fitted = yc -0.5*residual_table.shape[1] + yc_global

                # print(H,bkg_median)

                if syntax['remove_sat']:

                    if H+bkg_median >= syntax['sat_lvl']:
                        # print('here')
                        bkg_median = np.nan
                        H = np.nan
                        H_psf_err = np.nan
                        psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))
                        continue


            except Exception as e:
                logger.exception(e)
                bkg_median = np.nan
                H = np.nan
                H_psf_err = np.nan
                psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))
                continue

            if syntax['use_covarience']:

                H_psf_err = result.params['A'].stderr

            else:
                logger.warning('Error not computed')
                H_psf_err = 0

            psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))

            if return_subtraction_image:

                try:

                    image_section = image[int(yc_global -  syntax['scale']): int(yc_global + syntax['scale']),
                                          int(xc_global -  syntax['scale']): int(xc_global + syntax['scale'])]

                    image[int(yc_global  - syntax['scale']): int(yc_global +  syntax['scale']),
                          int(xc_global  - syntax['scale']): int(xc_global +  syntax['scale'])] =  image_section - build_psf(xc , yc, 0, H, residual_table)

                    image_section_subtraction = image_section - build_psf(xc , yc, 0, H, residual_table)


                    fig, ax1, = plt.subplots()

                    ax_before = ax1.inset_axes([0.95, 0.70, 0.4, 0.25])
                    ax_after  = ax1.inset_axes([0.95, 0.20, 0.4, 0.25])

                    ax1.imshow(image,
                               vmin = vmin,
                               vmax = vmax,
                               norm = norm,
                               origin = 'lower',
                               cmap = 'gist_heat',
                               interpolation = 'nearest')

                    ax1.set_xlim(0,image.shape[0])
                    ax1.set_ylim(0,image.shape[1])

                    ax1.scatter(xc_global,
                                yc_global,
                                marker = 'o',
                                facecolor = 'None',
                                color = 'green',
                                s = 25)


                    ax_before.imshow(image_section,
                                      vmin = vmin,
                                      vmax = vmax,
                                      norm = norm,
                                      origin = 'lower',
                                      cmap = 'gist_heat',
                                     interpolation = 'nearest')

                    ax_after.imshow(image_section_subtraction,
                                     vmin = vmin,
                                     vmax = vmax,
                                     norm = norm,
                                     origin = 'upper',
                                     cmap = 'gist_heat',
                                     interpolation = 'nearest')

                    ax_after.axis('off')
                    ax_before.axis('off')
                    ax1.axis('off')

                    ax_after.set_title('After')
                    ax_before.set_title('Before')

                    logger.info('Image %s / %s saved' % (str(idx),str(len(sources.index))))
                    plt.close(fig)

                except Exception as e:
                    logger.exception(e)
                    plt.close('all')
                    pass





            if syntax['show_residuals'] or show_plot == True or save_plot == True:
                fig = plt.figure(figsize = set_size(500,aspect =0.5))
                try:
                    from astropy.visualization import  ZScaleInterval

                    vmin,vmax = (ZScaleInterval(nsamples = 1500)).get_limits(source_base)

                    h, w = source_bkg_free.shape

                    x  = np.linspace(0, int(2*syntax['scale']), int(2*syntax['scale']))
                    y  = np.linspace(0, int(2*syntax['scale']), int(2*syntax['scale']))

                    X, Y = np.meshgrid(x, y)

                    ncols = 6
                    nrows = 3

                    heights = [1,1,0.75]
                    widths = [1,1,0.75,1,1,0.75]

                    grid = GridSpec(nrows, ncols ,wspace=0.5, hspace=0.5,
                                    height_ratios=heights,width_ratios = widths
                                    )
                    # grid = GridSpec(nrows, ncols ,wspace=0.3, hspace=0.5)



                    ax1   = fig.add_subplot(grid[0:2, 0:2])
                    ax1_B = fig.add_subplot(grid[2, 0:2])
                    ax1_R = fig.add_subplot(grid[0:2, 2])

                    ax2 = fig.add_subplot(grid[0:2, 3:5])

                    ax2_B = fig.add_subplot(grid[2, 3:5])
                    ax2_R = fig.add_subplot(grid[0:2, 5])


                    ax1_B.set_xlabel('X Pixel')
                    ax2_B.set_xlabel('X Pixel')

                    ax1.set_ylabel('Y Pixel')

                    # ax1.set_title('H:%d' % (H+bkg_median))
                    # ax2.set_ylabel('Y Pixel')




                    ax1_R.yaxis.tick_right()
                    ax2_R.yaxis.tick_right()


                    ax1.xaxis.tick_top()
                    ax2.xaxis.tick_top()

                    ax2.axes.yaxis.set_ticklabels([])
                    # ax2_R.axes.yaxis.set_ticklabels([])


                    bbox=ax1_R.get_position()
                    offset= -0.04
                    ax1_R.set_position([bbox.x0+ offset, bbox.y0 , bbox.x1-bbox.x0, bbox.y1 - bbox.y0])

                    bbox=ax2_R.get_position()
                    offset= -0.04
                    ax2_R.set_position([bbox.x0+ offset, bbox.y0 , bbox.x1-bbox.x0, bbox.y1 - bbox.y0])

                    bbox=ax1_B.get_position()
                    offset= 0.08
                    ax1_B.set_position([bbox.x0, bbox.y0+ offset , bbox.x1-bbox.x0, bbox.y1 - bbox.y0])

                    bbox=ax2_B.get_position()
                    offset= 0.08
                    ax2_B.set_position([bbox.x0, bbox.y0+ offset , bbox.x1-bbox.x0, bbox.y1 - bbox.y0])


                    ax1.imshow(source_base,
                               origin = 'lower',
                               aspect="auto",
                               vmin = vmin,
                               vmax = vmax,
                               interpolation = 'nearest'
                               )

                    ax1.scatter(xc,yc,label = 'Best fit',
                                marker = '+',
                                color = 'red',
                                s = 20)


                    ax1_R.plot(source_base[:,w//2],Y[:,w//2],marker = 'o',color = 'blue')
                    ax1_B.plot(X[h//2,:],source_base[h//2,:],marker = 'o',color = 'blue')

                    # include surface
                    ax1_R.plot(bkg_surface[:,w//2],Y[:,w//2],marker = 's',color = 'red')
                    ax1_B.plot(X[h//2,:],bkg_surface[h//2,:],marker = 's',color = 'red')

                    fitted_source = build_psf(xc,yc,0,H,residual_table)

                    # include fitted_source
                    ax1_R.plot((bkg_surface+fitted_source)[:,w//2],Y[:,w//2],marker = 's',color = 'green')
                    ax1_B.plot(X[h//2,:],(bkg_surface+fitted_source)[h//2,:],marker = 's',color = 'green')

                    '''
                    Subtracted image
                    '''

                    import matplotlib.ticker as ticker

                    ax1_B_yticks = np.array(ax1_B.get_yticks())
                    scale = order_shift(abs(ax1_B_yticks))
                    ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))

                    ax1_B.set_ylabel('$10^{%d}$ counts' % np.log10(order_shift(abs(ax1_B_yticks))))
                    ax1_B.yaxis.set_major_formatter(ticks)

                    ax1_R_yticks = np.array(ax1_R.get_xticks())
                    scale = order_shift(abs(ax1_R_yticks))
                    ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))

                    ax1_R.set_xlabel('$10^{%d}$ counts' % np.log10(order_shift(abs(ax1_R_yticks))))
                    ax1_R.xaxis.set_major_formatter(ticks)

                    ax2_B_yticks = np.array(ax2_B.get_yticks())
                    scale = order_shift(abs(ax2_B_yticks))
                    ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))

                    ax2_B.set_ylabel('$10^{%d}$ counts' % np.log10(order_shift(abs(ax2_B_yticks))))
                    ax2_B.yaxis.set_major_formatter(ticks)

                    ax2_R_yticks = np.array(ax2_R.get_xticks())
                    scale = order_shift(abs(ax2_R_yticks))
                    ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))

                    ax2_R.set_xlabel('$10^{%d}$ counts' % np.log10(order_shift(abs(ax2_R_yticks))))
                    ax2_R.xaxis.set_major_formatter(ticks)

                    subtracted_image = source_bkg_free - fitted_source + bkg_surface

                    ax2.imshow(subtracted_image,
                               origin = 'lower',
                               aspect="auto",
                               vmin = vmin,
                               vmax = vmax,
                               interpolation = 'nearest'
                               )

                    ax2.scatter(xc,yc,label = 'Best fit',
                                marker = '+',
                                color = 'red',
                                s = 20)

                    ax2_R.plot(subtracted_image[:,w//2],Y[:,w//2],marker = 'o',color = 'blue')
                    ax2_B.plot(X[h//2,:],subtracted_image[h//2,:],marker = 'o',color = 'blue')

                    # Show surface
                    ax2_R.plot(bkg_surface[:,w//2],Y[:,w//2],marker = 's',color = 'red')
                    ax2_B.plot(X[h//2,:],bkg_surface[h//2,:],marker = 's',color = 'red')

                    # include fitted_source
                    ax2_R.plot((bkg_surface+fitted_source)[:,w//2],Y[:,w//2],marker = 's',color = 'green')
                    ax2_B.plot(X[h//2,:],(bkg_surface+fitted_source)[h//2,:],marker = 's',color = 'green')



                    if save_plot == True:
                        fig.savefig(syntax['write_dir']+'target_psf_'+fname+'.pdf',
                                                    format = 'pdf',
                                                    # bbox_inches='tight'
                                                    )

                        logger.info('Image %s / %s saved' % (str(n+1),str(len(sources.index)) ))
                    else:

                        pathlib.Path(syntax['write_dir']+'/'+'psf_subtractions/').mkdir(parents = True, exist_ok=True)

                        plt.savefig(syntax['write_dir']+'psf_subtractions/'+'psf_subtraction_{}.png'.format(int(n)))

                    plt.close(fig)

                except Exception as e:
                    logger.exception(e)
                    plt.close('all')


        except Exception as e:
             logger.exception(e)


             bkg_median = np.nan
             H = np.nan
             H_psf_err = np.nan
             psf_params.append((idx,x_fitted,y_fitted,bkg_median,H,H_psf_err))

             continue


    new_df =  pd.DataFrame(psf_params,columns = ('idx','x_fitted','y_fitted','bkg','H_psf','H_psf_err'),index = sources.index)

    if return_fwhm:
        new_df['target_fwhm'] = target_PSF_FWHM,
        # new_df['target_fwhm_err'] =source_fwhm_err
    elif not no_print:
        print('  ')
    if not return_psf_model:


        return pd.concat([sources,new_df],axis = 1),build_psf