def test_legendre(): x = np.pi*np.linspace(0, 1., 100) y = np.sin(x) # Legendre pypeitFit = fitting.PypeItFit(xval=x, yval=y, func='legendre', order=np.array([3])) pypeitFit.fit() # = fitting.func_fit(x, y, 'legendre', 3) np.testing.assert_allclose(pypeitFit.fitc, np.array([ 6.37115652e-01, 6.83317251e-17, -6.84581686e-01, -7.59352737e-17]), atol=1e-9)
def test_pypeitfit(): out_file = data_path('test_fit.fits') if os.path.isfile(out_file): os.remove(out_file) pypeitFit = fitting.PypeItFit(fitc=np.arange(100).astype(float)) # Write pypeitFit.to_file(out_file) # Read pypeitFit2 = fitting.PypeItFit.from_file(out_file) assert np.array_equal(pypeitFit.fitc, pypeitFit2.fitc) pypeitFit2.to_file(out_file, overwrite=True) # Finish os.remove(out_file)
def test_polynomial(): """ Run the parameter setup script """ x = np.pi*np.linspace(0, 1., 100) y = np.sin(x) # Polynomial pypeitFit = fitting.PypeItFit(xval=x, yval=y, func='polynomial', order=np.array([3])) pypeitFit.fit() #pypeitFit = fitting.func_fit(x, y, 'polynomial', 3) np.testing.assert_allclose(pypeitFit.fitc, np.array([ -4.74660344e-02, 1.30745471e+00, -4.16175760e-01, 3.08557167e-18]), atol=1e-9) # Evaluate val = pypeitFit.eval(x) assert np.isclose(val[0], -0.04746603), 'Bad value'
def pypeit_arcspec(in_file, slit, binspec, binning=None): """ Load up the arc spectrum from an input JSON file Args: in_file (str): File containing the arc spectrum and or fit slit (int): slit index Returns: tuple: np.ndarray, np.ndarray, PypeItFit: wave, flux, pypeitFitting """ if 'json' in in_file: wv_dict = ltu.loadjson(in_file) iwv_calib = wv_dict[str(slit)] pypeitFitting = fitting.PypeItFit(fitc=np.array(iwv_calib['fitc']), func=iwv_calib['function'], minx=iwv_calib['fmin'], maxx=iwv_calib['fmax']) if binning is not None and binning != binspec: embed(header='Not ready for this yet!') else: x = np.arange(len(iwv_calib['spec'])) wv_vac = pypeitFitting.eval(x / iwv_calib['xnorm']) #wv_vac = utils.func_val(iwv_calib['fitc'], x/iwv_calib['xnorm'], iwv_calib['function'], # minx=iwv_calib['fmin'], maxx=iwv_calib['fmax']) flux = np.array(iwv_calib['spec']).flatten() elif 'fits' in in_file: wvcalib = wavecalib.WaveCalib.from_file(in_file) idx = np.where(wvcalib.spat_ids == slit)[0][0] flux = wvcalib.arc_spectra[:, idx] # npix = flux.size if binning is not None and binning != binspec: npix = int(npix * binning / binspec) x = np.arange(npix) / (npix - 1) else: x = np.arange(npix) / (npix - 1) # Evaluate wv_vac = wvcalib.wv_fits[idx].pypeitfit.eval(x) pypeitFitting = wvcalib.wv_fits[idx].pypeitfit else: raise IOError("Bad in_file {}".format(in_file)) # Return return wv_vac, flux, pypeitFitting
def test_wavefit(): "Fuss with the WaveFit DataContainer" out_file = data_path('test_wavefit.fits') if os.path.isfile(out_file): os.remove(out_file) pypeitFit = fitting.PypeItFit(fitc=np.arange(5).astype(float)) # ions = np.asarray(['CdI', 'HgI']) ion_bits = np.zeros(len(ions), dtype=wv_fitting.WaveFit.bitmask.minimum_dtype()) for kk, ion in enumerate(ions): ion_bits[kk] = wv_fitting.WaveFit.bitmask.turn_on(ion_bits[kk], ion) # Do it waveFit = wv_fitting.WaveFit(99, pypeitfit=pypeitFit, pixel_fit=np.arange(10).astype(float), wave_fit=np.linspace(1., 10., 10), sigrej=3., ion_bits=ion_bits) # Write waveFit.to_file(out_file) # Read waveFit2 = wv_fitting.WaveFit.from_file(out_file) assert np.array_equal(waveFit.pypeitfit.fitc, waveFit2.pypeitfit.fitc) # Write again waveFit2.to_file(out_file, overwrite=True) # And one more read waveFit2b = wv_fitting.WaveFit.from_file(out_file) assert np.array_equal(waveFit.pypeitfit.fitc, waveFit2b.pypeitfit.fitc) # Finish os.remove(out_file) # No fit waveFit3 = wv_fitting.WaveFit(99, pypeitfit=None, pixel_fit=np.arange(10).astype(float), wave_fit=np.linspace(1., 10., 10), sigrej=3., ion_bits=ion_bits) waveFit3.to_file(out_file) waveFit4 = wv_fitting.WaveFit.from_file(out_file) assert waveFit4.pypeitfit is None # Clean up os.remove(out_file)
def test_wavecalib(): "Fuss with the WaveCalib DataContainer" out_file = data_path('test_wavecalib.fits') if os.path.isfile(out_file): os.remove(out_file) # Pieces pypeitFit = fitting.PypeItFit(fitc=np.arange(5).astype(float), xval=np.linspace(1, 100., 100)) # 2D fit pypeitFit2 = fitting.PypeItFit(fitc=np.linspace((1, 2), (10, 20), 10), xval=np.linspace(1, 100., 100), x2=np.linspace(1, 100., 100)) waveFit = wv_fitting.WaveFit(232, pypeitfit=pypeitFit, pixel_fit=np.arange(10).astype(float), wave_fit=np.linspace(1., 10., 10)) waveCalib = wavecalib.WaveCalib(wv_fits=np.asarray([waveFit]), nslits=1, spat_ids=np.asarray([232]), wv_fit2d=pypeitFit2) # Write waveCalib.to_file(out_file) # Read waveCalib2 = wavecalib.WaveCalib.from_file(out_file) # Test assert np.array_equal(waveCalib.spat_ids, waveCalib2.spat_ids), 'Bad spat_ids' assert np.array_equal(waveCalib.wv_fits[0].pypeitfit.fitc, waveCalib2.wv_fits[0].pypeitfit.fitc), 'Bad fitc' assert np.array_equal(waveCalib.wv_fit2d.xval, waveCalib2.wv_fit2d.xval) # Write again! waveCalib2.to_file(out_file, overwrite=True) # Finish os.remove(out_file) # With None (failed wave) spat_ids = np.asarray([232, 949]) waveCalib3 = wavecalib.WaveCalib(wv_fits=np.asarray( [waveFit, wv_fitting.WaveFit(949)]), nslits=2, spat_ids=spat_ids, wv_fit2d=pypeitFit2) waveCalib3.to_file(out_file) waveCalib4 = wavecalib.WaveCalib.from_file(out_file) # Check masking slits = slittrace.SlitTraceSet(left_init=np.full((1000, 2), 2, dtype=float), right_init=np.full((1000, 2), 8, dtype=float), pypeline='MultiSlit', spat_id=spat_ids, nspat=2, PYP_SPEC='dummy') slits.mask_wvcalib(waveCalib3) assert slits.bitmask.flagged(slits.mask[1], flag='BADWVCALIB') # Finish os.remove(out_file)
def spec_flex_shift(obj_skyspec, arx_skyspec, arx_lines, mxshft=20): """ Calculate shift between object sky spectrum and archive sky spectrum Args: obj_skyspec (:class:`linetools.spectra.xspectrum1d.XSpectrum1d`): Spectrum of the sky related to our object arx_skyspec (:class:`linetools.spectra.xspectrum1d.XSpectrum1d`): Archived sky spectrum arx_lines (tuple): Line information returned by arc.detect_lines for the Archived sky spectrum mxshft (float, optional): Maximum allowed shift from flexure; note there are cases that have been known to exceed even 30 pixels.. Returns: dict: Contains flexure info """ # TODO None of these routines should have dependencies on XSpectrum1d! # Determine the brightest emission lines msgs.warn("If we use Paranal, cut down on wavelength early on") arx_amp, arx_amp_cont, arx_cent, arx_wid, _, arx_w, arx_yprep, nsig \ = arx_lines obj_amp, obj_amp_cont, obj_cent, obj_wid, _, obj_w, obj_yprep, nsig_obj \ = arc.detect_lines(obj_skyspec.flux.value) # Keep only 5 brightest amplitude lines (xxx_keep is array of # indices within arx_w of the 5 brightest) arx_keep = np.argsort(arx_amp[arx_w])[-5:] obj_keep = np.argsort(obj_amp[obj_w])[-5:] # Calculate wavelength (Angstrom per pixel) arx_disp = np.append( arx_skyspec.wavelength.value[1] - arx_skyspec.wavelength.value[0], arx_skyspec.wavelength.value[1:] - arx_skyspec.wavelength.value[:-1]) obj_disp = np.append( obj_skyspec.wavelength.value[1] - obj_skyspec.wavelength.value[0], obj_skyspec.wavelength.value[1:] - obj_skyspec.wavelength.value[:-1]) # Calculate resolution (lambda/delta lambda_FWHM)..maybe don't need # this? can just use sigmas arx_idx = (arx_cent + 0.5).astype( np.int)[arx_w][arx_keep] # The +0.5 is for rounding arx_res = arx_skyspec.wavelength.value[arx_idx]/\ (arx_disp[arx_idx]*(2*np.sqrt(2*np.log(2)))*arx_wid[arx_w][arx_keep]) obj_idx = (obj_cent + 0.5).astype( np.int)[obj_w][obj_keep] # The +0.5 is for rounding obj_res = obj_skyspec.wavelength.value[obj_idx]/ \ (obj_disp[obj_idx]*(2*np.sqrt(2*np.log(2)))*obj_wid[obj_w][obj_keep]) if not np.all(np.isfinite(obj_res)): msgs.warn( 'Failed to measure the resolution of the object spectrum, likely due to error ' 'in the wavelength image.') return None msgs.info("Resolution of Archive={0} and Observation={1}".format( np.median(arx_res), np.median(obj_res))) # Determine sigma of gaussian for smoothing arx_sig2 = np.power(arx_disp[arx_idx] * arx_wid[arx_w][arx_keep], 2) obj_sig2 = np.power(obj_disp[obj_idx] * obj_wid[obj_w][obj_keep], 2) arx_med_sig2 = np.median(arx_sig2) obj_med_sig2 = np.median(obj_sig2) if obj_med_sig2 >= arx_med_sig2: smooth_sig = np.sqrt(obj_med_sig2 - arx_med_sig2) # Ang smooth_sig_pix = smooth_sig / np.median(arx_disp[arx_idx]) arx_skyspec = arx_skyspec.gauss_smooth(smooth_sig_pix * 2 * np.sqrt(2 * np.log(2))) else: msgs.warn("Prefer archival sky spectrum to have higher resolution") smooth_sig_pix = 0. msgs.warn("New Sky has higher resolution than Archive. Not smoothing") #smooth_sig = np.sqrt(arx_med_sig**2-obj_med_sig**2) #Determine region of wavelength overlap min_wave = max(np.amin(arx_skyspec.wavelength.value), np.amin(obj_skyspec.wavelength.value)) max_wave = min(np.amax(arx_skyspec.wavelength.value), np.amax(obj_skyspec.wavelength.value)) #Smooth higher resolution spectrum by smooth_sig (flux is conserved!) # if np.median(obj_res) >= np.median(arx_res): # msgs.warn("New Sky has higher resolution than Archive. Not smoothing") #obj_sky_newflux = ndimage.gaussian_filter(obj_sky.flux, smooth_sig) # else: #tmp = ndimage.gaussian_filter(arx_sky.flux, smooth_sig) # arx_skyspec = arx_skyspec.gauss_smooth(smooth_sig_pix*2*np.sqrt(2*np.log(2))) #arx_sky.flux = ndimage.gaussian_filter(arx_sky.flux, smooth_sig) # Define wavelengths of overlapping spectra keep_idx = np.where((obj_skyspec.wavelength.value >= min_wave) & (obj_skyspec.wavelength.value <= max_wave))[0] #keep_wave = [i for i in obj_sky.wavelength.value if i>=min_wave if i<=max_wave] #Rebin both spectra onto overlapped wavelength range if len(keep_idx) <= 50: msgs.warn("Not enough overlap between sky spectra") return None # rebin onto object ALWAYS keep_wave = obj_skyspec.wavelength[keep_idx] arx_skyspec = arx_skyspec.rebin(keep_wave) obj_skyspec = obj_skyspec.rebin(keep_wave) # Trim edges (rebinning is junk there) arx_skyspec.data['flux'][0, :2] = 0. arx_skyspec.data['flux'][0, -2:] = 0. obj_skyspec.data['flux'][0, :2] = 0. obj_skyspec.data['flux'][0, -2:] = 0. # Set minimum to 0. For bad rebinning and for pernicious extractions obj_skyspec.data['flux'][0, :] = np.maximum(obj_skyspec.data['flux'][0, :], 0.) arx_skyspec.data['flux'][0, :] = np.maximum(arx_skyspec.data['flux'][0, :], 0.) # Normalize spectra to unit average sky count norm = np.sum(obj_skyspec.flux.value) / obj_skyspec.npix norm2 = np.sum(arx_skyspec.flux.value) / arx_skyspec.npix if norm <= 0: msgs.warn("Bad normalization of object in flexure algorithm") msgs.warn("Will try the median") norm = np.median(obj_skyspec.flux.value) if norm <= 0: msgs.warn("Improper sky spectrum for flexure. Is it too faint??") return None if norm2 <= 0: msgs.warn( 'Bad normalization of archive in flexure. You are probably using wavelengths ' 'well beyond the archive.') return None obj_skyspec.flux = obj_skyspec.flux / norm arx_skyspec.flux = arx_skyspec.flux / norm2 # Deal with bad pixels msgs.work("Need to mask bad pixels") # Deal with underlying continuum msgs.work("Consider taking median first [5 pixel]") everyn = obj_skyspec.npix // 20 pypeitFit_obj, _ = fitting.iterfit(obj_skyspec.wavelength.value, obj_skyspec.flux.value, nord=3, kwargs_bspline={'everyn': everyn}, kwargs_reject={ 'groupbadpix': True, 'maxrej': 1 }, maxiter=15, upper=3.0, lower=3.0) obj_sky_cont, _ = pypeitFit_obj.value(obj_skyspec.wavelength.value) obj_sky_flux = obj_skyspec.flux.value - obj_sky_cont pypeitFit_sky, _ = fitting.iterfit(arx_skyspec.wavelength.value, arx_skyspec.flux.value, nord=3, kwargs_bspline={'everyn': everyn}, kwargs_reject={ 'groupbadpix': True, 'maxrej': 1 }, maxiter=15, upper=3.0, lower=3.0) arx_sky_cont, _ = pypeitFit_sky.value(arx_skyspec.wavelength.value) arx_sky_flux = arx_skyspec.flux.value - arx_sky_cont # Consider sharpness filtering (e.g. LowRedux) msgs.work("Consider taking median first [5 pixel]") #Cross correlation of spectra #corr = np.correlate(arx_skyspec.flux, obj_skyspec.flux, "same") corr = np.correlate(arx_sky_flux, obj_sky_flux, "same") #Create array around the max of the correlation function for fitting for subpixel max # Restrict to pixels within maxshift of zero lag lag0 = corr.size // 2 #mxshft = settings.argflag['reduce']['flexure']['maxshift'] max_corr = np.argmax(corr[lag0 - mxshft:lag0 + mxshft]) + lag0 - mxshft subpix_grid = np.linspace(max_corr - 3., max_corr + 3., 7) #Fit a 2-degree polynomial to peak of correlation function. JFH added this if/else to not crash for bad slits if np.any(np.isfinite(corr[subpix_grid.astype(np.int)])): fit = fitting.PypeItFit(xval=subpix_grid, yval=corr[subpix_grid.astype(np.int)], func='polynomial', order=np.atleast_1d(2)) fit.fit() success = True max_fit = -0.5 * fit.fitc[1] / fit.fitc[2] else: fit = fitting.PypeItFit(xval=subpix_grid, yval=0.0 * subpix_grid, func='polynomial', order=np.atleast_1d(2)) fit.fit() success = False max_fit = 0.0 msgs.warn('Flexure compensation failed for one of your objects') #Calculate and apply shift in wavelength shift = float(max_fit) - lag0 msgs.info("Flexure correction of {:g} pixels".format(shift)) #model = (fit[2]*(subpix_grid**2.))+(fit[1]*subpix_grid)+fit[0] return dict(polyfit=fit, shift=shift, subpix=subpix_grid, corr=corr[subpix_grid.astype(np.int)], sky_spec=obj_skyspec, arx_spec=arx_skyspec, corr_cen=corr.size / 2, smooth=smooth_sig_pix, success=success)