示例#1
0
def get_flux_and_err(imagedat, psfmodel, xy, ntestpositions=100, psfradpix=3,
                     apradpix=3, skyannpix=None, skyalgorithm='sigmaclipping',
                     setskyval=None, recenter_target=True, recenter_fakes=True,
                     exptime=1, exact=True, ronoise=1, phpadu=1, verbose=False,
                     debug=False):
    """  Measure the flux and flux uncertainty for a source at the given x,y
    position using both aperture and psf-fitting photometry.

    Flux errors are measured by planting fake psfs or empty apertures into the
    sky annulus and recovering a distribution of fluxes with forced photometry.

    :param imagedat: target image numpy data array (with the star still there)
    :param psfmodel: psf model fits file or a 4-tuple with
           [gaussparam,lookuptable,psfmag,psfzpt]
    :param xy: x,y position of the center of the fake planting field
    :param ntestpositions: number of test positions for empty apertures
           and/or fake stars to use for determining the flux error empirically.
    :param psfradpix: radius to use for psf fitting, in pixels
    :param apradpix: radius of photometry aperture, in pixels
    :param skyannpix: inner and outer radius of sky annulus, in pixels
    :param skyalgorithm: algorithm to use for determining the sky value from
           the pixels within the sky annulus: 'sigmaclipping' or 'mmm'
    :param setskyval: if not None, use this value for the sky, ignoring
           the skyannulus
    :param recenter_target: use cntrd to locate the target center near the
           given xy position.
    :param recenter_fakes: recenter on each planted fake when recovering it
    :param exptime: exposure time of the image, for determining poisson noise
    :param ronoise: read-out noise, for determining aperture flux error
           analytically
    :param phpadu: photons-per-ADU, for determining aper flux err analytically
    :param verbose: turn verbosity on
    :param debug: enter pdb debugging mode
    :return: apflux, apfluxerr, psfflux, psffluxerr
             The flux measured through the given aperture and through
             psf fitting, along with associated errors.
    """
    if not np.any(skyannpix):
        skyannpix = [8, 15]

    # locate the target star center position
    x, y = xy
    if recenter_target:
        x, y = cntrd(imagedat, x, y, psfradpix)
        if x < 0 or y < 0:
            print("WARNING [photfunctions.py] : recentering failed")
            import pdb
            pdb.set_trace()

    # do aperture photometry directly on the source
    # (Note : using an arbitrary zeropoint of 25 here)
    aperout = aper(imagedat, x, y, phpadu=phpadu,
                   apr=apradpix, skyrad=skyannpix,
                   setskyval=setskyval,
                   zeropoint=25, exact=exact,
                   verbose=verbose, skyalgorithm=skyalgorithm,
                   debug=debug)
    apmag, apmagerr, apflux, apfluxerr, sky, skyerr, apbadflag, apoutstr = \
        aperout

    # define a set of test position points that uniformly samples the sky
    # annulus region, for planting empty apertures and/or fake stars
    rmin = float(skyannpix[0])
    rmax = float(skyannpix[1])
    u = np.random.uniform(rmin, rmax, ntestpositions)
    v = np.random.uniform(0, rmin + rmax, ntestpositions)
    r = np.where(v < u, u, rmax + rmin - u)
    theta = np.random.uniform(0, 2 * np.pi, ntestpositions)
    xtestpositions = r * np.cos(theta) + x
    ytestpositions = r * np.sin(theta) + y

    psfflux = psffluxerr = np.nan
    if psfmodel is not None:
        # set up the psf model realization
        gaussparam, lookuptable, psfmag, psfzpt = rdpsfmodel(psfmodel)
        psfmodel = [gaussparam, lookuptable, psfmag, psfzpt]
        pk = pkfit_class(imagedat, gaussparam, lookuptable, ronoise, phpadu)

        # do the psf fitting
        try:
            scale = pk.pkfit_fast_norecenter(1, x, y, sky, psfradpix)
            psfflux = scale * 10 ** (0.4 * (25. - psfmag))
        except RuntimeWarning:
            print("PythonPhot.pkfit_norecenter failed.")
            psfflux = np.nan

        if np.isfinite(psfflux):
            # remove the target star from the image
            imagedat = addtoimarray(imagedat, psfmodel, [x, y],
                                    fluxscale=-psfflux)

            # plant fakes and recover their fluxes with psf fitting
            # imdatsubarray = imagedat[y-rmax-2*psfradpix:y+rmax+2*psfradpix,
            #                x-rmax-2*psfradpix:x+rmax+2*psfradpix]
            fakecoordlist, fakefluxlist = [], []
            for xt, yt in zip(xtestpositions, ytestpositions):
                    # To ensure appropriate sampling of sub-pixel positions,
                    # we assign random sub-pixel offsets to each position.
                    xt = int(xt) + np.random.random()
                    yt = int(yt) + np.random.random()
                    fakefluxaper, fakefluxpsf, fakecoord = add_and_recover(
                        imagedat, psfmodel, [xt, yt], fluxscale=psfflux,
                        cleanup=True, psfradius=psfradpix, recenter=recenter_fakes)
                    if np.isfinite(fakefluxpsf):
                        fakecoordlist.append(fakecoord)
                        fakefluxlist.append(fakefluxpsf)
            fakefluxlist = np.array(fakefluxlist)
            fakefluxmean, fakefluxsigma = gaussian_fit_to_histogram(fakefluxlist)
            if abs(fakefluxmean - psfflux) > fakefluxsigma and verbose:
                print("WARNING: psf flux may be biased. Fake psf flux tests "
                      "found a significantly non-zero sky value not accounted for "
                      "in measurement of the target flux:  \\"
                      "Mean psf flux offset in sky annulus = %.3e\\" %
                      (fakefluxmean - psfflux) +
                      "sigma of fake flux distribution = %.3e" %
                      fakefluxsigma +
                      "NOTE: this is included as a systematic error, added in "
                      "quadrature to the psf flux err derived from fake psf "
                      "recovery.")
            psfflux_poissonerr = (poissonErr(psfflux * exptime, confidence=1) /
                                  exptime)
            # Total flux error is the quadratic sum of the poisson noise with
            # the systematic (shift) and statistical (dispersion) errors
            # inferred from fake psf planting and recovery
            psffluxerr = np.sqrt(psfflux_poissonerr**2 +
                                 (fakefluxmean - psfflux)**2 +
                                 fakefluxsigma**2)

    # drop down empty apertures and recover their fluxes with aperture phot
    # NOTE : if the star was removed for psf fitting, then we take advantage
    # of that to get aperture flux errors with the star gone.
    emptyaperout = aper(imagedat, np.array(xtestpositions),
                        np.array(ytestpositions), phpadu=phpadu,
                        apr=apradpix, setskyval=sky, zeropoint=25,
                        exact=False, verbose=verbose,
                        skyalgorithm=skyalgorithm, debug=debug)
    emptyapflux = emptyaperout[2]
    if np.any(np.isfinite(emptyapflux)):
        emptyapmeanflux, emptyapsigma = gaussian_fit_to_histogram(emptyapflux)
        emptyapbias = abs(emptyapmeanflux) - emptyapsigma
        if np.any(emptyapbias > 0) and verbose:
            print("WARNING: aperture flux may be biased. Empty aperture flux tests"
                  " found a significantly non-zero sky value not accounted for in "
                  "measurement of the target flux:  \\"
                  "Mean empty aperture flux in sky annulus = %s\\"
                  % emptyapmeanflux +
                  "sigma of empty aperture flux distribution = %s"
                  % emptyapsigma)
        if np.iterable(apflux):
            apflux_poissonerr = np.array(
                [poissonErr(fap * exptime, confidence=1) / exptime
                 for fap in apflux])
        else:
            apflux_poissonerr = (poissonErr(apflux * exptime, confidence=1) /
                                 exptime)
        apfluxerr = np.sqrt(apflux_poissonerr**2 +
                            emptyapbias**2 + emptyapsigma**2)

    else:
        if np.iterable(apradpix):
            apfluxerr = [np.nan for aprad in apradpix]
        else:
            apfluxerr = np.nan

    if psfmodel is not None and np.isfinite(psfflux):
        # return the target star back into the image
        imagedat = addtoimarray(imagedat, psfmodel, [x, y],
                                fluxscale=psfflux)

    if debug > 1:
        import pdb
        pdb.set_trace()

    return apflux, apfluxerr, psfflux, psffluxerr, sky, skyerr
