Ejemplo n.º 1
0
    def test_check_filters(self):
        filterlist = [
            'SDSS_U', 'SDSS_G', 'SDSS_R', 'SDSS_I', 'SDSS_Z', 'DECAM_U',
            'DECAM_G', 'DECAM_R', 'DECAM_I', 'DECAM_Z', 'DECAM_Y', 'WISE_W1',
            'WISE_W2'
        ]
        filters = ['XXXX', 'YYYY', 'ZZZZ']
        stdwave = np.linspace(3000, 11000, 10000)
        stdflux = np.cos(stdwave) + 100.
        mags = np.array([20, 21, 22])

        # No correct filters
        with self.assertRaises(SystemExit):
            normflux = normalize_templates(stdwave, stdflux, mags, filters)

        filters = filters + ['DECAM_R']
        #This should use DECAM_R for calibration
        mags = np.concatenate([mags, np.array([23])])
        normflux = normalize_templates(stdwave, stdflux, mags, filters)
        r = speclite.filters.load_filter('decam2014-r')
        rmag = r.get_ab_magnitude(1e-17 * normflux, stdwave)
        self.assertAlmostEqual(rmag, mags[-1])

        #check dimensionality
        self.assertEqual(stdflux.shape, normflux.shape)
Ejemplo n.º 2
0
    def test_check_filters(self):
        filterlist=['SDSS_U','SDSS_G','SDSS_R','SDSS_I','SDSS_Z','DECAM_U','DECAM_G','DECAM_R',
'DECAM_I','DECAM_Z','DECAM_Y','WISE_W1','WISE_W2']
        filters=['XXXX','YYYY','ZZZZ']
        stdwave=np.linspace(3000,11000,10000)
        stdflux=np.cos(stdwave)+100.
        mags=np.array([20,21,22])
        
        # No correct filters
        with self.assertRaises(SystemExit):
            normflux=normalize_templates(stdwave,stdflux,mags,filters)

        filters=filters+['DECAM_R']
        #This should use DECAM_R for calibration
        mags=np.concatenate([mags,np.array([23])])
        normflux=normalize_templates(stdwave,stdflux,mags,filters)
        r = speclite.filters.load_filter('decam2014-r')
        rmag = r.get_ab_magnitude(1e-17*normflux, stdwave)
        self.assertAlmostEqual(rmag, mags[-1])
        
        #check dimensionality
        self.assertEqual(stdflux.shape, normflux.shape)
Ejemplo n.º 3
0
    def test_normalize_templates(self):
        """
        Test for normalization to a given magnitude for calibration
        """

        stdwave=np.linspace(3000,11000,10000)
        stdflux=np.cos(stdwave)+100.
        mag = 20.0
        normflux=normalize_templates(stdwave,stdflux,mag,'DECAM_R')
        self.assertEqual(stdflux.shape, normflux.shape)

        r = speclite.filters.load_filter('decam2014-r')
        rmag = r.get_ab_magnitude(1e-17*normflux, stdwave)
        self.assertAlmostEqual(rmag, mag)
Ejemplo n.º 4
0
    def test_normalize_templates(self):
        """
        Test for normalization to a given magnitude for calibration
        """

        stdwave = np.linspace(3000, 11000, 10000)
        stdflux = np.cos(stdwave) + 100.
        mag = 20.0
        normflux = normalize_templates(stdwave, stdflux, mag, 'DECAM_R')
        self.assertEqual(stdflux.shape, normflux.shape)

        r = speclite.filters.load_filter('decam2014-r')
        rmag = r.get_ab_magnitude(1e-17 * normflux, stdwave)
        self.assertAlmostEqual(rmag, mag)
Ejemplo n.º 5
0
    def test_normalize_templates(self):
        """
        Test for normalization to a given magnitude for calibration
        """

        stdwave=np.linspace(3000,11000,10000)
        stdflux=np.cos(stdwave)+100.
        mag = 20.0
        normflux=normalize_templates(stdwave,stdflux,mag,'R','S')
        self.assertEqual(stdflux.shape, normflux.shape)

        r = load_legacy_survey_filter('R','S')
        rmag = r.get_ab_magnitude(1e-17*normflux, stdwave)
        self.assertAlmostEqual(rmag, mag)
