def test1minimize(self):
        """ I sigproc.fit.Minimizer Jacobian minimize """

        self.model.setup_parameters(value=[3.0, 1.0, 1.0])
        result = fit.minimize(self.x, self.y, self.model)
        names = self.model.parameters.name
        val = self.model.parameters.value
        err = self.model.parameters.stderr
        valr = [2.4272, 1.2912, 0.4928]
        errr = [0.0665, 0.0772, 0.0297]

        print(self.model.param2str(accuracy=4, output='result'))

        msg = 'Resulting value for {:s} is not correct within {:.0f}\%'
        pc = 0.05  # 5% difference is allowed
        for i in range(3):
            self.assertAlmostEqual(val[i],
                                   valr[i],
                                   delta=pc * valr[i],
                                   msg=msg.format(names[i], pc * 100))

        msg = 'Resulting std-error for {:s} is not correct within {:.0f}\%'
        pc = 0.1  # 10% difference is allowed
        for i in range(3):
            self.assertAlmostEqual(err[i],
                                   errr[i],
                                   delta=pc * errr[i],
                                   msg=msg.format(names[i], pc * 100))
 def test5mc_error(self):
     """ I sigproc.fit.Minimizer Function calculate_MC_error """
     result = fit.minimize(self.x, self.y, self.model)
     
     np.random.seed(11)
     mcerrors = result.calculate_MC_error(errors=0.5, points=100, short_output=True,
                                          verbose=False)
     
     msg = 'Short output returned wrong type (array expected)'
     self.assertEqual(np.shape(mcerrors), (3,), msg=msg)
     
     msg = 'Calculated MC errors are wrong'
     self.assertArrayAlmostEqual(mcerrors, [0.02156, 0.00108, 0.00607], places=3, msg=msg)
     
     np.random.seed(11)
     mcerrors = result.calculate_MC_error(errors=0.5, points=50, short_output=False, 
                                          verbose=False)
     
     msg = 'Long output returned wrong type (dict expected)'
     self.assertEqual(type(mcerrors), dict, msg=msg)
     
     msg = 'Not all parameters are included in output'
     self.assertTrue('ampl' in mcerrors, msg=msg)
     self.assertTrue('freq' in mcerrors, msg=msg)
     self.assertTrue('phase' in mcerrors, msg=msg)
     
     msg = 'MC error is not stored in the parameters object'
     params = self.model.parameters
     self.assertEqual(params['ampl'].mcerr, mcerrors['ampl'], msg=msg)
     self.assertEqual(params['freq'].mcerr, mcerrors['freq'], msg=msg)
     self.assertEqual(params['phase'].mcerr, mcerrors['phase'], msg=msg)
Example #3
0
def fitFunction(x, y, initial, function, vary):
    """
    Fit a function to a set of x and y values.
    
    @param x: The x grid
    @type x: array
    @param y: The y grid
    @type y: array
    @param initial: initial parameters
    @type initial: list
    @param function: The function to be fitted
    @type function: funclib.function (e.g. funclib.soft_parabola,funclib.gauss)
    @param vary: Allow initial parameter to be changed in fitting process. 
                 Must have same length as initial.
    @type vary: list[bool]
    
    @return: The model after minimization
    @rtype: funclib.function() (some function)
    """

    #-- fit only soft parabola
    #   1. setup model
    #mymodel = funclib.soft_parabola()
    if function == funclib.gauss and False in vary:
        mymodel = function(use_jacobian=False)
    else:
        mymodel = function()
    #   2. Initial values: e.g. for SP [int,vlsr,vexp,gamma]
    mymodel.setup_parameters(values=initial, vary=vary)
    #   3. minimize and evaluate fit
    result = fit.minimize(x, y, mymodel)
    return mymodel
