Beispiel #1
0
    def __init__(self, ra0, dec0, pa, x0, y0, x_scale=-1., y_scale=1.,
                 sys_rot=1.07, fplane_file=None, kind='fplane'):
        self.setup_logging()
        self.ra0 = ra0
        self.dec0 = dec0
        self.dra = 0.
        self.ddec = 0.
        self.dx = 0.
        self.dy = 0.
        self.x0 = x0
        self.y0 = y0
        self.pa = pa
        self.x_scale = x_scale
        self.y_scale = y_scale
        self.sys_rot = sys_rot
        self.fplane_file = fplane_file
        if self.fplane_file is None:
            self.log.info('No fplane file given.')
            self.log.info('Some functions will be unavailable until'
                          'an fplane is given.')
        else:
            if not pyhetdex_flag:
                self.log.warning('Cannot load fplane because pyhetdex '
                                 'is not available')
            else:
                self.fplane = FPlane(self.fplane_file)
        self.kind = kind
        self.set_effective_rotation()

        # Building tangent plane projection with scale 1"
        self.tp = self.setup_TP(self.ra0, self.dec0, self.rot, self.x0,
                                self.y0)
        self.tp_ifuslot = None
Beispiel #2
0
 def __init__(self,
              fplane_file,
              shot,
              dither_positions=[
                  ['000', 0.000, -1.270, -1.270, 0.000, 0.730, -0.730],
              ]):
     self.ifu_dxs = {}
     self.ifu_dys = {}
     self.shot = shot
     self.fplane = FPlane(fplane_file)
     self._store_dither_positions(dither_positions)
def generate_sencube_hdf(datevshot,
                         ra,
                         dec,
                         pa,
                         fplane_output_dir,
                         nx,
                         ny,
                         nz,
                         ifusize,
                         skip_ifus=[
                             "000", "600", "555", "601", "602", "603", "604",
                             "610", "611", "612", "613", "614", "615", "616"
                         ],
                         hdf_filename=None):
    """
    Generate an empty real or mock sensitivity HDF5 container, 
    with the proper astrometry in the cubes. Real containters
    are of the SensitivityCubeHDF5Container class and are 
    written to a file. The mock containers are of
    HDF5MockContainer class and do not have a real HDF5
    file - useful for simulations. 

    Parameters
    ----------
    datevshot : str
        the 8 digit YYYYMMDDvSSS date
        of the shot and the shot, used to get the
        correct focal plane file
    ra, dec, pa : float
        the astrometry of the shot
    fplane_output_dir : str
        directory to output fplane files to
    hdf_filename : str (optional)
        if passed, generate a real
        SensitivityCubeHDF5Container
        with this filename. If None
        generate a mock container.
    ifusize : float
        size of x,y of IFU in arcsec
    skip_ifus : list (optional)
        the IFUSLOTS to skip

    Returns
    -------
    hdfcont : SensitivityCubeHDF5Container or HDF5MockContainer
       a real or mock sensivity cube container depending on
       the ``hdf_filename`` parameter
    """
    if hdf_filename:
        hdfcont = SensitivityCubeHDF5Container(hdf_filename, mode="w")
    else:
        hdfcont = HDF5MockContainer()

    # Generate the shot astrometry
    rot = 360.0 - (pa + 90.)
    tp = TangentPlane(ra, dec, rot)

    date = datevshot[:8]
    fplane_bn = "{:s}_fplane.txt".format(date)
    fplane_fn = join(fplane_output_dir, fplane_bn)

    if not isfile(fplane_fn):
        get_fplane(fplane_fn, datestr=str(date))
        fplane = FPlane(fplane_fn)
    else:
        fplane = FPlane(fplane_fn)

    for ifuslot, ifu in iteritems(fplane.difus_ifuslot):

        if ifuslot in skip_ifus:
            continue

        ifuslot_str = "ifuslot_" + ifuslot
        # Note x, y swapped in focal fplane
        ra_ifu, dec_ifu = tp.xy2raDec(ifu.y, ifu.x)
        scube = create_sensitivity_cube_from_astrom(ra_ifu.item(),
                                                    dec_ifu.item(), pa, nx, ny,
                                                    nz, ifusize)
        hdfcont.add_sensitivity_cube(datevshot, ifuslot_str, scube)

    return hdfcont
