Ejemplo n.º 1
0
def test_Voigt1D_hum2(doppler):
    """Verify accuracy of Voigt profile in Humlicek approximation to Faddeeva.cc (SciPy)."""
    x = np.linspace(-20, 20, 400001)

    voi_w = models.Voigt1D(amplitude_L=2.0 / np.pi,
                           fwhm_L=1.0,
                           fwhm_G=doppler,
                           method='wofz')
    vf_w = voi_w(x)
    dvda_w = voi_w.fit_deriv(x,
                             x_0=0,
                             amplitude_L=2.0 / np.pi,
                             fwhm_L=1.0,
                             fwhm_G=doppler)

    voi_h = models.Voigt1D(amplitude_L=2.0 / np.pi,
                           fwhm_L=1.0,
                           fwhm_G=doppler,
                           method='humlicek2')
    vf_h = voi_h(x)
    dvda_h = voi_h.fit_deriv(x,
                             x_0=0,
                             amplitude_L=2.0 / np.pi,
                             fwhm_L=1.0,
                             fwhm_G=doppler)

    assert_allclose(vf_h, vf_w, rtol=1e-7 * (2 + 1 / np.sqrt(doppler)))
    assert_allclose(dvda_h, dvda_w, rtol=1e-9, atol=1e-7 * (1 + 30 / doppler))
Ejemplo n.º 2
0
def test_Voigt1D():
    voi = models.Voigt1D(amplitude_L=-0.5, x_0=1.0, fwhm_L=5.0, fwhm_G=5.0)
    xarr = np.linspace(-5.0, 5.0, num=40)
    yarr = voi(xarr)
    voi_init = models.Voigt1D(amplitude_L=-1.0, x_0=1.0, fwhm_L=5.0, fwhm_G=5.0)
    fitter = fitting.LevMarLSQFitter()
    voi_fit = fitter(voi_init, xarr, yarr)
    assert_allclose(voi_fit.param_sets, voi.param_sets)
Ejemplo n.º 3
0
def test_Voigt1D():
    voi = models.Voigt1D(amplitude_L=-0.5, x_0=1.0, fwhm_L=5.0, fwhm_G=5.0)
    xarr = np.linspace(-5.0, 5.0, num=40)
    yarr = voi(xarr)
    voi_init = models.Voigt1D(amplitude_L=-1.0, x_0=1.0, fwhm_L=5.0, fwhm_G=5.0)
    fitter = fitting.LevMarLSQFitter()
    voi_fit = fitter(voi_init, xarr, yarr)
    assert_allclose(voi_fit.param_sets, voi.param_sets)

    # Invalid method
    with pytest.raises(ValueError) as err:
        models.Voigt1D(method='test')
    assert str(err.value) ==\
        "Not a valid method for Voigt1D Faddeeva function: test."
Ejemplo n.º 4
0
def VoigtFit(pixels, projected_footprint, A, mu, sigma, residuals, i, plot):
    g_init = models.Voigt1D(amplitude_L=A,
                            x_0=mu,
                            fwhm_G=sigma * 2.,
                            fwhm_L=sigma * 3)
    #g_init.x_0.min = 0.
    #g_init.x_0.max = 80.
    #g_init.fwhm_L.min = 0.1
    #g_init.fwhm_G.min = 0.1
    #g_init.fwhm_L.max = 20.
    #g_init.fwhm_G.max = 20.
    fit_g = fitting.LevMarLSQFitter()
    psf = fit_g(g_init, pixels, projected_footprint)
    start = psf.x_0 - 5 * (psf.fwhm_L + psf.fwhm_G) / 2
    end = psf.x_0 + 5 * (psf.fwhm_L + psf.fwhm_G) / 2
    integral = (integrate.quad(lambda pixels: psf(pixels), start, end))[0]
    ''' begin Control plot'''
    if ((not i % 10) and (i < 400) and (plot == True)):
        pl.plot(pixels, psf(pixels), label='Voigt')
        pl.yscale('log')
        pl.ylim(1., 1E6)
        pl.plot(pixels, projected_footprint)
        pl.legend()
        pl.show()
    ''' End control plot'''
    return