Example #4
0
def fitFunction(x, y, initial, function, vary):

    """
    Fit a function to a set of x and y values.
    
    @param x: The x grid
    @type x: array
    @param y: The y grid
    @type y: array
    @param initial: initial parameters
    @type initial: list
    @param function: The function to be fitted
    @type function: funclib.function (e.g. funclib.soft_parabola,funclib.gauss)
    @param vary: Allow initial parameter to be changed in fitting process. 
                 Must have same length as initial.
    @type vary: list[bool]
    
    @return: The model after minimization
    @rtype: funclib.function() (some function)
    """

    # -- fit only soft parabola
    #   1. setup model
    # mymodel = funclib.soft_parabola()
    if function == funclib.gauss and False in vary:
        mymodel = function(use_jacobian=False)
    else:
        mymodel = function()
    #   2. Initial values: e.g. for SP [int,vlsr,vexp,gamma]
    mymodel.setup_parameters(values=initial, vary=vary)
    #   3. minimize and evaluate fit
    result = fit.minimize(x, y, mymodel)
    return mymodel
    def test3ci_interval(self):
        """ I sigproc.fit.Minimizer Function calculate_CI """
        model = self.model

        result = fit.minimize(self.x, self.y, model)
        ci = result.calculate_CI(parameters=None, sigmas=[0.674, 0.997])

        expected_ci = "\n\t{'ampl': {0.674: (1.4955, 1.5036), 0.997: (1.4873, 1.5117)}"\
                     +"\n\t'phase': {0.674: (3.1372, 3.1394), 0.997: (3.1349, 3.1417)},"\
                     +"\n\t'freq': {0.674: (0.1202, 0.1206), 0.997: (0.1197, 0.1210)}}"


        msg = 'dict output format is not correct:\nexpected: '+expected_ci+\
              '\nreceived: \n\t'+str(ci)
        self.assertTrue('ampl' in ci, msg=msg)
        self.assertTrue('phase' in ci, msg=msg)
        self.assertTrue('freq' in ci, msg=msg)

        self.assertTrue(0.674 in ci['ampl'] and 0.997 in ci['ampl'], msg=msg)
        self.assertTrue(0.674 in ci['phase'] and 0.997 in ci['phase'], msg=msg)
        self.assertTrue(0.674 in ci['freq'] and 0.997 in ci['freq'], msg=msg)

        msg = 'ci results are not correct, expected: \n' + expected_ci
        self.assertArrayAlmostEqual(ci['ampl'][0.674], [1.4955, 1.5036],
                                    places=2,
                                    msg=msg)
        self.assertArrayAlmostEqual(ci['ampl'][0.997], [1.4873, 1.5117],
                                    places=2,
                                    msg=msg)
        self.assertArrayAlmostEqual(ci['phase'][0.674], [3.1372, 3.1394],
                                    places=2,
                                    msg=msg)
        self.assertArrayAlmostEqual(ci['phase'][0.997], [3.1349, 3.1417],
                                    places=2,
                                    msg=msg)
        self.assertArrayAlmostEqual(ci['freq'][0.674], [0.1202, 0.1206],
                                    places=2,
                                    msg=msg)
        self.assertArrayAlmostEqual(ci['freq'][0.997], [0.1197, 0.1210],
                                    places=2,
                                    msg=msg)

        msg = 'ci intervals are not stored in the parameter object'
        for name, param in list(model.parameters.items()):
            self.assertTrue(0.674 in param.cierr, msg=msg)
            self.assertTrue(0.997 in param.cierr, msg=msg)

        msg = 'Reruning calculate_CI fails and raises an exception!'
        try:
            result.calculate_CI(parameters=None,
                                sigma=0.85,
                                maxiter=200,
                                short_output=True)
            result.calculate_CI(parameters=None,
                                sigma=0.95,
                                maxiter=200,
                                short_output=True)
        except Exception:
            self.fail(msg)
 def test2setupJacobian(self, mocked_class):
     """ sigproc.fit.Minimizer _setup_jacobian_function """
     result = fit.minimize(self.x, self.y, self.model, err=self.errors)
     
     msg = 'attr jacobian is not set'
     self.assertTrue(hasattr(result, 'jacobian'), msg=msg)
     
     msg = 'jacobian should be None in this case'
     self.assertTrue(result.jacobian == None, msg=msg)
    def test2setupJacobian(self, mocked_class):
        """ sigproc.fit.Minimizer _setup_jacobian_function """
        result = fit.minimize(self.x, self.y, self.model, err=self.errors)

        msg = 'attr jacobian is not set'
        self.assertTrue(hasattr(result, 'jacobian'), msg=msg)

        msg = 'jacobian should be None in this case'
        self.assertTrue(result.jacobian == None, msg=msg)
 def test1minimize(self):
     """ I sigproc.fit.Minimizer Function minimize """
     model = self.model
     
     result = fit.minimize(self.x, self.y, model)
     values = model.get_parameters()[0]
     
     msg = 'Fit did not converge to the correct values'
     self.assertAlmostEqual(values[0], self.value[0], places=2, msg=msg)
     self.assertAlmostEqual(values[1], self.value[1], places=2, msg=msg)
     self.assertAlmostEqual(values[2], self.value[2], places=2, msg=msg)
    def test1minimize(self):
        """ I sigproc.fit.Minimizer Function minimize """
        model = self.model

        result = fit.minimize(self.x, self.y, model)
        values = model.get_parameters()[0]

        msg = 'Fit did not converge to the correct values'
        self.assertAlmostEqual(values[0], self.value[0], places=2, msg=msg)
        self.assertAlmostEqual(values[1], self.value[1], places=2, msg=msg)
        self.assertAlmostEqual(values[2], self.value[2], places=2, msg=msg)
 def testMinimize(self):
     """ I sigproc.fit.Minimizer Model minimize """
     model = self.fitModel
     
     result = fit.minimize(self.x, self.y, model)
     functions = model.functions
     values1 = functions[0].get_parameters()[0]
     values2 = functions[1].get_parameters()[0]
     
     self.assertArrayAlmostEqual(values1, self.value1, places=2)
     self.assertArrayAlmostEqual(values2, self.value2, places=2)
    def testMinimize(self):
        """ I sigproc.fit.Minimizer Model minimize """
        model = self.fitModel

        result = fit.minimize(self.x, self.y, model)
        functions = model.functions
        values1 = functions[0].get_parameters()[0]
        values2 = functions[1].get_parameters()[0]

        self.assertArrayAlmostEqual(values1, self.value1, places=2)
        self.assertArrayAlmostEqual(values2, self.value2, places=2)
 def test1setupResiduals(self, mocked_class):
     """ sigproc.fit.Minimizer _setup_residual_function """
     
     result = fit.minimize(self.x, self.y, self.model)
     
     msg = 'attr residuals is not set'
     self.assertTrue(hasattr(result, 'residuals'), msg=msg)
     
     msg = 'residuals are not correctly calculated'
     residuals = result.residuals(self.parameters, self.x, self.y, 
                                  weights=self.weights)
     self.assertTrue(np.all(np.abs(residuals) < 1.0), msg=msg)
    def test1setupResiduals(self, mocked_class):
        """ sigproc.fit.Minimizer _setup_residual_function """

        result = fit.minimize(self.x, self.y, self.model)

        msg = 'attr residuals is not set'
        self.assertTrue(hasattr(result, 'residuals'), msg=msg)

        msg = 'residuals are not correctly calculated'
        residuals = result.residuals(self.parameters,
                                     self.x,
                                     self.y,
                                     weights=self.weights)
        self.assertTrue(np.all(np.abs(residuals) < 1.0), msg=msg)
 def test4ci2d_interval(self):
     """ I sigproc.fit.Minimizer Jacobian calculate_CI_2D """
     self.model.setup_parameters(value=[3.0, 1.0, 1.0])
     
     result = fit.minimize(self.x, self.y, self.model)
     x, y, ci = result.calculate_CI_2D(xpar='p1', ypar='p2', res=10, limits=None,
                                       ctype='prob')
     
     cir = [99.9977, 99.9019, 97.5451, 74.5012, 20.5787,
            38.3632, 87.5572, 99.1614, 99.9734, 99.9994]
            
     msg = 'The 2D CI values are not correct within 2\%'
     pc = 0.05
     for i in range(3):
         self.assertArrayAlmostEqual(ci[4], cir, delta = 2, msg = msg)
 def test3ci_interval(self):
     """ I sigproc.fit.Minimizer Jacobian calculate_CI """
     self.model.setup_parameters(value=[3.0, 1.0, 1.0])
     
     result = fit.minimize(self.x, self.y, self.model)
     ci = result.calculate_CI(parameters=None, sigmas=[0.994])
     cir = [[2.2378,2.6210],[1.0807,1.5276],[0.4015,0.5740]]
     val = self.model.parameters.value
     names = self.model.parameters.name
     
     msg = 'The 99.4\% CI interval for {:s} is not correct within {:.0f}\%'
     pc = 0.05
     for i,p in enumerate(names):
         self.assertArrayAlmostEqual(ci[p][0.994], cir[i], delta = pc*val[i],
                                msg=msg.format(p, pc*100))
 def test5mc_error(self):
     """ I sigproc.fit.Minimizer Jacobian calculate_MC_error """
     self.model.setup_parameters(value=[3.0, 1.0, 1.0])
     result = fit.minimize(self.x, self.y, self.model)
     result.calculate_MC_error(errors=self.err, points=100, short_output=True,
                               verbose=False)
     
     names = self.model.parameters.name
     err = self.model.parameters.mcerr
     errr = [0.0795, 0.0688, 0.0281]
     
     msg = 'Resulting MC-error for {:s} is not correct within {:.0f}\%'
     pc = 0.05 # 10% difference is allowed
     for i in range(3):
         self.assertAlmostEqual(err[i], errr[i], delta = pc*errr[i],
                                msg=msg.format(names[i], pc*100))
    def test4ci2d_interval(self):
        """ I sigproc.fit.Minimizer Function calculate_CI_2D """
        model = self.model

        result = fit.minimize(self.x, self.y, model)
        x, y, ci = result.calculate_CI_2D(xpar='ampl',
                                          ypar='freq',
                                          res=10,
                                          limits=None,
                                          ctype='prob')
        values, errors = model.get_parameters()

        msg = 'Auto limits are not value +- 5 * stderr'
        vmin = values - 5 * errors
        vmax = values + 5 * errors
        self.assertAlmostEqual(min(x), vmin[0], places=4, msg=msg)
        self.assertAlmostEqual(max(x), vmax[0], places=4, msg=msg)
        self.assertAlmostEqual(min(y), vmin[1], places=4, msg=msg)
        self.assertAlmostEqual(max(y), vmax[1], places=4, msg=msg)

        msg = 'Grid values are not correctly distributed'
        exp1 = [
            1.4790, 1.4835, 1.4881, 1.4927, 1.4972, 1.5018, 1.5064, 1.5109,
            1.5155, 1.5200
        ]
        exp2 = [
            0.11937, 0.11960, 0.11983, 0.12006, 0.12028, 0.12051, 0.12074,
            0.12097, 0.12120, 0.12143
        ]
        self.assertArrayAlmostEqual(x, exp1, places=3, msg=msg)
        self.assertArrayAlmostEqual(y, exp2, places=4, msg=msg)

        msg = 'resolution of the grid is not correct'
        self.assertEqual(len(ci), 10, msg=msg)
        self.assertEqual(len(ci[0]), 10, msg=msg)

        msg = 'CI values are not correct'
        exp1 = [
            99.9996, 99.9572, 98.2854, 79.4333, 27.5112, 25.6265, 77.7912,
            98.0525, 99.9489, 99.9995
        ]
        exp2 = [
            99.9996, 99.9565, 98.2751, 79.4030, 27.5112, 25.6436, 77.8194,
            98.0628, 99.9496, 99.9996
        ]
        self.assertArrayAlmostEqual(ci[4], exp1, places=2, msg=msg)
        self.assertArrayAlmostEqual(ci[:, 4], exp2, places=2, msg=msg)
    def test3ci_interval(self):
        """ I sigproc.fit.Minimizer Jacobian calculate_CI """
        self.model.setup_parameters(value=[3.0, 1.0, 1.0])

        result = fit.minimize(self.x, self.y, self.model)
        ci = result.calculate_CI(parameters=None, sigmas=[0.994])
        cir = [[2.2378, 2.6210], [1.0807, 1.5276], [0.4015, 0.5740]]
        val = self.model.parameters.value
        names = self.model.parameters.name

        msg = 'The 99.4\% CI interval for {:s} is not correct within {:.0f}\%'
        pc = 0.05
        for i, p in enumerate(names):
            self.assertArrayAlmostEqual(ci[p][0.994],
                                        cir[i],
                                        delta=pc * val[i],
                                        msg=msg.format(p, pc * 100))
    def test4ci2d_interval(self):
        """ I sigproc.fit.Minimizer Jacobian calculate_CI_2D """
        self.model.setup_parameters(value=[3.0, 1.0, 1.0])

        result = fit.minimize(self.x, self.y, self.model)
        x, y, ci = result.calculate_CI_2D(xpar='p1',
                                          ypar='p2',
                                          res=10,
                                          limits=None,
                                          ctype='prob')

        cir = [
            99.9977, 99.9019, 97.5451, 74.5012, 20.5787, 38.3632, 87.5572,
            99.1614, 99.9734, 99.9994
        ]

        msg = 'The 2D CI values are not correct within 2\%'
        pc = 0.05
        for i in range(3):
            self.assertArrayAlmostEqual(ci[4], cir, delta=2, msg=msg)
    def test5mc_error(self):
        """ I sigproc.fit.Minimizer Jacobian calculate_MC_error """
        self.model.setup_parameters(value=[3.0, 1.0, 1.0])
        result = fit.minimize(self.x, self.y, self.model)
        result.calculate_MC_error(errors=self.err,
                                  points=100,
                                  short_output=True,
                                  verbose=False)

        names = self.model.parameters.name
        err = self.model.parameters.mcerr
        errr = [0.0795, 0.0688, 0.0281]

        msg = 'Resulting MC-error for {:s} is not correct within {:.0f}\%'
        pc = 0.05  # 10% difference is allowed
        for i in range(3):
            self.assertAlmostEqual(err[i],
                                   errr[i],
                                   delta=pc * errr[i],
                                   msg=msg.format(names[i], pc * 100))
 def test3ci_interval(self):
     """ I sigproc.fit.Minimizer Function calculate_CI """
     model = self.model
     
     result = fit.minimize(self.x, self.y, model)
     ci = result.calculate_CI(parameters=None, sigmas=[0.674, 0.997])
     
     expected_ci = "\n\t{'ampl': {0.674: (1.4955, 1.5036), 0.997: (1.4873, 1.5117)}"\
                  +"\n\t'phase': {0.674: (3.1372, 3.1394), 0.997: (3.1349, 3.1417)},"\
                  +"\n\t'freq': {0.674: (0.1202, 0.1206), 0.997: (0.1197, 0.1210)}}"
     
     
     msg = 'dict output format is not correct:\nexpected: '+expected_ci+\
           '\nreceived: \n\t'+str(ci)
     self.assertTrue('ampl' in ci, msg=msg)
     self.assertTrue('phase' in ci, msg=msg)
     self.assertTrue('freq' in ci, msg=msg)
     
     self.assertTrue(0.674 in ci['ampl'] and 0.997 in ci['ampl'], msg=msg)
     self.assertTrue(0.674 in ci['phase'] and 0.997 in ci['phase'], msg=msg)
     self.assertTrue(0.674 in ci['freq'] and 0.997 in ci['freq'], msg=msg)
     
     msg = 'ci results are not correct, expected: \n' + expected_ci
     self.assertArrayAlmostEqual(ci['ampl'][0.674], [1.4955, 1.5036], places=2, msg=msg)
     self.assertArrayAlmostEqual(ci['ampl'][0.997], [1.4873, 1.5117], places=2, msg=msg)
     self.assertArrayAlmostEqual(ci['phase'][0.674], [3.1372, 3.1394], places=2, msg=msg)
     self.assertArrayAlmostEqual(ci['phase'][0.997], [3.1349, 3.1417], places=2, msg=msg)
     self.assertArrayAlmostEqual(ci['freq'][0.674], [0.1202, 0.1206], places=2, msg=msg)
     self.assertArrayAlmostEqual(ci['freq'][0.997], [0.1197, 0.1210], places=2, msg=msg)
     
     msg = 'ci intervals are not stored in the parameter object'
     for name, param in model.parameters.items():
         self.assertTrue(0.674 in param.cierr, msg=msg)
         self.assertTrue(0.997 in param.cierr, msg=msg)
     
     msg = 'Reruning calculate_CI fails and raises an exception!'
     try:
         result.calculate_CI(parameters=None, sigma=0.85, maxiter=200, short_output=True)
         result.calculate_CI(parameters=None, sigma=0.95, maxiter=200, short_output=True)
     except Exception:
         self.fail(msg)
    def test3perturbdata(self, mocked_class):
        """ sigproc.fit.Minimizer _perturb_input_data """
        result = fit.minimize(self.x, self.y, self.model, errors=self.errors)

        result.x = self.x.reshape(2, 50)
        result.y = self.y.reshape(2, 50)
        result.errors = result.errors.reshape(2, 50)

        np.random.seed(11)
        npoints = 100
        y_ = result._perturb_input_data(100)
        dy = np.abs(np.ravel(y_[0]) - np.ravel(result.y))

        print(dy)

        msg = 'Perturbed data has wrong shape'
        self.assertEqual(y_.shape, (100, ) + result.y.shape, msg=msg)

        msg = 'Perturbed data is NOT perturbed'
        self.assertTrue(np.all(dy > 0.), msg=msg)

        msg = 'Perturbed data is to strongly perturbed'
        self.assertTrue(np.all(dy < 3.), msg=msg)
 def test3perturbdata(self, mocked_class):
     """ sigproc.fit.Minimizer _perturb_input_data """
     result = fit.minimize(self.x, self.y, self.model, errors=self.errors)
     
     result.x = self.x.reshape(2,50)
     result.y = self.y.reshape(2,50)
     result.errors = result.errors.reshape(2,50)
     
     np.random.seed(11)
     npoints = 100
     y_ = result._perturb_input_data(100)
     dy = np.abs(np.ravel(y_[0]) - np.ravel(result.y))
     
     print dy
     
     msg = 'Perturbed data has wrong shape'
     self.assertEqual(y_.shape, (100,) + result.y.shape, msg=msg)
     
     msg = 'Perturbed data is NOT perturbed'
     self.assertTrue(np.all(dy > 0.), msg=msg)
     
     msg = 'Perturbed data is to strongly perturbed'
     self.assertTrue(np.all(dy < 3.), msg=msg)
    def test5mc_error(self):
        """ I sigproc.fit.Minimizer Function calculate_MC_error """
        result = fit.minimize(self.x, self.y, self.model)

        np.random.seed(11)
        mcerrors = result.calculate_MC_error(errors=0.5,
                                             points=100,
                                             short_output=True,
                                             verbose=False)

        msg = 'Short output returned wrong type (array expected)'
        self.assertEqual(np.shape(mcerrors), (3, ), msg=msg)

        msg = 'Calculated MC errors are wrong'
        self.assertArrayAlmostEqual(mcerrors, [0.02156, 0.00108, 0.00607],
                                    places=3,
                                    msg=msg)

        np.random.seed(11)
        mcerrors = result.calculate_MC_error(errors=0.5,
                                             points=50,
                                             short_output=False,
                                             verbose=False)

        msg = 'Long output returned wrong type (dict expected)'
        self.assertEqual(type(mcerrors), dict, msg=msg)

        msg = 'Not all parameters are included in output'
        self.assertTrue('ampl' in mcerrors, msg=msg)
        self.assertTrue('freq' in mcerrors, msg=msg)
        self.assertTrue('phase' in mcerrors, msg=msg)

        msg = 'MC error is not stored in the parameters object'
        params = self.model.parameters
        self.assertEqual(params['ampl'].mcerr, mcerrors['ampl'], msg=msg)
        self.assertEqual(params['freq'].mcerr, mcerrors['freq'], msg=msg)
        self.assertEqual(params['phase'].mcerr, mcerrors['phase'], msg=msg)
 def test1minimize(self):
     """ I sigproc.fit.Minimizer Jacobian minimize """
     
     self.model.setup_parameters(value=[3.0, 1.0, 1.0])
     result = fit.minimize(self.x, self.y, self.model)
     names = self.model.parameters.name
     val = self.model.parameters.value
     err = self.model.parameters.stderr
     valr = [2.4272, 1.2912, 0.4928]
     errr = [0.0665, 0.0772, 0.0297]
     
     print self.model.param2str(accuracy=4, output='result')
     
     msg = 'Resulting value for {:s} is not correct within {:.0f}\%'
     pc = 0.05 # 5% difference is allowed
     for i in range(3):
         self.assertAlmostEqual(val[i], valr[i], delta = pc*valr[i], 
                                msg=msg.format(names[i], pc*100))
                                
     msg = 'Resulting std-error for {:s} is not correct within {:.0f}\%'
     pc = 0.1 # 10% difference is allowed
     for i in range(3):
         self.assertAlmostEqual(err[i], errr[i], delta = pc*errr[i],
                                msg=msg.format(names[i], pc*100))
    def test4ci2d_interval(self):
        """ I sigproc.fit.Minimizer Function calculate_CI_2D """
        model = self.model
        
        result = fit.minimize(self.x, self.y, model)
        x, y, ci = result.calculate_CI_2D(xpar='ampl', ypar='freq', res=10, limits=None,
                                          ctype='prob')
        values, errors = model.get_parameters()
        
        msg = 'Auto limits are not value +- 5 * stderr'
        vmin = values - 5*errors
        vmax = values + 5*errors
        self.assertAlmostEqual(min(x), vmin[0], places=4, msg=msg)
        self.assertAlmostEqual(max(x), vmax[0], places=4, msg=msg)
        self.assertAlmostEqual(min(y), vmin[1], places=4, msg=msg)
        self.assertAlmostEqual(max(y), vmax[1], places=4, msg=msg)
        
        msg = 'Grid values are not correctly distributed'
        exp1 = [1.4790, 1.4835, 1.4881, 1.4927, 1.4972,
                1.5018, 1.5064, 1.5109, 1.5155, 1.5200]
        exp2 = [0.11937, 0.11960, 0.11983, 0.12006, 0.12028,
                0.12051, 0.12074, 0.12097, 0.12120, 0.12143]
        self.assertArrayAlmostEqual(x, exp1, places=3, msg=msg)
        self.assertArrayAlmostEqual(y, exp2, places=4, msg=msg)

        msg = 'resolution of the grid is not correct'
        self.assertEqual(len(ci), 10, msg=msg)
        self.assertEqual(len(ci[0]), 10, msg=msg)
        
        msg = 'CI values are not correct'
        exp1 = [99.9996, 99.9572, 98.2854, 79.4333, 27.5112, 
                25.6265, 77.7912, 98.0525, 99.9489, 99.9995]
        exp2 = [99.9996, 99.9565, 98.2751, 79.4030, 27.5112,
                25.6436, 77.8194, 98.0628, 99.9496, 99.9996]
        self.assertArrayAlmostEqual(ci[4], exp1, places=2, msg=msg)
        self.assertArrayAlmostEqual(ci[:,4], exp2, places=2, msg=msg)