def split_into_ifus(table,
                    unique_fields,
                    unique_ifus,
                    outdir,
                    NMAX=1000,
                    nsplit_start=0,
                    nsplit=1):
    """
    Split a catalogue into input catalogues
    and commands on single IFUs. Outputs
    catalogues and files full of calls
    to Karl's HETDEX simulation code

    Parameters
    ----------
    unique_fields, unique_ifus : array
        fields and IFUs to split up
    outdir : str
        output directory
    NMAX : int (optional)
        maximum source per IFU,
        further splits if bigger than this
 
    """

    # Maximum sources per ifu in one sim run
    # Second part of the filename
    fn2s = []
    ras = []
    decs = []
    for field in unique_fields:
        table_field = table[table["field"] == field]
        for ifuslot in unique_ifus:

            table_ifu = table_field[table_field["ifuslot"] == ifuslot]
            # Split into sub sets if too big to simulate at once
            nsplit = int(ceil(len(table_ifu) / NMAX))
            for isplit in range(nsplit):
                ttable = table_ifu[isplit * NMAX:(isplit + 1) * NMAX]

                date = field[:-4]
                fplane_fn = "{:s}_fplane.txt".format(date)
                if not isfile(fplane_fn):
                    get_fplane(fplane_fn, datestr=date)
                    fplane = FPlane(fplane_fn)
                else:
                    fplane = FPlane(fplane_fn)

                ifu = fplane.by_id(ifuslot[-3:], "ifuslot")

                fn2 = "{:03d}_{:s}_{:s}".format(ifu.specid, ifu.ifuslot,
                                                ifu.ifuid)
                if isplit + nsplit_start > 0:
                    fn2 = fn2 + "_{:d}".format(isplit + nsplit_start)

                outfn = join(outdir, "{:s}_{:s}.input".format(field, fn2))
                savetxt(
                    outfn,
                    transpose([
                        ttable["ra"], ttable["dec"], ttable["wave"],
                        ttable["flux_extincted"]
                    ]))
                ras.append(ttable["ifu_ra"][0])
                decs.append(ttable["ifu_dec"][0])
                fn2s.append(fn2)

        produce_rs1_runfile(
            join(outdir, "{:s}_{:d}.run".format(field, nsplit_start)), fn2s,
            ras, decs, field, nsplit)
