def _centre_fov(n=55, waverange=(1.0, 2.0)): xsky = np.array([-n, n]) * u.arcsec.to(u.deg) ysky = np.array([-n, n]) * u.arcsec.to(u.deg) sky_hdr = imp_utils.header_from_list_of_xy(xsky, ysky, 1/3600.) imp_hdr = imp_utils.header_from_list_of_xy([-n, n], [-n, n], 1, "D") imp_hdr.update(sky_hdr) fov = FieldOfView(imp_hdr, waverange=waverange*u.um, area=1*u.m**2) return fov
def _centre_micado_fov(n=128, waverange=(1.9, 2.4)): """ n [arcsec] """ xsky = np.array([-n, n]) * u.arcsec.to(u.deg) ysky = np.array([-n, n]) * u.arcsec.to(u.deg) pixscale = 0.004/3600. sky_hdr = imp_utils.header_from_list_of_xy(xsky, ysky, pixscale) imp_hdr = imp_utils.header_from_list_of_xy([-n, n], [-n, n], pixscale, "D") imp_hdr.update(sky_hdr) fov = FieldOfView(imp_hdr, waverange=waverange*u.um, area=1*u.m**2) return fov
def test_is_the_header_split_into_the_right_amount_of_chunks(self): hdr = imp_utils.header_from_list_of_xy([-1.024, 1.024], [-1.024, 1.024], 0.002) hdrs = imp_utils.split_header(hdr, 128) area_sum = np.sum([hdr["NAXIS1"] * hdr["NAXIS2"] for hdr in hdrs]) assert len(hdrs) == 64 assert area_sum == hdr["NAXIS1"] * hdr["NAXIS2"]
def _long_micado_slit_header(): x = np.array([-1.5, 13.5]) / 3600. y = np.array([-0.01, 0.01]) / 3600. pix_scale_deg = 0.004 / 3600. header = imp_utils.header_from_list_of_xy(x, y, pix_scale_deg) header["APERTURE"] = 0 return header
def big_small_hdus(self, big_wh=(20, 10), big_offsets=(0, 0), small_wh=(6, 3), small_offsets=(0, 0), pixel_scale=0.1): w, h = np.array(big_wh) // 2 x = np.array([-w, -w, w, w]) + big_offsets[0] y = np.array([h, -h, -h, h]) + big_offsets[1] big = imp_utils.header_from_list_of_xy(x, y, pixel_scale) im = np.ones([big["NAXIS2"], big["NAXIS1"]]) big = fits.ImageHDU(header=big, data=im) w, h = np.array(small_wh) // 2 x = np.array([-w, -w, w, w]) + small_offsets[0] y = np.array([h, -h, -h, h]) + small_offsets[1] small = imp_utils.header_from_list_of_xy(x, y, pixel_scale) im = np.ones([small["NAXIS2"], small["NAXIS1"]]) small = fits.ImageHDU(header=small, data=im) return big, small
def test_applies_dark_current_with_level_of_dit(self): level, dit, hw = 0.5, 10, 16 hdr = header_from_list_of_xy([-hw, hw], [-hw, hw], 1, "D") dtcr = Detector(hdr) dtcr.meta["dit"] = dit dtcr.meta["ndit"] = 1 dark_eff = DarkCurrent(value=level, **dtcr.meta) dtcr = dark_eff.apply_to(dtcr) assert np.average(dtcr.data) == 5.0 assert np.sum(dtcr.data) == level * dit * (hw * 2)**2
def test_DarkCurrent_plot(self): from scopesim.detector import Detector level, dit, hw = 0.5, 10, 16 from scopesim.optics.image_plane_utils import header_from_list_of_xy hdr = header_from_list_of_xy([-hw, hw], [-hw, hw], 1, "D") eff = electronic.DarkCurrent(value=0.1, dit=10, ndit=20) # raise error for ints! dtcr = Detector(hdr) if PLOTS: eff.plot(dtcr) plt.show()
def test_add_one_image_to_another_in_the_right_position(self): big = imp_utils.header_from_list_of_xy(x=np.array([-20, -20, 20, 20]), y=np.array([10, -10, -10, 10]), pixel_scale=0.1) im = np.zeros([big["NAXIS2"], big["NAXIS1"]]) big = fits.ImageHDU(header=big, data=im) small = imp_utils.header_from_list_of_xy( x=np.array([-3, -3, 3, 3]) + 10, y=np.array([1, -1, -1, 1]) - 5, pixel_scale=0.1) im = np.ones([small["NAXIS2"], small["NAXIS1"]]) small = fits.ImageHDU(header=small, data=im) big = imp_utils.add_imagehdu_to_imagehdu(small, big) ycen, xcen = np.array(big.data.shape) // 2 assert np.sum(big.data[:ycen, xcen:]) == np.sum(small.data) if PLOTS: plt.imshow(big.data, origin="lower") plt.show()
def test_BinnedImage_plot(self): """ Not implemented yet. What do we plot here? """ from scopesim.detector import Detector from scopesim.optics.image_plane_utils import header_from_list_of_xy level, dit, hw = 0.5, 10, 16 hdr = header_from_list_of_xy([-hw, hw], [-hw, hw], 1, "D") dtcr = Detector(hdr) eff = electronic.BinnedImage(bin_size=4) dtcr = eff.apply_to(dtcr) if PLOTS: plt.imshow(dtcr.data) plt.show()
def test_ShotNoise_plot(self): from scopesim.detector import Detector level, dit, hw = 0.5, 10, 16 from scopesim.optics.image_plane_utils import header_from_list_of_xy hdr = header_from_list_of_xy([-hw, hw], [-hw, hw], 1, "D") eff = electronic.ShotNoise() # Does it do something? dtcr = Detector(hdr) if PLOTS: fig = plt.figure(figsize=(6, 3)) plt.subplot(121) eff.plot(dtcr) plt.subplot(122) eff.plot_hist(dtcr) plt.show()
def test_BasicReadoutNoise_plot(self): from scopesim.detector import Detector level, dit, hw = 0.5, 10, 16 from scopesim.optics.image_plane_utils import header_from_list_of_xy hdr = header_from_list_of_xy([-hw, hw], [-hw, hw], 1, "D") eff = electronic.BasicReadoutNoise(noise_std=1, n_channels=1, ndit=1) dtcr = Detector(hdr) if PLOTS: fig = plt.figure(figsize=(6, 3)) plt.subplot(121) eff.plot(dtcr) plt.subplot(122) eff.plot_hist(dtcr) plt.show()
def is_field_in_fov(fov_header, field, wcs_suffix=""): """ Returns True if Source.field footprint is inside the FieldOfView footprint Parameters ---------- fov_header : fits.Header Header from a FieldOfView object field : [astropy.Table, astropy.ImageHDU] Field object from a Source object wcs_suffix : str ["S", "D"] Coordinate system: Sky or Detector Returns ------- is_inside_fov : bool """ if isinstance(field, fits.ImageHDU) and \ field.header.get("BG_SRC") is not None: is_inside_fov = True else: if isinstance(field, Table): x = list(utils.quantity_from_table("x", field, u.arcsec).to(u.deg).value) y = list(utils.quantity_from_table("y", field, u.arcsec).to(u.deg).value) s = wcs_suffix cdelt = utils.quantify(fov_header["CDELT1" + s], u.deg).value field_header = imp_utils.header_from_list_of_xy(x, y, cdelt, s) elif isinstance(field, (fits.ImageHDU, fits.PrimaryHDU)): field_header = field.header else: logging.warning("Input was neither Table nor ImageHDU: {}" "".format(field)) return False ext_xsky, ext_ysky = imp_utils.calc_footprint(field_header, wcs_suffix) fov_xsky, fov_ysky = imp_utils.calc_footprint(fov_header, wcs_suffix) is_inside_fov = min(ext_xsky) < max(fov_xsky) and \ max(ext_xsky) > min(fov_xsky) and \ min(ext_ysky) < max(fov_ysky) and \ max(ext_ysky) > min(fov_ysky) return is_inside_fov
def test_returns_headers(self): apl = ApertureList(filename="test_aperture_list.dat", no_mask=False, pixel_scale=0.01) hdrs = apl.fov_grid() assert all([isinstance(hdr, fits.Header) for hdr in hdrs]) if PLOTS: from scopesim.optics.image_plane import ImagePlane from scopesim.optics.image_plane_utils import header_from_list_of_xy from astropy.io.fits import ImageHDU x = np.array([-3, 3]) / 3600. y = np.array([-3, 3]) / 3600. pixel_scale = 0.01 / 3600 hdr = header_from_list_of_xy(x, y, pixel_scale) implane = ImagePlane(hdr) for i in range(4): hdu = ImageHDU(data=apl[i].mask.astype(int)+1, header=apl[i].header) implane.add(hdu) plt.imshow(implane.data, origin="lower") plt.show()
def test_final_header_is_smaller_for_odd_size(self, x, y, pix): hdr = imp_utils.header_from_list_of_xy([-1., x], [-1., y], pix) hdrs = imp_utils.split_header(hdr, 100) area_sum = np.sum([hdr["NAXIS1"] * hdr["NAXIS2"] for hdr in hdrs]) assert area_sum == hdr["NAXIS1"] * hdr["NAXIS2"]
def _basic_dtcr_header(n=20, pix_size=0.01): xs = [-pix_size * n / 2, pix_size * n / 2] hdr = header_from_list_of_xy(xs, xs, pix_size, "D") return hdr
def extract_area_from_imagehdu(imagehdu, fov_volume): """ Extracts the part of a ImageHDU that fits inside the fov_volume Parameters ---------- imagehdu : fits.ImageHDU The field ImageHDU, either an image of a wavelength [um] cube fov_volume : dict Contains {"xs": [xmin, xmax], "ys": [ymin, ymax], "waves": [wave_min, wave_max], "xy_unit": "deg" or "mm", "wave_unit": "um"} Returns ------- new_imagehdu : fits.ImageHDU """ hdr = imagehdu.header new_hdr = {} x_hdu, y_hdu = imp_utils.calc_footprint(imagehdu) # field edges in "deg" x_fov, y_fov = fov_volume["xs"], fov_volume["ys"] x0s, x1s = max(min(x_hdu), min(x_fov)), min(max(x_hdu), max(x_fov)) y0s, y1s = max(min(y_hdu), min(y_fov)), min(max(y_hdu), max(y_fov)) xp, yp = imp_utils.val2pix(hdr, np.array([x0s, x1s]), np.array([y0s, y1s])) (x0p, x1p), (y0p, y1p) = np.round(xp).astype(int), np.round(yp).astype(int) if x0p == x1p: x1p += 1 if y0p == y1p: y1p += 1 new_hdr = imp_utils.header_from_list_of_xy([x0s, x1s], [y0s, y1s], pixel_scale=hdr["CDELT1"]) if hdr["NAXIS"] == 3: # Look 0.5*wdel past the fov edges in each direction to catch any # slices where the middle wavelength value doesn't fall inside the # fov waverange, but up to 50% of the slice is actually inside the # fov waverange: # E.g. FOV: [1.92, 2.095], HDU bin centres: [1.9, 2.0, 2.1] # CDELT3 = 0.1, and HDU bin edges: [1.85, 1.95, 2.05, 2.15] # So 1.9 slice needs to be multiplied by 0.3, and 2.1 slice should be # multipled by 0.45 to reach the scaled contribution of the edge slices # This scaling factor is: # f = ((hdu_bin_centre - fov_edge [+/-] 0.5 * cdelt3) % cdelt3) / cdelt3 hdu_waves = get_cube_waveset(hdr) wdel = hdr["CDELT3"] wunit = u.Unit(hdr.get("CUNIT3", "AA")) fov_waves = utils.quantify(fov_volume["waves"], u.um).to(wunit).value mask = ((hdu_waves > fov_waves[0] - 0.5 * wdel) * (hdu_waves <= fov_waves[1] + 0.5 * wdel)) # need to go [+/-] half a bin # OC [2021-12-14] if fov range is not covered by the source return nothing if not np.any(mask): print("FOV {} um - {} um: not covered by Source".format(fov_waves[0], fov_waves[1])) return None i0p, i1p = np.where(mask)[0][0], np.where(mask)[0][-1] f0 = (abs(hdu_waves[i0p] - fov_waves[0] + 0.5 * wdel) % wdel) / wdel # blue edge f1 = (abs(hdu_waves[i1p] - fov_waves[1] - 0.5 * wdel) % wdel) / wdel # red edge data = imagehdu.data[i0p:i1p+1, y0p:y1p, x0p:x1p] data[0, :, :] *= f0 if i1p > i0p: data[-1, :, :] *= f1 # w0, w1 : the closest cube wavelengths outside the fov edge wavelengths # fov_waves : the fov edge wavelengths # f0, f1 : the scaling factors for the blue and red edge cube slices # # w0, w1 = hdu_waves[i0p], hdu_waves[i1p] # print(f"\nw0: {w0}, f0: {f0}, {fov_waves}, f1: {f1}, w1: {w1}") new_hdr.update({"NAXIS": 3, "NAXIS3": data.shape[0], "CRVAL3": hdu_waves[i0p], "CRPIX3": 0, "CDELT3": hdr["CDELT3"], "CUNIT3": hdr["CUNIT3"], "CTYPE3": hdr["CTYPE3"], "BUNIT": hdr["BUNIT"]}) else: data = imagehdu.data[y0p:y1p, x0p:x1p] new_hdr["SPEC_REF"] = hdr.get("SPEC_REF") new_imagehdu = fits.ImageHDU(data=data) new_imagehdu.header.update(new_hdr) return new_imagehdu