source: trunk/python/asapfitter.py@ 2537

Last change on this file since 2537 was 2535, checked in by Kana Sugimoto, 12 years ago

New Development: No

JIRA Issue: Yes (CAS-3749/TRAC-266)

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

interactive tests are required

  1. on CASA

(1) plot something by CASA sdplot task
(2) run CASA sdfit with plotlevel!=0
(3) close plotter window by pressing X button on the window
(4) plot something by sdplot task again.

  1. on stand-alone ASAP

(1) plot something by asapplotter
(2) run asapfitter.auto_fit with plot=T
(3) plot something by asapplotter again

Put in Release Notes: No

Module(s): sdplot, asapplotter

Description:

Fixed a bug which caused sdplot and asapplotter crash after closing
plotter loaded by the other modules (tasks) with X button. It was
a bug caused by the fact ID numbers of figure managers are
identical in both sdplot (asapplotter) and the other plotter.
It is also possible, that user loads plotter with the ID identical
to asapplotter and overrides it. This is because, user can select
arbitrary ID when generating a new plot.

asap.plotter._assert_plotter does more tests to make sure the
previous plotter GUI is alive, and properly reloads plotter if not.

Also some minor fixes to reset linewidth for plot invoked by
modules other than asapplot/sdplot, i.e., scantable, asapfitter,
and asapmath.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.9 KB
Line 
1import _asap
2from asap.parameters import rcParams
3from asap.logging import asaplog, asaplog_post_dec
4from asap.utils import _n_bools, mask_and
5
6
7class fitter:
8 """
9 The fitting class for ASAP.
10 """
11 def __init__(self):
12 """
13 Create a fitter object. No state is set.
14 """
15 self.fitter = _asap.fitter()
16 self.x = None
17 self.y = None
18 self.mask = None
19 self.fitfunc = None
20 self.fitfuncs = None
21 self.fitted = False
22 self.data = None
23 self.components = 0
24 self._fittedrow = 0
25 self._p = None
26 self._selection = None
27 self.uselinear = False
28
29 def set_data(self, xdat, ydat, mask=None):
30 """
31 Set the absissa and ordinate for the fit. Also set the mask
32 indicating valid points.
33 This can be used for data vectors retrieved from a scantable.
34 For scantable fitting use 'fitter.set_scan(scan, mask)'.
35 Parameters:
36 xdat: the abcissa values
37 ydat: the ordinate values
38 mask: an optional mask
39
40 """
41 self.fitted = False
42 self.x = xdat
43 self.y = ydat
44 if mask == None:
45 self.mask = _n_bools(len(xdat), True)
46 else:
47 self.mask = mask
48 return
49
50 @asaplog_post_dec
51 def set_scan(self, thescan=None, mask=None):
52 """
53 Set the 'data' (a scantable) of the fitter.
54 Parameters:
55 thescan: a scantable
56 mask: a msk retrieved from the scantable
57 """
58 if not thescan:
59 msg = "Please give a correct scan"
60 raise TypeError(msg)
61 self.fitted = False
62 self.data = thescan
63 self.mask = None
64 if mask is None:
65 self.mask = _n_bools(self.data.nchan(), True)
66 else:
67 self.mask = mask
68 return
69
70 @asaplog_post_dec
71 def set_function(self, **kwargs):
72 """
73 Set the function to be fit.
74 Parameters:
75 poly: use a polynomial of the order given with nonlinear least squares fit
76 lpoly: use polynomial of the order given with linear least squares fit
77 gauss: fit the number of gaussian specified
78 lorentz: fit the number of lorentzian specified
79 sinusoid: fit the number of sinusoid specified
80 Example:
81 fitter.set_function(poly=3) # will fit a 3rd order polynomial via nonlinear method
82 fitter.set_function(lpoly=3) # will fit a 3rd order polynomial via linear method
83 fitter.set_function(gauss=2) # will fit two gaussians
84 fitter.set_function(lorentz=2) # will fit two lorentzians
85 fitter.set_function(sinusoid=3) # will fit three sinusoids
86 """
87 #default poly order 0
88 n=0
89 if kwargs.has_key('poly'):
90 self.fitfunc = 'poly'
91 self.fitfuncs = ['poly']
92 n = kwargs.get('poly')
93 self.components = [n+1]
94 self.uselinear = False
95 elif kwargs.has_key('lpoly'):
96 self.fitfunc = 'poly'
97 self.fitfuncs = ['lpoly']
98 n = kwargs.get('lpoly')
99 self.components = [n+1]
100 self.uselinear = True
101 elif kwargs.has_key('gauss'):
102 n = kwargs.get('gauss')
103 self.fitfunc = 'gauss'
104 self.fitfuncs = [ 'gauss' for i in range(n) ]
105 self.components = [ 3 for i in range(n) ]
106 self.uselinear = False
107 elif kwargs.has_key('lorentz'):
108 n = kwargs.get('lorentz')
109 self.fitfunc = 'lorentz'
110 self.fitfuncs = [ 'lorentz' for i in range(n) ]
111 self.components = [ 3 for i in range(n) ]
112 self.uselinear = False
113 elif kwargs.has_key('sinusoid'):
114 n = kwargs.get('sinusoid')
115 self.fitfunc = 'sinusoid'
116 self.fitfuncs = [ 'sinusoid' for i in range(n) ]
117 self.components = [ 3 for i in range(n) ]
118 self.uselinear = False
119 else:
120 msg = "Invalid function type."
121 raise TypeError(msg)
122
123 self.fitter.setexpression(self.fitfunc,n)
124 self.fitted = False
125 return
126
127 @asaplog_post_dec
128 def fit(self, row=0, estimate=False):
129 """
130 Execute the actual fitting process. All the state has to be set.
131 Parameters:
132 row: specify the row in the scantable
133 estimate: auto-compute an initial parameter set (default False)
134 This can be used to compute estimates even if fit was
135 called before.
136 Example:
137 s = scantable('myscan.asap')
138 s.set_cursor(thepol=1) # select second pol
139 f = fitter()
140 f.set_scan(s)
141 f.set_function(poly=0)
142 f.fit(row=0) # fit first row
143 """
144 if ((self.x is None or self.y is None) and self.data is None) \
145 or self.fitfunc is None:
146 msg = "Fitter not yet initialised. Please set data & fit function"
147 raise RuntimeError(msg)
148
149 else:
150 if self.data is not None:
151 self.x = self.data._getabcissa(row)
152 self.y = self.data._getspectrum(row)
153 #self.mask = mask_and(self.mask, self.data._getmask(row))
154 if len(self.x) == len(self.mask):
155 self.mask = mask_and(self.mask, self.data._getmask(row))
156 else:
157 asaplog.push('lengths of data and mask are not the same. preset mask will be ignored')
158 asaplog.post('WARN','asapfit.fit')
159 self.mask=self.data._getmask(row)
160 asaplog.push("Fitting:")
161 i = row
162 out = "Scan[%d] Beam[%d] IF[%d] Pol[%d] Cycle[%d]" % (self.data.getscan(i),
163 self.data.getbeam(i),
164 self.data.getif(i),
165 self.data.getpol(i),
166 self.data.getcycle(i))
167 asaplog.push(out,False)
168 self.fitter.setdata(self.x, self.y, self.mask)
169 if self.fitfunc == 'gauss' or self.fitfunc == 'lorentz':
170 ps = self.fitter.getparameters()
171 if len(ps) == 0 or estimate:
172 self.fitter.estimate()
173 fxdpar = list(self.fitter.getfixedparameters())
174 if len(fxdpar) and fxdpar.count(0) == 0:
175 raise RuntimeError,"No point fitting, if all parameters are fixed."
176 if self.uselinear:
177 converged = self.fitter.lfit()
178 else:
179 converged = self.fitter.fit()
180 if not converged:
181 raise RuntimeError,"Fit didn't converge."
182 self._fittedrow = row
183 self.fitted = True
184 return
185
186 def store_fit(self, filename=None):
187 """
188 Save the fit parameters.
189 Parameters:
190 filename: if specified save as an ASCII file, if None (default)
191 store it in the scnatable
192 """
193 if self.fitted and self.data is not None:
194 pars = list(self.fitter.getparameters())
195 fixed = list(self.fitter.getfixedparameters())
196 from asap.asapfit import asapfit
197 fit = asapfit()
198 fit.setparameters(pars)
199 fit.setfixedparameters(fixed)
200 fit.setfunctions(self.fitfuncs)
201 fit.setcomponents(self.components)
202 fit.setframeinfo(self.data._getcoordinfo())
203 if filename is not None:
204 import os
205 filename = os.path.expandvars(os.path.expanduser(filename))
206 if os.path.exists(filename):
207 raise IOError("File '%s' exists." % filename)
208 fit.save(filename)
209 else:
210 self.data._addfit(fit,self._fittedrow)
211
212 @asaplog_post_dec
213 def set_parameters(self,*args,**kwargs):
214 """
215 Set the parameters to be fitted.
216 Parameters:
217 params: a vector of parameters
218 fixed: a vector of which parameters are to be held fixed
219 (default is none)
220 component: in case of multiple gaussians/lorentzians/sinusoidals,
221 the index of the target component
222 """
223 component = None
224 fixed = None
225 params = None
226
227 if len(args) and isinstance(args[0],dict):
228 kwargs = args[0]
229 if kwargs.has_key("fixed"): fixed = kwargs["fixed"]
230 if kwargs.has_key("params"): params = kwargs["params"]
231 if len(args) == 2 and isinstance(args[1], int):
232 component = args[1]
233 if self.fitfunc is None:
234 msg = "Please specify a fitting function first."
235 raise RuntimeError(msg)
236 if (self.fitfunc == "gauss" or self.fitfunc == "lorentz" or self.fitfunc == "sinusoid") and component is not None:
237 if not self.fitted and sum(self.fitter.getparameters()) == 0:
238 pars = _n_bools(len(self.components)*3, False)
239 fxd = _n_bools(len(pars), False)
240 else:
241 pars = list(self.fitter.getparameters())
242 fxd = list(self.fitter.getfixedparameters())
243 i = 3*component
244 pars[i:i+3] = params
245 fxd[i:i+3] = fixed
246 params = pars
247 fixed = fxd
248 self.fitter.setparameters(params)
249 if fixed is not None:
250 self.fitter.setfixedparameters(fixed)
251 return
252
253 @asaplog_post_dec
254 def set_gauss_parameters(self, peak, centre, fwhm,
255 peakfixed=0, centrefixed=0,
256 fwhmfixed=0,
257 component=0):
258 """
259 Set the Parameters of a 'Gaussian' component, set with set_function.
260 Parameters:
261 peak, centre, fwhm: The gaussian parameters
262 peakfixed,
263 centrefixed,
264 fwhmfixed: Optional parameters to indicate if
265 the paramters should be held fixed during
266 the fitting process. The default is to keep
267 all parameters flexible.
268 component: The number of the component (Default is the
269 component 0)
270 """
271 if self.fitfunc != "gauss":
272 msg = "Function only operates on Gaussian components."
273 raise ValueError(msg)
274 if 0 <= component < len(self.components):
275 d = {'params':[peak, centre, fwhm],
276 'fixed':[peakfixed, centrefixed, fwhmfixed]}
277 self.set_parameters(d, component)
278 else:
279 msg = "Please select a valid component."
280 raise ValueError(msg)
281
282 @asaplog_post_dec
283 def set_lorentz_parameters(self, peak, centre, fwhm,
284 peakfixed=0, centrefixed=0,
285 fwhmfixed=0,
286 component=0):
287 """
288 Set the Parameters of a 'Lorentzian' component, set with set_function.
289 Parameters:
290 peak, centre, fwhm: The lorentzian parameters
291 peakfixed,
292 centrefixed,
293 fwhmfixed: Optional parameters to indicate if
294 the paramters should be held fixed during
295 the fitting process. The default is to keep
296 all parameters flexible.
297 component: The number of the component (Default is the
298 component 0)
299 """
300 if self.fitfunc != "lorentz":
301 msg = "Function only operates on Lorentzian components."
302 raise ValueError(msg)
303 if 0 <= component < len(self.components):
304 d = {'params':[peak, centre, fwhm],
305 'fixed':[peakfixed, centrefixed, fwhmfixed]}
306 self.set_parameters(d, component)
307 else:
308 msg = "Please select a valid component."
309 raise ValueError(msg)
310
311 @asaplog_post_dec
312 def set_sinusoid_parameters(self, ampl, period, x0,
313 amplfixed=0, periodfixed=0,
314 x0fixed=0,
315 component=0):
316 """
317 Set the Parameters of a 'Sinusoidal' component, set with set_function.
318 Parameters:
319 ampl, period, x0: The sinusoidal parameters
320 amplfixed,
321 periodfixed,
322 x0fixed: Optional parameters to indicate if
323 the paramters should be held fixed during
324 the fitting process. The default is to keep
325 all parameters flexible.
326 component: The number of the component (Default is the
327 component 0)
328 """
329 if self.fitfunc != "sinusoid":
330 msg = "Function only operates on Sinusoidal components."
331 raise ValueError(msg)
332 if 0 <= component < len(self.components):
333 d = {'params':[ampl, period, x0],
334 'fixed': [amplfixed, periodfixed, x0fixed]}
335 self.set_parameters(d, component)
336 else:
337 msg = "Please select a valid component."
338 raise ValueError(msg)
339
340 def get_area(self, component=None):
341 """
342 Return the area under the fitted gaussian/lorentzian component.
343 Parameters:
344 component: the gaussian/lorentzian component selection,
345 default (None) is the sum of all components
346 Note:
347 This will only work for gaussian/lorentzian fits.
348 """
349 if not self.fitted: return
350 if self.fitfunc == "gauss" or self.fitfunc == "lorentz":
351 pars = list(self.fitter.getparameters())
352 from math import log,pi,sqrt
353 if self.fitfunc == "gauss":
354 fac = sqrt(pi/log(16.0))
355 elif self.fitfunc == "lorentz":
356 fac = pi/2.0
357 areas = []
358 for i in range(len(self.components)):
359 j = i*3
360 cpars = pars[j:j+3]
361 areas.append(fac * cpars[0] * cpars[2])
362 else:
363 return None
364 if component is not None:
365 return areas[component]
366 else:
367 return sum(areas)
368
369 @asaplog_post_dec
370 def get_errors(self, component=None):
371 """
372 Return the errors in the parameters.
373 Parameters:
374 component: get the errors for the specified component
375 only, default is all components
376 """
377 if not self.fitted:
378 msg = "Not yet fitted."
379 raise RuntimeError(msg)
380 errs = list(self.fitter.geterrors())
381 cerrs = errs
382 if component is not None:
383 if self.fitfunc == "gauss" or self.fitfunc == "lorentz" or self.fitfunc == "sinusoid":
384 i = 3*component
385 if i < len(errs):
386 cerrs = errs[i:i+3]
387 return cerrs
388
389
390 @asaplog_post_dec
391 def get_parameters(self, component=None, errors=False):
392 """
393 Return the fit paramters.
394 Parameters:
395 component: get the parameters for the specified component
396 only, default is all components
397 """
398 if not self.fitted:
399 msg = "Not yet fitted."
400 raise RuntimeError(msg)
401 pars = list(self.fitter.getparameters())
402 fixed = list(self.fitter.getfixedparameters())
403 errs = list(self.fitter.geterrors())
404 area = []
405 if component is not None:
406 if self.fitfunc == "poly" or self.fitfunc == "lpoly":
407 cpars = pars
408 cfixed = fixed
409 cerrs = errs
410 else:
411 i = 3*component
412 cpars = pars[i:i+3]
413 cfixed = fixed[i:i+3]
414 cerrs = errs[i:i+3]
415 if self.fitfunc == "gauss" or self.fitfunc == "lorentz":
416 a = self.get_area(component)
417 area = [a for i in range(3)]
418 else:
419 cpars = pars
420 cfixed = fixed
421 cerrs = errs
422 if self.fitfunc == "gauss" or self.fitfunc == "lorentz":
423 for c in range(len(self.components)):
424 a = self.get_area(c)
425 area += [a for i in range(3)]
426 fpars = self._format_pars(cpars, cfixed, errors and cerrs, area)
427 asaplog.push(fpars)
428 return {'params':cpars, 'fixed':cfixed, 'formatted': fpars,
429 'errors':cerrs}
430
431 def _format_pars(self, pars, fixed, errors, area):
432 out = ''
433 if self.fitfunc == "poly" or self.fitfunc == "lpoly":
434 c = 0
435 for i in range(len(pars)):
436 fix = ""
437 if len(fixed) and fixed[i]: fix = "(fixed)"
438 out += " p%d%s= %3.6f" % (c, fix, pars[i])
439 if errors : out += " (%1.6f)" % errors[i]
440 out += ","
441 c+=1
442 out = out[:-1] # remove trailing ','
443 else:
444 i = 0
445 c = 0
446 if self.fitfunc == "gauss" or self.fitfunc == "lorentz":
447 pnam = ["peak", "centre", "FWHM"]
448 elif self.fitfunc == "sinusoid":
449 pnam = ["amplitude", "period", "x0"]
450 aunit = ""
451 ounit = ""
452 if self.data:
453 aunit = self.data.get_unit()
454 ounit = self.data.get_fluxunit()
455 while i < len(pars):
456 fix0 = fix1 = fix2 = ""
457 if i < len(fixed)-2:
458 if fixed[i]: fix0 = "(fixed)"
459 if fixed[i+1]: fix1 = "(fixed)"
460 if fixed[i+2]: fix2 = "(fixed)"
461 out += " %2d: " % c
462 out += "%s%s = %3.3f %s, " % (pnam[0], fix0, pars[i], ounit)
463 out += "%s%s = %3.3f %s, " % (pnam[1], fix1, pars[i+1], aunit)
464 out += "%s%s = %3.3f %s\n" % (pnam[2], fix2, pars[i+2], aunit)
465 if len(area): out += " area = %3.3f %s %s\n" % (area[i], ounit, aunit)
466 c+=1
467 i+=3
468 return out
469
470
471 @asaplog_post_dec
472 def get_estimate(self):
473 """
474 Return the parameter estimates (for non-linear functions).
475 """
476 pars = self.fitter.getestimate()
477 fixed = self.fitter.getfixedparameters()
478 asaplog.push(self._format_pars(pars,fixed,None,None))
479 return pars
480
481 @asaplog_post_dec
482 def get_residual(self):
483 """
484 Return the residual of the fit.
485 """
486 if not self.fitted:
487 msg = "Not yet fitted."
488 raise RuntimeError(msg)
489 return self.fitter.getresidual()
490
491 @asaplog_post_dec
492 def get_chi2(self):
493 """
494 Return chi^2.
495 """
496 if not self.fitted:
497 msg = "Not yet fitted."
498 raise RuntimeError(msg)
499 ch2 = self.fitter.getchi2()
500 asaplog.push( 'Chi^2 = %3.3f' % (ch2) )
501 return ch2
502
503 @asaplog_post_dec
504 def get_fit(self):
505 """
506 Return the fitted ordinate values.
507 """
508 if not self.fitted:
509 msg = "Not yet fitted."
510 raise RuntimeError(msg)
511 return self.fitter.getfit()
512
513 @asaplog_post_dec
514 def commit(self):
515 """
516 Return a new scan where the fits have been commited (subtracted)
517 """
518 if not self.fitted:
519 msg = "Not yet fitted."
520 raise RuntimeError(msg)
521 from asap import scantable
522 if not isinstance(self.data, scantable):
523 msg = "Not a scantable"
524 raise TypeError(msg)
525 scan = self.data.copy()
526 scan._setspectrum(self.fitter.getresidual())
527 return scan
528
529 @asaplog_post_dec
530 def plot(self, residual=False, components=None, plotparms=False,
531 filename=None):
532 """
533 Plot the last fit.
534 Parameters:
535 residual: an optional parameter indicating if the residual
536 should be plotted (default 'False')
537 components: a list of components to plot, e.g [0,1],
538 -1 plots the total fit. Default is to only
539 plot the total fit.
540 plotparms: Inidicates if the parameter values should be present
541 on the plot
542 """
543 if not self.fitted:
544 return
545 if not self._p or self._p.is_dead:
546 from asap.asapplotter import new_asaplot
547 del self._p
548 self._p = new_asaplot(rcParams['plotter.gui'])
549 self._p.hold()
550 self._p.clear()
551 rcp('lines', linewidth=1)
552 self._p.set_panels()
553 self._p.palette(0)
554 tlab = 'Spectrum'
555 xlab = 'Abcissa'
556 ylab = 'Ordinate'
557 from numpy import ma,logical_not,logical_and,array
558 m = self.mask
559 if self.data:
560 tlab = self.data._getsourcename(self._fittedrow)
561 xlab = self.data._getabcissalabel(self._fittedrow)
562 if self.data._getflagrow(self._fittedrow):
563 m = [False]
564 else:
565 m = logical_and(self.mask,
566 array(self.data._getmask(self._fittedrow),
567 copy=False))
568
569 ylab = self.data._get_ordinate_label()
570
571 colours = ["#777777","#dddddd","red","orange","purple","green","magenta", "cyan"]
572 nomask=True
573 for i in range(len(m)):
574 nomask = nomask and m[i]
575 if len(m) == 1:
576 m = m[0]
577 invm = (not m)
578 else:
579 invm = logical_not(m)
580 label0='Masked Region'
581 label1='Spectrum'
582 if ( nomask ):
583 label0=label1
584 else:
585 y = ma.masked_array( self.y, mask = m )
586 self._p.palette(1,colours)
587 self._p.set_line( label = label1 )
588 self._p.plot( self.x, y )
589 self._p.palette(0,colours)
590 self._p.set_line(label=label0)
591 y = ma.masked_array(self.y,mask=invm)
592 self._p.plot(self.x, y)
593 if residual:
594 self._p.palette(7)
595 self._p.set_line(label='Residual')
596 y = ma.masked_array(self.get_residual(),
597 mask=invm)
598 self._p.plot(self.x, y)
599 self._p.palette(2)
600 if components is not None:
601 cs = components
602 if isinstance(components,int): cs = [components]
603 if plotparms:
604 self._p.text(0.15,0.15,str(self.get_parameters()['formatted']),size=8)
605 n = len(self.components)
606 self._p.palette(3)
607 for c in cs:
608 if 0 <= c < n:
609 lab = self.fitfuncs[c]+str(c)
610 self._p.set_line(label=lab)
611 y = ma.masked_array(self.fitter.evaluate(c), mask=invm)
612
613 self._p.plot(self.x, y)
614 elif c == -1:
615 self._p.palette(2)
616 self._p.set_line(label="Total Fit")
617 y = ma.masked_array(self.fitter.getfit(),
618 mask=invm)
619 self._p.plot(self.x, y)
620 else:
621 self._p.palette(2)
622 self._p.set_line(label='Fit')
623 y = ma.masked_array(self.fitter.getfit(),mask=invm)
624 self._p.plot(self.x, y)
625 xlim=[min(self.x),max(self.x)]
626 self._p.axes.set_xlim(xlim)
627 self._p.set_axes('xlabel',xlab)
628 self._p.set_axes('ylabel',ylab)
629 self._p.set_axes('title',tlab)
630 self._p.release()
631 if (not rcParams['plotter.gui']):
632 self._p.save(filename)
633
634 @asaplog_post_dec
635 def auto_fit(self, insitu=None, plot=False):
636 """
637 Return a scan where the function is applied to all rows for
638 all Beams/IFs/Pols.
639
640 """
641 from asap import scantable
642 if not isinstance(self.data, scantable) :
643 msg = "Data is not a scantable"
644 raise TypeError(msg)
645 if insitu is None: insitu = rcParams['insitu']
646 if not insitu:
647 scan = self.data.copy()
648 else:
649 scan = self.data
650 rows = xrange(scan.nrow())
651 # Save parameters of baseline fits as a class attribute.
652 # NOTICE: This does not reflect changes in scantable!
653 if len(rows) > 0: self.blpars=[]
654 asaplog.push("Fitting:")
655 for r in rows:
656 out = " Scan[%d] Beam[%d] IF[%d] Pol[%d] Cycle[%d]" % (scan.getscan(r),
657 scan.getbeam(r),
658 scan.getif(r),
659 scan.getpol(r),
660 scan.getcycle(r))
661 asaplog.push(out, False)
662 self.x = scan._getabcissa(r)
663 self.y = scan._getspectrum(r)
664 #self.mask = mask_and(self.mask, scan._getmask(r))
665 if len(self.x) == len(self.mask):
666 self.mask = mask_and(self.mask, self.data._getmask(row))
667 else:
668 asaplog.push('lengths of data and mask are not the same. preset mask will be ignored')
669 asaplog.post('WARN','asapfit.fit')
670 self.mask=self.data._getmask(row)
671 self.data = None
672 self.fit()
673 x = self.get_parameters()
674 fpar = self.get_parameters()
675 if plot:
676 self.plot(residual=True)
677 x = raw_input("Accept fit ([y]/n): ")
678 if x.upper() == 'N':
679 self.blpars.append(None)
680 continue
681 scan._setspectrum(self.fitter.getresidual(), r)
682 self.blpars.append(fpar)
683 if plot:
684 self._p.quit()
685 del self._p
686 self._p = None
687 return scan
Note: See TracBrowser for help on using the repository browser.