Beispiel #5
0
class Astrometry:
    '''
    Astrometry Class

    This class holds basic astrometric solutions for fits imaging and has a
    suite of functions designed to manage the conversion from on sky
    coordinates to on image coordinates (and vice versa) as well as take
    updates to the solution.  Other functionaility has a specific design
    for serving as the HET's fplane astrometry and building astrometric
    solutions for given ifuslot's based on the fplane astrometry.  This is
    useful for reconstructed VIRUS images.
    '''
    def __init__(self, ra0, dec0, pa, x0, y0, x_scale=-1., y_scale=1.,
                 sys_rot=1.07, fplane_file=None, kind='fplane'):
        self.setup_logging()
        self.ra0 = ra0
        self.dec0 = dec0
        self.dra = 0.
        self.ddec = 0.
        self.dx = 0.
        self.dy = 0.
        self.x0 = x0
        self.y0 = y0
        self.pa = pa
        self.x_scale = x_scale
        self.y_scale = y_scale
        self.sys_rot = sys_rot
        self.fplane_file = fplane_file
        if self.fplane_file is None:
            self.log.info('No fplane file given.')
            self.log.info('Some functions will be unavailable until'
                          'an fplane is given.')
        else:
            if not pyhetdex_flag:
                self.log.warning('Cannot load fplane because pyhetdex '
                                 'is not available')
            else:
                self.fplane = FPlane(self.fplane_file)
        self.kind = kind
        self.set_effective_rotation()

        # Building tangent plane projection with scale 1"
        self.tp = self.setup_TP(self.ra0, self.dec0, self.rot, self.x0,
                                self.y0)
        self.tp_ifuslot = None

    def setup_TP(self, ra0, dec0, rot, x0=0.0, y0=0.0):
        ''' TP is tangent plane '''
        ARCSECPERDEG = 1.0/3600.0

        # make a WCS object with appropriate FITS headers
        tp = wcs.WCS(naxis=2)
        tp.wcs.crpix = [x0, y0]  # central "pixel"
        tp.wcs.crval = [ra0, dec0]  # tangent point
        tp.wcs.ctype = ['RA---TAN', 'DEC--TAN']
        # pixel scale in deg.
        tp.wcs.cdelt = [ARCSECPERDEG * self.x_scale,
                        ARCSECPERDEG * self.y_scale]

        # Deal with PA rotation by adding rotation matrix to header
        rrot = deg2rad(rot)
        # clockwise rotation matrix
        tp.wcs.pc = [[cos(rrot), sin(rrot)], [-1.0*sin(rrot), cos(rrot)]]
        return tp

    def set_polynomial_platescale(self):
        ''' This has not been tested '''
        self.tp.wcs.a_0_0 = 0.177311
        self.tp.wcs.a_1_0 = -8.29099e-06
        self.tp.wcs.a_2_0 = -2.37318e-05
        self.tp.wcs.b_0_0 = 0.177311
        self.tp.wcs.b_0_1 = -8.29099e-06
        self.tp.wcs.b_0_2 = -2.37318e-05
        self.tp.wcs.a_order = 2
        self.tp.wcs.b_order = 2

    def setup_logging(self):
        '''Set up a logger for analysis with a name ``astrometry``.

        Use a StreamHandler to write to stdout and set the level to DEBUG if
        verbose is set from the command line
        '''
        self.log = logging.getLogger('astrometry')
        if not len(self.log.handlers):
            fmt = '[%(levelname)s - %(asctime)s] %(message)s'
            level = logging.INFO

            fmt = logging.Formatter(fmt)

            handler = logging.StreamHandler()
            handler.setFormatter(fmt)
            handler.setLevel(level)

            self.log = logging.getLogger('astrometry')
            self.log.setLevel(logging.DEBUG)
            self.log.addHandler(handler)

    def set_effective_rotation(self):
        ''' The rotation for the acam is correct '''
        if self.kind == 'fplane':
            self.rot = 360. - (90. + self.pa + self.sys_rot)
        elif self.kind == 'acam':
            self.rot = self.pa + self.sys_rot + 90.
        else:
            self.log.error('"kind" was not set to available options.')
            self.log.info('Available options are: %s and %s' % ('fplane',
                                                                'acam'))
            self.log.info('Next time please choose one of the options above.')
            self.log.info('Exiting due to error.')
            sys.exit(1)

    def update_projection(self):
        ''' Use this for a new projection with small adjustments '''
        self.set_effective_rotation()
        self.tp = self.setup_TP(self.ra0 + self.dra, self.dec0 + self.ddec,
                                self.rot, self.x0 + self.dx, self.y0 + self.dy)

    def get_ifuslot_ra_dec(self, ifuslot):
        ''' Fplane functionality required for this '''
        if self.fplane is None:
            return None
        ifu = self.fplane.by_ifuslot(ifuslot)
        # remember to flip x,y
        return self.tp.xy2raDec(ifu.y, ifu.x)

    def get_ifuspos_ra_dec(self, ifuslot, x, y):
        ''' Fplane functionality required for this '''
        if self.fplane is None or not pyhetdex_flag:
            return None
        ifu = self.fplane.by_ifuslot(ifuslot)
        # remember to flip x,y
        return self.tp.wcs_pix2world(ifu.y + x, ifu.x + y, 1)

    def get_ifuslot_projection(self, ifuslot, imscale, crx, cry):
        ''' Fplane functionality required for this '''
        if self.fplane is None or not pyhetdex_flag:
            return None
        ra, dec = self.get_ifuslot_ra_dec(ifuslot)
        self.tp_ifuslot = self.setup_TP(ra, dec, self.rot, crx, cry,
                                        x_scale=-imscale, y_scale=imscale)

    def convert_ifuslot_xy_to_new_xy(self, x, y, wcs):
        ''' Fplane functionality required for this '''
        if self.tp_ifuslot is None or not pyhetdex_flag:
            self.log.error('You have not setup the ifuslot projection yet.')
            self.log.error('To do so, call '
                           '"get_ifuslot_projection(ifuslot, imscale')
            return None
        ra, dec = self.tp_ifuslot.wcs.wcs_pix2world(x, y, 1)
        return wcs.wcs_world2pix(ra, dec, 1)
