Пример #1
0
 def read_rmf(self, rmffile):
     """Open rmf file containing the response matrix."""
     message.proc_start("Read RMF response matrix")
     stat = self.resp.read(rmffile)
     if stat != 0:
         message.proc_end(stat)
         message.error("Unable to read RMF/RSP file.")
         return
     else:
         self.input_resp = True
         message.proc_end(stat)
Пример #2
0
    def __read_pha2(self, pha2file, grating, bkgsubtract=True):
        """Method to read a PHA type II file.

        :param pha2file: PHA type II file name to read.
        :type pha2file: str
        :param grating: Name of the grating to read (HETG, METG or LETG).
        :type grating: str
        :param bkgsubtract: Subtract the background?
        :type bkgsubtract: bool
        """

        # Initialize PHA2 file type

        spec = Pha2()

        # Is the source spectrum there?
        message.proc_start("Read source spectrum")
        if os.path.isfile(pha2file):
            stat = spec.read(pha2file, background=bkgsubtract)
            if stat != 0:
                message.proc_end(stat)
                message.error("Failed to read source spectrum.")
                return 1
            else:
                message.proc_end(stat)
        else:
            message.proc_end(1)
            message.error(
                "Spectrum file {0} not found in path.".format(pha2file))
            return 1

        # Convert grating name to number
        if grating == 'HETG':
            ngrating = 1
        elif grating == 'METG':
            ngrating = 2
        elif grating == 'LETG':
            ngrating = 3
        else:
            message.error("Unsupported grating: '{0}'.".format(grating))
            return 1

        # Combine spectra from a single grating
        message.proc_start("Combining orders of the spectrum")
        (src, bkg) = spec.combine_orders(ngrating)
        if isinstance(src, Pha) and isinstance(bkg, Pha):
            message.proc_end(0)
        else:
            message.proc_end(1)
            return 1

        return src, bkg
Пример #3
0
 def read_corr(self, corrfile):
     """Read correction file if specified."""
     if corrfile is not None:
         message.proc_start("Read correction spectrum")
         stat = self.corr.read(corrfile)
         if stat != 0:
             message.proc_end(stat)
             message.error("Unable to read CORR file.")
             return
         else:
             message.proc_end(stat)
             self.input_corr = True
     else:
         message.error("No correction file specified.")
Пример #4
0
 def read_arf(self, arffile):
     """Read arf file containing the effective area."""
     if arffile is not None:
         message.proc_start("Read ARF effective area")
         stat = self.area.read(arffile)
         if stat != 0:
             message.proc_end(stat)
             message.error("Unable to read ARF file.")
             return
         else:
             self.input_area = True
             message.proc_end(stat)
     else:
         message.error("No effective area filename specified.")
Пример #5
0
 def read_background_pha(self, bkgfile):
     """Open a pha file containing the background spectrum (if specified)."""
     if bkgfile is not None:
         message.proc_start("Read background PHA spectrum")
         stat = self.back.read(bkgfile)
         if stat != 0:
             message.proc_end(stat)
             message.error("Unable to read background PHA file.")
             return
         else:
             self.input_back = True
             message.proc_end(stat)
     else:
         message.error("No background filename specified.")
Пример #6
0
    def __rmflist_to_res(self, rmflist, arflist):
        """Convert a list of compatible rmf and arf file into one res file. This is convenient for combining responses
        that are provided separately, like the Transmission Grating spectra from Chandra."""

        if len(rmflist) != len(arflist):
            message.error("ARF list and RMF list do not have the same length.")
            return 0

        rmfobjs = np.zeros(len(rmflist), dtype=object)
        arfobjs = np.zeros(len(arflist), dtype=object)
        rmf_orders = np.zeros(len(rmflist), dtype=int)
        arf_orders = np.zeros(len(arflist), dtype=int)

        i = 0
        for file in rmflist:
            message.proc_start("Reading response for order")
            rmf = Rmf()
            rmf.read(file)
            rmf_orders[i] = rmf.Order
            print(str(rmf_orders[i])+"  ", end='')
            if len(np.where(rmf_orders == rmf.Order)) != 1:
                message.error("There are two response files with the same order.")
                message.proc_end(1)
                return 1
            else:
                rmfobjs[i] = rmf
                message.proc_end(0)
            i = i + 1

        i=0
        for file in arflist:
            message.proc_start("Reading effective area for order")
            arf = Arf()
            arf.read(file)
            arf_orders[i] = arf.Order
            print(str(arf_orders[i])+"  ", end='')
            if len(np.where(arf_orders == arf.Order)) != 1:
                message.error("There are two effective area files for the same order.")
                message.proc_end(1)
                return 1
            else:
                arfobjs[i] = arf
                message.proc_end(0)
            i = i + 1

        arfsort = np.argsort(arf_orders)
        rmfsort = np.argsort(rmf_orders)

        # Calculate first response:
        res = rmf_to_res(rmfobjs[rmfsort[0]],arf=arfobjs[arfsort[0]])

        # Append the components from the other responses
        for i in np.arange(len(rmfsort)-1)+1:
            restmp = rmf_to_res(rmfobjs[rmfsort[i]],arf=arfobjs[arfsort[i]])
            res.append_component(restmp)

        return res