Ejemplo n.º 6
0
    def test_normalize_templates(self):
        """
        Test for normalization to a given magnitude for calibration
        """

        stdwave=np.linspace(3000,11000,10000)
        stdflux=np.cos(stdwave)+100.
        mags=np.array((20,21))
        filters=['SDSS_I','SDSS_R']
        #This should use SDSS_R for calibration
        normflux=normalize_templates(stdwave,stdflux,mags,filters)

        self.assertEqual(stdflux.shape, normflux.shape)

        r = speclite.filters.load_filter('sdss2010-r')
        rmag = r.get_ab_magnitude(1e-17*normflux, stdwave)
        self.assertAlmostEqual(rmag, mags[1])
Ejemplo n.º 7
0
    def test_normalize_templates(self):
        """
        Test for normalization to a given magnitude for calibration
        """
        
        stdwave=np.linspace(3000,11000,10000)
        stdflux=np.cos(stdwave)+100.
        mags=np.array((20,21))
        filters=['SDSS_I','SDSS_R']
        #This should use SDSS_R for calibration
        normflux=normalize_templates(stdwave,stdflux,mags,filters)

        self.assertEqual(stdflux.shape, normflux.shape)

        r = speclite.filters.load_filter('sdss2010-r')
        rmag = r.get_ab_magnitude(1e-17*normflux, stdwave)
        self.assertAlmostEqual(rmag, mags[1])