Beispiel #6
0
class DitherCreator(object):
    """Class to create dither files

    Initialize the dither file creator for this shot

    Read in the locations of the dithers for each IFU from
    dither_positions.

    Parameters
    ----------
    fplane_file : str
        path the focal plane file; it is parsed using
        :class:`~pyhetdex.het.fplane.FPlane`
    shot : :class:`pyhetdex.het.telescope.Shot` instance
        a ``shot`` instance that contains info on the image quality and
        normalization
    dither_positions : list of lists, optional
        list of lists with 2*n+1 elements: the first element is the ``id_``,
        used in, e.g., :meth:`create_dither`, then there are the n x-positions
        of the dithers and finally their n y-positions

    Attributes
    ----------
    ifu_dxs, ifu_dys : dictionaries
        key: ihmpid; key: x and y shifts for the dithers
    shot : :class:`pyhetdex.het.telescope.Shot` instance
    fplane : :class:`~pyhetdex.het.fplane.FPlane` instance

    Raises
    ------
    DitherPositionError
        if the number of x and y dither shifts for one id does not match
    """
    def __init__(self,
                 fplane_file,
                 shot,
                 dither_positions=[
                     ['000', 0.000, -1.270, -1.270, 0.000, 0.730, -0.730],
                 ]):
        self.ifu_dxs = {}
        self.ifu_dys = {}
        self.shot = shot
        self.fplane = FPlane(fplane_file)
        self._store_dither_positions(dither_positions)

    @classmethod
    def from_file(cls, fplane_file, shot, dither_positions_file):
        """Create an instance of the class reading the dither positions from a
        file.

        Parameters
        ----------
        fplane_file : str
            path the focal plane file; it is parsed using
            :class:`~pyhetdex.het.fplane.FPlane`
        shot : :class:`pyhetdex.het.telescope.Shot` instance
            a ``shot`` instance that contains info on the image quality and
            normalization
        dither_positions_file : str
            path to file containing the positions of the dithers for each
            IHMPID; the file must have the following format::

                ihmpid x1 x2 ... xn y1 y2 ... yn
        """
        dither_positions = []
        with open(dither_positions_file, 'r') as f:
            for line in f:
                els = line.strip().split()
                if '#' in els[0]:
                    continue
                else:
                    dither_positions.append(els)

        return cls(fplane_file, shot, dither_positions=dither_positions)

    def _store_dither_positions(self, dither_positions):
        '''Store the dither positions into the ``ifu_dxs`` and ``ifu_dys``
        dictionaries'''
        for dp in dither_positions:
            key = dp[0]
            if len(dp[1:]) % 2 == 1:
                msg = ("The line '{}' has a miss-matching"
                       " number of x and y entries")
                raise DitherPositionError(msg.format(dp))
            else:
                n_x = len(dp[1:]) // 2
            self.ifu_dxs[key] = array(dp[1:n_x + 1], dtype=float)
            self.ifu_dys[key] = array(dp[n_x + 1:], dtype=float)

    def dxs(self, id_, idtype='ifuslot'):
        """Returns the x shifts for the given ``id_``

        Parameters
        ----------
        id_ : str
            the id of the IFU
        idtype : str, optional
            type of the id; must be one of ``'ifuid'``, ``'ihmpid'``,
            ``'specid'``

        Returns
        -------
        ndarray
            x shifts
        """
        ifu = self.fplane.by_id(id_, idtype=idtype)
        return self.ifu_dxs[ifu.ifuslot]

    def dys(self, id_, idtype='ifuslot'):
        """Returns the y shifts for the given ``id_``

        Parameters
        ----------
        id_ : str
            the id of the IFU
        idtype : str, optional
            type of the id; must be one of ``'ifuid'``, ``'ihmpid'``,
            ``'specid'``

        Returns
        -------
        ndarray
            y shifts
        """
        ifu = self.fplane.by_id(id_, idtype=idtype)
        return self.ifu_dys[ifu.ifuslot]

    def create_dither(self,
                      id_,
                      basenames,
                      modelbases,
                      outfile,
                      idtype='ifuid'):
        """ Create a dither file

        Parameters
        ----------
        id_ : str
            the id of the IFU
        basenames, modelnames : list of strings
            the root of the file and model (distortion, fiber etc);
            their lengths must be the same as :attr:`ifu_dxs`
        outfile : str
            the output filename
        idtype : str, optional
            type of the id; must be one of ``'ifuid'``, ``'ifuslot'``,
            ``'specid'``
        """
        ifu = self.fplane.by_id(id_, idtype=idtype)
        dxs = self.dxs(id_, idtype=idtype)
        dys = self.dys(id_, idtype=idtype)

        if len(basenames) != len(dxs):
            msg = ("The number of elements in 'basenames' ({}) doesn't agree"
                   " with the expected number of dithers"
                   " ({})".format(len(basenames), len(dxs)))
            raise DitherCreationError(msg)
        if len(modelbases) != len(dxs):
            msg = ("The number of elements in 'modelbases' ({}) doesn't agree"
                   " with the expected number of dithers"
                   " ({})".format(len(modelbases), len(dxs)))
            raise DitherCreationError(msg)

        s = "# basename          modelbase           ditherx dithery\
                seeing norm airmass\n"

        line = "{:s} {:s} {:f} {:f} {:4.3f} {:5.4f} {:5.4f}\n"
        for dither, bn, mb, dx, dy in zip(it.count(start=1), basenames,
                                          modelbases, dxs, dys):
            seeing = self.shot.fwhm(ifu.x, ifu.y, dither)
            norm = self.shot.normalisation(ifu.x, ifu.y, dither)
            # TODO replace with something
            airmass = 1.22

            s += line.format(bn, mb, dx, dy, seeing, norm, airmass)

        with open(outfile, 'w') as f:
            f.write(s)
