def check_write_pha_fits_basic_roundtrip_crates(path):
    import pycrates
    ds = pycrates.CrateDataset(str(path), mode="r")

    assert ds.get_ncrates() == 2
    cr = ds.get_crate(2)

    assert cr.name == "SPECTRUM"
    assert cr.get_colnames() == ["CHANNEL", "COUNTS"]

    # undortunately crates auto-converts int32 to int64
    # (this is actually the underlying cxcdm module).
    #
    c0 = cr.get_column(0)
    assert c0.name == "CHANNEL"
    assert c0.values.dtype == np.int64
    assert c0.get_tlmin() == 1
    assert c0.get_tlmax() == 4

    c1 = cr.get_column(1)
    assert c1.name == "COUNTS"
    assert c1.values.dtype == np.int64
    # crates circa CIAO 4.14 doesn't return the correct values here;
    # it's returning int32 values and has got the negative value
    # wrong by 1
    assert c1.get_tlmin() <= -2147483647
    assert c1.get_tlmax() == 2147483647

    assert cr.get_key_value("HDUCLASS") == "OGIP"
    assert cr.get_key_value("HDUCLAS1") == "SPECTRUM"
    assert cr.get_key_value("HDUCLAS2") == "TOTAL"
    assert cr.get_key_value("HDUCLAS3") == "TYPE:I"
    assert cr.get_key_value("HDUCLAS4") == "COUNT"
    assert cr.get_key_value("HDUVERS") == "1.2.1"
    assert cr.get_key_value("TELESCOP") == "none"
    assert cr.get_key_value("INSTRUME") == "none"
    assert cr.get_key_value("FILTER") == "none"
    assert cr.get_key_value("POISSERR")

    assert cr.get_key_value("CHANTYPE") == "PI"
    assert cr.get_key_value("DETCHANS") == 4

    assert cr.get_key_value("SYS_ERR") == 0
    assert cr.get_key_value("QUALITY") == 0
    assert cr.get_key_value("GROUPING") == 0

    assert cr.get_key_value("AREASCAL") == pytest.approx(1.0)
    assert cr.get_key_value("BACKSCAL") == pytest.approx(1.0)
    assert cr.get_key_value("CORRSCAL") == 0

    for key in ["BACKFILE", "CORRFILE", "RESPFILE", "ANCRFILE"]:
        assert cr.get_key_value(key) == "none"

    # keywords we should have but currently don't
    for key in ["EXPOSURE"]:
        assert cr.get_key_value(key) is None
Beispiel #2
0
def apply_binning_to_image(binmap_file, image_file, root=None, clobber=False):
    """
    Applies the binning scheme from the binmap to an image of the same shape.

    Inputs:  binmap_file - fits file of map of bins (pixel values = bin numbers)
             image_file - fits file of image (must have the same shape as the binmap)
             root - root name of ouput map; defaults to image_file_root + "_binned"
             clobber - if True, overwrite any existing files

    Outputs: Binned version of the input image with each bin set to the mean value
             inside that bin, named "root.fits".

    """
    if root is None:
        root = os.path.splitext(image_file)[0] + '_binned'

    # Check if min bin is negative or starts or ends on the image boundary.
    # If so, assume it is not wanted (e.g., for wvt bin maps).
    #
    # Use a CrateDataset to read in the file to ensure any "extra"
    # blocks are retained. If this is not needed then
    # pycrates.read_file could be used here.
    #
    ds = pycrates.CrateDataset(binmap_file, mode='r')
    cr = ds.get_crate(0)
    assert isinstance(cr, pycrates.IMAGECrate)

    binimage = cr.get_image().values
    minbin = int(binimage.min())
    maxbin = int(binimage.max())
    if minbin < 0:
        minbin = 0
    inbin = numpy.where(binimage == minbin)
    if 0 in inbin[0] or numpy.size(binimage, 0) - 1 in inbin[0]:
        minbin += 1
    nbins = maxbin - minbin + 1

    icr = pycrates.read_file(image_file)
    assert isinstance(icr, pycrates.IMAGECrate)
    im = icr.get_image().values

    # Check that the binmap and image have the same shape
    if binimage.shape != im.shape:
        sys.exit('ERROR: Input binmap and image must have the same shape.')

    # make copy of the binmap
    binimage_out = binimage.astype(float)

    for i in range(nbins):
        inbin = numpy.where(binimage == i + minbin)
        binimage_out[inbin] = numpy.mean(im[inbin])

    cr.get_image().values = binimage_out
    ds.write(root + '.fits', clobber=clobber)