Ejemplo n.º 8
0
def main() :
    """ finds the best models of all standard stars in the frame
    and normlize the model flux. Output is written to a file and will be called for calibration.
    """

    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('--fiberflatexpid', type = int, help = 'fiberflat exposure ID')
    parser.add_argument('--fibermap', type = str, help = 'path of fibermap file')
    parser.add_argument('--models', type = str, help = 'path of spectro-photometric stellar spectra fits')
    parser.add_argument('--spectrograph', type = int, default = 0, help = 'spectrograph number, can go 0-9')
    parser.add_argument('--outfile', type = str, help = 'output file for normalized stdstar model flux')

    args = parser.parse_args()
    log = get_logger()
    # Call necessary environment variables. No need if add argument to give full file path.
    if 'DESI_SPECTRO_REDUX' not in os.environ:
        raise RuntimeError('Set environment DESI_SPECTRO_REDUX. It is needed to read the needed datafiles')

    DESI_SPECTRO_REDUX=os.environ['DESI_SPECTRO_REDUX']
    PRODNAME=os.environ['PRODNAME']
    if 'DESISIM' not in os.environ:
        raise RuntimeError('Set environment DESISIM. It will be neede to read the filter transmission files for calibration')

    DESISIM=os.environ['DESISIM']   # to read the filter transmission files

    if args.fibermap is None or args.models is None or \
       args.spectrograph is None or args.outfile is None or \
       args.fiberflatexpid is None:
        log.critical('Missing a required argument')
        parser.print_help()
        sys.exit(12)

    # read Standard Stars from the fibermap file
    # returns the Fiber id, filter names and mags for the standard stars

    fiber_tbdata,fiber_header=io.read_fibermap(args.fibermap, header=True)

    #- Trim to just fibers on this spectrograph
    ii =  (500*args.spectrograph <= fiber_tbdata["FIBER"])
    ii &= (fiber_tbdata["FIBER"] < 500*(args.spectrograph+1))
    fiber_tbdata = fiber_tbdata[ii]

    #- Get info for the standard stars
    refStarIdx=np.where(fiber_tbdata["OBJTYPE"]=="STD")
    refFibers=fiber_tbdata["FIBER"][refStarIdx]
    refFilters=fiber_tbdata["FILTER"][refStarIdx]
    refMags=fiber_tbdata["MAG"]

    fibers={"FIBER":refFibers,"FILTER":refFilters,"MAG":refMags}

    NIGHT=fiber_header['NIGHT']
    EXPID=fiber_header['EXPID']
    filters=fibers["FILTER"]
    if 'DESISIM' not in os.environ:
        raise RuntimeError('Set environment DESISIM. Can not find filter response files')
    basepath=DESISIM+"/data/"

    #now load all the skyfiles, framefiles, fiberflatfiles etc
    # all three channels files are simultaneously treated for model fitting
    skyfile={}
    framefile={}
    fiberflatfile={}
    for i in ["b","r","z"]:
        camera = i+str(args.spectrograph)
        skyfile[i] = io.findfile('sky', NIGHT, EXPID, camera)
        framefile[i] = io.findfile('frame', NIGHT, EXPID, camera)
        fiberflatfile[i] = io.findfile('fiberflat', NIGHT, args.fiberflatexpid, camera)

    #Read Frames, Flats and Sky files
    frameFlux={}
    frameIvar={}
    frameWave={}
    frameResolution={}
    framehdr={}
    fiberFlat={}
    ivarFlat={}
    maskFlat={}
    meanspecFlat={}
    waveFlat={}
    headerFlat={}
    sky={}
    skyivar={}
    skymask={}
    skywave={}
    skyhdr={}

    for i in ["b","r","z"]:
       #arg=(night,expid,'%s%s'%(i,spectrograph))
       #- minimal code change for refactored I/O, while not taking advantage of simplified structure
       frame = io.read_frame(framefile[i])
       frameFlux[i] = frame.flux
       frameIvar[i] = frame.ivar
       frameWave[i] = frame.wave
       frameResolution[i] = frame.resolution_data
       framehdr[i] = frame.header

       ff = io.read_fiberflat(fiberflatfile[i])
       fiberFlat[i] = ff.fiberflat
       ivarFlat[i] = ff.ivar
       maskFlat[i] = ff.mask
       meanspecFlat[i] = ff.meanspec
       waveFlat[i] = ff.wave
       headerFlat[i] = ff.header

       skymodel = io.read_sky(skyfile[i])
       sky[i] = skymodel.flux
       skyivar[i] = skymodel.ivar
       skymask[i] = skymodel.mask
       skywave[i] = skymodel.wave
       skyhdr[i] = skymodel.header

    # Convolve Sky with Detector Resolution, so as to subtract from data. Convolve for all 500 specs. Subtracting sky this way should be equivalent to sky_subtract

    convolvedsky={"b":sky["b"], "r":sky["r"], "z":sky["z"]}

    # Read the standard Star data and divide by flat and subtract sky

    stars=[]
    ivars=[]
    for i in fibers["FIBER"]:
        #flat and sky should have same wavelength binning as data, otherwise should be rebinned.

        stars.append((i,{"b":[frameFlux["b"][i]/fiberFlat["b"][i]-convolvedsky["b"][i],frameWave["b"]],
                         "r":[frameFlux["r"][i]/fiberFlat["r"][i]-convolvedsky["r"][i],frameWave["r"]],
                         "z":[frameFlux["z"][i]/fiberFlat["z"][i]-convolvedsky["z"][i],frameWave]},fibers["MAG"][i]))
        ivars.append((i,{"b":[frameIvar["b"][i]],"r":[frameIvar["r"][i,:]],"z":[frameIvar["z"][i,:]]}))


    stdwave,stdflux,templateid=io.read_stdstar_templates(args.models)

    #- Trim standard star wavelengths to just the range we need
    minwave = min([min(w) for w in frameWave.values()])
    maxwave = max([max(w) for w in frameWave.values()])
    ii = (minwave-10 < stdwave) & (stdwave < maxwave+10)
    stdwave = stdwave[ii]
    stdflux = stdflux[:, ii]

    log.info('Number of Standard Stars in this frame: {0:d}'.format(len(stars)))
    if len(stars) == 0:
        log.critical("No standard stars!  Exiting")
        sys.exit(1)

    # Now for each star, find the best model and normalize.

    normflux=[]
    bestModelIndex=np.arange(len(stars))
    templateID=np.arange(len(stars))
    chi2dof=np.zeros(len(stars))

    #- TODO: don't use 'l' as a variable name.  Can look like a '1'
    for k,l in enumerate(stars):
        log.info("checking best model for star {0}".format(l[0]))

        starindex=l[0]
        mags=l[2]
        filters=fibers["FILTER"][k]
        rflux=stars[k][1]["r"][0]
        bflux=stars[k][1]["b"][0]
        zflux=stars[k][1]["z"][0]
        flux={"b":bflux,"r":rflux,"z":zflux}

        #print ivars
        rivar=ivars[k][1]["r"][0]
        bivar=ivars[k][1]["b"][0]
        zivar=ivars[k][1]["z"][0]
        ivar={"b":bivar,"r":rivar,"z":zivar}

        resol_star={"r":frameResolution["r"][l[0]],"b":frameResolution["b"][l[0]],"z":frameResolution["z"][l[0]]}

        # Now find the best Model

        bestModelIndex[k],bestmodelWave,bestModelFlux,chi2dof[k]=match_templates(frameWave,flux,ivar,resol_star,stdwave,stdflux)

        log.info('Star Fiber: {0}; Best Model Fiber: {1}; TemplateID: {2}; Chisq/dof: {3}'.format(l[0],bestModelIndex[k],templateid[bestModelIndex[k]],chi2dof[k]))
        # Normalize the best model using reported magnitude
        modelwave,normalizedflux=normalize_templates(stdwave,stdflux[bestModelIndex[k]],mags,filters,basepath)
        normflux.append(normalizedflux)

    # Now write the normalized flux for all best models to a file
    normflux=np.array(normflux)
    stdfibers=fibers["FIBER"]
    data={}
    data['BESTMODEL']=bestModelIndex
    data['CHI2DOF']=chi2dof
    data['TEMPLATEID']=templateid[bestModelIndex]
    norm_model_file=args.outfile
    io.write_stdstar_model(norm_model_file,normflux,stdwave,stdfibers,data)