Beispiel #7
0
def generate_mangle_polyfile(args=None):
    """
    Command line call to generate a Mangle polygon file in vertices format

    Mangle reference: http://space.mit.edu/~molly/mangle/

    Parameters
    ----------
    args : list (optional)
        list of arguments to parse. If None, grab
        from command line

    """

    parser = argparse.ArgumentParser(description="""Generate a polygon file
                                     suitable for use in the Mangle mask
                                     software in vertices format. A line
                                     contains the four corners of an IFU in ra,
                                     dec. You can pass this to suitable Mangle
                                     commands, like poly2poly, with -iv4d
                                     input-type flag.""")

    parser.add_argument("shot_file",
                        help="""An ascii file containing the
                        header 'SHOTID RACEN DECCEN PARANGLE FPLANE' and
                        appropriate entries. Coordinates should be given in
                        degrees.""")

    parser.add_argument("out_file",
                        help="""File name for the Mangle compatible polygon
                        file""")

    parser.add_argument("rot_offset",
                        help="Rotation difference to add to PARANGLE",
                        default=0.0,
                        type=float)

    opts = parser.parse_args(args=args)

    tables = []

    try:
        table_shots = Table.read(opts.shot_file, format='ascii')
    except IOError as e:
        print("Problem opening input file {:s}".format(opts.shot_file))
        raise e

    fplane_name_last = ""
    for row in table_shots:

        if row['FPLANE'] != fplane_name_last or not fplane:
            fplane = FPlane(row['FPLANE'])
            fplane_name_last = row['FPLANE']

        # Carry out required changes to astrometry
        rot = 360.0 - (row['PARANGLE'] + 90.0 + opts.rot_offset)
        tp = TangentPlane(row['RACEN'], row['DECCEN'], rot)

        table = generate_ifu_corner_ra_decs(tp, fplane)
        print(row['SHOTID'])
        table['shotid'] = row['SHOTID']
        tables.append(table)

    table_out = vstack(tables)
    table_out.write(opts.out_file,
                    format='ascii.commented_header',
                    comment='#')
