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
Beispiel #3
0
    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)
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #7
0
    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()
Beispiel #8
0
    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
Beispiel #10
0
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
Beispiel #11
0
	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()
Beispiel #12
0
# 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