Exemplo n.º 1
0
def queryCoordSimbad(raw_coord, search_radius):
    #Import(s)
    import numpy as np
    from astropy import coordinates as coord
    from astropy import units as u
    from astroquery.simbad import Simbad
    from astropy.coordinates.sky_coordinate import SkyCoord

    #Action
    c = SkyCoord(raw_coord, unit=(u.hourangle, u.deg))
    c = c.to_string('hmsdms')
    result_table = Simbad.query_region(coord.SkyCoord(c, frame='icrs'),
                                       radius=('0d0m' + str(search_radius) +
                                               's'))
    names_col = result_table['MAIN_ID']
    id = str(names_col[0])[1:]
    return id
Exemplo n.º 2
0
def reduce_exposure(rawfiles=None,
                    draw=None,
                    dpro=None,
                    expid=None,
                    sof=None,
                    temprofolder=None,
                    rawfolder=None,
                    logfile=None,
                    outname='VISIR_OBS',
                    outfolder='.',
                    overwrite=False,
                    maxshift=0.5,
                    extract=True,
                    searcharea='chopthrow',
                    box=None,
                    statcalfolder=None,
                    crossrefim=None,
                    chopsubmeth='averchop',
                    AA_pos=None,
                    refpos=None,
                    alignmethod='fastgauss',
                    findbeams=True,
                    verbose=False,
                    ditsoftaver=1,
                    sky_xrange=None,
                    plot=False,
                    insmode=None,
                    sky_yrange=None,
                    debug=False,
                    instrument=None,
                    sourceext='unknown',
                    searchsmooth=0.2,
                    sigmaclip=[3, 1]):
    """
    main reduction routine: reduce a give exposure by combining the nods,
    perform alignment of DITs/chops in case of burst/half-cycle data and
    extract source images

    PARAMETERS:
     - rawfiles: (optional) list of raw fits files belonging to the exposure to reduce
     - draw: (optional) data structure containing all the raw file information as result of the routine reduce_indi_raw_files
     - dpro: (optional) data structure containg all the exposure information as a product of the routine group_files_into_observations
     - temprofolder: (optional) folder containing the temporary products, i.e., individually reduced files. If 'None', the output folder is taken
     - rawfolder: (optional) folder containing the raw fits files to be reduced
     - outname: (default='VISIR_OBS') name prefix for the logfile
     - outfolder: (default='.') folder to write output into
     - overwrite: (default=False) overwrite existing data?
     - maxshift: (default=0.5) maximum allowed discrepany between expected and found beam
                 position in a chop/nod pattern in arcsec
     - searchsmooth: (default=0.2) sigma for Gaussian smoothing for beam detection in arcsecc
     - extract: (default=True) Try detect a source in the combined nod image and extract subimage
     - searcharea: (default='chopthrow') specify the area to search for the sub beam as string in arcsec (e.g., "2 arcsec")
     - box: (optional) by default the subimage will have the size of the chopthrow
     - chopsubmeth: (default='averchop') method for the subtraction of the chops in burst mode. By default the average of the exposures in the offset position of the corresponding pair is used.
     - AA_pos: (optional) specify position of the beam in chop/nod position AA for burst alignment
     - refpos: (optional) instead of searching for the beam position, provide one directly for the alignment
     - findbeams: (default: True) Should the code try to find the beam positions or just extract at the computed position?
     - alignmethod: (default='fastgauss') specify the fitting algorithm for the frame alignment in burst mode data
     - verbose = False
     - sourceext: (default ='unknown') expected extent of the source ('compact', 'extended', 'unknown')

    NOT FULLY IMPLEMENTED:
     - call as a stand-alone routine, i.e., without providing draw and dpro
       This would require implementation extracting files from the sof.txt as
       well as reduction of the raw files with reduce_indi_raws

    TO BE ADDED LATER:
        - correct treatment of HR and HRX SPC
        - correct treatment of SPC ACQ data
        - proper modification of the headers of the products
        - de-rotation for classical imaging with pupil tracking (burst should work)
    """

    funname = "REDUCE_EXPOSURE"

    if debug:
        verbose = True

    noe = 0  # number of error counter
    now = 0  # number of warnings

    # --- flag whether only fits header or also table should be updated with
    #     new Gaussian fit paramaters (will be set false after reducing a burst)
    onlyhead = False

    if temprofolder is None:
        temprofolder = outfolder

    # --- create output folders
    hcycfolder = temprofolder + '/hcycles'
    bsumfolder = hcycfolder + '/blindsums'
    nodfolder = temprofolder + '/nods'

    if not os.path.exists(hcycfolder):
        os.makedirs(hcycfolder)

    if not os.path.exists(bsumfolder):
        os.makedirs(bsumfolder)

    if not os.path.exists(nodfolder):
        os.makedirs(nodfolder)

    # --- test whether output folder exists
    if not os.path.exists(outfolder):
        os.makedirs(outfolder)

    if logfile is None:
        logfile = outfolder + '/' + outname + ".log"
        mode = 'w'
    else:
        mode = 'a'

    _print_log_info(funname + ": Reduction of " + outname, logfile, mode=mode)

    # --- read some important exposure parameters:
    # --- if dpro is provided that is easy
    if dpro is not None:

        if instrument is None:
            instrument = dpro["instrument"][expid]

        if insmode is None:
            insmode = dpro["tempname"][expid].split("_")[0]

        targname = dpro["targname"][expid]
        datatype = dpro["datatype"][expid]
        tempname = dpro["tempname"][expid]
        setup = dpro["setup"][expid]
        pfov = dpro["pfov"][expid]
        chopthrow = dpro["chopthrow"][expid]
        chopangle = dpro["chopangle"][expid]
        noddir = dpro["noddir"][expid]
    else:
        targname = None
        datatype = None
        tempname = None
        setup = None
        pfov = None
        chopthrow = None
        noddir = None

    # --- if the table with the raw files is provided get them from there
    if expid is not None and draw is not None:
        rid = np.where(draw['EXPID'] == expid)[0]
        nf = len(rid)
        filenames = draw['filename'][rid]

        # --- abort reduction if we have any corrput files
        if 'CANNOT' in draw['INSTRUME'][rid]:
            msg = funname + ": ERROR: This observations contains corrupt files! Aborting..."
            _print_log_info(msg, logfile)

            return (now, noe + 1)

        # # --- in addition we can get some
        # if tempname is None:
        #     tempname = draw['TPL_ID'][rid[0]]
        # if insmode is None:
        #     insmode = draw['insmode'][rid[0]
        # #fram_format = draw['FRAM_FORMAT'][rid[0]]

    # --- or in case a sof file in esorex style is provided
    elif sof is not None:
        fsof = open(sof)
        # --- implement extraction of the fits files here
        # rawfiles =

    # --- if the raw files provided
    if rawfiles is not None:
        nf = len(rawfiles)
        rid = np.arange(0, nf)
        filenames = rawfiles.split('/')[-1]

        if rawfolder is None:
            rawfolder = rawfiles.split('/')[0:-1]

    # --- check that we actually have some files
    if nf == 0:
        msg = funname + ": ERROR: No corresponding raw files found! Aborting..."
        _print_log_info(msg, logfile)

        return (now, noe + 1)

    # --- TO BE IMPLEMENTED: check if blindsums are available or whether this needs to be done
    # ....

    # --- get the first head
    f0 = bsumfolder + '/' + filenames[0].replace(".fits", "_blindsum.fits")
    print(f0)
    hdu = fits.open(f0)
    head0 = hdu[0].header
    hdu.close()

    # --- in case we still don't have them, get some key parameters
    if targname is None:
        targname = _fits_get_info(head0, "target")

    if instrument is None:
        instrument = _fits_get_info(head0, "instrument")

    if insmode is None:
        insmode = _fits_get_info(head0, "insmode")

    if tempname is None:
        tempname = _fits_get_info(head0, "TPL ID")

    if datatype is None:
        datatype = _fits_get_info(head0, "datatype")

    if pfov is None:
        pfov = _fits_get_info(head0, "pfov")

    if setup is None:
        setup = _fits_get_info(head0, "setup")

    if chopthrow is None:
        chopthrow = _fits_get_info(head0, "CHOP THROW")

    if chopangle is None:
        chopangle = _fits_get_info(head0, "CHOP ANGLE")

    if noddir is None:
        noddir = _fits_get_info(head0, "CHOPNOD DIR")

    # --- compute the maximum field of view from half of
    if not box:
        box = int(np.floor(chopthrow / pfov))

    # --- check if target is a calibrator
    if insmode != "SPC":

        if "cal" in tempname:
            should_be_cal = True
            silent = False
        else:
            should_be_cal = False
            silent = True

        # --- first just check if the target is found in the calibrator table
        #     even if it was observed with a science template
        try:
            flux = _get_std_flux(head0,
                                 logfile=logfile,
                                 instrument=instrument,
                                 silent=silent)
        except:
            flux = -1

        # --- if a flux was found update the table and header
        if flux != -1:
            head0["HIERARCH ESO QC JYVAL"] = (
                flux, "STD flux density in filter used [Jy]")
            if dpro is not None:
                dpro['STDflux_Jy'][expid] = flux

        # --- if it was not found but the observation was with a cal template
        #     raise en error
        elif should_be_cal:
            msg = (
                funname +
                ": WARNING: target not found in flux reference table but should be calibrator"
            )

            _print_log_info(msg, logfile)

            now += 1

            flux = -1
    else:
        flux = -1

    msg = " - Target name: " + str(targname) + "\n"

    if flux > 0:
        msg += " - STD flux [Jy]: " + "{:.2f}".format(flux) + "\n"

    msg += (" - Instrument: " + str(instrument) + "\n" +
            " - Instrument mode: " + str(insmode) + "\n" +
            " - Template name: " + str(tempname) + "\n" + " - PFOV [as]: " +
            str(pfov) + "\n" + " - Setup: " + str(setup) + "\n" +
            " - Data type: " + str(datatype) + "\n" + " - Nod direction: " +
            str(noddir) + "\n" + " - Chop throw [as]: " + str(chopthrow) +
            "\n" + " - Chop angle [de]: " + str(chopangle) + "\n" +
            " - Max box size [px]: " + str(box))

    _print_log_info(msg, logfile, logtime=False)

    # === 1 Go over all files of the exposure and create nodding pairs
    msg = funname + ": Creating blind nodding pairs..."
    _print_log_info(msg, logfile, empty=1)

    nodims = []
    heads = []
    outnames = []

    ima = None
    imb = None
    heada = None
    # headb = None

    for i in range(nf):
        msg = (" - i, File: " + str(i) + ", " + filenames[i])
        _print_log_info(msg, logfile)

        fsingred = (bsumfolder + '/' +
                    filenames[i].replace(".fits", "_blindsum.fits"))

        # --- read in the data
        hdu = fits.open(fsingred)
        head = hdu[0].header
        im = hdu[0].data
        hdu.close()

        # --- nod pos A or B?
        if draw['NODPOS'][rid[i]] == 'A':
            ima = im
            heada = head
        else:
            imb = im

        # print(draw['NODPOS'][rid[i]], ima is not None, imb is not None)

        # === 1.1 combine files to nodding pairs
        if (i > 0) & (ima is not None) & (imb is not None):

            # --- build the nod subtracted image
            imc = ima - imb

            ind = int(np.floor(i / 2))

            fout = (nodfolder + '/' + outname + '_nod' +
                    "{:02.0f}".format(ind) + '.fits')

            if overwrite or not os.path.isfile(fout):
                fits.writeto(fout, imc, heada, overwrite=True)
                pout = fout.replace(".fits", "_per1.png")
                _simple_image_plot(imc, pout, percentile=1, pwidth=6)
                pout = fout.replace(".fits", "_minmax.png")
                _simple_image_plot(imc, pout, percentile=0.0001, pwidth=6)

                msg = (" - Output written: " + fout)
                _print_log_info(msg, logfile)

            # --- collect for further reduction
            nodims.append(imc)
            outnames.append(outname)
            heads.append(heada)

            ima = None
            imb = None
            heada = None

    msg = funname + ": Number of nodding pairs reduced: " + str(len(nodims))
    _print_log_info(msg, logfile)

    # === 2 Combination of the nodding pairs
    #    if len(heads) == 0:   # --- no idea why this was here???
    #        continue
    msg = funname + ": Merging blind nodding pairs..."
    _print_log_info(msg, logfile, empty=1)

    nodims = np.array(nodims)
    nnods = len(nodims)

    # --- check that we have at least one valid nodding pair
    if nnods == 0:
        msg = (funname +
               ": ERROR: No nodding pairs could be created! Aborting...")

        _print_log_info(msg, logfile)

        return (now, noe + 1)

    # --- 2.1 do jitter correction
    if 'HIERARCH ESO SEQ JITTER WIDTH' in head0:
        if head0['HIERARCH ESO SEQ JITTER WIDTH'] > 0:
            for i in range(nnods):
                # joff = V.compute_jitter(head=heads[iddd[m]])
                # print("      - Nod no / jitter offs.: ",m,joff)
                nodims[i] = _undo_jitter(im=nodims[i], head=heads[i])

    # msg = funname + ": 1. simple combination"
    # _print_log_info(msg, logfile)

    totim = np.nanmean(nodims, axis=0)
    fout = (outfolder + '/' + outnames[0] + '_all_fullframe.fits')

    # --- if no specfic range is supplied to measure the sky use the full
    #     illuminated area of the VISIR detector
    if sky_xrange is None:
        sky_xrange = _vp.max_illum_xrange

    if sky_yrange is None:
        sky_yrange = _vp.max_illum_yrange

    # --- background estimation:
    # bgim = _subtract_source(totim[sky_yrange[0]:sky_yrange[1],
    #                                sky_xrange[0]:sky_xrange[1]])

    bgim = sigma_clip(totim[sky_yrange[0]:sky_yrange[1],
                            sky_xrange[0]:sky_xrange[1]],
                      sigma=3,
                      maxiters=3,
                      masked=False)

    dpro["BGmed"][expid] = np.nanmedian(bgim)

    dpro["BGstd"][expid] = np.nanstd(bgim)

    msg = (" - BG median [ADU/DIT]: " + str(dpro["BGmed"][expid]) + "\n" +
           " - BG STDDEV [ADU/DIT]: " + str(dpro["BGstd"][expid]))
    _print_log_info(msg, logfile, logtime=False)

    if overwrite or not os.path.isfile(fout):
        fits.writeto(fout, totim, head0, overwrite=True)
        pout = fout.replace(".fits", "_per1.png")
        _simple_image_plot(totim, pout, percentile=1, pwidth=6)
        pout = fout.replace(".fits", "_minmax.png")
        _simple_image_plot(totim, pout, percentile=0.0001, pwidth=6)

        msg = (" - Output written: " + fout)
        _print_log_info(msg, logfile)

    # === 3 BURST & CYCSUM data ===
    if datatype == "halfcyc" or datatype == "burst":

        msg = (funname + ": Burst/halfcyc data encountered...")
        _print_log_info(msg, logfile, empty=1)

        burstfolder = hcycfolder + '/' + alignmethod

        if not os.path.exists(burstfolder):
            os.makedirs(burstfolder)

        try:
            noe = reduce_burst_exp(logfile, filenames, rawfolder, burstfolder,
                                   ditsoftaver, overwrite, noddir, bsumfolder,
                                   draw, rid, noe, box, AA_pos, alignmethod,
                                   chopsubmeth, refpos, verbose, searchsmooth,
                                   debug, plot, crossrefim, outfolder,
                                   outnames, head0, dpro, pfov, expid)
        except:
            e = sys.exc_info()
            msg = (funname + ": ERROR: Burst reduction failed!")

            noe = noe + 1

            _print_log_info(msg, logfile)

            if dpro is not None:
                dpro['noerr'][expid] = dpro['noerr'][expid] + 1

        onlyhead = True

    # === 4 Automatic source extraction for imaging
    bfound = "N/A"
    if extract and insmode != 'SPC':

        # ---- WARNING: de-rotation for classical imaging with pupil tracking still to be implemented!

        # --- first find the source positions
        if findbeams:

            msg = funname + ": Trying to detect beams in combined image..."
            _print_log_info(msg, logfile, empty=2)

            try:
                bfound, nowarn, beampos, fitparams = _find_beam_pos(
                    im=totim,
                    head=head0,
                    searcharea=searcharea,
                    fitbox=0.5 * box,
                    nodpos='both',
                    verbose=verbose,
                    sourceext=sourceext,
                    AA_pos=AA_pos,
                    plot=plot,
                    tol=maxshift / pfov,
                    instrument=instrument,
                    insmode=insmode,
                    logfile=logfile,
                    chopthrow=chopthrow,
                    noddir=noddir,
                    filt=setup,
                    pfov=pfov,
                    searchsmooth=searchsmooth,
                    sigmaclip=sigmaclip)

                now += nowarn

            except:
                e = sys.exc_info()
                msg = (funname +
                       ": WARNING: Beam Position could not be found: \n" +
                       str(e[1]) + ' ' + str(traceback.print_tb(e[2])) +
                       "\nContinue assuming the positions...")
                _print_log_info(msg, logfile)

                bfound = "fail"

            msg = funname + ": Result of beam search: " + bfound
            _print_log_info(msg, logfile)

            if bfound != "fail":

                # --- retrieve the coordinates of the found position
                wcs = WCS(head0)
                bra, bdec = wcs.wcs_pix2world(beampos[0, 1], beampos[0, 0], 0)

                bcoord = SkyCoord(ra=bra,
                                  dec=bdec,
                                  unit=(u.deg, u.deg),
                                  frame='icrs')

                tara = _fits_get_info(head0, keys="RA")
                tadec = _fits_get_info(head0, keys="DEC")

                angdist = _angular_distance(bra, bdec, tara, tadec)

                nbeams = len(beampos)
                bg = fitparams[0]
                amp = fitparams[1]
                fwhm = np.mean(fitparams[4:6])
                axrat = np.max(fitparams[4:6]) / np.min(fitparams[4:6])
                total = 0.25 * (np.pi * fitparams[1] * fitparams[4] *
                                fitparams[5]) / np.log(2.0)
                angle = fitparams[6]
                msg = (funname + ":\n" + " - Expected source position: " +
                       dpro["RA_hms"][expid] + " " + dpro["DEC_dms"][expid] +
                       "\n" + " - Found source params:\n" +
                       "     - Position x,y [px]: " +
                       '{:.1f}'.format(beampos[0, 1]) + "," +
                       '{:.1f}'.format(beampos[0, 0]) + "\n" +
                       "     - Position [wcs]: " + bcoord.to_string('hmsdms') +
                       "\n" + "     - Angular distance [as]: " +
                       '{:.2f}'.format(angdist) + "\n" + "     - BG: " +
                       str(bg) + "\n" + "     - Amplitude: " + str(amp) +
                       "\n" + "     - Total: " + str(total) + "\n" +
                       "     - Aver. FWHM [px]: " + str(fwhm) + "\n" +
                       "     - Aver. FWHM (min 0.3) [as]: " +
                       str(fwhm * pfov) + "\n" + "     - Maj./min axis: " +
                       str(axrat) + "\n" + "     - Angle: " + str(angle))

                _print_log_info(msg, logfile)

            else:
                now += 1

        else:
            bfound = "not tried"

        # === If bright enough, try Fine-centered addition
        # --- go over all nodding pairs and extract all the sub-images
        if bfound == "first attempt" or bfound == "global fit":

            msg = funname + ": Trying to detect and extract beams from individual nods..."
            _print_log_info(msg, logfile, empty=2)

            suffix = 'fine'

            es = extract_beams(nodims,
                               beampos,
                               box,
                               noddir,
                               head0,
                               logfile,
                               verbose,
                               debug,
                               outfolder,
                               outnames[0],
                               suffix,
                               nodalign=True,
                               updatedb=True,
                               dpro=dpro,
                               pfov=pfov,
                               fitparams=fitparams,
                               maxshift=maxshift,
                               sigmaclip=sigmaclip,
                               expid=expid,
                               onlyhead=onlyhead)

        else:
            es = 1

        # ================= Fixed extraction, if necessary

        # --- if a beam was detected do an extraction
        if findbeams and bfound != "fail" and es > 0:

            # --- first do some tests on fitted beams
            s = np.shape(nodims[0])
            use = np.full(nbeams, True)

            for k in range(nbeams):

                # --- first test that at least one of the beams is on the detector
                if ((beampos[k, 0] >= s[0]) | (beampos[k, 1] >= s[1])
                        | (beampos[k, 0] <= 0) | (beampos[k, 1] <= 0)):

                    use[k] = False

            # --- make sure that at least one beam is on the detector
            if np.sum(use) == 0:

                msg = (
                    " - WARNING: None of fitted beams on detector! Using reference position..."
                )

                _print_log_info(msg, logfile)

                now += 1

                bfound = "fail"

            else:

                msg = (
                    funname +
                    ": Extracting beams from individual nods at found fixed position ..."
                )

                _print_log_info(msg, logfile, empty=1)

            # --- label fits file depending on beam recovery mode/success:
            if bfound == "first attempt" or bfound == "global fit" or bfound == "global smooth":
                suffix = 'fixadet'
            elif bfound == "only one found":
                suffix = 'fix1det'

            es = extract_beams(nodims,
                               beampos,
                               box,
                               noddir,
                               head0,
                               logfile,
                               verbose,
                               debug,
                               outfolder,
                               outnames[0],
                               suffix,
                               updatedb=True,
                               dpro=dpro,
                               pfov=pfov,
                               sigmaclip=sigmaclip,
                               expid=expid,
                               onlyhead=onlyhead)

            noe += es

        # --- if none or only 1 beam was, or we did not search, estimate their
        #     position
        if not findbeams or bfound == "fail" or bfound == "only one found":

            # --- compute the expected beam positions
            msg = (funname +
                   ": Extraction using reference positions for the beams...")

            _print_log_info(msg, logfile)

            beampos = _calc_beampos(head=head0, verbose=verbose)
            nbeams = len(beampos)
            # fwhm = 7
            # bg = np.nanmedian(totim)
            # amp = np.nanmax(gaussian_filter(totim, sigma=3))

            # --- Modify the calculated positions in case the user provided the beam
            #     position of chop A nod A
            if AA_pos is not None:
                xdif = AA_pos[1] - beampos[0, 1]
                ydif = AA_pos[0] - beampos[0, 0]

                beampos[:, 1] = beampos[:, 1] + xdif
                beampos[:, 0] = beampos[:, 0] + ydif

            suffix = 'fixest'

            es = extract_beams(nodims,
                               beampos,
                               box,
                               noddir,
                               head0,
                               logfile,
                               verbose,
                               debug,
                               outfolder,
                               outnames[0],
                               suffix,
                               updatedb=False,
                               dpro=dpro,
                               pfov=pfov,
                               sigmaclip=sigmaclip,
                               expid=expid,
                               onlyhead=onlyhead)

            noe += es

    if bfound == "first attempt" or bfound == "global fit" or bfound == "global smooth":
        dpro['all_beams_det'][expid] = "T"
    else:
        dpro['all_beams_det'][expid] = "F"

    msg = (funname + ": Number of warnings for exposure: " + str(now))
    _print_log_info(msg, logfile, empty=1, screen=False)

    msg = (funname + ": Number of errors for exposure: " + str(noe))
    _print_log_info(msg, logfile, screen=False)

    return (now, noe)