def generate_ifu_mask(output_fn, survey_hdf, badshots_file, ramin, ramax, decmin, decmax, specific_shot=None,
                      xsize=25.0, ysize=25.0, other_cuts={}, specific_field=None):
    """
    Generate a mask of IFU corners from the survey HDF 

    Parameters
    ----------
    output_fn : str
        a file to output the ra/dec corners to
    survey_hdf : str
        path to the survey HDF
    badshots_file : str
        path to the bad shots file
    ramin, ramax, decmin, decmax : float
        restrict the mask to a subregion
    specific_shot : str (Optional)
        overides the ra and dec range
        and instead only outputs a bad
        mask only for the given shotid
    xsize, ysize : float
        half of the size of the IFU in x and y
        in arcseconds. Vertices are produced
        a +/-xsize and +/-ysize. Optional,
        default xsize=ysize=25.0
    other_cuts : dict
        dictionary of shot property and a
        2 element list of minimum and maximum
        allowed value
    """
    # Read in the survey file
    survey_hdf = tb.open_file(survey_hdf)
       
    # Read bad shots file
    table_bad_shots = Table.read(badshots_file, format="ascii", names = ["shotid"])
    bad_shots = array(table_bad_shots["shotid"])

    if specific_shot is not None:
        survey_ttable = survey_hdf.root.Survey.read_where('shotid == specific_shot')
    elif specific_field is not None:
        survey_ttable = survey_hdf.root.Survey.read_where('field == specific_field')
    else:   
        query = '(ra < ramax) & (ra > ramin) & (dec < decmax) & (dec > decmin)'

        for param, lims in iteritems(other_cuts):
            query += ' & ({:s} > {:f}) & ({:s} < {:f})'.format(param, lims[0], param, lims[1])

        print(query) 
        survey_ttable = survey_hdf.root.Survey.read_where(query)

    # Loop over the datevshots and see if there are bad amps
    polys = []
    shids = []
    for line in survey_ttable:
        print(line)
        # skip bad shots
        if line["shotid"] in bad_shots:
            continue

        date = line["date"]

        rot = 360.0 - (line["pa"] + 90.)
        tp = TangentPlane(line["ra"], line["dec"], rot)
        fplane_fn = "fplanes/{:d}_fplane.txt".format(date)
   
        if not isfile(fplane_fn):
           get_fplane(fplane_fn, datestr=str(date))
           fplane = FPlane(fplane_fn)
        else:
           fplane = FPlane(fplane_fn) 

        rect = [[-1.0*xsize, -1.0*xsize, xsize, xsize],
                [-1.0*ysize, ysize, ysize, -1.0*ysize]]
    
        for ifu in fplane.ifus: 
            x = array(rect[0]) + ifu.y  
            y = array(rect[1]) + ifu.x
            ra, dec = tp.xy2raDec(x, y)
            polys.append([ra[0], dec[0], ra[1], dec[1], 
                          ra[2], dec[2], ra[3], dec[3]])  
            shids.append(line["shotid"])
 
    # Should now have a list of polygons to output
    with open(output_fn, "w") as fp:
        for poly, shid in zip(polys, shids):
            fp.write("{:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:d}\n".format(*poly, shid))
def generate_bad_amp_mask(output_fn, survey_hdf, badamps_file, badshots_file, 
                          ramin, ramax, decmin, decmax, specific_shot=None):

    """
    Generate a Mangle-compatible list of ra/dec pairs corresponding
    to the corners of the bad amplifiers on the sky. The amplifiers
    are split into squares and rectangles to better follow their 
    shape.

    Parameters
    ----------
    output_fn : str
        a file to output the ra/dec corners to
    survey_hdf : str
        path to the survey HDF
    badamps_file : str
        path to the bad amps file
    ramin, ramax, decmin, decmax : float
        restrict the mask to a subregion
    specific_shot : str (Optional)
        overides the ra and dec range
        and instead only outputs a bad
        mask only for te given shotid

    """

    # Read in the survey file
    survey_hdf = tb.open_file(survey_hdf)
   
    if specific_shot is not None:
        # 20190209027
        survey_ttable = survey_hdf.root.Survey.read_where('shotid == specific_shot')
    else:    
        survey_ttable = survey_hdf.root.Survey.read_where('(ra < ramax) & (ra > ramin) & (dec < decmax) & (dec > decmin)')

    # Read in the bad amps
    table_bad_amps = Table.read(badamps_file, format="ascii", names = ["IFUSLOT", "AMP", "multiframe", 
                                                                       "start", "end"])
    # Read bad shots file
    table_bad_shots = Table.read(badshots_file, format="ascii", names = ["shotid"])
    bad_shots = array(table_bad_shots["shotid"])

    # Loop over the datevshots and see if there are bad amps
    polys = []
    amps = []
    for line in survey_ttable:

        date = line["date"]
        fplane_fn = "fplanes/{:d}_fplane.txt".format(date)

        if not isfile(fplane_fn):
           get_fplane(fplane_fn, datestr=str(date))
        else:
           fplane = FPlane(fplane_fn) 
 
        if line["shotid"] in bad_shots:
            # if shot bad mask all IFUS
            print("Masking whole bad shot found {:d}".format(line["shotid"]))
            bad_amps_here = Table()
            bad_amps_here["IFUSLOT"] = [int(x) for x in fplane.ifuslots]
            bad_amps_here["AMP"] = "AA"
        else:
            # otherwise just mask bad amps
            sel = (table_bad_amps["start"] <= date) & (table_bad_amps["end"] >= date) 
            bad_amps_here = table_bad_amps[sel]
    
        # If any, grab the focal plane and generate 
        # a tangent plane for the astrometry
        if len(bad_amps_here) > 0:
           print("{:d} has bad amps. Adding to mask".format(line["shotid"]))
    
           rot = 360.0 - (line["pa"] + 90.)
           tp = TangentPlane(line["ra"], line["dec"], rot)
                  
           for bad_amp in bad_amps_here:
               try:
                   ifu = fplane.by_ifuslot("{:03d}".format(bad_amp["IFUSLOT"]))
               except NoIFUError:
                   print("Warning. IFU {:d} not found for dateobs {:d}".format(bad_amp["IFUSLOT"], line["shotid"]))
                   continue
    
               if bad_amp["AMP"] == "AA":
                   # whole IFU with a little extra border
                   rects_to_mask = [[[-30.0, -30.0, 30.0, 30.0],
                                     [-30.0, 30.0, 30.0, -30.0]]]
               else:
                   # Check if the amps in this IFU are swapped around
                   ampkey = "{:03d}{:s}".format(bad_amp["IFUSLOT"], bad_amp["AMP"])
                   if ampkey in swapped_around_amps:
                       amp = swapped_around_amps[ampkey]
                   else:
                       amp = bad_amp["AMP"]
                    
                   # coordinates of amplifier for default dither and IFU cen
                   rects_to_mask = amp_corners[amp]
    
               for rect in rects_to_mask:
                   # Flip is correct
                   x = array(rect[0]) + ifu.y  
                   y = array(rect[1]) + ifu.x
                   ra, dec = tp.xy2raDec(x, y)
                   polys.append([ra[0], dec[0], ra[1], dec[1], 
                                 ra[2], dec[2], ra[3], dec[3]])
                  
                   amps.append(bad_amp["AMP"])
    
    # Should now have a list of polygons to output
    with open(output_fn, "w") as fp:
        for poly, amp in zip(polys, amps):
            fp.write("{:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:s}\n".format(*poly, amp))