Пример #7
0
    def read_source_pha(self, phafile):
        """Open a pha file containing the source spectrum."""
        message.proc_start("Read source PHA spectrum")
        stat = self.spec.read(phafile)
        if stat != 0:
            message.proc_end(stat)
            message.error("Unable to read source PHA file.")
            return
        else:
            self.input_spec = True
            message.proc_end(stat)

        # Check if first channel of spectrum is zero:
        if self.spec.FirstChannel == 0:
            self.first_channel_zero = True
        else:
            self.first_channel_zero = False
Пример #8
0
    def ogip_to_spex(self):
        """Convert the OGIP part of the OGIP region to spo and res objects."""

        # Check the consistency between the OGIP files
        stat = self.check_ogip()
        if stat != 0:
            message.error("Check of OGIP files failed.")
            return 1

        # Convert OGIP spectra to SPO object:
        if self.input_spec and self.input_resp:
            message.proc_start("Convert OGIP spectra to spo format")
            spo = pha_to_spo(self.spec,
                             self.resp,
                             back=self.back,
                             corr=self.corr)

            if isinstance(spo, Spo):
                self.spo = spo
                message.proc_end(0)
            else:
                message.proc_end(1)
                message.error("OGIP to SPO failed.")
                return 1

            message.proc_start("Convert OGIP response to res format")
            res = rmf_to_res(self.resp, arf=self.area)

            if isinstance(res, Res):
                self.res = res
                message.proc_end(0)
            else:
                message.error("OGIP to RES failed.")
                return 1
        else:
            message.error("Source spectrum or response not specified.")
            return 1

        # Correct for possible shifts in channels if first channel is 0
        if self.resp.FirstChannel == 0:
            self.correct_possible_shift()

        # Check output spo object
        checkspo = self.spo.check()
        if checkspo != 0:
            message.error("Resulting spo file is not OK.")
            return 1

        # Check
        checkres = self.res.check()
        if checkres != 0:
            message.error("Resulting res file is not OK.")
            return 1

        return 0
