def __call__(self, img, trace_object): self.last_trace = trace_object self.last_img = img if self.apwidth < 1: raise ValueError('apwidth must be >= 1') if self.skysep < 1: raise ValueError('skysep must be >= 1') if self.skywidth < 1: raise ValueError('skywidth must be >= 1') trace_line = trace_object.line onedspec = np.zeros_like(trace_line) skysubflux = np.zeros_like(trace_line) fluxerr = np.zeros_like(trace_line) for i in range(0, len(trace_line)): # first do the aperture flux # juuuust in case the trace gets too close to an edge widthup = self.apwidth / 2 widthdn = self.apwidth / 2 if (trace_line[i] + widthup > img.shape[0]): widthup = img.shape[0] - trace_line[i] - 1 if (trace_line[i] - widthdn < 0): widthdn = trace_line[i] - 1 # simply add up the total flux around the trace_line +/- width onedspec[i] = np.nansum( img[int(trace_line[i] - widthdn):int(trace_line[i] + widthup + 1), i] ) # now do the sky fit itrace_line = int(trace_line[i]) sky_y = np.append( np.arange( itrace_line - self.apwidth - self.skysep - self.skywidth, itrace_line - self.apwidth - self.skysep ), np.arange( itrace_line + self.apwidth + self.skysep + 1, itrace_line + self.apwidth + self.skysep + self.skywidth + 1 ) ) sky_flux = img[sky_y, i] if (self.skydeg > 0): # fit a polynomial to the sky in this column pfit = np.polyfit(sky_y, sky_flux, self.skydeg) # define the aperture in this column ap = np.arange( trace_line[i] - self.apwidth, trace_line[i] + self.apwidth + 1 ) # evaluate the polynomial across the aperture, and sum skysubflux[i] = np.nansum(np.polyval(pfit, ap)) elif (self.skydeg == 0): skysubflux[i] = np.nanmean(sky_flux) * (self.apwidth * 2.0 + 1) # finally, compute the error in this pixel sigma_bkg = np.nanstd(sky_flux) # stddev in the background data n_bkg = np.float(len(sky_y)) # number of bkgd pixels n_ap = self.apwidth * 2. + 1 # number of aperture pixels # based on aperture phot err description by F. Masci, Caltech: # http://wise2.ipac.caltech.edu/staff/fmasci/ApPhotUncert.pdf fluxerr[i] = np.sqrt( np.nansum(onedspec[i] - skysubflux[i]) + (n_ap + n_ap**22 / n_bkg) * (sigma_bkg**2) ) spec = Spectrum1D( spectral_axis=np.arange(len(onedspec)) * u.pixel, flux=onedspec * img.unit, uncertainty=StdDevUncertainty(fluxerr) ) skyspec = Spectrum1D( spectral_axis=np.arange(len(onedspec)) * u.pixel, flux=skysubflux * img.unit ) return spec, skyspec
def wrapper(data, spectral_axis, *args, **kwargs): spec = Spectrum1D(flux=u.Quantity(data), spectral_axis=spectral_axis) return func(spec, *args, **kwargs).flux.value
# The spectrum is in the second HDU of this file. f = fits.open(filename) specdata = f[1].data # doctest: +REMOTE_DATA z = cz.loc[gal][0] / 3e5 #z = f[2].data.Z[0] print(z) f.close() lamb_full = 10**specdata['loglam'] * u.AA # doctest: +REMOTE_DATA lamb = lamb_full[lamb_full < 6900 * u.AA] flux = specdata['flux'] * 10**-17 * u.Unit( 'erg cm-2 s-1 AA-1') # doctest: +REMOTE_DATA flux = flux[lamb_full < 6900 * u.AA] spec = Spectrum1D(spectral_axis=lamb, flux=flux) # doctest: +REMOTE_DATA cont_norm_spec = spec / fit_generic_continuum(spec)( spec.spectral_axis) # doctest: +REMOTE_DATA wave = np.array(cont_norm_spec.wavelength) flux = np.array(cont_norm_spec.flux) new_wave = wave / (1 + z) #/3e5)) new_flux = flux * (1 + z) #/3e5))#rebin_spec(new_wave,flux,wave) new_spec = Spectrum1D( spectral_axis=new_wave * u.AA, flux=new_flux * u.Unit('erg cm-2 s-1 AA-1')) # doctest: +REMOTE_DATA heii_wave = new_wave[(new_wave > 4665) & (new_wave < 4705)] heii_flux = new_flux[(new_wave > 4665) & (new_wave < 4705)] heii_spec = Spectrum1D(
snsp.generate_spectra(save_as=pickle_name, use_template_star=use_template_star) # print(snsp._preprocessed_snap.s['smooth']) # print(snsp._preprocessed_snap.s['mass']) # snsp.generate_spectra_from_pickle(pickle_name) # Do noise: # We defined the contamination range (the dispersion ) in a way that the residuals # between the constructed spectrum and the original spectrum of # the library to be less than ~ 0.073. print('Computing noise') sigma = 0.07 noise = np.random.normal( loc=0.0, scale=sigma * snsp.spectrum.flux) * snsp.spectrum.flux.unit print('Adding noise to signal') new_spectrum = Spectrum1D(spectral_axis=snsp.spectrum.wavelength, flux=snsp.spectrum.flux + noise) old_spectrum = copy.deepcopy(snsp.spectrum) snsp.spectrum = new_spectrum cg = CubeGenerator(snsp, bins=bins) cg.sum_on_line_of_sigth() cube = cg.create_spectral_cube() cube.write(out_name + 'pix{}_sum.fits'.format(bins), overwrite=True) im = cg.sph_projection_direct(num_threads=num_threads) cube_sph = cg.create_spectral_cube() if do_spectral_rebinning: muse_cube = muse_rebin(snsp.last_valid_freq, cube_sph) else: muse_cube = cube_sph
def get_toi837_li_equivalent_width(): spectrum_path = '../data/spectra/TOI-837_FEROS.fits' plotpath = '../results/TOI_837/feros_spectrum_get_li_equivalent_width.png' # # fit out the continuum to get the continuum normalized flux, over the # window of 6670 angstrom to 6713 angstrom. # xlim = [6670, 6713] hdul = fits.open(spectrum_path) d = hdul[0].data wav = d[0, 0] flx = d[3, 0] if isinstance(xlim, list): xmin = xlim[0] xmax = xlim[1] sel = (wav > xmin) & (wav < xmax) wav = wav[sel] flx = flx[sel] spec = Spectrum1D(spectral_axis=wav * u.AA, flux=flx * u.dimensionless_unscaled) if isinstance(xlim, list): exclude_regions = [] if xmin < 6709.2 and xmax > 6710.2: exclude_regions.append(SpectralRegion(6709.2 * u.AA, 6710.2 * u.AA)) if xmin < 6679 and xmax > 6681: exclude_regions.append(SpectralRegion(6679 * u.AA, 6681 * u.AA)) cont_flx = (fit_generic_continuum(spec, exclude_regions=exclude_regions)( spec.spectral_axis)) cont_norm_spec = spec / cont_flx # # to fit gaussians, look at 1-flux. # full_spec = Spectrum1D(spectral_axis=cont_norm_spec.wavelength, flux=(1 - cont_norm_spec.flux)) # # get the Li EW # region = SpectralRegion(6708.5 * u.AA, 6711.5 * u.AA) li_equiv_width = equivalent_width(cont_norm_spec, regions=region) li_centroid = centroid(full_spec, region) # # fit a gaussian too, and get ITS equiv width # https://specutils.readthedocs.io/en/stable/fitting.html # g_init = models.Gaussian1D(amplitude=0.2 * u.dimensionless_unscaled, mean=6709.7 * u.AA, stddev=0.5 * u.AA) g_fit = fit_lines(full_spec, g_init, window=(region.lower, region.upper)) y_fit = g_fit(full_spec.wavelength) fitted_spec = Spectrum1D(spectral_axis=full_spec.wavelength, flux=(1 - y_fit) * u.dimensionless_unscaled) fitted_li_equiv_width = equivalent_width(fitted_spec, regions=region) # # print bestfit params # print(42 * '=') print('got Li equiv width of {}'.format(li_equiv_width)) print('got fitted Li equiv width of {}'.format(fitted_li_equiv_width)) print('got Li centroid of {}'.format(li_centroid)) print('fit gaussian1d params are\n{}'.format(repr(g_fit))) print(42 * '=') # # plot the results # f, axs = plt.subplots(nrows=4, ncols=1, figsize=(6, 8)) axs[0].plot(wav, flx, c='k', zorder=3) axs[0].plot(wav, cont_flx, c='r', zorder=2) axs[1].plot(cont_norm_spec.wavelength, cont_norm_spec.flux, c='k') axs[2].plot(cont_norm_spec.wavelength, cont_norm_spec.flux, c='k') axs[3].plot(full_spec.wavelength, full_spec.flux, c='k') axs[3].plot(full_spec.wavelength, y_fit, c='g') txt = ('gaussian1d\namplitude:{:.3f}\nmean:{:.3f}\nstd:{:.3f}\nEW:{:.3f}'. format(g_fit.amplitude.value, g_fit.mean.value, g_fit.stddev.value, fitted_li_equiv_width)) axs[3].text(0.95, 0.95, txt, ha='right', va='top', transform=axs[3].transAxes, fontsize='xx-small') axs[0].set_ylabel('flux') axs[1].set_ylabel('contnorm flux') axs[2].set_ylabel('contnorm flux [zoom]') axs[3].set_ylabel('1 - (contnorm flux)') if isinstance(xlim, list): for ax in axs: ax.set_xlim(xlim) axs[2].set_xlim([6708.5, 6711.5]) axs[3].set_xlim([6708.5, 6711.5]) axs[-1].set_xlabel('wavelength [angstrom]') for ax in axs: format_ax(ax) outpath = '../results/TOI_837/toi837_li_equivalent_width_routine.png' savefig(f, outpath)
def test_spectrum1d_2d_data(): # This test makes sure that 2D spectra represented as Spectrum1D round-trip # Note that Spectrum1D will typically have a 1D spectral WCS even if the # data is N-dimensional, so we need to pad the WCS before passing it to # glue and un-pad it when translating back. # We test both the case where the WCS is 2D and the case where it is 1D wcs = WCS(naxis=1) wcs.wcs.ctype = ['FREQ'] wcs.wcs.cdelt = [10] wcs.wcs.set() flux = np.ones((3, 2)) * u.Unit('Jy') spec = Spectrum1D(flux, wcs=wcs, meta={'instrument': 'spamcam'}) assert spec.data.ndim == 2 assert spec.wcs.naxis == 1 data_collection = DataCollection() data_collection['spectrum'] = spec data = data_collection['spectrum'] assert isinstance(data, Data) assert len(data.main_components) == 1 assert data.main_components[0].label == 'flux' assert_allclose(data['flux'], flux.value) assert data.coords.pixel_n_dim == 2 assert data.coords.world_n_dim == 2 assert len(data.pixel_component_ids) == 2 assert len(data.world_component_ids) == 2 assert data.coordinate_components[0].label == 'Pixel Axis 0 [y]' assert data.coordinate_components[1].label == 'Pixel Axis 1 [x]' assert data.coordinate_components[2].label == 'Offset' assert data.coordinate_components[3].label == 'Frequency' assert_equal(data['Offset'], [[0, 0], [1, 1], [2, 2]]) assert_equal(data['Frequency'], [[10, 20], [10, 20], [10, 20]]) s, o = data.coords.pixel_to_world(1, 2) assert isinstance(s, SpectralCoord) # Check round-tripping of coordinates with pytest.warns(AstropyUserWarning, match='No observer defined on WCS'): px, py = data.coords.world_to_pixel(s, o) assert_allclose(px, 1) assert_allclose(py, 2) # Check round-tripping of translation spec_new = data.get_object(statistic=None) assert isinstance(spec_new, Spectrum1D) # The WCS object should be the same assert spec_new.wcs.pixel_n_dim == 1 assert spec_new.wcs.world_n_dim == 1 assert spec_new.wcs is spec.wcs # The metadata should still be present assert spec_new.meta['instrument'] == 'spamcam'
def add_single_spectra_to_map( spectra_map, *, header, data, spec_info=None, wcs_info=None, units_info=None, purpose_prefix=None, all_standard_units, all_keywords, valid_wcs, index=None, ): spec_wcs_info = {} spec_units_info = {} if wcs_info is not None: spec_wcs_info.update(wcs_info) if units_info is not None: spec_units_info.update(units_info) if spec_info is not None: spec_wcs_info.update(spec_info.get("wcs", {})) spec_units_info.update(spec_info.get("units", {})) purpose = spec_info.get("purpose") else: purpose = None purpose = get_purpose( header, purpose=purpose, purpose_prefix=purpose_prefix, all_keywords=all_keywords, index=index, ) if purpose == Purpose.SKIP: return None if valid_wcs or not spec_wcs_info: wcs = WCS(header) else: wcs = compute_wcs_from_keys_and_values(header, **spec_wcs_info) if all_standard_units: spec_units_info = {"flux_unit_keyword": "BUNIT"} flux_unit = get_flux_units_from_keys_and_values(header, **spec_units_info) flux = data * flux_unit meta = {"header": header, "purpose": PURPOSE_SPECTRA_MAP[purpose]} if purpose in CREATE_SPECTRA: spectrum = Spectrum1D(wcs=wcs, flux=flux, meta=meta) spectra_map[PURPOSE_SPECTRA_MAP[purpose]].append(spectrum) elif purpose in ERROR_PURPOSES: try: spectrum = spectra_map[PURPOSE_SPECTRA_MAP[purpose]][-1] except IndexError: raise ValueError(f"No spectra to associate with {purpose}") aligned_flux = pixel_to_pixel(wcs, spectrum.wcs, flux) spectrum.uncertainty = UNCERTAINTY_MAP[purpose](aligned_flux) spectrum.meta["uncertainty_header"] = header # We never actually want to return something, this just flags it to pylint # that we know we're breaking out of the function when skip is selected return None
def test_pre_full(data_path, template_path, z_lit, z_bound, spec_type): """Returns the full spectrum best-fit redshift and min chi2. Tests the redshift array on the entire spectrum to ensure bounds are appropriate. Parameters ---------- data_path : str Path to data spectrum file template_path : str Path to template spectrum file z_lit : float Literature redshift for target object z_bound : float Amount to add and subtract from z_lit for redshifts to test spec_type : str Indicates if looking at coadded spectrum or single 1D spectrum Returns ------- tm_result[0] : float Best-fit redshift tm_result[1] : float Minimum chi-squared """ #Import data data_wave_full, data_cut_wave, data_flux_full, data_cut_flux, data_noise_full, data_cut_noise = prep_data(data_path, spec_type) #Import smoothed template template_wave, smoothed_template_flux, smoothed_template_noise = prep_template(template_path) #Continuum normalize over whole wavelength range norm_data_wave, norm_data_flux, norm_data_noise = continuum_normalize(np.min(data_cut_wave), np.max(data_cut_wave), data_cut_flux, data_cut_wave, data_cut_noise) norm_template_wave, norm_template_flux, norm_template_noise = continuum_normalize(np.min(template_wave), np.max(template_wave), smoothed_template_flux, template_wave, smoothed_template_noise) #Put spectra into Spectrum1D objects data_spec = Spectrum1D(spectral_axis=norm_data_wave*u.Angstrom, flux=norm_data_flux*(u.erg/u.s/u.cm**2/u.Angstrom), uncertainty=StdDevUncertainty(norm_data_noise)) template_spec = Spectrum1D(spectral_axis=norm_template_wave*u.Angstrom, flux=norm_template_flux*(u.Lsun/u.micron)) #Plot before plt.figure(figsize=(12,4)) plt.plot(data_spec.spectral_axis, data_spec.flux, label='observed') plt.plot(template_spec.spectral_axis, template_spec.flux, label='template') plt.legend() plt.show() #Fit redshifts redshifts = np.linspace(z_lit-z_bound, z_lit+z_bound, 1000) tm_result = template_redshift(observed_spectrum=data_spec, template_spectrum=template_spec, redshift=redshifts) #Plot after plt.figure(figsize=(12,4)) plt.plot(data_spec.spectral_axis, data_spec.flux, label='observed') plt.plot(tm_result[2].spectral_axis, tm_result[2].flux, label='redshifted template') plt.legend() plt.show() plt.figure(figsize=(12,4)) plt.plot(template_spec.spectral_axis, template_spec.flux, label='template') plt.plot(tm_result[2].spectral_axis, tm_result[2].flux, label='redshifted template') plt.legend() plt.show() return tm_result[0], tm_result[1]
def chunk_redshift(data_wave, data_flux, data_noise, template_path, z_lit, targ_delta, overhang, z_test, z_bound, position): """Returns the bestfit redshift of each chunk. Parameters ---------- data_wave : tuple Data wavelength array data_flux : tuple Data flux array data_noise : tuple Data noise array template_path : str Path to template spectrum file z_lit : float Literature redshift of target object targ_delta : float Wavelength chunk size in Angstroms overhang : float Amount of wavelength overhang template chunks should have in Angstroms z_test : float Starting redshift for chunks (measured by eye in 1 chunk) z_bound : float Amount to add and subtract from z_lit for redshifts to test position : str 'before' or 'after' to indicate if pre- or post-flexure correction Results ------- bestfit_redshift : tuple Best fitting redshift for each chunk best_chi2 : tuple Minimum chi squared for each chunk redshifted_spectra : tuple Redshifted chunks chi2 : tuple All chi2 """ #Get data chunks data_wave_chunks, data_flux_chunks, data_noise_chunks = data_chunks(data_wave, data_flux, data_noise, targ_delta) #Get template_chunks temp_wave_chunks, temp_flux_chunks, temp_noise_chunks, temp_central_wavelengths, central_waves = template_chunks(data_wave, data_flux, data_noise, template_path, z_lit, targ_delta, overhang, position) #Find redshifts of each chunk observed_chunks = [] temp_chunks = [] for i in range(len(data_wave_chunks)): observed_chunks.append(Spectrum1D(spectral_axis=data_wave_chunks[i]*u.Angstrom, flux=data_flux_chunks[i]*(u.erg/u.s/u.cm**2/u.Angstrom), uncertainty=InverseVariance(data_noise_chunks[i]))) temp_chunks.append(Spectrum1D(spectral_axis=temp_wave_chunks[i]*u.Angstrom, flux=temp_flux_chunks[i]*(u.Lsun/u.micron), uncertainty=StdDevUncertainty(temp_noise_chunks[i]))) redshifts_chunks = np.linspace(z_test-z_bound, z_test+z_bound, 1000) fitted_redshift_results = [] bestfit_redshift = np.zeros(len(data_wave_chunks)) best_chi2 = np.zeros(len(data_wave_chunks)) redshifted_spectra = [] chi2 = [] for i in range(len(data_wave_chunks)): fitted_redshift_results.append(template_redshift(observed_spectrum=observed_chunks[i], template_spectrum=temp_chunks[i], redshift=redshifts_chunks)) bestfit_redshift[i] = fitted_redshift_results[i][0] best_chi2[i] = fitted_redshift_results[i][1] redshifted_spectra.append(fitted_redshift_results[i][2]) chi2.append(fitted_redshift_results[i][3]) return bestfit_redshift, best_chi2, redshifted_spectra, chi2
def __call__(self, image, trace_object, disp_axis=1, crossdisp_axis=0, bkgrd_prof=models.Polynomial1D(2), variance=None, mask=None, unit=None): """ Run the Horne calculation on a region of an image and extract a 1D spectrum. Parameters ---------- image : `~astropy.nddata.NDData` or array-like, required The input 2D spectrum from which to extract a source. An NDData object must specify uncertainty and a mask. An array requires use of the `variance`, `mask`, & `unit` arguments. trace_object : `~specreduce.tracing.Trace`, required The associated 1D trace object created for the 2D image. disp_axis : int, optional The index of the image's dispersion axis. [default: 1] crossdisp_axis : int, optional The index of the image's cross-dispersion axis. [default: 0] bkgrd_prof : `~astropy.modeling.Model`, optional A model for the image's background flux. [default: models.Polynomial1D(2)] variance : `~numpy.ndarray`, optional (Only used if `image` is not an NDData object.) The associated variances for each pixel in the image. Must have the same dimensions as `image`. [default: None] mask : `~numpy.ndarray`, optional (Only used if `image` is not an NDData object.) Whether to mask each pixel in the image. Must have the same dimensions as `image`. If blank, all non-NaN pixels are unmasked. [default: None] unit : `~astropy.units.core.Unit` or str, optional (Only used if `image` is not an NDData object.) The associated unit for the data in `image`. If blank, fluxes are interpreted as unitless. [default: None] Returns ------- spec_1d : `~specutils.Spectrum1D` The final, Horne extracted 1D spectrum. """ # handle image and associated data based on image's type if isinstance(image, NDData): img = np.ma.array(image.data, mask=image.mask) unit = image.unit if image.unit is not None else u.Unit() if image.uncertainty is not None: # prioritize NDData's uncertainty over variance argument if image.uncertainty.uncertainty_type == 'var': variance = image.uncertainty.array elif image.uncertainty.uncertainty_type == 'std': # NOTE: CCDData defaults uncertainties given as pure arrays # to std and logs a warning saying so upon object creation. # should we remind users again here? warnings.warn("image NDData object's uncertainty " "interpreted as standard deviation. if " "incorrect, use VarianceUncertainty when " "assigning image object's uncertainty.") variance = image.uncertainty.array**2 elif image.uncertainty.uncertainty_type == 'ivar': variance = 1 / image.uncertainty.array else: # other options are InverseVariance and UnknownVariance raise ValueError( "image NDData object has unexpected " "uncertainty type. instead, try " "VarianceUncertainty or StdDevUncertainty.") else: # ignore variance arg to focus on updating NDData object raise ValueError('image NDData object lacks uncertainty') else: if any(arg is None for arg in (variance, mask, unit)): raise ValueError('if image is a numpy array, the variance, ' 'mask, and unit arguments must be specified. ' 'consider wrapping that information into one ' 'object by instead passing an NDData image.') if image.shape != variance.shape: raise ValueError('image and variance shapes must match') if image.shape != mask.shape: raise ValueError('image and mask shapes must match') # fill in non-required arguments if empty if mask is None: mask = np.ma.masked_invalid(image) if isinstance(unit, str): unit = u.Unit(unit) else: unit = unit if unit is not None else u.Unit() # create image img = np.ma.array(image, mask=mask) # co-add signal in each image column ncols = img.shape[crossdisp_axis] xd_pixels = np.arange(ncols) # y plot dir / x spec dir coadd = img.sum(axis=disp_axis) / ncols # fit source profile, using Gaussian model as a template # NOTE: could add argument for users to provide their own model gauss_prof = models.Gaussian1D(amplitude=coadd.max(), mean=coadd.argmax(), stddev=2) # Fit extraction kernel to column with combined gaussian/bkgrd model ext_prof = gauss_prof + bkgrd_prof fitter = fitting.LevMarLSQFitter() fit_ext_kernel = fitter(ext_prof, xd_pixels, coadd) # use compound model to fit a kernel to each image column # NOTE: infers Gaussian1D source profile; needs generalization for others kernel_vals = [] norms = [] for col_pix in range(img.shape[disp_axis]): # set gaussian model's mean as column's corresponding trace value fit_ext_kernel.mean_0 = trace_object.trace[col_pix] # NOTE: support for variable FWHMs forthcoming and would be here # fit compound model to column fitted_col = fit_ext_kernel(xd_pixels) # save result and normalization kernel_vals.append(fitted_col) norms.append(fit_ext_kernel.amplitude_0 * fit_ext_kernel.stddev_0 * np.sqrt(2 * np.pi)) # transform fit-specific information kernel_vals = np.array(kernel_vals).T norms = np.array(norms) # calculate kernel normalization, masking NaNs g_x = np.ma.sum(kernel_vals**2 / variance, axis=crossdisp_axis) # sum by column weights weighted_img = np.ma.divide(img * kernel_vals, variance) result = np.ma.sum(weighted_img, axis=crossdisp_axis) / g_x # multiply kernel normalization into the extracted signal extraction = result * norms # convert the extraction to a Spectrum1D object pixels = np.arange(img.shape[disp_axis]) * u.pix spec_1d = Spectrum1D(spectral_axis=pixels, flux=extraction * unit) return spec_1d
def centroid_offsets(targ_bounds, data_wave, data_flux, sky_cents): """Returns amount by which extracted skylines are offset from model and the nearest wavelength value to each. Parameters ---------- targ_bounds : tuple List of tuples defining bounds of region around each skyline to examine data_wave : tuple Wavelength array data_flux : tuple Flux array sky_cents : tuple Skymodel centroids Returns ------- nearest_waves : tuple Nearest wavelength value to centroid offsets : tuple Offset between data and skymodel """ regions = SpectralRegion(targ_bounds[0][0]*u.Angstrom,targ_bounds[0][-1]*u.Angstrom) for i in range(1, len(targ_bounds)): regions += SpectralRegion(targ_bounds[i][0]*u.Angstrom, targ_bounds[i][-1]*u.Angstrom) #Normalize data targ_norm_wave, targ_norm_flux, targ_norm_noise = continuum_normalize(np.min(data_wave), np.max(data_wave), data_flux, data_wave, np.zeros(len(data_flux))) #Find offsets target = Spectrum1D(spectral_axis=targ_norm_wave*u.Angstrom, flux=targ_norm_flux*u.ct) sub_spec = extract_region(target, regions) offsets = np.zeros(len(sky_cents)) nearest_waves = np.zeros(len(sky_cents)) for i, sub in enumerate(sub_spec): an_disp = sub.flux.max() an_ampl = sub.flux.min() an_mean = sub.spectral_axis[sub.flux.argmax()] nearest_waves[i] = an_mean.value an_stdv = np.sqrt(np.sum((sub.spectral_axis - an_mean)**2) / (len(sub.spectral_axis) - 1)) plt.figure() plt.scatter(an_mean.value, an_disp.value, marker='o', color='#e41a1c', s=100, label='data') plt.scatter(sky_cents[i], an_disp.value, marker='o', color='k', s=100, label='archive') plt.vlines([an_mean.value - an_stdv.value, an_mean.value + an_stdv.value], sub.flux.min().value, sub.flux.max().value, color='#377eb8', ls='--', lw=2) g_init = ( models.Const1D(an_disp) + models.Gaussian1D(amplitude=(an_ampl - an_disp), mean=an_mean, stddev=an_stdv) ) g_fit = fit_lines(sub, g_init) line_fit = g_fit(sub.spectral_axis) plt.plot(sub.spectral_axis, sub.flux, color='#e41a1c', lw=2) plt.plot(sub.spectral_axis, line_fit, color='#377eb8', lw=2) plt.axvline(an_mean.value, color='#e41a1c', ls='--', lw=2) plt.legend() offsets[i] = an_mean.value - sky_cents[i].value return nearest_waves, offsets
from astropy.convolution import Gaussian1DKernel, convolve INPUT_spec = 'CS31_CNO_0n.spec' fwhm = 0.20 #SIGMA=8 SIGMA = fwhm / 2.35482 * 100 wl, fl = np.genfromtxt(INPUT_spec, skip_header=2, unpack=True) wl2, fl2 = np.genfromtxt('fluxCS31_0.norm.nulbad.0.200', skip_header=2, unpack=True) spec1 = Spectrum1D(spectral_axis=wl * u.A, flux=fl * u.Jy) #spec1_tsmooth = trapezoid_smooth(spec1, width=3) #spec1_bsmooth = box_smooth(spec1, width=3) #spec1_msmooth = median_smooth(spec1, width=3) spec1_gsmooth = gaussian_smooth(spec1, stddev=SIGMA) g = Gaussian1DKernel(stddev=SIGMA) # Convolve data z = convolve(fl, g) #plt.plot(spec1.spectral_axis, spec1.flux) plt.plot(wl2, fl2) plt.plot(wl, z)
def line_fit(spec, spec_err, wave_obj, dwave=10. * u.AA, dwave_cont=100. * u.AA, sigmamax=14. * u.AA): ''' Function to fit a 1D gaussian to a HETDEX spectrum from get_spec.py Parameters ---------- spec 1D spectrum from a row in the table provided by get_spec.py. Will assume unit of 10**-17*u.Unit('erg cm-2 s-1 AA-1') if no units are provided. spec_err 1D spectral uncertainty from table provided by get_spec.py. Will assume unit of 10**-17*u.Unit('erg cm-2 s-1 AA-1') if no units are provided. wave_obj wavelength you want to fit, an astropy quantity dwave spectral region above and below wave_obj to fit a line, an astropy quantity. Default is 10.*u.AA dwave_cont spectral region to fit continuum. Default is +/- 100.*u.AA sigmamax Maximum linewidth (this is sigma/stdev of the gaussian fit) to allow for a fit. Assumes unit of u.AA if not given Returns ------- ''' try: spectrum = Spectrum1D(flux=spec, spectral_axis=(2.0 * np.arange(1036) + 3470.) * u.AA, uncertainty=StdDevUncertainty(spec_err), velocity_convention=None) except ValueError: spectrum = Spectrum1D( flux=spec * 10**-17 * u.Unit('erg cm-2 s-1 AA-1'), spectral_axis=(2.0 * np.arange(1036) + 3470.) * u.AA, uncertainty=StdDevUncertainty(spec_err * 10**-17 * u.Unit('erg cm-2 s-1 AA-1')), velocity_convention=None) # measure continuum over 2*dwave_cont wide window first: cont_region = SpectralRegion((wave_obj - dwave_cont), (wave_obj + dwave_cont)) cont_spectrum = extract_region(spectrum, cont_region) cont = np.median(cont_spectrum.flux) if np.isnan(cont): #set continuum if its NaN print('Continuum fit is NaN. Setting to 0.0') cont = 0.0 * cont_spectrum.unit # now get region to fit the continuum subtracted line sub_region = SpectralRegion((wave_obj - dwave), (wave_obj + dwave)) sub_spectrum = extract_region(spectrum, sub_region) try: line_param = estimate_line_parameters(sub_spectrum - cont, models.Gaussian1D()) except: return None if np.isnan(line_param.amplitude.value): print('Line fit yields NaN result. Exiting.') return None try: sigma = np.minimum(line_param.stddev, sigmamax) except ValueError: sigma = np.minimum(line_param.stddev, sigmamax * u.AA) if np.isnan(sigma): sigma = sigmamax g_init = models.Gaussian1D(amplitude=line_param.amplitude, mean=line_param.mean, stddev=sigma) # lineregion = SpectralRegion((wave_obj-2*sigma), (wave_obj+2*sigma)) # cont = fit_generic_continuum(sub_spectrum, exclude_regions=lineregion, # model=models.Linear1D(slope=0)) #r1 = SpectralRegion((wave_obj-dwave), (wave_obj-2*sigma)) #r2 = SpectralRegion((wave_obj+2*sigma), (wave_obj+dwave)) #fitcontregion = r1 + r2 #fit_cont_spectrum = extract_region(sub_spectrum, fitcontregion) #cont = np.mean(np.hstack([fit_cont_spectrum[0].flux, fit_cont_spectrum[1].flux])) #contspec = cont(sub_spectrum.spectral_axis) g_fit = fit_lines(sub_spectrum - cont, g_init) x = np.arange(wave_obj.value - dwave.value, wave_obj.value + dwave.value, 0.5) * u.AA y_fit = g_fit(x) line_flux_model = np.sum(y_fit * 0.5 * u.AA) chi2 = calc_chi2(sub_spectrum - cont, g_fit) sn = np.sum(np.array(sub_spectrum.flux)) / np.sqrt( np.sum(sub_spectrum.uncertainty.array**2)) line_flux_data = line_flux(sub_spectrum - cont).to(u.erg * u.cm**-2 * u.s**-1) line_flux_data_err = np.sqrt(np.sum(sub_spectrum.uncertainty.array**2)) #fitted_region = SpectralRegion((line_param.mean - 2*sigma), # (line_param.mean + 2*sigma)) #fitted_spectrum = extract_region(spectrum, fitted_region) #line_param = estimate_line_parameters(fitted_spectrum, models.Gaussian1D()) #sn = np.sum(np.array(fitted_spectrum.flux)) / np.sqrt(np.sum( # fitted_spectrum.uncertainty.array**2)) #line_flux_data = line_flux(fitted_spectrum).to(u.erg * u.cm**-2 * u.s**-1) #line_flux_data_err = np.sqrt(np.sum(fitted_spectrum.uncertainty.array**2)) return line_param, sn, chi2, sigma, line_flux_data, line_flux_model, line_flux_data_err, g_fit, cont
max(mplot2.y[right:]), num=len(mplot2.x)) y_model = mplot2.y y_cn = y_model / y_cont dcontplot = DataPlot() dcont = Data1D("normalized", mplot.x, y_cn) dcontplot.prepare(dcont) dcontplot.plot() os.chdir(r"/home/dtyler/Desktop/DocumentsDT/outputs/standard_" + fits_image_filename) plt.savefig(fits_image_filename[:-4] + "norm_continuum.png") os.chdir(r"/home/dtyler/Desktop/DocumentsDT/Programs") ################################### ### calculate equivalent width spectrum = Spectrum1D(flux=y_cn * u.dimensionless_unscaled, spectral_axis=mplot.x * u.AA) eqw = equivalent_width(spectrum) """print("EW", eqw.value) print('norm factor', norm_factor) for par in bmdl.pars: print(par.fullname, par.val)""" ### print output to file os.chdir( r"/home/dtyler/Desktop/DocumentsDT/Analysis/standard_eq_widths") fil = open(fits_image_filename[:-4] + ".txt", 'w') fil.write('eqw' + '\t' + str(eqw.value) + '\n') fil.write('norm' + '\t' + str(norm_factor) + '\n') for par in bmdl.pars:
def load_aaomega_file(filename, *args, **kwargs): with read_fileobj_or_hdulist(filename, *args, **kwargs) as fits_file: fits_header = fits_file[AAOMEGA_SCIENCE_INDEX].header # fits_file is the hdulist var_idx = None rwss_idx = None for idx, extn in enumerate(fits_file): if extn.name == "VARIANCE": var_idx = idx if extn.name == "RWSS": rwss_idx = idx # science data fits_data = fits_file[AAOMEGA_SCIENCE_INDEX].data # read in Fibre table data.... ftable = Table(fits_file[AAOMEGA_FIBRE_INDEX].data) # A SpectrumList to hold all the Spectrum1D objects sl = SpectrumList() # the row var contains the pixel data from the science frame for i, row in enumerate(fits_data): # Definitely need deepcopy here, otherwise it does *NOT* work! fib_header = deepcopy(fits_header) # Adjusting some values from primary header so individual fibre # spectra have meaningful headers fib_header["FLDNAME"] = (fits_header["OBJECT"], "Name of 2dF .fld file") fib_header["FLDRA"] = ( fits_header["MEANRA"], "Right Ascension of 2dF field", ) fib_header["FLDDEC"] = (fits_header["MEANDEC"], "Declination of 2dF field") # Now for the fibre specific information from the Fibre Table # (extension 2) # Caution: RA and DEC are stored in RADIANS in the FIBRE TABLE! fib_header["RA"] = ( ftable["RA"][i] * 180.0 / np.pi, "Right Ascension of fibre from configure .fld file", ) fib_header["DEC"] = ( ftable["DEC"][i] * 180.0 / np.pi, "Declination of fibre from configure .fld file", ) fib_header["OBJECT"] = ( ftable["NAME"][i], "Name of target observed by fibre", ) fib_header["OBJCOM"] = ( ftable["COMMENT"][i], "Comment from configure .fld file for target", ) fib_header["OBJMAG"] = ( ftable["MAGNITUDE"][i], "Magnitude of target observed by fibre", ) fib_header["OBJTYPE"] = ( ftable["TYPE"][i], "Type of target observed by fibre", ) fib_header["OBJPIV"] = ( ftable["PIVOT"][i], "Pivot number used to observe target", ) fib_header["OBJPID"] = ( ftable["PID"][i], "Program ID from configure .fld file", ) fib_header["OBJX"] = ( ftable["X"][i], "X coord of target observed by fibre (microns)", ) fib_header["OBJY"] = ( ftable["Y"][i], "Y coord of target observed by fibre (microns)", ) fib_header["OBJXERR"] = ( ftable["XERR"][i], "X coord error of target observed by fibre (microns)", ) fib_header["OBJYERR"] = ( ftable["YERR"][i], "Y coord error of target observed by fibre (microns)", ) fib_header["OBJTHETA"] = ( ftable["THETA"][i], "Angle of fibre used to observe target", ) fib_header["OBJRETR"] = ( ftable["RETRACTOR"][i], "Retractor number used to observe target", ) # WLEN added around 2005 according to AAOmega obs manual... # so not always available if "WLEN" in ftable.colnames: fib_header["OBJWLEN"] = ( ftable["WLEN"][i], "Retractor of target observed by fibre", ) # ftable['TYPE'][i]: # P == program (science) # S == sky # U == unallocated or unused # F == fiducial (guide) fibre # N == broken, dead or no fibre meta = {"header": fib_header} if ftable["TYPE"][i] == "P": meta["purpose"] = "reduced" elif ftable["TYPE"][i] == "S": meta["purpose"] = "sky" else: # Don't include other fibres that are not science or sky continue wcs = compute_wcs_from_keys_and_values(fib_header, **AAOMEGA_2DF_WCS_SETTINGS) flux = row * AAOMEGA_2DF_FLUX_UNIT meta["fibre_index"] = i # Our science spectrum spectrum = Spectrum1D(wcs=wcs, flux=flux, meta=meta) # If the VARIANCE spectrum exists, add it as an additional spectrum # in the meta dict with key 'variance' if var_idx is not None: var_data = fits_file[var_idx].data var_flux = var_data[i] * AAOMEGA_2DF_FLUX_UNIT**2 spectrum.uncertainty = VarianceUncertainty(var_flux) # If the RWSS spectrum exists, add it as an additional spectrum in # the meta dict with key 'science_sky' # This is an optional extension produced by 2dfdr on request: all # spectra without the average/median sky subtraction # Useful in case users want to do their own sky subtraction. if rwss_idx is not None: rwss_data = fits_file[rwss_idx].data rwss_flux = rwss_data[i] * AAOMEGA_2DF_FLUX_UNIT rwss_meta = {"header": fib_header, "purpose": "science_sky"} spectrum.meta["science_sky"] = Spectrum1D(wcs=wcs, flux=rwss_flux, meta=rwss_meta) # Add our spectrum to the list. # The additional spectra are accessed using # spectrum.meta['variance'] and spectrum.meta['science_sky'] sl.append(spectrum) add_labels(sl) return sl
def dered_corr(data_path, template_path, wave_corr, z_lit, z_bound, spec_type): """Function to check remaining flexure after flexure correction has been applied. Parameters ---------- data_path : str Path to data spectrum file template_path : str Path to template spectrum file wave_corr : tuple Flexure-corrected wavelength array z_lit : float Literature redshift for target object z_bound : float Amount to add and subtract from z_lit for redshifts to test spec_type : str Indicates if looking at coadded spectrum or single 1D spectrum Returns ------- tm_result_corr : float Best-fitting redshift post flexure-correction dered_wave : tuple De-redshifted wavelength array """ #Get data data_wave_full, data_cut_wave, data_flux_full, data_cut_flux, data_noise_full, data_cut_noise = prep_data(data_path, spec_type) #Get template template_wave, smoothed_template_flux, smoothed_template_noise = prep_template(template_path) #Find redshift of new, corrected spectrum and de-redshift it to match the template #Continuum-norm over whole blue range norm_wave_corr, norm_corr_flux, norm_corr_noise = continuum_normalize(np.min(wave_corr), np.max(wave_corr), data_cut_flux, wave_corr, data_cut_noise) norm_template_wave, norm_template_flux, norm_template_noise = continuum_normalize(np.min(template_wave), np.max(template_wave), smoothed_template_flux, template_wave, smoothed_template_noise) #Plot before plt.figure(figsize=(12,4)) plt.plot(norm_wave_corr, norm_corr_flux, label='observed') plt.plot(norm_template_wave, norm_template_flux, label='template') plt.legend() #Find new redshift of whole spectrum corr_spec = Spectrum1D(spectral_axis=norm_wave_corr*u.Angstrom, flux=norm_corr_flux*(u.erg/u.s/u.cm**2/u.Angstrom), uncertainty=StdDevUncertainty(norm_corr_noise)) template_spec = Spectrum1D(spectral_axis=norm_template_wave*u.Angstrom, flux=norm_template_flux*(u.Lsun/u.micron)) pre_redshifts = np.linspace(z_lit-z_bound, z_lit+z_bound, 1000) tm_result_corr = template_redshift(observed_spectrum=corr_spec, template_spectrum=template_spec, redshift=pre_redshifts) #Plot after plt.figure(figsize=(12,4)) plt.plot(corr_spec.spectral_axis, corr_spec.flux, label='observed') plt.plot(tm_result_corr[2].spectral_axis, tm_result_corr[2].flux, label='redshifted template') plt.legend() plt.figure(figsize=(12,4)) plt.plot(template_spec.spectral_axis, template_spec.flux, label='template') plt.plot(tm_result_corr[2].spectral_axis, tm_result_corr[2].flux, label='redshifted template') plt.legend() #De-redshift data dered_wave = norm_wave_corr/(1+z_lit) plt.figure(figsize=(12,4)) plt.plot(dered_wave, norm_corr_flux, label='de-redshifted data') plt.plot(norm_template_wave, norm_template_flux, label='template') plt.legend() return tm_result_corr, dered_wave
def test_from_spectrum1d(mode): if mode == 'wcs3d': # This test is intended to be run with the version of Spectrum1D based # on NDCube 2.0 pytest.importorskip("ndcube", minversion="1.99") # Set up simple spatial+spectral WCS wcs = WCS(naxis=3) wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN', 'FREQ'] wcs.wcs.set() flux = np.ones((4, 4, 5))*u.Unit('Jy') uncertainty = VarianceUncertainty(np.square(flux*0.1)) mask = np.zeros((4, 4, 5)) kwargs = {'wcs': wcs, 'uncertainty': uncertainty, 'mask': mask} else: flux = [2, 3, 4, 5] * u.Jy uncertainty = VarianceUncertainty([0.1, 0.1, 0.1, 0.1] * u.Jy**2) mask = [False, False, False, False] if mode == 'wcs1d': wcs = WCS(naxis=1) wcs.wcs.ctype = ['FREQ'] wcs.wcs.set() kwargs = {'wcs': wcs, 'uncertainty': uncertainty, 'mask': mask} else: kwargs = {'spectral_axis': [1, 2, 3, 4] * u.Hz, 'uncertainty': uncertainty, 'mask': mask} spec = Spectrum1D(flux, **kwargs) data_collection = DataCollection() data_collection['spectrum'] = spec data = data_collection['spectrum'] assert isinstance(data, Data) assert len(data.main_components) == 3 assert data.main_components[0].label == 'flux' assert_allclose(data['flux'], flux.value) component = data.get_component('flux') assert component.units == 'Jy' # Check uncertainty parsing within glue data object assert data.main_components[1].label == 'uncertainty' assert_allclose(data['uncertainty'], uncertainty.array) component = data.get_component('uncertainty') assert component.units == 'Jy2' # Check round-tripping via single attribute reference spec_new = data.get_object(attribute='flux', statistic=None) assert isinstance(spec_new, Spectrum1D) assert_quantity_allclose(spec_new.spectral_axis, [1, 2, 3, 4] * u.Hz) if mode == 'wcs3d': assert_quantity_allclose(spec_new.flux, np.ones((5, 4, 4))*u.Unit('Jy')) else: assert_quantity_allclose(spec_new.flux, [2, 3, 4, 5] * u.Jy) assert spec_new.uncertainty is None # Check complete round-tripping, including uncertainties spec_new = data.get_object(statistic=None) assert isinstance(spec_new, Spectrum1D) assert_quantity_allclose(spec_new.spectral_axis, [1, 2, 3, 4] * u.Hz) if mode == 'wcs3d': assert_quantity_allclose(spec_new.flux, np.ones((5, 4, 4))*u.Unit('Jy')) assert spec_new.uncertainty is not None print(spec_new.uncertainty) print(uncertainty) assert_quantity_allclose(spec_new.uncertainty.quantity, np.ones((5, 4, 4))*0.01*u.Jy**2) else: assert_quantity_allclose(spec_new.flux, [2, 3, 4, 5] * u.Jy) assert spec_new.uncertainty is not None assert_quantity_allclose(spec_new.uncertainty.quantity, [0.1, 0.1, 0.1, 0.1] * u.Jy**2)
def mos_niriss_parser(app, data_dir, obs_label=""): """ Attempts to parse all data for a NIRISS dataset in the specified directory, which should include: - *_direct_*_cal.fits : Direct 2D image - *_direct_*_cat.ecsv : Source catalog - *_WFSSR_*_cal.fits : 2D spectra in first orientation - *_WFSSC_*_cal.fits : 2D spectra in second orientation - *_WFSSR_*_x1d.fits : 1D spectra in first orientatiom - *_WFSSC_*_x1d.fits : 1D spectra in second orientatiom The spectra from the "C" files (horizontal orientation) are showed in the viewers by default. """ p = Path(data_dir) if not p.is_dir(): raise ValueError("{} is not a valid directory path".format(data_dir)) source_cat = sorted(list(p.glob("{}*_direct_*_cat.ecsv".format(obs_label)))) direct_image = sorted(list(p.glob("{}*_direct_dit1*_i2d.fits".format(obs_label)))) spec2d_r = sorted(list(p.glob("{}*_WFSSR_*_cal.fits".format(obs_label)))) spec2d_c = sorted(list(p.glob("{}*_WFSSC_*_cal.fits".format(obs_label)))) spec1d_r = sorted(list(p.glob("{}*_WFSSR_*_x1d.fits".format(obs_label)))) spec1d_c = sorted(list(p.glob("{}*_WFSSC_*_x1d.fits".format(obs_label)))) file_lists = { "Source Catalog": source_cat, "Direct Image": direct_image, "2D Spectra C": spec2d_c, "2D Spectra R": spec2d_r, "1D Spectra C": spec1d_c, "1D Spectra R": spec1d_r } # Convert from pathlib Paths back to strings for key in file_lists: file_lists[key] = [str(x) for x in file_lists[key]] _warn_if_not_found(app, file_lists) # Parse relevant information from source catalog cat_fields = ["id", "sky_centroid.ra", "sky_centroid.dec"] source_ids = [] ras = [] decs = [] image_add = [] pupil_id_dict = {} # Retrieve source information for source_catalog_num in range(0, len(file_lists["Source Catalog"])): cat_file = file_lists["Source Catalog"][source_catalog_num] parsed_cat_fields = _fields_from_ecsv(cat_file, cat_fields, delimiter=" ") pupil = [x for x in cat_file.split("/")[-1].split("_") if x[0] == "F" or x[0] == "f"][0] pupil_id_dict[pupil] = {} for row in parsed_cat_fields: pupil_id_dict[pupil][int(row[0])] = (row[1], row[2]) # Read in direct image filters image_dict = {} filter_wcs = {} # Set up a dictionary of datasets to add to glue add_to_glue = {} print("Loading: Images") for image_file in file_lists["Direct Image"]: im_split = image_file.split("/")[-1].split("_") pupil = [x for x in image_file.split("/")[-1].split("_") if x[0] == "F" or x[0] == "f"][0] image_label = "Image {} {}".format(im_split[0], pupil) with fits.open(image_file) as file_obj: data_iter = get_image_data_iterator(app, file_obj, "Image", ext=None) data_obj = [d[0] for d in data_iter] # We do not use the generated labels image_data = data_obj[0] # Grab the first one. TODO: Error if multiple found? with fits.open(image_file) as temp: filter_wcs[pupil] = temp[1].header image_data.label = image_label add_to_glue[image_label] = image_data image_dict[pupil] = image_label # Parse 2D spectra spec_labels_2d = [] for f in ["2D Spectra C", "2D Spectra R"]: for fname in file_lists[f]: print(f"Loading: {f} sources") orientation = f[-1] filter_name = [x for x in fname.split("/")[-1].split("_") if x[0] == "F" or x[0] == "f"][0] with fits.open(fname, memmap=False) as temp: sci_hdus = [] wav_hdus = {} for i in range(len(temp)): if "EXTNAME" in temp[i].header: if temp[i].header["EXTNAME"] == "SCI": sci_hdus.append(i) wav_hdus[i] = ('WAVELENGTH', temp[i].header['EXTVER']) # Now get a Spectrum1D object for each SCI HDU for sci in sci_hdus: if temp[sci].header["SPORDER"] == 1: data = temp[sci].data meta = temp[sci].header # The wavelength is stored in a WAVELENGTH HDU. This is # a 2D array, but in order to be able to use Spectrum1D # we use the average wavelength for all image rows wav = temp[wav_hdus[sci]].data.mean(axis=0) * u.micron spec2d = Spectrum1D(data * u.one, spectral_axis=wav, meta=meta) spec2d.meta['INSTRUME'] = 'NIRISS' label = "{} Source {} spec2d {}".format(filter_name, temp[sci].header["SOURCEID"], orientation ) ra, dec = pupil_id_dict[filter_name][temp[sci].header["SOURCEID"]] source_ids.append("Source Catalog: {} Source ID: {}". format(filter_name, temp[sci].header["SOURCEID"])) ras.append(ra) decs.append(dec) image_add.append(image_dict[filter_name]) spec_labels_2d.append(label) add_to_glue[label] = spec2d spec_labels_1d = [] for f in ["1D Spectra C", "1D Spectra R"]: for fname in file_lists[f]: print(f"Loading: {f} sources") with fits.open(fname, memmap=False) as temp: # TODO: Remove this once valid SRCTYPE values are present in all headers for hdu in temp: if "SRCTYPE" in hdu.header and\ (hdu.header["SRCTYPE"] in ["POINT", "EXTENDED"]): pass else: hdu.header["SRCTYPE"] = "EXTENDED" specs = SpectrumList.read(temp, format="JWST x1d multi") filter_name = [x for x in fname.split("/")[-1].split("_") if x[0] == "F" or x[0] == "f"][0] # Orientation denoted by "C" or "R" orientation = f[-1] for spec in specs: if spec.meta['header']['SPORDER'] == 1 and\ spec.meta['header']['EXTNAME'] == "EXTRACT1D": label = "{} Source {} spec1d {}".format(filter_name, spec.meta['header']['SOURCEID'], orientation ) spec_labels_1d.append(label) add_to_glue[label] = spec # Add the datasets to glue - we do this in one step so that we can easily # optimize by avoiding recomputing the full link graph at every add with app.data_collection.delay_link_manager_update(): for label, data in add_to_glue.items(): app.add_data(data, label, notify_done=False) # We then populate the table inside this context manager as _add_to_table # does operations that also trigger link manager updates. _add_to_table(app, source_ids, "Source ID") _add_to_table(app, ras, "Right Ascension") _add_to_table(app, decs, "Declination") _add_to_table(app, image_add, "Images") _add_to_table(app, spec_labels_1d, "1D Spectra") _add_to_table(app, spec_labels_2d, "2D Spectra") app.get_viewer('table-viewer')._shared_image = True
def EW(specname,name): lamb, flux= np.genfromtxt(specname, skip_header=1, unpack=True) flux = flux * u.Unit('J cm-2 s-1 AA-1') #flux = flux * u.Unit('erg cm-2 s-1 AA-1') lamb= lamb * u.AA spec = Spectrum1D(spectral_axis=lamb, flux=flux) # normalization is not so good cont_norm_spec = spec / fit_generic_continuum(spec)(spec.spectral_axis) print('-----------'+name+'------------') #line A EWa = equivalent_width(cont_norm_spec, regions=SpectralRegion(8493*u.AA, 8502*u.AA)) #FWHMa = fwhm(cont_norm_spec, regions=SpectralRegion(8493*u.AA, 8502*u.AA)) print('EW A line: '+str(EWa)) #line B EWb = equivalent_width(cont_norm_spec, regions=SpectralRegion(8533*u.AA, 8551*u.AA)) print('EW B line: '+str(EWb)) #line C EWc = equivalent_width(cont_norm_spec, regions=SpectralRegion(8655*u.AA, 8670*u.AA)) print('EW C line: '+str(EWc)) #open log file #nonlinear to metal-poor V_VHB = -2.0 EWbc= (EWb+EWc) EWbc= float(EWbc/(1. * u.AA)) EWp = (EWbc)**(-1.5) #nonlinear to metal-poor #Wl = float(EWb / (1. * u.AA)) + float(EWc / (1. * u.AA)) + (0.64 * V_VHB) #FeH= -2.81 + 0.44*Wl # FeH constants to V-VHB a=-2.87 b=0.195 c=0.458 d=-0.913 e=0.0155 #float all FeH = a + b * V_VHB + c * EWbc + d * EWp + e * EWbc * V_VHB print('[Fe/H]: '+str(FeH)) #change relampled spectrum to noise spectrum LOG = open('./EWs/EWfile-'+name+'.txt', 'w') #LOG = open('./EWs/EWfileRE-'+name+'.txt', 'w') LOG.write('Log file of '+ name +' \n \n') LOG.write('Input Spectrum: '+ specname +' \n \n') LOG.write('EW A line: '+ str(EWa) +' \n') LOG.write('EW B line: '+ str(EWb) +' \n') LOG.write('EW C line: '+ str(EWc) +' \n') LOG.write('[Fe/H]_CaT: '+ str(FeH) +' \n') f1 = plt.figure(figsize=(16,9)) ax = f1.add_subplot(111) ax.plot(cont_norm_spec.spectral_axis, cont_norm_spec.flux) ax.set_xlim([8480,8690]) ax.set_ylabel('Flux (J cm-2 s-1 AA-1)') ax.set_xlabel('Wavelength ( $\AA$ )') ax.axvspan(8498-float(EWa / (2. * u.AA)) , 8498+float(EWa / (2. * u.AA)) , alpha=0.2, color='red') ax.axvspan(8542-float(EWb / (2. * u.AA)) , 8542+float(EWb / (2. * u.AA)) , alpha=0.2, color='red') ax.axvspan(8662-float(EWc / (2. * u.AA)) , 8662+float(EWc / (2. * u.AA)) , alpha=0.2, color='red') #change relampled spectrum to noise spectrum plt.savefig('./EWs/EW-figs/EW'+name+'.pdf')
def vue_fit_model_to_cube(self, *args, **kwargs): if self._warn_if_no_equation(): return data = self.app.data_collection[self._selected_data_label] # First, ensure that the selected data is cube-like. It is possible # that the user has selected a pre-existing 1d data object. if data.ndim != 3: snackbar_message = SnackbarMessage( f"Selected data {self._selected_data_label} is not cube-like", color='error', sender=self) self.hub.broadcast(snackbar_message) return # Get the primary data component attribute = data.main_components[0] component = data.get_component(attribute) temp_values = data.get_data(attribute) # Transpose the axis order values = np.moveaxis(temp_values, 0, -1) * u.Unit(component.units) # We manually create a Spectrum1D object from the flux information # in the cube we select wcs = data.coords.sub([WCSSUB_SPECTRAL]) spec = Spectrum1D(flux=values, wcs=wcs) # TODO: in vuetify >2.3, timeout should be set to -1 to keep open # indefinitely snackbar_message = SnackbarMessage("Fitting model to cube...", loading=True, timeout=0, sender=self) self.hub.broadcast(snackbar_message) # Retrieve copy of the models with proper "fixed" dictionaries # TODO: figure out why this was causing the parallel fitting to fail #models_to_fit = self._reinitialize_with_fixed() models_to_fit = self._initialized_models.values() fitted_model, fitted_spectrum = fit_model_to_spectrum( spec, models_to_fit, self.model_equation, run_fitter=True) # Save fitted 3D model in a way that the cubeviz # helper can access it. self.app._fitted_3d_model = fitted_model # Transpose the axis order back values = np.moveaxis(fitted_spectrum.flux.value, -1, 0) count = max( map(lambda s: int(next(iter(re.findall("\d$", s)), 0)), self.data_collection.labels)) + 1 label = f"{self.model_label} [Cube] {count}" # Create new glue data object output_cube = Data(label=label, coords=data.coords) output_cube['flux'] = values output_cube.get_component('flux').units = \ fitted_spectrum.flux.unit.to_string() # Add to data collection self.app.data_collection.append(output_cube) snackbar_message = SnackbarMessage("Finished cube fitting", color='success', loading=False, sender=self) self.hub.broadcast(snackbar_message)
xn=[] yn=[] n=0 for i in point: xn.append(point[n][0]) yn.append(point[n][1]) n=n+1 #-------------------------------- #Creating spectrums to fit y=-y+1.0 y = y * u.Unit('J cm-2 s-1 AA-1') # reading spec spec = Spectrum1D(spectral_axis=x*u.AA, flux=y) #-------------------------------- #LINES LIMITS if interactive_mode == False: lines = find_lines_derivative(spec, flux_threshold= 1 - _flux_threshold_) if len(lines)>3: print('Check _flux_threshold_' ) try: lineAcentre=lines[0][0] lineBcentre=lines[1][0] lineCcentre=lines[2][0] # if the code was only able to find the CaT-b line
def vue_fit_model_to_cube(self, *args, **kwargs): if self._warn_if_no_equation(): return if self.selected_data in self.app.data_collection.labels: data = self.app.data_collection[self.selected_data] else: # User selected some subset from spectrum viewer, just use original cube data = self.app.data_collection[0] # First, ensure that the selected data is cube-like. It is possible # that the user has selected a pre-existing 1d data object. if data.ndim != 3: snackbar_message = SnackbarMessage( f"Selected data {self.selected_data} is not cube-like", color='error', sender=self) self.hub.broadcast(snackbar_message) return # Get the primary data component attribute = data.main_components[0] component = data.get_component(attribute) temp_values = data.get_data(attribute) # Transpose the axis order values = np.moveaxis(temp_values, 0, -1) * u.Unit(component.units) # We manually create a Spectrum1D object from the flux information # in the cube we select wcs = data.coords.sub([WCSSUB_SPECTRAL]) spec = Spectrum1D(flux=values, wcs=wcs) # TODO: in vuetify >2.3, timeout should be set to -1 to keep open # indefinitely snackbar_message = SnackbarMessage("Fitting model to cube...", loading=True, timeout=0, sender=self) self.hub.broadcast(snackbar_message) # Retrieve copy of the models with proper "fixed" dictionaries models_to_fit = self._reinitialize_with_fixed() try: fitted_model, fitted_spectrum = fit_model_to_spectrum( spec, models_to_fit, self.model_equation, run_fitter=True, window=self._window) except ValueError: snackbar_message = SnackbarMessage("Cube fitting failed", color='error', loading=False, sender=self) self.hub.broadcast(snackbar_message) raise # Save fitted 3D model in a way that the cubeviz # helper can access it. for m in fitted_model: temp_label = "{} ({}, {})".format(self.model_label, m["x"], m["y"]) self.app.fitted_models[temp_label] = m["model"] # Transpose the axis order back values = np.moveaxis(fitted_spectrum.flux.value, -1, 0) count = max( map(lambda s: int(next(iter(re.findall(r"\d$", s)), 0)), self.data_collection.labels)) + 1 label = f"{self.model_label} [Cube] {count}" # Create new glue data object output_cube = Data(label=label, coords=data.coords) output_cube['flux'] = values output_cube.get_component('flux').units = \ fitted_spectrum.flux.unit.to_string() # Add to data collection self.app.add_data(output_cube, label) if self.selected_viewer != 'None': # replace the contents in the selected viewer with the results from this plugin self.app.add_data_to_viewer(self.viewer_to_id.get( self.selected_viewer), label, clear_other_data=True) snackbar_message = SnackbarMessage("Finished cube fitting", color='success', loading=False, sender=self) self.hub.broadcast(snackbar_message)
def load_MAST_calspec(filename, remote=True, cache=True, show_progress=False): """ Load a standard star spectrum from the `calspec` database at MAST. These spectra are provided in FITS format and are described in detail at: https://www.stsci.edu/hst/instrumentation/reference-data-for-calibration-and-tools/astronomical-catalogs/calspec # noqa If `remote` is True, the spectrum will be downloaded from MAST. Set `remote` to False to load a local file. Parameters ---------- filename : str FITS filename of the standard star spectrum, e.g. g191b2b_005.fits. remote : bool (default = True) If True, download the spectrum from MAST. If False, check if `filename` exists and load it. cache : bool (default = True) Toggle whether downloaded data is cached or not. show_progress : bool (default = True) Toggle whether download progress bar is shown. Returns ------- spectrum : None or `~specutils.Spectrum1D` If the spectrum can be loaded, return it as a `~specutils.Spectrum1D`. Otherwise return None. The spectral_axis units are Å (`~astropy.units.angstrom`) and the flux units are milli-Janskys (`~astropy.units.mJy`). """ if remote: url = f"https://archive.stsci.edu/hlsps/reference-atlases/cdbs/calspec/{filename}" try: file_path = download_file( url, cache=cache, show_progress=show_progress, pkgname='specreduce' ) except Exception as e: msg = f"Downloading of {filename} failed: {e}" warnings.warn(msg, AstropyUserWarning) file_path = None else: if os.path.isfile(filename): file_path = filename else: msg = f"Provided filename, {filename}, does not exist or is not a valid file." warnings.warn(msg, AstropyUserWarning) file_path = None if file_path is None: return None else: hdr, wave, flux = synphot.specio.read_fits_spec(file_path) # the calspec data stores flux in synphot's FLAM units. convert to flux units # supported directly by astropy.units. mJy is chosen since it's the JWST # standard and can easily be converted to/from AB magnitudes. flux_mjy = synphot.units.convert_flux(wave, flux, u.mJy) spectrum = Spectrum1D(spectral_axis=wave, flux=flux_mjy) return spectrum
sne_name = folder_path.split('/')[-1] #input command for redshift redshift = float(input('Input Redshift of SNe:')) z = 1 + redshift #turn each file name into array #apply obs to rest wavelength and flux conversion #create list of spectrum files, and continuum files cont_list = [] data = [] for epoch in file_list: file = np.genfromtxt(fname= epoch) lamb = (file[:, 0] / z) * u.AA flux = file[:, 1] * 10 ** -15 * u.Unit('erg cm-2 s-1 AA-1') spec = Spectrum1D(spectral_axis=lamb, flux=flux) data.append(spec) cont_list.append((spec /spec) * fit_generic_continuum(spec)(spec.spectral_axis)) #plot to find the lines to calculate EW spcplt = data[0] cntplt = cont_list[0] f, ax = plt.subplots() ax.step(spcplt.wavelength, spcplt.flux) ax.step(cntplt.wavelength, cntplt.flux) ax.set_xlim(6000*u.AA, 7500*u.AA) ax.grid(True) wave = [6563, 6678, 7065, 7155] for line in wave: plt.axvline(x = line)
q += 1 slice = int(0.3 * (right - left)) #weird koefficient line = np.zeros(len(data)) for i in range(len(data)): line[i] = lines[i][slice] #result 1d line not_real_lambda = range(len(line)) slice_light = left - 10 #slice of stray light -- continuum line_light = np.zeros(len(data)) for i in range(len(data)): line_light[i] = data[i][slice] line -= line_light #finding emissions and absorptions lines spectrum = Spectrum1D(flux=line * u.Jy, spectral_axis=not_real_lambda * u.um) #units only for work lines1 = find_lines_derivative(spectrum, flux_threshold=100) lam = [] #wavelenghts of lines max_flux = [] #amplitudes of lines num = len(lines1) for i in range(num): max_flux.append(line[int(lines1['line_center'].value[i])]) lam.append(lines1['line_center'].value[i]) std_hands = 1 compound_model = models.Gaussian1D(max_flux[0], lam[0], std_hands) for i in range(1, num): compound_model += models.Gaussian1D(max_flux[i], lam[i], std_hands) fitter = fitting.LevMarLSQFitter() compound_fit = fitter(compound_model, not_real_lambda[0:1000], line[0:1000])
def to_object(self, data_or_subset, attribute=None, statistic='mean'): """ Convert a glue Data object to a Spectrum1D object. Parameters ---------- data_or_subset : `glue.core.data.Data` or `glue.core.subset.Subset` The data to convert to a Spectrum1D object attribute : `glue.core.component_id.ComponentID` The attribute to use for the Spectrum1D data statistic : {'minimum', 'maximum', 'mean', 'median', 'sum', 'percentile'} The statistic to use to collapse the dataset """ if isinstance(data_or_subset, Subset): data = data_or_subset.data subset_state = data_or_subset.subset_state else: data = data_or_subset subset_state = None if isinstance(data.coords, WCSCoordinates): # Find spectral axis spec_axis = data.coords.wcs.naxis - 1 - data.coords.wcs.wcs.spec # Find non-spectral axes axes = tuple(i for i in range(data.ndim) if i != spec_axis) kwargs = {'wcs': data.coords.wcs.sub([WCSSUB_SPECTRAL])} elif isinstance(data.coords, SpectralCoordinates): kwargs = {'spectral_axis': data.coords.spectral_axis} else: raise TypeError( 'data.coords should be an instance of WCSCoordinates or SpectralCoordinates' ) if isinstance(attribute, str): attribute = data.id[attribute] elif len(data.main_components) == 0: raise ValueError('Data object has no attributes.') elif attribute is None: if len(data.main_components) == 1: attribute = data.main_components[0] else: raise ValueError( "Data object has more than one attribute, so " "you will need to specify which one to use as " "the flux for the spectrum using the " "attribute= keyword argument.") component = data.get_component(attribute) # Collapse values to profile if data.ndim > 1: # Get units and attach to value values = data.compute_statistic(statistic, attribute, axis=axes, subset_state=subset_state) mask = None else: values = data.get_data(attribute) if subset_state is None: mask = None else: mask = data.get_mask(subset_state=subset_state) values = values.copy() values[~mask] = np.nan values = values * u.Unit(component.units) return Spectrum1D(values, mask=mask, **kwargs)
def vue_spatial_convolution(self, *args): """ Use astropy convolution machinery to smooth the spatial dimensions of the data cube. """ size = float(self.stddev) label = f"Smoothed {self._selected_data.label} spatial stddev {size}" if label in self.data_collection: # immediately cancel before smoothing snackbar_message = SnackbarMessage( "Data with selected stddev already exists, canceling operation.", color="error", sender=self) self.hub.broadcast(snackbar_message) return # Get information from the flux component attribute = self._selected_data.main_components[0] cube = self._selected_data.get_object(cls=Spectrum1D, attribute=attribute, statistic=None) flux_unit = cube.flux.unit # Extend the 2D kernel to have a length 1 spectral dimension, so that # we can do "3d" convolution to the whole cube kernel = np.expand_dims(Gaussian2DKernel(size), 2) # TODO: in vuetify >2.3, timeout should be set to -1 to keep open # indefinitely snackbar_message = SnackbarMessage( "Smoothing spatial slices of cube...", loading=True, timeout=0, sender=self) self.hub.broadcast(snackbar_message) convolved_data = convolve(cube, kernel) # Create a new cube with the old metadata. Note that astropy # convolution generates values for masked (NaN) data. newcube = Spectrum1D(flux=convolved_data * flux_unit, wcs=cube.wcs) # add data to the collection self.app.add_data(newcube, label) if self.selected_viewer != 'None': # replace the contents in the selected viewer with the results from this plugin self.app.add_data_to_viewer(self.viewer_to_id.get( self.selected_viewer), label, clear_other_data=True) snackbar_message = SnackbarMessage( f"Data set '{self._selected_data.label}' smoothed successfully.", color="success", sender=self) self.hub.broadcast(snackbar_message)
if __name__ == "__main__": wline = [185.999] band = 'R1' order = int(band[1]) np.random.seed(0) x = np.linspace(180., 190., 100) y = 3 * np.exp(-0.5 * (x - 185.999)**2 / 0.1**2) y += np.random.normal(0., 0.2, x.shape) y_continuum = 3.2 * np.exp(-0.5 * (x - 5.6)**2 / 4.8**2) y += y_continuum #create spectrum to fit spectrum = Spectrum1D(flux=y * u.Jy, spectral_axis=x * u.um) noise_region = SpectralRegion(180. * u.um, 184. * u.um) spectrum = noise_region_uncertainty(spectrum, noise_region) #line_region = [(185.52059807*u.um, 186.47740193*u.um)] g1_fit = fit_generic_continuum(spectrum, model=models.Polynomial1D(1)) y_continuum_fitted = g1_fit(x * u.um) plt.plot(x, y, label='spectrum') plt.errorbar(x, y, yerr=spectrum.uncertainty.array, color='b') plt.plot(x, y_continuum_fitted, label='cont_0') plt.title('Continuum+line Fitting') plt.grid(True) line = LineFitterMult(spectrum, wline,
def create_spectrum1d(xmin, xmax, uncertainty=None): flux = np.ones(xmax - xmin) * u.Jy wavelength = np.arange(xmin, xmax) * 0.1 * u.nm uncertainty = StdDevUncertainty(np.ones(xmax - xmin) * u.Jy) if uncertainty is not None else None return Spectrum1D(spectral_axis=wavelength, flux=flux, uncertainty=uncertainty)
def specviz_spectrum1d_parser(app, data, data_label=None, format=None, show_in_viewer=True): """ Loads a data file or `~specutils.Spectrum1D` object into Specviz. Parameters ---------- data : str, `~specutils.Spectrum1D`, or `~specutils.SpectrumList` Spectrum1D, SpectrumList, or path to compatible data file. data_label : str The Glue data label found in the ``DataCollection``. format : str Loader format specification used to indicate data format in `~specutils.Spectrum1D.read` io method. """ # If no data label is assigned, give it a unique identifier if not data_label: data_label = "specviz_data|" + str( base64.b85encode(uuid.uuid4().bytes), "utf-8") if isinstance(data, SpectrumCollection): raise TypeError("SpectrumCollection detected." " Please provide a Spectrum1D or SpectrumList") elif isinstance(data, Spectrum1D): data = [data] data_label = [data_label] elif isinstance(data, SpectrumList): pass else: path = pathlib.Path(data) if path.is_file(): try: data = [Spectrum1D.read(str(path), format=format)] data_label = [data_label] except IORegistryError: # Multi-extension files may throw a registry error data = SpectrumList.read(str(path), format=format) else: raise FileNotFoundError("No such file: " + str(path)) if isinstance(data, SpectrumList): if not isinstance(data_label, (list, tuple)): temp_labels = [] for i in range(len(data)): temp_labels.append(f"{data_label} {i}") data_label = temp_labels elif len(data_label) != len(data): raise ValueError( f"Length of data labels list ({len(data_label)}) is different" f" than length of list of data ({len(data)})") # If there's already data in the viewer, convert units if needed current_unit = None current_spec = app.get_data_from_viewer("spectrum-viewer") if current_spec != {} and current_spec is not None: spec_key = list(current_spec.keys())[0] current_unit = current_spec[spec_key].spectral_axis.unit with app.data_collection.delay_link_manager_update(): for i in range(len(data)): spec = data[i] if current_unit is not None and spec.spectral_axis.unit != current_unit: spec = Spectrum1D( flux=spec.flux, spectral_axis=spec.spectral_axis.to(current_unit)) app.add_data(spec, data_label[i]) # Only auto-show the first spectrum in a list if i == 0 and show_in_viewer: app.add_data_to_viewer("spectrum-viewer", data_label[i])