def VoigtFit(pixels, profile, A, mu, sigma, weights=[], **kwargs):
    #pixels = pixels[20:60] est a smaller footprint. bof. definitley need a weight map.
    #profile = profile[20:60]
    ### I will return the gaussian profile as a weight map
    #weights=1/profile**2
    weights = np.ones(len(pixels))
    g_init = models.Voigt1D(amplitude_L=A,
                            x_0=mu,
                            fwhm_G=sigma * 2.,
                            fwhm_L=sigma * 3)
    #g_init.x_0.min = 0.
    #g_init.x_0.max = 80.
    #g_init.fwhm_L.min = 0.1
    #g_init.fwhm_G.min = 0.1
    #g_init.fwhm_L.max = 20.
    #g_init.fwhm_G.max = 20.
    fit_g = fitting.LevMarLSQFitter()
    psf = fit_g(g_init, pixels, profile, weights=weights)
    start = psf.x_0 - 5 * (psf.fwhm_L + psf.fwhm_G) / 2
    end = psf.x_0 + 5 * (psf.fwhm_L + psf.fwhm_G) / 2
    integral = (integrate.quad(lambda pixels: psf(pixels), start, end))[0]
    ''' begin Control plot'''
    #pl.plot(pixels, psf(pixels), label='Voigt')
    #pl.plot(pixels, weights, label='weights')
    #pl.yscale('log')
    #pl.ylim(1., 1E6)
    #pl.plot(profile)
    #pl.legend()
    #pl.show()
    ''' End control plot'''
    return integral, psf.fwhm_L.value, psf.fwhm_G.value
Ejemplo n.º 6
0
def test_Voigt1D_norm(algorithm):
    """Test integral of normalized Voigt profile."""
    voi = models.Voigt1D(amplitude_L=1.0/np.pi, x_0=0.0, fwhm_L=2.0, fwhm_G=1.5, method=algorithm)
    if algorithm == 'wofz':
        atol = 1e-14
    else:
        atol = 1e-8
    assert_allclose(quad(voi, -np.inf, np.inf)[0], 1.0, atol=atol)
Ejemplo n.º 7
0
def test_voigt_model():
    """
    Currently just tests that the model peaks at its origin.
    Regression test for https://github.com/astropy/astropy/issues/3942
    """

    m = models.Voigt1D(x_0=5, amplitude_L=10, fwhm_L=0.5, fwhm_G=0.9)
    x = np.arange(0, 10, 0.01)
    y = m(x)
    assert y[500] == y.max()  # y[500] is right at the center
Ejemplo n.º 8
0
def test_parameter_description():

    model = models.Gaussian1D(1.5, 2.5, 3.5)
    assert model.amplitude._description == "Amplitude (peak value) of the Gaussian"
    assert model.mean._description == "Position of peak (Gaussian)"

    model = models.Voigt1D(x_0=5, amplitude_L=10, fwhm_L=0.5, fwhm_G=0.9)
    assert model.amplitude_L._description == "The Lorentzian amplitude"
    assert model.fwhm_L._description == "The Lorentzian full width at half maximum"
    assert model.fwhm_G._description == "The Gaussian full width at half maximum"
