Changes in / [55b283e8:b968fe8] in sasmodels
- Location:
- sasmodels
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/compare.py
r82c299f r608e31e 1 1 #!/usr/bin/env python 2 2 # -*- coding: utf-8 -*- 3 """ 4 Program to compare models using different compute engines. 5 6 This program lets you compare results between OpenCL and DLL versions 7 of the code and between precision (half, fast, single, double, quad), 8 where fast precision is single precision using native functions for 9 trig, etc., and may not be completely IEEE 754 compliant. This lets 10 make sure that the model calculations are stable, or if you need to 11 tag the model as double precision only. 12 13 Run using ./compare.sh (Linux, Mac) or compare.bat (Windows) in the 14 sasmodels root to see the command line options. 15 16 Note that there is no way within sasmodels to select between an 17 OpenCL CPU device and a GPU device, but you can do so by setting the 18 PYOPENCL_CTX environment variable ahead of time. Start a python 19 interpreter and enter:: 20 21 import pyopencl as cl 22 cl.create_some_context() 23 24 This will prompt you to select from the available OpenCL devices 25 and tell you which string to use for the PYOPENCL_CTX variable. 26 On Windows you will need to remove the quotes. 27 """ 28 29 from __future__ import print_function 30 31 USAGE = """ 32 usage: compare.py model N1 N2 [options...] [key=val] 33 34 Compare the speed and value for a model between the SasView original and the 35 sasmodels rewrite. 36 37 model is the name of the model to compare (see below). 38 N1 is the number of times to run sasmodels (default=1). 39 N2 is the number times to run sasview (default=1). 40 41 Options (* for default): 42 43 -plot*/-noplot plots or suppress the plot of the model 44 -lowq*/-midq/-highq/-exq use q values up to 0.05, 0.2, 1.0, 10.0 45 -nq=128 sets the number of Q points in the data set 46 -1d*/-2d computes 1d or 2d data 47 -preset*/-random[=seed] preset or random parameters 48 -mono/-poly* force monodisperse/polydisperse 49 -cutoff=1e-5* cutoff value for including a point in polydispersity 50 -pars/-nopars* prints the parameter set or not 51 -abs/-rel* plot relative or absolute error 52 -linear/-log*/-q4 intensity scaling 53 -hist/-nohist* plot histogram of relative error 54 -res=0 sets the resolution width dQ/Q if calculating with resolution 55 -accuracy=Low accuracy of the resolution calculation Low, Mid, High, Xhigh 56 -edit starts the parameter explorer 57 58 Any two calculation engines can be selected for comparison: 59 60 -single/-double/-half/-fast sets an OpenCL calculation engine 61 -single!/-double!/-quad! sets an OpenMP calculation engine 62 -sasview sets the sasview calculation engine 63 64 The default is -single -sasview. Note that the interpretation of quad 65 precision depends on architecture, and may vary from 64-bit to 128-bit, 66 with 80-bit floats being common (1e-19 precision). 67 68 Key=value pairs allow you to set specific values for the model parameters. 69 """ 70 71 # Update docs with command line usage string. This is separate from the usual 72 # doc string so that we can display it at run time if there is an error. 73 # lin 74 __doc__ = __doc__ + """ 75 Program description 76 ------------------- 77 78 """ + USAGE 79 80 3 81 4 82 import sys … … 25 103 # List of available models 26 104 MODELS = [basename(f)[:-3] 27 for f in sorted(glob.glob(joinpath(ROOT, "models","[a-zA-Z]*.py")))]105 for f in sorted(glob.glob(joinpath(ROOT, "models", "[a-zA-Z]*.py")))] 28 106 29 107 # CRUFT python 2.6 … … 76 154 elif p.endswith('_pd_type'): 77 155 return v 78 elif any(s in p for s in ('theta', 'phi','psi')):156 elif any(s in p for s in ('theta', 'phi', 'psi')): 79 157 # orientation in [-180,180], orientation pd in [0,45] 80 158 if p.endswith('_pd'): 81 return [0, 45]159 return [0, 45] 82 160 else: 83 161 return [-180, 180] … … 86 164 elif p.endswith('_pd'): 87 165 return [0, 1] 88 elif p in ['background', 'scale']: 166 elif p == 'background': 167 return [0, 10] 168 elif p == 'scale': 89 169 return [0, 1e3] 170 elif p == 'case_num': 171 # RPA hack 172 return [0, 10] 173 elif v < 0: 174 # Kxy parameters in rpa model can be negative 175 return [2*v, -2*v] 90 176 else: 91 return [0, (2*v if v >0 else 1)]177 return [0, (2*v if v > 0 else 1)] 92 178 93 179 def _randomize_one(p, v): 94 180 """ 95 Randomiz ingparameter.96 """ 97 if any(p.endswith(s) for s in ('_pd_n', '_pd_nsigma','_pd_type')):181 Randomize a single parameter. 182 """ 183 if any(p.endswith(s) for s in ('_pd_n', '_pd_nsigma', '_pd_type')): 98 184 return v 99 185 else: … … 101 187 102 188 def randomize_pars(pars, seed=None): 189 """ 190 Generate random values for all of the parameters. 191 192 Valid ranges for the random number generator are guessed from the name of 193 the parameter; this will not account for constraints such as cap radius 194 greater than cylinder radius in the capped_cylinder model, so 195 :func:`constrain_pars` needs to be called afterward.. 196 """ 103 197 np.random.seed(seed) 104 198 # Note: the sort guarantees order `of calls to random number generator 105 pars = dict((p, _randomize_one(p,v))106 for p, v in sorted(pars.items()))199 pars = dict((p, _randomize_one(p, v)) 200 for p, v in sorted(pars.items())) 107 201 return pars 108 202 … … 110 204 """ 111 205 Restrict parameters to valid values. 206 207 This includes model specific code for models such as capped_cylinder 208 which need to support within model constraints (cap radius more than 209 cylinder radius in this case). 112 210 """ 113 211 name = model_definition.name 114 212 if name == 'capped_cylinder' and pars['cap_radius'] < pars['radius']: 115 pars['radius'], pars['cap_radius'] = pars['cap_radius'],pars['radius']213 pars['radius'], pars['cap_radius'] = pars['cap_radius'], pars['radius'] 116 214 if name == 'barbell' and pars['bell_radius'] < pars['radius']: 117 pars['radius'], pars['bell_radius'] = pars['bell_radius'],pars['radius']215 pars['radius'], pars['bell_radius'] = pars['bell_radius'], pars['radius'] 118 216 119 217 # Limit guinier to an Rg such that Iq > 1e-30 (single precision cutoff) … … 122 220 q_max = 1.0 # high q maximum 123 221 rg_max = np.sqrt(90*np.log(10) + 3*np.log(pars['scale']))/q_max 124 pars['rg'] = min(pars['rg'], rg_max)222 pars['rg'] = min(pars['rg'], rg_max) 125 223 126 224 if name == 'rpa': … … 136 234 137 235 def parlist(pars): 138 return "\n".join("%s: %s"%(p,v) for p,v in sorted(pars.items())) 236 """ 237 Format the parameter list for printing. 238 """ 239 return "\n".join("%s: %s"%(p, v) for p, v in sorted(pars.items())) 139 240 140 241 def suppress_pd(pars): … … 151 252 152 253 def eval_sasview(model_definition, data): 254 """ 255 Return a model calculator using the SasView fitting engine. 256 """ 153 257 # importing sas here so that the error message will be that sas failed to 154 258 # import rather than the more obscure smear_selection not imported error … … 158 262 # convert model parameters from sasmodel form to sasview form 159 263 #print("old",sorted(pars.items())) 160 modelname, pars= revert_model(model_definition, {})161 #print("new",sorted( pars.items()))264 modelname, _ = revert_model(model_definition, {}) 265 #print("new",sorted(_pars.items())) 162 266 sas = __import__('sas.models.'+modelname) 163 ModelClass = getattr(getattr(sas.models, modelname,None),modelname,None)267 ModelClass = getattr(getattr(sas.models, modelname, None), modelname, None) 164 268 if ModelClass is None: 165 269 raise ValueError("could not find model %r in sas.models"%modelname) … … 184 288 185 289 def calculator(**pars): 290 """ 291 Sasview calculator for model. 292 """ 186 293 # paying for parameter conversion each time to keep life simple, if not fast 187 294 _, pars = revert_model(model_definition, pars) 188 for k, v in pars.items():295 for k, v in pars.items(): 189 296 parts = k.split('.') # polydispersity components 190 297 if len(parts) == 2: … … 209 316 } 210 317 def eval_opencl(model_definition, data, dtype='single', cutoff=0.): 318 """ 319 Return a model calculator using the OpenCL calculation engine. 320 """ 211 321 try: 212 322 model = core.load_model(model_definition, dtype=dtype, platform="ocl") … … 221 331 222 332 def eval_ctypes(model_definition, data, dtype='double', cutoff=0.): 223 if dtype=='quad': 333 """ 334 Return a model calculator using the DLL calculation engine. 335 """ 336 if dtype == 'quad': 224 337 dtype = 'longdouble' 225 338 model = core.load_model(model_definition, dtype=dtype, platform="dll") … … 229 342 230 343 def time_calculation(calculator, pars, Nevals=1): 344 """ 345 Compute the average calculation time over N evaluations. 346 347 An additional call is generated without polydispersity in order to 348 initialize the calculation engine, and make the average more stable. 349 """ 231 350 # initialize the code so time is more accurate 232 351 value = calculator(**suppress_pd(pars)) … … 238 357 239 358 def make_data(opts): 359 """ 360 Generate an empty dataset, used with the model to set Q points 361 and resolution. 362 363 *opts* contains the options, with 'qmax', 'nq', 'res', 364 'accuracy', 'is2d' and 'view' parsed from the command line. 365 """ 240 366 qmax, nq, res = opts['qmax'], opts['nq'], opts['res'] 241 367 if opts['is2d']: … … 255 381 256 382 def make_engine(model_definition, data, dtype, cutoff): 383 """ 384 Generate the appropriate calculation engine for the given datatype. 385 386 Datatypes with '!' appended are evaluated using external C DLLs rather 387 than OpenCL. 388 """ 257 389 if dtype == 'sasview': 258 390 return eval_sasview(model_definition, data) … … 265 397 266 398 def compare(opts, limits=None): 267 Nbase, Ncomp = opts['N1'], opts['N2'] 399 """ 400 Preform a comparison using options from the command line. 401 402 *limits* are the limits on the values to use, either to set the y-axis 403 for 1D or to set the colormap scale for 2D. If None, then they are 404 inferred from the data and returned. When exploring using Bumps, 405 the limits are set when the model is initially called, and maintained 406 as the values are adjusted, making it easier to see the effects of the 407 parameters. 408 """ 409 Nbase, Ncomp = opts['n1'], opts['n2'] 268 410 pars = opts['pars'] 269 411 data = opts['data'] … … 296 438 resid = (base_value - comp_value) 297 439 relerr = resid/comp_value 298 _print_stats("|%s - %s|"%(base.engine,comp.engine)+(" "*(3+len(comp.engine))), resid) 299 _print_stats("|(%s - %s) / %s|"%(base.engine,comp.engine,comp.engine), relerr) 440 _print_stats("|%s-%s|"%(base.engine, comp.engine) + (" "*(3+len(comp.engine))), 441 resid) 442 _print_stats("|(%s-%s)/%s|"%(base.engine, comp.engine, comp.engine), 443 relerr) 300 444 301 445 # Plot if requested … … 321 465 if Nbase > 0: plt.subplot(132) 322 466 plot_theory(data, comp_value, view=view, plot_data=False, limits=limits) 323 plt.title("%s t=%.1f ms"%(comp.engine, comp_time))467 plt.title("%s t=%.1f ms"%(comp.engine, comp_time)) 324 468 #cbar_title = "log I" 325 469 if Ncomp > 0 and Nbase > 0: 326 470 plt.subplot(133) 327 471 if '-abs' in opts: 328 err, errstr,errview = resid, "abs err", "linear"472 err, errstr, errview = resid, "abs err", "linear" 329 473 else: 330 err, errstr,errview = abs(relerr), "rel err", "log"474 err, errstr, errview = abs(relerr), "rel err", "log" 331 475 #err,errstr = base/comp,"ratio" 332 476 plot_theory(data, None, resid=err, view=errview, plot_data=False) … … 340 484 plt.figure() 341 485 v = relerr 342 v[v==0] = 0.5*np.min(np.abs(v[v!=0])) 343 plt.hist(np.log10(np.abs(v)), normed=1, bins=50); 344 plt.xlabel('log10(err), err = |(%s - %s) / %s|'%(base.engine, comp.engine, comp.engine)); 486 v[v == 0] = 0.5*np.min(np.abs(v[v != 0])) 487 plt.hist(np.log10(np.abs(v)), normed=1, bins=50) 488 plt.xlabel('log10(err), err = |(%s - %s) / %s|' 489 % (base.engine, comp.engine, comp.engine)) 345 490 plt.ylabel('P(err)') 346 491 plt.title('Distribution of relative error between calculation engines') … … 362 507 "zero-offset:%+.3e"%np.mean(err), 363 508 ] 364 print(label+" " .join(data))509 print(label+" "+" ".join(data)) 365 510 366 511 … … 368 513 # =========================================================================== 369 514 # 370 USAGE="""371 usage: compare.py model N1 N2 [options...] [key=val]372 373 Compare the speed and value for a model between the SasView original and the374 sasmodels rewrite.375 376 model is the name of the model to compare (see below).377 N1 is the number of times to run sasmodels (default=1).378 N2 is the number times to run sasview (default=1).379 380 Options (* for default):381 382 -plot*/-noplot plots or suppress the plot of the model383 -lowq*/-midq/-highq/-exq use q values up to 0.05, 0.2, 1.0, 10.0384 -nq=128 sets the number of Q points in the data set385 -1d*/-2d computes 1d or 2d data386 -preset*/-random[=seed] preset or random parameters387 -mono/-poly* force monodisperse/polydisperse388 -cutoff=1e-5* cutoff value for including a point in polydispersity389 -pars/-nopars* prints the parameter set or not390 -abs/-rel* plot relative or absolute error391 -linear/-log*/-q4 intensity scaling392 -hist/-nohist* plot histogram of relative error393 -res=0 sets the resolution width dQ/Q if calculating with resolution394 -accuracy=Low accuracy of the resolution calculation Low, Mid, High, Xhigh395 -edit starts the parameter explorer396 397 Any two calculation engines can be selected for comparison:398 399 -single/-double/-half/-fast sets an OpenCL calculation engine400 -single!/-double!/-quad! sets an OpenMP calculation engine401 -sasview sets the sasview calculation engine402 403 The default is -single -sasview. Note that the interpretation of quad404 precision depends on architecture, and may vary from 64-bit to 128-bit,405 with 80-bit floats being common (1e-19 precision).406 407 Key=value pairs allow you to set specific values for the model parameters.408 409 Available models:410 """411 412 413 515 NAME_OPTIONS = set([ 414 516 'plot', 'noplot', … … 431 533 432 534 def columnize(L, indent="", width=79): 535 """ 536 Format a list of strings into columns for printing. 537 """ 433 538 column_width = max(len(w) for w in L) + 1 434 539 num_columns = (width - len(indent)) // column_width … … 443 548 444 549 def get_demo_pars(model_definition): 550 """ 551 Extract demo parameters from the model definition. 552 """ 445 553 info = generate.make_info(model_definition) 446 554 # Get the default values for the parameters 447 pars = dict((p[0], p[2]) for p in info['parameters'])555 pars = dict((p[0], p[2]) for p in info['parameters']) 448 556 449 557 # Fill in default values for the polydispersity parameters … … 460 568 461 569 def parse_opts(): 462 flags = [arg for arg in sys.argv[1:] if arg.startswith('-')] 463 values = [arg for arg in sys.argv[1:] if not arg.startswith('-') and '=' in arg] 464 args = [arg for arg in sys.argv[1:] if not arg.startswith('-') and '=' not in arg] 570 """ 571 Parse command line options. 572 """ 573 flags = [arg for arg in sys.argv[1:] 574 if arg.startswith('-')] 575 values = [arg for arg in sys.argv[1:] 576 if not arg.startswith('-') and '=' in arg] 577 args = [arg for arg in sys.argv[1:] 578 if not arg.startswith('-') and '=' not in arg] 465 579 models = "\n ".join("%-15s"%v for v in MODELS) 466 580 if len(args) == 0: 467 581 print(USAGE) 582 print("\nAvailable models:") 468 583 print(columnize(MODELS, indent=" ")) 469 584 sys.exit(1) 470 585 if args[0] not in MODELS: 471 print("Model %r not available. Use one of:\n %s"%(args[0], models))586 print("Model %r not available. Use one of:\n %s"%(args[0], models)) 472 587 sys.exit(1) 473 588 if len(args) > 3: 474 print("expected parameters: model N opencl Nsasview")589 print("expected parameters: model N1 N2") 475 590 476 591 invalid = [o[1:] for o in flags 477 592 if o[1:] not in NAME_OPTIONS 478 and not any(o.startswith('-%s='%t) for t in VALUE_OPTIONS)]593 and not any(o.startswith('-%s='%t) for t in VALUE_OPTIONS)] 479 594 if invalid: 480 595 print("Invalid options: %s"%(", ".join(invalid))) … … 538 653 539 654 if len(engines) == 0: 540 engines.extend(['single', 'sasview'])655 engines.extend(['single', 'sasview']) 541 656 elif len(engines) == 1: 542 657 if engines[0][0] != 'sasview': … … 550 665 model_definition = core.load_model_definition(name) 551 666 552 N1 = int(args[1]) if len(args) > 1 else 1553 N2 = int(args[2]) if len(args) > 2 else 1667 n1 = int(args[1]) if len(args) > 1 else 1 668 n2 = int(args[2]) if len(args) > 2 else 1 554 669 555 670 # Get demo parameters from model definition, or use default parameters … … 583 698 # Create the computational engines 584 699 data, _index = make_data(opts) 585 if N1:700 if n1: 586 701 base = make_engine(model_definition, data, engines[0], opts['cutoff']) 587 702 else: 588 703 base = None 589 if N2:704 if n2: 590 705 comp = make_engine(model_definition, data, engines[1], opts['cutoff']) 591 706 else: … … 596 711 'name' : name, 597 712 'def' : model_definition, 598 ' N1' : N1,599 ' N2' : N2,713 'n1' : n1, 714 'n2' : n2, 600 715 'presets' : presets, 601 716 'pars' : pars, … … 635 750 def __init__(self, opts): 636 751 from bumps.cli import config_matplotlib 637 import bumps_model752 from . import bumps_model 638 753 config_matplotlib() 639 754 self.opts = opts … … 643 758 active = [base + ext 644 759 for base in info['partype']['pd-1d'] 645 for ext in ['', '_pd','_pd_n','_pd_nsigma']]760 for ext in ['', '_pd', '_pd_n', '_pd_nsigma']] 646 761 active.extend(info['partype']['fixed-1d']) 647 762 for k in active: … … 658 773 def numpoints(self): 659 774 """ 660 Return the number of points 775 Return the number of points. 661 776 """ 662 777 return len(self.pars) + 1 # so dof is 1 … … 664 779 def parameters(self): 665 780 """ 666 Return a dictionary of parameters 781 Return a dictionary of parameters. 667 782 """ 668 783 return self.pars 669 784 670 785 def nllf(self): 786 """ 787 Return cost. 788 """ 671 789 return 0. # No nllf 672 790 … … 675 793 Plot the data and residuals. 676 794 """ 677 pars = dict((k, v.value) for k, v in self.pars.items())795 pars = dict((k, v.value) for k, v in self.pars.items()) 678 796 pars.update(self.pd_types) 679 797 self.opts['pars'] = pars -
sasmodels/models/hollow_cylinder.c
r07e72e6 r0420af7 114 114 double norm,volume; //final calculation variables 115 115 116 if (core_radius >= radius || radius >= length) { 117 return NAN; 118 } 119 116 120 delrho = solvent_sld - sld; 117 121 lower = 0.0; … … 132 136 } 133 137 134 //FIXME: Factor of two difference135 138 double Iqxy(double qx, double qy, double radius, double core_radius, double length, double sld, 136 139 double solvent_sld, double theta, double phi) -
sasmodels/models/hollow_cylinder.py
rd18f8a8 r0420af7 64 64 """ 65 65 66 from numpy import inf66 from numpy import pi, inf 67 67 68 68 name = "hollow_cylinder" … … 92 92 source = ["lib/J1.c", "lib/gauss76.c", "hollow_cylinder.c"] 93 93 94 def ER(radius, core_radius, length): 95 if radius == 0 or length == 0: 96 return 0.0 97 len1 = radius 98 len2 = length/2.0 99 term1 = len1*len1*2.0*len2/2.0 100 term2 = 1.0 + (len2/len1)*(1.0 + 1/len2/2.0)*(1.0 + pi*len1/len2/2.0) 101 ddd = 3.0*term1*term2 102 diam = pow(ddd, (1.0/3.0)) 103 return diam 104 105 def VR(radius, core_radius, length): 106 vol_core = pi*core_radius*core_radius*length 107 vol_total = pi*radius*radius*length 108 vol_shell = vol_total - vol_core 109 return vol_shell, vol_total 110 94 111 # parameters for demo 95 112 demo = dict(scale=1.0,background=0.0,length=400.0,radius=30.0,core_radius=20.0, … … 97 114 radius_pd=.2, radius_pd_n=9, 98 115 length_pd=.2, length_pd_n=10, 116 core_radius_pd=.2, core_radius_pd_n=9, 99 117 theta_pd=10, theta_pd_n=5, 100 118 ) … … 109 127 # Parameters for unit tests 110 128 tests = [ 111 [{"radius" : 30.0},0.00005,1764.926] 129 [{"radius" : 30.0},0.00005,1764.926], 130 [{},'VR',1.8], 131 [{},0.001,1756.76] 112 132 ] -
sasmodels/models/lib/Si.c
rdcef2ee r7d256c8 1 int factorial(int f);2 1 double Si(double x); 3 2 4 // integral of sin(x)/x : approximated to w/i1%3 // integral of sin(x)/x Taylor series approximated to w/i 0.1% 5 4 double Si(double x) 6 5 { 7 int i;8 int nmax=6;9 6 double out; 10 long power;11 7 double pi = 4.0*atan(1.0); 12 8 … … 16 12 out = pi/2.0; 17 13 18 for (i=0; i<nmax-2; i+=1) { 19 out_cos += pow(-1.0, i) * (double)factorial(2*i) / pow(x, 2*i+1); 20 out_sin += pow(-1.0, i) * (double)factorial(2*i+1) / pow(x, 2*i+2); 21 } 14 // Explicitly writing factorial values triples the speed of the calculation 15 out_cos = 1/x - 2/pow(x,3) + 24/pow(x,5) - 720/pow(x,7) + 40320/pow(x,9); 16 out_sin = 1/x - 6/pow(x,4) + 120/pow(x,6) - 5040/pow(x,8) + 362880/pow(x,10); 22 17 23 18 out -= cos(x) * out_cos; … … 26 21 } 27 22 28 out = 0.0; 23 // Explicitly writing factorial values triples the speed of the calculation 24 out = x - pow(x, 3)/18 + pow(x,5)/600 - pow(x,7)/35280 + pow(x,9)/3265920; 29 25 30 for (i=0; i<nmax; i+=1) { 31 if (i==0) { 32 out += x; 33 continue; 34 } 35 36 power = pow(x,(2 * i + 1)); 37 out += pow(-1.0, i) * power / ((2.0 * (double)i + 1.0) * (double)factorial(2 * i + 1)); 38 39 //printf ("Si=%g %g %d\n", x, out, i); 40 } 41 26 //printf ("Si=%g %g\n", x, out); 42 27 return out; 43 28 } 44 45 int factorial(int f)46 {47 if ( f == 0 )48 return 1;49 return(f * factorial(f - 1));50 }
Note: See TracChangeset
for help on using the changeset viewer.