def generate_bad_amp_mask_fits(output_fn, survey_hdf, badamps_fits, ramin, ramax, decmin, 
                               decmax, specific_shot = None):
    """
    Generate a Mangle-compatible list of ra/dec pairs corresponding
    to the corners of the bad amplifiers on the sky. The amplifiers
    are split into squares and rectangles to better follow their 
    shape.

    Parameters
    ----------
    output_fn : str
        a file to output the ra/dec corners to
    survey_hdf : str
        path to the survey HDF
    badamps_file : str
        path to the bad amps file
    ramin, ramax, decmin, decmax : float
        restrict the mask to a subregion
    specific_shot : str (Optional)
        overides the ra and dec range
        and instead only outputs a bad
        mask only for te given shotid
    """
    # Read in the survey file
    survey_hdf = tb.open_file(survey_hdf)
   
    if specific_shot is not None:
        # 20190209027
        survey_ttable = survey_hdf.root.Survey.read_where('shotid == specific_shot')
    else:    
        survey_ttable = survey_hdf.root.Survey.read_where('(ra < ramax) & (ra > ramin) & (dec < decmax) & (dec > decmin)')

    # Read in the bad amps
    table_bad_amps = Table.read(badamps_fits)
    table_bad_amps = table_bad_amps[table_bad_amps["flag"] == 0]
    pattern = re.compile("multi_[0-9]{3}_([0-9]{3})_[0-9]{3}_([RLU]{2})") 
    table_bad_amps["AMP"] = [pattern.findall(x)[0][1] for x in table_bad_amps["multiframe"]]
    table_bad_amps["IFUSLOT"] = [pattern.findall(x)[0][0] for x in table_bad_amps["multiframe"]]

    # Loop over the datevshots and see if there are bad amps
    polys = []
    amps = []
    for line in survey_ttable:
        bad_amps_here = table_bad_amps[table_bad_amps["shotid"] == line["shotid"]]

        # If any, grab the focal plane and generate 
        # a tangent plane for the astrometry
        if len(bad_amps_here) > 0:
            #print("{:d} has bad amps. Adding to mask".format(line["shotid"]))
    
            date = line["date"]
            fplane_fn = "fplanes/{:d}_fplane.txt".format(date)

            if not isfile(fplane_fn):
               get_fplane(fplane_fn, datestr=str(date))
            else:
               fplane = FPlane(fplane_fn) 
    

            rot = 360.0 - (line["pa"] + 90.)
            tp = TangentPlane(line["ra"], line["dec"], rot)
                   
            for bad_amp in bad_amps_here:
                try:
                    ifu = fplane.by_ifuslot("{:s}".format(bad_amp["IFUSLOT"]))
                except NoIFUError:
                    print("Warning. IFU {:s} not found for dateobs {:d}".format(bad_amp["IFUSLOT"], line["shotid"]))
                    continue
    
                # Check if the amps in this IFU are swapped around
                ampkey = "{:s}{:s}".format(bad_amp["IFUSLOT"], bad_amp["AMP"])
                if ampkey in swapped_around_amps:
                    amp = swapped_around_amps[ampkey]
                else:
                    amp = bad_amp["AMP"]
                 
                # coordinates of amplifier for default dither and IFU cen
                rects_to_mask = amp_corners[amp]
    
                for rect in rects_to_mask:
                    # Flip is correct
                    x = array(rect[0]) + ifu.y  
                    y = array(rect[1]) + ifu.x
                    ra, dec = tp.xy2raDec(x, y)
                    polys.append([ra[0], dec[0], ra[1], dec[1], 
                                  ra[2], dec[2], ra[3], dec[3]])
                   
                    amps.append(bad_amp["AMP"])
    
    # Should now have a list of polygons to output
    with open(output_fn, "w") as fp:
        for poly, amp in zip(polys, amps):
            fp.write("{:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:7.6f} {:s}\n".format(*poly, amp))
