def _run_functions(self, *args, **kwargs): """ Run fitting on the initialized models, fixing any parameters marked as such by the user, then update the displauyed parameters with fit values """ temp_results = [] for function in FUNCTIONS: # Centroid function requires a region argument, create one to pass if function == "Centroid": spectral_axis = self._spectrum1d.spectral_axis if self._spectrum1d.mask is None: spec_region = SpectralRegion(spectral_axis[0], spectral_axis[-1]) else: spec_region = self._spectrum1d.spectral_axis[np.where( self._spectrum1d.mask == False)] spec_region = SpectralRegion(spec_region[0], spec_region[-1]) temp_result = FUNCTIONS[function](self._spectrum1d, spec_region) else: temp_result = FUNCTIONS[function](self._spectrum1d) temp_results.append({ 'function': function, 'result': str(temp_result) }) self.result_available = True self.results = [] self.results = temp_results
def snr_spec(flux, wl, n): sample = len(wl) noise = n * np.asarray(random.sample(range(0, len(wl)), sample)) / len(wl) unc = StdDevUncertainty(noise) fluxn = [[] for i in range(len(wl))] i = 0 for inc in unc: fluxn[i] = flux[i] + noise[i] i = i + 1 spec1d = Spectrum1D(spectral_axis=wl * u.AA, flux=fluxn * u.Jy, uncertainty=unc) #ax = plt.subplots()[1] #ax.plot(spec1d.spectral_axis, spec1d.flux) #ax.set_xlim([3520,3550]) sn1 = snr(spec1d, SpectralRegion(3070 * u.AA, 3090 * u.AA)) sn = snr_derived(spec1d, SpectralRegion(3070 * u.AA, 3090 * u.AA)) #print('SNR1: '+ str(snr(spec1d)), SpectralRegion(3500*u.AA, 3550*u.AA)) print('SNR: ' + str(sn1)) #print('SNR: '+ str(sn)) #print('FWHM:'+str(fwhm(spec1d))) #0.042 = snr 50 # try: return fluxn except: raise Exception('Check S/N function')
def line(spec,wave1,wave2): # finding the centorid and deriving guesses parameters centre=centroid(spec, SpectralRegion(wave1*u.AA, wave2*u.AA)) centre=float(centre/(1. * u.AA)) FWHM=fwhm(spec) FWHM=float(FWHM/(1. * u.AA)) A=line_flux(spec, SpectralRegion(lamb1*u.AA, lamb2*u.AA)) a=1* u.Unit('J cm-2 s-1 AA-1') A=float(A/(1. * u.AA*a)) # PARAMETERS return [centre,A,FWHM]
def EW(lamb,flux,name): 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) #cont_norm_spec = spec / fit_generic_continuum(spec)(spec.spectral_axis) cont_norm_spec = spec 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(lineBlims[0]*u.AA, lineBlims[1]*u.AA)) print('EW B line: '+str(EWb)) #line C EWc = equivalent_width(cont_norm_spec, regions=SpectralRegion(lineClims[0]*u.AA, lineClims[1]*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)) return [EWb,EWc,FeH]
def sky_centroids(sky_wave, smoothed_sky_flux, smoothed_sky_noise): """Returns centroids of skylines in the sky model. Parameters ---------- sky_wave : tuple Wavelength array smoothed_sky_flux : tuple Smoothed flux array smoothed_sky_noise : tuple Smoothed noise array Returns ------- sky_cents : tuple Skyline centroids """ #Normalize sky model sky_norm_wave, sky_norm_flux, sky_norm_noise = continuum_normalize(np.min(sky_wave), np.max(sky_wave), smoothed_sky_flux, sky_wave, smoothed_sky_noise) #Find centroids of skylines in model centroids = [7843, 7916, 7995.5, 8347, 8467, 8829.5, 8922, 9378, 9442, 9794] sky_bounds = [[7835, 7850], [7908, 7920], [7990, 8000], [8340, 8353], [8460, 8475], [8820, 8835], [8915, 8930], [9370, 9385], [9435, 9447], [9785, 9799]] sky = Spectrum1D(spectral_axis=sky_norm_wave*u.Angstrom, flux=sky_norm_flux*u.ct) regions_sky = [] for i in range(len(sky_bounds)): regions_sky.append(SpectralRegion(sky_bounds[i][0]*u.Angstrom, sky_bounds[i][-1]*u.Angstrom)) sky_cents = centroid(sky, regions_sky) return sky_cents
def get_spectral_regions(self): """ Retrieves glue subset objects from the spectrum viewer and converts them to `~specutils.SpectralRegion` objects. Returns ------- spec_regs : dict Mapping from the names of the subsets to the subsets expressed as `specutils.SpectralRegion` objects. """ regions = self.app.get_subsets_from_viewer("spectrum-viewer", subset_type="spectral") spec_regs = {} for name, reg in regions.items(): # Use the region's unit. If N/A, use the reference_data's unit = reg.meta.get("spectral_axis_unit", self.get_spectra( self.app.get_viewer("spectrum-viewer" ).state.reference_data.label, apply_slider_redshift=False ).spectral_axis.unit ) spec_reg = SpectralRegion.from_center(reg.center.x * unit, reg.width * unit) spec_regs[name] = spec_reg return spec_regs
def test_statistics_gui_roi_spectrum(specviz_gui): # Ensure that the test is run on an unmodified workspace instance workspace = new_workspace(specviz_gui) hub = Hub(workspace=workspace) # Make region of interest cutout, using default cutout at .3 from the # middle in either direction specviz_gui.current_workspace.current_plot_window.plot_widget._on_add_linear_region( ) # Simulate cutout for truth data spectrum = extract_region(hub.plot_item._data_item.spectrum, SpectralRegion(*hub.selected_region_bounds)) # pull out stats dictionary stats_dict = specviz_gui.current_workspace._plugin_bars['Statistics'].stats # Generate truth comparisons truth_dict = { 'mean': spectrum.flux.mean(), 'median': np.median(spectrum.flux), 'stddev': spectrum.flux.std(), 'centroid': centroid(spectrum, region=None), 'snr': "N/A", 'fwhm': fwhm(spectrum), 'ew': equivalent_width(spectrum), 'total': line_flux(spectrum), 'maxval': spectrum.flux.max(), 'minval': spectrum.flux.min() } # compare! assert stats_dict == truth_dict workspace.close()
def detect_spikes(self, candidate_threshold: float = 100, falloff_fraction: float = 0.75, check_offset: float = 3) \ -> List[SpectralRegion]: # Candidates are threshold X mean(of whole spectrum) (simple algorithm for now - maybe look at localised mean) mean_flux = np.mean(self.flux) flux_threshold = candidate_threshold * mean_flux # candidate_threshold must not have units or we get adu2 candidates = np.where(self.flux >= flux_threshold)[0] spikes = [] ix_last_flux_to_check = len(self.flux) - check_offset - 1 for c_ix in candidates: if (c_ix + 1) in candidates: # If its neighbour is in the list then it can't be a spike pass elif check_offset <= c_ix <= ix_last_flux_to_check: # A spike must have a precipitous fall in non_ss_spectra either side # (by default, losing > 0.75 non_ss_spectra either side) previous_flux = self.flux[c_ix - check_offset] next_flux = self.flux[c_ix + check_offset] this_flux = self.flux[c_ix] falloff = this_flux - ( (this_flux - mean_flux) * falloff_fraction) if falloff > previous_flux and falloff > next_flux: lambda_from = self.wavelength[c_ix - check_offset] lambda_to = self.wavelength[c_ix + check_offset] spikes.append(SpectralRegion(lambda_from, lambda_to)) return spikes
def measure_ew_emission_line(model, emline, wave_margin=300, redshift=0.0, cont_low=5, cont_upp=3, cont_degree=2): """Measure the EW of an emission line after normalization.""" # Decide the wavelength range wave_flag = ((model['wave'] >= emline['cen'] - wave_margin) & (model['wave'] <= emline['cen'] + wave_margin)) wave_use = model['wave'][wave_flag] flux_em = model['spec_em'][wave_flag] flux_ne = model['spec_ne'][wave_flag] # Normalize the spectrum, so the continuum level is 1.0 flux_em_norm = sigma_clipping_continuum(wave_use, flux_em, low=5, upp=2, degree=cont_degree) flux_ne_norm = sigma_clipping_continuum(wave_use, flux_ne, low=2, upp=5, degree=cont_degree) # Form a Spectrum1D object ew_em = equivalent_width( Spectrum1D(spectral_axis=wave_use * u.AA, flux=flux_em_norm * u.Unit('erg cm-2 s-1 AA-1')), regions=SpectralRegion(emline['low'] * u.AA * (1.0 + redshift), emline['upp'] * u.AA * (1.0 + redshift)), continuum=1).value ew_ne = equivalent_width( Spectrum1D(spectral_axis=wave_use * u.AA, flux=flux_ne_norm * u.Unit('erg cm-2 s-1 AA-1')), regions=SpectralRegion(emline['low'] * u.AA * (1.0 + redshift), emline['upp'] * u.AA * (1.0 + redshift)), continuum=1).value return ew_ne - ew_em
def getRegion(self, index=None): if index is not None: subset = self._v1d.state.layers[index].layer if hasattr(subset, 'subset_state') and isinstance( subset.subset_state, RangeSubsetState): return SpectralRegion(subset.subset_state.lo * u.AA, subset.subset_state.hi * u.AA) else: return None else: return [ SpectralRegion(l.layer.subset_state.lo * u.AA, l.layer.subset_state.hi * u.AA) for l in self._v1d.state.layers if hasattr(l.layer, 'subset_state') and isinstance(l.layer.subset_state, RangeSubsetState) ]
def plot_feros_spectra(spectrum_path, outpath=None, xlim=None): 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) f, ax = plt.subplots() ax.plot(wav, flx, c='k', zorder=3) 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) ax.plot(wav, cont_flx, c='r', zorder=2) ax.set_xlabel('wavelength [angstrom]') ax.set_ylabel('relative flux') if isinstance(xlim, list): ax.set_xlim(xlim) format_ax(ax) savefig(f, outpath)
def _calculate_statistics(self, *args, **kwargs): """ Run the line analysis functions on the selected data/subset and display the results. """ if self.selected_spectrum == "": self.result_available = False return self._spectrum1d = self.app.get_data_from_viewer( "spectrum-viewer", data_label=self.selected_spectrum) if self.selected_subset != "Entire Spectrum": mask = self.app.get_data_from_viewer( "spectrum-viewer", data_label=self.selected_subset).mask self._spectrum1d.mask = mask temp_results = [] for function in FUNCTIONS: # Centroid function requires a region argument, create one to pass if function == "Centroid": spectral_axis = self._spectrum1d.spectral_axis if self._spectrum1d.mask is None: spec_region = SpectralRegion(spectral_axis[0], spectral_axis[-1]) else: spec_region = self._spectrum1d.spectral_axis[np.where( self._spectrum1d.mask == 0)] spec_region = SpectralRegion(spec_region[0], spec_region[-1]) temp_result = FUNCTIONS[function](self._spectrum1d, spec_region) else: temp_result = FUNCTIONS[function](self._spectrum1d) temp_results.append({ 'function': function, 'result': str(temp_result) }) self.results = temp_results self.result_available = True
def _on_subset_selected(self, event): # If "None" selected, reset based on bounds of selected data if self.selected_subset == "None": cube = self._selected_data.get_object(cls=SpectralCube) self.spectral_min = cube.spectral_axis[0].value self.spectral_max = cube.spectral_axis[-1].value else: spec_sub = self._spectral_subsets[self.selected_subset] unit = u.Unit(self.spectral_unit) spec_reg = SpectralRegion.from_center(spec_sub.center.x * unit, spec_sub.width * unit) self.spectral_min = spec_reg.lower.value self.spectral_max = spec_reg.upper.value
def _on_subset_selected(self, event): # If "None" selected, reset based on bounds of selected data if self.selected_subset == "None": self._window = None self.spectral_min = self._spectrum1d.spectral_axis[0].value self.spectral_max = self._spectrum1d.spectral_axis[-1].value else: spec_sub = self._spectral_subsets[self.selected_subset] unit = u.Unit(self.spectral_unit) spreg = SpectralRegion.from_center(spec_sub.center.x * unit, spec_sub.width * unit) self._window = (spreg.lower, spreg.upper) self.spectral_min = spreg.lower.value self.spectral_max = spreg.upper.value
def findlines(self): ''' Returns arrays with wavelength positions of absorption and emission lines. ''' wavelength = self.wavelength flux = self.flux spectrum = Spectrum1D(flux=flux * u.Jy, spectral_axis=wavelength * u.Angstrom) noise_region = SpectralRegion(3000 * u.Angstrom, 10000 * u.Angstrom) spectrum = noise_region_uncertainty(spectrum, noise_region) lines = find_lines_threshold(spectrum, noise_factor=4) emit = lines['line_type'] == 'emission' absorb = lines['line_type'] == 'absorption' emission_lines = lines['line_center'][emit] try: emission_lines_arr = np.zeros(len(emission_lines)) except TypeError: print('no emission lines!') emission_lines_arr = None emission_lines = None if emission_lines is not None: for i in range(len(emission_lines)): emission_lines_arr[i] = emission_lines.data[i] absorption_lines = ['line_type'] == 'absorption' try: absorption_lines_arr = np.zeros(len(absorption_lines)) except TypeError: print('no absorption lines!') absorption_lines_arr = None absorption_lines = None if absorption_lines is not None: for j in range(len(absorption_lines)): absorption_lines_arr[j] = absorption_lines.data[j] self.emit = emission_lines_arr self.absorb = absorption_lines_arr return (emission_lines_arr, absorption_lines_arr)
def _calc_EW(self, wavelengths, flux, continuum): """ Generic function to calculate (using SpecUtils) the Equivalent width of a line requires: Wavelengths (assumed to be in nm) Flux (assumed to be in photons) Continuum (assumed to be in photons) The equivalent width will be measured on the normalized spectrum (flux/continuum) output: Equivalent_Width (Quantity) """ norm_spec = (flux / continuum) * u.photon norm_spec_wave = wavelengths * u.nanometer not_nan = ~np.isnan(norm_spec) spec = Spectrum1D(spectral_axis=norm_spec_wave[not_nan][::-1], flux=norm_spec[not_nan][::-1]) spec_region = SpectralRegion( np.min(wavelengths) * u.nm, np.max(wavelengths) * u.nm) return equivalent_width(spec, regions=spec_region)
def get_spectral_regions(self): """ Retrieves glue subset objects from the spectrum viewer and converts them to `~specutils.SpectralRegion` objects. Returns ------- spec_regs : dict Mapping from the names of the subsets to the subsets expressed as `specutils.SpectralRegion` objects. """ regions = self.app.get_subsets_from_viewer('spectrum-viewer') spec_regs = {} for name, reg in regions.items(): unit = reg.meta.get('spectral_axis_unit', u.Unit('Angstrom')) spec_reg = SpectralRegion.from_center(reg.center.x * unit, reg.width * unit) spec_regs[name] = spec_reg return spec_regs
#plt.figure() #plt.plot(heii_wave, heii_norm.flux) center = 6565 #4685 lower = center - 10 upper = center + 10 plt.figure() plt.plot(new_wave, new_flux, 'b') plt.xlim(lower, upper) peak = new_wave[(new_wave < upper) & (new_wave > lower)][np.argmax( new_flux[(new_wave < upper) & (new_wave > lower)])] print( peak, equivalent_width(new_spec, regions=SpectralRegion(lower * u.AA, upper * u.AA))) # # center = 4686 # lower = center - 10 # upper = center + 10 # plt.figure(gal) # plt.plot(heii_wave,heii_norm.flux,'b') # plt.xlim(lower,upper) # #plt.ylim(0,3) # print(heii_wave[np.argmax(heii_flux)], # equivalent_width(heii_norm, regions=SpectralRegion(lower*u.AA, upper*u.AA))) #from spectres import spectres #x = spectres(wave, new_wave, flux) #print (x)
def gaussian_fit(self,spectrum,submin,submax,star_name): #noise_region=SpectralRegion(2.26*u.um,2.3*u.um) #spectrum=noise_region_uncertainty(spectrum,noise_region) #lines = find_lines_threshold(spectrum, noise_factor=4) #print(lines[lines['line_type'] == 'emission']) #print(lines[lines['line_type'] == 'absorption']) #amp=0.00075 #centre=2.1675 #sub region defined from function parameters sub_region=SpectralRegion(submin*u.um,submax*u.um) #spectrum extracted from subregion sub_spectrum=extract_region(spectrum,sub_region) #continuum fitted g1_fit=fit_generic_continuum(spectrum) y_continuum_fitted=g1_fit(sub_spectrum.spectral_axis) #continuum fit plotted with subregion spectrum plt.plot(sub_spectrum.spectral_axis,sub_spectrum.flux) plt.plot(sub_spectrum.spectral_axis,y_continuum_fitted) plt.title('Continuum Fitting') plt.grid(True) plt.show() #continuum substracted to show intensity sub_spectrum=sub_spectrum-y_continuum_fitted #initial gaussian fitted to estimate parameters for more accurate fitting estimates=estimate_line_parameters(sub_spectrum,models.Gaussian1D()) #new gauss_axis variable created for a smooth fit gauss_axis=np.linspace(min(sub_spectrum.spectral_axis),max(sub_spectrum.spectral_axis),2000) #gaussian fit from previous estimates produced g_init=estimates g_fit=fit_lines(sub_spectrum,g_init) y_fit=g_fit(gauss_axis) #fit plotted plt.plot(sub_spectrum.spectral_axis,sub_spectrum.flux) plt.plot(gauss_axis,y_fit) #linestrength found strength=(max(y_fit)) #f=open('brackett_strengths.txt','a') #f.write(star_name + ' - '+ str(strength) + 'Jy') #f.close() plt.title('Single fit peak') plt.grid(True) plt.legend('Original Spectrum','Specutils Fit Result') plt.show()
###input values to integrate EW through input_list = [] input_list.append(input('Ha wavelengths:')) input_list.append(input('HeI2 wavelengths:')) input_list.append(input('HeI3 wavelengths:')) input_list.append(input('FeII2 wavelengths:')) ew_list = [] cont_lst = [] ##iterate through each observation to calculate Intensity. for cont,spec in zip(cont_list,data): ew_iter = [] cont_iter = [] for inp ,region in zip(input_list,region_list): cont_iter.append(extract_region(cont, SpectralRegion(region[0] * u.AA, region[1] * u.AA)).flux) ew_iter.append(equivalent_width(spec, regions=SpectralRegion(int(inp.split(' ')[0]) * u.AA, int(inp.split(' ')[1]) * u.AA), continuum=extract_region(cont, SpectralRegion(region[0] * u.AA, region[1] * u.AA)).flux)) ew_list.append(ew_iter) cont_lst.append(cont_iter) ew1 = [] ew2 = [] ew3 = [] ew4 = [] for i in ew_list: ew1.append(np.mean(np.array(i[0]))) ew2.append(np.mean(np.array(i[1]))) ew3.append(np.mean(np.array(i[2]))) ew4.append(np.mean(np.array(i[3])))
def spectral_region_over(cls, lambda_from: float, lambda_to: float, units: str = "Angstrom") \ -> SpectralRegion: return SpectralRegion(Quantity(lambda_from, units), Quantity(lambda_to, units))
def __init__(self, spectrumIn, wline, band, polyOrder=1, widthFactor=4., verbose=0): # ======================================================================= # Initial check in spectral_axis # ======================================================================= if not (isinstance(spectrumIn.spectral_axis, u.Quantity) and isinstance(spectrumIn.flux, u.Quantity)): raise ValueError("Spectral axis must be a `Quantity` object.") if not spectrumIn.spectral_axis.unit == u.um: raise ValueError("Spectral axis is not in units of microns") self.polyOrder = polyOrder resv = self.get_spec_resolution(int(band[1]), np.array(wline, dtype=float)) # ======================================================================= # Convert delta velocity to delta lambda in microns with astropy units equivalencies # ======================================================================= # c_kms = const.c.to('km/s') # fwhmum = (resv * u.kilometer/ u.s / c_kms) * wline rest_wline = wline * u.um fwhmum_q = (resv * u.km / u.s).to( u.um, equivalencies=u.doppler_optical(rest_wline)) - rest_wline fwhmum = fwhmum_q.value fwhm_to_sigma = 1. / (8 * np.log(2))**0.5 lineWidthEstimate = fwhmum * fwhm_to_sigma wmin, wmax = wline[0] - (widthFactor * fwhmum), wline[-1] + (widthFactor * fwhmum) if verbose: print("wline, wmin, wmax, resv, fwhmum, lineWidthEstimate: ", wline, wmin, wmax, resv, fwhmum, lineWidthEstimate) self.wmin = wmin self.wmax = wmax self.spectrumIn = spectrumIn # ======================================================================= # mask non finite elements # ======================================================================= spectrum = self.__finite(spectrumIn, verbose=verbose) self.spectrum = spectrum wunit = self.spectrum.spectral_axis.unit region = SpectralRegion(self.wmin * wunit, self.wmax * wunit) spectrum_region = extract_region(self.spectrum, region) # ======================================================================= # Compute peak flux estimates for model parameters starting values in the region # ======================================================================= peakFluxEstimate = [] for wsline in wline: wave = spectrum_region.spectral_axis.value #just the ndarray and not Quantity flux = spectrum_region.flux.value wdist = np.abs(wsline - wave) if verbose: print(wsline, min(wdist), max(wdist)) indexLine = np.where(wdist == min(wdist))[0][0] if verbose: print("indexLine= {}".format(indexLine)) peakEstimate = np.mean(flux[indexLine - 1:indexLine + 1]) if verbose: print('Estimates for peak init {}'.format(peakEstimate)) cont_sample = np.concatenate((flux[:5], flux[-5:]), axis=None) continuumEstimate = np.median( np.concatenate((flux[:5], flux[-5:]), axis=None)) peakFluxEstimate = np.append(peakFluxEstimate, peakEstimate - continuumEstimate) if verbose: print('Estimates for peak & continuum {}, {}'.format( peakFluxEstimate, continuumEstimate)) # ======================================================================= # Construct model compound (branching off lines+continuum or continuum) # ======================================================================= try: lineModel_init = models.Polynomial1D(self.polyOrder, c0=continuumEstimate, name='cont') for xi in range(len(wline)): lineModel_init += models.Gaussian1D( amplitude=peakFluxEstimate[xi], mean=wline[xi], stddev=lineWidthEstimate[xi], name='g{}'.format(xi + 1)) fitter = LevMarLSQFitter() lineModel = fit_lines(self.spectrum, lineModel_init, fitter=fitter, window=region) fitResult = lineModel(self.spectrum.spectral_axis) findLine = 1 self.flux = [] self.sigma = [] for idx in range(len(wline)): #momentarily taking advantage of astropy units for conversion line_amp = (lineModel.unitless_model[idx + 1].amplitude.value * u.Jy).to(u.Watt / u.m**2 / u.Hz) line_sig = (lineModel.unitless_model[idx + 1].stddev.value * u.um).to(u.Hz, equivalencies=u.spectral()) self.flux = np.append(self.flux, (line_amp * line_sig * np.sqrt(2. * np.pi)).value) self.sigma = np.append(self.sigma, line_sig.value) except: if verbose: print('Exception') lineModel_init = models.Polynomial1D(self.polyOrder, c0=continuumEstimate, name='cont') fitter = LevMarLSQFitter() lineModel = fit_lines( self.spectrum, lineModel_init, fitter=fitter, window=region ) #the problem is narrow window where the contribution of the continuum sample is small fitResult = lineModel(self.spectrum.spectral_axis) findLine = 0 self.model = lineModel self.fitResult = fitResult self.findLine = findLine self.fitter = fitter # ======================================================================= # Preserve continuum Polynomial model # ======================================================================= # there are two types of models, those that are based on # `~astropy.modeling.models.PolynomialModel` and therefore # require the ``degree`` parameter when instantiating the # class , and "everything else" that does not require an # "extra" parameter for class instantiation. compound_model = lineModel.n_submodels > 1 if compound_model: self.continuumModel = lineModel.unitless_model[0] else: self.continuumModel = lineModel.unitless_model if findLine: self.continuum = [] self.peak = [] self.centre = [] self.sigma = [] self.fwhm = [] self.chiSquared = (self.fitter.fit_info['fvec']**2).sum() / ( len(self.fitter.fit_info['fvec']) - len(self.fitter.fit_info['param_cov'].data)) self.stddev = np.sqrt( np.diag(fitter.fit_info['cov_x'] )) #standard deviations pertaining to all parameters. params_idx = [ int(param.split('_', -1)[-1]) for param in self.model.param_names ] self.fluxError = [] self.fluxErrorRelative = [] for idx in range(len(wline)): self.continuum = np.append( self.continuum, self.continuumModel( lineModel.unitless_model[idx + 1].mean.value)) self.peak = np.append( self.peak, lineModel.unitless_model[idx + 1].amplitude.value) self.centre = np.append( self.centre, lineModel.unitless_model[idx + 1].mean.value) self.sigma = np.append( self.sigma, lineModel.unitless_model[idx + 1].stddev.value) self.fwhm = np.append(self.fwhm, self.sigma / fwhm_to_sigma) line_amp = (lineModel.unitless_model[idx + 1].amplitude.value * u.Jy).to(u.Watt / u.m**2 / u.Hz) line_sig = (lineModel.unitless_model[idx + 1].stddev.value * u.um).to(u.Hz, equivalencies=u.spectral()) param_idx = [ i for i, value in enumerate(params_idx) if value == (idx + 1) ] self.fluxErrorRelative = np.append( self.fluxErrorRelative, np.sqrt( np.sum((self.stddev / self.model.parameters)[np.array( [param_idx])][np.array([0, -1])]**2.))) self.fluxError = self.fluxErrorRelative * self.flux self.width = self.fwhm else: self.continuum = np.array( [np.median(flux) for i in range(len(wline))]) self.flux = self.peak = self.sigma = self.fwhm = self.width = np.array( [0. for i in range(len(wline))]) self.centre = wline if verbose: print('Line Not Detected. Continuum: {}'.format( self.continuum)) self.line_spec = self.get_line_spec() self.chiSquared = (self.fitter.fit_info['fvec']**2).sum() / ( len(self.fitter.fit_info['fvec']) - len(self.fitter.fit_info['param_cov'].data)) return
import numpy as np from copy import copy from datetime import datetime from typing import List, Tuple, Union from astropy.units import si from astropy.modeling import CompoundModel from astropy.modeling.models import Gaussian1D, Polynomial1D from astropy.modeling.fitting import LevMarLSQFitter from specutils import SpectralRegion, Spectrum1D from specutils.fitting.continuum import fit_generic_continuum from spectroscopy import Spectrum1DEx, fit_utilities _b_e_exclusion_regions = SpectralRegion([(3900, 4150), (4300, 4700), (4800, 4950), (4960, 5080)] * si.AA) _r_e_exclusion_regions = SpectralRegion([(5900, 6100), (6450, 6750)] * si.AA) def fit(spectrum: Spectrum1DEx, key: str = None) -> List[CompoundModel]: """ This is the switchboard method for the spectral fitting. If it can find a specific method to do the fitting it'll call that, otherwise it falls back on to generic methods. """ this_module = __import__(__name__) method = f"fit_{key if key is not None else spectrum.name}" if hasattr(this_module, method): func = getattr(this_module, method) elif spectrum.is_blue: func = fit_blue_arm_spectrum else: func = fit_red_arm_spectrum
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
#MASK lines continum and SNR #define mask SNR mask_CATb = (x > regions_ex[0]) & (x < regions_ex[1]) mask_CATc = (x > regions_ex[2]) & (x < regions_ex[3]) mask_SNR = [any(tup) for tup in zip(mask_CATb, mask_CATc)] Imask_SNR = np.invert(mask_SNR) #corr offset Corr_rv= rv1(LINES[list(LINES.keys())[1]],float(lineBcentre/(1. * u.AA))) Che_model=fit_generic_continuum(spec, exclude_regions=[SpectralRegion(corr_mask(regions_ex[0],Corr_rv)*u.AA, corr_mask(regions_ex[1],Corr_rv)*u.AA), SpectralRegion(corr_mask(regions_ex[2],Corr_rv)*u.AA, corr_mask(regions_ex[3],Corr_rv)*u.AA), SpectralRegion(corr_mask(8409,Corr_rv)*u.AA, corr_mask(8415,Corr_rv)*u.AA), SpectralRegion(corr_mask(8415,Corr_rv)*u.AA, corr_mask(8422,Corr_rv)*u.AA), SpectralRegion(corr_mask(8422,Corr_rv)*u.AA, corr_mask(8428,Corr_rv)*u.AA), SpectralRegion(corr_mask(8431,Corr_rv)*u.AA, corr_mask(8442,Corr_rv)*u.AA), SpectralRegion(corr_mask(8465,Corr_rv)*u.AA, corr_mask(8471,Corr_rv)*u.AA), SpectralRegion(corr_mask(8579,Corr_rv)*u.AA, corr_mask(8585,Corr_rv)*u.AA), SpectralRegion(corr_mask(8595,Corr_rv)*u.AA, corr_mask(8600,Corr_rv)*u.AA), SpectralRegion(corr_mask(8610,Corr_rv)*u.AA, corr_mask(8623,Corr_rv)*u.AA), ]) #Che_model=fit_generic_continuum(spec, exclude_regions=[SpectralRegion(regions_ex[0]*u.AA, regions_ex[1]*u.AA), # SpectralRegion(regions_ex[2]*u.AA, regions_ex[3]*u.AA)])
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, band,
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 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')
input_list = [] input_list.append(input('NII wavelengths:')) input_list.append(input('HeI1 wavelengths:')) input_list.append(input('FeII1 wavelengths:')) input_list.append(input('OI1 wavelengths:')) ew_list = [] cont_lst = [] ##iterate through each observation to calculate Intensity. for cont, spec in zip(cont_list, data): ew_iter = [] cont_iter = [] for inp, region in zip(input_list, region_list): cont_iter.append( extract_region(cont, SpectralRegion(region[0] * u.AA, region[1] * u.AA)).flux) ew_iter.append( equivalent_width(spec, regions=SpectralRegion( int(inp.split(' ')[0]) * u.AA, int(inp.split(' ')[1]) * u.AA), continuum=extract_region( cont, SpectralRegion(region[0] * u.AA, region[1] * u.AA)).flux)) ew_list.append(ew_iter) cont_lst.append(cont_iter) ew1 = [] ew2 = [] ew3 = []
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