Exemplo n.º 3
0
def group_raws_to_obs(ftabraw, ftablog, ftabpro, maxgap=5,
                                logfile=None, overwrite=False):
    """
    Look into the raw file table and identify individual observations/exposures
    and print out a new table containing the information of the latter
    PARAMETERS:
     - maxgap: (default 5) provide the maximum time gap between two files in
               min
    """

    funname = "GROUP_RAWS_TO_OBS"

    if logfile is None:
        logfile = ftabraw.replace("_raw.csv", ".log")

    if not os.path.isfile(logfile):
        mode = 'w'
    else:
        mode = 'a'

    _print_log_info('\n\nNew execution of ' + funname + '\n',
                   logfile, mode=mode)

    # --- column names for the product table and their data format
    cols = OrderedDict({
                  "expid": "{0:d}",
                  "targname": '{:s}',
                  "RA_deg": '{:.6f}',
                  "DEC_deg": '{:.5f}',
                  "RA_hms": '{:s}',
                  "DEC_dms": '{:s}',
                  "progid": '{:s}',
                  "obsid": "{0:d}",
                  "dateobs": '{:s}',
                  "mjd": '{:.8f}',
                  "tempno": "{0:d}",
                  "instrument": '{:s}',
                  "insmode": '{:s}',
                  "tempname": '{:s}',
                  "datatype": '{:s}',
                  "setup": '{:s}',
                  "pfov": '{:.4f}',
                  "dit": '{:.4f}',
                  "nodtime": '{:.0f}',
                  "noddir": '{:s}',
                  "jitter": '{:.1f}',
                  "chopthrow": '{:.1f}',
                  "chopangle": '{:.1f}',
                  "chopfreq": '{:.2f}',
                  "expnof": "{0:d}",
                  "nof": "{0:d}",
                  "exptime": '{:.0f}',
                  "grade": '{:s}',
                  "posang": '{:.1f}',
                  "absrot_mean": '{:.2f}',
                  "absrot_start": '{:.2f}',
                  "absrot_end": '{:.2f}',
                  "parang_mean": '{:.2f}',
                  "parang_start": '{:.2f}',
                  "parang_end": '{:.2f}',
                  "alt_mean": '{:.2f}',
                  "alt_start": '{:.2f}',
                  "alt_end": '{:.2f}',
                  "az_mean": '{:.2f}',
                  "az_start": '{:.2f}',
                  "az_end": '{:.2f}',
                  "airm_mean": '{:.3f}',
                  "airm_start": '{:.3f}',
                  "airm_end": '{:.3f}',
                  "pwv_median": '{:.2f}',
                  "pwv_stddev": '{:.2f}',
                  "skytemp_median": '{:.0f}',
                  "skytemp_stddev": '{:.0f}',
                  "pressure_median": '{:.2f}',
                  "pressure_stddev": '{:.2f}',
                  "humidity_median": '{:.1f}',
                  "humidity_stddev": '{:.1f}',
                  "temperature_median": '{:.2f}',
                  "temperature_stddev": '{:.2f}',
                  "m1temp_median": '{:.2f}',
                  "m1temp_stddev": '{:.2f}',
                  "winddir_median": '{:.0f}',
                  "winddir_stddev": '{:.1f}',
                  "windspeed_median": '{:.2f}',
                  "windspeed_stddev": '{:.2f}',
                  "cohertime_median": '{:.4f}',
                  "cohertime_stddev": '{:.4f}',
                  "asmfwhm_median": '{:.2f}',
                  "asmfwhm_stddev": '{:.2f}',
                  "iafwhm_median": '{:.2f}',
                  "iafwhm_stddev": '{:.2f}',
                  "iafwhmlo_median": '{:.2f}',
                  "iafwhmlo_stddev": '{:.2f}',
                  "chopamed_median": '{:.0f}',
                  "chopamed_stddev": '{:.2f}',
                  "chopastd_median": '{:.1f}',
                  "chopastd_stddev": '{:.2f}',
                  "chopbmed_median": '{:.0f}',
                  "chopbmed_stddev": '{:1f}',
                  "chopbstd_median": '{:.1f}',
                  "chopbstd_stddev": '{:.2f}',
                  "cdifmed_median": '{:.2f}',
                  "cdifmed_stddev": '{:.2f}',
                  "cdifstd_median": '{:.2f}',
                  "cdifstd_stddev": '{:.2f}'
                  })


    # --- read the raw file
    draw = ascii.read(ftabraw, header_start=0, delimiter=',',
                              guess=False)


    # --- make all columns to MaskedColumns
    draw = Table(draw, masked=True, copy=False)  # Convert to masked table

    # --- throw out entries with ERRORS
    ids = [j for j, s in enumerate(draw['INSTRUME']) if 'CANNOT' not in s]
    nbad = len(draw) - len(ids)

    if nbad > 0:
        msg = (funname + ": WARNING: Number of raw files with errors: "
               + str(nbad)
               + " --> Excluding them...")

        _print_log_info(msg, logfile)

    draw = draw[ids]

    # --- data table integrity check
    dupl = _duplicates_in_column(table=draw, colname='MJD-OBS')
    if dupl is not None:
        msg = (funname + ": ERROR: Duplicated files in raw table! MJDs: "
               + ', '.join([str(e) for e in dupl])
               + '. Exiting...')

        _print_log_info(msg, logfile)
        raise TypeError(msg)

    dg = ascii.read(ftablog, header_start=0, delimiter=',',
                              guess=False)

    # --- make sure that the raw files are sorted by date time (some might
    #     have been added later)
    id = np.argsort(draw['MJD-OBS'])
    draw = draw[id]

    # --- throw out incomplete entries (e.g., non-nodded through slit images)
    id = np.where(np.array(draw['nodexptime'], dtype=float) > 0)[0]
    draw = draw[id]

    n = len(draw)

    # --- group the raw files into exposures
    expid = np.zeros(n, dtype=int)
    counter = 0
    nof = 1

    for i in np.arange(1, n):
        if (draw['OBS_ID'][i-1] != draw['OBS_ID'][i]):
            counter = counter + 1
        elif (draw['TPLNO'][i-1] != draw['TPLNO'][i]):
            counter = counter + 1
        elif (draw['TPL_EXPNO'][i-1] >= draw['TPL_EXPNO'][i]):
            counter = counter + 1
        elif (draw['TPL_EXPNO'][i-1] == draw['TPL_NEXP'][i-1]):
            counter = counter + 1
        elif (draw['MJD-OBS'][i] - draw['MJD-OBS'][i-1] > maxgap/60.0/24.0):
            counter = counter + 1

        expid[i] = counter

    # --- get the start MJDs of each exposure as unique identifier
    nexp = counter + 1
    mjds = np.zeros(nexp)

    for i in range(nexp):

        id = np.where(expid == i)[0]
        mjds[i] = draw['MJD-OBS'][id[0]]


    # --- make and backup copy just in case
    if os.path.isfile(ftabpro):
        shutil.copy(ftabpro, ftabpro.replace(".csv", "_backup.csv"))

    # --- in case the file exists update and append information to dpro file
    if os.path.isfile(ftabpro) and not overwrite:

        new = False
        dpro = ascii.read(ftabpro)

        # --- change all string columns to object type so that their string
        #     length becomes variable
        for col in dpro.itercols():
            if col.dtype.kind in 'SU':
                dpro.replace_column(col.name, col.astype('object'))

        # --- make all columns to MaskedColumns
        dpro = Table(dpro, masked=True, copy=False)  # Convert to masked table

    # --- if the file not exists generate the empty table structure
    else:
      new = True

      # --- generate the output table
      dpro = Table()
      for c in cols.keys():
          if 'd' in cols[c]:
              dt = int
          elif 's' in cols[c]:
              dt = object
          elif 'f' in cols[c]:
              dt = float
          dpro.add_column(MaskedColumn(np.ma.masked_all(nexp),
                                       name=c, dtype=dt))


    # --- loop over all the exposures by MJD:
    # for i in tqdm(range(nexp)):
    for i in range(nexp):

        if not new:
            # --- check if already an entry in the table for that exposure
            id = np.where(np.isclose(dpro["mjd"], mjds[i], atol=1e-7,
                                     rtol=0))[0]

            # --- otherwise add a row at the end of the table
            if len(id) == 0:
                dpro.add_row()
                id = len(dpro)-1

                # --- make sure that the columns have fill values of the right type
                for c in dpro.colnames:
                    if dpro[c].dtype == "object":
                        dpro[c][id] = ""

            else:
                id = id[0]



        # --- for new table just take the i-th row
        else:
            id = i

        # --- now gather all files belonging to that exposure
        ids = np.where(expid == i)[0]

        j = ids[0]


        #print(i, mjds[i], id, dpro["mjd"][id], j)

        # print(i,id,j)

        nof = len(ids)
        dpro["nof"][id] = nof

        dpro["expid"][id] = i

        if nof == 1 and "MoveToSlit" not in draw['TPL_ID'][j]:
            msg = ("WARNING: Only one raw file found for exposure. "
                   + "Exp ID: " + str(i)
                   + "; DATE-OBS: " + draw['DATE-OBS'][j]
                   + "; TPL_ID: " + draw['TPL_ID'][j]

                   )

            _print_log_info(msg, logfile)



        # print(i,j, draw['CYCSUM'][j])
        if "Burst" in draw['TPL_ID'][j]:
            dpro["datatype"][id] = 'burst'
        elif (draw['CYCSUM'][j] == 'False') | (draw['CYCSUM'][j] == 'F'):
            dpro["datatype"][id] = 'halfcyc'
        else:
            dpro["datatype"][id] = 'cycsum'


        dpro["dateobs"][id] = draw['DATE-OBS'][j][0:19]

        # --- fetch the grade and add it
        idg = [g for g, s in enumerate(dg['filename'])
               if dpro["dateobs"][id] in s]
        if idg:
            dpro["grade"][id] = dg['grade'][idg[0]]
        else:
            dpro["grade"][id] = "N"

        # --- fill in the other values <--- would be better to get that from
        #     fits_get_info in the future! For now hard-coded FILT3 for ISAAC
        if draw['INSTRUME'][j] == "VISIR":
            if draw['insmode'][j] == 'IMG':
                dpro["setup"][id] = str(draw['INS_FILT1_NAME'][j])
            elif draw['insmode'][j] == 'ACQ-SPC-SPC':
                dpro["setup"][id] = str(draw['INS_FILT2_NAME'][j])
            elif draw['insmode'][j] == 'ACQ-IMG-SPC':
                dpro["setup"][id] = str(draw['INS_FILT1_NAME'][j])
            elif draw['insmode'][j] == 'SPC':
                dpro["setup"][id] = str(draw['INS_SLIT1_WID'][j])
            else:
                print(draw['insmode'][j])
                raise ValueError()
        elif draw['INSTRUME'][j] == "ISAAC":
            if draw['insmode'][j] == 'IMG':
                if draw['INS_FILT3_NAME'][j] != "open":
                    dpro["setup"][id] = str(draw['INS_FILT3_NAME'][j])
                else:
                    dpro["setup"][id] = str(draw['INS_FILT4_NAME'][j])
            elif draw['insmode'][j] == 'SPC':
                dpro["setup"][id] = str(draw['INS_FILT3_NAME'][j])

        # --- if pupil tracking was used add that info to the mode
        if draw['ALTAZTRACK'][j] == 'True':
            dpro["insmode"][id] = str(draw['insmode'][j]) + "-PT"
        else:
            dpro["insmode"][id] = str(draw['insmode'][j])

        try:
            dpro["exptime"][id] = float(draw['nodexptime'][j])*nof
        except:
            dpro["exptime"][id] = np.ma.masked

        dpro["targname"][id] = draw['TARG_NAME'][j]
        dpro["RA_deg"][id] = draw['RA'][j]
        dpro["DEC_deg"][id] = draw['DEC'][j]

        coord = SkyCoord(ra=draw['RA'][j], dec=draw['DEC'][j], unit=(u.deg, u.deg), frame='icrs')

        dpro["RA_hms"][id] = coord.to_string('hmsdms').split()[0]
        dpro["DEC_dms"][id] = coord.to_string('hmsdms').split()[1]
        dpro["progid"][id] = draw['PROG_ID'][j]
        dpro["obsid"][id] = draw['OBS_ID'][j]
        dpro["mjd"][id] = mjds[i]
        dpro["instrument"][id] = draw['INSTRUME'][j]
        dpro["tempno"][id] = draw['TPLNO'][j]
        dpro["tempname"][id] = draw['TPL_ID'][j]
        dpro["expnof"][id] = draw['TPL_NEXP'][j]

        dpro["pfov"][id] = draw['PFOV'][j]

        if not draw['SEQ1_DIT'].mask[j]:
            dpro["dit"][id] = draw['SEQ1_DIT'][j]
        # else:
        #     dpro["dit"][id] = -1

        # print(draw['CHOPNOD_DIR'][j], type(draw['CHOPNOD_DIR'][j]))

        if not draw['CHOPNOD_DIR'].mask[j]:
            dpro["noddir"][id] = draw['CHOPNOD_DIR'][j]
        else:
            dpro["noddir"][id] = "None?"

        if not draw['JITTER_WIDTH'].mask[j]:
            dpro["jitter"][id] = draw['JITTER_WIDTH'][j]
        else:
            dpro["jitter"][id] = 0

        if not draw['CHOP_THROW'].mask[j]:
            dpro["chopthrow"][id] = draw['CHOP_THROW'][j]
        # else:
        #     dpro["chopthrow"][id] = -1

        if not draw['CHOP_POSANG'].mask[j]:
            dpro["chopangle"][id] = draw['CHOP_POSANG'][j]
        # else:
        #     dpro["chopthrow"][id] = -1

        if not draw['CHOP_FREQ'].mask[j]:
            dpro["chopfreq"][id] = draw['CHOP_FREQ'][j]
        # else:
        #     dpro["chopfreq"][id] = -1

        # --- actual time between two nods
        # print(i, id,j, nof)
        if nof > 1:
            dpro["nodtime"][id] = (draw['MJD-OBS'][j+nof-1]
                                   - draw['MJD-OBS'][j]) / (nof -1) * 24 * 3600

        # --- now the changing telescope parameters
        if not any(draw['POSANG'].mask[j:j+nof]):
            dpro["posang"][id] = np.ma.mean(draw['POSANG'][j:j+nof])

        fill_values(draw, dpro, 'ABSROT', 'absrot', id, j, nof,
                    ktype='tel', deg=True)
        fill_values(draw, dpro, 'PARANG', 'parang', id, j, nof,
                    ktype='tel')
        fill_values(draw, dpro, 'TEL_ALT', 'alt', id, j, nof, ktype='tel')
        fill_values(draw, dpro, 'TEL_AZ', 'az', id, j, nof, ktype='tel', deg=True)
        fill_values(draw, dpro, 'TEL_AIRM', 'airm', id, j, nof, ktype='tel')

        # --- now the ambient parameters
        fill_values(draw, dpro, 'IWV', 'pwv', id, j, nof,
                    ktype='ambi', lowlim=0, uplim=10)
        fill_values(draw, dpro, 'IRSKY_TEMP', 'skytemp', id, j, nof,
                    ktype='ambi', lowlim=-150, uplim=-35)
        fill_values(draw, dpro, 'AMBI_PRES', 'pressure', id, j, nof,
                    ktype='ambi')
        fill_values(draw, dpro, 'RHUM', 'humidity', id, j, nof,
                    ktype='ambi')
        fill_values(draw, dpro, 'AMBI_TEMP', 'temperature', id, j, nof,
                    ktype='ambi')
        fill_values(draw, dpro, 'M1_TEMP', 'm1temp', id, j, nof,
                    ktype='ambi')
        fill_values(draw, dpro, 'WINDDIR', 'winddir', id, j, nof,
                    ktype='ambi', deg=True)
        fill_values(draw, dpro, 'WINDSP', 'windspeed', id, j, nof,
                    ktype='ambi')
        fill_values(draw, dpro, 'TAU0', 'cohertime', id, j, nof,
                    ktype='ambi')
        fill_values(draw, dpro, 'TEL_AMBI_FWHM', 'asmfwhm', id, j, nof,
                    ktype='ambi', lowlim=0.1, uplim=3)
        fill_values(draw, dpro, 'IA_FWHM', 'iafwhm', id, j, nof, ktype='ambi',
                    lowlim=0.1, uplim=3)
        fill_values(draw, dpro, 'IA_FWHMLINOBS', 'iafwhmlo', id, j, nof,
                    lowlim=0.1, uplim=3, ktype='ambi')

        # --- now the background parameters
        fill_values(draw, dpro, 'chopamed', 'chopamed', id, j, nof, ktype='bg')
        fill_values(draw, dpro, 'chopastd', 'chopastd', id, j, nof, ktype='bg')
        fill_values(draw, dpro, 'chopbmed', 'chopbmed', id, j, nof, ktype='bg')
        fill_values(draw, dpro, 'chopbstd', 'chopbstd', id, j, nof, ktype='bg')
        fill_values(draw, dpro, 'cdifmed', 'cdifmed', id, j, nof, ktype='bg')
        fill_values(draw, dpro, 'cdifstd', 'cdifstd', id, j, nof, ktype='bg')

        # --- check is the PWV STD was larger during a file than in between
        if not any(draw['IWVSTD'].mask[j:j+nof]):
            iwvstd = np.ma.mean(draw['IWVSTD'][j:j+nof])

            if dpro['pwv_stddev'][id] is not None:
                if (iwvstd > dpro['pwv_stddev'][id]) & (iwvstd > 0):
                    dpro['pwv_stddev'][id] = iwvstd

            else:
                dpro['pwv_stddev'][id] = iwvstd


        # --- clean some bad values:
        if dpro['exptime'][id] == 0:
            dpro['exptime'][id] = None



    # --- write the finished table sorted by MJD
    dpro.sort('mjd')


    # --- write out results


    # print(len(dpro), ftabpro)


    # masked = check_masked(dpro)

    # if len(masked) > 0:
    #     msg = (funname + ": WARNING: DPRO contains masked values: " + str(masked))

    #     if logfile is not None:
    #         _print_log_info(msg, logfile)

    #     # raise ValueError(msg)


    # nones = check_none(dpro)

    # if len(nones) > 0:
    #     msg = (funname + ": WARNING: DPRO contains None's: " + str(nones))

    #     if logfile is not None:
    #         _print_log_info(msg, logfile)

    #     # raise ValueError(msg)

    # # return(dpro)

    dpro.write(ftabpro, delimiter=',', format='ascii',
               fill_values=[(ascii.masked, '')], formats=cols,
               overwrite=True)


    # --- update the draw table with the expid column
    if 'EXPID' in draw.colnames:
        draw['EXPID'] = expid
    else:
        newCol = Column(expid, name='EXPID')
        draw.add_column(newCol)


    draw.write(ftabraw, delimiter=',', format='ascii',
               fill_values=[(ascii.masked, '')], overwrite=True)

    msg = (" - Number of separate exposures found: " + str(counter+1))
    _print_log_info(msg, logfile)

    return(dpro)