Beispiel #11
0
    def __init__(self,
                 datevshot,
                 release=None,
                 flim_model=None,
                 rad=3.5,
                 ffsky=False,
                 wavenpix=3,
                 d25scale=3.0,
                 verbose=False,
                 sclean_bad=True,
                 log_level="WARNING"):

        self.conf = HDRconfig()
        self.extractor = Extract()
        self.shotid = int(datevshot.replace("v", ""))
        self.date = datevshot[:8]
        self.rad = rad
        self.ffsky = ffsky
        self.wavenpix = wavenpix
        self.sclean_bad = sclean_bad

        logger = logging.getLogger(name="ShotSensitivity")
        logger.setLevel(log_level)

        if verbose:
            raise DeprecationWarning(
                "Using verbose is deprecated, set log_level instead")
            logger.setLevel("DEBUG")

        logger.info("shotid: {:d}".format(self.shotid))

        if not release:
            self.release = self.conf.LATEST_HDR_NAME
        else:
            self.release = release

        logger.info("Data release: {:s}".format(self.release))
        self.survey = Survey(survey=self.release)

        # Set up flux limit model
        self.f50_from_noise, self.sinterp, interp_sigmas \
                                       = return_flux_limit_model(flim_model,
                                                                 cache_sim_interp=False,
                                                                 verbose=verbose)

        # Generate astrometry for this shot
        survey_sel = (self.survey.shotid == self.shotid)
        self.shot_pa = self.survey.pa[survey_sel][0]
        self.shot_ra = self.survey.ra[survey_sel][0]
        self.shot_dec = self.survey.dec[survey_sel][0]
        rot = 360.0 - (self.shot_pa + 90.)
        self.tp = TangentPlane(self.shot_ra, self.shot_dec, rot)

        #Set up masking
        logger.info("Using d25scale {:f}".format(d25scale))
        self.setup_mask(d25scale)

        # Set up spectral extraction
        if release == "hdr1":
            fwhm = self.survey.fwhm_moffat[survey_sel][0]
        else:
            fwhm = self.survey.fwhm_virus[survey_sel][0]

        logger.info("Using Moffat PSF with FWHM {:f}".format(fwhm))
        self.moffat = self.extractor.moffat_psf(fwhm, 3. * rad, 0.25)
        self.extractor.load_shot(self.shotid, fibers=True, survey=self.release)

        # Set up the focal plane astrometry
        fplane_table = self.extractor.shoth5.root.Astrometry.fplane

        # Bit of a hack to avoid changing pyhetdex
        with NamedTemporaryFile(mode='w') as tpf:
            for row in fplane_table.iterrows():
                tpf.write(
                    "{:03d} {:8.5f} {:8.5f} {:03d} {:03d} {:03d} {:8.5f} {:8.5f}\n"
                    .format(row['ifuslot'], row['fpx'], row['fpy'],
                            row['specid'], row['specslot'], row['ifuid'],
                            row['ifurot'], row['platesc']))
            tpf.seek(0)
            self.fplane = FPlane(tpf.name)