Ejemplo n.º 9
0
def Signal_Analysis(x, y, params):
    #Create fit using initial parameters
    #Stop fit from wandering onto random spikes of noise
    bound_centre = (params[0] - 15, params[0] + 15)
    bound_width = (params[3] - 2, params[3] + 30)
    #bound_amp = (0,params[1]),'amplitude_L': bound_amp
    bound_parameters = {'x_o': bound_centre, 'fwhm_G': bound_width}
    fit_init = models.Voigt1D(params[0],
                              params[1],
                              params[2],
                              params[3],
                              bounds=bound_parameters)
    fit = fitting.LevMarLSQFitter()
    fitted_model = fit(fit_init, x, y)
    #Get value for fit peak (amplitude_L is not applicable)
    peak = abs(min(fitted_model(x)))
    #Post-Processing
    #Formula for voight width is found Olivero 1977
    fwhm_V = (fitted_model.fwhm_L / 2) + m.sqrt((fitted_model.fwhm_L**2) / 4 +
                                                fitted_model.fwhm_G**2)
    window_length = (1 / 3) * fwhm_V
    if window_length > 1:
        box_window = convolution.Box1DKernel(window_length)
        y = convolution.convolve(y, box_window)
    else:
        window_length = 2
        box_window = convolution.Box1DKernel(window_length)
        y = convolution.convolve(y, box_window)

    #Signal-to-Noise
    baseline = y - fitted_model(
        x
    )  #not cheating....just to find the sigma of the non-peak reason lazily
    noise = stats.mad_std(baseline)
    snr = abs(peak) / noise
    if snr > 4:
        peak = min(y)
        snr = abs(peak) / noise
    else:
        pass

    return y, fitted_model, snr
Ejemplo n.º 10
0
                    y_0=1.5,
                    ellip=0.0,
                    theta=0.0),
 astmodels.Sine1D(amplitude=10., frequency=0.5, phase=1.),
 astmodels.Cosine1D(amplitude=10., frequency=0.5, phase=1.),
 astmodels.Tangent1D(amplitude=10., frequency=0.5, phase=1.),
 astmodels.ArcSine1D(amplitude=10., frequency=0.5, phase=1.),
 astmodels.ArcCosine1D(amplitude=10., frequency=0.5, phase=1.),
 astmodels.ArcTangent1D(amplitude=10., frequency=0.5, phase=1.),
 astmodels.Trapezoid1D(amplitude=10., x_0=0.5, width=5., slope=1.),
 astmodels.TrapezoidDisk2D(amplitude=10.,
                           x_0=0.5,
                           y_0=1.5,
                           R_0=5.,
                           slope=1.),
 astmodels.Voigt1D(x_0=0.55, amplitude_L=10., fwhm_L=0.5, fwhm_G=0.9),
 astmodels.BlackBody(scale=10.0, temperature=6000. * u.K),
 astmodels.Drude1D(amplitude=10.0, x_0=0.5, fwhm=2.5),
 astmodels.Plummer1D(mass=10.0, r_plum=5.0),
 astmodels.BrokenPowerLaw1D(amplitude=10,
                            x_break=0.5,
                            alpha_1=2.0,
                            alpha_2=3.5),
 astmodels.ExponentialCutoffPowerLaw1D(10, 0.5, 2.0, 7.),
 astmodels.LogParabola1D(
     amplitude=10,
     x_0=0.5,
     alpha=2.,
     beta=3.,
 ),
 astmodels.PowerLaw1D(amplitude=10., x_0=0.5, alpha=2.0),