def check_csc_pha_roundtrip_crates(path):
    import pycrates
    ds = pycrates.CrateDataset(str(path), mode="r")

    assert ds.get_ncrates() == 2
    cr = ds.get_crate(2)

    assert cr.name == "SPECTRUM"
    assert cr.get_colnames() == ["CHANNEL", "COUNTS"]

    c0 = cr.get_column(0)
    assert c0.name == "CHANNEL"
    assert c0.values.dtype == np.int64
    assert c0.get_tlmin() == 1
    assert c0.get_tlmax() == 1024

    c1 = cr.get_column(1)
    assert c1.name == "COUNTS"
    assert c1.values.dtype == np.float32
    assert c1.get_tlmin() == pytest.approx(-3.4028235e+38)
    assert c1.get_tlmax() == pytest.approx(3.4028235e+38)

    assert cr.get_key_value("HDUCLASS") == "OGIP"
    assert cr.get_key_value("HDUCLAS1") == "SPECTRUM"
    assert cr.get_key_value("HDUCLAS2") == "TOTAL"
    assert cr.get_key_value("HDUCLAS3") == "COUNT"
    assert cr.get_key_value("HDUCLAS4") == "TYPE:I"
    assert cr.get_key_value("HDUVERS") == "1.1.0"
    assert cr.get_key_value("HDUVERS1") == "1.1.0"

    assert cr.get_key_value("POISSERR")

    assert cr.get_key_value("TELESCOP") == "CHANDRA"
    assert cr.get_key_value("INSTRUME") == "ACIS"
    assert cr.get_key_value("FILTER") == "none"

    assert cr.get_key_value("EXPOSURE") == pytest.approx(37664.157219191)
    assert cr.get_key_value("BACKSCAL") == pytest.approx(2.2426552620567e-06)
    assert cr.get_key_value("CORRFILE") == "none"
    assert cr.get_key_value("ANCRFILE") == "acisf01575_001N001_r0085_arf3.fits"
    assert cr.get_key_value("BACKFILE") == "acisf01575_001N001_r0085_pha3.fits"
    assert cr.get_key_value("RESPFILE") == "acisf01575_001N001_r0085_rmf3.fits"

    assert cr.get_key_value("CHANTYPE") == "PI"
    assert cr.get_key_value("DETCHANS") == 1024

    assert cr.get_key_value("CORRSCAL") == 0
    assert cr.get_key_value("SYS_ERR") == 0

    assert cr.get_key_value("AREASCAL") == pytest.approx(1.0)
    assert cr.get_key_value("QUALITY") == 0
    assert cr.get_key_value("GROUPING") == 0
Beispiel #4
0
def read_ensemble(infile):
    """Read in the ensemble file.

    Parameters
    ----------
    infile : str
        The master-hull file for this ensemble. It must have
        HULLMATCH and HULLLIST blocks.

    Notes
    -----
    At present only HULLLIST is looked for and used.

    """

    ds = pycrates.CrateDataset(infile, mode='r')

    # We are primarily interested in the HULLLIST block, but may
    # need to look at HULLMATCH for QA cases.
    #
    cr = ds.get_crate('HULLLIST')
    nhulls = cr.get_nrows()
    if nhulls == 0:
        raise IOError("No rows in HULLLIST block of {}".format(infile))

    ver = cr.get_key_value('CHSVER')
    ensemble = cr.get_key_value('ENSEMBLE')

    stacks = []
    for i in range(cr.get_key_value('STKIDNUM')):
        stacks.append(cr.get_key_value('STKID{:03d}'.format(i)))

    if stacks == []:
        raise IOError("No stacks stored in {}".format(infile))

    coords = []
    for nv, eqpos in zip(cr.NVERTEX.values,
                         cr.EQPOS.values):
        if nv == 0:
            cs = None
        else:
            cs = eqpos[:, :nv].copy()

        coords.append(cs)

    return {'infile': infile,
            'version': ver,
            'ensemble': ensemble,
            'stacks': stacks,
            'hulls': coords}
Beispiel #5
0
def transform_region_fits(infile, outfile, wcs_in, wcs_out):
    """Transform coordinates in FITS input file.

    This routine is called assuming the output file is to
    be clobbered if it already exists.
    """

    # Read in the whole file in case there are any other interesting
    # blocks in the file that should be retained.
    #
    ds = pycrates.CrateDataset(infile, mode='r')
    cr = ds.get_crate(2)
    assert isinstance(cr, pycrates.TABLECrate)

    # NOTE: the EQPOS column could be read directly, which would
    # avoid the need to convert the input file from physical to
    # celestial coordinates.
    #
    shapes = cr.get_column('SHAPE').values
    pos = cr.get_column('POS').values

    # Since the shapes are being processed on a row-by-row bases,
    # do not try and convert all the coordinates in one go (which
    # is supported by the apply and invert methods).
    #
    for i in xrange(0, cr.get_nrows()):

        shape = shapes[i].upper()
        if shape == 'POLYGON':

            coords_cel = wcs_in.apply(pos[i].T)
            coords_out = wcs_out.invert(coords_cel).T

            # Overwrite this row
            pos[i] = coords_out

        elif shape == 'ROTBOX':
            # It should be possible to encode ROTBOX in CXC FITS
            # region files.
            #
            sys.exit('ERROR: Sorry, only polygons are currently supported for FITS regions.')

    cr.get_column('POS').values = pos
    ds.write(outfile, clobber=True)