Exemplo n.º 4
0
def extract_beams(nodims,
                  beampos,
                  box,
                  noddir,
                  head0,
                  logfile,
                  verbose,
                  debug,
                  outfolder,
                  outbasename,
                  suffix,
                  nodalign=False,
                  updatedb=False,
                  dpro=None,
                  fitparams=None,
                  pfov=None,
                  expid=None,
                  onlyhead=False,
                  maxshift=None,
                  sigmaclip=None):

    # === 4.1 Blind addition
    # --- go over all nodding pairs and extract all the sub-images
    #     first blindly
    funname = "EXTRACT_BEAMS"
    # nowarn = 0
    noerr = 0

    subims = []
    # titles = []
    nbeams = len(beampos[:, 0])
    nnods = len(nodims)

    beamposstr = ""
    for b in range(nbeams):
        beamposstr = (beamposstr + "{:3.0f}".format(beampos[b, 0]) + ", " +
                      "{:3.0f}".format(beampos[b, 1]) + " | ")

    msg = (" - No of beams: " + str(nbeams) + "\n" + " - Beampos: " +
           beamposstr + "\n")

    _print_log_info(msg, logfile)

    # --- exclude beams that are not on the detector
    s = np.shape(nodims[0])
    use = np.full(nbeams, True)
    beamsign = np.zeros(nbeams)

    for k in range(nbeams):

        beamsign[k] = (-1)**(np.ceil(0.5 * k))

        if ((beampos[k, 0] >= s[0]) | (beampos[k, 1] >= s[1])
                | (beampos[k, 0] <= 0) | (beampos[k, 1] <= 0)):

            msg = (" - Beampos not on frame: " + str(beampos[k, 0]) + ', ' +
                   str(beampos[k, 1]) + ". Exclude...")

            _print_log_info(msg, logfile)

            use[k] = False

    beampos = beampos[use, :]
    beamsign = beamsign[use]
    nbeams = len(beampos)

    # --- make sure that at least one beam is on the detector
    if nbeams == 0:

        msg = (" - Error: No beam on detector: " + str(beampos[k, 0]) + ', ' +
               str(beampos[k, 1]) + ". Exiting...")

        _print_log_info(msg, logfile)

        return (noerr + 1)

    # --- for the case of fine-centred extraction
    if nodalign:

        # --- get the expected beam parameters from the fit
        guessbg = fitparams[0]
        guessamp = np.abs(fitparams[1])
        guessFWHM = np.mean(fitparams[4:6])

        minamp = 0.3 * guessamp
        minFWHM = 0.5 * guessFWHM
        maxFWHM = 2 * guessFWHM

        maxshift /= pfov  # maxshift in px

        fitbox = int(np.max([6 * guessFWHM, 0.25 * box]))

        nexp = nnods * nbeams
        nfound = 0

        for j in tqdm(range(nnods)):
            for k in range(nbeams):

                try:
                    params, _, _ = _find_source(nodims[j] * beamsign[k],
                                                guesspos=beampos[k, :],
                                                searchbox=box,
                                                fitbox=fitbox,
                                                method='mpfit',
                                                guessbg=guessbg,
                                                guessamp=guessamp,
                                                guessFWHM=guessFWHM,
                                                minamp=minamp,
                                                minFWHM=minFWHM,
                                                maxFWHM=maxFWHM)

                except:

                    if verbose:
                        msg = (
                            " - WARNING: Could not find source. Continuing...")

                        _print_log_info(msg, logfile)

                    # if dpro is not None:
                    #     dpro['nowarn'][expid] = dpro['nowarn'][expid] + 1

                    continue