Ejemplo n.º 9
0
def main(args):
    """ finds the best models of all standard stars in the frame
    and normlize the model flux. Output is written to a file and will be called for calibration.
    """

    log = get_logger()

    log.info("mag delta %s = %f (for the pre-selection of stellar models)" %
             (args.color, args.delta_color))

    frames = {}
    flats = {}
    skies = {}

    spectrograph = None
    starfibers = None
    starindices = None
    fibermap = None

    # READ DATA
    ############################################

    for filename in args.frames:

        log.info("reading %s" % filename)
        frame = io.read_frame(filename)
        header = fits.getheader(filename, 0)
        frame_fibermap = frame.fibermap
        frame_starindices = np.where(frame_fibermap["OBJTYPE"] == "STD")[0]
        camera = safe_read_key(header, "CAMERA").strip().lower()

        if spectrograph is None:
            spectrograph = frame.spectrograph
            fibermap = frame_fibermap
            starindices = frame_starindices
            starfibers = fibermap["FIBER"][starindices]

        elif spectrograph != frame.spectrograph:
            log.error("incompatible spectrographs %d != %d" %
                      (spectrograph, frame.spectrograph))
            raise ValueError("incompatible spectrographs %d != %d" %
                             (spectrograph, frame.spectrograph))
        elif starindices.size != frame_starindices.size or np.sum(
                starindices != frame_starindices) > 0:
            log.error("incompatible fibermap")
            raise ValueError("incompatible fibermap")

        if frames.has_key(camera):
            log.error(
                "cannot handle for now several frame of same camera (%s)" %
                camera)
            raise ValueError(
                "cannot handle for now several frame of same camera (%s)" %
                camera)

        frames[camera] = frame

    for filename in args.skymodels:
        log.info("reading %s" % filename)
        sky = io.read_sky(filename)
        header = fits.getheader(filename, 0)
        camera = safe_read_key(header, "CAMERA").strip().lower()

        # NEED TO ADD MORE CHECKS
        if skies.has_key(camera):
            log.error("cannot handle several skymodels of same camera (%s)" %
                      camera)
            raise ValueError(
                "cannot handle several skymodels of same camera (%s)" % camera)

        skies[camera] = sky

    for filename in args.fiberflats:
        log.info("reading %s" % filename)
        header = fits.getheader(filename, 0)
        flat = io.read_fiberflat(filename)
        camera = safe_read_key(header, "CAMERA").strip().lower()

        # NEED TO ADD MORE CHECKS
        if flats.has_key(camera):
            log.error("cannot handle several flats of same camera (%s)" %
                      camera)
            raise ValueError(
                "cannot handle several flats of same camera (%s)" % camera)
        flats[camera] = flat

    if starindices.size == 0:
        log.error("no STD star found in fibermap")
        raise ValueError("no STD star found in fibermap")

    log.info("found %d STD stars" % starindices.size)

    imaging_filters = fibermap["FILTER"][starindices]
    imaging_mags = fibermap["MAG"][starindices]

    log.warning(
        "NO MAG ERRORS IN FIBERMAP, I AM IGNORING MEASUREMENT ERRORS !!")
    log.warning(
        "NO EXTINCTION VALUES IN FIBERMAP, I AM IGNORING THIS FOR NOW !!")

    # DIVIDE FLAT AND SUBTRACT SKY , TRIM DATA
    ############################################
    for cam in frames:

        if not skies.has_key(cam):
            log.warning("Missing sky for %s" % cam)
            frames.pop(cam)
            continue
        if not flats.has_key(cam):
            log.warning("Missing flat for %s" % cam)
            frames.pop(cam)
            continue

        frames[cam].flux = frames[cam].flux[starindices]
        frames[cam].ivar = frames[cam].ivar[starindices]

        frames[cam].ivar *= (frames[cam].mask[starindices] == 0)
        frames[cam].ivar *= (skies[cam].ivar[starindices] != 0)
        frames[cam].ivar *= (skies[cam].mask[starindices] == 0)
        frames[cam].ivar *= (flats[cam].ivar[starindices] != 0)
        frames[cam].ivar *= (flats[cam].mask[starindices] == 0)
        frames[cam].flux *= (frames[cam].ivar > 0)  # just for clean plots
        for star in range(frames[cam].flux.shape[0]):
            ok = np.where((frames[cam].ivar[star] > 0)
                          & (flats[cam].fiberflat[star] != 0))[0]
            if ok.size > 0:
                frames[cam].flux[star] = frames[cam].flux[star] / flats[
                    cam].fiberflat[star] - skies[cam].flux[star]
    nstars = starindices.size
    starindices = None  # we don't need this anymore

    # READ MODELS
    ############################################
    log.info("reading star models in %s" % args.starmodels)
    stdwave, stdflux, templateid, teff, logg, feh = io.read_stdstar_templates(
        args.starmodels)

    # COMPUTE MAGS OF MODELS FOR EACH STD STAR MAG
    ############################################
    model_filters = []
    for tmp in np.unique(imaging_filters):
        if len(tmp) > 0:  # can be one empty entry
            model_filters.append(tmp)

    log.info("computing model mags %s" % model_filters)
    model_mags = np.zeros((stdflux.shape[0], len(model_filters)))
    fluxunits = 1e-17 * units.erg / units.s / units.cm**2 / units.Angstrom
    for index in range(len(model_filters)):
        filter_response = load_filter(model_filters[index])
        for m in range(stdflux.shape[0]):
            model_mags[m, index] = filter_response.get_ab_magnitude(
                stdflux[m] * fluxunits, stdwave)
    log.info("done computing model mags")

    # LOOP ON STARS TO FIND BEST MODEL
    ############################################
    bestModelIndex = np.arange(nstars)
    templateID = np.arange(nstars)
    chi2dof = np.zeros((nstars))
    redshift = np.zeros((nstars))
    normflux = []

    for star in range(nstars):

        log.info("finding best model for observed star #%d" % star)

        # np.array of wave,flux,ivar,resol
        wave = {}
        flux = {}
        ivar = {}
        resolution_data = {}
        for camera in frames:
            band = camera[0]
            wave[band] = frames[camera].wave
            flux[band] = frames[camera].flux[star]
            ivar[band] = frames[camera].ivar[star]
            resolution_data[band] = frames[camera].resolution_data[star]

        # preselec models based on magnitudes

        # compute star color
        index1, index2 = get_color_filter_indices(imaging_filters[star],
                                                  args.color)
        if index1 < 0 or index2 < 0:
            log.error("cannot compute '%s' color from %s" %
                      (color_name, filters))
        filter1 = imaging_filters[star][index1]
        filter2 = imaging_filters[star][index2]
        star_color = imaging_mags[star][index1] - imaging_mags[star][index2]

        # compute models color
        model_index1 = -1
        model_index2 = -1
        for i, fname in enumerate(model_filters):
            if fname == filter1:
                model_index1 = i
            elif fname == filter2:
                model_index2 = i

        if model_index1 < 0 or model_index2 < 0:
            log.error("cannot compute '%s' model color from %s" %
                      (color_name, filters))
        model_colors = model_mags[:, model_index1] - model_mags[:,
                                                                model_index2]

        # selection
        selection = np.where(
            np.abs(model_colors - star_color) < args.delta_color)[0]

        log.info(
            "star#%d fiber #%d, %s = %s-%s = %f, number of pre-selected models = %d/%d"
            % (star, starfibers[star], args.color, filter1, filter2,
               star_color, selection.size, stdflux.shape[0]))

        index_in_selection, redshift[star], chi2dof[star] = match_templates(
            wave,
            flux,
            ivar,
            resolution_data,
            stdwave,
            stdflux[selection],
            teff[selection],
            logg[selection],
            feh[selection],
            ncpu=args.ncpu,
            z_max=args.z_max,
            z_res=args.z_res)

        bestModelIndex[star] = selection[index_in_selection]

        log.info(
            'Star Fiber: {0}; TemplateID: {1}; Redshift: {2}; Chisq/dof: {3}'.
            format(starfibers[star], bestModelIndex[star], redshift[star],
                   chi2dof[star]))
        # Apply redshift to original spectrum at full resolution
        tmp = np.interp(stdwave, stdwave / (1 + redshift[star]),
                        stdflux[bestModelIndex[star]])
        # Normalize the best model using reported magnitude
        normalizedflux = normalize_templates(stdwave, tmp, imaging_mags[star],
                                             imaging_filters[star])
        normflux.append(normalizedflux)

    # Now write the normalized flux for all best models to a file
    normflux = np.array(normflux)
    data = {}
    data['BESTMODEL'] = bestModelIndex
    data['TEMPLATEID'] = bestModelIndex  # IS THAT IT?
    data['CHI2DOF'] = chi2dof
    data['REDSHIFT'] = redshift
    norm_model_file = args.outfile
    io.write_stdstar_models(args.outfile, normflux, stdwave, starfibers, data)