Example #27
0
def fitLP(filename=None,lprof=None,theory=0,show=0,cfg='',convert_ms_kms=0,\
          vary_pars=['vexp'],i_vexp=15.0,i_gamma=1.0,do_gauss=0):
    '''
    Fit a line profile with a soft parabola, and a Gaussian component if 
    required. 
    
    The method automatically checks if a second component is needed (eg an 
    extra absorption component). An estimate of the expansion velocity (width 
    of the profile) and an improved guess of the vlsr are given. 
    
    A guess for the gas terminal velocity is returned, as well as its error and
    the fitted profile (sp/gaussian, and if applicable extra gaussian and the 
    full fit). 
    
    @keyword filename: The filename to the data file of the line profile. If 
                       None a line profile object is expected. 
                       
                       (default: None)
    @type filename: string
    @keyword lprof: A line profile object (LPDataReader or inheriting classes)
                    If None, a filename is expected! If not None, the results
                    are saved in this object as well as returned upon method 
                    call
                    
                    (default: None)
    @type lprof: LPDataReader()
    @keyword convert_ms_kms: Convert velocity grid from m/s to km/s.
    
                             (default: 0)
    @type convert_ms_kms: bool
    @keyword theory: If theoretical profile, and filename is given, set vlsr to
                     0 and read two columns. lprof not relevant if True.
                     
                     (default: 0)
    @type theory: bool
    @keyword vary_pars: The soft parabola parameters varied (can only be vexp
                        or gamma for now). The initial values for parameters
                        listed here are not used. If 'gamma' is requested, a 
                        reasonable guess for i_vexp when calling the method 
                        will improve the fitting results. This is done for the 
                        first guess only! If a Gaussian absorption is present
                        improving these first guesses won't make much of a 
                        difference. However, the first guess value for gamma
                        is still used. Vexp is always varied if absorption is 
                        present.
                        
                        (default: ['vexp'])                        
    @type vary_pars: list[string]
    @keyword i_vexp: The initial guess for the expansion velocity. Not relevant
                     if vexp is included in vary_pars.
                     
                     (default: 15.0)
    @type i_vexp: float
    @keyword i_gamma: The initial guess for the gamma parameter of soft parab.
                      Not relevant if gamma is included in vary_pars.
                      
                      (default: 1.0)
    @type i_gamma: float
    @keyword do_gauss: Force a Gaussian fit regardless of soft parabola fit 
                       results. Still does the soft parabola fit first to allow
                       for comparison of parameters.
                        
                       (default: 0)
    @type do_gauss: bool
    @keyword show: Show the results of the fit
                    
                   (default: 0)
    @type show: bool
    
    @return: dictionary including [vexp,evexp,gamma,egamma,fitprof,gaussian,\
             fullfit,dintint,fgintint] 
    @rtype: dict[float,float,float,float,funclib.Function(),\
                 funclib.Function(),funclib.Function()]
    
    '''

    print '*******************************************'
    if theory and filename <> None:
        d = DataIO.readCols(filename=filename)
        vel = d[0]
        flux = d[1]
        vlsr = 0.0
    else:
        if filename is None:
            filename = lprof.filename
        print '** Fitting line profile in %s.' % filename
        if lprof is None:
            lprof = readLineProfile(filename)
        vel = lprof.getVelocity()
        flux = lprof.getFlux()
        vel = vel[-np.isnan(flux)]
        flux = flux[-np.isnan(flux)]
        vlsr = lprof.getVlsr()

    if convert_ms_kms:
        vel = vel / 1000.
    #-- Initial values: [peak tmb,vlsr,vexp,gamma]
    #   For the central peak value, get a first guess from the data
    #   Attempt multiple vexp values and return the best fitting case.
    #   The initial values are given, with an arbitrary value for the vexp key
    i_mid = argmin(np.abs(vel - vlsr))
    peak = mean(flux[i_mid - 2:i_mid + 3])

    #-- Vary vexp or gamma if requested. If not requested i_vexp or i_gamma are
    #   used.
    #   Multiple values for gamma are tried and the best fitting model
    #   is then chosen based on the relative error of the fitted gamma.
    if 'gamma' in vary_pars:
        igammas = array([-0.5, -0.1, 0.1, 0.5, 1.0, 2.0, 4.0])
        firstguess = varyInitialFit(vel,flux,[peak,vlsr,i_vexp,0.0],index=3,\
                                    values=igammas,vary_window=1,vary=[1,1,1,1],\
                                    function=funclib.soft_parabola)
        i_gamma = firstguess.get_parameters()[0][3]
    #-- varyInitialFit adapts the velocity window itself. No more
    #   assumptions needed for the expansion velocity
    ivexps = array([50., 40., 30., 25., 20., 15., 10.])
    if 'vexp' in vary_pars:
        firstguess = varyInitialFit(vel,flux,[peak,vlsr,0.,i_gamma],index=2,\
                                    values=ivexps,vary_window=1,vary=[1,1,1,1],\
                                    function=funclib.soft_parabola)

    vexp = abs(firstguess.get_parameters()[0][2])
    window = 2.
    print 'First guess fit, using a soft parabola:'
    print firstguess.param2str(accuracy=5)

    #-- If vexp > 100, replace with 50. This value is unrealistic, and might be
    #   improved with an extra Gaussian. If not, it will be replaced with a
    #   full Gaussian fit anyway
    if vexp > 100: vexp = 50.
    #-- Check whether irregularities are present in the profile.
    #   Initial parameters for a gaussian are returned if true.
    #   For this, a broad selection is made of the profile, to allow for a
    #   decent noise determination outside the line profile
    keep = np.abs(vel - vlsr) <= (2 * window * vexp)
    velsel, fluxsel = vel[keep], flux[keep]
    include_gauss = checkLPShape(velsel,
                                 fluxsel,
                                 vlsr,
                                 vexp,
                                 window,
                                 show=show)

    #-- Do the fit of the line again, including an extra gaussian if
    #   irregularities are present.
    if include_gauss <> None:
        #-- fit soft para model + gaussian
        #   1. Set up new soft parabola for several guesses of vexp
        ivexps = list(ivexps)
        initial = [peak, vlsr, 0., i_gamma]
        all_init = [[p] * len(ivexps) for i, p in enumerate(initial) if i != 2]
        all_init.insert(2, ivexps)
        functions = [funclib.soft_parabola() for i in ivexps]
        [
            ff.setup_parameters(values=initi)
            for ff, initi in zip(functions, zip(*all_init))
        ]
        #   2. setup gaussian
        gaussians = [funclib.gauss() for ff in functions]
        #   initial guesses assuming an interstellar absorption line from the
        #   checkLPShape method
        [
            gg.setup_parameters(values=include_gauss,
                                vary=[True, True, True, False])
            for gg in gaussians
        ]
        #   3. combine soft para + gaussian, and minimize fit
        mymodels = [
            fit.Model(functions=[ff, gg])
            for ff, gg in zip(functions, gaussians)
        ]
        [fit.minimize(vel[np.abs(vel-vlsr)<=(init[2]*1.5)],\
                      flux[np.abs(vel-vlsr)<=(init[2]*1.5)],\
                      mymodel)
         for mymodel,init in zip(mymodels,zip(*all_init))]
        #   4. Select the best fitting result based on the error on vexp
        mymodels = [fg for fg in mymodels if fg.get_parameters()[1][2] != 0.]
        functions = [
            ff for fg, ff in zip(mymodels, functions)
            if fg.get_parameters()[1][2] != 0.
        ]
        gaussians = [
            gg for fg, gg in zip(mymodels, gaussians)
            if fg.get_parameters()[1][2] != 0.
        ]
        fitvalues = array([fg.get_parameters()[0][2] for fg in mymodels])
        fiterrors = array([fg.get_parameters()[1][2] for fg in mymodels])
        mymodel = mymodels[argmin(abs(fiterrors / fitvalues))]
        finalfit = functions[argmin(abs(fiterrors / fitvalues))]
        gaussian = gaussians[argmin(abs(fiterrors / fitvalues))]
        print 'Improved fit, including extra Gaussian:'
        print mymodel.param2str(accuracy=5)
    else:
        #-- if gamma is requested to be varied, allow another iteration on
        #   gamma with the best vexp guess we already have.
        if 'gamma' in vary_pars:
            finalfit = varyInitialFit(vel,flux,[peak,vlsr,vexp,0.0],\
                                      index=3,values=igammas,vary_window=1,\
                                      function=funclib.soft_parabola,\
                                      vary=[True,True,True,True])
            print 'Final fit with soft parabola, second gamma iteration:'
            print finalfit.param2str(accuracy=5)
        #-- firstguess is best we can do at the moment
        else:
            finalfit = firstguess

    #-- If the relative error on vexp is larger than 30%, usually something
    #   funky is going on in the emission line. Try a Gaussian instead.
    fvlsr = finalfit.get_parameters()[0][1]
    fevlsr = finalfit.get_parameters()[1][1]
    vexp = abs(finalfit.get_parameters()[0][2])
    evexp = abs(finalfit.get_parameters()[1][2])
    gamma = finalfit.get_parameters()[0][3]
    egamma = finalfit.get_parameters()[1][3]
    #-- Gamma has to be positive. If it isnt, dont bother with Gaussian
    #   (double peaked line profile will not be fitted well with a Gaussian!)
    if (evexp/vexp > 0.40 and gamma > 0) or (evexp/vexp > 0.20 and vexp> 30.) \
            or do_gauss:
        #-- Go back to default window to try a Gaussian fit
        #keep = np.abs(vel-vlsr)<=(80)
        #velselg,fluxselg = vel[keep],flux[keep]
        do_gauss = 1
        include_gauss = None
        #-- FWHM is twice vexp!
        sigmas = 2 * ivexps / (2. * sqrt(2. * log(2.)))
        finalfit = varyInitialFit(vel,flux,[peak,vlsr,0.,0.],index=2,\
                                  values=sigmas,function=funclib.gauss,\
                                  vary_window=1,vary=[True,True,True,False])
        vexp = abs(
            finalfit.get_parameters()[0][2]) * (2. * sqrt(2. * log(2.))) / 2.
        evexp = abs(
            finalfit.get_parameters()[1][2]) * (2. * sqrt(2. * log(2.))) / 2.
        fvlsr = finalfit.get_parameters()[0][1]
        fevlsr = finalfit.get_parameters()[1][1]
        gamma, egamma = None, None
        window = 3.
        print 'Improved fit, using a gaussian instead of soft parabola:'
        print finalfit.param2str(accuracy=5)

    #-- Compute numerical integrations.
    #   After fitting, window for integration should be 0.6*window. vexp is
    #   not expected to be too small anymore as in checkLPShape
    keep = np.abs(vel - vlsr) <= (0.6 * window * vexp)
    velsel = vel[keep]
    flux_first = firstguess.evaluate(velsel)
    flux_final = finalfit.evaluate(velsel)
    dimb = trapz(y=flux[keep], x=velsel)
    fi_final = trapz(y=flux_final, x=velsel)
    print('I_mb (emission line data): %f'\
          %dimb)
    print('I_mb (SP -- initial guess): %f'\
          %trapz(y=flux_first,x=velsel))
    print('I_mb (SP -- final guess): %f'\
          %fi_final)
    if include_gauss <> None:
        fitted_flux = mymodel.evaluate(velsel)
        print('I_mb (SP + Gauss fit): %f'\
              %trapz(y=fitted_flux,x=velsel))
    print('Final v_exp guess: %.4f +/- %.4f km/s' % (vexp, evexp))
    if gamma <> None:
        print('Final gamma guess: %.4f +/- %.4f' % (gamma, egamma))
    print('Final vlsr guess: %.4f +/- %.4f' % (fvlsr, fevlsr))
    fwhm = getLPDataFWHM(lprof)
    print('The FWHM is %.2f km/s.' % (fwhm))

    #-- plot
    if show or cfg:
        plt.clf()
        #-- improve velocity window for plotting
        keep = np.abs(vel - vlsr) <= (1.5 * window * vexp)
        velsel, fluxsel = vel[keep], flux[keep]
        vel_highres = np.linspace(velsel[0], velsel[-1], 10000)
        flux_final_highres = finalfit.evaluate(vel_highres)
        flux_first_highres = firstguess.evaluate(vel_highres)
        if include_gauss <> None:
            flux_full_highres = mymodel.evaluate(vel_highres)
        if show:
            plt.step(velsel,fluxsel,'-r',where='mid',lw=3,\
                     label='Observed profile')
            plt.plot(vel_highres,flux_first_highres,'b-',lw=3,\
                     label='First guess')
            plt.plot(vel_highres,flux_final_highres,'g--',lw=3,\
                     label='Improved guess')
            if include_gauss <> None:
                plt.plot(vel_highres,flux_full_highres,'g-',lw=2,\
                         label='Full fit (including Gaussian)')
            leg = plt.legend(loc='best', fancybox=True)
            leg.get_frame().set_alpha(0.5)
            plt.show()
        if cfg:
            pf = '%s_fitted_%s'%(do_gauss and 'gaussFit' or 'SPFit',\
                                 os.path.split(filename)[1])
            keytags = ['Observed profile', 'Improved guess']
            line_types = [
                '-r',
                '-b',
            ]
            x = [velsel, vel_highres]
            y = [fluxsel, flux_final_highres]
            if include_gauss <> None:
                line_types.append('g--')
                x.append(vel_highres)
                y.append(flux_full_highres)
                keytags.append('Full fit (including Gaussian)')
            pf = Plotting2.plotCols(x=x,y=y,filename=pf,cfg=cfg,linewidth=5,\
                                    yaxis='$T_\mathrm{mb}\ (\mathrm{K})$',\
                                    xaxis='$v (\mathrm{km}/\mathrm{s})$',\
                                    keytags=keytags,line_types=line_types,\
                                    histoplot=[0])
            print 'Your figure can be found at %s .' % pf
    #-- Collecting all relevant results and returning.
    results = dict()
    #-- If a Gaussian was used for the main profile fit
    results['do_gauss'] = do_gauss
    #-- Fitted parameters and errors
    results['vlsr'] = fvlsr
    results['evlsr'] = fevlsr
    results['vexp'] = vexp
    results['evexp'] = evexp
    results['fwhm'] = fwhm
    #-- Gamma is None if no soft parabola was fitted
    results['gamma'] = gamma
    results['egamma'] = egamma

    #-- Integrated line strengths: main line fit, data themselves, fit window
    results['fgintint'] = fi_final
    results['dintint'] = dimb
    results['intwindow'] = window * 0.6

    #-- Saving parameters for later evaluation. Full fit is accessible by
    #   making the functions separately and setting pars, then using fit.Model
    results['fitprof'] = (do_gauss and 'gauss' or 'soft_parabola',\
                          list(finalfit.get_parameters()[0]))
    if include_gauss <> None:
        results['fitabs'] = ('gauss', list(gaussian.get_parameters()[0]))
    else:
        results['fitabs'] = None

    return results