Пример #9
0
def clean_region(reg):
    """Remove bad channels and channels with zero response from the region.

    :param reg: Input Region object.
    :type reg: pyspextools.io.Region
    """

    if not isinstance(reg, Region):
        message.error("The input object is not of type Region.")
        return -1

    if reg.spo.empty:
        message.error("The input spo object is empty.")
        return -1

    if reg.res.empty:
        message.error("The input spo object is empty.")
        return -1

    message.proc_start("Identify bad channels in spectrum and response matrix and re-index matrix")

    (chanmask, groupmask, respmask) = __get_bad_channel_masks(reg)

    if not isinstance(chanmask, np.ndarray):
        return -1

    message.proc_end(0)

    # Print number of good and bad channels
    goodchan = np.sum(chanmask)
    badchan = chanmask.size - goodchan

    print("Number of good channels: {0}".format(goodchan))
    print("Number of bad channels:  {0}".format(badchan))

    if goodchan == 0:
        message.error("All channels appear to be bad. Please check your input files.")
        return -1

    message.proc_start("Removing bad channels from spectral region")

    # Fix binning issues first. Make sure bin ends before bad channel and starts after bad channel.
    for i in np.arange(reg.spo.nchan):
        if not chanmask[i]:
            if i != 0:
                reg.spo.last[i-1] = True
            if i != reg.spo.nchan - 1:
                reg.spo.first[i+1] = True

    spo = reg.spo

    spo.echan1 = reg.spo.echan1[chanmask]
    spo.echan2 = reg.spo.echan2[chanmask]
    spo.tints = reg.spo.tints[chanmask]
    spo.ochan = reg.spo.ochan[chanmask]
    spo.dochan = reg.spo.dochan[chanmask]
    spo.mbchan = reg.spo.mbchan[chanmask]
    spo.dbchan = reg.spo.dbchan[chanmask]
    spo.brat = reg.spo.brat[chanmask]
    spo.ssys = reg.spo.ssys[chanmask]
    spo.bsys = reg.spo.bsys[chanmask]
    spo.used = reg.spo.used[chanmask]
    spo.first = reg.spo.first[chanmask]
    spo.last = reg.spo.last[chanmask]

    # Count the number of good channels
    for i in np.arange(spo.nregion):
        spo.nchan[i] = np.sum(chanmask)

    # Check the consistency of the new object
    stat = spo.check()

    # Show result to user
    message.proc_end(stat)

    # Copy the filtered object to the original region
    reg.spo = spo

    # Print number of good and bad groups
    badgroup = groupmask.size - np.sum(groupmask)

    print("Number of original groups:       {0}".format(groupmask.size))
    print("Number of zero-response groups:  {0}".format(badgroup))

    # Print number of removed response elements
    badelements = respmask.size - np.sum(respmask)

    print("Number of original response elements:  {0}".format(respmask.size))
    print("Number of bad response elements:       {0}".format(badelements))

    message.proc_start("Removing bad channels from response matrix")

    # Mask response array
    reg.res.resp = reg.res.resp[respmask]
    if reg.res.resp_der:
        reg.res.dresp = reg.res.dresp[respmask]

    # Mask group arrays
    reg.res.eg1 = reg.res.eg1[groupmask]
    reg.res.eg2 = reg.res.eg2[groupmask]
    reg.res.ic1 = reg.res.ic1[groupmask]
    reg.res.ic2 = reg.res.ic2[groupmask]
    reg.res.nc = reg.res.nc[groupmask]
    if reg.res.area_scal:
        reg.res.relarea = reg.res.relarea[groupmask]

    eg_start = 0

    for icomp in np.arange(reg.res.ncomp):
        reg.res.nchan[icomp] = np.sum(chanmask)
        eg_end = reg.res.neg[icomp] + eg_start
        reg.res.neg[icomp] = np.sum(groupmask[eg_start:eg_end])
        eg_start = eg_end + 1

    stat = reg.res.check()

    message.proc_end(stat)

    return reg
Пример #10
0
    def check_ogip(self):
        """Check consistency of the OGIP files in this class."""

        # Check consistency of the source spectrum
        message.proc_start("Check OGIP source spectrum")

        spec = self.spec.check()

        if spec != 0:
            message.proc_end(1)
            print(spec)
            return 1
        else:
            message.proc_end(0)

        # Check consistency of the background spectrum
        if self.input_back:
            # Is the background file in order?
            message.proc_start("Check OGIP background spectrum")
            back = self.back.check()
            if back != 0:
                message.proc_end(1)
                print(back)
                return 1
            back = self.spec.checkCompatibility(self.back)
            if back != 0:
                message.proc_end(1)
                message.error(
                    "Background spectrum is not compatible with source spectrum."
                )
                return 1
            message.proc_end(0)

        # Check consistency of the correction spectrum
        if self.input_corr:
            # Is the correction file in order?
            message.proc_start("Check OGIP correction spectrum")
            corr = self.corr.check()
            if corr != 0:
                message.proc_end(1)
                print(corr)
                return 1
            corr = self.spec.checkCompatibility(self.corr)
            if corr != 0:
                message.proc_end(1)
                message.error(
                    "Correction spectrum is not compatible with source spectrum."
                )
                return 1
            message.proc_end(0)

        # Check rmf file
        message.proc_start("Check OGIP response matrix")
        resp = self.resp.check()
        if resp != 0:
            message.proc_end(1)
            print(resp)
            return 1
        message.proc_end(0)

        # Check consistency between ARF and RMF
        if self.input_area:
            # Is the effective area file in order?
            message.proc_start("Check OGIP effective area file")
            area = self.area.check()
            if area != 0:
                message.proc_end(1)
                print(area)
                return 1
            # Is the effective area consistent with the response?
            area = self.resp.checkCompatibility(self.area)
            if area != 0:
                message.proc_end(1)
                message.error(
                    "Effective area file is not consistent with the provided response file."
                )
                return 1
            message.proc_end(0)

        return 0