indices_y = np.where(d_y > 0)[0]

width_x = len(indices_x)
width_y = len(indices_y)

fwhm = (width_x + width_y) / 2

rms = (np.std(image - image.mean(axis=0)))

x, y, flux, sharp, round = find(image, fwhm=fwhm, hmin=rms * 3, verbose=False)

xpos, ypos = x, y

# run aper to get mags and sky values for specified coords
mag, magerr, flux, fluxerr, sky, skyerr, badflag, outstr = \
    aper.aper(image, xpos, ypos, phpadu=1, apr=5, zeropoint=25,
              skyrad=[40, 50], badpix=[0, 10000000000], exact=True, verbose=False)
# use the stars at those coords to generate a PSF model
gauss, psf, psfmag = \
    getpsf.getpsf(image, xpos, ypos,
                  mag, sky, 1, 1, np.arange(len(xpos)),
                  fitrad=window/2, psfname='output_psf.fits', psfrad=int(window+20)/2, verbose=False)

print("Amplitude of Gauss Function : " + str(gauss[0]))
print("X-Axis Best Fit Offset : " + str(gauss[1]))
print("Y-Axis Best Fit Offset : " + str(gauss[2]))
print("Standard deviation of the gauss function on the X-axis : " +
      str(gauss[3]))
print("Standard deviation of the gauss function on the Y-axis : " +
      str(gauss[4]))

