def test_nirspec_nrs1_wcs(self): """ Regression test of creating a WCS object and doing pixel to sky transformation. """ input_file = self.get_data(*self.test_dir, 'jw00023001001_01101_00001_NRS1_ramp_fit.fits') ref_file = self.get_data(*self.ref_loc, 'jw00023001001_01101_00001_NRS1_ramp_fit_assign_wcs.fits') result = AssignWcsStep.call(input_file, save_results=True, suffix='assign_wcs') result.close() im = ImageModel(result.meta.filename) imref = ImageModel(ref_file) for slit in ['S200A1', 'S200A2', 'S400A1', 'S1600A1']: w = nirspec.nrs_wcs_set_input(im, slit) grid = grid_from_bounding_box(w.bounding_box) ra, dec, lam = w(*grid) wref = nirspec.nrs_wcs_set_input(imref, slit) raref, decref, lamref = wref(*grid) assert_allclose(ra, raref, equal_nan=True) assert_allclose(dec, decref, equal_nan=True) assert_allclose(lam, lamref, equal_nan=True)
def test_nirspec_ifu_wcs(envopt, _jail, test_id, input_file, truth_file): """ Regression test of creating a WCS object and doing pixel to sky transformation. """ del test_id input_file = get_bigdata('jwst-pipeline', envopt, 'nirspec', 'test_wcs', 'nrs1-ifu', input_file) truth_file = get_bigdata('jwst-pipeline', envopt, 'nirspec', 'test_wcs', 'nrs1-ifu', 'truth', truth_file) result = AssignWcsStep.call(input_file, save_results=True, suffix='assign_wcs') result.close() im = ImageModel(result.meta.filename) imref = ImageModel(truth_file) w = nirspec.nrs_wcs_set_input(im, 0) grid = grid_from_bounding_box(w.bounding_box) ra, dec, lam = w(*grid) wref = nirspec.nrs_wcs_set_input(imref, 0) raref, decref, lamref = wref(*grid) # equal_nan is used here as many of the entries are nan. # The domain is defined but it is only a few entries in there that are valid # as it is a curved narrow slit. assert_allclose(ra, raref, equal_nan=True) assert_allclose(dec, decref, equal_nan=True) assert_allclose(lam, lamref, equal_nan=True)
def __init__(self, median_model, input_models): #Pull out the needed information from the Median IFUCube self.median_skycube = median_model self.instrument = median_model.meta.instrument.name self.detector = median_model.meta.instrument.detector #information on how the IFUCube was constructed self.weight_power = median_model.meta.weight_power self.weighting = median_model.meta.weighting # if AREA ABORT self.rois = median_model.meta.roi_spatial self.roiw = median_model.meta.roi_wave #basic information about the type of data self.grating = None self.filter = None self.subchannel = None self.channel = None if (self.instrument == 'MIRI'): self.channel = median_model.meta.instrument.channel self.subchannel = median_model.meta.instrument.band elif (self.instrument == 'NIRSPEC'): self.grating = median_model.meta.instrument.grating self.filter = median_model.meta.instrument.filter # find the ra,dec,lambda of each element of the IFUCube self.naxis1 = self.median_skycube.data.shape[2] self.naxis2 = self.median_skycube.data.shape[1] self.naxis3 = self.median_skycube.data.shape[0] self.cdelt1 = median_model.meta.wcsinfo.cdelt1 * 3600.0 self.cdelt2 = median_model.meta.wcsinfo.cdelt2 * 3600.0 self.cdelt3 = median_model.meta.wcsinfo.cdelt3 * 3600.0 xcube, ycube, zcube = wcstools.grid_from_bounding_box( self.median_skycube.meta.wcs.bounding_box, step=(1, 1, 1)) cube_pos1, cube_pos2, cube_pos3 = self.median_skycube.meta.wcs( xcube, ycube, zcube) num = self.naxis1 * self.naxis2 * self.naxis3 flux = self.median_skycube.data self.cube_ra = cube_pos1 self.cube_dec = cube_pos2 self.cube_wave = cube_pos3 self.cube_flux = flux self.lam_centers = self.cube_wave[:, 0, 0] # self.cube_ra = np.reshape(cube_pos1,num) # self.cube_dec = np.reshape(cube_pos2,num) # self.cube_wave = np.reshape(cube_pos3,num) # self.cube_flux = np.reshape(flux,num) # print('size of input cube',cube_pos1.shape,cube_pos2.shape,cube_pos3.shape) # initialize blotted images to be original input images self.input_models = input_models
def test_miri_ifu_wcs(self): """ Regression test of creating a WCS object and doing pixel to sky transformation. """ input_file = self.get_data( self.test_dir, 'jw00024001001_01101_00001_MIRIFUSHORT_uncal_MiriSloperPipeline.fits' ) result = AssignWcsStep.call(input_file, save_results=True) # Get the region file region = RegionsModel(crds_client.get_reference_file( result, 'regions')) # Choose the same plane as in the miri.py file (hardcoded for now). regions = region.regions[7, :, :] # inputs x, y = grid_from_bounding_box(result.meta.wcs.bounding_box) # Get indices where pixels == 0. These should be NaNs in the output. ind_zeros = regions == 0 cwd = os.path.abspath('.') os.makedirs('truth', exist_ok=True) os.chdir('truth') truth_file = self.get_data( *self.ref_loc, 'jw00024001001_01101_00001_MIRIFUSHORT_assign_wcs.fits') os.chdir(cwd) truth = ImageModel(truth_file) ra, dec, lam = result.meta.wcs(x, y) raref, decref, lamref = truth.meta.wcs(x, y) assert_allclose(ra, raref, equal_nan=True) assert_allclose(dec, decref, equal_nan=True) assert_allclose(lam, lamref, equal_nan=True) # Test that we got NaNs at ind_zero assert (np.isnan(ra).nonzero()[0] == ind_zeros.nonzero()[0]).all() assert (np.isnan(ra).nonzero()[1] == ind_zeros.nonzero()[1]).all() # Test the inverse transform x1, y1 = result.meta.wcs.backward_transform(ra, dec, lam) assert (np.isnan(x1).nonzero()[0] == ind_zeros.nonzero()[0]).all() assert (np.isnan(x1).nonzero()[1] == ind_zeros.nonzero()[1]).all() # Also run a smoke test with values outside the region. dec[100][200] = -80 ra[100][200] = 7 lam[100][200] = 15 x2, y2 = result.meta.wcs.backward_transform(ra, dec, lam) assert np.isnan(x2[100][200]) assert np.isnan(x2[100][200])
def test_nirspec_wcs_roundtrip(nirspec_rate): im = AssignWcsStep.call(nirspec_rate) im = Extract2dStep.call(im) im = ResampleSpecStep.call(im) for slit in im.slits: x, y = grid_from_bounding_box(slit.meta.wcs.bounding_box) ra, dec, lam = slit.meta.wcs(x, y) xp, yp = slit.meta.wcs.invert(ra, dec, lam) assert_allclose(x, xp, atol=1e-8) assert_allclose(y, yp, atol=1e-8)
def test_miri_lrs_masterbg_user(self): """ Regression test of masterbackgound subtraction with lrs, with user provided 1-D background """ # input file has the background added input_file = self.get_data(*self.test_dir, 'miri_lrs_sci+bkg_cal.fits') # user provided 1-D background user_background = self.get_data(*self.test_dir, 'miri_lrs_bkg_x1d.fits') result = MasterBackgroundStep.call(input_file, user_background=user_background, save_results=True) # Compare result (background subtracted image) to science image with no # background. Subtract these images, smooth the subtracted image and # the mean should be close to zero. input_sci_cal_file = self.get_data(*self.test_dir, 'miri_lrs_sci_cal.fits') input_sci = datamodels.open(input_sci_cal_file) # find the LRS region bb = result.meta.wcs.bounding_box x, y = grid_from_bounding_box(bb) result_lrs_region = result.data[y.astype(int), x.astype(int)] sci_lrs_region = input_sci.data[y.astype(int), x.astype(int)] # do a 5 sigma clip on the science image sci_mean = np.nanmean(sci_lrs_region) sci_std = np.nanstd(sci_lrs_region) upper = sci_mean + sci_std * 5.0 lower = sci_mean - sci_std * 5.0 mask_clean = np.logical_and(sci_lrs_region < upper, sci_lrs_region > lower) sub = result_lrs_region - sci_lrs_region mean_sub = np.absolute(np.mean(sub[mask_clean])) atol = 0.1 rtol = 0.001 assert_allclose(mean_sub, 0, atol=atol, rtol=rtol) # Test 3 Compare background subtracted science data (results) # to a truth file. truth_file = self.get_data( *self.ref_loc, 'miri_lrs_sci+bkg_masterbackgroundstep.fits') result_file = result.meta.filename outputs = [(result_file, truth_file)] self.compare_outputs(outputs) result.close() input_sci.close()
def spec_footprint(in_wcs, bounding_box=None, mask=None): """ Returns wcs footprint grid coordinates where NaNs are masked. Build-7 workaround. """ x, y = wcstools.grid_from_bounding_box(in_wcs.bounding_box, step=(1, 1), center=True) ra, dec, lam = in_wcs(x, y) m = np.isnan(lam) return ma.array([ra, dec, lam], mask=[m, m, m])
def calc_gwcs_pixmap(in_wcs, out_wcs, shape=None): """ Return a pixel grid map from input frame to output frame. """ if shape: bb = wcs_bbox_from_shape(shape) log.debug("Bounding box from data shape: {}".format(bb)) else: bb = in_wcs.bounding_box log.debug("Bounding box from WCS: {}".format(in_wcs.bounding_box)) grid = wcstools.grid_from_bounding_box(bb, step=(1, 1)) pixmap = np.dstack(reproject(in_wcs, out_wcs)(grid[0], grid[1])) return pixmap
def calc_gwcs_pixmap(in_wcs, out_wcs, shape=None): """ Return a pixel grid map from input frame to output frame. """ if shape: bb = bounding_box_from_shape(shape) log.debug("Bounding box from data shape: {}".format(bb)) else: bb = in_wcs.bounding_box log.debug("Bounding box from WCS: {}".format(in_wcs.bounding_box)) grid = wcstools.grid_from_bounding_box(bb, step=(1, 1)) pixmap = np.dstack(reproject(in_wcs, out_wcs)(grid[0], grid[1])) return pixmap
def bkg_for_ifu_image(input, tab_wavelength, tab_background): """Create a 2-D background for an IFUImageModel Parameters ---------- input : `~jwst.datamodels.IFUImageModel` The input science data. tab_wavelength : 1-D ndarray The wavelength column read from the 1-D background table. tab_background : 1-D ndarray The flux column read from the 1-D background table, divided by the npixels column. Returns ------- background : `~jwst.datamodels.IFUImageModel` A copy of `input` but with the data replaced by the background, "expanded" from 1-D to 2-D. """ background = input.copy() background.data[:, :] = 0. if input.meta.instrument.name.upper() == "NIRSPEC": list_of_wcs = nirspec.nrs_ifu_wcs(input) for ifu_wcs in list_of_wcs: x, y = grid_from_bounding_box(ifu_wcs.bounding_box) wl_array = ifu_wcs(x, y)[2] wl_array[np.isnan(wl_array)] = -1. bkg_flux = np.interp(wl_array, tab_wavelength, tab_background, left=0., right=0.) background.data[y.astype(int), x.astype(int)] = bkg_flux.copy() elif input.meta.instrument.name.upper() == "MIRI": shape = input.data.shape grid = np.indices(shape, dtype=np.float64) wl_array = ifu_wcs(grid[1], grid[0])[2] wl_array[np.isnan(wl_array)] = -1. bkg_flux = np.interp(wl_array, tab_wavelength, tab_background, left=0., right=0.) background.data[:, :] = bkg_flux.copy() else: raise RuntimeError("Exposure type {} is not supported." .format(input.meta.exposure.type)) return background
def test_miri_lrs_masterbg_user(self): """ Regression test of masterbackgound subtraction with lrs, with user provided 1-D background """ # input file has the background added input_file = self.get_data(*self.test_dir, 'miri_lrs_sci+bkg_cal.fits') # user provided 1-D background user_background = self.get_data(*self.test_dir, 'miri_lrs_bkg_x1d.fits') result = MasterBackgroundStep.call(input_file, user_background=user_background, save_results=True) # Compare result (background subtracted image) to science image with no # background. Subtract these images, smooth the subtracted image and # the mean should be close to zero. input_sci_cal_file = self.get_data(*self.test_dir, 'miri_lrs_sci_cal.fits') input_sci = datamodels.open(input_sci_cal_file) # find the LRS region bb = result.meta.wcs.bounding_box x, y = grid_from_bounding_box(bb) result_lrs_region = result.data[y.astype(int), x.astype(int)] sci_lrs_region = input_sci.data[y.astype(int), x.astype(int)] # do a 5 sigma clip on the science image sci_mean = np.nanmean(sci_lrs_region) sci_std = np.nanstd(sci_lrs_region) upper = sci_mean + sci_std*5.0 lower = sci_mean - sci_std*5.0 mask_clean = np.logical_and(sci_lrs_region < upper, sci_lrs_region > lower) sub = result_lrs_region - sci_lrs_region mean_sub = np.absolute(np.mean(sub[mask_clean])) atol = 0.1 rtol = 0.001 assert_allclose(mean_sub, 0, atol=atol, rtol=rtol) # Test 3 Compare background subtracted science data (results) # to a truth file. truth_file = self.get_data(*self.ref_loc, 'miri_lrs_sci+bkg_masterbackgroundstep.fits') result_file = result.meta.filename outputs = [(result_file, truth_file)] self.compare_outputs(outputs) result.close() input_sci.close()
def update_s_region_spectral(model): swcs = model.meta.wcs bbox = swcs.bounding_box if bbox is None: bbox = bounding_box_from_shape(model.data.shape) x, y = grid_from_bounding_box(bbox) ra, dec, lam = swcs(x, y) footprint = np.array([[np.nanmin(ra), np.nanmin(dec)], [np.nanmax(ra), np.nanmin(dec)], [np.nanmax(ra), np.nanmax(dec)], [np.nanmin(ra), np.nanmax(dec)]]) update_s_region_keyword(model, footprint)
def update_s_region_spectral(model): swcs = model.meta.wcs bbox = swcs.bounding_box if bbox is None: bbox = wcs_bbox_from_shape(model.data.shape) x, y = grid_from_bounding_box(bbox) ra, dec, lam = swcs(x, y) footprint = np.array([[np.nanmin(ra), np.nanmin(dec)], [np.nanmax(ra), np.nanmin(dec)], [np.nanmax(ra), np.nanmax(dec)], [np.nanmin(ra), np.nanmax(dec)]]) update_s_region_keyword(model, footprint)
def _max_virtual_slit_extent(self, wcs_list, target_ra, target_dec): """ Compute min & max slit coordinates for all nods in the "virtual" slit frame. NOTE: this code, potentially, might have troubles dealing with large dithers such that ``target_ra`` and ``target_dec`` may not be converted to slit frame (i.e., result in ``NaN``). A more sophisticated algorithm may be needed to "stitch" large dithers. But then distortions may come into play. """ y_slit_min = np.inf y_slit_max = -np.inf t0 = 0 for wcs in wcs_list: d2s = wcs.get_transform('detector', 'slit_frame') w2s = wcs.get_transform('world', 'slit_frame') x, y = wcstools.grid_from_bounding_box(wcs.bounding_box) ra, dec, lam = wcs(x, y) good = np.logical_and(np.isfinite(ra), np.isfinite(dec)) x = x[good] y = y[good] lm = lam[good] _, yslit, _ = d2s(x, y) # position of the target in the slit relative to its position # for the refence image: ts = w2s(target_ra, target_dec, np.mean(lm))[1] - t0 if wcs is wcs_list[0]: t0 = ts ts = 0 y_slit_min_i = np.min(yslit) - ts y_slit_max_i = np.max(yslit) - ts if y_slit_min_i < y_slit_min: y_slit_min = y_slit_min_i if y_slit_max_i > y_slit_max: y_slit_max = y_slit_max_i return y_slit_min, y_slit_max
def test_NIRCAMForwardRowGrismDispersion(): xmodels = [ Polynomial1D(1, c0=0.59115385, c1=0.00038615), Polynomial1D(1, c0=-0.16596154, c1=0.00019308) ] ymodels = [Polynomial1D(1, c0=0., c1=0.), Polynomial1D(1, c0=0., c1=0.)] lmodels = [ Polynomial1D(1, c0=2.4, c1=2.6), Polynomial1D(1, c0=2.4, c1=2.6) ] model = transforms.NIRCAMForwardRowGrismDispersion([1, 2], lmodels, xmodels, ymodels) x0 = 913.7 y0 = 15.5 order = 1 slit = create_slit(model, x0, y0, order) expected = np.array([[ 3.03973415, 3.04073814, 3.04174213, 3.04274612, 3.04375011, 3.0447541 ], [3.03973415, 3.04073814, 3.04174213, 3.04274612, 3.04375011, 3.0447541], [ 3.03973415, 3.04073814, 3.04174213, 3.04274612, 3.04375011, 3.0447541 ], [ 3.03973415, 3.04073814, 3.04174213, 3.04274612, 3.04375011, 3.0447541 ], [ 3.03973415, 3.04073814, 3.04174213, 3.04274612, 3.04375011, 3.0447541 ], [ 3.03973415, 3.04073814, 3.04174213, 3.04274612, 3.04375011, 3.0447541 ]]) # refactored call x, y = grid_from_bounding_box(slit.meta.wcs.bounding_box) wavelength = compute_wavelength_array( slit ) # x, y, np.zeros(x.shape) +x0, np.zeros(y.shape)+y0, np.zeros(x.shape)+order) assert_allclose(wavelength, expected) with pytest.raises(ValueError): slit = create_slit(model, x0, y0, 3) compute_wavelength_array(slit)
def test_miri_ifu_wcs(self): """ Regression test of creating a WCS object and doing pixel to sky transformation. """ input_file = self.get_data(self.test_dir, 'jw00024001001_01101_00001_MIRIFUSHORT_uncal_MiriSloperPipeline.fits') result = AssignWcsStep.call(input_file, save_results=True) # Get the region file region = RegionsModel(crds_client.get_reference_file(result, 'regions')) # inputs x, y = grid_from_bounding_box(result.meta.wcs.bounding_box) # Get indices where pixels == 0. These should be NaNs in the output. ind_zeros = region.regions == 0 cwd = os.path.abspath('.') os.makedirs('truth', exist_ok=True) os.chdir('truth') truth_file = self.get_data(*self.ref_loc, 'jw00024001001_01101_00001_MIRIFUSHORT_assign_wcs.fits') os.chdir(cwd) truth = ImageModel(truth_file) ra, dec, lam = result.meta.wcs(x, y) raref, decref, lamref = truth.meta.wcs(x, y) assert_allclose(ra, raref, equal_nan=True) assert_allclose(dec, decref, equal_nan=True) assert_allclose(lam, lamref, equal_nan=True) # Test that we got NaNs at ind_zero assert(np.isnan(ra).nonzero()[0] == ind_zeros.nonzero()[0]).all() assert(np.isnan(ra).nonzero()[1] == ind_zeros.nonzero()[1]).all() # Test the inverse transform x1, y1 = result.meta.wcs.backward_transform(ra, dec, lam) assert(np.isnan(x1).nonzero()[0] == ind_zeros.nonzero()[0]).all() assert (np.isnan(x1).nonzero()[1] == ind_zeros.nonzero()[1]).all() # Also run a smoke test with values outside the region. dec[100][200] = -80 ra[100][200] = 7 lam[100][200] = 15 x2, y2 = result.meta.wcs.backward_transform(ra, dec, lam) assert np.isnan(x2[100][200]) assert np.isnan(x2[100][200])
def test_tweakreg_with_gaia(run_image3pipeline, rtdata_module, root): """ Test that WCS object works as expected """ rtdata = rtdata_module rtdata.input = root + "_nrca5_cal.fits" output_crf = root + "_nrca5_a3001_crf.fits" rtdata.output = output_crf rtdata.get_truth("truth/test_nircam_align_to_gaia/" + output_crf) with datamodels.open(rtdata.output) as model, datamodels.open(rtdata.truth) as model_truth: grid = grid_from_bounding_box(model.meta.wcs.bounding_box) ra, dec = model.meta.wcs(*grid) ra_truth, dec_truth = model_truth.meta.wcs(*grid) assert_allclose(ra, ra_truth) assert_allclose(dec, dec_truth)
def test_nircam_image_stage2_wcs(run_image2pipeline, rtdata_module): """Test that WCS object works as expected""" rtdata = rtdata_module rtdata.input = "jw42424001001_01101_00001_nrca5_uncal.fits" output = "jw42424001001_01101_00001_nrca5_assign_wcs.fits" rtdata.output = output rtdata.get_truth(f"truth/test_nircam_image_stages/{output}") with datamodels.open(rtdata.output) as model, datamodels.open(rtdata.truth) as model_truth: grid = grid_from_bounding_box(model.meta.wcs.bounding_box) ra, dec = model.meta.wcs(*grid) ra_truth, dec_truth = model_truth.meta.wcs(*grid) assert_allclose(ra, ra_truth) assert_allclose(dec, dec_truth)
def ifu_world_coord(wcs_ifu_grating): """ Return RA, DEC, LAM for all slices in the NRS IFU.""" ra_all = [] dec_all = [] lam_all = [] im, refs = wcs_ifu_grating(grating="G140H", filter="F100LP") for sl in range(30): slice_wcs = nirspec.nrs_wcs_set_input(im, sl) x, y = wcstools.grid_from_bounding_box(slice_wcs.bounding_box) r, d, lam = slice_wcs(x, y) ra_all.append(r) dec_all.append(d) lam_all.append(lam) ra_all = np.concatenate([r.flatten() for r in ra_all]) dec_all = np.concatenate([r.flatten() for r in dec_all]) lam_all = np.concatenate([r.flatten() for r in lam_all]) return ra_all, dec_all, lam_all
def test_miri_fixed_slit_wcs(self): """ Regression test of creating a WCS object and doing pixel to sky transformation. """ input_file = self.get_data(self.test_dir, 'jw00035001001_01101_00001_mirimage_rate.fits') result = AssignWcsStep.call(input_file, save_results=True) truth_file = self.get_data(os.path.join(*self.ref_loc), 'jw00035001001_01101_00001_mirimage_assign_wcs.fits') truth = ImageModel(truth_file) x, y = grid_from_bounding_box(result.meta.wcs.bounding_box) ra, dec, lam = result.meta.wcs(x, y) raref, decref, lamref = truth.meta.wcs(x, y) assert_allclose(ra, raref) assert_allclose(dec, decref) assert_allclose(lam, lamref)
def test_in_slice(slice, wcs_ifu_grating, ifu_world_coord): """ Test that the number of valid outputs from a slice forward transform equals the valid pixels within the slice from the slice backward transform. """ ra_all, dec_all, lam_all = ifu_world_coord im, refs = wcs_ifu_grating("G140H", "F100LP") slice_wcs = nirspec.nrs_wcs_set_input(im, slice) slicer2world = slice_wcs.get_transform('slicer', 'world') detector2slicer = slice_wcs.get_transform('detector', 'slicer') x, y = wcstools.grid_from_bounding_box(slice_wcs.bounding_box) onslice_ind = in_ifu_slice(slice_wcs, ra_all, dec_all, lam_all) slx, sly, sllam = slicer2world.inverse(ra_all, dec_all, lam_all) xinv, yinv = detector2slicer.inverse(slx[onslice_ind], sly[onslice_ind], sllam[onslice_ind]) r, d, _ = slice_wcs(x, y) assert r[~np.isnan(r)].size == xinv.size
def compute_wavelength_array(slit): """ Compute the wavelength array for a slit with gwcs object Parameters ---------- slit : `~jwst.datamodels.SlitModel` JWST slit datamodel containing a meta.wcs GWCS object Returns ------- wavelength : numpy.array The wavelength array """ transform = slit.meta.wcs.forward_transform x, y = grid_from_bounding_box(slit.meta.wcs.bounding_box) wavelength = transform(x, y)[2] return wavelength
def test_miri_lrs_slitless_wcs(run_tso_spec2_pipeline, fitsdiff_default_kwargs, rtdata_module): """Compare the assign_wcs output of a MIRI LRS slitless calwebb_tso3 pipeline.""" rtdata = rtdata_module output = f"{DATASET_ID}_assign_wcs.fits" # get input assign_wcs and truth file rtdata.output = output rtdata.get_truth("truth/test_miri_lrs_slitless_tso_spec2/" + output) # Compare the output and truth file with datamodels.open(rtdata.output) as im, datamodels.open( rtdata.truth) as im_truth: x, y = grid_from_bounding_box(im.meta.wcs.bounding_box) ra, dec, lam = im.meta.wcs(x, y) ratruth, dectruth, lamtruth = im_truth.meta.wcs(x, y) assert_allclose(ra, ratruth) assert_allclose(dec, dectruth) assert_allclose(lam, lamtruth)
def test_build_interpolated_output_wcs(miri_rate_pair): im1, im2 = miri_rate_pair driz = ResampleSpecData(ModelContainer([im1, im2])) output_wcs = driz.build_interpolated_output_wcs() # Make sure that all RA, Dec values in the input image have a location in # the output frame grid = grid_from_bounding_box(im2.meta.wcs.bounding_box) ra, dec, lam = im2.meta.wcs(*grid) x, y = output_wcs.invert(ra, dec, lam) # This currently fails, as we see a slight offset # assert (x > 0).all() # Make sure the output slit size is larger than the input slit size # for this nodded data assert driz.data_size[1] > ra.shape[1]
def test_miri_lrs_slitless_wcs(run_tso_spec2_pipeline, fitsdiff_default_kwargs, rtdata_module): rtdata = rtdata_module output = f"{DATASET_ID}_assign_wcs.fits" # get input assign_wcs and truth file rtdata.output = output rtdata.get_truth("truth/test_miri_lrs_slitless_tso_spec2/" + output) # Open the output and truth file im = datamodels.open(rtdata.output) im_truth = datamodels.open(rtdata.truth) x, y = grid_from_bounding_box(im.meta.wcs.bounding_box) ra, dec, lam = im.meta.wcs(x, y) ratruth, dectruth, lamtruth = im_truth.meta.wcs(x, y) assert_allclose(ra, ratruth) assert_allclose(dec, dectruth) assert_allclose(lam, lamtruth)
def test_wavecorr(): hdul = create_nirspec_mos_file() msa_meta = os.path.join( jwst.__path__[0], *['assign_wcs', 'tests', 'data', 'msa_configuration.fits']) hdul[0].header['MSAMETFL'] = msa_meta hdul[0].header['MSAMETID'] = 12 im = datamodels.ImageModel(hdul) im_wcs = AssignWcsStep.call(im) im_ex2d = Extract2dStep.call(im_wcs) im_ex2d.slits[0].meta.wcs.bounding_box = ((-.5, 1432.5), (-.5, 37.5)) im_src = SourceTypeStep.call(im_ex2d) im_wave = WavecorrStep.call(im_src) # test dispersion is of the correct order # there's one slit only slit = im_src.slits[0] x, y = wcstools.grid_from_bounding_box(slit.meta.wcs.bounding_box) dispersion = wavecorr.compute_dispersion(slit.meta.wcs, x, y) assert_allclose(dispersion[~np.isnan(dispersion)], 1e-9, atol=1e-10) # the difference in wavelength should be of the order of e-10 in um assert_allclose(im_src.slits[0].wavelength - im_wave.slits[0].wavelength, 1e-10) # test on both sides of the shutter source_xpos1 = -.2 source_xpos2 = .2 ra, dec, lam = slit.meta.wcs(x, y) ref_name = im_wave.meta.ref_file.wavecorr.name freference = datamodels.WaveCorrModel( WavecorrStep.reference_uri_to_cache_path(ref_name, im.crds_observatory)) zero_point1 = wavecorr.compute_zero_point_correction( lam, freference, source_xpos1, 'MOS', dispersion) zero_point2 = wavecorr.compute_zero_point_correction( lam, freference, source_xpos2, 'MOS', dispersion) diff_correction = np.abs(zero_point1[1] - zero_point2[1]) assert_allclose(diff_correction[diff_correction.nonzero()].mean(), 0.02, atol=0.01)
def compute_dispersion(wcs): """ Compute the pixel dispersion. Parameters ---------- wcs : `~gwcs.wcs.WCS` The WCS object for this slit. Returns ------- dispersion : ndarray The pixel dispersion [in m]. """ xpix, ypix = wcstools.grid_from_bounding_box(wcs.bounding_box, step=(1, 1)) xleft = xpix - 0.5 xright = xpix + 0.5 _, _, lamright = wcs(xright, ypix) _, _, lamleft = wcs(xleft, ypix) return (lamright - lamleft) * 10**-6
def test_miri_image_wcs(run_image2, rtdata_module, fitsdiff_default_kwargs): rtdata = rtdata_module # get input assign_wcs and truth file output = "det_image_1_MIRIMAGE_F770Wexp1_5stars_assign_wcs.fits" rtdata.output = output rtdata.get_truth("truth/test_miri_image_stages/" + output) # Open the output and truth file with datamodels.open(rtdata.output) as im, datamodels.open(rtdata.truth) as im_truth: x, y = grid_from_bounding_box(im.meta.wcs.bounding_box) ra, dec = im.meta.wcs(x, y) ratruth, dectruth = im_truth.meta.wcs(x, y) assert_allclose(ra, ratruth) assert_allclose(dec, dectruth) # Test the inverse transform xtest, ytest = im.meta.wcs.backward_transform(ra, dec) xtruth, ytruth = im_truth.meta.wcs.backward_transform (ratruth, dectruth) assert_allclose(xtest, xtruth) assert_allclose(ytest, ytruth)
def compute_footprint_spectral(model): """ Determine spatial footprint for spectral observations using the instrument model. Parameters ---------- output_model : `~jwst.datamodels.IFUImageModel` The output of assign_wcs. """ swcs = model.meta.wcs bbox = swcs.bounding_box if bbox is None: bbox = wcs_bbox_from_shape(model.data.shape) x, y = grid_from_bounding_box(bbox) ra, dec, lam = swcs(x, y) footprint = np.array([[np.nanmin(ra), np.nanmin(dec)], [np.nanmax(ra), np.nanmin(dec)], [np.nanmax(ra), np.nanmax(dec)], [np.nanmin(ra), np.nanmax(dec)]]) return footprint
def test_miri_mrs_wcs(run_spec2, fitsdiff_default_kwargs): rtdata, asn_path = run_spec2 # get input assign_wcs and truth file output = "ifushort_ch12_assign_wcs.fits" rtdata.output = output rtdata.get_truth(f"truth/test_miri_mrs/{output}") # Open the output and truth file with datamodels.open(rtdata.output) as im, datamodels.open(rtdata.truth) as im_truth: x, y = grid_from_bounding_box(im.meta.wcs.bounding_box) ra, dec, lam = im.meta.wcs(x, y) ratruth, dectruth, lamtruth = im_truth.meta.wcs(x, y) assert_allclose(ra, ratruth) assert_allclose(dec, dectruth) assert_allclose(lam, lamtruth) # Test the inverse transform xtest, ytest = im.meta.wcs.backward_transform(ra, dec, lam) xtruth, ytruth = im_truth.meta.wcs.backward_transform(ratruth, dectruth, lamtruth) assert_allclose(xtest, xtruth) assert_allclose(ytest, ytruth)
def compute_dispersion(wcs): """ Compute the pixel dispersion. Parameters ---------- wcs : `~gwcs.wcs.WCS` The WCS object for this slit. Returns ------- dispersion : ndarray The pixel dispersion [in m]. """ xpix, ypix = wcstools.grid_from_bounding_box(wcs.bounding_box, step=(1, 1)) xleft = xpix - 0.5 xright = xpix + 0.5 _, _, lamright = wcs(xright, ypix) _, _, lamleft = wcs(xleft, ypix) return (lamright - lamleft) * 10 ** -6
def test_wcs(tmpdir, distortion, step): file_name = str(tmpdir / 'distortion.asdf') dist = rdm.DistortionRefModel(distortion) dist.save(file_name) l2im = create_image() l2_wcs = step(l2im, file_name) assert l2_wcs.meta.wcs is not None assert l2_wcs.meta.cal_step.assign_wcs == 'COMPLETE' x, y = grid_from_bounding_box(l2_wcs.meta.wcs.bounding_box) ra, dec = l2_wcs.meta.wcs(x, y) assert_allclose(ra, l2im.meta.wcsinfo.ra_ref, atol=2.3) assert_allclose(dec, l2im.meta.wcsinfo.dec_ref, atol=1.3) # verify inputs outside the bounding box return NaNs ra, dec = l2_wcs.meta.wcs(-5, 3) assert np.isnan(ra) assert np.isnan(dec)
def test_miri_image_wcs(self): """ Regression test of creating a WCS object and doing pixel to sky transformation. """ input_file = self.get_data(self.test_dir, "jw00001001001_01101_00001_MIRIMAGE_ramp_fit.fits") result = AssignWcsStep.call(input_file, save_results=True) cwd = os.path.abspath('.') os.makedirs('truth', exist_ok=True) os.chdir('truth') truth_file = self.get_data(*self.ref_loc, "jw00001001001_01101_00001_MIRIMAGE_assign_wcs.fits") os.chdir(cwd) truth = ImageModel(truth_file) x, y = grid_from_bounding_box(result.meta.wcs.bounding_box) ra, dec = result.meta.wcs(x, y) raref, decref = truth.meta.wcs(x, y) assert_allclose(ra, raref) assert_allclose(dec, decref)
def test_miri_lrs_slit_wcs(run_pipeline, fitsdiff_default_kwargs): rtdata = run_pipeline # get input assign_wcs and truth file output = "jw00623032001_03102_00001_mirimage_assign_wcs.fits" rtdata.output = output rtdata.get_truth("truth/test_miri_lrs_slit_spec2/" + output) # Compare the output and truth file with datamodels.open(output) as im, datamodels.open(rtdata.truth) as im_truth: x, y = grid_from_bounding_box(im.meta.wcs.bounding_box) ra, dec, lam = im.meta.wcs(x, y) ratruth, dectruth, lamtruth = im_truth.meta.wcs(x, y) assert_allclose(ra, ratruth) assert_allclose(dec, dectruth) assert_allclose(lam, lamtruth) # Test the inverse transform xtest, ytest = im.meta.wcs.backward_transform(ra, dec, lam) xtruth, ytruth = im_truth.meta.wcs.backward_transform(ratruth, dectruth, lamtruth) assert_allclose(xtest, xtruth) assert_allclose(ytest, ytruth)
def test_miri_slitless_wcs(self): """ Regression test of creating a WCS object and doing pixel to sky transformation. """ input_file = self.get_data(self.test_dir, "jw80600012001_02101_00003_mirimage_rateints.fits") result = AssignWcsStep.call(input_file, save_results=True) cwd = os.path.abspath('.') os.makedirs('truth', exist_ok=True) os.chdir('truth') truth_file = self.get_data(*self.ref_loc, "jw80600012001_02101_00003_mirimage_assignwcsstep.fits") os.chdir(cwd) truth = CubeModel(truth_file) x, y = grid_from_bounding_box(result.meta.wcs.bounding_box) ra, dec, lam = result.meta.wcs(x, y) raref, decref, lamref = truth.meta.wcs(x, y) assert_allclose(ra, raref) assert_allclose(dec, decref) assert_allclose(lam, lamref)
def compute_wavelength_array(slit): """ Compute the wavelength array for a slit with gwcs object Parameters ---------- slit : `~jwst.datamodels.SlitModel` JWST slit datamodel containing a meta.wcs GWCS object Returns ------- wavelength : numpy.array The wavelength array """ grid = grid_from_bounding_box(slit.meta.wcs.bounding_box) shape = grid[0].shape wavelength = np.empty(shape, dtype=np.float64) transform = slit.meta.wcs.forward_transform for j in range(shape[0]): for i in range(shape[1]): wavelength[j, i] = transform(grid[0][j, i], grid[1][j, i])[2] return wavelength
def test_spatial_transform(): """ Calling the backwards WCS transform gives the same results for ``negative RA`` and ``negative RA + 360``. """ im = ImageModel() im.meta.wcsinfo._instance.update(wcsinfo) im.meta.instrument._instance.update(instrument) im.meta.exposure._instance.update(exposure) im.meta.observation._instance.update(observation) im.meta.subarray._instance.update(subarray) im = AssignWcsStep.call(im) im.data = np.random.rand(416, 72) im.error = np.random.rand(416, 72) im.dq = np.random.rand(416, 72) im = ResampleSpecStep.call(im) x, y = grid_from_bounding_box(im.meta.wcs.bounding_box) ra, dec, lam = im.meta.wcs(x, y) ra1 = np.where(ra < 0, 360 + ra, ra) assert_allclose(im.meta.wcs.invert(ra, dec, lam), im.meta.wcs.invert(ra1, dec, lam))
def test_nirspec_masterbackground_mos_user1d(self): """ Regression test of master background subtraction for NRS MOS when a user 1-D spectrum is provided. """ # input file has 2-D background image added to it input_file = self.get_data(*self.test_dir, 'nrs_mos_sci+bkg_cal.fits') # user provide 1-D background was created from the 2-D background image input_1dbkg_file = self.get_data(*self.test_dir, 'nrs_mos_bkg_x1d.fits') result = MasterBackgroundStep.call(input_file, user_background=input_1dbkg_file, save_results=True) # _________________________________________________________________________ # One of out tests is to compare the 1-D extracted spectra from # the science image (no background added) and the masterbackground subtracted # data. # run resample_spec on results from MasterBackground step result_2d = ResampleSpecStep.call(result) # run 1-D extract on results from MasterBackground step result_1d = Extract1dStep.call(result_2d) # get input science data with background added input_sci_cal_file = self.get_data(*self.test_dir, 'nrs_mos_sci_cal.fits') # get 1-D extract on original science data without background # this reference data was also run through ResampleSpec input_sci_1d_file = self.get_data(*self.ref_loc, 'nrs_mos_sci_extract1dstep.fits') input_sci = datamodels.open(input_sci_cal_file) sci_cal_1d = datamodels.open(input_sci_1d_file) num_spec = len(sci_cal_1d.spec) # the user 1D spectrum may not cover the entire wavelength range of the # science data. Find the wavelength range of user 1-D spectra input_1dbkg_1d = datamodels.open(input_1dbkg_file) user_wave = input_1dbkg_1d.spec[0].spec_table['wavelength'] user_flux = input_1dbkg_1d.spec[0].spec_table['flux'] user_wave_valid = np.where(user_flux > 0) min_user_wave = np.amin(user_wave[user_wave_valid]) max_user_wave = np.amax(user_wave[user_wave_valid]) input_1dbkg_1d.close() # loop over each slit and perform 2 tests on each slit for i in range(num_spec): # ______________________________________________________________________ # Test 1 compare extracted spectra data from the science data # to extracted spectra from the output # from MasterBackground subtraction. sci_spec_1d = sci_cal_1d.spec[i].spec_table['flux'] sci_wave = sci_cal_1d.spec[i].spec_table['wavelength'] result_spec_1d = result_1d.spec[i].spec_table['flux'] # find the waverange covered by both user 1-D and science slit sci_wave_valid = np.where(sci_spec_1d > 0) min_wave = np.amin(sci_wave[sci_wave_valid]) max_wave = np.amax(sci_wave[sci_wave_valid]) if min_user_wave > min_wave: min_wave = min_user_wave if max_user_wave < max_wave: max_wave = max_user_wave sub_spec = sci_spec_1d - result_spec_1d valid = np.where(np.logical_and(sci_wave > min_wave, sci_wave < max_wave)) sub_spec = sub_spec[valid] mean_sub = np.nanmean(sub_spec) atol = 1.5 assert_allclose(mean_sub, 0, atol=atol) # ______________________________________________________________________ # Test 2 compare the science data with no background # to the output from the masterBackground Subtraction step # background subtracted science image. bb = input_sci.slits[i].meta.wcs.bounding_box x, y = grid_from_bounding_box(bb) ra, dec, lam = input_sci.slits[i].meta.wcs(x, y) valid = np.isfinite(lam) sci = input_sci.slits[i].data result_slit = result.slits[i].data # check for outliers in the science image that could cause test # to fail. These could be cosmic ray hits or other yeck that # messes up the science data - ignores these cases sci_mean = np.nanmean(sci[valid]) sci_std = np.nanstd(sci[valid]) upper = sci_mean + sci_std*5.0 lower = sci_mean - sci_std*5.0 mask_clean = np.logical_and(sci[valid] < upper, sci[valid] > lower) # for this slit subtract the background subtracted data from # the science data with no background added sub = result_slit - sci # do not look at outliers - they confuse things sub_valid = sub[valid] mean_sub = np.mean(sub_valid[mask_clean]) atol = 0.1 assert_allclose(np.absolute(mean_sub), 0, atol=atol) # ______________________________________________________________________ # Test 3 Compare background sutracted science data (results) # to a truth file. This data is MultiSlit data result_file = result.meta.filename ref_file = self.get_data(*self.ref_loc, 'nrs_mos_sci+bkg_masterbackgroundstep.fits') outputs = [(result_file, ref_file)] self.compare_outputs(outputs) input_sci.close() result.close()
def find_footprint_NIRSPEC(input, flag_data, coord_system): #******************************************************************************** """ Short Summary ------------- For each slice find: a. the min and max spatial coordinates (alpha,beta) or (V2-v3) depending on coordinate system. axis a = naxis 1, axis b = naxis2 b. min and max wavelength is also determined. , beta and lambda for those slices Parameters ---------- input: input model (or file) Returns ------- min and max spaxial coordinates and wavelength for channel. """ # loop over all the region (Slices) in the Channel # based on regions mask (indexed by slice number) find all the detector # x,y values for slice. Then convert the x,y values to v2,v3,lambda # return the min & max of spatial coords and wavelength - these are of the pixel centers nslices = 30 a_slice = np.zeros(nslices * 2) b_slice = np.zeros(nslices * 2) lambda_slice = np.zeros(nslices * 2) k = 0 # for NIRSPEC there are 30 regions log.info('Looping over slices to determine cube size .. this takes a while') for i in range(nslices): slice_wcs = nirspec.nrs_wcs_set_input(input, i) x, y = wcstools.grid_from_bounding_box(slice_wcs.bounding_box, step=(1, 1), center=True) if coord_system == 'world': coord1, coord2, lam = slice_wcs(x, y) elif coord_system == 'alpha-beta': raise InvalidCoordSystem(" The Alpha-Beta Coordinate system is not valid (at this time) for NIRSPEC data") # detector2slicer = input.meta.wcs.get_transform('detector','slicer') # coord1,coord2,lam = detector2slicer(x,y) else: # error the coordinate system is not defined raise NoCoordSystem(" The output cube coordinate system is not definded") #________________________________________________________________________________ # For each slice test for 0/360 wrapping in ra. # If exists it makes it difficult to determine ra range of IFU cube. coord1_wrap = wrap_ra(coord1) a_min = np.nanmin(coord1_wrap) a_max = np.nanmax(coord1_wrap) a_slice[k] = a_min a_slice[k + 1] = a_max b_slice[k] = np.nanmin(coord2) b_slice[k + 1] = np.nanmax(coord2) lambda_slice[k] = np.nanmin(lam) lambda_slice[k + 1] = np.nanmax(lam) k = k + 2 #________________________________________________________________________________ # now test the ra slices for conistency. Adjust if needed. a_slice_wrap = wrap_ra(a_slice) a_min = np.nanmin(a_slice_wrap) a_max = np.nanmax(a_slice_wrap) b_min = min(b_slice) b_max = max(b_slice) lambda_min = min(lambda_slice) lambda_max = max(lambda_slice) if (a_min == 0.0 and a_max == 0.0 and b_min == 0.0 and b_max == 0.0): log.info('This NIRSPEC exposure has no IFU data on it - skipping file') return a_min, a_max, b_min, b_max, lambda_min, lambda_max
def build_interpolated_output_wcs(self, refmodel=None): """ Create a spatial/spectral WCS output frame Creates output frame by linearly fitting RA, Dec along the slit and producing a lookup table to interpolate wavelengths in the dispersion direction. Parameters ---------- refmodel : `~jwst.datamodels.DataModel` The reference input image from which the fiducial WCS is created. If not specified, the first image in self.input_models is used. Returns ------- output_wcs : `~gwcs.WCS` object A gwcs WCS object defining the output frame WCS """ if refmodel is None: refmodel = self.input_models[0] refwcs = refmodel.meta.wcs bb = refwcs.bounding_box grid = wcstools.grid_from_bounding_box(bb) ra, dec, lam = np.array(refwcs(*grid)) spectral_axis = find_dispersion_axis(lam) spatial_axis = spectral_axis ^ 1 # Compute the wavelength array, trimming NaNs from the ends wavelength_array = np.nanmedian(lam, axis=spectral_axis) wavelength_array = wavelength_array[~np.isnan(wavelength_array)] # Compute RA and Dec up the slit (spatial direction) at the center # of the dispersion. Use spectral_axis to determine slicing dimension lam_center_index = int((bb[spectral_axis][1] - bb[spectral_axis][0]) / 2) if not spectral_axis: ra_array = ra.T[lam_center_index] dec_array = dec.T[lam_center_index] else: ra_array = ra[lam_center_index] dec_array = dec[lam_center_index] ra_array = ra_array[~np.isnan(ra_array)] dec_array = dec_array[~np.isnan(dec_array)] fitter = LinearLSQFitter() fit_model = Linear1D() pix_to_ra = fitter(fit_model, np.arange(ra_array.shape[0]), ra_array) pix_to_dec = fitter(fit_model, np.arange(dec_array.shape[0]), dec_array) # Tabular interpolation model, pixels -> lambda pix_to_wavelength = Tabular1D(lookup_table=wavelength_array, bounds_error=False, fill_value=None, name='pix2wavelength') # Tabular models need an inverse explicitly defined. # If the wavelength array is decending instead of ascending, both # points and lookup_table need to be reversed in the inverse transform # for scipy.interpolate to work properly points = wavelength_array lookup_table = np.arange(wavelength_array.shape[0]) if not np.all(np.diff(wavelength_array) > 0): points = points[::-1] lookup_table = lookup_table[::-1] pix_to_wavelength.inverse = Tabular1D(points=points, lookup_table=lookup_table, bounds_error=False, fill_value=None, name='wavelength2pix') # For the input mapping, duplicate the spatial coordinate mapping = Mapping((spatial_axis, spatial_axis, spectral_axis)) # Sometimes the slit is perpendicular to the RA or Dec axis. # For example, if the slit is perpendicular to RA, that means # the slope of pix_to_ra will be nearly zero, so make sure # mapping.inverse uses pix_to_dec.inverse. The auto definition # of mapping.inverse is to use the 2nd spatial coordinate, i.e. Dec. if np.isclose(pix_to_dec.slope, 0, atol=1e-8): mapping_tuple = (0, 1) # Account for vertical or horizontal dispersion on detector if spatial_axis: mapping.inverse = Mapping(mapping_tuple[::-1]) else: mapping.inverse = Mapping(mapping_tuple) # The final transform transform = mapping | pix_to_ra & pix_to_dec & pix_to_wavelength det = cf.Frame2D(name='detector', axes_order=(0, 1)) sky = cf.CelestialFrame(name='sky', axes_order=(0, 1), reference_frame=coord.ICRS()) spec = cf.SpectralFrame(name='spectral', axes_order=(2,), unit=(u.micron,), axes_names=('wavelength',)) world = cf.CompositeFrame([sky, spec], name='world') pipeline = [(det, transform), (world, None)] output_wcs = WCS(pipeline) # compute the output array size in WCS axes order, i.e. (x, y) output_array_size = [0, 0] output_array_size[spectral_axis] = len(wavelength_array) output_array_size[spatial_axis] = len(ra_array) # turn the size into a numpy shape in (y, x) order self.data_size = tuple(output_array_size[::-1]) bounding_box = resample_utils.wcs_bbox_from_shape(self.data_size) output_wcs.bounding_box = bounding_box return output_wcs
def blot_images(self): """ Short Summary ------------ Core blotting module Initialize blot_model = input_model 1. Loop over every data model to be blotted and find ra,dec,wavelength for every slice pixel. 2. Using WCS of input image convert ra,dec of input model to tangent plane values: xi,eta 3. For the median sky cube convert the ra,dec of each x,y in this cube to xi,eta using the wcs of the imput image 4. a. Loop over every input_model valid IFU slice pixel and find the median image pixels that fall within the ROI of the center of pixel. The ROI parameters are the same as those used to construct the median sky cube and are stored in the meta data of the Median Cube. b. After all the overlapping median pixels have been found find the weighted flux using this overlapping pixels. The weighting is based on the distance between the detector pixel and the median flux pixel in tangent plane plane. Additional weighting parameters are read in from the median cube meta data. c. The blotted flux (blot.data) = the weighted flux determined in step b. """ t0 = time.time() blot_models = datamodels.ModelContainer() lower_limit = 0.01 instrument_info = instrument_defaults.InstrumentInfo() for model in self.input_models: blot = model.copy() blot.err = None blot.dq = None filename = model.meta.filename indx = filename.rfind('.fits') blot_flux = np.zeros(model.shape, dtype=np.float32) #________________________________________________________________________________ # From the x,y pixel for detector. For MIRI we only work on one channel at a time if self.instrument == 'MIRI': this_par1 = self.channel # only one channel is blotted at a time ch_name = '_ch' + this_par1 blot.meta.filename = filename[:indx] + ch_name + '_blot.fits' # get the detector values for this model xstart, xend = instrument_info.GetMIRISliceEndPts(this_par1) ydet, xdet = np.mgrid[:1024, :1032] #mask out the side channel we aren not working on pixel_mask = np.full(model.shape, False, dtype=bool) pixel_mask[:, xstart:xend] = True ra_det, dec_det, lam_det = model.meta.wcs(xdet, ydet) elif self.instrument == 'NIRSPEC': blot.meta.filename = filename[:indx] + '_blot.fits' # initialize the ra,dec, and wavelength arrays # we will loop over slices and fill in values # the flag_det will be set when a slice pixel is filled in # at the end we will use this flag to pull out valid data ra_det = np.zeros((2048, 2048)) dec_det = np.zeros((2048, 2048)) lam_det = np.zeros((2048, 2048)) flag_det = np.zeros((2048, 2048)) # for NIRSPEC each file has 30 slices # wcs information access seperately for each slice nslices = 30 log.info('Looping over 30 slices on NIRSPEC detector, this takes a little while') for ii in range(nslices): slice_wcs = nirspec.nrs_wcs_set_input(model, ii) x, y = wcstools.grid_from_bounding_box(slice_wcs.bounding_box) ra, dec, lam = slice_wcs(x, y) # the slices are curved on detector so a rectangular region # returns NaNs valid = ~np.isnan(lam) ra = ra[valid] dec = dec[valid] lam = lam[valid] x = x[valid] y = y[valid] xind = _toindex(x) yind = _toindex(y) xind = np.ndarray.flatten(xind) yind = np.ndarray.flatten(yind) ra = np.ndarray.flatten(ra) dec = np.ndarray.flatten(dec) lam = np.ndarray.flatten(lam) ra_det[yind, xind] = ra dec_det[yind, xind] = dec lam_det[yind, xind] = lam flag_det[yind, xind] = 1 # Done looping over slices log.info('Blotting back %s', model.meta.filename) if self.instrument == 'MIRI': valid3 = np.isfinite(lam_det) good_data1 = valid3 & pixel_mask good_data = np.where(good_data1) elif self.instrument == 'NIRSPEC': good_data = np.where(flag_det == 1) y, x = good_data ra_blot = ra_det[good_data] dec_blot = dec_det[good_data] wave_blot = lam_det[good_data] crval1 = model.meta.wcsinfo.crval1 crval2 = model.meta.wcsinfo.crval2 # x,y detector pixels --> xi, eta xi_blot, eta_blot = coord.radec2std(crval1, crval2, ra_blot, dec_blot) # cube spaxel ra,dec values --> xi, eta xi_cube, eta_cube = coord.radec2std(crval1, crval2, self.cube_ra, self.cube_dec) nplane = self.naxis1 * self.naxis2 self.xi_centers = np.reshape(xi_cube[0, :, :], nplane) self.eta_centers = np.reshape(eta_cube[0, :, :], nplane) num = ra_blot.size #________________________________________________________________________________ # For every detector pixel find the overlapping median cube spaxels. # A median spaxel that falls withing the ROI of the center of the detector pixel # in the tangent plane is flagged as an overlapping pixel for ipt in range(0, num - 1): # xx,yy are the index value of the orginal detector frame - # blot image yy = y[ipt] xx = x[ipt] # find the cube values that fall withing ROI of detector xx,yy xdistance = (xi_blot[ipt] - self.xi_centers) ydistance = (eta_blot[ipt] - self.eta_centers) radius = np.sqrt(xdistance * xdistance + ydistance * ydistance) # indexr holds the index of the sky median spaxels that fall within # the spatial ROI of xx,yy location indexr = np.where(radius <= self.rois) #indexz holds the index of the sky median spaxels that fall within # the spectral ROI of wave length assocation with xx,yy indexz = np.where(abs(self.lam_centers - wave_blot[ipt]) <= self.roiw) # Pull out the Cube spaxels falling with ROI regions wave_found = self.lam_centers[indexz] xi_found = self.xi_centers[indexr] eta_found = self.eta_centers[indexr] #________________________________________________________________________________ # form the arrays to be used calculated the weighting d1 = np.array(xi_found - xi_blot[ipt]) / self.cdelt1 d2 = np.array(eta_found - eta_blot[ipt]) / self.cdelt2 d3 = np.array(wave_found - wave_blot[ipt]) / self.cdelt3 dxy = d1 * d1 + d2 * d2 dxy_matrix = np.tile(dxy[np.newaxis].T, [1, d3.shape[0]]) d3_matrix = np.tile(d3 * d3, [dxy_matrix.shape[0], 1]) wdistance = dxy_matrix + d3_matrix weight_distance = np.power(np.sqrt(wdistance), self.weight_power) weight_distance[weight_distance < lower_limit] = lower_limit weight_distance = 1.0 / weight_distance # determine the spaxel xx_cube,yy_cube values of these spaxels in # the ROI so they can be used to pull out the flux of the median # sky cube. yy_cube = (indexr[0] / self.naxis1).astype(np.int) xx_cube = indexr[0] - yy_cube * self.naxis1 scf = np.array([self.cube_flux[zz, yy_cube[ir], xx_cube[ir]] for ir, rr in enumerate(indexr[0]) for zz in indexz[0]]) scf = np.reshape(scf, weight_distance.shape) blot_flux[yy, xx] = np.sum(weight_distance * scf) blot_weight = np.sum(weight_distance) # check for blot_weight !=0 if blot_weight == 0: blot_flux[yy, xx] = 0 else: blot_flux[yy, xx] = blot_flux[yy, xx] / blot_weight #________________________________________________________________________________ blot.data = blot_flux blot_models.append(blot) t1 = time.time() log.info("Time Blot images = %.1f.s" % (t1 - t0,)) return blot_models
def extract_slit(input_model, slit, exp_type): """ Extract a slit from a full frame image. Parameters ---------- input_model : `~jwst.datamodels.image.ImageModel` or `~jwst.datamodels.cube.CubeModel` The input model. slit : `~jwst.transforms.models.Slit` A slit object. exp_type : str The exposure type. Returns ------- new_model : `~jwst.datamodels.SlitModel` The slit data model with WCS attached to it. """ slit_wcs = nirspec.nrs_wcs_set_input(input_model, slit.name) xlo, xhi, ylo, yhi = offset_wcs(slit_wcs) log.info('Name of subarray extracted: %s', slit.name) log.info('Subarray x-extents are: %s %s', xlo, xhi) log.info('Subarray y-extents are: %s %s', ylo, yhi) ndim = len(input_model.data.shape) if ndim == 2: slit_slice = np.s_[ylo: yhi, xlo: xhi] ext_data = input_model.data[slit_slice].copy() ext_err = input_model.err[slit_slice].copy() ext_dq = input_model.dq[slit_slice].copy() ext_var_rnoise = input_model.var_rnoise[slit_slice].copy() ext_var_poisson = input_model.var_poisson[slit_slice].copy() int_times = None elif ndim == 3: slit_slice = np.s_[:, ylo: yhi, xlo: xhi] ext_data = input_model.data[slit_slice].copy() ext_err = input_model.err[slit_slice].copy() ext_dq = input_model.dq[slit_slice].copy() ext_var_rnoise = input_model.var_rnoise[slit_slice].copy() ext_var_poisson = input_model.var_poisson[slit_slice].copy() if (pipe_utils.is_tso(input_model) and hasattr(input_model, 'int_times')): log.debug("TSO data, so copying the INT_TIMES table.") int_times = input_model.int_times.copy() else: int_times = None else: raise ValueError("extract_2d does not work with " "{0} dimensional data".format(ndim)) slit_wcs.bounding_box = util.bounding_box_from_shape(ext_data.shape) # compute wavelengths x, y = wcstools.grid_from_bounding_box(slit_wcs.bounding_box, step=(1, 1)) ra, dec, lam = slit_wcs(x, y) lam = lam.astype(np.float32) new_model = datamodels.SlitModel(data=ext_data, err=ext_err, dq=ext_dq, wavelength=lam, var_rnoise=ext_var_rnoise, var_poisson=ext_var_poisson, int_times=int_times) log.info('Input model type is {}'.format(input_model.__class__.__name__)) new_model.update(input_model) new_model.meta.wcs = slit_wcs return new_model, xlo, xhi, ylo, yhi
def do_correction(input_model, barshadow_model): """Do the Bar Shadow Correction Parameters ---------- input_model: MultiSlitModel datamodel object science data model to be corrected barshadow_model: BarshadowModel datamodel object bar shadow datamodel from reference file Returns ------- output_model: MultiSlitModel datamodel object Science datamodel with bar shadow extensions added """ # # Input is a MultiSlitModel science data model # A MultislitModel has a member .slits that behaves like # a list of Slits, each of which has several data arrays and # keyword metadata items associated # # At this point we're going to have to assume that a Slit is composed # of a set of slit[].nshutters in a vertical line # # Reference file information is a 1x1 ref file and a 1x3 ref file # Both the 1x1 and 1x3 ref files are 1001 pixel high and go from # -1 to 1 in their Y value WCS. # # exp_type = input_model.meta.exposure.type log.info(exp_type) # # Create the pieces that are put together to make the barshadow model shutter_elements = create_shutter_elements(barshadow_model) w0 = barshadow_model.crval1 wave_increment = barshadow_model.cdelt1 y_increment = barshadow_model.cdelt2 shutter_height = 1.0 / y_increment # For each slitlet for slitlet in input_model.slits: slitlet_number = slitlet.slitlet_id log.info('Working on slitlet %d' % slitlet_number) if has_uniform_source(slitlet): shutter_status = slitlet.shutter_state if len(shutter_status) > 0: shadow = create_shadow(shutter_elements, shutter_status) # # For each pixel in the slit subarray # Make a grid of indices for pixels in the subarray x, y = wcstools.grid_from_bounding_box(slitlet.meta.wcs.bounding_box, step=(1,1)) # Create the transformation from slit_frame to detector det2slit = slitlet.meta.wcs.get_transform('detector', 'slit_frame') # Use this transformation to calculate x, y and wavelength xslit, yslit, wavelength = det2slit(x, y) # The returned y values are scaled to where the slit height is 1 # (i.e. a slit goes from -0.5 to 0.5). The barshadow array is scaled # so that the separation between the slit centers is 1, i.e. slit height # + interslit bar yslit = yslit / SLITRATIO # Convert the Y and wavelength to a pixel location # in the bar shadow array index_of_fiducial = shutter_status.find('x') # # The shutters go downwards, i.e. the first shutter in shutter_status corresponds to # the last in the shadow array. So the center of the first shutter referred to in # shutter_status has an index of shadow.shape[0] - shutter_height. Each subsequent # shutter center has an index shutter_height greater. index_of_fiducial_in_array = shadow.shape[0] - shutter_height * (1 + index_of_fiducial) yrow = index_of_fiducial_in_array - yslit * shutter_height wcol = (wavelength - w0)/wave_increment # Interpolate the bar shadow correction for non-Nan pixels correction = interpolate(yrow, wcol, shadow) # Add the correction array and variance to the datamodel slitlet.barshadow = correction else: log.info("Slitlet %d has zero length, correction skipped" % slitlet_number) # # Put an array of ones in a correction extension slitlet.barshadow = np.ones(slitlet.data.shape) else: log.info("Bar shadow correction skipped for slitlet %d (source not uniform)" % slitlet_number) # # Put an array of ones in a correction extension slitlet.barshadow = np.ones(slitlet.data.shape) return input_model
def test_nirspec_ifu_masterbg_user(self): """ Regression test of master background subtraction for NRS IFU when a user 1-D spectrum is provided. """ # input file has 2-D background image added to it input_file = self.get_data(*self.test_dir, 'prism_sci_bkg_cal.fits') # user-provided 1-D background was created from the 2-D background image user_background = self.get_data(*self.test_dir, 'prism_bkg_x1d.fits') result = MasterBackgroundStep.call(input_file, user_background=user_background, save_results=True) # Test 1 compare extracted spectra data with # no background added to extracted spectra from the output # from MasterBackground subtraction. First cube_build has to be run # on the data. result_s3d = CubeBuildStep.call(result) # run 1-D extract on results from MasterBackground step result_1d = Extract1dStep.call(result_s3d, subtract_background=False) # get the 1-D extracted spectrum from the science data in truth directory input_sci_1d_file = self.get_data(*self.ref_loc, 'prism_sci_extract1d.fits') sci_1d = datamodels.open(input_sci_1d_file) # read in the valid wavelengths of the user-1d user_background_model = datamodels.open(user_background) user_wave = user_background_model.spec[0].spec_table['wavelength'] user_flux = user_background_model.spec[0].spec_table['net'] user_wave_valid = np.where(user_flux > 0) min_user_wave = np.amin(user_wave[user_wave_valid]) max_user_wave = np.amax(user_wave[user_wave_valid]) user_background_model.close() # find the waverange covered by both user and science sci_spec_1d = sci_1d.spec[0].spec_table['net'] sci_spec_wave = sci_1d.spec[0].spec_table['wavelength'] result_spec_1d = result_1d.spec[0].spec_table['net'] sci_wave_valid = np.where(sci_spec_1d > 0) min_wave = np.amin(sci_spec_wave[sci_wave_valid]) max_wave = np.amax(sci_spec_wave[sci_wave_valid]) if min_user_wave > min_wave: min_wave = min_user_wave if max_user_wave < max_wave: max_wave = max_user_wave sub_spec = sci_spec_1d - result_spec_1d valid = np.where(np.logical_and(sci_spec_wave > min_wave, sci_spec_wave < max_wave)) sub_spec = sub_spec[valid] sub_spec = sub_spec[1:-2] # endpoints are wacky mean_sub = np.absolute(np.nanmean(sub_spec)) atol = 5.0 assert_allclose(mean_sub, 0, atol=atol) # Test 2 compare the science data with no background # to the output from the masterBackground Subtraction step # background subtracted science image. input_sci_cal_file = self.get_data(*self.test_dir, 'prism_sci_cal.fits') input_sci_model = datamodels.open(input_sci_cal_file) # We don't want the slices gaps to impact the statisitic # loop over the 30 Slices for i in range(30): slice_wcs = nirspec.nrs_wcs_set_input(input_sci_model, i) x, y = grid_from_bounding_box(slice_wcs.bounding_box) ra, dec, lam = slice_wcs(x, y) valid = np.isfinite(lam) result_slice_region = result.data[y.astype(int), x.astype(int)] sci_slice_region = input_sci_model.data[y.astype(int), x.astype(int)] sci_slice = sci_slice_region[valid] result_slice = result_slice_region[valid] sub = result_slice - sci_slice # check for outliers in the science image sci_mean = np.nanmean(sci_slice) sci_std = np.nanstd(sci_slice) upper = sci_mean + sci_std*5.0 lower = sci_mean - sci_std*5.0 mask_clean = np.logical_and(sci_slice < upper, sci_slice > lower) sub_mean = np.absolute(np.nanmean(sub[mask_clean])) atol = 2.0 assert_allclose(sub_mean, 0, atol=atol) # Test 3 Compare background sutracted science data (results) # to a truth file. This data is MultiSlit data input_sci_model.close() result_file = result.meta.filename truth_file = self.get_data(*self.ref_loc, 'prism_sci_bkg_masterbackgroundstep.fits') outputs = [(result_file, truth_file)] self.compare_outputs(outputs) input_sci_model.close() result.close()
def bkg_for_ifu_image(input, tab_wavelength, tab_background): """Create a 2-D background for an IFUImageModel Parameters ---------- input : `~jwst.datamodels.IFUImageModel` The input science data. tab_wavelength : 1-D ndarray The wavelength column read from the 1-D background table. tab_background : 1-D ndarray The flux column read from the 1-D background table, divided by the npixels column. Returns ------- background : `~jwst.datamodels.IFUImageModel` A copy of `input` but with the data replaced by the background, "expanded" from 1-D to 2-D. The dq flags are set to DO_NOT_USE for the pixels outside the region provided in the X1D background wavelength table. """ background = input.copy() background.data[:, :] = 0. min_wave = np.amin(tab_wavelength) max_wave = np.amax(tab_wavelength) if input.meta.instrument.name.upper() == "NIRSPEC": list_of_wcs = nirspec.nrs_ifu_wcs(input) for ifu_wcs in list_of_wcs: x, y = grid_from_bounding_box(ifu_wcs.bounding_box) wl_array = ifu_wcs(x, y)[2] wl_array[np.isnan(wl_array)] = -1. # mask wavelengths not covered by the master background mask_limit = (wl_array > max_wave) | (wl_array < min_wave) wl_array[mask_limit] = -1 # mask_limit is indices into each WCS slice grid. Need them in # full frame coordinates, so we use x and y, which are full-frame full_frame_ind = y[mask_limit].astype(int), x[mask_limit].astype(int) # TODO - add another DQ Flag something like NO_BACKGROUND when we have space in dqflags background.dq[full_frame_ind] = np.bitwise_or(background.dq[full_frame_ind], dqflags.pixel['DO_NOT_USE']) bkg_flux = np.interp(wl_array, tab_wavelength, tab_background, left=0., right=0.) background.data[y.astype(int), x.astype(int)] = bkg_flux.copy() elif input.meta.instrument.name.upper() == "MIRI": shape = input.data.shape grid = np.indices(shape, dtype=np.float64) wl_array = input.meta.wcs(grid[1], grid[0])[2] # first remove the nans from wl_array and replace with -1 mask = np.isnan(wl_array) wl_array[mask] = -1. # next look at the limits of the wavelength table mask_limit = (wl_array > max_wave) | (wl_array < min_wave) wl_array[mask_limit] = -1 # TODO - add another DQ Flag something like NO_BACKGROUND when we have space in dqflags background.dq[mask_limit] = np.bitwise_or(background.dq[mask_limit], dqflags.pixel['DO_NOT_USE']) bkg_flux = np.interp(wl_array, tab_wavelength, tab_background, left=0., right=0.) background.data[:, :] = bkg_flux.copy() else: raise RuntimeError("Exposure type {} is not supported." .format(input.meta.exposure.type)) return background
def _nanminmax(wcsobj): x, y = grid_from_bounding_box(wcsobj.bounding_box) ra, dec, lam = wcsobj(x, y) return np.nanmin(ra), np.nanmax(ra), np.nanmin(dec), np.nanmax(dec)
def do_correction(input_model, pathloss_model): """ Short Summary ------------- Execute all tasks for Path Loss Correction Parameters ---------- input_model : data model object science data to be corrected pathloss_model : pathloss model object pathloss correction data Returns ------- output_model : data model object Science data with pathloss extensions added """ exp_type = input_model.meta.exposure.type log.info(exp_type) output_model = input_model.copy() if exp_type == 'NRS_MSASPEC': slit_number = 0 for slit in output_model.slits: slit_number = slit_number + 1 log.info('Working on slit {}'.format(slit_number)) size = slit.data.size # That has data.size > 0 if size > 0: # Get centering xcenter, ycenter = get_center(exp_type, slit) # Calculate the 1-d wavelength and pathloss vectors # for the source position # Get the aperture from the reference file that matches the slit nshutters = util.get_num_msa_open_shutters(slit.shutter_state) aperture = get_aperture_from_model(pathloss_model, nshutters) if aperture is not None: (wavelength_pointsource, pathloss_pointsource_vector, is_inside_slitlet) = calculate_pathloss_vector(aperture.pointsource_data, aperture.pointsource_wcs, xcenter, ycenter) (wavelength_uniformsource, pathloss_uniform_vector, dummy) = calculate_pathloss_vector(aperture.uniform_data, aperture.uniform_wcs, xcenter, ycenter) if is_inside_slitlet: # Wavelengths in the reference file are in meters, #need them to be in microns wavelength_pointsource *= 1.0e6 wavelength_uniformsource *= 1.0e6 wavelength_array = slit.wavelength # Compute the pathloss 2D correction if is_pointsource(slit.source_type): pathloss_2d = interpolate_onto_grid( wavelength_array, wavelength_pointsource, pathloss_pointsource_vector) else: pathloss_2d = interpolate_onto_grid( wavelength_array, wavelength_uniformsource, pathloss_uniform_vector) # Apply the pathloss 2D correction and attach to datamodel slit.data /= pathloss_2d slit.err /= pathloss_2d slit.var_poisson /= pathloss_2d**2 slit.var_rnoise /= pathloss_2d**2 slit.pathloss = pathloss_2d else: log.warning("Source is outside slit. Skipping " "pathloss correction for slit {}".format(slit_number)) else: log.warning("Cannot find matching pathloss model for slit " "with {} shutters, skipping pathloss correction for this " "slit".format(nshutters)) continue else: log.warning("Slit has data size = {}, skipping " "pathloss correction for this slitlet".format(size)) output_model.meta.cal_step.pathloss = 'COMPLETE' elif exp_type in ['NRS_FIXEDSLIT', 'NRS_BRIGHTOBJ']: slit_number = 0 is_inside_slit = True # For each slit for slit in output_model.slits: log.info(slit.name) slit_number = slit_number + 1 # Get centering xcenter, ycenter = get_center(exp_type, slit) # Calculate the 1-d wavelength and pathloss vectors # for the source position # Get the aperture from the reference file that matches the slit aperture = get_aperture_from_model(pathloss_model, slit.name) if aperture is not None: log.info("Using aperture {}".format(aperture.name)) (wavelength_pointsource, pathloss_pointsource_vector, is_inside_slit) = calculate_pathloss_vector(aperture.pointsource_data, aperture.pointsource_wcs, xcenter, ycenter) (wavelength_uniformsource, pathloss_uniform_vector, dummy) = calculate_pathloss_vector(aperture.uniform_data, aperture.uniform_wcs, xcenter, ycenter) if is_inside_slit: # Wavelengths in the reference file are in meters, need them to be # in microns wavelength_pointsource *= 1.0e6 wavelength_uniformsource *= 1.0e6 wavelength_array = slit.wavelength # Compute the pathloss 2D correction if is_pointsource(slit.source_type): pathloss_2d = interpolate_onto_grid( wavelength_array, wavelength_pointsource, pathloss_pointsource_vector) else: pathloss_2d = interpolate_onto_grid( wavelength_array, wavelength_uniformsource, pathloss_uniform_vector) # Apply the pathloss 2D correction and attach to datamodel slit.data /= pathloss_2d slit.err /= pathloss_2d slit.var_poisson /= pathloss_2d**2 slit.var_rnoise /= pathloss_2d**2 slit.pathloss = pathloss_2d else: log.warning("Source is outside slit. Skipping " "pathloss correction for slit {}".format(slit.name)) else: log.warning("Cannot find matching pathloss model for aperture {} " "skipping pathloss correction for this slit".format(slit.name)) continue output_model.meta.cal_step.pathloss = 'COMPLETE' elif exp_type == 'NRS_IFU': # IFU targets are always inside slit # Get centering xcenter, ycenter = get_center(exp_type, None) # Calculate the 1-d wavelength and pathloss vectors # for the source position aperture = pathloss_model.apertures[0] (wavelength_pointsource, pathloss_pointsource_vector, dummy) = calculate_pathloss_vector(aperture.pointsource_data, aperture.pointsource_wcs, xcenter, ycenter) (wavelength_uniformsource, pathloss_uniform_vector, dummy) = calculate_pathloss_vector(aperture.uniform_data, aperture.uniform_wcs, xcenter, ycenter) # Wavelengths in the reference file are in meters, need them to be # in microns wavelength_pointsource *= 1.0e6 wavelength_uniformsource *= 1.0e6 # Create the 2-d pathloss arrays, initialize with NaNs wavelength_array = np.zeros(input_model.shape, dtype=np.float32) wavelength_array.fill(np.nan) for slice in NIRSPEC_IFU_SLICES: slice_wcs = nirspec.nrs_wcs_set_input(input_model, slice) x, y = wcstools.grid_from_bounding_box(slice_wcs.bounding_box) xmin = int(x.min()) xmax = int(x.max()) ymin = int(y.min()) ymax = int(y.max()) ra, dec, wavelength = slice_wcs(x, y) wavelength_array[ymin:ymax+1, xmin:xmax+1] = wavelength # Compute the pathloss 2D correction if is_pointsource(input_model.meta.target.source_type): pathloss_2d = interpolate_onto_grid( wavelength_array, wavelength_pointsource, pathloss_pointsource_vector) else: pathloss_2d = interpolate_onto_grid( wavelength_array, wavelength_uniformsource, pathloss_uniform_vector) # Apply the pathloss 2D correction and attach to datamodel output_model.data /= pathloss_2d output_model.err /= pathloss_2d output_model.var_poisson /= pathloss_2d**2 output_model.var_rnoise /= pathloss_2d**2 output_model.pathloss = pathloss_2d # This might be useful to other steps output_model.wavelength = wavelength_array output_model.meta.cal_step.pathloss = 'COMPLETE' elif exp_type == 'NIS_SOSS': """NIRISS SOSS pathloss correction is basically a correction for the flux from the 2nd and 3rd order dispersion that falls outside the subarray aperture. The correction depends on the pupil wheel position and column number (or wavelength). The simple option is to do the correction by column number, then the only interpolation needed is a 1-d interpolation into the pupil wheel position dimension.""" # Omit correction if this is a TSO observation if input_model.meta.visit.tsovisit: log.warning("NIRISS SOSS TSO observations skip the pathloss step") output_model.meta.cal_step.pathloss = 'SKIPPED' return output_model pupil_wheel_position = input_model.meta.instrument.pupil_position if pupil_wheel_position is None: log.warning("Unable to get pupil wheel position from PWCPOS keyword " "for {}".format(input_model.meta.filename)) log.warning("Pathloss correction skipped") output_model.meta.cal_step.pathloss = 'SKIPPED' return output_model subarray = input_model.meta.subarray.name # Get the aperture from the reference file that matches the subarray aperture = get_aperture_from_model(pathloss_model, subarray) if aperture is None: log.warning("Unable to get Aperture from reference file " "for subarray {}".format(subarray)) log.warning("Pathloss correction skipped") output_model.meta.cal_step.pathloss = 'SKIPPED' return output_model else: log.info("Aperture {} selected from reference file".format(aperture.name)) pathloss_array = aperture.pointsource_data[0] nrows, ncols = pathloss_array.shape _, data_ncols = input_model.data.shape correction = np.ones(data_ncols, dtype=np.float32) crpix1 = aperture.pointsource_wcs.crpix1 crval1 = aperture.pointsource_wcs.crval1 cdelt1 = aperture.pointsource_wcs.cdelt1 pupil_wheel_index = crpix1 + (pupil_wheel_position - crval1) / cdelt1 - 1 if pupil_wheel_index < 0 or pupil_wheel_index > (ncols - 2): log.info("Pupil Wheel position outside reference file coverage") log.info("Setting pathloss correction to 1.0") else: ix = int(pupil_wheel_index) dx = pupil_wheel_index - ix crpix2 = aperture.pointsource_wcs.crpix2 crval2 = aperture.pointsource_wcs.crval2 cdelt2 = aperture.pointsource_wcs.cdelt2 for row in range(data_ncols): row_1indexed = row + 1 refrow_index = math.floor(crpix2 + (row_1indexed - crval2) / cdelt2 - 0.5) if refrow_index < 0 or refrow_index > (nrows - 1): correction[row] = 1.0 else: correction[row] = (1.0 - dx) * pathloss_array[refrow_index, ix] + \ dx * pathloss_array[refrow_index, ix + 1] pathloss_2d = np.broadcast_to(correction, input_model.data.shape) output_model.data /= pathloss_2d output_model.err /= pathloss_2d output_model.var_poisson /= pathloss_2d**2 output_model.var_rnoise /= pathloss_2d**2 output_model.pathloss = pathloss_2d output_model.meta.cal_step.pathloss = 'COMPLETE' return output_model
def __init__(self, median_model, input_models): """ Short Summary ------------- Class Blot cube holds all the main varibles for blotting an IFU Cube back to the detector Information is pulled out of the Median Sky Cube created by a previous run of cube_build in single mode. Basic parameters of the instrument the data is for is stored. The ra,dec, and wavelenth of the median sky cube is set up Parameters --------- median_model: median input sky cube created from a median stack of all the individual input_models mapped to the full IFU cube imprint on the sky input_models: data model a blotted image is created for each science image Returns ------- CubeBlot class initialzied """ #Pull out the needed information from the Median IFUCube self.median_skycube = median_model self.instrument = median_model.meta.instrument.name self.detector = median_model.meta.instrument.detector #information on how the IFUCube was constructed self.weight_power = median_model.meta.ifu.weight_power self.weighting = median_model.meta.ifu.weighting self.rois = median_model.meta.ifu.roi_spatial self.roiw = median_model.meta.ifu.roi_wave #basic information about the type of data self.grating = None self.filter = None self.subchannel = None self.channel = None if self.instrument == 'MIRI': self.channel = median_model.meta.instrument.channel self.subchannel = median_model.meta.instrument.band.lower() elif self.instrument == 'NIRSPEC': self.grating = median_model.meta.instrument.grating self.filter = median_model.meta.instrument.filter # find the ra,dec,lambda of each element of the IFUCube self.naxis1 = self.median_skycube.data.shape[2] self.naxis2 = self.median_skycube.data.shape[1] self.naxis3 = self.median_skycube.data.shape[0] self.cdelt1 = median_model.meta.wcsinfo.cdelt1 * 3600.0 self.cdelt2 = median_model.meta.wcsinfo.cdelt2 * 3600.0 self.cdelt3 = median_model.meta.wcsinfo.cdelt3 * 3600.0 #_______________________________________________________________________________ xcube, ycube, zcube = wcstools.grid_from_bounding_box( self.median_skycube.meta.wcs.bounding_box, step=(1, 1, 1)) self.cube_ra, self.cube_dec, self.cube_wave = self.median_skycube.meta.wcs( xcube, ycube, zcube) flux = self.median_skycube.data self.cube_flux = flux #wavelength slices self.lam_centers = self.cube_wave[:, 0, 0] # initialize blotted images to be original input images self.input_models = input_models