Beispiel #6
0
def read_table_blocks(arg, make_copy=False):

    filename = ''
    dataset = None
    close_dataset = False
    if isinstance(arg, pycrates.TABLECrate):
        filename = arg.get_filename()
        dataset = arg.get_dataset()
    elif isinstance(arg, pycrates.CrateDataset):
        filename = arg.get_filename()
        dataset = arg
    elif type(arg) in (str, unicode, numpy.str_):
        filename = arg
        dataset = pycrates.CrateDataset(arg)
        close_dataset = True
    else:
        raise IOErr('badfile', arg, "CrateDataset obj")

    # index of block number starts at 1
    blockidx = numpy.arange(dataset.get_ncrates()) + 1

    cols = {}
    hdr = {}

    for ii in blockidx:
        crate = dataset.get_crate(ii)
        hdr[ii] = {}
        names = crate.get_keynames()
        for name in names:
            hdr[ii][name] = crate.get_key_value(name)

        cols[ii] = {}
        # skip over primary
        if crate.name == 'PRIMARY':
            continue

        names = crate.get_colnames()
        for name in names:
            cols[ii][name] = crate.get_column(name).values

    if close_dataset:
        close_crate_dataset(dataset)
    return filename, cols, hdr
Beispiel #7
0
def create_mhull(outfile,
                 ensemble,
                 revision,
                 hullmd,
                 cpts,
                 mhulls,
                 polys,
                 mstzero=9000,
                 compzero=0,
                 creator=None):
    """Create the final master hull file.

    Parameters
    ----------
    outfile : str
        The name of the file to create. This must not exist.
    ensemble : str
        The ensemble name (e.g. 'ens0000100_001').
    revision : int
        The revision number of the finalized mhull file.
    hullmd : dict
        The mhull metadata read from the last revision (from
        chs_utils.read_master_hulls).
    cpts : list
        The component information.
    mhulls : dict
        The master hulls (the keys are the master id).
    polys : dict
        The master hull outlines (the keys are the master id)
    mstzero : int, optional
        The offset to apply to the Master_Id values for both the
        HULLMATCH and HULLLIST blocks. It must be 0 or greater.
        This value is written out as the MSTZERO keyword in the
        header.
    compzero : int, optional
        The component offset to apply to the component values
        when writing out the HULLMATCH block. It is expected to be
        0. This value is written out as the COMPZERO keyword
        in the header.
    creator : str or None
        The value for the CREATOR keyword, if set.

    Notes
    -----
    Deleted components are re-numbered to have unique master ids:
    -1, -2, ...
    """
    """DBG:
    print("***"); print(hullmd)
    print("***"); print(cpts)
    print("***"); print(mhulls)
    print("***"); print(polys)
    print(polys[1]['eqpos'].shape[1])
    """

    assert mstzero >= 0

    header = {
        'compzero': compzero,
        'mstzero': mstzero,
        'stacks': hullmd['stacks'],
        'svdqafile': hullmd['svdqafile'],
        'centroidfile': hullmd['centroidfile']
    }

    # track the deleted components
    ndel = 0

    hullmatch = defaultdict(list)
    for cpt in cpts:

        # A component can have multiple rows in this table
        mids = utils.get_user_setting(cpt, 'master_id')

        incl_in_centroid = utils.get_user_setting(cpt, 'include_in_centroid')

        # TODO: check that this hasn't already been done,
        #       because it should have
        #
        if len(mids) == 1:
            assert cpt['match_type'] in ['unambiguous', 'deleted'], cpt
        else:
            assert cpt['match_type'] == 'ambiguous', cpt

        for mid in mids:

            assert mid != 0, mids
            if mid < 0:
                ndel += 1

                mid = -1 * ndel
                nhulls = -1

            else:
                nhulls = mhulls[mid]['ncpts']

            hullmatch['master_id'].append(mid)

            hullmatch['nhulls'].append(nhulls)

            hullmatch['stackid'].append(cpt['stack'])
            hullmatch['component'].append(cpt['component'])
            hullmatch['match_type'].append(cpt['match_type'])

            # Do not really care about the area any longer (and am not
            # carrying the value around), so just use a terminal value.
            #
            try:
                hullmatch['area'].append(cpt['area'])
            except KeyError:
                hullmatch['area'].append(np.nan)

            hullmatch['eband'].append(cpt['eband'])
            hullmatch['likelihood'].append(cpt['likelihood'])

            hullmatch['man_code'].append(cpt['man_code'])

            hullmatch['mrg3rev'].append(cpt['mrg3rev'])
            hullmatch['include_in_centroid'].append(incl_in_centroid)
            hullmatch['stksvdqa'].append(cpt['stksvdqa'])

    # Be explicit in the ordering here
    #
    hulllist = defaultdict(list)
    nmasters = 0
    for mid in sorted(list(mhulls.keys())):

        mhull = mhulls[mid]
        status = utils.get_user_setting(mhull, 'useraction')
        if status == chs_status.DELETE:
            continue

        nmasters += 1

        poly = polys[mid]
        eqpos = poly['eqpos']
        nvertex = eqpos.shape[1]

        status = utils.get_user_setting(mhull, 'useraction')

        # This will have to be updated, but at present, when only
        # supporting deletion or acceptance of the proposed hull,
        # neither flag is set.
        #
        # TODO: fix this
        man_match = False
        man_reg = False

        hulllist['master_id'].append(mid)
        hulllist['status'].append(status)
        hulllist['base_stk'].append(poly['base_stk'])
        hulllist['manmatch'].append(man_match)
        hulllist['manreg'].append(man_reg)
        hulllist['nvertex'].append(nvertex)
        hulllist['nstkhull'].append(mhull['ncpts'])
        hulllist['eqpos'].append(eqpos)

    utils.create_mhull_file(ensemble,
                            revision,
                            outfile,
                            hullmatch,
                            hulllist,
                            header,
                            creator=creator)

    # Check we can read in both blocks, but no real validation
    # beyond that.
    #
    chk = pycrates.CrateDataset(outfile, mode='r')
    assert chk.get_ncrates() == 3
    assert chk.get_current_crate() == 2

    # Note: hack for nrows=None to mean empty table
    def valid(idx, name, nrows=None):
        bl = chk.get_crate(idx)
        assert bl.name == name
        if nrows is None:
            assert bl.get_shape() == (0, ), bl.get_shape()
        else:
            assert bl.get_nrows() == nrows, (bl.get_nrows(), nrows)

    # TODO: is len(cpts) actually correct when we have ambiguous
    # matches?
    valid(1, 'PRIMARY')
    valid(2, 'HULLMATCH', len(cpts))
    valid(3, 'HULLLIST', nmasters)
    chk = None