Ejemplo n.º 11
0
def test_single_peak_estimate():
    """
    Single Peak fit.
    """

    # Create the spectrum
    x_single, y_single = single_peak()
    s_single = Spectrum1D(flux=y_single*u.Jy, spectral_axis=x_single*u.um)

    #
    # Estimate parameter Gaussian1D
    # we give the true values for the Gaussian because it actually *should*
    # be pretty close to the true values, because it's a Gaussian...
    #

    g_init = estimate_line_parameters(s_single, models.Gaussian1D())


    assert np.isclose(g_init.amplitude.value, 3., rtol=.2)
    assert np.isclose(g_init.mean.value, 6.3, rtol=.1)
    assert np.isclose(g_init.stddev.value, 0.8, rtol=.3)

    assert g_init.amplitude.unit == u.Jy
    assert g_init.mean.unit == u.um
    assert g_init.stddev.unit == u.um

    #
    # Estimate parameter Lorentz1D
    # unlike the Gaussian1D here we do hand-picked comparison values, because
    # the "single peak" is a Gaussian and therefore the Lorentzian fit shouldn't
    # be quite right anyway
    #

    g_init = estimate_line_parameters(s_single, models.Lorentz1D())

    assert np.isclose(g_init.amplitude.value, 3.354169257846847)
    assert np.isclose(g_init.x_0.value, 6.218588636687762)
    assert np.isclose(g_init.fwhm.value, 1.6339001193853715)

    assert g_init.amplitude.unit == u.Jy
    assert g_init.x_0.unit == u.um
    assert g_init.fwhm.unit == u.um

    #
    # Estimate parameter Voigt1D
    #

    g_init = estimate_line_parameters(s_single, models.Voigt1D())

    assert np.isclose(g_init.amplitude_L.value, 3.354169257846847)
    assert np.isclose(g_init.x_0.value, 6.218588636687762)
    assert np.isclose(g_init.fwhm_L.value, 1.1553418541989058)
    assert np.isclose(g_init.fwhm_G.value, 1.1553418541989058)

    assert g_init.amplitude_L.unit == u.Jy
    assert g_init.x_0.unit == u.um
    assert g_init.fwhm_L.unit == u.um
    assert g_init.fwhm_G.unit == u.um


    #
    # Estimate parameter RickerWavelet1D
    #
    mh = models.RickerWavelet1D
    estimators = {
        'amplitude': lambda s: max(s.flux),
        'x_0': lambda s: centroid(s, region=None),
        'sigma': lambda s: fwhm(s)
    }
    #mh._constraints['parameter_estimator'] = estimators
    mh.amplitude.estimator = lambda s: max(s.flux)
    mh.x_0.estimator = lambda s: centroid(s, region=None)
    mh.sigma.estimator = lambda s: fwhm(s)

    g_init = estimate_line_parameters(s_single, mh)

    assert np.isclose(g_init.amplitude.value, 3.354169257846847)
    assert np.isclose(g_init.x_0.value, 6.218588636687762)
    assert np.isclose(g_init.sigma.value, 1.6339001193853715)

    assert g_init.amplitude.unit == u.Jy
    assert g_init.x_0.unit == u.um
    assert g_init.sigma.unit == u.um
Ejemplo n.º 12
0
def test_single_peak_estimate():
    """
    Single Peak fit.
    """

    # Create the spectrum
    x_single, y_single = single_peak()
    s_single = Spectrum1D(flux=y_single*u.Jy, spectral_axis=x_single*u.um)

    #
    # Estimate parameter Gaussian1D
    #

    g_init = estimate_line_parameters(s_single, models.Gaussian1D())

    assert np.isclose(g_init.amplitude.value, 3.354169257846847)
    assert np.isclose(g_init.mean.value, 6.218588636687762)
    assert np.isclose(g_init.stddev.value, 1.6339001193853715)

    assert g_init.amplitude.unit == u.Jy
    assert g_init.mean.unit == u.um
    assert g_init.stddev.unit == u.um

    #
    # Estimate parameter Lorentz1D
    #

    g_init = estimate_line_parameters(s_single, models.Lorentz1D())

    assert np.isclose(g_init.amplitude.value, 3.354169257846847)
    assert np.isclose(g_init.x_0.value, 6.218588636687762)
    assert np.isclose(g_init.fwhm.value, 1.6339001193853715)

    assert g_init.amplitude.unit == u.Jy
    assert g_init.x_0.unit == u.um
    assert g_init.fwhm.unit == u.um

    #
    # Estimate parameter Voigt1D
    #

    g_init = estimate_line_parameters(s_single, models.Voigt1D())

    assert np.isclose(g_init.amplitude_L.value, 3.354169257846847)
    assert np.isclose(g_init.x_0.value, 6.218588636687762)
    assert np.isclose(g_init.fwhm_L.value, 1.1553418541989058)
    assert np.isclose(g_init.fwhm_G.value, 1.1553418541989058)

    assert g_init.amplitude_L.unit == u.Jy
    assert g_init.x_0.unit == u.um
    assert g_init.fwhm_L.unit == u.um
    assert g_init.fwhm_G.unit == u.um


    #
    # Estimate parameter MexicanHat1D
    #
    mh = models.MexicanHat1D()
    estimators = {
        'amplitude': lambda s: max(s.flux),
        'x_0': lambda s: centroid(s, region=None),
        'stddev': lambda s: fwhm(s)
    }
    mh._constraints['parameter_estimator'] = estimators

    g_init = estimate_line_parameters(s_single, mh)

    assert np.isclose(g_init.amplitude.value, 3.354169257846847)
    assert np.isclose(g_init.x_0.value, 6.218588636687762)
    assert np.isclose(g_init.stddev.value, 1.6339001193853715)

    assert g_init.amplitude.unit == u.Jy
    assert g_init.x_0.unit == u.um
    assert g_init.stddev.unit == u.um