# plt.plot(gauss)
示例#3
0
def add_and_recover(imagedat, psfmodel, xy, fluxscale=1, psfradius=5,
                    skyannpix=None, skyalgorithm='sigmaclipping',
                    setskyval=None, recenter=False, ronoise=1, phpadu=1,
                    cleanup=True, verbose=False, debug=False):
    """  Add a single fake star psf model to the image at the given position
    and flux scaling, re-measure the flux at that position and report it,
    Also deletes the planted psf from the imagedat array so that we don't
    pollute that image array.

    :param imagedat: target image numpy data array
    :param psfmodel: psf model fits file or tuple with [gaussparam,lookuptable]
    :param xy: x,y position for fake psf, using the IDL/python convention
        where [0,0] is the lower left corner.
    :param fluxscale: flux scaling to apply to the planted psf
    :param recenter: use cntrd to locate the center of the added psf, instead
        of relying on the input x,y position to define the psf fitting
    :param cleanup: remove the planted psf from the input imagedat array.
    :return:
    """
    if not skyannpix:
        skyannpix = [8, 15]

    # add the psf to the image data array
    imdatwithpsf = addtoimarray(imagedat, psfmodel, xy, fluxscale=fluxscale)

    # TODO: allow for uncertainty in the x,y positions

    gaussparam, lookuptable, psfmag, psfzpt = rdpsfmodel(psfmodel)

    # generate an instance of the pkfit class for this psf model
    # and target image
    pk = pkfit_class(imdatwithpsf, gaussparam, lookuptable, ronoise, phpadu)
    x, y = xy

    if debug:
        from .photfunctions import showpkfit
        from matplotlib import pyplot as pl, cm

        fig = pl.figure(3)
        showpkfit(imdatwithpsf, psfmodel, xy, 11, fluxscale, verbose=True)

        fig = pl.figure(1)
        pl.imshow(imdatwithpsf[y - 20:y + 20, x - 20:x + 20], cmap=cm.Greys,
                  interpolation='nearest')
        pl.colorbar()

        import pdb

        pdb.set_trace()

    if recenter:
        xc, yc = cntrd(imdatwithpsf, x, y, psfradius, verbose=verbose)
        if xc > 0 and yc > 0 and abs(xc - xy[0]) < 5 and abs(yc - xy[1]) < 5:
            x, y = xc, yc
    # do aperture photometry to get the sky
    aperout = aper(imdatwithpsf, x, y, phpadu=phpadu,
                   apr=psfradius * 3, skyrad=skyannpix,
                   setskyval=(setskyval is not None and setskyval),
                   zeropoint=psfzpt, exact=False,
                   verbose=verbose, skyalgorithm=skyalgorithm,
                   debug=debug)
    apmag, apmagerr, apflux, apfluxerr, sky, skyerr, apbadflag, apoutstr\
        = aperout

    # do the psf fitting
    try:
        scale = pk.pkfit_fast_norecenter(1, x, y, sky, psfradius)
        fluxpsf = scale * 10 ** (-0.4 * (psfmag - psfzpt))
    except RuntimeWarning:
        print("photfunctions.add_and_recover failed on RuntimeWarning")
        fluxpsf = -99
    if cleanup:
        # remove the fake psf from the image
        imagedat = addtoimarray(imdatwithpsf, psfmodel, xy,
                                fluxscale=-fluxscale)

    return apflux[0], fluxpsf, [x, y]