#                print('params: ', params)
#                print('params[2:4]', params[2:4])
#                print('beamsign: ', beamsign)
#                print('box: ', box)
#                print('i,j: ',i,j)

                cim = _crop_image(nodims[j] * beamsign[k],
                                  box=box,
                                  cenpos=params[2:4],
                                  silent=True)

                dist = beampos[k, :] - params[2:4]

                if verbose:
                    msg = (" - Nod: " + str(j) + " Beam: " + str(k) +
                           " Detected shift: " + str(dist))

                    _print_log_info(msg, logfile)

                    bg_t = params[0]
                    amp_t = params[1]
                    fwhm_t = np.mean(params[4:6])
                    axrat_t = np.max(params[4:6]) / np.min(params[4:6])
                    total_t = 0.25 * (np.pi * params[1] * params[4] *
                                      params[5]) / np.log(2.0)
                    angle_t = params[6]
                    msg = (" - Found source params:\n" + "     - BG: " +
                           str(bg_t) + "\n" + "     - Amplitude: " +
                           str(amp_t) + "\n" + "     - Total: " +
                           str(total_t) + "\n" + "     - Aver. FWHM [px]: " +
                           str(fwhm_t) + "\n" + "     - Aver. FWHM [as]: " +
                           str(fwhm_t * pfov) + "\n" +
                           "     - Maj./min axis: " + str(axrat_t) + "\n" +
                           "     - Angle: " + str(angle_t))

                    _print_log_info(msg, logfile)

                if np.sqrt(dist[0]**2 + dist[1]**2) > maxshift:

                    if verbose:
                        msg = (" - WARNING: shift too large! Exclude frame")
                        _print_log_info(msg, logfile)
                    # if dpro is not None:
                    #     dpro['nowarn'][expid] = dpro['nowarn'][expid] + 1

                    # noe = noe+1
                    continue

                # --- all double beams have to be halved but added twice
                if (noddir == "PARALLEL") & (beamsign[k] > 0):
                    subims.append(0.5 * cim)
                    subims.append(0.5 * cim)
                else:
                    subims.append(cim)

                nfound += 1
                # titles.append("Nod: " + str(j) + " Beam: " + str(k))

        msg = (funname + ": Number of detected beams/expected: " +
               str(nfound) + "/" + str(nexp))
        _print_log_info(msg, logfile)

        # --- if object not detected in individual nods give warning
        if nfound == 0:
            msg = (
                funname +
                ": No source detected in the individual nods! Source probably too faint..."
            )
            _print_log_info(msg, logfile)

            return (noerr + 1)

        # -- also if <50% of the beams were found abort reject the fine
        #    tuned extraction and proceed with bling
        elif nfound < 0.5 * nexp:
            msg = (
                funname +
                ": <50% of the beams detected in individual nods! Source probably too faint... "
            )
            _print_log_info(msg, logfile)

            return (noerr + 1)

    # --- fixed extraction
    else:

        for j in tqdm(range(nnods)):
            for k in range(nbeams):

                cim = _crop_image(nodims[j] * beamsign[k],
                                  box=box,
                                  cenpos=beampos[k, :],
                                  silent=True)

                # --- all double beams have to be halved but added twice
                if (noddir == "PARALLEL") & (beamsign[k] > 0):
                    subims.append(0.5 * cim)
                    subims.append(0.5 * cim)
                else:
                    subims.append(cim)

    # --- update the WCS in the fits header
    newhead = update_wcs(head0, beampos[0, 1], beampos[0, 0], box)

    # --- write out
    if debug:
        fout = (outfolder + '/' + outbasename + '_all_extr_cube.fits')
        fits.writeto(fout, subims, newhead, overwrite=True)