Ejemplo n.º 13
0
def make_2d_spectra(separation_fwhm=0, n_targets=1, fwhm=8., intens=0., noise_level=1., plots=False):
    x = 4056
    y = 1550

    header_copy = fits.getheader('/data/simon/data/soar/work/20161114_eng/reduced_data/fzh.0298_CVSO166_400m2_gg455.fits')
    header_copy['OBJECT'] = 'Test-%s'%str(separation_fwhm)
    header_copy['HISTORY'] = 'Simulated spectrum N-sources %s separation_fwhm %s FWHM %s' % (n_targets, separation_fwhm, fwhm)

    targets = n_targets
    target_separation_fwhm = float(separation_fwhm)
    image = []
    if targets > 1:
        target_location = [y / 2. - target_separation_fwhm / float(targets) * fwhm,
                           y / 2. + target_separation_fwhm / float(targets) * fwhm]
        print separation_fwhm, int(y / 2.), target_location, (target_separation_fwhm / targets) * fwhm
    else:
        target_location = [y / 2.]

    sub_x = np.linspace(0, 10, x)
    y_axis = range(y)

    spectrum = [[] for i in range(int(targets))]

    for i in range(x):
        if noise_level == 0:
            data = np.ones(y)
        else:
            data = np.random.normal(10, noise_level, y)
        for tar in range(int(targets)):
            amplitude = intens * intensity(sub_x[i])
            # theo_snr = amplitude / noise_level
            # print theo_snr
            # gauss = models.Gaussian1D(amplitude=amplitude, mean=target_location[tar], stddev=fwhm) 8.24687326842
            voigt = models.Voigt1D(amplitude_L=amplitude, x_0=target_location[tar], fwhm_L=0.942561669206, fwhm_G=fwhm)
            # gauss2 = models.Gaussian1D(amplitude=amplitude, mean=target_location[1], stddev=fwhm)

            # spectrum[tar].append(amplitude)
            sd = voigt(y_axis)
            spectrum[tar].append(np.sum(sd[int(target_location[tar] - 2.5 * fwhm):int(target_location[tar] + 2.5 * fwhm)]))
            # gauss.amplitude.value = amplitude
            data += voigt(y_axis)

        # signal_peak = xxxx
        # snr = 5
        # noise_level = 400.
        # noise_amplitude = signal_peak / snr
        # data =  np.random.normal(noise_level, noise_amplitude, data.shape)

        # data2 = gauss2(y_axis)
        # plt.plot(data)
        # plt.plot(data2)
        # plt.show()

        if i == int(x / 2.) and plots:
            # plt.title('FWHM Separation: %s' % separation_fwhm)
            plt.title('Intensity: %s' % (intens))
            plt.axvline(int(target_location[tar] - 2.5 * fwhm), color='r')
            plt.axvline(int(target_location[tar] + 2.5 * fwhm), color='r')
            plt.plot(data)
            plt.show()

        image.append(data)

        # plt.plot(y_axis, data)
        # plt.show()
        # rotated = zip(*original[::-1])
    rotated_image = zip(*image[::-1])
    hdu_name_file = '20161128_single-object_n%s_s%s-fwhm_%1.3f_int.fits'% (str(int(n_targets)), str(int(separation_fwhm)), intens)
    print(hdu_name_file)
    new_hdu = fits.PrimaryHDU(rotated_image, header=header_copy)
    new_hdu.writeto(hdu_name_file, clobber=True)
    for part_index in range(len(spectrum)):
        # f = open('obj.save', 'wb')
        # cPickle.dump(my_obj, f, protocol=cPickle.HIGHEST_PROTOCOL)
        # f.close()
        f = open(hdu_name_file.replace('.fits','_%s.pkl' % str(part_index + 1)), 'wb')
        pickle.dump(spectrum[part_index][::-1], f, protocol=pickle.HIGHEST_PROTOCOL)
        f.close()
        plt.plot(spectrum[part_index][::-1])
        plt.title('Simulated spectrum')
        plt.xlabel('Pixel Axis')
        plt.ylabel('Peak Intensity')
        plt.savefig('img/' + hdu_name_file.replace('.fits','.png'), dpi=300)
        if plots:
            plt.show()

    if plots:
        plt.title('Target Separation %s - N targets %s' % (str(separation_fwhm), targets))
        plt.imshow(rotated_image)
        plt.show()
