def test_flux_calib_corr(datadir, plot=False): """ Test the handling of the flux calibration """ filename = datadir.join("test_sensitivity_cube.fits").strpath #filename = datadir.join("test.fits").strpath fcalib_corr = datadir.join("polyvals.txt").strpath wavelengths = [3500.0, 5500.0] alphas = [-3.5, -3.5] scube = SensitivityCube.from_file(filename, wavelengths, alphas) scube_corr1 = SensitivityCube.from_file(filename, wavelengths, alphas) scube_corr1.apply_flux_recalibration( 1.0, flux_calib_correction_file=fcalib_corr) # The aper_corr should be multiplied though in the cubes ratio = scube.sigmas.filled() / scube_corr1.sigmas.filled() if plot: import matplotlib.pyplot as plt ra, dec, wls = scube.wcs.wcs_pix2world(0, 0, range(scube.sigmas.shape[0]), 0) plt.plot(wls, (scube.f50vals[:, 10, 10] - scube_corr1.sigmas[:, 10, 10]) / scube.sigmas[:, 10, 10]) plt.show() # Check something happened assert ratio != pytest.approx(1.0)
def sensitivity_cube(datadir): """ A sensitivity cube to add or read""" filename = datadir.join("test_sensitivity_cube.fits").strpath wavelengths = [3500.0, 5500.0] alphas = [-3.5, -3.5] return SensitivityCube.from_file(filename, wavelengths, alphas)
def test_aper_corr(datadir, aper_corr): """ Test the handling of the aperture correction """ filename = datadir.join("test_sensitivity_cube.fits").strpath wavelengths = [3500.0, 5500.0] alphas = [-3.5, -3.5] scube = SensitivityCube.from_file(filename, wavelengths, alphas, aper_corr=aper_corr) scube_corr1 = SensitivityCube.from_file(filename, wavelengths, alphas, aper_corr=1.0) # The aper_corr should be multiplied though in the cubes ratio = scube.sigmas.filled() / scube_corr1.sigmas.filled() assert ratio == pytest.approx(aper_corr)
def test_completeness_func(datadir, flux, model, sncut, expected): """ Test that a value is returned """ filename = datadir.join("test_sensitivity_cube.fits").strpath wavelengths = [3500.0, 5500.0] alphas = [-3.5, -3.5] if model == "hdr1": scube = SensitivityCube.from_file(filename, wavelengths, alphas, nsigma=1.0, flim_model=model, aper_corr=None) else: scube = SensitivityCube.from_file(filename, wavelengths, alphas, nsigma=1.0, flim_model=model) c = scube.return_completeness(flux, 161.4201, 50.8822, 3478, sncut) assert c == pytest.approx(expected)
def create_sensitivity_cube_from_astrom(racen, deccen, pa, nx, ny, nz, ifusize, wrange=[3470.0, 5542.0], **kwargs): """ Return an (empty) sensitivity cube object to fill with data from simulations later Parameters ---------- racen, deccen : float the central coordinates of the IFU pa : float the IFU rotation nx, ny, nz : int the dimensions of the cube in ra, dec, wave ifusize : float the length of an IFU side in arcsec wrange : array (optional) the lower and upper wavelength limits in Angstrom ***kwargs : arguments to pass to SensitivityCube """ cards = {} cards["NAXIS"] = 3 cards["NAXIS1"] = nx cards["NAXIS2"] = ny cards["NAXIS3"] = nz cards["CTYPE1"] = "RA---TAN" cards["CTYPE2"] = "DEC--TAN" cards["CTYPE3"] = "Wave " cards["CUNIT1"] = "deg " cards["CUNIT2"] = "deg " cards["CRPIX1"] = nx / 2. + 0.5 cards["CRPIX2"] = ny / 2. + 0.5 cards["CRPIX3"] = 1.0 coord = SkyCoord(racen * u.deg, deccen * u.deg) cards["CRVAL1"] = racen #deg cards["CRVAL2"] = deccen #deg cards["CRVAL3"] = wrange[0] #AA deltapix = (float(ifusize) / nx / 3600.0) # this is rotation in focal plane, maybe not the IFU rot = deg2rad(pa) cards["CROTA2"] = pa cards["CD1_1"] = deltapix * cos(rot) cards["CD1_2"] = deltapix * sin(rot) cards["CD1_3"] = 0.0 cards["CD2_1"] = -1.0 * deltapix * sin(rot) cards["CD2_2"] = deltapix * cos(rot) cards["CD2_3"] = 0.0 cards["CD3_1"] = 0.0 cards["CD3_2"] = 0.0 cards["CD3_3"] = (wrange[1] - wrange[0]) / nz header = Header(cards=cards) sigmas = zeros((nz, ny, nx)) alphas = zeros((nz, ny, nx)) return SensitivityCube(sigmas, header, None, alphas, aper_corr=1.0, nsigma=1.0, **kwargs)
def add_sensitivity_cube_to_hdf5(args=None): """ Command line tool to add a sensitivity cube(s) to an HDF5 containter """ import argparse parser = argparse.ArgumentParser( description="Add sensitivty cubes to HDF5 container", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument( "--regex", default=".*(2[0-9]{7}v[0-9]{3})_[0-9]{3}_([0-9]{3})", help="""Regex with two capture groups, the first for datevshot the second for IFU slot""", ) parser.add_argument("--append", action="store_true", help="Append to existing HDF5") parser.add_argument( "--alpha", type=float, help="Alpha for Fleming function", default=-3.5 ) parser.add_argument( "fits_files", type=str, nargs="+", help="Files to add, filename must follow convention set out in --regex option", ) parser.add_argument("hdf5_out", type=str, help="HDF5 container to add to") opts = parser.parse_args(args=args) if opts.append: hdfcont = SensitivityCubeHDF5Container(opts.hdf5_out, mode="a") else: hdfcont = SensitivityCubeHDF5Container(opts.hdf5_out, mode="w") # Compile the regex so it's faster regex = compile(opts.regex) for fn in opts.fits_files: m = regex.match(fn) datevshot = m.group(1) ifuslot = m.group(2) _logger.info( "Inserting {:s} with dateshot {:s} and IFU slot {:s}".format( fn, datevshot, ifuslot ) ) scube = SensitivityCube.from_file( fn, [3500.0, 5500.0], [opts.alpha, opts.alpha] ) hdfcont.add_sensitivity_cube("virus_" + datevshot, "ifuslot_" + ifuslot, scube) hdfcont.close()
def extract_ifu_sensitivity_cube(self, ifuslot, datevshot=None, cache_sim_interp=True): """ Extract the sensitivity cube from IFU (ifuslot). If multiple shots are saved in the file specify which to use with datevshot Parameters ---------- ifuslot : string the IFU slot to extract datevshot : string (Optional) the datevshot if multiple shots are stored in the HDF5. If None then test that one shot is present and return the IFU for that cache_sim_interp : bool cache the simulation interpolator to speed up execution (default: True) Returns ------- scube : hetdex_api.flux_limits.sensitivity_cube:SensitivityCube the sensitivity cube """ # Use first shot if dateshot not specified if not datevshot: shots = self.h5file.list_nodes(self.h5file.root) nshots = len(shots) if nshots > 1: _logger.warn( """Datevshot not specified but multiple shots in file! Using first in file.""" ) shot = shots[0] else: shot = self.h5file.get_node(self.h5file.root, name=datevshot) if self.h5mask: mask = self.h5mask.get_node(self.h5mask.root.Mask, name=ifuslot).read() else: mask = None # Now get the desired IFU ifu = self.h5file.get_node(shot, name=ifuslot) # Extract the data we need for a sensitivity cube header = ifu.attrs.header wavelengths = ifu.attrs.wavelengths alphas = ifu.attrs.alphas # Remove any aperture correction sigmas = ifu.read() / ifu.attrs.aper_corr try: nsigma = ifu.attrs.nsigma except AttributeError as e: if self.flim_model == "hdr1": nsigma = 6.0 else: nsigma = 1.0 print("No nsigma found, assuming nsigma={:2.1f} ".format(nsigma)) # Force apcor to be 1.0 here, so we don't double count it return SensitivityCube(sigmas, header, wavelengths, alphas, nsigma=nsigma, flim_model=self.flim_model, aper_corr=self.aper_corr, mask=mask, cache_sim_interp=cache_sim_interp)
def itercubes(self, datevshot=None, cache_sim_interp=True): """ Iterate over the IFUs Parameters ---------- datevshot : str (Optional) specify a datevshot or just take the first shot in the HDF5 file Yields ------ ifuslot : str the IFU slot of the returned IFU scube : SensitivityCube a sensitivity cube object """ # Use first shot if dateshot not specified if not datevshot: shots = self.h5file.list_nodes(self.h5file.root) nshots = len(shots) if nshots > 1: _logger.warn( """Datevshot not specified but multiple shots in file! Using first in file.""" ) shot = shots[0] else: shot = self.h5file.get_node(self.h5file.root, name=datevshot) first = True warn = True for ifu in shot: # Extract the data we need for a sensitivity cube header = ifu.attrs.header wavelengths = ifu.attrs.wavelengths alphas = ifu.attrs.alphas sigmas = ifu.read() / ifu.attrs.aper_corr if first: verbose = self.verbose first = False else: verbose = False if self.h5mask: mask = self.h5mask.get_node(self.h5mask.root.Mask, name=ifu.name).read() else: mask = None try: nsigma = ifu.attrs.nsigma except AttributeError as e: if self.flim_model == "hdr1": nsigma = 6.0 else: nsigma = 1.0 if warn: print("No nsigma found, assuming nsigma={:2.1f} (warning will not repeat)".format(nsigma)) warn = False # XXX HACK HACK HACK to change alpha #alphas = [-1.9, -1.9] yield ifu.name, SensitivityCube(sigmas, header, wavelengths, alphas, flim_model=self.flim_model, aper_corr=self.aper_corr, nsigma=nsigma, mask=mask, verbose=verbose, cache_sim_interp=cache_sim_interp)
def collapse_datacubes_command(args=None): """ Produce combined flux limit versus wavelength estimates for sensitivity cubes passed on command line """ import argparse # Command line options parser = argparse.ArgumentParser(description=""" Collapse the RA and DEC of a single or set of sensitivity cube(s) to produce one file of 50% flux limit versus wavelength. """) parser.add_argument("--plot", type=str, help="Filename for optional plot", default="") parser.add_argument( "--fmin", help="Minimum flux to consider when interpolating for 50% limit", type=float, default=1e-17) parser.add_argument( "--fmax", help="""Maximum flux to consider when interpolating for 50% limit. Regions not 99% at this flux ignored!""", type=float, default=1e-15) parser.add_argument( "--nbins", help= "Number of flux bin to use when interpolating to measure 50% limit", type=int, default=100) parser.add_argument( "--alpha", help="The alpha of the Fleming function fit (default=-3.5)", default=-3.5, type=float) parser.add_argument( "scubes", nargs='+', help="The sensitivity cube(s) you want to collapse and combine") parser.add_argument("output", help="(Ascii) file to output to", type=str) opts = parser.parse_args(args=args) # Stores a list of the completeness versus flux and lambda for # all the cubes compl_cube_list = [] # Flux bins to compute completeness in fluxes = linspace(opts.fmin, opts.fmax, opts.nbins) # A list of the number of good pixels in each cube as # a function of wavelength npixes_list = [] for cube_name in opts.scubes: # Currently fix the alpha versus wavelength tscube = SensitivityCube.from_file(cube_name, [3500.0, 5500.0], [opts.alpha, opts.alpha]) lambda_, npix, compls = return_spatially_collapsed_cube(tscube, fluxes) compl_cube_list.append(compls) npixes_list.append(npix) # Produce a combined cube of completness versus flux and lambda, # weighted by the number of good pixels in each cube combined_cube = compl_cube_list[0] * npixes_list[0] for npix, compl_cube in zip(npixes_list[1:], compl_cube_list[1:]): combined_cube += npix * compl_cube # This should give the correct completeness versus lambda and flux, ignoring empty pixels combined_cube /= sum(array(npixes_list), axis=0) # This should give is the 50% flux limits and the new alpha values f50vals, alphas = compute_new_fleming_fits(lambda_, fluxes, combined_cube) if opts.plot: plot_collapsed_cube(f50vals, alphas, lambda_, fluxes, combined_cube, opts.plot) # Write out table = Table([lambda_, f50vals, alphas], names=["wavelength", "f50", "alpha"]) table.write(opts.output, format="ascii")