#        fout = fout.replace(".fits", "_log.png")
#        I.make_gallery(ims=subims, outname=fout, pfovs=pfov, log=True,
#                       papercol=2, ncols=nbeams, cmap='gnuplot2', titles=titles,
#                       inv=False, permin=40, permax=99.9, titcols='white')
#
#        fout = fout.replace("_log.png", "_lin.png")
#        I.make_gallery(ims=subims, outname=fout, pfovs=pfov, log=False,
#                       papercol=2, ncols=nbeams, cmap='gnuplot2', titles=titles,
#                       inv=False, permin=40, permax=99.9, titcols='white')

# --- average all the sub-images
    totim = np.nanmean(subims, axis=0)

    # --- optional sigma clipping for the beam search
    if sigmaclip is not None:
        nrepl, totim = _replace_hotpix(totim,
                                       sigmathres=sigmaclip[0],
                                       niters=sigmaclip[1],
                                       verbose=verbose)

        msg = (funname + ": Number of replaced (hot) pixels: " + str(nrepl))
        _print_log_info(msg, logfile)

    if updatedb:

        # --- position of the found source
        tara = _fits_get_info(head0, keys="RA")
        tadec = _fits_get_info(head0, keys="DEC")

        wcs = WCS(head0)
        ra, dec = wcs.wcs_pix2world(beampos[0, 1], beampos[0, 0], 0)
        coord = SkyCoord(ra=ra, dec=dec, unit=(u.deg, u.deg), frame='icrs')

        dpro['GF_x_px'][expid] = beampos[0, 1]
        dpro['GF_y_px'][expid] = beampos[0, 0]
        dpro['GF_RA_hms'][expid] = coord.to_string('hmsdms').split()[0]
        dpro['GF_DEC_dms'][expid] = coord.to_string('hmsdms').split()[1]
        dpro['GF_angdist_as'][expid] = _angular_distance(ra, dec, tara, tadec)

        try:
            update_base_params(totim,
                               newhead,
                               dpro,
                               pfov,
                               expid,
                               onlyhead=onlyhead)
        except:
            e = sys.exc_info()
            msg = (funname + ": ERROR: Failed to update base parameters: \n" +
                   str(e[1]) + '' + str(traceback.print_tb(e[2])))
            _print_log_info(msg, logfile)
            noerr = noerr + 1

    fout = (outfolder + '/' + outbasename + '_all_extr-' + suffix + '.fits')
    fits.writeto(fout, totim, newhead, overwrite=True)

    pout = fout.replace(".fits", "_log.png")
    _simple_image_plot(totim, pout, scale="log", pfov=pfov, cenax=True)

    pout = fout.replace(".fits", "_lin.png")

    # --- if the source is probably faint, use lower colour thresholds
    if suffix == "fixest" or suffix == "fix1det":
        percentile = 1
    else:
        percentile = None

    _simple_image_plot(totim,
                       pout,
                       pfov=pfov,
                       cenax=True,
                       percentile=percentile)

    # --- to be uncommented once the make_gallery routine has been changed
    # try:
    #     fout = fout.replace(".fits", "_log.png")
    #     I.make_gallery(ims=[totim], outname=fout, pfovs=pfov, log=True,
    #                 papercol=1, ncols=1, cmap='gnuplot2',
    #                 inv=False, permin=40, permax=99.9, latex=False)

    #     fout = fout.replace("_log.png", "_lin.png")
    #     I.make_gallery(ims=[totim], outname=fout, pfovs=pfov, log=False,
    #                 papercol=1, ncols=1, cmap='gnuplot2',
    #                 inv=False, permin=40, permax=99.9, latex=False)

    # except:
    #     e = sys.exc_info()
    #     msg = (funname + ": ERROR:  Gallery plots failed to be created: \n"
    #            + str(e[1]) + '' + str(traceback.print_tb(e[2])))
    #     _print_log_info(msg, logfile)
    #     noe = noe + 1

    msg = " - Output written: " + fout
    _print_log_info(msg, logfile)

    return (noerr)