Source code for sofia_redux.instruments.hawc.steps.stepskydip

# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""Skydip plots pipeline step."""

import os

from astropy import log
from matplotlib.backends.backend_agg \
    import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np

from sofia_redux.instruments.hawc.datatext import DataText
from sofia_redux.instruments.hawc.stepmoparent import StepMOParent

__all__ = ['StepSkydip']


[docs] class StepSkydip(StepMOParent): """ Produce diagnostic plots from skydip data. This step uses demodulated data taken in sky dip mode to produce a plot of averaged raw data vs. elevation. If a skydip fit was produced by the scan map algorithm, it is also stored as a plot. This step should be run as the final step in the skydip recipe. Pipeline steps for this mode should be run in this order: - `sofia_redux.instruments.hawc.steps.StepCheckhead` - `sofia_redux.instruments.hawc.steps.StepScanMap` - `sofia_redux.instruments.hawc.steps.StepFluxjump` - `sofia_redux.instruments.hawc.steps.StepPrepare` - `sofia_redux.instruments.hawc.steps.StepDemodulate` - `sofia_redux.instruments.hawc.steps.StepDmdPlot` - `sofia_redux.instruments.hawc.steps.StepDmdCut` - `sofia_redux.instruments.hawc.steps.StepSkydip` This step produces two PNG images, saved to the same directory and base name as the input data. The output data is otherwise identical to the input data. """
[docs] def setup(self): """ Set parameters and metadata for the pipeline step. Output files have PRODTYPE = 'skydip', and are named with the step abbreviation 'SDP'. Parameters defined for this step are: indata : str Column name for the data to be displayed (vs. elevation). Either 'R array AVG' or 'T array AVG' is recommended. """ # Name of the pipeline reduction step self.name = 'skydip' self.description = 'Make Skydip Plots' # Shortcut for pipeline reduction step and identifier for # saved file names. self.procname = 'sdp' # Clear Parameter list self.paramlist = [] # Append parameters self.paramlist.append(['indata', 'R array AVG', 'Input Data: Column name for data ' 'to be used'])
[docs] def run(self): """ Run the data reduction algorithm. Because this step is multi-in, multi-out (MIMO), self.datain must be a list of DataFits objects. The output is also a list of DataFits objects, stored in self.dataout. The process is: 1. Plot elevation vs. averaged raw data. 2. Read in a scan map skydip fit ('tmp*.dat' in the same directory as the input data). 3. Plot scan map fit data. """ # Make image from demodulated data # Input is copied to output, unmodified self.dataout = self.datain # Get elevation and averaged median signal # for each chop, for each file. elev = np.zeros((0, )) signal = np.zeros((0, )) # Loop through each file and collect data for din in self.datain: elev = np.append(elev, din.table['Elevation'] - din.table['Elevation Error'] / 3600.) dat = din.table[self.getarg('indata')] while len(dat.shape) > 1: dat = np.median(dat, axis=1) signal = np.append(signal, dat) # Make output data list self.auxout = [] # Remove high and low points sigmin = np.nanpercentile(signal, 2) sigmax = np.nanpercentile(signal, 98) with np.errstate(invalid='ignore'): elev = elev[np.where(signal > sigmin)] signal = signal[np.where(signal > sigmin)] elev = elev[np.where(signal < sigmax)] signal = signal[np.where(signal < sigmax)] # Plot data fig = Figure() FigureCanvas(fig) ax = fig.add_subplot() ax.plot(1.0 / np.sin(np.pi * elev / 180.), signal, 'rd') ax.set_xlabel('1 / sin(Elevation)') ax.set_ylabel('Median Signal (%s)' % self.getarg('indata')) pngname = self.datain[-1].filename.replace('.fits', '_skydiplot.png') fig.savefig(pngname) self.auxout.append(pngname) log.info('Saved result %s' % pngname) # Make image from scanmap data # Search for output .dat file datfile = os.path.basename(self.datain[0].filenamebegin + 'SMP' + self.datain[0].filenameend + '.dat') if not os.path.isfile(datfile): return # Load data dt = DataText(config=self.config) dt.load(datfile) # Get data elev = [] obs = [] model = [] for dat in dt.data: # Don't record data with no observations if '...' in dat: continue spl = dat.split('\t') elev.append(float(spl[0])) obs.append(float(spl[1])) model.append(float(spl[2])) elev = np.array(elev) obs = np.array(obs) model = np.array(model) # Get taulabel and remove bad characters taulabel = 'tau = ' + str(dt.getheadval('tau')).replace('+-', r'$\pm$') taulabel = ''.join([c for c in taulabel if ord(c) < 128]) # Make Plot fname = os.path.split(self.datain[0].filenamebegin)[1] fig = Figure() FigureCanvas(fig) ax = fig.add_subplot() ax.plot(elev, model, 'k-', linewidth=2, label='Model: %s' % taulabel) ax.plot(elev, obs, 'rs-', linewidth=1, markersize=3, label='Skydip %s' % fname) # label axis ax.set_xlabel('Elevation (deg)') ax.set_ylabel('Mean Pixel Response (counts)') fig.legend() # Save image and add to output pngname = self.datain[-1].filename.replace('.fits', '_skymodel.png') fig.savefig(pngname) self.auxout.append(pngname) log.info('Saved result %s' % pngname) # Remove the temporary scanmap file try: log.debug(f"Removing {datfile}") os.remove(datfile) except OSError: pass