Ejemplo n.º 14
0
print('-' * 20)
print('Amplitude:', optim_ly[0])
print('Mean:     ', optim_ly[1])
print('Sigma:    ', optim_ly[2])
print('Error:    ', np.sqrt(np.diag(covar_ly)))
print('Peak:     ', lorentz(np.where(sum_y == np.max(sum_y))[0][0], *optim_ly))
print('FWHM:     ', FWHM(sum_y, optim_ly, lorentz))

## Voigt

lwx = FWHM(sum_x, optim_lx, lorentz)
dif_lx = lwx[1] - lwx[0]
lwy = FWHM(sum_y, optim_ly, lorentz)
dif_ly = lwy[1] - lwy[0]

vx_init = models.Voigt1D(x_0 = max_locx, amplitude_L = optim_lx[0], \
                         fwhm_L = dif_lx, fwhm_G = dif_gx)
fit_vx = fitting.LevMarLSQFitter()
vx = fit_vx(vx_init, xra, sum_x)

vy_init = models.Voigt1D(x_0 = max_locy, amplitude_L = optim_ly[0], \
                         fwhm_L = dif_ly, fwhm_G = dif_gy)
fit_vy = fitting.LevMarLSQFitter()
vy = fit_vy(vy_init, yra, sum_y)

optim_vx, covar_vx = curve_fit(voigt,
                               xra,
                               sum_x,
                               p0=[1, max_locx, dif_lx, dif_gx])