示例#4
0
def simulate_image(psffits=None,
                   theofits=None,
                   obsfits=None,
                   distance=None,
                   extfits=None,
                   wlen=None,
                   pfov=None,
                   filt=None,
                   psfext=0,
                   obsext=0,
                   bgstd=0,
                   bgmed=0,
                   silent=False,
                   posang=0,
                   foi_as=4,
                   outname=None,
                   manscale=None,
                   fnucdiam_as=0.45,
                   suffix=None,
                   fitsize=None,
                   outfolder='.',
                   writesimfits=False,
                   writetheofits=False,
                   writeallfits=False,
                   writefitplot=False,
                   debug=False,
                   returncmod=True,
                   fitpsf=False,
                   saveplot=False,
                   meastime=False):
    """
    Simulate a (VISIR) imaging observaion given a real image, a PSF reference
    image and a model image for a provided object distance and position angle

    Current constraints:
        - The routine assumes that the images are squares (x = y)
    """

    if meastime:
        tstart = time.time()

    psfim = fits.getdata(psffits, ext=psfext)
    psfhead = fits.getheader(psffits)

    # print(obsfits, obsext)
    # ==== 1. Load Observational and PSF data ====
    if obsfits is not None:
        obsim = fits.getdata(obsfits, ext=obsext)
        obshead = fits.getheader(obsfits)

        if wlen is None:
            wlen = float(obshead['WAVELEN'])

        if pfov is None:
            pfov = obshead['PFOV']

        if filt is None:
            filt = obshead['Filter']

    else:
        if wlen is None:
            wlen = float(psfhead['WAVELEN'])

        if pfov is None:
            pfov = psfhead['PFOV']

        if filt is None:
            filt = psfhead['Filter']

    wlenstr = "{:.1f}".format(wlen)
    diststr = "{:.1f}".format(distance)
    pastr = "{:.0f}".format(posang)

    # --- optinal cropping of the PSF image
    if fitsize is not None:
        psfim = _crop_image(psfim,
                            box=fitsize,
                            cenpos=_get_pointsource(psfim)[0][2:4])

    psfsize = np.array(np.shape(psfim))
    psfsize_as = psfsize[0] * pfov

    if not silent:
        print("Obs. wavelength [um]: " + wlenstr)

    # ==== 2. Prepare theoretical image for convolution ====
    theohdu = fits.open(theofits)
    theohead = theohdu[0].header

    # --- check if provided file is the full cube
    if 'NAXIS3' in theohead:
        # find the right frame to be extracted
        mind, mwlen = find_wlen_match(theofits, wlen)
        theoim = theohdu[0].data[mind]

        if not silent:
            print(" - Model wavelength [um] | frame no: " + str(mwlen) +
                  " | " + str(mind))

    else:
        theoim = theohdu[0].data

    theohdu.close()

    if meastime:
        print(" - All files read. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("THEOIM: ")
        plt.imshow(theoim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- if a manual scaling (fudge) factor was provided, apply to the model
    if manscale:
        theoim = theoim * manscale

    # --- rotate the model image (this changes its extent and thus has to be
    #     done first)
    # unrot = np.copy(theoim)
    if np.abs(posang - 0) > 0.01:
        theoim = ndimage.interpolation.rotate(theoim, 180 - posang, order=0)

    if meastime:
        print(" - Model rotated. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("THEOIM_ROT: ")
        plt.imshow(theoim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- size units of the theoretical image
    theopixsize_pc = theohead['CDELT1']
    theosize = np.array(np.shape(theoim))
    theosize_pc = theopixsize_pc * theosize[0]  # pc
    theosize_as = 2 * np.arctan(
        theosize_pc / distance / 2.0e6) * 180 / np.pi * 3600
    theopixsize_as = (2 * np.arctan(theopixsize_pc / distance / 2.e6) * 180 /
                      np.pi * 3600)

    #    plt.imshow(unrot, origin='bottom', norm=LogNorm())
    #    plt.imshow(theoim, origin='bottom', norm=LogNorm())
    #    plt.imshow(theoim, origin='bottom')

    #    theopfov = angsize/theosize[0]

    # normalize image to flux = 1
    # theoim = theoim/np.sum(theoim)
    # theoim = theoim/np.max(theoim)

    # --- convert to the right flux units
    # surface brightness unit in cube is W/m^2/arcsec2
    # -> convert to flux density in mJy
    # print(np.sum(theoim))
    freq = 2.99793e8 / (1e-6 * wlen)
    # print(freq)

    theoim = theoim * 1.0e29 / freq * theopixsize_as**2

    theototflux = np.sum(theoim)

    # --- resample to the same pixel size as the observation
    # print(   "- Resampling the model image to instrument pixel size...")
    # --- the ndimage.zoom sometimes creates weird artifacts and thus might not
    #     be a good choice. Instead, use the self-made routine
    # theoim_resres = ndimage.zoom(theoim_res, theopixsize_as/pfov, order=0)
    # --- do the rasmpling before scaling it to the size of the observation to
    #     save computing time
    theoim_res, sizerat = _increase_pixelsize(theoim,
                                              oldpfov=theopixsize_as,
                                              newpfov=pfov,
                                              meastime=False)

    if meastime:
        print(" - Pixelsize increased. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("sizerat: ", sizerat)
        print("THEOIM_RESRES: ")
        plt.imshow(theoim_res,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- if the PSF frame size is larger than the model extend the model frame
    framerat = psfsize_as / theosize_as

    if debug is True:
        print("psfsize_as: ", psfsize_as)
        print("theosize_as: ", theosize_as)
        print("theopixsize_as: ", theopixsize_as)
        print("theosize_pc: ", theosize_pc)
        print("theosize: ", theosize)
        print("framerat: ", framerat)
    #print("newsize: ", newsize)

    if framerat > 1.0:
        theoim_resres = _extend_image(theoim_res, fac=framerat)

        if meastime:
            print(" - Frame extended. Elapsed time: ", time.time() - tstart)

        if debug is True:
            print("thesize_ext: ", np.shape(theoim_resres))
            print("THEOIM_RESRES: ")
            plt.imshow(theoim_resres,
                       origin='bottom',
                       norm=LogNorm(),
                       interpolation='nearest')
            plt.show()
        # plt.imshow(theoim_res, origin='bottom')

    else:
        theoim_resres = np.copy(theoim_res)

    theosize_new = np.array(np.shape(theoim_resres))

    if debug is True:
        print("theosize_new: ", theosize_new)
        print("new theosize_as: ", theosize_new * pfov)

    # print(np.sum(theoim_resres))

    # --- after resampling we need to re-establish the right flux levels
    theoim_resres = theoim_resres / np.sum(theoim_resres) * theototflux

    # plt.imshow(theoim_resres, origin='bottom', norm=LogNorm())
    # plt.imshow(theoim_resres, origin='bottom')

    # ==== 4. Optionally, apply a foreground extinction map if provided ====
    if extfits:

        exthdu = fits.open(extfits)
        exthead = exthdu[0].header
        extpfov = exthead["PFOV"]

        # check if provided file is the full cube
        if 'NAXIS3' in exthead:

            # find the right frame to be extracted
            if not mind:
                mind, mwlen = find_wlen_match(theofits, wlen)

            extim = exthdu[0].data[mind]

        else:
            extim = exthdu[0].data

        # plt.imshow(extim,origin='bottom')
        # plt.show()

        exthdu.close()

        # --- Do we have to resample the extinction map
        if np.abs(extpfov - pfov) > 0.001:
            # print("Resampling extinction map... ")
            extim = ndimage.zoom(extim, 1.0 * pfov / extpfov, order=0)

        # --- match the size of the extinction map to the one of the model
        # image
        extsize = np.array(np.shape(extim))

        if theosize_new[0] > extsize[0]:
            print(" - ERROR: provided extinction map is too small to cover \
                   the imaged region. Abort...")
            return ()

        if extsize[0] > theosize_new[0]:
            # print("Cuttting exctinction map...")
            extim = _crop_image(extim, box=theosize_new)

        # --- finally apply the extinction map
        theoim_resres = theoim_resres * extim
        # print('Maximum extinction: ',np.min(extim))

    # ==== 5. Obtain the Airy function from the calibrator ====
    if debug is True:
        print("PSF_IM: ")
        plt.imshow(psfim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- set up the fitting of the STD star image
    # --- assume that the maximum of the (cropped) image is the STD star
    if fitpsf is True:
        psfmax = np.max(psfim)
        psfmaxpos = np.unravel_index(np.argmax(psfim), psfsize)
        # print(im_bg, im_max, im_size, im_maxpos)

        # --- Use an Airy plus a Moffat + constant as PSF model
        c_init = models.Const2D(amplitude=np.median(psfim))

        oa_init = obsc_AiryDisk2D(amplitude=psfmax,
                                  x_0=psfmaxpos[1],
                                  y_0=psfmaxpos[0],
                                  radius=5,
                                  obsrat=0.15)

        m_init = models.Moffat2D(amplitude=psfmax,
                                 x_0=psfmaxpos[1],
                                 y_0=psfmaxpos[0],
                                 gamma=5,
                                 alpha=1)

        a_plus_m = oa_init + m_init + c_init

        # print(psfmax, psfmaxpos)

        # --- Selection of fitting method
        fit_meth = fitting.LevMarLSQFitter()

        # --- Do the Fitting:
        y, x = np.mgrid[:psfsize[0], :psfsize[1]]
        am_fit = fit_meth(a_plus_m, x, y, psfim)
        # print(am_fit.radius_0.value, am_fit.obsrat_0.value)

        # then refine the fit by subtracting another Moffat
        # a_plus_m_minus_m = am_fit - m_init
        # amm_fit = fit_meth(a_plus_m_minus_m, x, y, psfim)
        # z_amm = amm_fit(x, y)
        #        res = psfim - z_amm
        #print(amm_fit.radius_0.value, amm_fit.obsrat_0.value)

        if meastime:
            print(" - PSf fit. Elapsed time: ", time.time() - tstart)

        if debug is True:
            print("PSF_FIT: ")
            plt.imshow(am_fit(x, y),
                       origin='bottom',
                       norm=LogNorm(),
                       interpolation='nearest')
            plt.show()

        # --- Genarate the PSF image with the fitted model
        # --- check whether the model image is on sky larger than the PSF image
        #     and if this is the case increase the grid and adjust the positions
        #     of the fits
        size_diff = theosize_new[0] - psfsize[0]
        if size_diff > 0:
            y, x = np.mgrid[:theosize_new[0], :theosize_new[1]]
            am_fit.x_0_0 = am_fit.x_0_0 + 0.5 * size_diff
            am_fit.y_0_0 = am_fit.y_0_0 + 0.5 * size_diff
            am_fit.x_0_1 = am_fit.x_0_1 + 0.5 * size_diff
            am_fit.y_0_1 = am_fit.y_0_1 + 0.5 * size_diff

        # --- create the final PSF image by subtracting the constant terms
        # and normalise by its area
        psf = am_fit(x, y) - am_fit.amplitude_2.value
        psf = psf / np.sum(psf)

        if debug is True:
            print("PSF_FINAL: ")
            plt.imshow(psf,
                       origin='bottom',
                       norm=LogNorm(),
                       interpolation='nearest')
            plt.show()

        # plt.imshow(psf, origin='bottom', norm=LogNorm())
        # plt.show()
        if writeallfits is True:

            fout = psffits.replace('.fits', '_fit.fits')
            if 'OBS NAME' in psfhead:
                psfhead.remove('OBS NAME')
            fits.writeto(fout, psf, psfhead, overwrite=True)

        # --- optinally, write the a plot documenting the quality of the PSF fit
        if writefitplot:

            fitplotfname = psffits.split('/')[-1]
            fitplotfname = fitplotfname.replace('.fits', '_fitplot.pdf')
            fitplotfname = outfolder + '/' + fitplotfname

            maxrad = int(0.5 * np.sqrt(0.5) * np.min(psfsize))

            _make_fit_plots(psfim,
                            am_fit(x, y),
                            fitplotfname,
                            am_fit.x_0_0,
                            am_fit.y_0_0,
                            maxrad,
                            inv=True,
                            cmap='gist_heat',
                            labelcolor='black')

    # --- alternatively the PSF can also be direclty provided so that no fit is
    #     necessary
    else:
        # normalise the provided psf
        psf = psfim - np.nanmin(psfim)
        psf = psf / np.nansum(psf)
        psf[np.argwhere(np.isnan(psf))] = 0

    # ==== 6. Convolution of the model image with the PSF ====
    simim = scipy.signal.convolve2d(theoim_resres, psf, mode='same')
    # print(np.sum(simim))

    if meastime:
        print(" - Model convolved. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("SIMIM:")
        plt.imshow(simim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # ==== 7. Flux measurements and application of noise ====
    # --- Flux measurement on the model image before applying noise
    ftotmod = int(np.nansum(simim))

    apos = 0.5 * np.array(theosize_new)

    nucrad_px = 0.5 * fnucdiam_as / pfov
    from aper import aper
    (mag, magerr, flux, fluxerr, sky, skyerr, badflag,
     outstr) = aper(simim,
                    apos[1],
                    apos[0],
                    apr=nucrad_px,
                    exact=True,
                    setskyval=0)

    fnucmod = int(flux)

    if not silent:
        print(' - Model total | nuclear flux [mJy]:    ' + str(ftotmod) +
              ' | ' + str(fnucmod))

    plotsize_px = int(1.0 * foi_as / pfov)

    # --- Measure the background noise from the real observation
    if obsfits is not None:
        bg = np.copy(obsim)
        #    bgsize = np.shape(bg)
        params, _, _ = _get_pointsource(obsim)
        xpos = params[2]
        ypos = params[3]

        bg[int(ypos - 0.5 * plotsize_px):int(ypos + 0.5 * plotsize_px),
           int(xpos - 0.5 * plotsize_px):int(xpos + 0.5 * plotsize_px)] = 0

        if bgstd == 0:
            bgstd = np.std(bg[bg != 0])

        if bgmed == 0:
            bgmed = np.median(bg[bg != 0])
        # plt.imshow(bg, origin='bottom', norm=LogNorm(), cmap='gist_heat')

        # --- crop the observational image to the requested plot size
        cim = _crop_image(obsim,
                          box=plotsize_px,
                          cenpos=_get_pointsource(obsim)[0][2:4])
        newsize = np.shape(cim)

        # --- make flux measurements on the nucleus and total
        ftotobs = int(np.sum(cim - bgmed))
        apos = 0.5 * np.array(newsize)

        (mag, magerr, flux, fluxerr, sky, skyerr, badflag,
         outstr) = aper(cim,
                        apos[1],
                        apos[0],
                        apr=nucrad_px,
                        exact=True,
                        setskyval=0)

        fnucobs = int(flux)

        if not silent:
            print(' - Observed total | nuclear flux [mJy]: ' + str(ftotobs) +
                  ' | ' + str(fnucobs))

    else:
        cim = None

    if meastime:
        print(" - Fluxes measured. Elapsed time: ", time.time() - tstart)

    # pdb.set_trace()
    # --- crop the simulated image to the requested plot size
    params, _, _ = _get_pointsource(simim)
    csimim = _crop_image(simim,
                         box=plotsize_px,
                         cenpos=_get_pointsource(simim)[0][2:4])
    if debug is True:
        plt.imshow(simim, origin='bottom', cmap='gist_heat', norm=LogNorm())
        plt.title('simim')
        plt.show()
        print(plotsize_px, np.shape(csimim))
        plt.imshow(csimim, origin='bottom', cmap='gist_heat', norm=LogNorm())
        plt.title('csimim')
        plt.show()

    if meastime:
        print(" - Obs cropped. Elapsed time: ", time.time() - tstart)

    # --- generate an artifical noise frame with same properties as in real
    #     image
    if bgstd > 0:
        artbg = np.random.normal(scale=bgstd,
                                 size=(plotsize_px, plotsize_px)) + bgmed
        # artbg = 0

        # --- apply the artificial background
        # csimim = csimim/np.max(csimim)*(np.max(cim)-bgmed)+bgmed+artbg
        csimim = csimim + artbg

    # --- crop the PSF image to the requested plot size
    cpsf = _crop_image(psf,
                       box=plotsize_px,
                       cenpos=_get_pointsource(psf)[0][2:4])

    # print(bgmed, bgstd, np.std(artbg), np.median(cim), np.median(csimim),
    # np.std(cim), np.std(csimim), np.max(cim), np.min(cim), np.max(csimim),
    # np.min(csimim))

    if meastime:
        print(" - PSF cropped. Elapsed time: ", time.time() - tstart)

    theopfov = theopixsize_as
    theobox = int(np.round(plotsize_px * pfov / theopfov))

    if returncmod:
        # --- crop the model imae to the requested plot size
        if framerat > 1.0:
            theoim_res_0 = _extend_image(theoim, fac=framerat)
        else:
            theoim_res_0 = theoim
        cmod = _crop_image(theoim_res_0, box=theobox, exact=False)
        # plt.imshow(cmod, origin='bottom', norm=LogNorm(), cmap='gist_heat')

        if meastime:
            print(" - Theo cropped. Elapsed time: ", time.time() - tstart)

    else:
        cmod = None

    # --- write out the simulated image as a fits file
    if not outname:

        theofitsfile = theofits.split("/")[-1]

        out_str = theofitsfile.replace("_total.fits", "")

        out_str = (out_str + "_pa" + pastr + "_dist" + diststr + "_wlen" +
                   wlenstr)

        if suffix:
            out_str = out_str + "_" + suffix

    else:
        out_str = outname

    out_str = outfolder + '/' + out_str

    if writeallfits or writetheofits:

        fout = out_str + '_mod.fits'

        theohead["Filter"] = filt
        theohead["WAVELEN"] = wlen
        theohead["PFOV"] = theopixsize_as

        fits.writeto(fout, cmod, theohead, overwrite=True)

        if saveplot:
            _simple_image_plot(cmod, fout.replace(".fits", ".png"), log=True)

    if writeallfits or writesimfits:

        fout = out_str + '_sim.fits'
        theohead["PFOV"] = pfov
        theohead["BUNIT"] = 'mJy'

        ts = np.shape(simim)
        theohead["CRPIX1"] = 0.5 * ts[1]
        theohead["CDELT1"] = pfov
        theohead["CTYPE1"] = 'arcsec'
        theohead["CRPIX2"] = 0.5 * ts[0]
        theohead["CDELT2"] = pfov
        theohead["CTYPE2"] = 'arcsec'

        fits.writeto(fout, csimim, theohead, overwrite=True)

        if saveplot:
            _simple_image_plot(csimim, fout.replace(".fits", ".png"), log=True)

    if meastime:
        print(" - All finished: ", time.time() - tstart)

    return (cpsf, cmod, cim, csimim, wlen, pfov, theopfov)