Example #28
0
def fitLP(
    filename=None,
    lprof=None,
    theory=0,
    show=0,
    cfg="",
    convert_ms_kms=0,
    vary_pars=["vexp"],
    i_vexp=15.0,
    i_gamma=1.0,
    do_gauss=0,
):

    """
    Fit a line profile with a soft parabola, and a Gaussian component if 
    required. 
    
    The method automatically checks if a second component is needed (eg an 
    extra absorption component). An estimate of the expansion velocity (width 
    of the profile) and an improved guess of the vlsr are given. 
    
    A guess for the gas terminal velocity is returned, as well as its error and
    the fitted profile (sp/gaussian, and if applicable extra gaussian and the 
    full fit). 
    
    @keyword filename: The filename to the data file of the line profile. If 
                       None a line profile object is expected. 
                       
                       (default: None)
    @type filename: string
    @keyword lprof: A line profile object (LPDataReader or inheriting classes)
                    If None, a filename is expected! If not None, the results
                    are saved in this object as well as returned upon method 
                    call
                    
                    (default: None)
    @type lprof: LPDataReader()
    @keyword convert_ms_kms: Convert velocity grid from m/s to km/s.
    
                             (default: 0)
    @type convert_ms_kms: bool
    @keyword theory: If theoretical profile, and filename is given, set vlsr to
                     0 and read two columns. lprof not relevant if True.
                     
                     (default: 0)
    @type theory: bool
    @keyword vary_pars: The soft parabola parameters varied (can only be vexp
                        or gamma for now). The initial values for parameters
                        listed here are not used. If 'gamma' is requested, a 
                        reasonable guess for i_vexp when calling the method 
                        will improve the fitting results. This is done for the 
                        first guess only! If a Gaussian absorption is present
                        improving these first guesses won't make much of a 
                        difference. However, the first guess value for gamma
                        is still used. Vexp is always varied if absorption is 
                        present.
                        
                        (default: ['vexp'])                        
    @type vary_pars: list[string]
    @keyword i_vexp: The initial guess for the expansion velocity. Not relevant
                     if vexp is included in vary_pars.
                     
                     (default: 15.0)
    @type i_vexp: float
    @keyword i_gamma: The initial guess for the gamma parameter of soft parab.
                      Not relevant if gamma is included in vary_pars.
                      
                      (default: 1.0)
    @type i_gamma: float
    @keyword do_gauss: Force a Gaussian fit regardless of soft parabola fit 
                       results. Still does the soft parabola fit first to allow
                       for comparison of parameters.
                        
                       (default: 0)
    @type do_gauss: bool
    @keyword show: Show the results of the fit
                    
                   (default: 0)
    @type show: bool
    
    @return: dictionary including [vexp,evexp,gamma,egamma,fitprof,gaussian,\
             fullfit,dintint,fgintint] 
    @rtype: dict[float,float,float,float,funclib.Function(),\
                 funclib.Function(),funclib.Function()]
    
    """

    print "*******************************************"
    if theory and filename <> None:
        d = DataIO.readCols(filename=filename)
        vel = d[0]
        flux = d[1]
        vlsr = 0.0
    else:
        if filename is None:
            filename = lprof.filename
        print "** Fitting line profile in %s." % filename
        if lprof is None:
            lprof = readLineProfile(filename)
        vel = lprof.getVelocity()
        flux = lprof.getFlux()
        vel = vel[-np.isnan(flux)]
        flux = flux[-np.isnan(flux)]
        vlsr = lprof.getVlsr()

    if convert_ms_kms:
        vel = vel / 1000.0
    # -- Initial values: [peak tmb,vlsr,vexp,gamma]
    #   For the central peak value, get a first guess from the data
    #   Attempt multiple vexp values and return the best fitting case.
    #   The initial values are given, with an arbitrary value for the vexp key
    i_mid = argmin(np.abs(vel - vlsr))
    peak = mean(flux[i_mid - 2 : i_mid + 3])

    # -- Vary vexp or gamma if requested. If not requested i_vexp or i_gamma are
    #   used.
    #   Multiple values for gamma are tried and the best fitting model
    #   is then chosen based on the relative error of the fitted gamma.
    if "gamma" in vary_pars:
        igammas = array([-0.5, -0.1, 0.1, 0.5, 1.0, 2.0, 4.0])
        firstguess = varyInitialFit(
            vel,
            flux,
            [peak, vlsr, i_vexp, 0.0],
            index=3,
            values=igammas,
            vary_window=1,
            vary=[1, 1, 1, 1],
            function=funclib.soft_parabola,
        )
        i_gamma = firstguess.get_parameters()[0][3]
    # -- varyInitialFit adapts the velocity window itself. No more
    #   assumptions needed for the expansion velocity
    ivexps = array([50.0, 40.0, 30.0, 25.0, 20.0, 15.0, 10.0])
    if "vexp" in vary_pars:
        firstguess = varyInitialFit(
            vel,
            flux,
            [peak, vlsr, 0.0, i_gamma],
            index=2,
            values=ivexps,
            vary_window=1,
            vary=[1, 1, 1, 1],
            function=funclib.soft_parabola,
        )

    vexp = abs(firstguess.get_parameters()[0][2])
    window = 2.0
    print "First guess fit, using a soft parabola:"
    print firstguess.param2str(accuracy=5)

    # -- If vexp > 100, replace with 50. This value is unrealistic, and might be
    #   improved with an extra Gaussian. If not, it will be replaced with a
    #   full Gaussian fit anyway
    if vexp > 100:
        vexp = 50.0
    # -- Check whether irregularities are present in the profile.
    #   Initial parameters for a gaussian are returned if true.
    #   For this, a broad selection is made of the profile, to allow for a
    #   decent noise determination outside the line profile
    keep = np.abs(vel - vlsr) <= (2 * window * vexp)
    velsel, fluxsel = vel[keep], flux[keep]
    include_gauss = checkLPShape(velsel, fluxsel, vlsr, vexp, window, show=show)

    # -- Do the fit of the line again, including an extra gaussian if
    #   irregularities are present.
    if include_gauss <> None:
        # -- fit soft para model + gaussian
        #   1. Set up new soft parabola for several guesses of vexp
        ivexps = list(ivexps)
        initial = [peak, vlsr, 0.0, i_gamma]
        all_init = [[p] * len(ivexps) for i, p in enumerate(initial) if i != 2]
        all_init.insert(2, ivexps)
        functions = [funclib.soft_parabola() for i in ivexps]
        [ff.setup_parameters(values=initi) for ff, initi in zip(functions, zip(*all_init))]
        #   2. setup gaussian
        gaussians = [funclib.gauss() for ff in functions]
        #   initial guesses assuming an interstellar absorption line from the
        #   checkLPShape method
        [gg.setup_parameters(values=include_gauss, vary=[True, True, True, False]) for gg in gaussians]
        #   3. combine soft para + gaussian, and minimize fit
        mymodels = [fit.Model(functions=[ff, gg]) for ff, gg in zip(functions, gaussians)]
        [
            fit.minimize(
                vel[np.abs(vel - vlsr) <= (init[2] * 1.5)], flux[np.abs(vel - vlsr) <= (init[2] * 1.5)], mymodel
            )
            for mymodel, init in zip(mymodels, zip(*all_init))
        ]
        #   4. Select the best fitting result based on the error on vexp
        mymodels = [fg for fg in mymodels if fg.get_parameters()[1][2] != 0.0]
        functions = [ff for fg, ff in zip(mymodels, functions) if fg.get_parameters()[1][2] != 0.0]
        gaussians = [gg for fg, gg in zip(mymodels, gaussians) if fg.get_parameters()[1][2] != 0.0]
        fitvalues = array([fg.get_parameters()[0][2] for fg in mymodels])
        fiterrors = array([fg.get_parameters()[1][2] for fg in mymodels])
        mymodel = mymodels[argmin(abs(fiterrors / fitvalues))]
        finalfit = functions[argmin(abs(fiterrors / fitvalues))]
        gaussian = gaussians[argmin(abs(fiterrors / fitvalues))]
        print "Improved fit, including extra Gaussian:"
        print mymodel.param2str(accuracy=5)
    else:
        # -- if gamma is requested to be varied, allow another iteration on
        #   gamma with the best vexp guess we already have.
        if "gamma" in vary_pars:
            finalfit = varyInitialFit(
                vel,
                flux,
                [peak, vlsr, vexp, 0.0],
                index=3,
                values=igammas,
                vary_window=1,
                function=funclib.soft_parabola,
                vary=[True, True, True, True],
            )
            print "Final fit with soft parabola, second gamma iteration:"
            print finalfit.param2str(accuracy=5)
        # -- firstguess is best we can do at the moment
        else:
            finalfit = firstguess

    # -- If the relative error on vexp is larger than 30%, usually something
    #   funky is going on in the emission line. Try a Gaussian instead.
    fvlsr = finalfit.get_parameters()[0][1]
    fevlsr = finalfit.get_parameters()[1][1]
    vexp = abs(finalfit.get_parameters()[0][2])
    evexp = abs(finalfit.get_parameters()[1][2])
    gamma = finalfit.get_parameters()[0][3]
    egamma = finalfit.get_parameters()[1][3]
    # -- Gamma has to be positive. If it isnt, dont bother with Gaussian
    #   (double peaked line profile will not be fitted well with a Gaussian!)
    if (evexp / vexp > 0.40 and gamma > 0) or (evexp / vexp > 0.20 and vexp > 30.0) or do_gauss:
        # -- Go back to default window to try a Gaussian fit
        # keep = np.abs(vel-vlsr)<=(80)
        # velselg,fluxselg = vel[keep],flux[keep]
        do_gauss = 1
        include_gauss = None
        # -- FWHM is twice vexp!
        sigmas = 2 * ivexps / (2.0 * sqrt(2.0 * log(2.0)))
        finalfit = varyInitialFit(
            vel,
            flux,
            [peak, vlsr, 0.0, 0.0],
            index=2,
            values=sigmas,
            function=funclib.gauss,
            vary_window=1,
            vary=[True, True, True, False],
        )
        vexp = abs(finalfit.get_parameters()[0][2]) * (2.0 * sqrt(2.0 * log(2.0))) / 2.0
        evexp = abs(finalfit.get_parameters()[1][2]) * (2.0 * sqrt(2.0 * log(2.0))) / 2.0
        fvlsr = finalfit.get_parameters()[0][1]
        fevlsr = finalfit.get_parameters()[1][1]
        gamma, egamma = None, None
        window = 3.0
        print "Improved fit, using a gaussian instead of soft parabola:"
        print finalfit.param2str(accuracy=5)

    # -- Compute numerical integrations.
    #   After fitting, window for integration should be 0.6*window. vexp is
    #   not expected to be too small anymore as in checkLPShape
    keep = np.abs(vel - vlsr) <= (0.6 * window * vexp)
    velsel = vel[keep]
    flux_first = firstguess.evaluate(velsel)
    flux_final = finalfit.evaluate(velsel)
    dimb = trapz(y=flux[keep], x=velsel)
    fi_final = trapz(y=flux_final, x=velsel)
    print ("I_mb (emission line data): %f" % dimb)
    print ("I_mb (SP -- initial guess): %f" % trapz(y=flux_first, x=velsel))
    print ("I_mb (SP -- final guess): %f" % fi_final)
    if include_gauss <> None:
        fitted_flux = mymodel.evaluate(velsel)
        print ("I_mb (SP + Gauss fit): %f" % trapz(y=fitted_flux, x=velsel))
    print ("Final v_exp guess: %.4f +/- %.4f km/s" % (vexp, evexp))
    if gamma <> None:
        print ("Final gamma guess: %.4f +/- %.4f" % (gamma, egamma))
    print ("Final vlsr guess: %.4f +/- %.4f" % (fvlsr, fevlsr))
    fwhm = getLPDataFWHM(lprof)
    print ("The FWHM is %.2f km/s." % (fwhm))

    # -- plot
    if show or cfg:
        plt.clf()
        # -- improve velocity window for plotting
        keep = np.abs(vel - vlsr) <= (1.5 * window * vexp)
        velsel, fluxsel = vel[keep], flux[keep]
        vel_highres = np.linspace(velsel[0], velsel[-1], 10000)
        flux_final_highres = finalfit.evaluate(vel_highres)
        flux_first_highres = firstguess.evaluate(vel_highres)
        if include_gauss <> None:
            flux_full_highres = mymodel.evaluate(vel_highres)
        if show:
            plt.step(velsel, fluxsel, "-r", where="mid", lw=3, label="Observed profile")
            plt.plot(vel_highres, flux_first_highres, "b-", lw=3, label="First guess")
            plt.plot(vel_highres, flux_final_highres, "g--", lw=3, label="Improved guess")
            if include_gauss <> None:
                plt.plot(vel_highres, flux_full_highres, "g-", lw=2, label="Full fit (including Gaussian)")
            leg = plt.legend(loc="best", fancybox=True)
            leg.get_frame().set_alpha(0.5)
            plt.show()
        if cfg:
            pf = "%s_fitted_%s" % (do_gauss and "gaussFit" or "SPFit", os.path.split(filename)[1])
            keytags = ["Observed profile", "Improved guess"]
            line_types = ["-r", "-b"]
            x = [velsel, vel_highres]
            y = [fluxsel, flux_final_highres]
            if include_gauss <> None:
                line_types.append("g--")
                x.append(vel_highres)
                y.append(flux_full_highres)
                keytags.append("Full fit (including Gaussian)")
            pf = Plotting2.plotCols(
                x=x,
                y=y,
                filename=pf,
                cfg=cfg,
                linewidth=5,
                yaxis="$T_\mathrm{mb}\ (\mathrm{K})$",
                xaxis="$v (\mathrm{km}/\mathrm{s})$",
                keytags=keytags,
                line_types=line_types,
                histoplot=[0],
            )
            print "Your figure can be found at %s ." % pf
    # -- Collecting all relevant results and returning.
    results = dict()
    # -- If a Gaussian was used for the main profile fit
    results["do_gauss"] = do_gauss
    # -- Fitted parameters and errors
    results["vlsr"] = fvlsr
    results["evlsr"] = fevlsr
    results["vexp"] = vexp
    results["evexp"] = evexp
    results["fwhm"] = fwhm
    # -- Gamma is None if no soft parabola was fitted
    results["gamma"] = gamma
    results["egamma"] = egamma

    # -- Integrated line strengths: main line fit, data themselves, fit window
    results["fgintint"] = fi_final
    results["dintint"] = dimb
    results["intwindow"] = window * 0.6

    # -- Saving parameters for later evaluation. Full fit is accessible by
    #   making the functions separately and setting pars, then using fit.Model
    results["fitprof"] = (do_gauss and "gauss" or "soft_parabola", list(finalfit.get_parameters()[0]))
    if include_gauss <> None:
        results["fitabs"] = ("gauss", list(gaussian.get_parameters()[0]))
    else:
        results["fitabs"] = None

    return results