optim_vy, covar_vy = curve_fit(voigt,
                               yra,
                               sum_y,
Ejemplo n.º 15
0
    def get_model_with_fitter(model_type, x, y):
        min_x, max_x = np.min(x), np.max(x)
        location_param = np.mean(x)
        amplitude_param = np.max(np.abs(y))
        spread_param = (max_x - min_x) / len(x)

        if model_type == FittingModels.GAUSSIAN_PLUS_LINEAR:
            model = models.Gaussian1D(
                amplitude=amplitude_param,
                mean=location_param,
                stddev=spread_param) + models.Polynomial1D(degree=1)
            fitter = fitting.LevMarLSQFitter()

        elif model_type == FittingModels.LORENTZIAN_PLUS_LINEAR:
            model = models.Lorentz1D(
                amplitude=amplitude_param,
                x_0=location_param,
                fwhm=spread_param) + models.Polynomial1D(degree=1)
            fitter = fitting.LevMarLSQFitter()

        elif model_type == FittingModels.VOIGT_PLUS_LINEAR:
            model = models.Voigt1D(
                x_0=location_param,
                amplitude_L=amplitude_param,
                fwhm_L=spread_param,
                fwhm_G=spread_param) + models.Polynomial1D(degree=1)
            fitter = fitting.LevMarLSQFitter()

        elif model_type == FittingModels.GAUSSIAN:
            model = models.Gaussian1D(amplitude=amplitude_param,
                                      mean=location_param,
                                      stddev=spread_param)
            fitter = fitting.LevMarLSQFitter()

        elif model_type == FittingModels.LORENTZIAN:
            model = models.Lorentz1D(amplitude=amplitude_param,
                                     x_0=location_param,
                                     fwhm=spread_param)
            fitter = fitting.LevMarLSQFitter()

        elif model_type == FittingModels.VOIGT:
            model = models.Voigt1D(x_0=location_param,
                                   amplitude_L=amplitude_param,
                                   fwhm_L=spread_param,
                                   fwhm_G=spread_param)
            fitter = fitting.LevMarLSQFitter()

        elif model_type == FittingModels.CHEBYSHEV_3:
            model = models.Chebyshev1D(degree=3)
            fitter = fitting.LinearLSQFitter()

        elif model_type == FittingModels.POLYNOMIAL_1:
            model = models.Polynomial1D(degree=1)
            fitter = fitting.LinearLSQFitter()

        elif model_type == FittingModels.POLYNOMIAL_2:
            model = models.Polynomial1D(degree=2)
            fitter = fitting.LinearLSQFitter()

        elif model_type == FittingModels.POLYNOMIAL_3:
            model = models.Polynomial1D(degree=3)
            fitter = fitting.LinearLSQFitter()

        else:
            raise Exception("Model " + str(model_type) +
                            " not in default models list.")

        return model, fitter
Ejemplo n.º 16
0
    def _fit_model_to_fluxOLD(self,
                              trace_names,
                              application_data,
                              fitting_models,
                              selected_data,
                              custom_model=None,
                              custom_fitter=None,
                              do_update_client=True,
                              include_fit_substracted_trace=False):
        # http://learn.astropy.org/rst-tutorials/User-Defined-Model.html
        # https://docs.astropy.org/en/stable/modeling/new-model.html
        # https://docs.astropy.org/en/stable/modeling/index.html
        # https://docs.astropy.org/en/stable/modeling/reference_api.html

        curve_mapping = {
            name: ind
            for ind, name in enumerate(application_data['traces'])
        }

        for fitting_model in fitting_models:
            for trace_name in trace_names:
                #ind = trace_indexes[trace_name]

                trace = application_data['traces'].get(trace_name)
                curve_number = curve_mapping[trace_name]

                x = np.asarray([
                    point['x'] for point in selected_data["points"]
                    if point['curveNumber'] == curve_number
                ])
                y = np.asarray([
                    point['y'] for point in selected_data["points"]
                    if point['curveNumber'] == curve_number
                ])
                ind = [
                    point['pointIndex'] for point in selected_data["points"]
                    if point['curveNumber'] == curve_number
                ]

                y_err = np.asarray(
                    trace["flux_error"]
                )[ind] if trace["flux_error"] is not None or len(
                    trace["flux_error"]) > 0 else None

                min_x, max_x = np.min(x), np.max(x)

                if custom_model is None and custom_fitter is None:

                    location_param = np.mean(x)
                    amplitude_param = np.max(np.abs(y))
                    spread_param = (max_x - min_x) / len(x)

                    if fitting_model == FittingModels.GAUSSIAN_PLUS_LINEAR:
                        data_model = models.Gaussian1D(
                            amplitude=amplitude_param,
                            mean=location_param,
                            stddev=spread_param) + models.Polynomial1D(
                                degree=1)
                    elif fitting_model == FittingModels.LORENTZIAN_PLUS_LINEAR:
                        data_model = models.Lorentz1D(
                            amplitude=amplitude_param,
                            x_0=location_param,
                            fwhm=spread_param) + models.Polynomial1D(degree=1)
                    elif fitting_model == FittingModels.VOIGT_PLUS_LINEAR:
                        data_model = models.Voigt1D(
                            x_0=location_param,
                            amplitude_L=amplitude_param,
                            fwhm_L=spread_param,
                            fwhm_G=spread_param) + models.Polynomial1D(
                                degree=1)
                    else:
                        raise Exception("Unsupported fitting model " +
                                        str(fitting_model))

                    fitting_model_name = fitting_model
                    fitter = fitting.LevMarLSQFitter()
                    fitted_model = fitter(data_model, x, y, weights=1. / y_err)

                else:
                    fitting_model_name = str(custom_model)
                    fitter = custom_fitter
                    fitted_model = fitter(custom_model,
                                          x,
                                          y,
                                          weights=1. / y_err)

                x_grid = np.linspace(min_x, max_x, 5 * len(x))
                y_grid = fitted_model(x_grid)

                parameter_errors = np.sqrt(
                    np.diag(fitter.fit_info['param_cov'])
                ) if fitter.fit_info['param_cov'] is not None else None

                fitted_trace_name = "fit" + str(
                    len(application_data['fitted_models']) +
                    1) + "_" + trace_name
                ancestors = trace['ancestors'] + [trace_name]
                flambda = [f for f in np.asarray(trace['flambda'])[ind]]
                fitted_trace = Trace(name=fitted_trace_name,
                                     wavelength=[x for x in x_grid],
                                     flux=[y for y in y_grid],
                                     ancestors=ancestors,
                                     spectrum_type=SpectrumType.FIT,
                                     color="black",
                                     linewidth=1,
                                     alpha=1.0,
                                     wavelength_unit=trace['wavelength_unit'],
                                     flux_unit=trace['flux_unit'],
                                     flambda=flambda,
                                     catalog=trace['catalog']).to_dict()

                self._set_color_for_new_trace(fitted_trace, application_data)
                self._add_trace_to_data(application_data, fitted_trace_name,
                                        fitted_trace, False)

                if include_fit_substracted_trace:
                    fitted_trace_name = "fit_substr_" + str(
                        len(application_data['fitted_models']) +
                        1) + "_" + trace_name
                    ancestors = trace['ancestors'] + [trace_name]

                    y_grid2 = fitted_model(x)
                    flux = y - y_grid2

                    f_labmda = fl.convert_flux(
                        flux=x,
                        wavelength=y,
                        from_flux_unit=trace['flux_unit'],
                        to_flux_unit=FluxUnit.F_lambda,
                        to_wavelength_unit=trace.get('flux_unit'))

                    fitted_trace = Trace(
                        name=fitted_trace_name,
                        wavelength=[x for x in x],
                        flux=[y for y in flux],
                        ancestors=ancestors,
                        spectrum_type=SpectrumType.FIT,
                        color="black",
                        linewidth=1,
                        alpha=1.0,
                        wavelength_unit=trace['wavelength_unit'],
                        flux_unit=trace['flux_unit'],
                        flambda=f_labmda,
                        catalog=trace['catalog']).to_dict()

                    self._set_color_for_new_trace(fitted_trace,
                                                  application_data)
                    self._add_trace_to_data(application_data,
                                            fitted_trace_name,
                                            fitted_trace,
                                            do_update_client=False)

                fitted_info = {}
                fitted_info['name'] = fitted_trace_name
                fitted_info['ancestors'] = ancestors
                fitted_info['model'] = fitting_model_name
                fitted_info['parameters'] = {
                    x: y
                    for (x, y) in zip(fitted_model.param_names,
                                      fitted_model.parameters)
                }
                fitted_info['parameter_errors'] = {
                    x: y
                    for (x,
                         y) in zip(fitted_model.param_names, parameter_errors)
                } if parameter_errors is not None else None
                fitted_info['selection_indexes'] = ind
                fitted_info['wavelength_unit'] = trace['wavelength_unit']
                fitted_info['flux_unit'] = trace['flux_unit']

                # add to application data:
                fitted_models = application_data['fitted_models']
                fitted_models[fitted_trace_name] = fitted_info
                application_data['fitted_models'] = fitted_models

                application_data['traces'][fitted_trace_name] = fitted_trace
                application_data['fitted_models'][
                    fitted_trace_name] = fitted_info
                self.write_info(
                    "fitting model2122 : " +
                    str(application_data['fitted_models'][fitted_trace_name]))

        if do_update_client:
            self.update_client()