def check_write_pha_fits_with_extras_roundtrip_crates(path, etime, bscal):
    import pycrates
    ds = pycrates.CrateDataset(str(path), mode="r")

    assert ds.get_ncrates() == 2
    cr = ds.get_crate(2)

    assert cr.name == "SPECTRUM"
    assert cr.get_colnames() == ["CHANNEL", "COUNTS", "GROUPING", "QUALITY", "AREASCAL"]

    c0 = cr.get_column(0)
    assert c0.name == "CHANNEL"
    assert c0.values.dtype == np.int64
    assert c0.get_tlmin() == 1
    assert c0.get_tlmax() == 10

    c1 = cr.get_column(1)
    assert c1.name == "COUNTS"
    assert c1.values.dtype == np.float32
    assert c1.get_tlmin() == pytest.approx(-3.4028235e+38)
    assert c1.get_tlmax() == pytest.approx(3.4028235e+38)

    c2 = cr.get_column(2)
    assert c2.name == "GROUPING"
    assert c2.values.dtype == np.int16
    assert c2.get_tlmin() == -32768
    assert c2.get_tlmax() == 32767

    c3 = cr.get_column(3)
    assert c3.name == "QUALITY"
    assert c3.values.dtype == np.int16
    assert c3.get_tlmin() == -32768
    assert c3.get_tlmax() == 32767

    c4 = cr.get_column(4)
    assert c4.name == "AREASCAL"
    assert c4.values.dtype == np.float64
    assert c4.get_tlmin() < 1e308
    assert c4.get_tlmax() > 1e308

    assert cr.get_key_value("HDUCLASS") == "OGIP"
    assert cr.get_key_value("HDUCLAS1") == "SPECTRUM"
    assert cr.get_key_value("HDUCLAS2") == "TOTAL"
    assert cr.get_key_value("HDUCLAS3") == "COUNT"
    assert cr.get_key_value("HDUCLAS4") == "TYPE:I"
    assert cr.get_key_value("HDUVERS") == "1.2.1"
    assert cr.get_key_value("HDUVERS1") is None

    assert cr.get_key_value("POISSERR")

    assert cr.get_key_value("TELESCOP") == "CHANDRA"
    assert cr.get_key_value("INSTRUME") == "ACIS"
    assert cr.get_key_value("FILTER") == "NONE"

    assert cr.get_key_value("EXPOSURE") == pytest.approx(etime)
    assert cr.get_key_value("BACKSCAL") == pytest.approx(bscal)
    assert cr.get_key_value("CORRFILE") == "None"
    assert cr.get_key_value("ANCRFILE") == "made-up-ancrfile.fits"

    assert cr.get_key_value("CHANTYPE") == "PI"
    assert cr.get_key_value("DETCHANS") == 10

    assert cr.get_key_value("CORRSCAL") == 0

    for key in ["BACKFILE", "RESPFILE"]:
        assert cr.get_key_value(key) == "none"

    assert cr.get_key_value("SYS_ERR") == 0

    # We do not have these keywords as they are stored as columns
    for key in ["AREASCAL", "QUALITY", "GROUPING"]:
        assert cr.get_key_value(key) is None