Пример #11
0
    def correct_possible_shift(self):
        """See if there is a shift in the response array. When the spectral channels start at 0 in OGIP responses,
        then there is a possibility that the channel indexing needs to be shifted by 1. The SPEX format first
        channel should always be 1. Run this function after the conversion of OGIP to SPEX had taken place
        and if the first channel in the OGIP spectrum is 0.
        The ogip_to_spex method calls this function by default."""

        if not isinstance(self.spo, Spo) or not isinstance(self.res, Res):
            message.error(
                "Could not find spo and res information in this region.")
            return 1

        # This code is not prepared for situations where the channel arrays are swapped, like for RGS.
        # Luckily, the combination of a 0 first channel and a swapped array are rare.
        # In that case, we stop with a warning:
        if self.spo.swap:
            message.warning(
                "Not auto-detecting shifts in the response array. ")
            return 1

        # Start with the OGIP response object
        # Check the channel indices for the first group with useful data
        # Find such a group first in OGIP response:
        i = 0
        while True:
            # Find an energy bin with at least one response group.
            if self.resp.NumberGroups[i] == 0:
                i = i + 1
            else:
                break

        # Save the energy boundaries and calculate the average model energy for the group
        elow = self.resp.LowEnergy[i]
        ehigh = self.resp.HighEnergy[i]
        target_energy = (elow + ehigh) / 2.0

        # For this group, save the first channel of the group (F_CHAN)
        fchan = self.resp.FirstChannelGroup[0]

        # Find the array index for this channel number
        j = 0
        while True:
            if self.resp.Channel[j] != fchan:
                j = j + 1
            else:
                break

        # For this array index, the corresponding channel energy boundaries should be:
        lchan = self.resp.ChannelLowEnergy[j]
        hchan = self.resp.ChannelHighEnergy[j]
        target_channel = (lchan + hchan) / 2.0

        # Now find the same group and channel in the SPEX format objects
        # Find the group in the res object for the same model energy bin (with target_energy):
        s = 0
        while True:
            if self.res.eg1[s] < target_energy and self.res.eg2[
                    s] > target_energy:
                break
            else:
                s = s + 1

        # Find the target channel number in spo file
        t = 0
        while True:
            if self.spo.echan1[t] < target_channel and self.spo.echan2[
                    t] > target_channel:
                break
            else:
                t = t + 1

        # Corresponding first channel of this group according to SPEX format
        ic1 = self.res.ic1[s]

        # Calculate the difference between the SPEX channel number and the OGIP one.
        shift = int(t + 1 - ic1)

        if shift != 0:
            message.warning("Shift in response array detected.")
            message.proc_start(
                "Trying to shift indices with {0} ".format(shift))
            stat = self.res.channel_shift(shift)
            message.proc_end(stat)
        else:
            print("No shift in response array detected. Continuing...")

        return 0
Пример #12
0
    def ogip_to_spex(self):
        """Convert the OGIP part of the OGIP region to spo and res objects."""

        # Check the consistency between the OGIP files
        stat = self.check_ogip()
        if stat != 0:
            message.error("Check of OGIP files failed.")
            return 1

        # Convert OGIP spectra to SPO object:
        if self.input_spec and self.input_resp:
            message.proc_start("Convert OGIP spectra to spo format")
            spo = pha_to_spo(self.spec,
                             self.resp,
                             back=self.back,
                             corr=self.corr,
                             save_grouping=self.save_grouping)

            if isinstance(spo, Spo):
                self.spo = spo
                message.proc_end(0)
            else:
                message.proc_end(1)
                message.error("OGIP to SPO failed.")
                return 1

            message.proc_start("Convert OGIP response to res format")
            res = rmf_to_res(self.resp, matext=0, arf=self.area)
            if self.resp.NumberMatrixExt > 1:
                for i in range(self.resp.NumberMatrixExt - 1):
                    rescomp = rmf_to_res(self.resp,
                                         matext=i + 1,
                                         arf=self.area)
                    res.append_component(rescomp, iregion=1, isector=1)

            if isinstance(res, Res):
                self.res = res
                message.proc_end(0)
            else:
                message.error("OGIP to RES failed.")
                return 1
        else:
            message.error("Source spectrum or response not specified.")
            return 1

        # Correct for possible shifts in channels if first channel is 0
        if self.resp.ebounds.FirstChannel == 0:
            for i in range(self.resp.NumberMatrixExt):
                self.correct_possible_shift(i)

        # Check output spo object
        checkspo = self.spo.check()
        if checkspo != 0:
            message.error("Resulting spo file is not OK.")
            return 1

        # Check
        checkres = self.res.check()
        if checkres != 0:
            message.error("Resulting res file is not OK.")
            return 1

        return 0