Ejemplo n.º 10
0
def main(args):
    """ finds the best models of all standard stars in the frame
    and normlize the model flux. Output is written to a file and will be called for calibration.
    """

    log = get_logger()

    log.info("mag delta %s = %f (for the pre-selection of stellar models)" %
             (args.color, args.delta_color))

    frames = {}
    flats = {}
    skies = {}

    spectrograph = None
    starfibers = None
    starindices = None
    fibermap = None

    # READ DATA
    ############################################

    for filename in args.frames:

        log.info("reading %s" % filename)
        frame = io.read_frame(filename)
        header = fits.getheader(filename, 0)
        frame_fibermap = frame.fibermap
        frame_starindices = np.where(frame_fibermap["OBJTYPE"] == "STD")[0]

        # check magnitude are well defined or discard stars
        tmp = []
        for i in frame_starindices:
            mags = frame_fibermap["MAG"][i]
            ok = np.sum((mags > 0) & (mags < 30))
            if np.sum((mags > 0) & (mags < 30)) == mags.size:
                tmp.append(i)
        frame_starindices = np.array(tmp).astype(int)

        camera = safe_read_key(header, "CAMERA").strip().lower()

        if spectrograph is None:
            spectrograph = frame.spectrograph
            fibermap = frame_fibermap
            starindices = frame_starindices
            starfibers = fibermap["FIBER"][starindices]

        elif spectrograph != frame.spectrograph:
            log.error("incompatible spectrographs %d != %d" %
                      (spectrograph, frame.spectrograph))
            raise ValueError("incompatible spectrographs %d != %d" %
                             (spectrograph, frame.spectrograph))
        elif starindices.size != frame_starindices.size or np.sum(
                starindices != frame_starindices) > 0:
            log.error("incompatible fibermap")
            raise ValueError("incompatible fibermap")

        if not camera in frames:
            frames[camera] = []
        frames[camera].append(frame)

    for filename in args.skymodels:
        log.info("reading %s" % filename)
        sky = io.read_sky(filename)
        header = fits.getheader(filename, 0)
        camera = safe_read_key(header, "CAMERA").strip().lower()
        if not camera in skies:
            skies[camera] = []
        skies[camera].append(sky)

    for filename in args.fiberflats:
        log.info("reading %s" % filename)
        header = fits.getheader(filename, 0)
        flat = io.read_fiberflat(filename)
        camera = safe_read_key(header, "CAMERA").strip().lower()

        # NEED TO ADD MORE CHECKS
        if camera in flats:

            log.warning(
                "cannot handle several flats of same camera (%s), will use only the first one"
                % camera)
            #raise ValueError("cannot handle several flats of same camera (%s)"%camera)
        else:
            flats[camera] = flat

    if starindices.size == 0:
        log.error("no STD star found in fibermap")
        raise ValueError("no STD star found in fibermap")

    log.info("found %d STD stars" % starindices.size)

    imaging_filters = fibermap["FILTER"][starindices]
    imaging_mags = fibermap["MAG"][starindices]

    log.warning(
        "NO MAG ERRORS IN FIBERMAP, I AM IGNORING MEASUREMENT ERRORS !!")

    ebv = np.zeros(starindices.size)
    if "SFD_EBV" in fibermap.columns.names:
        log.info("Using 'SFD_EBV' from fibermap")
        ebv = fibermap["SFD_EBV"][starindices]
    else:
        log.warning("NO EXTINCTION VALUES IN FIBERMAP!!")

    # DIVIDE FLAT AND SUBTRACT SKY , TRIM DATA
    ############################################
    for cam in frames:

        if not cam in skies:
            log.warning("Missing sky for %s" % cam)
            frames.pop(cam)
            continue
        if not cam in flats:
            log.warning("Missing flat for %s" % cam)
            frames.pop(cam)
            continue

        flat = flats[cam]
        for frame, sky in zip(frames[cam], skies[cam]):
            frame.flux = frame.flux[starindices]
            frame.ivar = frame.ivar[starindices]
            frame.ivar *= (frame.mask[starindices] == 0)
            frame.ivar *= (sky.ivar[starindices] != 0)
            frame.ivar *= (sky.mask[starindices] == 0)
            frame.ivar *= (flat.ivar[starindices] != 0)
            frame.ivar *= (flat.mask[starindices] == 0)
            frame.flux *= (frame.ivar > 0)  # just for clean plots
            for star in range(frame.flux.shape[0]):
                ok = np.where((frame.ivar[star] > 0)
                              & (flat.fiberflat[star] != 0))[0]
                if ok.size > 0:
                    frame.flux[star] = frame.flux[star] / flat.fiberflat[
                        star] - sky.flux[star]
            frame.resolution_data = frame.resolution_data[starindices]

    nstars = starindices.size
    starindices = None  # we don't need this anymore

    # READ MODELS
    ############################################
    log.info("reading star models in %s" % args.starmodels)
    stdwave, stdflux, templateid, teff, logg, feh = io.read_stdstar_templates(
        args.starmodels)

    # COMPUTE MAGS OF MODELS FOR EACH STD STAR MAG
    ############################################
    model_filters = []
    for tmp in np.unique(imaging_filters):
        if len(tmp) > 0:  # can be one empty entry
            model_filters.append(tmp)

    log.info("computing model mags %s" % model_filters)
    model_mags = np.zeros((stdflux.shape[0], len(model_filters)))
    fluxunits = 1e-17 * units.erg / units.s / units.cm**2 / units.Angstrom

    for index in range(len(model_filters)):
        if model_filters[index].startswith('WISE'):
            log.warning('not computing stdstar {} mags'.format(
                model_filters[index]))
            continue

        filter_response = load_filter(model_filters[index])
        for m in range(stdflux.shape[0]):
            model_mags[m, index] = filter_response.get_ab_magnitude(
                stdflux[m] * fluxunits, stdwave)
    log.info("done computing model mags")

    mean_extinction_delta_mags = None
    mean_ebv = np.mean(ebv)
    if mean_ebv > 0:
        log.info(
            "Compute a mean delta_color from average E(B-V) = %3.2f based on canonial model star"
            % mean_ebv)
        # compute a mean delta_color from mean_ebv based on canonial model star
        #######################################################################
        # will then use this color offset in the model pre-selection
        # find canonical f-type model: Teff=6000, logg=4, Fe/H=-1.5
        canonical_model = np.argmin((teff - 6000.0)**2 + (logg - 4.0)**2 +
                                    (feh + 1.5)**2)
        canonical_model_mags_without_extinction = model_mags[canonical_model]
        canonical_model_mags_with_extinction = np.zeros(
            canonical_model_mags_without_extinction.shape)

        canonical_model_reddened_flux = stdflux[
            canonical_model] * dust_transmission(stdwave, mean_ebv)
        for index in range(len(model_filters)):
            if model_filters[index].startswith('WISE'):
                log.warning('not computing stdstar {} mags'.format(
                    model_filters[index]))
                continue
            filter_response = load_filter(model_filters[index])
            canonical_model_mags_with_extinction[
                index] = filter_response.get_ab_magnitude(
                    canonical_model_reddened_flux * fluxunits, stdwave)

        mean_extinction_delta_mags = canonical_model_mags_with_extinction - canonical_model_mags_without_extinction

    # LOOP ON STARS TO FIND BEST MODEL
    ############################################
    linear_coefficients = np.zeros((nstars, stdflux.shape[0]))
    chi2dof = np.zeros((nstars))
    redshift = np.zeros((nstars))
    normflux = []

    star_colors_array = np.zeros((nstars))
    model_colors_array = np.zeros((nstars))

    for star in range(nstars):

        log.info("finding best model for observed star #%d" % star)

        # np.array of wave,flux,ivar,resol
        wave = {}
        flux = {}
        ivar = {}
        resolution_data = {}
        for camera in frames:

            for i, frame in enumerate(frames[camera]):
                identifier = "%s-%d" % (camera, i)
                wave[identifier] = frame.wave
                flux[identifier] = frame.flux[star]
                ivar[identifier] = frame.ivar[star]
                resolution_data[identifier] = frame.resolution_data[star]

        # preselec models based on magnitudes

        # compute star color
        index1, index2 = get_color_filter_indices(imaging_filters[star],
                                                  args.color)
        if index1 < 0 or index2 < 0:
            log.error("cannot compute '%s' color from %s" %
                      (color_name, filters))
        filter1 = imaging_filters[star][index1]
        filter2 = imaging_filters[star][index2]
        star_color = imaging_mags[star][index1] - imaging_mags[star][index2]
        star_colors_array[star] = star_color

        # compute models color
        model_index1 = -1
        model_index2 = -1
        for i, fname in enumerate(model_filters):
            if fname == filter1:
                model_index1 = i
            elif fname == filter2:
                model_index2 = i

        if model_index1 < 0 or model_index2 < 0:
            log.error("cannot compute '%s' model color from %s" %
                      (color_name, filters))
        model_colors = model_mags[:, model_index1] - model_mags[:,
                                                                model_index2]

        # apply extinction here
        # use the colors derived from the cannonical model with the mean ebv of the stars
        # and simply apply a scaling factor based on the ebv of this star
        # this is sufficiently precise for the broad model pre-selection we are doing here
        # the exact reddening of the star to each pre-selected model is
        # apply afterwards
        if mean_extinction_delta_mags is not None and mean_ebv != 0:
            delta_color = (mean_extinction_delta_mags[model_index1] -
                           mean_extinction_delta_mags[model_index2]
                           ) * ebv[star] / mean_ebv
            model_colors += delta_color
            log.info(
                "Apply a %s-%s color offset = %4.3f to the models for star with E(B-V)=%4.3f"
                % (model_filters[model_index1], model_filters[model_index2],
                   delta_color, ebv[star]))
        # selection

        selection = np.abs(model_colors - star_color) < args.delta_color
        # smallest cube in parameter space including this selection (needed for interpolation)
        new_selection = (teff >= np.min(teff[selection])) & (teff <= np.max(
            teff[selection]))
        new_selection &= (logg >= np.min(logg[selection])) & (logg <= np.max(
            logg[selection]))
        new_selection &= (feh >= np.min(feh[selection])) & (feh <= np.max(
            feh[selection]))
        selection = np.where(new_selection)[0]

        log.info(
            "star#%d fiber #%d, %s = %s-%s = %f, number of pre-selected models = %d/%d"
            % (star, starfibers[star], args.color, filter1, filter2,
               star_color, selection.size, stdflux.shape[0]))

        # apply extinction to selected_models
        dust_transmission_of_this_star = dust_transmission(stdwave, ebv[star])
        selected_reddened_stdflux = stdflux[
            selection] * dust_transmission_of_this_star

        coefficients, redshift[star], chi2dof[star] = match_templates(
            wave,
            flux,
            ivar,
            resolution_data,
            stdwave,
            selected_reddened_stdflux,
            teff[selection],
            logg[selection],
            feh[selection],
            ncpu=args.ncpu,
            z_max=args.z_max,
            z_res=args.z_res,
            template_error=args.template_error)

        linear_coefficients[star, selection] = coefficients

        log.info(
            'Star Fiber: {0}; TEFF: {1}; LOGG: {2}; FEH: {3}; Redshift: {4}; Chisq/dof: {5}'
            .format(starfibers[star], np.inner(teff,
                                               linear_coefficients[star]),
                    np.inner(logg, linear_coefficients[star]),
                    np.inner(feh, linear_coefficients[star]), redshift[star],
                    chi2dof[star]))

        # Apply redshift to original spectrum at full resolution
        model = np.zeros(stdwave.size)
        for i, c in enumerate(linear_coefficients[star]):
            if c != 0:
                model += c * np.interp(stdwave, stdwave *
                                       (1 + redshift[star]), stdflux[i])

        # Apply dust extinction
        model *= dust_transmission_of_this_star

        # Compute final model color
        mag1 = load_filter(model_filters[model_index1]).get_ab_magnitude(
            model * fluxunits, stdwave)
        mag2 = load_filter(model_filters[model_index2]).get_ab_magnitude(
            model * fluxunits, stdwave)
        model_colors_array[star] = mag1 - mag2

        # Normalize the best model using reported magnitude
        normalizedflux = normalize_templates(stdwave, model,
                                             imaging_mags[star],
                                             imaging_filters[star])
        normflux.append(normalizedflux)

    # Now write the normalized flux for all best models to a file
    normflux = np.array(normflux)
    data = {}
    data['LOGG'] = linear_coefficients.dot(logg)
    data['TEFF'] = linear_coefficients.dot(teff)
    data['FEH'] = linear_coefficients.dot(feh)
    data['CHI2DOF'] = chi2dof
    data['REDSHIFT'] = redshift
    data['COEFF'] = linear_coefficients
    data['DATA_%s' % args.color] = star_colors_array
    data['MODEL_%s' % args.color] = model_colors_array
    norm_model_file = args.outfile
    io.write_stdstar_models(args.outfile, normflux, stdwave, starfibers, data)