Beispiel #9
0
def paint_map(binmap_file, fit_file, vars_to_map, root=None,
              fit2_file=None, second_comp=None, best_fit=None,
              Fprob=None, clobber=False):
    """
    Paints the binmap with the values from spectral fitting.

    Inputs:  binmap_file - fits file of map of bins (pixel values = bin numbers)
             fit_file - output of fit_spectra.py with fit results
             vars_to_map - variables to map. A map is created for each variable
             root - root name of ouput map(s); defaults to "output"
             fit2_file - output of fit_spectra.py with fit results for 2-comp model
             second_comp - name of component added to single-temperature model to
                           make the 2-comp model whose results are given in fit2_file
             best_fit - index (1 or 2) of "best" fit; used only when fit2_file and
                        second_comp are specified
             Fprob - F-test probability; used only when fit2_file and
                     second_comp are specified
             clobber - if True, overwrite any existing files

    Outputs: Writes maps using the output of "fit_spectra.py". The maps are called:
             root_kT_map.fits, root_Z_map.fits, etc.

    """
    # Check if min bin is negative or starts or ends on the image boundary.
    # If so, assume it is not wanted (e.g., for wvt bin maps).
    #
    # Using the pycrates.read_file routine returns a single block in
    # the file. This means when it is written out, any other blocks
    # are lost (other than associated GTI blocks). As it is not clear
    # whether this is an issue here, I use pycrates.CrateDataset to
    # read in the whole file.
    #
    ds = pycrates.CrateDataset(binmap_file, mode='r')

    # Assume the image data is in the first block
    cr = ds.get_crate(0)
    assert isinstance(cr, pycrates.IMAGECrate)

    binimage = cr.get_image().values
    minbin = int(binimage.min())
    maxbin = int(binimage.max())
    if minbin < 0:
        minbin = 0
    inbin = numpy.where(binimage == minbin)
    if 0 in inbin[0] or numpy.size(binimage, 0) - 1 in inbin[0]:
        minbin += 1
    nbins = maxbin - minbin + 1

    # Read in the fit results file and
    # calculate errors and check for upper limits
    data1 = read_fit_results(fit_file)
    if 'kT' in data1.dtype.names:
        kT_err = numpy.sqrt(data1['kT_lo']**2 + data1['kT_hi']**2)
        upper_limits = numpy.where(kT_err / data1['kT'] >= 1.0)
        kT_err[upper_limits] = data1['kT'][upper_limits]
    if 'Z' in data1.dtype.names:
        Z_err = numpy.sqrt(data1['Z_lo']**2 + data1['Z_hi']**2)
        upper_limits = numpy.where(Z_err / data1['Z'] >= 1.0)
        Z_err[upper_limits] = data1['Z'][upper_limits]
    if 'nH' in data1.dtype.names:
        nH_err = numpy.sqrt(data1['nH_lo']**2 + data1['nH_hi']**2)
        upper_limits = numpy.where(nH_err / data1['nH'] >= 1.0)
        nH_err[upper_limits] = data1['nH'][upper_limits]
    if 'norm' in data1.dtype.names:
        norm_err = numpy.sqrt(data1['norm_lo']**2 + data1['norm_hi']**2)
        upper_limits = numpy.where(norm_err / data1['norm'] >= 1.0)
        norm_err[upper_limits] = data1['norm'][upper_limits]

    if fit2_file is not None:
        data2 = read_fit_results(fit2_file, second_comp=second_comp)
        if 'kT' in data2.dtype.names:
            kT2_err = numpy.sqrt(data2['kT_lo']**2 + data2['kT_hi']**2)
            upper_limits = numpy.where(kT2_err / data2['kT'] >= 1.0)
            kT2_err[upper_limits] = data2['kT'][upper_limits]
        if 'kT1' in data2.dtype.names:
            kT1_err = numpy.sqrt(data2['kT1_lo']**2 + data2['kT1_hi']**2)
            upper_limits = numpy.where(kT1_err / data2['kT1'] >= 1.0)
            kT1_err[upper_limits] = data2['kT1'][upper_limits]
        if 'kT2' in data2.dtype.names:
            kT2_err = numpy.sqrt(data2['kT2_lo']**2 + data2['kT2_hi']**2)
            upper_limits = numpy.where(kT2_err / data2['kT2'] >= 1.0)
            kT2_err[upper_limits] = data2['kT2'][upper_limits]
        if 'Z' in data2.dtype.names:
            Z2_err = numpy.sqrt(data2['Z_lo']**2 + data2['Z_hi']**2)
            upper_limits = numpy.where(Z2_err / data2['Z'] >= 1.0)
            Z2_err[upper_limits] = data2['Z'][upper_limits]
        if 'Z1' in data2.dtype.names:
            Z1_err = numpy.sqrt(data2['Z1_lo']**2 + data2['Z1_hi']**2)
            upper_limits = numpy.where(Z1_err / data2['Z1'] >= 1.0)
            Z1_err[upper_limits] = data2['Z1'][upper_limits]
        if 'Z2' in data2.dtype.names:
            Z2_err = numpy.sqrt(data2['Z2_lo']**2 + data2['Z2_hi']**2)
            upper_limits = numpy.where(Z2_err / data2['Z2'] >= 1.0)
            Z2_err[upper_limits] = data2['Z2'][upper_limits]
        if 'nH' in data2.dtype.names:
            nH2_err = numpy.sqrt(data2['nH_lo']**2 + data2['nH_hi']**2)
            upper_limits = numpy.where(nH2_err / data2['nH'] >= 1.0)
            nH2_err[upper_limits] = data2['nH'][upper_limits]
        if 'norm' in data2.dtype.names:
            norm2_err = numpy.sqrt(data2['norm_lo']**2 + data2['norm_hi']**2)
            upper_limits = numpy.where(norm2_err / data2['norm'] >= 1.0)
            norm2_err[upper_limits] = data2['norm'][upper_limits]
        if 'norm1' in data2.dtype.names:
            norm1_err = numpy.sqrt(data2['norm1_lo']**2 + data2['norm1_hi']**2)
            upper_limits = numpy.where(norm1_err / data2['norm1'] >= 1.0)
            norm1_err[upper_limits] = data2['norm1'][upper_limits]
        if 'norm2' in data2.dtype.names:
            norm2_err = numpy.sqrt(data2['norm2_lo']**2 + data2['norm2_hi']**2)
            upper_limits = numpy.where(norm2_err / data2['norm2'] >= 1.0)
            norm2_err[upper_limits] = data2['norm2'][upper_limits]
        if 'plindx' in data2.dtype.names:
            plindx_err = numpy.sqrt(data2['plindx_lo']**2 + data2['plindx_hi']**2)
        if 'mdot' in data2.dtype.names:
            mkcnorm_err = numpy.sqrt(data2['mkcnorm_lo']**2 + data2['mkcnorm_hi']**2)
            upper_limits = numpy.where(mkcnorm_err / data2['mkcnorm'] >= 1.0)
            mkcnorm_err[upper_limits] = data2['mkcnorm'][upper_limits]

    nreg1 = len(data1)
    if fit2_file is not None:
        nreg2 = len(data2)
    else:
        nreg2 = nreg1

    # Make sure both have the same length, and they match the number of bins in the binmap
    if nreg1 != nreg2:
        sys.exit('ERROR: The two fits have a different number of regions. Please check the fitting results')
    if nreg1 != nbins or nreg2 != nbins:
        sys.exit('ERROR: Number of regions does not match the number of bins. Please check the fitting results')

    if best_fit is None:
        best_fit = numpy.ones(nreg1, dtype=int)

    # import pdb; pdb.set_trace()
    # make copies of the binmap as needed
    if 'kT' in vars_to_map:
        if second_comp == 'mekal' or second_comp == 'apec':
            binimage_kT1 = numpy.zeros(binimage.shape, dtype=float)
            binimage_kT2 = numpy.zeros(binimage.shape, dtype=float)
        else:
            binimage_kT = numpy.zeros(binimage.shape, dtype=float)
    if 'Z' in vars_to_map:
        if second_comp == 'mekal' or second_comp == 'apec':
            binimage_Z1 = numpy.zeros(binimage.shape, dtype=float)
            binimage_Z2 = numpy.zeros(binimage.shape, dtype=float)
        else:
            binimage_Z = numpy.zeros(binimage.shape, dtype=float)
    if 'plindx' in vars_to_map:
        binimage_plindx = numpy.zeros(binimage.shape, dtype=float)
    if 'mdot' in vars_to_map:
        binimage_mkcnorm = numpy.zeros(binimage.shape, dtype=float)
    if 'nH' in vars_to_map:
        binimage_nH = numpy.zeros(binimage.shape, dtype=float)

    if 'norm' in vars_to_map:
        if second_comp == 'mekal' or second_comp == 'apec':
            binimage_norm1 = numpy.zeros(binimage.shape, dtype=float)
            binimage_norm2 = numpy.zeros(binimage.shape, dtype=float)
        else:
            binimage_norm = numpy.zeros(binimage.shape, dtype=float)
    if 'fkT' in vars_to_map:
        if second_comp == 'mekal' or second_comp == 'apec':
            binimage_fkT1 = numpy.zeros(binimage.shape, dtype=float)
            binimage_fkT2 = numpy.zeros(binimage.shape, dtype=float)
        else:
            binimage_fkT = numpy.zeros(binimage.shape, dtype=float)
    if 'fZ' in vars_to_map:
        if second_comp == 'mekal' or second_comp == 'apec':
            binimage_fZ1 = numpy.zeros(binimage.shape, dtype=float)
            binimage_fZ2 = numpy.zeros(binimage.shape, dtype=float)
        else:
            binimage_fZ = numpy.zeros(binimage.shape, dtype=float)

    if 'fplindx' in vars_to_map:
        binimage_fplindx = numpy.zeros(binimage.shape, dtype=float)
    if 'fmdot' in vars_to_map:
        binimage_fmkcnorm = numpy.zeros(binimage.shape, dtype=float)
    if 'fnH' in vars_to_map:
        binimage_fnH = numpy.zeros(binimage.shape, dtype=float)

    if 'fnorm' in vars_to_map:
        if second_comp == 'mekal' or second_comp == 'apec':
            binimage_fnorm1 = numpy.zeros(binimage.shape, dtype=float)
            binimage_fnorm2 = numpy.zeros(binimage.shape, dtype=float)
        else:
            binimage_fnorm = numpy.zeros(binimage.shape, dtype=float)

    if 'chi2' in vars_to_map:
        binimage_chi2 = numpy.zeros(binimage.shape, dtype=float)

    if Fprob is not None:
        binimage_Fprob = numpy.zeros(binimage.shape, dtype=float)

    for k in range(nreg1):
        # First, make sure the loop index matches the reg_id of the region of interest (i.e. to catch entries that are out order or missing)
        if k + minbin == data1['reg_id'][k]:
            i = k
        else:
            i = data1['reg_id'][k]

        # find all pixels in region of interest
        inbin = numpy.where(binimage == i + minbin)
        if 'kT' in vars_to_map:
            if second_comp == 'mekal' or second_comp == 'apec':
                if data2['kT1'][i] <= data2['kT2'][i]:
                    binimage_kT1[inbin] = data2['kT1'][i]
                    binimage_kT2[inbin] = data2['kT2'][i]
                else:
                    binimage_kT1[inbin] = data2['kT2'][i]
                    binimage_kT2[inbin] = data2['kT1'][i]
                if best_fit[i] == 1:  # single-temp model preferred
                    binimage_kT1[inbin] = data1['kT'][i]
                    binimage_kT2[inbin] = data1['kT'][i]
            else:
                if best_fit[i] == 1:
                    binimage_kT[inbin] = data1['kT'][i]
                else:
                    binimage_kT[inbin] = data2['kT'][i]
        if 'Z' in vars_to_map:
            if second_comp == 'mekal' or second_comp == 'apec':
                if data2['kT1'][i] <= data2['kT2'][i]:
                    binimage_Z1[inbin] = data2['Z1'][i]
                    binimage_Z2[inbin] = data2['Z2'][i]
                else:
                    binimage_Z1[inbin] = data2['Z2'][i]
                    binimage_Z2[inbin] = data2['Z1'][i]
                if best_fit[i] == 1:
                    binimage_Z1[inbin] = data1['Z'][i]
                    binimage_Z2[inbin] = data1['Z'][i]
            else:
                if best_fit[i] == 1:
                    binimage_Z[inbin] = data1['Z'][i]
                else:
                    binimage_Z[inbin] = data2['Z'][i]
        if 'plindx' in vars_to_map:
            if best_fit[i] == 1:
                binimage_plindx[inbin] = 0.0
            else:
                binimage_plindx[inbin] = data2['plindx'][i]
        if 'mdot' in vars_to_map:
            if best_fit[i] == 1:
                binimage_mkcnorm[inbin] = 0.0
            else:
                binimage_mkcnorm[inbin] = data2['mkcnorm'][i]
        if 'nH' in vars_to_map:
            if best_fit[i] == 1:
                binimage_nH[inbin] = data1['nH'][i]
            else:
                binimage_nH[inbin] = data2['nH'][i]
        if 'norm' in vars_to_map:
            if second_comp == 'mekal' or second_comp == 'apec':
                if data2['kT1'][i] <= data2['kT2'][i]:
                    binimage_norm1[inbin] = data2['norm1'][i]
                    binimage_norm2[inbin] = data2['norm2'][i]
                else:
                    binimage_norm1[inbin] = data2['norm2'][i]
                    binimage_norm2[inbin] = data2['norm1'][i]
                if best_fit[i] == 1:  # single-temp model preferred
                    binimage_norm1[inbin] = data1['norm'][i]
                    binimage_norm2[inbin] = data1['norm'][i]
            else:
                if best_fit[i] == 1:
                    binimage_norm[inbin] = data1['norm'][i]
                else:
                    binimage_norm[inbin] = data2['norm'][i]
        if 'fkT' in vars_to_map:
            if second_comp == 'mekal' or second_comp == 'apec':
                if data2['kT1'][i] <= data2['kT2'][i]:
                    binimage_fkT1[inbin] = kT1_err[i]
                    binimage_fkT2[inbin] = kT2_err[i]
                else:
                    binimage_fkT1[inbin] = kT2_err[i]
                    binimage_fkT2[inbin] = kT1_err[i]
                if best_fit[i] == 1:  # single-temp model preferred
                    binimage_fkT1[inbin] = kT_err[i]
                    binimage_fkT2[inbin] = kT_err[i]
            else:
                if best_fit[i] == 1:
                    binimage_fkT[inbin] = kT_err[i]
                else:
                    binimage_fkT[inbin] = kT2_err[i]
        if 'fZ' in vars_to_map:
            if second_comp == 'mekal' or second_comp == 'apec':
                if data2['kT1'][i] <= data2['kT2'][i]:
                    binimage_fZ1[inbin] = Z1_err[i]
                    binimage_fZ2[inbin] = Z2_err[i]
                else:
                    binimage_fZ1[inbin] = Z2_err[i]
                    binimage_fZ2[inbin] = Z1_err[i]
                if best_fit[i] == 1:  # single-temp model preferred
                    binimage_fZ1[inbin] = Z_err[i]
                    binimage_fZ2[inbin] = Z_err[i]
            else:
                if best_fit[i] == 1:
                    binimage_fZ[inbin] = Z_err[i]
                else:
                    binimage_fZ[inbin] = Z2_err[i]
        if 'fplindx' in vars_to_map:
            if best_fit[i] == 1:
                binimage_fplindx[inbin] = 0.0
            else:
                binimage_fplindx[inbin] = plindx_err[i]
        if 'fmdot' in vars_to_map:
            if best_fit[i] == 1:
                binimage_fmkcnorm[inbin] = 0.0
            else:
                binimage_fmkcnorm[inbin] = mkcnorm_err[i]
        if 'fnH' in vars_to_map:
            if best_fit[i] == 1:
                binimage_fnH[inbin] = nH_err[i]
            else:
                binimage_fnH[inbin] = nH2_err[i]
        if 'fnorm' in vars_to_map:
            if second_comp == 'mekal' or second_comp == 'apec':
                if data2['norm1'][i] <= data2['norm2'][i]:
                    binimage_fnorm1[inbin] = norm1_err[i]
                    binimage_fnorm2[inbin] = norm2_err[i]
                else:
                    binimage_fnorm1[inbin] = norm2_err[i]
                    binimage_fnorm2[inbin] = norm1_err[i]
                if best_fit[i] == 1:  # single-temp model preferred
                    binimage_fnorm1[inbin] = norm_err[i]
                    binimage_fnorm2[inbin] = norm_err[i]
            else:
                if best_fit[i] == 1:
                    binimage_fnorm[inbin] = norm_err[i]
                else:
                    binimage_fnorm[inbin] = norm2_err[i]
        if 'chi2' in vars_to_map:
            if best_fit[i] == 1:
                binimage_chi2[inbin] = data1['chi2'][i]
            else:
                binimage_chi2[inbin] = data2['chi2'][i]

        if Fprob is not None:
            binimage_Fprob[inbin] = Fprob[i]

    if root is None:
        root = 'output'

    def saveimg(idata, suffix):
        """Write out the data using the given suffix."""
        cr.get_image().values = idata
        ds.write(root + suffix, clobber=clobber)

    has_2cpt = second_comp == 'mekal' or second_comp == 'apec'
    if 'kT' in vars_to_map:
        if has_2cpt:
            saveimg(binimage_kT1, '_kT1_map.fits')
            saveimg(binimage_kT2, '_kT2_map.fits')
        else:
            saveimg(binimage_kT, '_kT_map.fits')
    if 'Z' in vars_to_map:
        if has_2cpt:
            saveimg(binimage_Z1, '_Z1_map.fits')
            saveimg(binimage_Z2, '_Z2_map.fits')
        else:
            saveimg(binimage_Z, '_Z_map.fits')
    if 'plindx' in vars_to_map:
        saveimg(binimage_plindx, '_plindx_map.fits')
    if 'mdot' in vars_to_map:
        saveimg(binimage_mkcnorm, '_mdot_map.fits')
    if 'nH' in vars_to_map:
        saveimg(binimage_nH, '_nH_map.fits')
    if 'norm' in vars_to_map:
        if has_2cpt:
            saveimg(binimage_norm1, '_norm1_map.fits')
            saveimg(binimage_norm2, '_norm2_map.fits')
        else:
            saveimg(binimage_norm, '_norm_map.fits')
    if 'fkT' in vars_to_map:
        if has_2cpt:
            saveimg(binimage_fkT1, '_fkT1_map.fits')
            saveimg(binimage_fkT2, '_fkT2_map.fits')
        else:
            saveimg(binimage_fkT, '_fkT_map.fits')
    if 'fZ' in vars_to_map:
        if has_2cpt:
            saveimg(binimage_fZ1, '_fZ1_map.fits')
            saveimg(binimage_fZ2, '_fZ2_map.fits')
        else:
            saveimg(binimage_fZ, '_fZ_map.fits')
    if 'fnorm' in vars_to_map:
        if has_2cpt:
            saveimg(binimage_fnorm1, '_fnorm1_map.fits')
            saveimg(binimage_fnorm2, '_fnorm2_map.fits')
        else:
            saveimg(binimage_fnorm, '_fnorm_map.fits')
    if 'fplindx' in vars_to_map:
        saveimg(binimage_fplindx, '_fplindx_map.fits')
    if 'fmdot' in vars_to_map:
        saveimg(binimage_fmkcnorm, '_fmdot_map.fits')
    if 'fnH' in vars_to_map:
        saveimg(binimage_fnH, '_fnH_map.fits')
    if 'chi2' in vars_to_map:
        saveimg(binimage_chi2, '_chi2_map.fits')
    if Fprob is not None:
        saveimg(binimage_Fprob, '_Ftest_map.fits')
