def test_input_exceptions(self): ''' Test exceptions thrown upon wrong input ''' if not self.isthere_dsfinterp(): return import dsfinterp nf = 9 fvalues, InputWorkspaces, xvalues, HWHM = self.generateWorkspaces(nf, 0.01, 0.01) # workspaces sim0 to sim8 (nine workpaces) # Try passing different number of InputWorkspaces and parameter values try: fvalueswrong = ' '.join(fvalues.split()[:-1]) # one less value func_string = 'name=DSFinterp1DFit,InputWorkspaces="{0}",ParameterValues="{1}",'.format(InputWorkspaces,fvalueswrong) +\ 'LoadErrors=0,LocalRegression=1,RegressionType=quadratic,RegressionWindow=6,' +\ 'WorkspaceIndex=0,Intensity=1.0,TargetParameter=0.01;' Fit( Function=func_string, InputWorkspace= 'targetW', WorkspaceIndex=0, StartX=xvalues[0], EndX=xvalues[-1], CreateOutput=1, MaxIterations=0 ) except Exception as e: self.assertTrue('Number of InputWorkspaces and ParameterValues should be the same' in str(e)) else: assert False, 'Did not raise any exception' # Try passing the wrong workspace index try: func_string = 'name=DSFinterp1DFit,InputWorkspaces="{0}",ParameterValues="{1}",'.format(InputWorkspaces,fvalues) +\ 'LoadErrors=0,LocalRegression=1,RegressionType=quadratic,RegressionWindow=6,' +\ 'WorkspaceIndex=1,Intensity=1.0,TargetParameter=0.01;' Fit( Function=func_string, InputWorkspace= 'targetW', WorkspaceIndex=0, StartX=xvalues[0], EndX=xvalues[-1], CreateOutput=1, MaxIterations=0 ) except Exception as e: self.assertTrue('Numer of histograms in Workspace sim0 does not allow for workspace index 1' in str(e)) else: assert False, 'Did not raise any exception' #Try passing the wrong type of regression try: func_string = 'name=DSFinterp1DFit,InputWorkspaces="{0}",ParameterValues="{1}",'.format(InputWorkspaces,fvalues) +\ 'LoadErrors=0,LocalRegression=1,RegressionType=baloney,RegressionWindow=6,' +\ 'WorkspaceIndex=0,Intensity=1.0,TargetParameter=0.01;' Fit( Function=func_string, InputWorkspace= 'targetW', WorkspaceIndex=0, StartX=xvalues[0], EndX=xvalues[-1], CreateOutput=1, MaxIterations=0 ) except Exception as e: self.assertTrue('Regression type baloney not implemented' in str(e)) else: assert False, 'Did not raise any exception' # Try passing an inappropriate regression window for the regression type selected try: func_string = 'name=DSFinterp1DFit,InputWorkspaces="{0}",ParameterValues="{1}",'.format(InputWorkspaces,fvalues) +\ 'LoadErrors=0,LocalRegression=1,RegressionType=quadratic,RegressionWindow=3,' +\ 'WorkspaceIndex=0,Intensity=1.0,TargetParameter=0.01;' Fit( Function=func_string, InputWorkspace= 'targetW', WorkspaceIndex=0, StartX=xvalues[0], EndX=xvalues[-1], CreateOutput=1, MaxIterations=0 ) except Exception as e: self.assertTrue('RegressionWindow must be equal or bigger than 4 for regression type quadratic' in str(e)) else: assert False, 'Did not raise any exception' self.cleanup(nf)
def test_function_returns_are_correct_type_when_output_ws_is_requested(self): if platform.system() == 'Darwin': # crashes return output_name = "fitWS" retvals = Fit("name=FlatBackground", self._raw_ws, Output="fitWS") self._check_returns_are_correct_type_with_workspaces(retvals) self.assertTrue(output_name + '_Workspace' in mtd)
def test_function_accepts_all_arguments_as_keywords(self): if platform.system() == 'Darwin': # crashes return output_name = "kwargsfitWS" retvals = Fit(Function="name=FlatBackground", InputWorkspace=self._raw_ws, Output=output_name) self._check_returns_are_correct_type_with_workspaces(retvals) self.assertTrue(output_name + '_Workspace' in mtd)
def test_function_returns_are_correct_type_when_no_output_ws_requested(self): if platform.system() == 'Darwin': # crashes return retvals = Fit("name=FlatBackground", self._raw_ws) self.assertEquals(len(retvals), 2) self.assertTrue(isinstance(retvals[0], str)) self.assertTrue(isinstance(retvals[1], float))
def test_function_returns_are_correct_type_when_no_output_ws_requested( self): if platform.system() == 'Darwin': # crashes return retvals = Fit("name=FlatBackground", self._raw_ws) self.assertTrue(isinstance(retvals.OutputStatus, str)) self.assertTrue(isinstance(retvals.OutputChi2overDoF, float))
def setUpClass(cls): delta = 0.33 x = np.linspace(0., 15., 100) x_offset = np.linspace(delta / 2, 15. + delta / 2, 100) x_offset_neg = np.linspace(-delta / 2, 15. - delta / 2, 100) testFunction = GausOsc(Frequency=1.5, A=0.22) y1 = testFunction(x_offset_neg) y2 = testFunction(x_offset) y = y1 / 2 + y2 / 2 ws = CreateWorkspace(x, y) convolution = FunctionFactory.createCompositeFunction('Convolution') innerFunction = FunctionFactory.createInitialized('name=GausOsc,A=0.2,Sigma=0.2,Frequency=1,Phi=0') deltaFunctions = FunctionFactory.createInitialized( '(name=DeltaFunction,Height=0.5,Centre={},ties=(Height=0.5,Centre={});name=DeltaFunction,Height=0.5,' 'Centre={},ties=(Height=0.5,Centre={}))'.format( -delta / 2, -delta / 2, delta / 2, delta / 2)) convolution.setAttributeValue('FixResolution', False) convolution.add(innerFunction) convolution.add(deltaFunctions) MultiDomainSingleFunction = FunctionFactory.createInitializedMultiDomainFunction( 'name=GausOsc,A=0.2,Sigma=0.2,Frequency=1,Phi=0', 2) MultiDomainConvolutionFunction = FunctionFactory.createInitializedMultiDomainFunction(str(convolution), 2) DoublePulseFit(Function=MultiDomainSingleFunction, InputWorkspace=ws, InputWorkspace_1=ws, CreateOutput=True, PulseOffset=delta, StartX=0.0, EndX=15.0, Output='DoublePulseFit', MaxIterations=1) Fit(Function=MultiDomainConvolutionFunction, InputWorkspace=ws, InputWorkspace_1=ws, CreateOutput=True, StartX=0.0, EndX=15.0, Output='Fit', MaxIterations=1)
def do_fit_all(self, ws_list, do_sequential=True): fitprop_list = [] prev_fitprop = self.view.read_fitprop_from_browser() for ws in ws_list: logger.notice(f'Starting to fit workspace {ws}') fitprop = deepcopy(prev_fitprop) # update I/O workspace name fitprop['properties']['Output'] = ws fitprop['properties']['InputWorkspace'] = ws # do fit fit_output = Fit(**fitprop['properties']) # update results fitprop['status'] = fit_output.OutputStatus funcstr = str(fit_output.Function.fun) fitprop['properties']['Function'] = funcstr if "success" in fitprop['status'].lower() and do_sequential: # update function in prev fitprop to use for next workspace prev_fitprop['properties']['Function'] = funcstr # update last fit in fit browser and save setup self.view.update_browser(fit_output.OutputStatus, funcstr, ws) # append a deep copy to output list (will be initial parameters if not successful) fitprop_list.append(fitprop) logger.notice('Sequential fitting finished.') self.fit_all_done_notifier.notify_subscribers(fitprop_list)
def _findLine(self, ws): """Return a peak position workspace.""" # TODO There should be a better algorithm in Mantid to achieve this. integratedWSName = self._names.withSuffix('integrated') integratedWS = Integration(InputWorkspace=ws, OutputWorkspace=integratedWSName, EnableLogging=self._subalgLogging) transposedWSName = self._names.withSuffix('transposed') transposedWS = Transpose(InputWorkspace=integratedWS, OutputWorkspace=transposedWSName, EnableLogging=self._subalgLogging) self._cleanup.cleanup(integratedWS) # Convert spectrum numbers to WS indices. wsIndices = numpy.arange(0, ws.getNumberHistograms()) xs = transposedWS.dataX(0) ys = transposedWS.readY(0) numpy.copyto(xs, wsIndices) indexOfMax = ys.argmax() heightGuess = ys[indexOfMax] posGuess = xs[indexOfMax] sigmaGuess = 3 f = 'name=Gaussian, PeakCentre={}, Height={}, Sigma={}'.format( posGuess, heightGuess, sigmaGuess) fitResult = Fit(Function=f, InputWorkspace=transposedWS, EnableLogging=self._subalgLogging) self._cleanup.cleanup(transposedWS) peakPos = fitResult.Function.PeakCentre posTable = self._createFakePeakPositionTable(peakPos) return posTable
def refit_peaks(self, bad_params): xvals = self.getProperty('InputWorkspace').value.readX(0).copy() if not bad_params: return np.zeros(len(xvals)), None fit_func = '' fit_constr = '' for i, (centre, height, width) in enumerate(bad_params): fit_func += 'name=Gaussian,PeakCentre=%f,Height=%f,Sigma=%f;' % ( centre, height, width) fit_constr += '%f<f%d.PeakCentre<%f,%f<f%d.Height<%f,%f<f%d.Sigma<%d,' \ % (centre * (1 - self._refit_tolerance), i, centre * (1 + self._refit_tolerance), height * (1 - self._refit_tolerance), i, height * (1 + self._refit_tolerance), self._min_sigma, i, self._max_sigma) if fit_constr.count('PeakCentre') == 1: fit_constr = fit_constr.replace('f0.', '') _, _, _, param, fit_result, _, _ = Fit( Function=fit_func, InputWorkspace=self.getProperty('InputWorkspace').value, Output='fit_result', Minimizer='Levenberg-MarquardtMD', OutputCompositeMembers=True, StartX=min(xvals), EndX=max(xvals), Constraints=fit_constr, StoreInADS=False) return fit_result.readY(1).copy(), param
def test_LorentzianFit(self): ''' Fit a set or Lorentzians with "noisy" HWHM against a reference lorentzian ''' if not self.isthere_dsfinterp(): return import dsfinterp nf = 20 startf = 0.01 df = 0.01 fvalues, InputWorkspaces, xvalues, HWHM = self.generateWorkspaces( nf, startf, df, e=True) # Do the fit starting from different initial guesses for iif in range(0, nf, 6): guess = startf + iif * df func_string = 'name=DSFinterp1DFit,InputWorkspaces="{0}",ParameterValues="{1}",'.format(InputWorkspaces,fvalues) +\ 'LoadErrors=0,LocalRegression=1,RegressionType=quadratic,RegressionWindow=6,' +\ 'WorkspaceIndex=0,Intensity=1.0,TargetParameter={0};'.format(guess) Fit(Function=func_string, InputWorkspace='targetW', WorkspaceIndex=0, StartX=xvalues[0], EndX=xvalues[-1], CreateOutput=1, MaxIterations=20) ws = mtd['targetW_Parameters'] optimizedf = ws.row(1)['Value'] self.assertTrue( abs(1.0 - optimizedf / HWHM) < 0.05) #five percent error self.cleanup(nf)
def test_multi_domain_fit(self): x = np.linspace(-10, 10) y1 = np.exp(-(x-2)**2) y2 = 2*np.exp(-x**2) y3 = 3*np.exp(-(x+2)**2) ws1 = CreateWorkspace(x, y1) ws2 = CreateWorkspace(x, y2) ws3 = CreateWorkspace(x, y3) mdf = MultiDomainFunction(Gaussian(), Gaussian(), Gaussian(), Global=["Sigma"]) res = Fit(mdf, InputWorkspace=ws1, InputWorkspace_1=ws2, InputWorkspace_2=ws3) f = res.Function self.assertAlmostEqual(f[0].Sigma, 0.707107, 6) self.assertAlmostEqual(f[1].Sigma, 0.707107, 6) self.assertAlmostEqual(f[2].Sigma, 0.707107, 6) self.assertAlmostEqual(f[0].Height, 1, 6) self.assertAlmostEqual(f[1].Height, 2, 6) self.assertAlmostEqual(f[2].Height, 3, 6) self.assertAlmostEqual(f[0].PeakCentre, 2, 6) self.assertAlmostEqual(f[1].PeakCentre, 0, 6) self.assertAlmostEqual(f[2].PeakCentre, -2, 6)
def do_a_fit(x, function, guess, target, atol=0.01): r"""Carry out a fit and compare to target parameters Parameters ---------- x : sequence of floats Domain values for evaluating the function . function : str Registered function name. guess : dict Parameter names with their initial values. target : dict Parameter names with the values to be obtained after the fit. atol : float Absolute tolerance parameter when evaluating expected_output against output values. """ model = FunctionWrapper(function, **target) w = CreateWorkspace(x, model(x) * np.random.uniform(0.95, 1.05, len(x)), np.ones(len(x)), Nspec=1) fit = Fit(FunctionWrapper(function, **guess), w) otarget = OrderedDict(target) np.allclose([fit.Function[p] for p in otarget.keys()], list(otarget.values()), atol)
def test_other_arguments_are_accepted_by_keyword(self): if platform.system() == 'Darwin': # crashes return output_name = "otherargs_fitWS" retvals = Fit("name=FlatBackground", self._raw_ws, MaxIterations=2, Output=output_name) self._check_returns_are_correct_type_with_workspaces(retvals) self.assertTrue(output_name + '_Workspace' in mtd)
def test_Fit_works_with_multidomain_functions(self): x1 = np.arange(10) y1 = np.empty(0) y2 = np.empty(0) y3 = np.empty(0) for x in x1: y1 = np.append(y1, 3) y2 = np.append(y2, 2.9 + 3*x) y3 = np.append(y3, 3.1 + 3*x*x) x = np.concatenate((x1,x1,x1)) y = np.concatenate((y1,y2,y3)) data_name = 'dataWS' run_algorithm('CreateWorkspace',OutputWorkspace=data_name,DataX=x, DataY=y,DataE=np.ones(30),NSpec=3,UnitX='TOF') f1 = ';name=UserFunction,$domains=i,Formula=a+b*x+c*x^2' func= 'composite=MultiDomainFunction,NumDeriv=1' + f1 + f1 + f1 + ';ties=(f2.a=f1.a=f0.a)' output_name = "fitWS" Fit(Function=func,InputWorkspace=data_name,WorkspaceIndex=0,Output=output_name, InputWorkspace_1=data_name,WorkspaceIndex_1=1,InputWorkspace_2=data_name,WorkspaceIndex_2=2) self.assertTrue(output_name + '_Parameters' in mtd) params = mtd[output_name+'_Parameters'] self.assertEqual(params.rowCount(), 10) self.assertAlmostEqual(params.row(0)['Value'], 3.0, 10) self.assertAlmostEqual(params.row(3)['Value'], 3.0, 10) self.assertAlmostEqual(params.row(6)['Value'], 3.0, 10) self.assertAlmostEqual(params.row(4)['Value'], 3.0, 1) self.assertAlmostEqual(params.row(8)['Value'], 3.0, 1)
def do_fit_gaussian(self, index): """ Performs gaussian fit of the specified spectrum returns fitStatus, chiSq, covarianceTable, paramTable """ result = None x_values = np.array(self.workspace.readX(index)) y_values = np.array(self.workspace.readY(index)) # get peak centre position, assuming that it is the point with the highest value imax = np.argmax(y_values) height = y_values[imax] # check for zero or negative signal if height <= 0.: self.log().warning("Workspace %s, detector %d has maximum <= 0" % (self.workspace.getName(), index)) return result # guess sigma (assume the signal is sufficiently smooth) # the _only_ peak is at least three samples wide # selecting samples above .5 ("full width at half maximum") indices = np.argwhere(y_values > 0.5 * height) nentries = len(indices) if nentries < 3: self.log().warning( "Spectrum " + str(index) + " in workspace " + self.workspace.getName() + " has a too narrow peak. Cannot guess sigma. Check your data.") return result minIndex = indices[0, 0] maxIndex = indices[-1, 0] # full width at half maximum: fwhm = sigma * (2.*np.sqrt(2.*np.log(2.))) fwhm = np.fabs(x_values[maxIndex] - x_values[minIndex]) sigma = fwhm / (2. * np.sqrt(2. * np.log(2.))) # execute Fit algorithm tryCentre = x_values[imax] fitFun = "name=Gaussian,PeakCentre=%s,Height=%s,Sigma=%s" % ( tryCentre, height, sigma) startX = tryCentre - 3.0 * fwhm endX = tryCentre + 3.0 * fwhm # pylint: disable=assignment-from-none # result = fitStatus, chiSq, covarianceTable, paramTable result = Fit(InputWorkspace=self.workspace, WorkspaceIndex=index, StartX=startX, EndX=endX, Output='EPPfit', Function=fitFun, CreateOutput=True, OutputParametersOnly=True) return result
def PyExec(self): workspace = self.getProperty("Workspace").value index = self.getProperty("Index").value nhist = workspace.getNumberHistograms() # index must be in <0,nhist) if index >= nhist: self._error("Index " + str(index) + " is out of range for the workspace " + workspace.name()) x_values = np.array(workspace.readX(index)) y_values = np.array(workspace.readY(index)) # get peak centre position, assuming that it is the point with the highest value imax = np.argmax(y_values) height = y_values[imax] # check for zero or negative signal if height <= 0.: self._warning("Workspace %s, detector %d has maximum <= 0" % (workspace.name(), index)) return # guess sigma (assume the signal is sufficiently smooth) # the _only_ peak is at least three samples wide # selecting samples above .5 ("full width at half maximum") indices = np.argwhere(y_values > 0.5*height) nentries = len(indices) if nentries < 3: self._warning("Spectrum " + str(index) + " in workspace " + workspace.name() + " has a too narrow peak. Cannot guess sigma. Check your data.") return minIndex = indices[0,0] maxIndex = indices[-1,0] # full width at half maximum: fwhm = sigma * (2.*np.sqrt(2.*np.log(2.))) fwhm = np.fabs(x_values[maxIndex] - x_values[minIndex]) sigma = fwhm / (2.*np.sqrt(2.*np.log(2.))) # execute Fit algorithm tryCentre = x_values[imax] fitFun = "name=Gaussian,PeakCentre=%s,Height=%s,Sigma=%s" % (tryCentre,height,sigma) startX = tryCentre - 3.0*fwhm endX = tryCentre + 3.0*fwhm # pylint: disable = unpacking-non-sequence, assignment-from-none fit_output = Fit( InputWorkspace=workspace, WorkspaceIndex=index, Function=fitFun, CreateOutput=True, OutputParametersOnly=True, StartX=startX, EndX=endX) if not 'success' == fit_output.OutputStatus: self._warning("For detector " + str(index) + " in workspace " + workspace.name() + "fit was not successful. Input guess parameters were " + str(fitFun)) return fitParams = fit_output.OutputParameters.column(1) self._setOutput(fitParams[1],fitParams[2]) # [peakCentre,sigma]
def test_Fit_accepts_EnableLogging_keyword(self): if platform.system() == 'Darwin': # crashes return output_name = "otherargs_fitWS" retvals = Fit("name=FlatBackground", self._raw_ws, MaxIterations=2, Output=output_name, EnableLogging=False) self.assertTrue(output_name + '_Workspace' in mtd)
def test_use_in_fit(self): workspace = create_test_workspace( create_model("MsdGauss", Height=1.0, MSD=0.05), 1000) function_string = create_function_string("MsdGauss", Height=1.0, MSD=0.05) Fit(Function=function_string, InputWorkspace=workspace, StartX=1.2, EndX=1200)
def test_fit(self): if self.skipTest(): # python2.6 doesn't have skipping decorators return from random import random variation = lambda x: x * ( 1 + (random() - 0.5) / 5. ) # range [x*0.9, x*1.1] should be bigger but not until parameter constraints have been exposed to python #Generate a data workspace using random parameters around {'height':0.1,'tau':100,'beta':1} parms = { 'height': variation(0.1), 'tau': variation(100), 'beta': variation(1) } Nh = 2000 de = 0.0004 AlgorithmFactory.subscribe(_InternalMakeSEFTData) alg = testhelpers.run_algorithm('_InternalMakeSEFTData', nhalfbins=Nh, de=de, OutputWorkspace='_test_seft_data', **parms) sx = -Nh * de + de / 2 ex = (Nh - 1) * de + de / 2 func_string = 'name=StretchedExpFT,height=0.1,tau=100,beta=1' Fit(Function=func_string, InputWorkspace='_test_seft_data', StartX=sx, EndX=ex, CreateOutput=1, MaxIterations=20) ws = mtd['_test_seft_data_Parameters'] fitted = '' for irow in range(ws.rowCount()): row = ws.row(irow) name = row['Name'] if name == 'Cost function value': value = row['Value'] elif name in parms.keys(): fitted += '{0}={1}, '.format(name, row['Value']) target = ', '.join('{0}={1}'.format(key, val) for key, val in parms.items()) msg = 'Cost function {0} too high\nTargets were {1},\nbut obtained {2}'.format( value, target, fitted) self.assertTrue(value < 5.0, msg) msg = 'Cost function {0}\nStarted with height=0.1, tau=100, beta=1\nTargets were {1},\nobtained {2}'.format( value, target, fitted) mtd.remove('_test_seft_data') mtd.remove('_test_seft_data_NormalisedCovarianceMatrix') mtd.remove('_test_seft_data_Parameters') mtd.remove('_test_seft_data_Workspace')
def test_fit_succeeds_with_expected_answer(self): AlgorithmFactory.subscribe(_InternalMakeLinear) alg = testhelpers.run_algorithm("_InternalMakeLinear", A0=1.0,A1=0.75,OutputWorkspace='_test_linear') func_string="name=Example1DFunction,A0=0.0,A1=0.0" Fit(Function=func_string,InputWorkspace="_test_linear",StartX=1000,EndX=6000,CreateOutput=1,MaxIterations=2) mtd.remove('_test_linear') mtd.remove('_test_linear_NormalisedCovarianceMatrix') mtd.remove('_test_linear_Parameters') mtd.remove('_test_linear_Workspace')
def do_fit(tg, fString, shape): """ Given a target shape and initial fit function guess, carry out the fit :param tg: dictionary of target fitting parameters :param fString: initial guess of the fit function :param shape: Gaussian or Lorentzian, either integrated or not :return: success or failure of the fit """ if 'Gaussian' in shape: E0 = planck_constant / tg['tau'] # Analytical Fourier transform of exp(-(t/tau)**2) functor = lambda E: np.sqrt(np.pi) / E0 * np.exp(-(np.pi * E / E0)**2) elif 'Lorentzian' in shape: hwhm = planck_constant / (2 * np.pi * tg['tau']) # Analytical Fourier transform of exp(-t/tau) functor = lambda E: (1.0 / np.pi) * hwhm / (hwhm**2 + E**2) if 'Integrated' in shape: # when testing function PrimStretchedExpFT def ifunctor(E): """Numerical integral of the functor within each energy bin""" de = (E[-1] - E[0]) / (len(E) - 1.0) # energy spacing rf = 100 # make the energy domain a grid 100 times finer efine = np.arange(E[0] - de, E[-1] + 2 * de, de / rf) values = functor(efine) # evaluate on the finer grid primitive = np.cumsum( values) / rf # cummulative sum, giving the integral # bb are bin boundaries delimiting bins of width de and centered at the E values bb = (E[1:] + E[:-1]) / 2 # internal bin boundaries bb = np.insert(bb, 0, 2 * E[0] - bb[0]) # external lower bin boundary bb = np.append(bb, 2 * E[-1] - bb[-1]) # external upper bin boundary # return the integral over each energy bin return np.interp(bb[1:], efine, primitive) - np.interp( bb[:-1], efine, primitive) createData(ifunctor) else: # when testing function StretchedExpFT createData(functor) # Create workspace "data" Fit(Function=fString, InputWorkspace="data", MaxIterations=100, Output="fit") return assertFit(mtd["fit_Parameters"], tg)
def general_fit(self, xvals, yvals, peakids): if not peakids: return np.zeros(len(yvals)), None fit_func = '' fit_constr = '' for i, pid in enumerate(peakids): win_size = int(self._estimate_sigma * len(xvals) / (max(xvals) - min(xvals))) if win_size < 2: win_size = 2 centre, height, sigma = tuple( self.estimate_single_parameters(xvals, yvals, pid, win_size)) if sigma < self._min_sigma: centre = xvals[pid] height = yvals[pid] sigma = self._estimate_sigma fit_func += 'name=Gaussian,PeakCentre={},Height={},Sigma={};'.format( centre, height, sigma) fit_constr += '%f<f%d.PeakCentre<%f,%f<f%d.Height<%f,%f<f%d.Sigma<%d,' \ % (xvals[pid] * (1 - self._general_tolerance), i, xvals[pid] * (1 + self._general_tolerance), yvals[pid] * (1 - self._general_tolerance), i, yvals[pid] * (1 + self._general_tolerance), self._min_sigma, i, self._max_sigma) if fit_func == '': return yvals, self.function_difference(yvals, np.zeros(len(yvals)), np.sqrt(yvals)) if fit_constr.count('PeakCentre') == 1: fit_constr = fit_constr.replace('f0.', '') _, _, _, param, fit_result, _, _ = Fit( Function=fit_func, InputWorkspace=self.getProperty('InputWorkspace').value, Output='fit_result', Minimizer='Levenberg-MarquardtMD', OutputCompositeMembers=True, StartX=min(xvals), EndX=max(xvals), Constraints=fit_constr, StoreInADS=False) return fit_result.readY(1).copy(), param
def _fit_gaussian(self, inWS, ws, x, y, startX, endX, output_fit): """fits ws to Gaussian + Flat background""" function = f"name=FlatBackground, A0={np.nanmin(y)};" \ f"name=Gaussian, PeakCentre={x[np.nanargmax(y)]}, Height={np.nanmax(y) - np.nanmin(y)}, Sigma=0.25" constraints = f"f0.A0 > 0, f1.Height > 0, {x.min()} < f1.PeakCentre < {x.max()}" fit_result = "" try: fit_result = Fit(function, ws, Output=str(ws), IgnoreInvalidData=True, OutputParametersOnly=not output_fit, Constraints=constraints, StartX=startX, EndX=endX, EnableLogging=False) except RuntimeError as e: self.log().warning("Failed to fit workspace {}: {}".format( inWS, e)) return fit_result
def test_fit_succeeds_with_expected_answer(self): AlgorithmFactory.subscribe(_InternalMakeGaussian) alg = testhelpers.run_algorithm("_InternalMakeGaussian", Height=300, Centre=2100, Sigma=700, OutputWorkspace='_test_gauss') func_string = "name=ExamplePeakFunction,NTerms=3,Height=309.92,PeakCentre=2105,Sigma=710.2" Fit(Function=func_string, InputWorkspace="_test_gauss", StartX=150, EndX=4310, CreateOutput=1, MaxIterations=2) mtd.remove('_test_gauss') mtd.remove('_test_gauss_NormalisedCovarianceMatrix') mtd.remove('_test_gauss_Parameters') mtd.remove('_test_gauss_Workspace')
def testLorentzian(self): """ Test the Fourier transform of a exponential is a Lorentzian""" if self.skipTest: # python2.6 doesn't have skipping decorators return # Target parameters tau = 100.0 # picoseconds beta = 1.0 # exponential height = 1.0 # We want identity, not just proporcionality # Analytical Fourier transform of exp(-t/tau) hwhm = StretchedExpFTTest._planck_constant / (2 * np.pi * tau) functor = lambda E: (1.0 / np.pi) * hwhm / (hwhm**2 + E**2) self.createData(functor) # Create workspace "data" # Initial guess reasonably far from target parameters fString = "name=StretchedExpFT,Height=3.0,Tau=50,Beta=1.5,Centre=0.0002;" +\ "name=FlatBackground,A0=0.0" Fit(Function=fString, InputWorkspace="data", MaxIterations=100, Output="fit") self.asserFit(mtd["fit_Parameters"], height, beta, tau) self.cleanFit()
def do_a_fit(x, function, guess, target, fixes=None, atol=0.01): r"""Carry out a fit and compare to target parameters Parameters ---------- x : sequence of floats Domain values for evaluating the function . function : str Registered function name. guess : dict Parameter names with their initial values. target : dict Parameter names with the values to be obtained after the fit. fixes : list List of fitting parameters to fix during the fit atol : float Absolute tolerance parameter when evaluating expected_output against output values. Returns ------- list [0] Evaluation of the comparison between evaluated and expected values. [1] output of the call to Fit algorithm """ target_model = FunctionWrapper(function, **target) y = target_model(x) e = np.ones(len(x)) w = CreateWorkspace(x, y, e, Nspec=1) model = FunctionWrapper(function, **guess) if fixes is not None: [model.fix(p) for p in fixes] fit = Fit(model, w, NIterations=2000) otarget = OrderedDict(target) return np.allclose([fit.Function[p] for p in otarget.keys()], list(otarget.values()), atol), fit
def sel_const(runs, dist=4.0, thickness=5e-3, show_fits=False, show_quality=False): """Calculate the spin echo length of the instrument Parameters ---------- runs: list of Workspaces A list of the workspecaes containing the polarisation versus wavelength for consecutive detector tubes dist: float The distances from the sample to the detector in meters. The default is 4.0 thickness: float The distance between detector tubes in meters. Defaults to 5mm. show_fits: bool If true, plots the sinusoid fits used to calculate the frequency show_quality: bool If true, plots the frequency versus tube position to confirm that the frequency grows linearly with position. Returns ------- A float containing the spin echo length, in nanometers, of a one angstrom neutron. """ freqs = [] for run in runs: x = run.extractX()[0] x = (x[1:] + x[:-1]) / 2 p = run.extractY()[0] p[np.isnan(p)] = 0 fp = np.fft.fft(p) conv = len(x) / (np.max(x) - np.min(x)) max_arg = (np.nanargmax(np.abs(fp[1:int(len(p) / 2)])) + 1) amp = np.abs(fp[max_arg]) / len(x) max_arg = max_arg * conv / len(x) model = "name=UserFunction,Formula=e*cos(x*f),f={},e={}" Fit(Function=model.format(max_arg * 2 * np.pi, amp), InputWorkspace=run, StartX=3, EndX=7, CreateOutput=True) result = mtd[run.getName() + "_Parameters"] freqs.append(np.abs(result.column(1)[1] / 2 / np.pi)) if not show_fits: DeleteWorkspace(run.getName() + "_Parameters") DeleteWorkspace(run.getName() + "_NormalisedCovarianceMatrix") DeleteWorkspace(run.getName() + "_Workspace") def model(x, m, b): """A simple linear model""" return m * x + b xs = np.arange(len(runs)) * thickness fit, _ = curve_fit(model, xs, freqs) sel_fits = CreateWorkspace(xs, freqs) Fit(Function="name=LinearBackground", InputWorkspace=sel_fits, CreateOutput=True) result = 0.1 * mtd["sel_fits_Parameters"].column(1)[1] * dist if not show_quality: DeleteWorkspace("sel_fits_Parameters") DeleteWorkspace("sel_fits_NormalisedCovarianceMatrix") DeleteWorkspace("sel_fits_Workspace") DeleteWorkspace("sel_fits") return result
def _create_fit_workspace(): fn_input = FIT_DICT['properties'] Fit(**fn_input)
def test_fit(self): if self.skipTest: # python2.6 doesn't have skipping decorators return from random import random # the variation range below is [x*0.9, x*1.1]. Should be bigger, # but not until parameter constraints have been exposed to python variation = lambda x: x * (1 + (random() - 0.5) / 5.) # Generate data workspace using random parameters around height=0.1,tau=100, beta=1, # and centered at the origin parms = { 'height': variation(0.1), 'tau': variation(100), 'beta': variation(1) } Nh = 2000 de = 0.0004 AlgorithmFactory.subscribe(_InternalMakeSEFTData) alg = testhelpers.run_algorithm('_InternalMakeSEFTData', nhalfbins=Nh, de=de, OutputWorkspace='_test_seft_data', **parms) sx = -Nh * de + de / 2 ex = (Nh - 1) * de + de / 2 # Our initial guess is not centered at the origin, but around de initial_parameters = 'height=0.1, tau=100, beta=1, Origin=%f' % variation( de) func_string = 'name=StretchedExpFT,%s' % initial_parameters Fit(Function=func_string, InputWorkspace='_test_seft_data', StartX=sx, EndX=ex, CreateOutput=1, MaxIterations=20) parms[ 'Origin'] = 0.0 # insert (Origin,0.0) to parameter dictionary for assessment of fit results ws = mtd['_test_seft_data_Parameters'] #tr() fitted = '' unacceptable_chi_square = 3.0 chi_square = unacceptable_chi_square for irow in range(ws.rowCount()): row = ws.row(irow) name = row['Name'] if name == 'Cost function value': chi_square = row['Value'] elif name in parms.keys(): fitted += '{0}={1}, '.format(name, row['Value']) target = ', '.join('{0}={1}'.format(key, val) for key, val in parms.items()) msg = 'Cost function {0} too high\nTargets were {1},\nbut obtained {2}'.format( chi_square, target, fitted) self.assertTrue(chi_square < unacceptable_chi_square, msg) msg = 'Cost function ={0}\n'.format(chi_square) msg += 'Target parameters were {0}\n'.format(target) msg += 'Started with parameters {0}\n'.format(initial_parameters) msg += 'obtained fitted parameters {0}'.format(fitted) print msg mtd.remove('_test_seft_data') mtd.remove('_test_seft_data_NormalisedCovarianceMatrix') mtd.remove('_test_seft_data_Parameters') mtd.remove('_test_seft_data_Workspace')
def PyExec(self): # ---------------------------------------------------------- # Imports resonance parameters from ResParam dictionary depending on the selected foil type. # ---------------------------------------------------------- files = self.getProperty("Files").value files = self.validateFileInputs(files) foilType = self.getProperty("FoilType").value divE = float(self.getProperty("Ediv").value) isCalib = self.getProperty("Calibration").value mass = self.ResParamsDict[foilType + '_Mass'] TD = self.ResParamsDict[foilType + '_TD'] # Energy parameters are in eV energy = self.ResParamsDict[foilType + '_En'] TwogG = self.ResParamsDict[foilType+'_TwogG'] Gg = self.ResParamsDict[foilType+'_Gg'] startE = self.ResParamsDict[foilType+'_startE'] endE = self.ResParamsDict[foilType+'_endE'] refTemp = float(self.getProperty("ReferenceTemp").value) isDebug = self.getProperty("Debug").value if not isCalib: if 'S_fit_Parameters' not in mtd: self.log().warning("No calibration files found. Please run this algorithm will 'Calibration' ticked to " "generate the calibration workspace.") return self.monitorTransfit(files, foilType, divE) file = files[0] discard, fileName = path.split(file) fnNoExt = path.splitext(fileName)[0] # Define the gaussian width at the reference temperature # Factor of 1e3 converting to meV for Mantid width_300 = 1000.0 * np.sqrt(4 * energy * self.k * refTemp * mass / (self.e * (1 + mass) ** 2)) # ---------------------------------------------------------- # Perform fits based on whether isCalib is flagged (calibration or measurement) # ---------------------------------------------------------- if isCalib: lorentzFWHM = 1000.0 * (0.5 * TwogG + Gg) # Take peak position starting guess from tabulated value peakPosGuess = energy * 1000 fileName_monitor = fnNoExt + '_monitor' # For guessing initial background values, read in y-data and use starting value as value for b0 wsBgGuess = mtd[fileName_monitor] bg0guess = 0.86 * wsBgGuess.readY(0)[0] # New Voigt function as from Igor pro function Fit(Function='name=PEARLTransVoigt,Position=' + str(peakPosGuess) + ',LorentzianFWHM=' + str( lorentzFWHM) + ',GaussianFWHM=' + str(width_300) + ',Amplitude=1.6,Bg0=' + str( bg0guess) + ',Bg1=0.0063252,Bg2=0,constraints=(1<LorentzianFWHM,' + str(width_300) + '<GaussianFWHM)', InputWorkspace=fileName_monitor, MaxIterations=200, Output='S_fit') # DeleteWorkspace('S_fit_NormalisedCovarianceMatrix') DeleteWorkspace(fileName_monitor) else: S_fit = mtd['S_fit_Parameters'] lorentzFWHM = S_fit.column(1)[1] gaussianFWHM = S_fit.column(1)[2] # Calculates the gaussian resolution contribution, sometimes constraints are broken and the value can drop # below that from width_300 alone, in which case the instrument contribution is set to zero. if gaussianFWHM > width_300: gaussianFWHM_inst = np.sqrt(gaussianFWHM**2 - width_300**2) else: gaussianFWHM_inst = 0 # # New Voigt function as from Igor pro function Fit(Function='name=PEARLTransVoigt,Position=' + str(S_fit.column(1)[0]) + ',LorentzianFWHM=' + str(lorentzFWHM) + ',GaussianFWHM=' + str(gaussianFWHM) + ',Amplitude=' + str(S_fit.column(1)[3]) + ',Bg0=' + str(S_fit.column(1)[4]) + ',Bg1=' + str(S_fit.column(1)[5]) + ',Bg2=' + str(S_fit.column(1)[6]) + ',constraints=(' + str(gaussianFWHM) + '<GaussianFWHM),ties=(LorentzianFWHM=' + str(lorentzFWHM) + ')', InputWorkspace=fnNoExt + '_monitor', MaxIterations=200, Output='T_fit') # DeleteWorkspace('T_fit_NormalisedCovarianceMatrix') DeleteWorkspace(fnNoExt + '_monitor') T_fit = mtd['T_fit_Parameters'] gaussian_FWHM_fitted = T_fit.column(1)[2] width_T = np.sqrt(gaussian_FWHM_fitted ** 2 - gaussianFWHM_inst ** 2) # Factor of 1e-3 converts back from meV to eV Teff = (((width_T * 1e-3) ** 2) * self.e * ((1 + mass) ** 2)) / (4 * 1e-3 * T_fit.column(1)[0] * self.k * mass) Teff_low = ((((width_T - T_fit.column(2)[2]) * 1e-3) ** 2) * self.e * ((1 + mass) ** 2))/(4 * 1e-3 * (T_fit.column(1)[0] + T_fit.column(2)[0]) * self.k * mass) Teff_high = ((((width_T + T_fit.column(2)[2]) * 1e-3) ** 2) * self.e * ((1 + mass) ** 2)) /\ (4 * 1e-3 * (T_fit.column(1)[0] - T_fit.column(2)[0]) * self.k * mass) errTeff = 0.5*(Teff_high-Teff_low) # ---------------------------------------------------------- # If the temperature is too far below the Debye temperature, then the result is inaccurate. Else the # temperature is calculated assuming free gas formulation # ---------------------------------------------------------- if 8 * Teff < 3 * TD: self.log().information("The effective temperature is currently too far below the Debye temperature to" "give an accurate measure.") Tactual = Teff Terror = errTeff else: Tactual = 3 * TD / (4 * np.log((8 * Teff + 3 * TD)/(8 * Teff - 3 * TD))) Tactual_high = 3 * TD / (4 * np.log((8 * (Teff + errTeff) + 3 * TD) / (8 * (Teff + errTeff) - 3 * TD))) Tactual_low = 3 * TD / (4 * np.log((8 * (Teff - errTeff) + 3 * TD) / (8 * (Teff - errTeff) - 3 * TD))) Terror = 0.5 * (np.abs(Tactual - Tactual_high) + np.abs(Tactual - Tactual_low)) # Introduce a catchment for unphysically small determined errors, setting to defualt value if below a set # threshold Terror_flag = 0 if Terror < 5.0: Terror_flag = 1 Terror = 10.0 if isDebug: self.log().information("-----------------------------") self.log().information("Debugging....") self.log().information("The Debye temperature is " + str(TD) + " K") self.log().information("The effective temperature is: {:.1f}" .format(Teff) + "+/- {:.1f}".format(errTeff) + " K") self.log().information("Energy bin width set to " + str(1000 * divE) + " meV") self.log().information("E range is between " + str(startE) + " and " + str(endE)) self.log().information("Gaussian width at this reference temperature is: {:.2f}".format(width_300) + " meV") self.log().information("Lorentzian FWHM is fixed: {:.2f}".format(lorentzFWHM)+" meV") self.log().information("Gaussian FWHM is fitted as: {:.2f}".format(gaussian_FWHM_fitted) + " meV") self.log().information("Instrumental contribution is: {:.2f}".format(gaussianFWHM_inst) + " meV") self.log().information("Temperature contribution is: {:.2f}".format(width_T) + " meV") self.log().information("-----------------------------") self.log().information("Sample temperature is: {:.1f}".format(Tactual) + " +/- {:.1f}".format(Terror) + " K") if Terror_flag == 1: self.log().information("(the default error, as determined error unphysically small)")