Changes in / [5b1392f:72c969b] in sasview
- Files:
-
- 8 added
- 5 deleted
- 24 edited
Legend:
- Unmodified
- Added
- Removed
-
park-1.2.1/park/expression.py
r3570545 re3efa6b3 160 160 """%("\n ".join(exprs),"\n ".join(code)) 161 161 162 #print "Function:",function 162 #print "Function:",functiondef 163 163 exec functiondef in globals,locals 164 164 retfn = locals['eval_expressions'] … … 185 185 # Check symbol rename 186 186 assert substitute(expr,{'a.b.x':'Q'}) == 'Q + sin(4*pi*a.c) + Q/a.b' 187 assert substitute(expr,{'a.b':'Q'}) == 'a.b.x + sin(4*pi*a.c) + a.b.x/Q' 187 188 188 189 … … 249 250 for expr in ['G4.cage', 'M0.cage', 'M1.G1 + *2', 250 251 'piddle', 251 ' import sys; print "p0wned"',252 '5; import sys; print "p0wned"', 252 253 '__import__("sys").argv']: 253 254 try: -
park-1.2.1/park/fitresult.py
r3570545 r95d58d3 67 67 improvements. 68 68 """ 69 #import traceback; traceback.print_stack() 69 70 self.progress_time = time.time() 70 71 self.progress_percent = 0 … … 228 229 229 230 def __str__(self): 231 #import traceback; traceback.print_stack() 230 232 if self.parameters == None: return "No results" 231 233 L = ["P%-3d %s"%(n+1,p.summarize()) for n,p in enumerate(self.parameters)] -
park-1.2.1/park/parameter.py
r3570545 rfb7180c 15 15 __all__ = ['Parameter', 'ParameterSet'] 16 16 17 import math 17 18 import numpy 18 19 import expression … … 162 163 """ 163 164 range = ['.']*10 164 lo,hi = p.range165 portion = ( p.value-lo)/(hi-lo)165 lo,hi = self.range 166 portion = (self.value-lo)/(hi-lo) 166 167 if portion < 0: portion = 0. 167 168 elif portion >= 1: portion = 0.99999999 … … 169 170 range[bar] = '|' 170 171 range = "".join(range) 171 return "%25s %s %g in [%g,%g]" % ( p.name,range,p.value,lo,hi)172 return "%25s %s %g in [%g,%g]" % (self.name,range,self.value,lo,hi) 172 173 173 174 def isfitted(self): return self.status == 'fitted' … … 273 274 for p in self: 274 275 if parts[1] == p.name: 275 if len(par s) == 2:276 if len(parts) == 2: 276 277 return p 277 278 elif isinstance(p, ParameterSet): 278 279 return p._byname(parts[1:]) 280 else: 281 raise 279 282 return None 280 283 … … 283 286 parts = name.split('.') 284 287 if parts[0] == self.name: 285 p = _byname(self,name.split('.'))288 p = self._byname(name.split('.')) 286 289 if p: return p 287 290 raise KeyError("parameter %s not in parameter set"%name) -
run.py
rbbd97e5 rf8940db 8 8 Usage: 9 9 10 ./run.py [args] 10 ./run.py [(module|script) args...] 11 12 Without arguments run.py runs sasview. With arguments, run.py will run 13 the given module or script. 11 14 """ 12 15 … … 14 17 import sys 15 18 import imp 16 from glob import glob17 19 from contextlib import contextmanager 18 20 from os.path import abspath, dirname, join as joinpath … … 49 51 return mod 50 52 51 def import_dll(modname ):53 def import_dll(modname, build_path): 52 54 """Import a DLL from the build directory""" 55 import sysconfig 56 ext = sysconfig.get_config_var('SO') 53 57 # build_path comes from context 54 path = glob(joinpath(build_path, *modname.split('.'))+'.*')[0]58 path = joinpath(build_path, *modname.split('.'))+ext 55 59 #print "importing", modname, "from", path 56 60 return imp.load_dynamic(modname, path) … … 70 74 71 75 # Make sure that we have a private version of mplconfig 72 mplconfig = joinpath(abspath(dirname(__file__)), '.mplconfig')73 os.environ['MPLCONFIGDIR'] = mplconfig74 if not os.path.exists(mplconfig): os.mkdir(mplconfig)76 #mplconfig = joinpath(abspath(dirname(__file__)), '.mplconfig') 77 #os.environ['MPLCONFIGDIR'] = mplconfig 78 #if not os.path.exists(mplconfig): os.mkdir(mplconfig) 75 79 #import matplotlib 76 80 #matplotlib.use('Agg') … … 80 84 try: import periodictable 81 85 except: addpath(joinpath(root, '..','periodictable')) 86 87 try: import bumps 88 except: addpath(joinpath(root, '..','bumps')) 82 89 83 90 # select wx version … … 107 114 sans.pr.core = import_package('sans.pr.core', 108 115 joinpath(build_path, 'sans', 'pr', 'core')) 109 #import_dll('park._modeling' )116 #import_dll('park._modeling', build_path) 110 117 111 118 #park = import_package('park',os.path.join(build_path,'park')) … … 122 129 123 130 if __name__ == "__main__": 124 # start sasview125 #import multiprocessing126 #multiprocessing.freeze_support()127 131 prepare() 128 from sans.sansview.sansview import SasView129 SasView()132 from sans.sansview.sansview import run 133 run() -
sansview/sansview.py
re2271c5 r1790664 36 36 PLUGIN_MODEL_DIR = 'plugin_models' 37 37 APP_NAME = 'SasView' 38 def run(): 39 sys.path.append(os.path.join("..","..","..")) 40 from multiprocessing import freeze_support 41 freeze_support() 42 sasview = SasView() 43 38 44 39 class SasViewApp(gui_manager.ViewApp): 45 40 """ … … 63 58 # modules. We load the fitting module here 64 59 # to ensure a complete Windows executable build. 65 60 66 61 # Fitting perspective 67 62 try: … … 72 67 logging.error("Fitting problems: " + str(inst)) 73 68 logging.error("%s: could not find Fitting plug-in module"% APP_NAME) 74 logging.error(sys.exc_value) 75 69 logging.error(sys.exc_value) 70 76 71 # P(r) perspective 77 72 try: … … 113 108 self.gui.clean_plugin_models(PLUGIN_MODEL_DIR) 114 109 # Start the main loop 115 self.gui.MainLoop() 116 110 self.gui.MainLoop() 117 111 118 112 119 if __name__ == "__main__": 113 def run(): 120 114 from multiprocessing import freeze_support 121 115 freeze_support() 122 #Process(target=SasView).start() 123 sasview = SasView() 116 if len(sys.argv) > 1: 117 ## Run sasview as an interactive python interpreter 118 #if sys.argv[1] == "-i": 119 # sys.argv = ["ipython", "--pylab"] 120 # from IPython import start_ipython 121 # sys.exit(start_ipython()) 122 thing_to_run = sys.argv[1] 123 sys.argv = sys.argv[1:] 124 import runpy 125 if os.path.exists(thing_to_run): 126 runpy.run_path(thing_to_run, run_name="__main__") 127 else: 128 runpy.run_module(thing_to_run, run_name="__main__") 129 else: 130 SasView() 124 131 125 132 if __name__ == "__main__": 133 run() 134 -
sansview/setup_exe.py
r27b7acc r6fe5100 319 319 'reportlab.platypus', 320 320 ]) 321 packages.append('IPython') 321 322 includes = ['site'] 322 323 -
setup.py
r968aa6e r880f170 69 69 70 70 71 enable_openmp = True 71 enable_openmp = True 72 72 73 73 if sys.platform =='darwin': -
src/sans/fit/AbstractFitEngine.py
r6c00702 rbf5e985 3 3 #import logging 4 4 import sys 5 import math 5 6 import numpy 6 import math 7 import park 7 8 8 from sans.dataloader.data_info import Data1D 9 9 from sans.dataloader.data_info import Data2D 10 _SMALLVALUE = 1.0e-10 11 12 class SansParameter(park.Parameter): 13 """ 14 SANS model parameters for use in the PARK fitting service. 15 The parameter attribute value is redirected to the underlying 16 parameter value in the SANS model. 17 """ 18 def __init__(self, name, model, data): 19 """ 20 :param name: the name of the model parameter 21 :param model: the sans model to wrap as a park model 22 """ 23 park.Parameter.__init__(self, name) 24 self._model, self._name = model, name 25 self.data = data 26 self.model = model 27 #set the value for the parameter of the given name 28 self.set(model.getParam(name)) 29 30 def _getvalue(self): 31 """ 32 override the _getvalue of park parameter 33 34 :return value the parameter associates with self.name 35 36 """ 37 return self._model.getParam(self.name) 38 39 def _setvalue(self, value): 40 """ 41 override the _setvalue pf park parameter 42 43 :param value: the value to set on a given parameter 44 45 """ 46 self._model.setParam(self.name, value) 47 48 value = property(_getvalue, _setvalue) 49 50 def _getrange(self): 51 """ 52 Override _getrange of park parameter 53 return the range of parameter 54 """ 55 #if not self.name in self._model.getDispParamList(): 56 lo, hi = self._model.details[self.name][1:3] 57 if lo is None: lo = -numpy.inf 58 if hi is None: hi = numpy.inf 59 if lo > hi: 60 raise ValueError, "wrong fit range for parameters" 61 62 return lo, hi 63 64 def get_name(self): 65 """ 66 """ 67 return self._getname() 68 69 def _setrange(self, r): 70 """ 71 override _setrange of park parameter 72 73 :param r: the value of the range to set 74 75 """ 76 self._model.details[self.name][1:3] = r 77 range = property(_getrange, _setrange) 78 79 80 class Model(park.Model): 81 """ 82 PARK wrapper for SANS models. 10 _SMALLVALUE = 1.0e-10 11 12 # Note: duplicated from park 13 class FitHandler(object): 14 """ 15 Abstract interface for fit thread handler. 16 17 The methods in this class are called by the optimizer as the fit 18 progresses. 19 20 Note that it is up to the optimizer to call the fit handler correctly, 21 reporting all status changes and maintaining the 'done' flag. 22 """ 23 done = False 24 """True when the fit job is complete""" 25 result = None 26 """The current best result of the fit""" 27 28 def improvement(self): 29 """ 30 Called when a result is observed which is better than previous 31 results from the fit. 32 33 result is a FitResult object, with parameters, #calls and fitness. 34 """ 35 def error(self, msg): 36 """ 37 Model had an error; print traceback 38 """ 39 def progress(self, current, expected): 40 """ 41 Called each cycle of the fit, reporting the current and the 42 expected amount of work. The meaning of these values is 43 optimizer dependent, but they can be converted into a percent 44 complete using (100*current)//expected. 45 46 Progress is updated each iteration of the fit, whatever that 47 means for the particular optimization algorithm. It is called 48 after any calls to improvement for the iteration so that the 49 update handler can control I/O bandwidth by suppressing 50 intermediate improvements until the fit is complete. 51 """ 52 def finalize(self): 53 """ 54 Fit is complete; best results are reported 55 """ 56 def abort(self): 57 """ 58 Fit was aborted. 59 """ 60 61 # TODO: not sure how these are used, but they are needed for running the fit 62 def update_fit(self, last=False): pass 63 def set_result(self, result=None): self.result = result 64 65 class Model: 66 """ 67 Fit wrapper for SANS models. 83 68 """ 84 69 def __init__(self, sans_model, sans_data=None, **kw): 85 70 """ 86 71 :param sans_model: the sans model to wrap using park interface 87 88 """ 89 park.Model.__init__(self, **kw) 72 73 """ 90 74 self.model = sans_model 91 75 self.name = sans_model.name 92 76 self.data = sans_data 93 #list of parameters names 94 self.sansp = sans_model.getParamList() 95 #list of park parameter 96 self.parkp = [SansParameter(p, sans_model, sans_data) for p in self.sansp] 97 #list of parameter set 98 self.parameterset = park.ParameterSet(sans_model.name, pars=self.parkp) 99 self.pars = [] 100 77 101 78 def get_params(self, fitparams): 102 79 """ 103 80 return a list of value of paramter to fit 104 81 105 82 :param fitparams: list of paramaters name to fit 106 107 """ 108 list_params = [] 109 self.pars = [] 110 self.pars = fitparams 111 for item in fitparams: 112 for element in self.parkp: 113 if element.name == str(item): 114 list_params.append(element.value) 115 return list_params 116 83 84 """ 85 return [self.model.getParam(k) for k in fitparams] 86 117 87 def set_params(self, paramlist, params): 118 88 """ 119 89 Set value for parameters to fit 120 90 121 91 :param params: list of value for parameters to fit 122 123 """ 124 try: 125 for i in range(len(self.parkp)): 126 for j in range(len(paramlist)): 127 if self.parkp[i].name == paramlist[j]: 128 self.parkp[i].value = params[j] 129 self.model.setParam(self.parkp[i].name, params[j]) 130 except: 131 raise 132 92 93 """ 94 for k,v in zip(paramlist, params): 95 self.model.setParam(k,v) 96 97 def set(self, **kw): 98 self.set_params(*zip(*kw.items())) 99 133 100 def eval(self, x): 134 101 """ 135 102 Override eval method of park model. 136 103 137 104 :param x: the x value used to compute a function 138 105 """ … … 141 108 except: 142 109 raise 143 110 144 111 def eval_derivs(self, x, pars=[]): 145 112 """ … … 154 121 instead of calling eval. 155 122 """ 156 return [] 157 158 123 raise NotImplementedError('no derivatives available') 124 125 def __call__(self, x): 126 return self.eval(x) 127 159 128 class FitData1D(Data1D): 160 129 """ … … 185 154 """ 186 155 Data1D.__init__(self, x=x, y=y, dx=dx, dy=dy) 156 self.num_points = len(x) 187 157 self.sans_data = data 188 158 self.smearer = smearer … … 251 221 """ 252 222 return self.qmin, self.qmax 253 223 224 def size(self): 225 """ 226 Number of measurement points in data set after masking, etc. 227 """ 228 return len(self.x) 229 254 230 def residuals(self, fn): 255 231 """ … … 293 269 def __init__(self, sans_data2d, data=None, err_data=None): 294 270 Data2D.__init__(self, data=data, err_data=err_data) 295 """ 296 Data can be initital with a data (sans plottable) 297 or with vectors. 298 """ 271 # Data can be initialized with a sans plottable or with vectors. 299 272 self.res_err_image = [] 273 self.num_points = 0 # will be set by set_data 300 274 self.idx = [] 301 275 self.qmin = None … … 339 313 self.idx = (self.idx) & (self.mask) 340 314 self.idx = (self.idx) & (numpy.isfinite(self.data)) 315 self.num_points = numpy.sum(self.idx) 341 316 342 317 def set_smearer(self, smearer): … … 372 347 """ 373 348 return self.qmin, self.qmax 374 349 350 def size(self): 351 """ 352 Number of measurement points in data set after masking, etc. 353 """ 354 return numpy.sum(self.idx) 355 375 356 def residuals(self, fn): 376 357 """ … … 409 390 410 391 411 class SansAssembly: 412 """ 413 Sans Assembly class a class wrapper to be call in optimizer.leastsq method 414 """ 415 def __init__(self, paramlist, model=None, data=None, fitresult=None, 416 handler=None, curr_thread=None, msg_q=None): 417 """ 418 :param Model: the model wrapper fro sans -model 419 :param Data: the data wrapper for sans data 420 421 """ 422 self.model = model 423 self.data = data 424 self.paramlist = paramlist 425 self.msg_q = msg_q 426 self.curr_thread = curr_thread 427 self.handler = handler 428 self.fitresult = fitresult 429 self.res = [] 430 self.true_res = [] 431 self.func_name = "Functor" 432 self.theory = None 433 434 def chisq(self): 435 """ 436 Calculates chi^2 437 438 :param params: list of parameter values 439 440 :return: chi^2 441 442 """ 443 total = 0 444 for item in self.true_res: 445 total += item * item 446 if len(self.true_res) == 0: 447 return None 448 return total / len(self.true_res) 449 450 def __call__(self, params): 451 """ 452 Compute residuals 453 :param params: value of parameters to fit 454 """ 455 #import thread 456 self.model.set_params(self.paramlist, params) 457 #print "params", params 458 self.true_res, theory = self.data.residuals(self.model.eval) 459 self.theory = copy.deepcopy(theory) 460 # check parameters range 461 if self.check_param_range(): 462 # if the param value is outside of the bound 463 # just silent return res = inf 464 return self.res 465 self.res = self.true_res 466 467 if self.fitresult is not None: 468 self.fitresult.set_model(model=self.model) 469 self.fitresult.residuals = self.true_res 470 self.fitresult.iterations += 1 471 self.fitresult.theory = theory 472 473 #fitness = self.chisq(params=params) 474 fitness = self.chisq() 475 self.fitresult.pvec = params 476 self.fitresult.set_fitness(fitness=fitness) 477 if self.msg_q is not None: 478 self.msg_q.put(self.fitresult) 479 480 if self.handler is not None: 481 self.handler.set_result(result=self.fitresult) 482 self.handler.update_fit() 483 484 if self.curr_thread != None: 485 try: 486 self.curr_thread.isquit() 487 except: 488 #msg = "Fitting: Terminated... Note: Forcing to stop " 489 #msg += "fitting may cause a 'Functor error message' " 490 #msg += "being recorded in the log file....." 491 #self.handler.stop(msg) 492 raise 493 494 return self.res 495 496 def check_param_range(self): 497 """ 498 Check the lower and upper bound of the parameter value 499 and set res to the inf if the value is outside of the 500 range 501 :limitation: the initial values must be within range. 502 """ 503 504 #time.sleep(0.01) 505 is_outofbound = False 506 # loop through the fit parameters 507 for p in self.model.parameterset: 508 param_name = p.get_name() 509 if param_name in self.paramlist: 510 511 # if the range was defined, check the range 512 if numpy.isfinite(p.range[0]): 513 if p.value == 0: 514 # This value works on Scipy 515 # Do not change numbers below 516 value = _SMALLVALUE 517 else: 518 value = p.value 519 # For leastsq, it needs a bit step back from the boundary 520 val = p.range[0] - value * _SMALLVALUE 521 if p.value < val: 522 self.res *= 1e+6 523 524 is_outofbound = True 525 break 526 if numpy.isfinite(p.range[1]): 527 # This value works on Scipy 528 # Do not change numbers below 529 if p.value == 0: 530 value = _SMALLVALUE 531 else: 532 value = p.value 533 # For leastsq, it needs a bit step back from the boundary 534 val = p.range[1] + value * _SMALLVALUE 535 if p.value > val: 536 self.res *= 1e+6 537 is_outofbound = True 538 break 539 540 return is_outofbound 541 542 392 543 393 class FitEngine: 544 394 def __init__(self): … … 546 396 Base class for scipy and park fit engine 547 397 """ 548 #List of parameter names to fit549 self.param_list = []550 398 #Dictionnary of fitArrange element (fit problems) 551 399 self.fit_arrange_dict = {} … … 571 419 572 420 """ 573 if model == None: 574 raise ValueError, "AbstractFitEngine: Need to set model to fit" 575 576 new_model = model 421 if not pars: 422 raise ValueError("no fitting parameters") 423 424 if model is None: 425 raise ValueError("no model to fit") 426 577 427 if not issubclass(model.__class__, Model): 578 new_model = Model(model, data) 579 580 if len(constraints) > 0: 581 for constraint in constraints: 582 name, value = constraint 583 try: 584 new_model.parameterset[str(name)].set(str(value)) 585 except: 586 msg = "Fit Engine: Error occurs when setting the constraint" 587 msg += " %s for parameter %s " % (value, name) 588 raise ValueError, msg 589 590 if len(pars) > 0: 591 temp = [] 592 for item in pars: 593 if item in new_model.model.getParamList(): 594 temp.append(item) 595 self.param_list.append(item) 596 else: 597 598 msg = "wrong parameter %s used " % str(item) 599 msg += "to set model %s. Choose " % str(new_model.model.name) 600 msg += "parameter name within %s" % \ 601 str(new_model.model.getParamList()) 602 raise ValueError, msg 603 604 #A fitArrange is already created but contains data_list only at id 605 if self.fit_arrange_dict.has_key(id): 606 self.fit_arrange_dict[id].set_model(new_model) 607 self.fit_arrange_dict[id].pars = pars 608 else: 609 #no fitArrange object has been create with this id 610 fitproblem = FitArrange() 611 fitproblem.set_model(new_model) 612 fitproblem.pars = pars 613 self.fit_arrange_dict[id] = fitproblem 614 vals = [] 615 for name in pars: 616 vals.append(new_model.model.getParam(name)) 617 self.fit_arrange_dict[id].vals = vals 618 else: 619 raise ValueError, "park_integration:missing parameters" 620 428 model = Model(model, data) 429 430 sasmodel = model.model 431 available_parameters = sasmodel.getParamList() 432 for p in pars: 433 if p not in available_parameters: 434 raise ValueError("parameter %s not available in model %s; use one of [%s] instead" 435 %(p, sasmodel.name, ", ".join(available_parameters))) 436 437 if id not in self.fit_arrange_dict: 438 self.fit_arrange_dict[id] = FitArrange() 439 440 self.fit_arrange_dict[id].set_model(model) 441 self.fit_arrange_dict[id].pars = pars 442 self.fit_arrange_dict[id].vals = [sasmodel.getParam(name) for name in pars] 443 self.fit_arrange_dict[id].constraints = constraints 444 621 445 def set_data(self, data, id, smearer=None, qmin=None, qmax=None): 622 446 """ … … 700 524 self.vals = [] 701 525 self.selected = 0 702 526 703 527 def set_model(self, model): 704 528 """ … … 752 576 """ 753 577 return self.selected 754 755 756 IS_MAC = True757 if sys.platform.count("win32") > 0:758 IS_MAC = False759 760 578 761 579 class FResult(object): … … 765 583 def __init__(self, model=None, param_list=None, data=None): 766 584 self.calls = None 767 self.pars = []768 585 self.fitness = None 769 586 self.chisqr = None … … 776 593 self.residuals = [] 777 594 self.index = [] 778 self.parameters = None779 self.is_mac = IS_MAC780 595 self.model = model 781 596 self.data = data … … 803 618 if self.pvec == None and self.model is None and self.param_list is None: 804 619 return "No results" 805 n = len(self.model.parameterset) 806 807 result_param = zip(xrange(n), self.model.parameterset) 808 msg1 = ["[Iteration #: %s ]" % self.iterations] 809 msg3 = ["=== goodness of fit: %s ===" % (str(self.fitness))] 810 if not self.is_mac: 811 msg2 = ["P%-3d %s......|.....%s" % \ 812 (p[0], p[1], p[1].value)\ 813 for p in result_param if p[1].name in self.param_list] 814 msg = msg1 + msg3 + msg2 815 else: 816 msg = msg1 + msg3 817 msg = "\n".join(msg) 818 return msg 620 621 sasmodel = self.model.model 622 pars = enumerate(sasmodel.getParamList()) 623 msg1 = "[Iteration #: %s ]" % self.iterations 624 msg3 = "=== goodness of fit: %s ===" % (str(self.fitness)) 625 msg2 = ["P%-3d %s......|.....%s" % (i, v, sasmodel.getParam(v)) 626 for i,v in pars if v in self.param_list] 627 msg = [msg1, msg3] + msg2 628 return "\n".join(msg) 819 629 820 630 def print_summary(self): 821 631 """ 822 632 """ 823 print s elf633 print str(self) -
src/sans/fit/Fitting.py
r5777106 re3efa6b3 8 8 from sans.fit.ScipyFitting import ScipyFit 9 9 from sans.fit.ParkFitting import ParkFit 10 from sans.fit.BumpsFitting import BumpsFit 10 11 12 ENGINES={ 13 'scipy': ScipyFit, 14 'park': ParkFit, 15 'bumps': BumpsFit, 16 } 11 17 12 18 class Fit(object): … … 26 32 27 33 """ 28 def __init__(self, engine='scipy' ):34 def __init__(self, engine='scipy', *args, **kw): 29 35 """ 30 36 """ … … 32 38 self._engine = None 33 39 self.fitter_id = None 34 self.set_engine(engine )40 self.set_engine(engine, *args, **kw) 35 41 36 42 def __setattr__(self, name, value): … … 49 55 self.__dict__[name] = value 50 56 51 def set_engine(self, word ):57 def set_engine(self, word, *args, **kw): 52 58 """ 53 59 Select the type of Fit … … 59 65 60 66 """ 61 if word == "scipy": 62 self._engine = ScipyFit() 63 elif word == "park": 64 self._engine = ParkFit() 65 else: 66 raise ValueError, "enter the keyword scipy or park" 67 try: 68 self._engine = ENGINES[word](*args, **kw) 69 except KeyError, exc: 70 raise KeyError("fit engine should be one of scipy, park or bumps") 67 71 68 72 def fit(self, msg_q=None, q=None, handler=None, -
src/sans/fit/Loader.py
r5777106 r6fe5100 8 8 This class is loading values from given file or value giving by the user 9 9 """ 10 11 10 def __init__(self, x=None, y=None, dx=None, dy=None): 11 raise NotImplementedError("a code search shows that this code is not active, and you are not seeing this message") 12 12 # variable to store loaded values 13 13 self.x = x -
src/sans/fit/ParkFitting.py
r9d6d5ba r8d074d9 24 24 from sans.fit.AbstractFitEngine import FitEngine 25 25 from sans.fit.AbstractFitEngine import FResult 26 26 27 class SansParameter(park.Parameter): 28 """ 29 SANS model parameters for use in the PARK fitting service. 30 The parameter attribute value is redirected to the underlying 31 parameter value in the SANS model. 32 """ 33 def __init__(self, name, model, data): 34 """ 35 :param name: the name of the model parameter 36 :param model: the sans model to wrap as a park model 37 """ 38 park.Parameter.__init__(self, name) 39 #self._model, self._name = model, name 40 self.data = data 41 self.model = model 42 #set the value for the parameter of the given name 43 self.set(model.getParam(name)) 44 45 # TODO: model is missing parameter ranges for dispersion parameters 46 if name not in model.details: 47 #print "setting details for",name 48 model.details[name] = ["", None, None] 49 50 def _getvalue(self): 51 """ 52 override the _getvalue of park parameter 53 54 :return value the parameter associates with self.name 55 56 """ 57 return self.model.getParam(self.name) 58 59 def _setvalue(self, value): 60 """ 61 override the _setvalue pf park parameter 62 63 :param value: the value to set on a given parameter 64 65 """ 66 self.model.setParam(self.name, value) 67 68 value = property(_getvalue, _setvalue) 69 70 def _getrange(self): 71 """ 72 Override _getrange of park parameter 73 return the range of parameter 74 """ 75 #if not self.name in self._model.getDispParamList(): 76 lo, hi = self.model.details[self.name][1:3] 77 if lo is None: lo = -numpy.inf 78 if hi is None: hi = numpy.inf 79 if lo > hi: 80 raise ValueError, "wrong fit range for parameters" 81 82 return lo, hi 83 84 def get_name(self): 85 """ 86 """ 87 return self._getname() 88 89 def _setrange(self, r): 90 """ 91 override _setrange of park parameter 92 93 :param r: the value of the range to set 94 95 """ 96 self.model.details[self.name][1:3] = r 97 range = property(_getrange, _setrange) 98 99 100 class ParkModel(park.Model): 101 """ 102 PARK wrapper for SANS models. 103 """ 104 def __init__(self, sans_model, sans_data=None, **kw): 105 """ 106 :param sans_model: the sans model to wrap using park interface 107 108 """ 109 park.Model.__init__(self, **kw) 110 self.model = sans_model 111 self.name = sans_model.name 112 self.data = sans_data 113 #list of parameters names 114 self.sansp = sans_model.getParamList() 115 #list of park parameter 116 self.parkp = [SansParameter(p, sans_model, sans_data) for p in self.sansp] 117 #list of parameter set 118 self.parameterset = park.ParameterSet(sans_model.name, pars=self.parkp) 119 self.pars = [] 120 121 def get_params(self, fitparams): 122 """ 123 return a list of value of paramter to fit 124 125 :param fitparams: list of paramaters name to fit 126 127 """ 128 list_params = [] 129 self.pars = fitparams 130 for item in fitparams: 131 for element in self.parkp: 132 if element.name == str(item): 133 list_params.append(element.value) 134 return list_params 135 136 def set_params(self, paramlist, params): 137 """ 138 Set value for parameters to fit 139 140 :param params: list of value for parameters to fit 141 142 """ 143 try: 144 for i in range(len(self.parkp)): 145 for j in range(len(paramlist)): 146 if self.parkp[i].name == paramlist[j]: 147 self.parkp[i].value = params[j] 148 self.model.setParam(self.parkp[i].name, params[j]) 149 except: 150 raise 151 152 def eval(self, x): 153 """ 154 Override eval method of park model. 155 156 :param x: the x value used to compute a function 157 """ 158 try: 159 return self.model.evalDistribution(x) 160 except: 161 raise 162 163 def eval_derivs(self, x, pars=[]): 164 """ 165 Evaluate the model and derivatives wrt pars at x. 166 167 pars is a list of the names of the parameters for which derivatives 168 are desired. 169 170 This method needs to be specialized in the model to evaluate the 171 model function. Alternatively, the model can implement is own 172 version of residuals which calculates the residuals directly 173 instead of calling eval. 174 """ 175 return [] 176 177 27 178 class SansFitResult(fitresult.FitResult): 28 179 def __init__(self, *args, **kwrds): … … 244 395 return fitpars 245 396 246 def all_results(self, result):397 def extend_results_with_calculated_parameters(self, result): 247 398 """ 248 399 Extend result from the fit with the calculated parameters. … … 292 443 # dividing residuals by N in order to be consistent with Scipy 293 444 m.chisq = numpy.sum(m.residuals**2/N) 294 resid.append(m.weight*m.residuals /math.sqrt(N))445 resid.append(m.weight*m.residuals) 295 446 self.residuals = numpy.hstack(resid) 296 447 N = len(self.residuals) 297 448 self.degrees_of_freedom = N-k if N>k else 1 298 449 self.chisq = numpy.sum(self.residuals**2) 299 return self.chisq 450 return self.chisq/self.degrees_of_freedom 300 451 301 452 class ParkFit(FitEngine): … … 354 505 if fproblem.get_to_fit() == 1: 355 506 fitproblems.append(fproblem) 356 if len(fitproblems) == 0: 507 if len(fitproblems) == 0: 357 508 raise RuntimeError, "No Assembly scheduled for Park fitting." 358 return359 509 for item in fitproblems: 360 parkmodel = item.get_model() 510 model = item.get_model() 511 parkmodel = ParkModel(model.model, model.data) 512 parkmodel.pars = item.pars 361 513 if reset_flag: 362 514 # reset the initial value; useful for batch … … 364 516 ind = item.pars.index(name) 365 517 parkmodel.model.setParam(name, item.vals[ind]) 518 519 # set the constraints into the model 520 for p,v in item.constraints: 521 parkmodel.parameterset[str(p)].set(str(v)) 366 522 367 523 for p in parkmodel.parameterset: 368 524 ## does not allow status change for constraint parameters 369 525 if p.status != 'computed': 370 if p.get_name() in item.pars:526 if p.get_name() in item.pars: 371 527 ## make parameters selected for 372 528 #fit will be between boundaries … … 383 539 def fit(self, msg_q=None, 384 540 q=None, handler=None, curr_thread=None, 385 541 ftol=1.49012e-8, reset_flag=False): 386 542 """ 387 543 Performs fit with park.fit module.It can perform fit with one model … … 407 563 localfit = SansFitSimplex() 408 564 localfit.ftol = ftol 409 565 localfit.xtol = 1e-6 566 410 567 # See `park.fitresult.FitHandler` for details. 411 568 fitter = SansFitMC(localfit=localfit, start_points=1) … … 416 573 try: 417 574 result = fit.fit(self.problem, fitter=fitter, handler=handler) 418 self.problem. all_results(result)575 self.problem.extend_results_with_calculated_parameters(result) 419 576 420 577 except LinAlgError: 421 578 raise ValueError, "SVD did not converge" 579 580 if result is None: 581 raise RuntimeError("park did not return a fit result") 422 582 423 583 for m in self.problem.parts: … … 427 587 small_result.theory = theory 428 588 small_result.residuals = residuals 429 small_result.pvec = [] 430 small_result.cov = [] 431 small_result.stderr = [] 432 small_result.param_list = [] 433 small_result.residuals = m.residuals 434 if result is not None: 435 for p in result.parameters: 436 if p.data.name == small_result.data.name and \ 437 p.model.name == small_result.model.name: 438 small_result.index = m.data.idx 439 small_result.fitness = result.fitness 440 small_result.pvec.append(p.value) 441 small_result.stderr.append(p.stderr) 442 name_split = p.name.split('.') 443 name = name_split[1].strip() 444 if len(name_split) > 2: 445 name += '.' + name_split[2].strip() 446 small_result.param_list.append(name) 589 small_result.index = m.data.idx 590 small_result.fitness = result.fitness 591 592 # Extract the parameters that are part of this model; make sure 593 # they match the fitted parameters for this model, and place them 594 # in the same order as they occur in the model. 595 pars = {} 596 for p in result.parameters: 597 #if p.data.name == small_result.data.name and 598 if p.model.name == small_result.model.name: 599 model_name, par_name = p.name.split('.', 1) 600 pars[par_name] = (p.value, p.stderr) 601 #assert len(pars.keys()) == len(m.model.pars) 602 v,dv = zip(*[pars[p] for p in m.model.pars]) 603 small_result.pvec = v 604 small_result.stderr = dv 605 small_result.param_list = m.model.pars 606 607 # normalize chisq by degrees of freedom 608 dof = len(small_result.residuals)-len(small_result.pvec) 609 small_result.fitness = numpy.sum(residuals**2)/dof 610 447 611 result_list.append(small_result) 448 612 if q != None: -
src/sans/fit/ScipyFitting.py
r5777106 r8d074d9 1 2 3 1 """ 4 2 ScipyFitting module contains FitArrange , ScipyFit, … … 6 4 simple fit with scipy optimizer. 7 5 """ 6 import sys 7 import copy 8 8 9 9 import numpy 10 import sys11 12 10 13 11 from sans.fit.AbstractFitEngine import FitEngine 14 from sans.fit.AbstractFitEngine import SansAssembly 15 from sans.fit.AbstractFitEngine import FitAbort 16 from sans.fit.AbstractFitEngine import Model 17 from sans.fit.AbstractFitEngine import FResult 12 from sans.fit.AbstractFitEngine import FResult 13 14 class SansAssembly: 15 """ 16 Sans Assembly class a class wrapper to be call in optimizer.leastsq method 17 """ 18 def __init__(self, paramlist, model=None, data=None, fitresult=None, 19 handler=None, curr_thread=None, msg_q=None): 20 """ 21 :param Model: the model wrapper fro sans -model 22 :param Data: the data wrapper for sans data 23 24 """ 25 self.model = model 26 self.data = data 27 self.paramlist = paramlist 28 self.msg_q = msg_q 29 self.curr_thread = curr_thread 30 self.handler = handler 31 self.fitresult = fitresult 32 self.res = [] 33 self.true_res = [] 34 self.func_name = "Functor" 35 self.theory = None 36 37 def chisq(self): 38 """ 39 Calculates chi^2 40 41 :param params: list of parameter values 42 43 :return: chi^2 44 45 """ 46 total = 0 47 for item in self.true_res: 48 total += item * item 49 if len(self.true_res) == 0: 50 return None 51 return total / (len(self.true_res) - len(self.paramlist)) 52 53 def __call__(self, params): 54 """ 55 Compute residuals 56 :param params: value of parameters to fit 57 """ 58 #import thread 59 self.model.set_params(self.paramlist, params) 60 #print "params", params 61 self.true_res, theory = self.data.residuals(self.model.eval) 62 self.theory = copy.deepcopy(theory) 63 # check parameters range 64 if self.check_param_range(): 65 # if the param value is outside of the bound 66 # just silent return res = inf 67 return self.res 68 self.res = self.true_res 69 70 if self.fitresult is not None: 71 self.fitresult.set_model(model=self.model) 72 self.fitresult.residuals = self.true_res 73 self.fitresult.iterations += 1 74 self.fitresult.theory = theory 75 76 #fitness = self.chisq(params=params) 77 fitness = self.chisq() 78 self.fitresult.pvec = params 79 self.fitresult.set_fitness(fitness=fitness) 80 if self.msg_q is not None: 81 self.msg_q.put(self.fitresult) 82 83 if self.handler is not None: 84 self.handler.set_result(result=self.fitresult) 85 self.handler.update_fit() 86 87 if self.curr_thread != None: 88 try: 89 self.curr_thread.isquit() 90 except: 91 #msg = "Fitting: Terminated... Note: Forcing to stop " 92 #msg += "fitting may cause a 'Functor error message' " 93 #msg += "being recorded in the log file....." 94 #self.handler.stop(msg) 95 raise 96 97 return self.res 98 99 def check_param_range(self): 100 """ 101 Check the lower and upper bound of the parameter value 102 and set res to the inf if the value is outside of the 103 range 104 :limitation: the initial values must be within range. 105 """ 106 107 #time.sleep(0.01) 108 is_outofbound = False 109 # loop through the fit parameters 110 model = self.model.model 111 for p in self.paramlist: 112 value = model.getParam(p) 113 low,high = model.details[p][1:3] 114 if low is not None and numpy.isfinite(low): 115 if p.value == 0: 116 # This value works on Scipy 117 # Do not change numbers below 118 value = _SMALLVALUE 119 # For leastsq, it needs a bit step back from the boundary 120 val = low - value * _SMALLVALUE 121 if value < val: 122 self.res *= 1e+6 123 is_outofbound = True 124 break 125 if high is not None and numpy.isfinite(high): 126 # This value works on Scipy 127 # Do not change numbers below 128 if value == 0: 129 value = _SMALLVALUE 130 # For leastsq, it needs a bit step back from the boundary 131 val = high + value * _SMALLVALUE 132 if value > val: 133 self.res *= 1e+6 134 is_outofbound = True 135 break 136 137 return is_outofbound 18 138 19 139 class ScipyFit(FitEngine): … … 50 170 """ 51 171 FitEngine.__init__(self) 52 self.fit_arrange_dict = {}53 self.param_list = []54 172 self.curr_thread = None 55 173 #def fit(self, *args, **kw): … … 68 186 msg = "Scipy can't fit more than a single fit problem at a time." 69 187 raise RuntimeError, msg 70 return 71 elif len(fitproblem) == 0 : 188 elif len(fitproblem) == 0 : 72 189 raise RuntimeError, "No Assembly scheduled for Scipy fitting." 73 return74 190 model = fitproblem[0].get_model() 75 191 if reset_flag: … … 87 203 88 204 # Check the initial value if it is within range 89 self._check_param_range(model)205 _check_param_range(model.model, self.param_list) 90 206 91 result = FResult(model=model, data=data, param_list=self.param_list) 92 result.pars = fitproblem[0].pars 207 result = FResult(model=model.model, data=data, param_list=self.param_list) 93 208 result.fitter_id = self.fitter_id 94 209 if handler is not None: 95 210 handler.set_result(result=result) 211 functor = SansAssembly(paramlist=self.param_list, 212 model=model, 213 data=data, 214 handler=handler, 215 fitresult=result, 216 curr_thread=curr_thread, 217 msg_q=msg_q) 96 218 try: 97 219 # This import must be here; otherwise it will be confused when more … … 99 221 from scipy import optimize 100 222 101 functor = SansAssembly(paramlist=self.param_list,102 model=model,103 data=data,104 handler=handler,105 fitresult=result,106 curr_thread=curr_thread,107 msg_q=msg_q)108 223 out, cov_x, _, mesg, success = optimize.leastsq(functor, 109 224 model.get_params(self.param_list), 110 111 225 ftol=ftol, 226 full_output=1) 112 227 except: 113 228 if hasattr(sys, 'last_type') and sys.last_type == KeyboardInterrupt: … … 142 257 143 258 144 def _check_param_range(self, model): 145 """ 146 Check parameter range and set the initial value inside 147 if it is out of range. 148 149 : model: park model object 150 """ 151 is_outofbound = False 152 # loop through parameterset 153 for p in model.parameterset: 154 param_name = p.get_name() 155 # proceed only if the parameter name is in the list of fitting 156 if param_name in self.param_list: 157 # if the range was defined, check the range 158 if numpy.isfinite(p.range[0]): 159 if p.value <= p.range[0]: 160 # 10 % backing up from the border if not zero 161 # for Scipy engine to work properly. 162 shift = self._get_zero_shift(p.range[0]) 163 new_value = p.range[0] + shift 164 p.value = new_value 165 is_outofbound = True 166 if numpy.isfinite(p.range[1]): 167 if p.value >= p.range[1]: 168 shift = self._get_zero_shift(p.range[1]) 169 # 10 % backing up from the border if not zero 170 # for Scipy engine to work properly. 171 new_value = p.range[1] - shift 172 # Check one more time if the new value goes below 173 # the low bound, If so, re-evaluate the value 174 # with the mean of the range. 175 if numpy.isfinite(p.range[0]): 176 if new_value < p.range[0]: 177 new_value = (p.range[0] + p.range[1]) / 2.0 178 # Todo: 179 # Need to think about when both min and max are same. 180 p.value = new_value 181 is_outofbound = True 182 183 return is_outofbound 184 185 def _get_zero_shift(self, range): 186 """ 187 Get 10% shift of the param value = 0 based on the range value 188 189 : param range: min or max value of the bounds 190 """ 191 if range == 0: 192 shift = 0.1 193 else: 194 shift = 0.1 * range 195 196 return shift 197 259 def _check_param_range(model, param_list): 260 """ 261 Check parameter range and set the initial value inside 262 if it is out of range. 263 264 : model: park model object 265 """ 266 # loop through parameterset 267 for p in param_list: 268 value = model.getParam(p) 269 low,high = model.details.setdefault(p,["",None,None])[1:3] 270 # if the range was defined, check the range 271 if low is not None and value <= low: 272 value = low + _get_zero_shift(low) 273 if high is not None and value > high: 274 value = high - _get_zero_shift(high) 275 # Check one more time if the new value goes below 276 # the low bound, If so, re-evaluate the value 277 # with the mean of the range. 278 if low is not None and value < low: 279 value = 0.5 * (low+high) 280 model.setParam(p, value) 281 282 def _get_zero_shift(limit): 283 """ 284 Get 10% shift of the param value = 0 based on the range value 285 286 : param range: min or max value of the bounds 287 """ 288 return 0.1 * (limit if limit != 0.0 else 1.0) 289 198 290 199 291 #def profile(fn, *args, **kw): -
src/sans/fit/__init__.py
r5777106 r6fe5100 1 from .AbstractFitEngine import FitHandler -
src/sans/guiframe/gui_statusbar.py
r8001331 r0e29289 211 211 """ 212 212 """ 213 wxStatusB.SetStatusText(self, text , number)213 wxStatusB.SetStatusText(self, text.split('\n',1)[0], number) 214 214 self.list_msg.append(text) 215 215 self.status_color.SetBackgroundColour(GREEN) -
src/sans/perspectives/fitting/basepage.py
r116e1a7 rbf5e985 96 96 self.num_points = None 97 97 ## default fitengine type 98 self.engine_type = ' scipy'98 self.engine_type = 'bumps' 99 99 ## smear default 100 100 self.current_smearer = None … … 835 835 infor = "warning" 836 836 else: 837 msg = "Error was occured"838 msg += " :No valid parameter values to paste from the clipboard..."837 msg = "Error occured: " 838 msg += "No valid parameter values to paste from the clipboard..." 839 839 infor = "error" 840 840 wx.PostEvent(self._manager.parent, … … 2183 2183 else: 2184 2184 tcrtl.SetBackgroundColour("pink") 2185 msg = "Model Error: wrong value entered: %s" % sys.exc_value2185 msg = "Model Error: wrong value entered: %s" % sys.exc_value 2186 2186 wx.PostEvent(self.parent, StatusEvent(status=msg)) 2187 2187 return 2188 2188 except: 2189 2189 tcrtl.SetBackgroundColour("pink") 2190 msg = "Model Error: wrong value entered: %s" % sys.exc_value2190 msg = "Model Error: wrong value entered: %s" % sys.exc_value 2191 2191 wx.PostEvent(self.parent, StatusEvent(status=msg)) 2192 2192 return … … 2199 2199 #is_modified = True 2200 2200 else: 2201 msg = "Cannot Plot :No npts in that Qrange!!! "2201 msg = "Cannot plot: No points in Q range!!! " 2202 2202 wx.PostEvent(self.parent, StatusEvent(status=msg)) 2203 2203 else: 2204 2204 tcrtl.SetBackgroundColour("pink") 2205 msg = "Model Error: wrong value entered!!!"2205 msg = "Model Error: wrong value entered!!!" 2206 2206 wx.PostEvent(self.parent, StatusEvent(status=msg)) 2207 2207 self.save_current_state() … … 2240 2240 else: 2241 2241 tcrtl.SetBackgroundColour("pink") 2242 msg = "Model Error: wrong value entered: %s" % sys.exc_value2242 msg = "Model Error: wrong value entered: %s" % sys.exc_value 2243 2243 wx.PostEvent(self._manager.parent, StatusEvent(status=msg)) 2244 2244 return 2245 2245 except: 2246 2246 tcrtl.SetBackgroundColour("pink") 2247 msg = "Model Error: wrong value entered: %s" % sys.exc_value2247 msg = "Model Error: wrong value entered: %s" % sys.exc_value 2248 2248 wx.PostEvent(self._manager.parent, StatusEvent(status=msg)) 2249 2249 return … … 2256 2256 is_modified = True 2257 2257 else: 2258 msg = "Cannot Plot :No npts in that Qrange!!! "2258 msg = "Cannot Plot: No points in Q range!!! " 2259 2259 wx.PostEvent(self._manager.parent, StatusEvent(status=msg)) 2260 2260 else: 2261 2261 tcrtl.SetBackgroundColour("pink") 2262 msg = "Model Error: wrong value entered!!!"2262 msg = "Model Error: wrong value entered!!!" 2263 2263 wx.PostEvent(self._manager.parent, StatusEvent(status=msg)) 2264 2264 self.save_current_state() … … 2431 2431 self.qmax.SetBackgroundColour("pink") 2432 2432 self.qmax.Refresh() 2433 msg = " Npts of Data Error :"2434 msg += " No or too little npts of%s." % data.name2433 msg = "Data Error: " 2434 msg += "Too few points in %s." % data.name 2435 2435 wx.PostEvent(self._manager.parent, StatusEvent(status=msg)) 2436 2436 self.fitrange = False … … 2466 2466 self.qmax.SetBackgroundColour("pink") 2467 2467 self.qmax.Refresh() 2468 msg = " Npts of Data Error :"2469 msg += " No or too little npts of%s." % data.name2468 msg = "Data Error: " 2469 msg += "Too few points in %s." % data.name 2470 2470 wx.PostEvent(self._manager.parent, StatusEvent(status=msg)) 2471 2471 self.fitrange = False … … 2518 2518 2519 2519 except: 2520 msg = "Wrong Fit parameter range entered"2520 msg = "Wrong fit parameter range entered" 2521 2521 wx.PostEvent(self._manager.parent, 2522 2522 StatusEvent(status=msg)) … … 2986 2986 self.qmin.SetValue(str(self.qmin_x)) 2987 2987 self.qmax.SetValue(str(self.qmax_x)) 2988 self.s et_npts2fit()2988 self.show_npts2fit() 2989 2989 # At this point, some button and variables satatus (disabled?) 2990 2990 # should be checked such as color that should be reset to … … 3874 3874 to fit if implemented 3875 3875 """ 3876 def s et_npts2fit(self):3876 def show_npts2fit(self): 3877 3877 """ 3878 3878 setValue Npts for fitting if implemented -
src/sans/perspectives/fitting/console.py
r5777106 r35086c3 5 5 import time 6 6 import wx 7 import park 8 from park.fitresult import FitHandler 7 from sans.fit import FitHandler 9 8 10 9 class ConsoleUpdate(FitHandler): … … 88 87 Print result object 89 88 """ 90 msg = " \n %s \n" % s elf.result.__str__()89 msg = " \n %s \n" % str(self.result) 91 90 wx.PostEvent(self.parent, StatusEvent(status=msg)) 92 91 … … 129 128 130 129 131 def update_fit(self, msg="",last=False):130 def update_fit(self, last=False): 132 131 """ 133 132 """ … … 136 135 self.update_duration = t1 137 136 self.fit_duration += self.elapsed_time 138 str_time = time.strftime("% a, %d %b %Y%H:%M:%S ", time.localtime(t1))139 UPDATE_INTERVAL = 0.5137 str_time = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(t1)) 138 UPDATE_INTERVAL = 5.0 140 139 u_flag = False 141 140 if self.fit_duration >= UPDATE_INTERVAL: 142 141 self.fit_duration = 0 143 142 u_flag = True 144 if not last: 145 msg += "Fit Updates ... %s \n" % str_time 146 else: 147 msg += "Final updates ........." 143 msg = str_time 148 144 if u_flag or last: 149 145 if self.result is not None: … … 164 160 msg += str(self.result) 165 161 msg += "\n" 166 if not last:167 msg += "About %s s elapsed......... \n" % \168 str (UPDATE_INTERVAL)169 162 else: 170 163 msg += "No result available\n" -
src/sans/perspectives/fitting/fit_thread.py
ra855fec re3efa6b3 18 18 19 19 def __init__(self, 20 21 22 23 24 batch_inputs=None,25 20 fn, 21 page_id, 22 handler, 23 batch_outputs, 24 batch_inputs=None, 25 pars=None, 26 26 completefn = None, 27 27 updatefn = None, … … 30 30 ftol = None, 31 31 reset_flag = False): 32 CalcThread.__init__(self,completefn, 32 CalcThread.__init__(self, 33 completefn, 33 34 updatefn, 34 35 yieldtime, … … 80 81 list_map_get_attr.append(map_getattr) 81 82 #from multiprocessing import Pool 82 inputs = zip(list_map_get_attr, self.fitter, list_fit_function,83 83 inputs = zip(list_map_get_attr, self.fitter, list_fit_function, 84 list_q, list_q, list_handler,list_curr_thread,list_ftol, 84 85 list_reset_flag) 85 86 result = map(map_apply, inputs) … … 87 88 self.complete(result=result, 88 89 batch_inputs=self.batch_inputs, 89 90 batch_outputs=self.batch_outputs, 90 91 page_id=self.page_id, 91 92 pars = self.pars, -
src/sans/perspectives/fitting/fitpage.py
rd44648e rbf5e985 661 661 wx.EXPAND | wx.ADJUST_MINSIZE, 0) 662 662 663 if self.engine_type == "park": 664 self.text_disp_max.Show(True) 665 self.text_disp_min.Show(True) 663 self.text_disp_max.Show(True) 664 self.text_disp_min.Show(True) 666 665 667 666 for item in self.model.dispersion.keys(): … … 738 737 wx.EXPAND | wx.ADJUST_MINSIZE, 0) 739 738 740 if self.engine_type == "park": 741 ctl3.Show(True) 742 ctl4.Show(True) 739 ctl3.Show(True) 740 ctl4.Show(True) 743 741 744 742 elif p == "npts": … … 1005 1003 return 1006 1004 1007 if len(self._manager.fit_thread_list) > 0 and\1008 self._manager._fit_engine != "park" and\1009 self._manager.sim_page != None and \1010 self._manager.sim_page.uid == self.uid:1005 if (len(self._manager.fit_thread_list) > 0 1006 and self._manager._fit_engine not in ("park","bumps") 1007 and self._manager.sim_page != None 1008 and self._manager.sim_page.uid == self.uid): 1011 1009 msg = "The FitEnging will be set to 'ParkMC'\n" 1012 1010 msg += " to fit with more than one data set..." … … 1268 1266 if check_float(tcrtl): 1269 1267 flag = self._onparamEnter_helper() 1270 self.s et_npts2fit()1268 self.show_npts2fit() 1271 1269 if self.fitrange: 1272 1270 temp_smearer = None … … 1468 1466 #self.data.mask = index_data 1469 1467 #self.Npts_fit.SetValue(str(len(self.data.mask))) 1470 self.s et_npts2fit()1468 self.show_npts2fit() 1471 1469 else: 1472 1470 index_data = ((self.qmin_x <= self.data.x) & \ … … 1766 1764 # try re draw the model plot if it exists 1767 1765 self._draw_model() 1768 self.s et_npts2fit()1766 self.show_npts2fit() 1769 1767 elif self.model == None: 1770 1768 self.panel.MakeModal(False) 1771 1769 event.Skip() 1772 self.s et_npts2fit()1770 self.show_npts2fit() 1773 1771 msg = "No model is found on updating MASK in the model plot... " 1774 1772 wx.PostEvent(self._manager.parent, StatusEvent(status=msg)) … … 2056 2054 return npts2fit 2057 2055 2058 def s et_npts2fit(self):2056 def show_npts2fit(self): 2059 2057 """ 2060 2058 setValue Npts for fitting … … 2068 2066 return self.tcChi.GetValue() 2069 2067 2070 def get_param_list(self):2071 """2072 :return self.param_toFit: list containing references to TextCtrl2073 checked.Theses TextCtrl will allow reference to parameters to fit.2074 2075 :raise: if return an empty list of parameter fit will nnote work2076 properly so raise ValueError,"missing parameter to fit"2077 """2078 if self.param_toFit != []:2079 return self.param_toFit2080 else:2081 msg = "missing parameters to fit"2082 wx.MessageBox(msg, 'warning')2083 return False2084 2085 2068 def onsetValues(self, chisqr, p_name, out, cov): 2086 2069 """ … … 2108 2091 if chisqr != None and numpy.isfinite(chisqr): 2109 2092 #format chi2 2110 if self.engine_type == "park":2111 npt_fit = float(self.get_npts2fit())2112 2093 chi2 = format_number(chisqr, True) 2113 2094 self.tcChi.SetValue(chi2) -
src/sans/perspectives/fitting/fitpanel.py
rb6a181b reff93b8 64 64 self.sim_page = None 65 65 self.batch_page = None 66 self.fit_engine_type = " scipy"66 self.fit_engine_type = "bumps" 67 67 ## get the state of a page 68 68 self.Bind(basepage.EVT_PAGE_INFO, self._onGetstate) -
src/sans/perspectives/fitting/fitproblem.py
r5777106 r5777106 454 454 return self.itervalues() 455 455 456 def 456 def set_result(self, result, fid): 457 457 """ 458 458 """ -
src/sans/perspectives/fitting/fitting.py
r9afebe1 r9afebe1 36 36 from .fitproblem import FitProblemDictionary 37 37 from .fitpanel import FitPanel 38 from .resultpanel import ResultPanel, PlotResultEvent 39 38 40 from .fit_thread import FitThread 39 41 from .pagestate import Reader … … 42 44 from sans.perspectives.calculator.model_editor import EditorWindow 43 45 from sans.guiframe.gui_manager import MDIFrame 46 47 # TODO: remove globals from interface to bumps options! 48 # Default bumps to use the levenberg-marquardt optimizer 49 import bumps.fitters 50 bumps.fitters.FIT_DEFAULT = 'lm' 44 51 45 52 MAX_NBR_DATA = 4 … … 78 85 # Start with a good default 79 86 self.elapsed = 0.022 80 # the type of optimizer selected, park or scipy81 self.fitter = None82 87 self.fit_panel = None 83 #let fit ready84 self.fitproblem_count = None85 88 #Flag to let the plug-in know that it is running stand alone 86 89 self.standalone = True … … 88 91 self.closed_page_dict = {} 89 92 ## Fit engine 90 self._fit_engine = ' scipy'93 self._fit_engine = 'bumps' 91 94 self._gui_engine = None 92 95 ## Relative error desired in the sum of squares (float); scipy only … … 111 114 self.scipy_id = wx.NewId() 112 115 self.park_id = wx.NewId() 116 self.bumps_id = wx.NewId() 113 117 self.menu1 = None 114 118 self.new_model_frame = None … … 198 202 wx.EVT_MENU(owner, self.park_id, self._onset_engine_park) 199 203 200 self.menu1.FindItemById(self.scipy_id).Check(True) 201 self.menu1.FindItemById(self.park_id).Check(False) 204 bumps_help = "Bumps: fitting and uncertainty analysis. More in Help window...." 205 self.menu1.AppendCheckItem(self.bumps_id, "Bumps fit", 206 bumps_help) 207 wx.EVT_MENU(owner, self.bumps_id, self._onset_engine_bumps) 208 209 self.menu1.FindItemById(self.scipy_id).Check(self._fit_engine=="scipy") 210 self.menu1.FindItemById(self.park_id).Check(self._fit_engine=="park") 211 self.menu1.FindItemById(self.bumps_id).Check(self._fit_engine=="bumps") 202 212 self.menu1.AppendSeparator() 203 213 self.id_tol = wx.NewId() … … 207 217 ftol_help) 208 218 wx.EVT_MENU(owner, self.id_tol, self.show_ftol_dialog) 219 220 self.id_bumps_options = wx.NewId() 221 bopts_help = "Bumps fitting options" 222 self.menu1.Append(self.id_bumps_options, 'Bumps &Options', bopts_help) 223 wx.EVT_MENU(owner, self.id_bumps_options, self.on_bumps_options) 224 self.bumps_options_menu = self.menu1.FindItemById(self.id_bumps_options) 225 self.bumps_options_menu.Enable(True) 226 227 self.id_result_panel = wx.NewId() 228 self.menu1.Append(self.id_result_panel, "Fit Results", "Show fit results panel") 229 wx.EVT_MENU(owner, self.id_result_panel, lambda ev: self.result_frame.Show()) 209 230 self.menu1.AppendSeparator() 210 231 … … 511 532 self.perspective = [] 512 533 self.perspective.append(self.fit_panel.window_name) 534 535 self.result_frame = MDIFrame(self.parent, None, ResultPanel.window_caption, (220, 200)) 536 self.result_panel = ResultPanel(parent=self.result_frame, manager=self) 537 self.perspective.append(self.result_panel.window_name) 513 538 514 539 #index number to create random model name … … 525 550 #Send the fitting panel to guiframe 526 551 self.mypanels.append(self.fit_panel) 552 self.mypanels.append(self.result_panel) 527 553 return self.mypanels 528 554 … … 818 844 StatusEvent(status=msg, info='warning')) 819 845 dialog.Destroy() 846 847 def on_bumps_options(self, event=None): 848 from bumps.gui.fit_dialog import OpenFitOptions 849 OpenFitOptions() 820 850 821 851 def stop_fit(self, uid): … … 951 981 :param uid: id related to the panel currently calling this fit function. 952 982 """ 953 flag = True 954 ## count the number of fitproblem schedule to fit 955 fitproblem_count = 0 956 for value in self.page_finder.values(): 957 if value.get_scheduled() == 1: 958 fitproblem_count += 1 959 self._gui_engine = self._return_engine_type() 960 self.fitproblem_count = fitproblem_count 961 if self._fit_engine == "park": 962 engineType = "Simultaneous Fit" 983 if uid is None: raise RuntimeError("no page to fit") # Should never happen 984 985 # Remember the user selected fit engine before the fit. Simultaneous 986 # fitting may change the selected engine, so it needs to be restored 987 # when the fit is complete. 988 self._gui_engine = self._fit_engine 989 990 sim_page_uid = getattr(self.sim_page, 'uid', None) 991 batch_page_uid = getattr(self.batch_page, 'uid', None) 992 993 if uid == sim_page_uid: 994 fit_type = 'simultaneous' 995 elif uid == batch_page_uid: 996 fit_type = 'combined_batch' 963 997 else: 964 engineType = "Single Fit" 998 fit_type = 'single' 999 1000 # if constrained fit, don't use scipy leastsq directly 1001 if fit_type == 'simultaneous': 1002 if self._fit_engine not in ("park","bumps"): 1003 self._on_change_engine(engine='bumps') 1004 1005 965 1006 fitter_list = [] 966 1007 sim_fitter = None 967 is_single_fit = True 968 batch_on = False 969 if self.sim_page is not None and self.sim_page.uid == uid: 1008 if fit_type == 'simultaneous': 970 1009 #simulatanous fit only one engine need to be created 971 ## if simultaneous fit change automatically the engine to park972 self._on_change_engine(engine='park')973 1010 sim_fitter = Fit(self._fit_engine) 974 1011 sim_fitter.fitter_id = self.sim_page.uid 975 1012 fitter_list.append(sim_fitter) 976 is_single_fit = False 977 batch_on = self.sim_page.batch_on 978 979 self.fitproblem_count = fitproblem_count 980 if self._fit_engine == "park": 981 engineType = "Simultaneous Fit" 982 else: 983 engineType = "Single Fit" 984 1013 985 1014 self.current_pg = None 986 1015 list_page_id = [] 987 1016 fit_id = 0 988 batch_inputs = {} 989 batch_outputs = {} 990 for page_id, value in self.page_finder.iteritems(): 1017 for page_id, page_info in self.page_finder.iteritems(): 991 1018 # For simulfit (uid give with None), do for-loop 992 1019 # if uid is specified (singlefit), do it only on the page. 993 if engineType == "Single Fit": 994 #combine more than 1 batch page on single mode 995 if self.batch_page is None or self.batch_page.uid != uid: 996 if page_id != uid: 997 continue 1020 if page_id in (sim_page_uid, batch_page_uid): continue 1021 if fit_type == "single" and page_id != uid: continue 1022 998 1023 try: 999 if value.get_scheduled() == 1: 1000 value.nbr_residuals_computed = 0 1001 #Get list of parameters name to fit 1002 pars = [] 1003 templist = [] 1024 if page_info.get_scheduled() == 1: 1025 page_info.nbr_residuals_computed = 0 1004 1026 page = self.fit_panel.get_page_by_id(page_id) 1005 1027 self.set_fit_weight(uid=page.uid, 1006 1028 flag=page.get_weight_flag(), 1007 1029 is2d=page._is_2D()) 1008 templist = page.get_param_list() 1009 flag = page._update_paramv_on_fit() 1010 if not flag: 1030 if not page.param_toFit: 1031 msg = "No fitting parameters for %s"%page.window_caption 1032 wx.PostEvent(page.parent.parent, 1033 StatusEvent(status=msg, info="error", 1034 type="stop")) 1035 return False 1036 if not page._update_paramv_on_fit(): 1011 1037 msg = "Fitting range or parameter values are" 1012 1038 msg += " invalid in %s" % \ … … 1015 1041 StatusEvent(status=msg, info="error", 1016 1042 type="stop")) 1017 return flag 1018 for element in templist: 1019 name = str(element[1]) 1020 pars.append(name) 1021 fitproblem_list = value.values() 1043 return False 1044 1045 pars = [str(element[1]) for element in page.param_toFit] 1046 fitproblem_list = page_info.values() 1022 1047 for fitproblem in fitproblem_list: 1023 1048 if sim_fitter is None: 1024 1049 fitter = Fit(self._fit_engine) 1025 1050 fitter.fitter_id = page_id 1026 self._fit_helper(fitproblem=fitproblem,1027 pars=pars,1028 fitter=fitter,1029 fit_id=fit_id,1030 batch_inputs=batch_inputs,1031 batch_outputs=batch_outputs)1032 1051 fitter_list.append(fitter) 1033 1052 else: 1034 1053 fitter = sim_fitter 1035 self._fit_helper(fitproblem=fitproblem,1054 self._add_problem_to_fit(fitproblem=fitproblem, 1036 1055 pars=pars, 1037 1056 fitter=fitter, 1038 fit_id=fit_id, 1039 batch_inputs=batch_inputs, 1040 batch_outputs=batch_outputs) 1057 fit_id=fit_id) 1041 1058 fit_id += 1 1042 1059 list_page_id.append(page_id) 1043 current_page_id = page_id 1044 value.clear_model_param() 1060 page_info.clear_model_param() 1045 1061 except KeyboardInterrupt: 1046 flag = True1047 1062 msg = "Fitting terminated" 1048 1063 wx.PostEvent(self.parent, StatusEvent(status=msg, info="info", 1049 1064 type="stop")) 1050 return flag1065 return True 1051 1066 except: 1052 flag = False 1053 msg = "%s error: %s" % (engineType, sys.exc_value) 1067 msg = "Fitting error: %s" % str(sys.exc_value) 1054 1068 wx.PostEvent(self.parent, StatusEvent(status=msg, info="error", 1055 1069 type="stop")) 1056 return flag1070 return False 1057 1071 ## If a thread is already started, stop it 1058 1072 #if self.calc_fit!= None and self.calc_fit.isrunning(): … … 1066 1080 improvement_delta=0.1) 1067 1081 self._mac_sleep(0.2) 1068 ## perform single fit 1069 try: 1082 1083 # batch fit 1084 batch_inputs = {} 1085 batch_outputs = {} 1086 if fit_type == "simultaneous": 1087 page = self.sim_page 1088 elif fit_type == "combined_batch": 1089 page = self.batch_page 1090 else: 1070 1091 page = self.fit_panel.get_page_by_id(uid) 1071 batch_on = page.batch_on 1072 except: 1073 try: 1074 #if the id cannot be found then we deal with a self.sim_page 1075 #or a self.batch_page 1076 if self.sim_page is not None and uid == self.sim_page.uid: 1077 batch_on = self.sim_page.batch_on 1078 if self.batch_page is not None and uid == self.batch_page.uid: 1079 batch_on = self.batch_page.batch_on 1080 except: 1081 batch_on = False 1082 1083 # batch fit 1084 if batch_on: 1092 if page.batch_on: 1085 1093 calc_fit = FitThread(handler=handler, 1086 1094 fn=fitter_list, … … 1093 1101 reset_flag=self.batch_reset_flag) 1094 1102 else: 1095 # single fit: not batch and not simul fit1096 if not is_single_fit:1097 current_page_id = self.sim_page.uid1098 1103 ## Perform more than 1 fit at the time 1099 1104 calc_fit = FitThread(handler=handler, … … 1105 1110 completefn=self._fit_completed, 1106 1111 ftol=self.ftol) 1107 self.fit_thread_list[current_page_id] = calc_fit 1112 #self.fit_thread_list[current_page_id] = calc_fit 1113 self.fit_thread_list[uid] = calc_fit 1108 1114 calc_fit.queue() 1115 calc_fit.ready(2.5) 1109 1116 msg = "Fitting is in progress..." 1110 1117 wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress")) 1111 1118 1112 self.ready_fit(calc_fit=calc_fit) 1113 return flag 1114 1115 def ready_fit(self, calc_fit): 1116 """ 1117 Ready for another fit 1118 """ 1119 if self.fitproblem_count != None and self.fitproblem_count > 1: 1120 calc_fit.ready(2.5) 1121 else: 1122 time.sleep(0.4) 1123 1119 return True 1120 1124 1121 def remove_plot(self, uid, fid=None, theory=False): 1125 1122 """ … … 1261 1258 self.page_finder[uid].schedule_tofit(value) 1262 1259 1263 def _fit_helper(self, fitproblem, pars, fitter, fit_id, 1264 batch_inputs, batch_outputs): 1260 def _add_problem_to_fit(self, fitproblem, pars, fitter, fit_id): 1265 1261 """ 1266 1262 Create and set fit engine with series of data and model … … 1571 1567 wx.PostEvent(self.parent, StatusEvent(status=msg, info="info", 1572 1568 type="stop")) 1569 wx.PostEvent(self.result_panel, PlotResultEvent(result=result)) 1573 1570 # reset fit_engine if changed by simul_fit 1574 1571 if self._fit_engine != self._gui_engine: … … 1682 1679 self._on_change_engine('scipy') 1683 1680 1681 def _onset_engine_bumps(self, event): 1682 """ 1683 set engine to bumps 1684 """ 1685 self._on_change_engine('bumps') 1686 1684 1687 def _on_slicer_event(self, event): 1685 1688 """ … … 1714 1717 break 1715 1718 1716 def _return_engine_type(self):1717 """1718 return the current type of engine1719 """1720 return self._fit_engine1721 1722 1719 def _on_change_engine(self, engine='park'): 1723 1720 """ … … 1733 1730 self.menu1.FindItemById(self.park_id).Check(True) 1734 1731 self.menu1.FindItemById(self.scipy_id).Check(False) 1732 self.menu1.FindItemById(self.bumps_id).Check(False) 1733 elif engine == "scipy": 1734 self.menu1.FindItemById(self.park_id).Check(False) 1735 self.menu1.FindItemById(self.scipy_id).Check(True) 1736 self.menu1.FindItemById(self.bumps_id).Check(False) 1735 1737 else: 1736 1738 self.menu1.FindItemById(self.park_id).Check(False) 1737 self.menu1.FindItemById(self.scipy_id).Check(True) 1739 self.menu1.FindItemById(self.scipy_id).Check(False) 1740 self.menu1.FindItemById(self.bumps_id).Check(True) 1738 1741 ## post a message to status bar 1739 1742 msg = "Engine set to: %s" % self._fit_engine -
src/sans/perspectives/fitting/simfitpage.py
r5777106 rbf5e985 138 138 self.Layout() 139 139 break 140 self._onAdd_constraint(None) 140 141 #self._onAdd_constraint(None) 141 142 142 143 def onFit(self, event): … … 152 153 ## making sure all parameters content a constraint 153 154 ## validity of the constraint expression is own by fit engine 154 if self.parent._manager._fit_engine != "park"and flag:155 if self.parent._manager._fit_engine not in ("park","bumps") and flag: 155 156 msg = "The FitEnging will be set to 'Park' fit engine\n" 156 157 msg += " for the simultaneous fit..." … … 378 379 box_description = wx.StaticBox(self, -1,"Easy Setup ") 379 380 boxsizer = wx.StaticBoxSizer(box_description, wx.HORIZONTAL) 380 sizer_constraint = wx.BoxSizer(wx.HORIZONTAL |wx.LEFT|wx.RIGHT|wx.EXPAND)381 sizer_constraint = wx.BoxSizer(wx.HORIZONTAL) 381 382 self.model_cbox_left = wx.ComboBox(self, -1, style=wx.CB_READONLY) 382 383 self.model_cbox_left.Clear() … … 814 815 815 816 for fid in self.page_finder[id].iterkeys(): 816 self.page_finder[id].set_model_param(param, 817 constraint, fid=fid) 817 # wrap in param/constraint in str() to remove unicode 818 self.page_finder[id].set_model_param(str(param), 819 str(constraint), fid=fid) 818 820 break 819 821 return True -
test/park_integration/test/batch_fit.py
r5777106 re3efa6b3 1 2 3 import math4 import numpy5 1 import copy 6 2 import time … … 15 11 16 12 17 18 19 13 def classMapper(classInstance, classFunc, *args): 20 14 """ … … 28 22 29 23 30 31 class BatchScipyFit: 24 class BatchFit: 32 25 """ 33 26 test fit module … … 69 62 model.setParam('cyl_phi.npts', 3) 70 63 model.setParam('cyl_theta.nsigmas', 10) 71 """ for 2 data cyl_theta = 60.0 [deg] cyl_phi= 60.0 [deg]"""64 # for 2 data cyl_theta = 60.0 [deg] cyl_phi= 60.0 [deg] 72 65 fitter.set_model(model, i, self.param_to_fit, 73 66 self.list_of_constraints) … … 84 77 self.list_of_mapper.append(classMapper) 85 78 86 def reset_value(self ):79 def reset_value(self, engine='scipy'): 87 80 """ 88 81 Initialize inputs for the map function … … 93 86 self.list_of_constraints = [] 94 87 self.list_of_mapper = [] 95 engine ="scipy" 96 88 97 89 path = "testdata_line3.txt" 98 90 self._reset_helper(path=path, engine=engine, npts=NPTS) … … 113 105 114 106 115 def test_map_fit(self ):107 def test_map_fit(self, n=0): 116 108 """ 117 """ 118 results = map(classMapper,self.list_of_fitter, self.list_of_function) 119 print len(results) 120 for result in results: 121 print result.fitness, result.stderr, result.pvec 109 """ 110 if n > 0: 111 self._test_process_map_fit(n=n) 112 else: 113 results = map(classMapper,self.list_of_fitter, self.list_of_function) 114 print len(results) 115 for result in results: 116 print result.fitness, result.stderr, result.pvec 122 117 123 118 def test_process_map_fit(self, n=1): … … 144 139 """ 145 140 def setUp(self): 146 self.test = Batch ScipyFit(qmin=None, qmax=None)141 self.test = BatchFit(qmin=None, qmax=None) 147 142 148 143 149 def __test_fit1(self):144 def test_fit1(self): 150 145 """test fit with python built in map function---- full range of each data""" 151 146 self.test.test_map_fit() 152 147 153 def __test_fit2(self):148 def test_fit2(self): 154 149 """test fit with python built in map function---- common range for all data""" 155 150 self.test.set_range(qmin=0.013, qmax=0.05) 156 151 self.test.reset_value() 157 152 self.test.test_map_fit() 153 raise Exception("fail") 158 154 159 155 def test_fit3(self): … … 161 157 self.test.set_range(qmin=None, qmax=None) 162 158 self.test.reset_value() 163 self.test.test_ process_map_fit(n=2)159 self.test.test_map_fit(n=1) 164 160 165 161 def test_fit4(self): … … 167 163 self.test.set_range(qmin=-1, qmax=10) 168 164 self.test.reset_value() 169 self.test.test_ process_map_fit(n=1)165 self.test.test_map_fit(n=3) 170 166 171 167 -
test/run_one.py
r95fb3e8 r76f132a 1 1 #!/usr/bin/env python 2 2 3 import os 3 4 import sys … … 8 9 9 10 run_py = joinpath(dirname(dirname(abspath(__file__))), 'run.py') 10 #print run_py11 11 run = imp.load_source('sasview_run', run_py) 12 12 run.prepare() 13 13 #print "\n".join(sys.path) 14 test_path,test_file = splitpath( sys.argv[1])15 print " test file",sys.argv[1]14 test_path,test_file = splitpath(abspath(sys.argv[1])) 15 print "=== testing:",sys.argv[1] 16 16 #print test_path, test_file 17 17 sys.argv = [sys.argv[0]] … … 20 20 test = imp.load_source('tests',test_file) 21 21 unittest.main(test, testRunner=xmlrunner.XMLTestRunner(output='logs')) 22
Note: See TracChangeset
for help on using the changeset viewer.