def _fit_1D(initial_model, spectrum, run_fitter): """ Fits an astropy CompoundModel to a Spectrum1D instance. Parameters ---------- spectrum : :class:`specutils.spectrum.Spectrum1D` The spectrum to be fitted. initial_model : :class: `astropy.modeling.CompoundModel` Initial guess for the model to be fitted. run_fitter : bool When False (the default), the function composes the compound model and returns it without fitting. Returns ------- :class: `astropy.modeling.CompoundModel` The model resulting from the fit. :class:`specutils.spectrum.Spectrum1D` The realization of the fitted model as a spectrum. """ if run_fitter: output_model = fit_lines(spectrum, initial_model) output_values = output_model(spectrum.spectral_axis) else: # Return without fitting. output_model = initial_model output_values = initial_model(spectrum.spectral_axis) # Build return spectrum output_spectrum = Spectrum1D(spectral_axis=spectrum.spectral_axis, flux=output_values) return output_model, output_spectrum
def __call__(self): results = {'x': [], 'y': [], 'fitted_model': [], 'fitted_values': []} for parameters in self.param_set: x = parameters[0] y = parameters[1] # Calling the Spectrum1D constructor for every spaxel # turned out to be less expensive than expected. Experiments # show that the cost amounts to a couple percent additional # running time in comparison with a version that uses a 3D # spectrum as input. Besides, letting an externally-created # spectrum reference into the callable somehow prevents it # to execute. This behavior was seen also with other functions # passed to the callable. flux = self.cube[x, y, :] # transposed! sp = Spectrum1D(spectral_axis=self.wave, flux=flux) import sys, traceback try: fitted_model = fit_lines(sp, self.model) fitted_values = fitted_model(self.wave) results['x'].append(x) results['y'].append(y) results['fitted_model'].append(fitted_model.unitless_model) results['fitted_values'].append(fitted_values.value) except: pass # print("Exception on [{},{}]:".format(x, y), sys.exc_info()[0]) # print(results) return results
def _on_fit_clicked(self, model_plot_data_item): fit_mod = fit_lines(self.hub.data_item.spectrum, result) flux = fit_mod(self.hub.data_item.spectrum.spectral_axis) new_spec = Spectrum1D( flux=flux, spectral_axis=self.hub.data_item.spectrum.spectral_axis) # self.hub.model.add_data(new_spec, "Fitted Model Spectrum") # Update the stored plot data item object for this model editor model # self._model_editor_model.plot_data_item.data_item.set_data(new_spec) # Fitted quantity models do not preserve the names of the sub models # which are used to relate the fitted sub models back to the displayed # models in the model editor. Go through and hope that their order is # preserved. if result.n_submodels() > 1: for i, x in enumerate(result): fit_mod.unitless_model._submodels[i].name = x.name sub_mods = [x for x in fit_mod.unitless_model] else: fit_mod.unitless_model.name = result.name sub_mods = [fit_mod.unitless_model] disp_mods = {item.text(): item for item in model_editor_model.items} for i, sub_mod in enumerate(sub_mods): # Get the base astropy model object model_item = disp_mods.get(sub_mod.name) # For each of the children `StandardItem`s, parse out their # individual stored values for cidx in range(model_item.rowCount()): param_name = model_item.child(cidx, 0).data() if result.n_submodels() > 1: parameter = getattr(fit_mod, "{0}_{1}".format(param_name, i)) else: parameter = getattr(fit_mod, param_name) model_item.child(cidx, 1).setText("{:.4g}".format(parameter.value)) model_item.child(cidx, 1).setData(parameter.value, Qt.UserRole + 1) model_item.child(cidx, 3).setData(parameter.fixed, Qt.UserRole + 1) for i in range(0, 3): self.model_tree_view.resizeColumnToContents(i)
def run(self): """ Implicitly called when the thread is started. Performs the operation. """ self.status.emit("Fitting model...", 0) fit_mod = fit_lines(self.spectrum, self.model, fitter=self.fitter, window=self.window, **self.fitter_kwargs) if not self.fitter.fit_info.get('message', ""): self.status.emit("Fit completed successfully!", 5000) else: self.status.emit("Fit completed, but with warnings.", 5000) self.result.emit(fit_mod)
def __call__(self, parameters): x = parameters[0] y = parameters[1] # Calling the Spectrum1D constructor for every spaxel # turned out to be less expensive than expected. Experiments # show that the cost amounts to a couple percent additional # running time in comparison with a version that uses a 3D # spectrum as input. Besides, letting an externally-created # spectrum reference into the callable somehow prevents it # to execute. This behavior was seen also with other functions # passed to the callable. flux = self.cube[x, y, :] # transposed! sp = Spectrum1D(spectral_axis=self.wave, flux=flux) fitted_model = fit_lines(sp, self.model) fitted_values = fitted_model(self.wave) return (x, y, fitted_model, fitted_values)
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 _on_fit_clicked(self, eq_pop_up=True): if eq_pop_up: self._on_equation_edit_button_clicked() # Grab the currently selected plot data item from the data list plot_data_item = self.hub.plot_item # If this item is not a model data item, bail if not isinstance(plot_data_item.data_item, ModelDataItem): return data_item = self._get_selected_data_item() if data_item is None: return spectral_region = self.hub.spectral_regions # Compose the compound model from the model editor sub model tree view model_editor_model = plot_data_item.data_item.model_editor_model result = model_editor_model.evaluate() if result is None: QMessageBox.warning( self, "Please add models to fit.", "Models can be added by clicking the" " green \"add\" button and selecting a" " model from the drop-down menu") return # Load options fitter = FITTERS[self.fitting_options["fitter"]] output_formatter = "{:0.%sg}" % self.fitting_options['displayed_digits'] kwargs = {} if fitter is fitting.LevMarLSQFitter: kwargs['maxiter'] = self.fitting_options['max_iterations'] kwargs['acc'] = self.fitting_options['relative_error'] kwargs['epsilon'] = self.fitting_options['epsilon'] # Run the compound model through the specutils fitting routine. Ensure # that the returned values are always in units of the current plot by # passing in the spectrum with the spectral axis and flux # converted to plot units. spectrum = data_item.spectrum.with_spectral_unit( plot_data_item.spectral_axis_unit) spectrum = spectrum.new_flux_unit(plot_data_item.data_unit) fit_mod = fit_lines(spectrum, result, fitter=fitter(), window=spectral_region, **kwargs) if fit_mod is None: return # Fitted quantity models do not preserve the names of the sub models # which are used to relate the fitted sub models back to the displayed # models in the model editor. Go through and hope that their order is # preserved. # TODO: Uncomment for when specutils function is working with units # if result.n_submodels() > 1: # for i, x in enumerate(result): # fit_mod.unitless_model._submodels[i].name = x.name # sub_mods = [x for x in fit_mod.unitless_model] # else: # fit_mod.unitless_model.name = result.name # sub_mods = [fit_mod.unitless_model] if result.n_submodels() > 1: sub_mods = [x for x in fit_mod._submodels] for i, x in enumerate(result): fit_mod._submodels[i].name = x.name else: fit_mod.name = result.name sub_mods = [fit_mod] # Get a list of the displayed name for each sub model in the tree view disp_mods = {item.text(): item for item in model_editor_model.items} for i, sub_mod in enumerate(sub_mods): # Get the base astropy model object model_item = disp_mods.get(sub_mod.name) # For each of the children `StandardItem`s, parse out their # individual stored values for cidx in range(model_item.rowCount()): param_name = model_item.child(cidx, 0).data() if result.n_submodels() > 1: parameter = getattr(fit_mod, "{0}_{1}".format(param_name, i)) else: parameter = getattr(fit_mod, param_name) model_item.child(cidx, 1).setText( output_formatter.format(parameter.value)) model_item.child(cidx, 1).setData(parameter.value, Qt.UserRole + 1) model_item.child(cidx, 3).setData(parameter.fixed, Qt.UserRole + 1) for i in range(0, 4): self.model_tree_view.resizeColumnToContents(i) # Update the displayed data on the plot self._redraw_model()
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
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
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
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()
# define your array to store all of the arrays with points for a fit for each peak all_peak_fits = [] # define your array to store the parameters of each fit all_gaus_fits = [] for i in range(len(absorptions)): # initialize your gaussian fit -- I approximated values for amplitude and stddev by looking a tthe plot # then it will loop through each peak by its mean gaus_init = models.Gaussian1D(amplitude=-100*u.Jy, mean=absorptions[i][0], stddev=20*u.Angstrom) # fit the spectrum with the initialized gaussian looking only in a window of 100Å before and after each peak # this will only fit one peak and not confuse the program to fit the entire spectrum # gaus_fit returns the parameters of this fit gaus_fit = fit_lines(spec_normalized, gaus_init, window = SpectralRegion(absorptions[i][0]-100*u.Angstrom,absorptions[i][0]+100*u.Angstrom)) # peak fit applies those parameters to the wavelengths of the spectral axis peak_fit = gaus_fit(spec_normalized.spectral_axis) # append your findings all_gaus_fits.append(gaus_fit) all_peak_fits.append(peak_fit) print('Absorption peak:',i,'\n','mean:',gaus_fit.mean[0],'Angstrom', '\n','FWHM',gaus_fit.fwhm) plt.figure() plt.plot(spec_normalized.spectral_axis, spec_normalized.flux) # plot each gaussian fit