Beispiel #10
0
def read_wcs_transform(infile, blocknum):
    """Read in the physical to celestial transform from the file.

    Return a WCSTransform object that represents the physical to
    celestial - e.g. SKY (Chandra)/POS (XMM) to Ra,Dec - from the
    given block.

    Parameters
    ----------
    infile : str
        The name of the file to read in.
    blocknum : int
        The block number to use: this follows CFITSIO convention,
        rather than the CXC DM, and numbers the first block 0.
        If the block is a table then the column "EQPOS" and then
        "EQSRC" is searched for. If it is an image then the first
        axis that returns a WCSTransform is used. If no transform
        is found an IOError is raised
        (and then hopefully the logic here tweaked to support the
        "problem" file structure).

    Returns
    -------
    tr
        A pytransform.WCSTransform object.

    Notes
    -----
    It attempts to be somewhat general, and support images or tables,
    but it does rely on the FITS header information being "Chandra
    like".
    """

    # The get_crate routine uses 0 to indicate the "interesting"
    # (or default) block to open, so need to add 1 to blocknum.
    #
    ds = pycrates.CrateDataset(infile, mode='r')
    cr = ds.get_crate(blocknum + 1)

    if isinstance(cr, pycrates.TABLECrate):
        for name in ['EQPOS', 'EQSRC']:
            try:
                tr = cr.get_transform(name)
            except ValueError:
                continue

            # In older CIAO's this could be WCSTanTransform
            if isinstance(tr, pytransform.WCSTransform):
                return tr

        raise IOError("No transform found from table {}".format(infile))

    elif isinstance(cr, pycrates.IMAGECrate):

        # loop through all the axes; can probably guarantee
        # that the first one is not relevant, but just in case.
        #
        for axis in cr.get_axisnames():
            try:
                tr = cr.get_transform(axis)
            except KeyError:
                continue

            # In older CIAO's this could be WCSTanTransform
            if isinstance(tr, pytransform.WCSTransform):
                return tr

        raise IOError("No transform found from image {}".format(infile))

    else:
        raise IOError("Unexpected crate: {}".format(type(cr)))