Changeset 2afc26d in sasmodels
- Timestamp:
- Apr 8, 2016 12:15:23 PM (9 years ago)
- Branches:
- master, core_shell_microgels, costrafo411, magnetic_model, release_v0.94, release_v0.95, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- a86f6c0
- Parents:
- 4bfd277 (diff), 416609b (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent. - Files:
-
- 6 added
- 44 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/models/elliptical_cylinder.py
rec45c4f r416609b 81 81 """ 82 82 83 import math 84 from numpy import pi, inf 83 from numpy import pi, inf, sqrt 85 84 86 85 name = "elliptical_cylinder" … … 117 116 @param length: Length of the cylinder 118 117 """ 119 radius = math.sqrt(r_minor * r_minor * axis_ratio)118 radius = sqrt(r_minor * r_minor * axis_ratio) 120 119 ddd = 0.75 * radius * (2 * radius * length + (length + radius) * (length + pi * radius)) 121 120 return 0.5 * (ddd) ** (1. / 3.) -
sasmodels/models/line.py
rec45c4f r416609b 9 9 .. math:: 10 10 11 I(q) = A + B \cdot q11 I(q) = \text{scale} (A + B \cdot q) + \text{background} 12 12 13 13 .. note:: … … 15 15 16 16 .. math:: 17 I(q) = I(qx) \cdot I(qy)17 I(q) = \text{scale} (I(qx) \cdot I(qy)) + \text{background} 18 18 19 19 References … … 51 51 """ 52 52 inten = intercept + slope*q 53 # TODO: In SasView code additional formula for list has been specified.54 # if inten(x) = intercept + slope*x:55 # then if q is a list, Iq=inten(x[0]*math.cos(x[1]))*inten(x[0]*math.sin(x[1]))56 53 return inten 57 54 … … 65 62 :return: 2D-Intensity 66 63 """ 67 # TODO: SasView document ion lists 2D intensity as Iq(qx)*Iq(qy) but code says:68 # return self._line(x[1])64 # TODO: SasView documents 2D intensity as Iq(qx)*Iq(qy), but returns Iq(qy) 65 # Note: SasView.run([r, theta]) does return Iq(qx)*Iq(qy) 69 66 return Iq(qx, *args)*Iq(qy, *args) 70 67 71 68 Iqxy.vectorized = True # Iqxy accepts an array of qx, qy values 72 69 73 demo = dict(scale=1.0, background=0, intercept=1.0, slope=1.0)74 75 70 tests = [ 76 77 [{'intercept': 1.0, 78 'slope': 1.0, 79 }, 1.0, 2.001], 80 81 [{'intercept': 1.0, 82 'slope': 1.0, 83 }, 0.0, 1.001], 84 85 [{'intercept': 1.0, 86 'slope': 1.0, 87 }, 0.4, 1.401], 88 89 [{'intercept': 1.0, 90 'slope': 1.0, 91 }, 1.3, 2.301], 92 93 [{'intercept': 1.0, 94 'slope': 1.0, 95 }, 0.5, 1.501], 96 97 [{'intercept': 1.0, 98 'slope': 1.0, 99 }, [0.4, 0.5], [1.401, 1.501]], 100 101 [{'intercept': 1.0, 102 'slope': 1.0, 103 'background': 0.0, 104 }, (1.3, 1.57), 5.911], 71 [{'intercept': 1.0, 'slope': 1.0, }, 1.0, 2.001], 72 [{'intercept': 1.0, 'slope': 1.0, }, 0.0, 1.001], 73 [{'intercept': 1.0, 'slope': 1.0, }, 0.4, 1.401], 74 [{'intercept': 1.0, 'slope': 1.0, }, 1.3, 2.301], 75 [{'intercept': 1.0, 'slope': 1.0, }, 0.5, 1.501], 76 [{'intercept': 1.0, 'slope': 1.0, }, [0.4, 0.5], [1.401, 1.501]], 77 [{'intercept': 1.0, 'slope': 1.0, 'background': 0.0, }, (1.3, 1.57), 5.911], 105 78 ] -
sasmodels/models/onion.py
rea05c87 r2afc26d 382 382 # "A[1]": 0, "A[2]": -1, "A[3]": 1e-4, "A[4]": 1, 383 383 } 384 -
sasmodels/models/polymer_excl_volume.py
rec45c4f r416609b 93 93 """ 94 94 95 from math import sqrt 96 from numpy import inf, power 95 from numpy import inf, power, sqrt 97 96 from scipy.special import gammainc, gamma 98 97 … … 114 113 115 114 116 def Iq(q, 117 rg=60.0, 118 porod_exp=3.0): 115 def Iq(q, rg=60.0, porod_exp=3.0): 119 116 """ 120 117 :param q: Input q-value (float or [float, float]) … … 144 141 :return: 2D-Intensity 145 142 """ 146 147 143 return Iq(sqrt(qx**2 + qy**2), *args) 148 144 149 145 Iqxy.vectorized = True # Iqxy accepts an array of qx, qy values 150 146 151 152 demo = dict(scale=1, background=0.0,153 rg=60.0,154 porod_exp=3.0)155 147 156 148 tests = [ -
sasmodels/models/two_lorentzian.py
rec45c4f r416609b 34 34 """ 35 35 36 from math import sqrt 37 from numpy import inf, power 36 from numpy import inf, power, sqrt 38 37 39 38 name = "two_lorentzian" -
doc/developer/index.rst
r56fc97a rb85be2d 3 3 *************************** 4 4 5 .. toctree:: 6 :numbered: 4 7 :maxdepth: 4 5 8 9 calculator.rst -
sasmodels/bumps_model.py
rea75043 r6d6508e 81 81 from bumps.names import Parameter 82 82 83 pars = {} 84 for p in model_info['parameters']: 83 pars = {} # floating point parameters 84 pd_types = {} # distribution names 85 for p in model_info.parameters.call_parameters: 85 86 value = kwargs.pop(p.name, p.default) 86 87 pars[p.name] = Parameter.default(value, name=p.name, limits=p.limits) 87 for name in model_info['partype']['pd-2d']: 88 for xpart, xdefault, xlimits in [ 89 ('_pd', 0., pars[name].limits), 90 ('_pd_n', 35., (0, 1000)), 91 ('_pd_nsigma', 3., (0, 10)), 92 ]: 93 xname = name + xpart 94 xvalue = kwargs.pop(xname, xdefault) 95 pars[xname] = Parameter.default(xvalue, name=xname, limits=xlimits) 96 97 pd_types = {} 98 for name in model_info['partype']['pd-2d']: 99 xname = name + '_pd_type' 100 xvalue = kwargs.pop(xname, 'gaussian') 101 pd_types[xname] = xvalue 88 if p.polydisperse: 89 for part, default, limits in [ 90 ('_pd', 0., pars[p.name].limits), 91 ('_pd_n', 35., (0, 1000)), 92 ('_pd_nsigma', 3., (0, 10)), 93 ]: 94 name = p.name + part 95 value = kwargs.pop(name, default) 96 pars[name] = Parameter.default(value, name=name, limits=limits) 97 name = p.name + '_pd_type' 98 pd_types[name] = kwargs.pop(name, 'gaussian') 102 99 103 100 if kwargs: # args not corresponding to parameters -
sasmodels/compare.py
rf247314 r6d6508e 38 38 from . import core 39 39 from . import kerneldll 40 from . import product41 40 from .data import plot_theory, empty_data1D, empty_data2D 42 41 from .direct_model import DirectModel … … 210 209 Choose a parameter range based on parameter name and initial value. 211 210 """ 211 # process the polydispersity options 212 212 if p.endswith('_pd_n'): 213 213 return [0, 100] … … 222 222 else: 223 223 return [-180, 180] 224 elif p.endswith('_pd'): 225 return [0, 1] 224 226 elif 'sld' in p: 225 227 return [-0.5, 10] 226 elif p.endswith('_pd'):227 return [0, 1]228 228 elif p == 'background': 229 229 return [0, 10] 230 230 elif p == 'scale': 231 231 return [0, 1e3] 232 elif p == 'case_num':233 # RPA hack234 return [0, 10]235 232 elif v < 0: 236 # Kxy parameters in rpa model can be negative237 233 return [2*v, -2*v] 238 234 else: … … 240 236 241 237 242 def _randomize_one( p, v):238 def _randomize_one(model_info, p, v): 243 239 """ 244 240 Randomize a single parameter. … … 246 242 if any(p.endswith(s) for s in ('_pd_n', '_pd_nsigma', '_pd_type')): 247 243 return v 244 245 # Find the parameter definition 246 for par in model_info.parameters.call_parameters: 247 if par.name == p: 248 break 248 249 else: 249 return np.random.uniform(*parameter_range(p, v)) 250 251 252 def randomize_pars(pars, seed=None): 250 raise ValueError("unknown parameter %r"%p) 251 252 if len(par.limits) > 2: # choice list 253 return np.random.randint(len(par.limits)) 254 255 limits = par.limits 256 if not np.isfinite(limits).all(): 257 low, high = parameter_range(p, v) 258 limits = (max(limits[0], low), min(limits[1], high)) 259 260 return np.random.uniform(*limits) 261 262 263 def randomize_pars(model_info, pars, seed=None): 253 264 """ 254 265 Generate random values for all of the parameters. … … 261 272 with push_seed(seed): 262 273 # Note: the sort guarantees order `of calls to random number generator 263 pars = dict((p, _randomize_one( p, v))274 pars = dict((p, _randomize_one(model_info, p, v)) 264 275 for p, v in sorted(pars.items())) 265 276 return pars … … 273 284 cylinder radius in this case). 274 285 """ 275 name = model_info ['id']286 name = model_info.id 276 287 # if it is a product model, then just look at the form factor since 277 288 # none of the structure factors need any constraints. … … 294 305 # Make sure phi sums to 1.0 295 306 if pars['case_num'] < 2: 296 pars['Phi a'] = 0.297 pars['Phi b'] = 0.307 pars['Phi1'] = 0. 308 pars['Phi2'] = 0. 298 309 elif pars['case_num'] < 5: 299 pars['Phi a'] = 0.300 total = sum(pars['Phi'+c] for c in ' abcd')301 for c in ' abcd':310 pars['Phi1'] = 0. 311 total = sum(pars['Phi'+c] for c in '1234') 312 for c in '1234': 302 313 pars['Phi'+c] /= total 303 314 … … 306 317 Format the parameter list for printing. 307 318 """ 308 if is2d:309 exclude = lambda n: False310 else:311 partype = model_info['partype']312 par1d = set(partype['fixed-1d']+partype['pd-1d'])313 exclude = lambda n: n not in par1d314 319 lines = [] 315 for p in model_info['parameters']:316 if exclude(p.name): continue320 parameters = model_info.parameters 321 for p in parameters.user_parameters(pars, is2d): 317 322 fields = dict( 318 value=pars.get(p. name, p.default),319 pd=pars.get(p. name+"_pd", 0.),320 n=int(pars.get(p. name+"_pd_n", 0)),321 nsigma=pars.get(p. name+"_pd_nsgima", 3.),322 type=pars.get(p. name+"_pd_type", 'gaussian'))323 value=pars.get(p.id, p.default), 324 pd=pars.get(p.id+"_pd", 0.), 325 n=int(pars.get(p.id+"_pd_n", 0)), 326 nsigma=pars.get(p.id+"_pd_nsgima", 3.), 327 type=pars.get(p.id+"_pd_type", 'gaussian')) 323 328 lines.append(_format_par(p.name, **fields)) 324 329 return "\n".join(lines) … … 363 368 364 369 # grab the sasview model, or create it if it is a product model 365 if model_info ['composition']:366 composition_type, parts = model_info ['composition']370 if model_info.composition: 371 composition_type, parts = model_info.composition 367 372 if composition_type == 'product': 368 373 from sas.models.MultiplicationModel import MultiplicationModel … … 454 459 """ 455 460 # initialize the code so time is more accurate 456 value = calculator(**suppress_pd(pars)) 461 if Nevals > 1: 462 value = calculator(**suppress_pd(pars)) 457 463 toc = tic() 458 464 for _ in range(max(Nevals, 1)): # make sure there is at least one eval … … 661 667 """ 662 668 # Get the default values for the parameters 663 pars = dict((p.name, p.default) for p in model_info['parameters']) 664 665 # Fill in default values for the polydispersity parameters 666 for p in model_info['parameters']: 667 if p.type in ('volume', 'orientation'): 668 pars[p.name+'_pd'] = 0.0 669 pars[p.name+'_pd_n'] = 0 670 pars[p.name+'_pd_nsigma'] = 3.0 671 pars[p.name+'_pd_type'] = "gaussian" 669 pars = {} 670 for p in model_info.parameters.call_parameters: 671 parts = [('', p.default)] 672 if p.polydisperse: 673 parts.append(('_pd', 0.0)) 674 parts.append(('_pd_n', 0)) 675 parts.append(('_pd_nsigma', 3.0)) 676 parts.append(('_pd_type', "gaussian")) 677 for ext, val in parts: 678 if p.length > 1: 679 dict(("%s%d%s"%(p.id,k,ext), val) for k in range(p.length)) 680 else: 681 pars[p.id+ext] = val 672 682 673 683 # Plug in values given in demo 674 684 if use_demo: 675 pars.update(model_info ['demo'])685 pars.update(model_info.demo) 676 686 return pars 677 687 … … 774 784 775 785 if len(engines) == 0: 776 engines.extend(['single', ' sasview'])786 engines.extend(['single', 'double']) 777 787 elif len(engines) == 1: 778 if engines[0][0] != ' sasview':779 engines.append(' sasview')788 if engines[0][0] != 'double': 789 engines.append('double') 780 790 else: 781 791 engines.append('single') … … 807 817 #pars.update(set_pars) # set value before random to control range 808 818 if opts['seed'] > -1: 809 pars = randomize_pars( pars, seed=opts['seed'])819 pars = randomize_pars(model_info, pars, seed=opts['seed']) 810 820 print("Randomize using -random=%i"%opts['seed']) 811 821 if opts['mono']: … … 879 889 model_info = opts['def'] 880 890 pars, pd_types = bumps_model.create_parameters(model_info, **opts['pars']) 891 # Initialize parameter ranges, fixing the 2D parameters for 1D data. 881 892 if not opts['is2d']: 882 active = [base + ext 883 for base in model_info['partype']['pd-1d'] 884 for ext in ['', '_pd', '_pd_n', '_pd_nsigma']] 885 active.extend(model_info['partype']['fixed-1d']) 886 for k in active: 887 v = pars[k] 888 v.range(*parameter_range(k, v.value)) 893 for p in model_info.parameters.user_parameters(is2d=False): 894 for ext in ['', '_pd', '_pd_n', '_pd_nsigma']: 895 k = p.name+ext 896 v = pars.get(k, None) 897 if v is not None: 898 v.range(*parameter_range(k, v.value)) 889 899 else: 890 900 for k, v in pars.items(): -
sasmodels/compare_many.py
rce346b6 r8bd7b77 20 20 21 21 from . import core 22 from . import generate23 22 from .compare import (MODELS, randomize_pars, suppress_pd, make_data, 24 23 make_engine, get_pars, columnize, … … 109 108 110 109 if is_2d: 111 if not model_info[' has_2d']:110 if not model_info['parameters'].has_2d: 112 111 print(',"1-D only"') 113 112 return … … 125 124 try: 126 125 result = fn(**pars) 127 except KeyboardInterrupt: 128 raise 129 except: 126 except Exception: 130 127 traceback.print_exc() 131 128 print("when comparing %s for %d"%(name, seed)) … … 246 243 base = sys.argv[5] 247 244 comp = sys.argv[6] if len(sys.argv) > 6 else "sasview" 248 except :245 except Exception: 249 246 traceback.print_exc() 250 247 print_usage() -
sasmodels/convert.json
rec45c4f r6e7ff6d 602 602 "RPAModel", 603 603 { 604 "N1": "Na", "N2": "Nb", "N3": "Nc", "N4": "Nd", 605 "L1": "La", "L2": "Lb", "L3": "Lc", "L4": "Ld", 606 "v1": "va", "v2": "vb", "v3": "vc", "v4": "vd", 607 "b1": "ba", "b2": "bb", "b3": "bc", "b4": "bd", 608 "Phi1": "Phia", "Phi2": "Phib", "Phi3": "Phic", "Phi4": "Phid", 604 609 "case_num": "lcase_n" 605 610 } … … 620 625 } 621 626 ], 627 "_spherepy": [ 628 "SphereModel", 629 { 630 "sld": "sldSph", 631 "radius": "radius", 632 "sld_solvent": "sldSolv" 633 } 634 ], 622 635 "spherical_sld": [ 623 636 "SphereSLDModel", 624 637 { 638 "n": "n_shells", 625 639 "radius_core": "rad_core", 626 640 "sld_solvent": "sld_solv" -
sasmodels/convert.py
rf247314 r6d6508e 153 153 def revert_name(model_info): 154 154 _read_conversion_table() 155 oldname, oldpars = CONVERSION_TABLE.get(model_info ['id'], [None, {}])155 oldname, oldpars = CONVERSION_TABLE.get(model_info.id, [None, {}]) 156 156 return oldname 157 157 158 158 def _get_old_pars(model_info): 159 159 _read_conversion_table() 160 oldname, oldpars = CONVERSION_TABLE.get(model_info ['id'], [None, {}])160 oldname, oldpars = CONVERSION_TABLE.get(model_info.id, [None, {}]) 161 161 return oldpars 162 162 … … 165 165 Convert model from new style parameter names to old style. 166 166 """ 167 if model_info ['composition']is not None:168 composition_type, parts = model_info ['composition']167 if model_info.composition is not None: 168 composition_type, parts = model_info.composition 169 169 if composition_type == 'product': 170 170 P, S = parts … … 180 180 181 181 # Note: update compare.constrain_pars to match 182 name = model_info ['id']183 if name in MODELS_WITHOUT_SCALE or model_info ['structure_factor']:182 name = model_info.id 183 if name in MODELS_WITHOUT_SCALE or model_info.structure_factor: 184 184 if oldpars.pop('scale', 1.0) != 1.0: 185 185 warnings.warn("parameter scale not used in sasview %s"%name) 186 if name in MODELS_WITHOUT_BACKGROUND or model_info ['structure_factor']:186 if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor: 187 187 if oldpars.pop('background', 0.0) != 0.0: 188 188 warnings.warn("parameter background not used in sasview %s"%name) … … 206 206 elif name == 'rpa': 207 207 # convert scattering lengths from femtometers to centimeters 208 for p in "L a", "Lb", "Lc", "Ld":208 for p in "L1", "L2", "L3", "L4": 209 209 if p in oldpars: oldpars[p] *= 1e-13 210 210 elif name == 'core_shell_parallelepiped': … … 225 225 Restrict parameter values to those that will match sasview. 226 226 """ 227 name = model_info ['id']227 name = model_info.id 228 228 # Note: update convert.revert_model to match 229 if name in MODELS_WITHOUT_SCALE or model_info ['structure_factor']:229 if name in MODELS_WITHOUT_SCALE or model_info.structure_factor: 230 230 pars['scale'] = 1 231 if name in MODELS_WITHOUT_BACKGROUND or model_info ['structure_factor']:231 if name in MODELS_WITHOUT_BACKGROUND or model_info.structure_factor: 232 232 pars['background'] = 0 233 233 # sasview multiplies background by structure factor -
sasmodels/core.py
r4d76711 r6d6508e 2 2 Core model handling routines. 3 3 """ 4 from __future__ import print_function 5 4 6 __all__ = [ 5 "list_models", "load_model _info", "precompile_dll",6 "build_model", " make_kernel", "call_kernel", "call_ER_VR",7 "list_models", "load_model", "load_model_info", 8 "build_model", "precompile_dll", 7 9 ] 8 10 9 from os.path import basename, dirname, join as joinpath , splitext11 from os.path import basename, dirname, join as joinpath 10 12 from glob import glob 11 13 12 14 import numpy as np 13 15 14 from . import models15 from . import weights16 16 from . import generate 17 # TODO: remove circular references between product and core 18 # product uses call_ER/call_VR, core uses make_product_info/ProductModel 19 #from . import product 17 from . import modelinfo 18 from . import product 20 19 from . import mixture 21 20 from . import kernelpy … … 24 23 from . import kernelcl 25 24 HAVE_OPENCL = True 26 except :25 except Exception: 27 26 HAVE_OPENCL = False 28 29 try:30 np.meshgrid([])31 meshgrid = np.meshgrid32 except ValueError:33 # CRUFT: np.meshgrid requires multiple vectors34 def meshgrid(*args):35 if len(args) > 1:36 return np.meshgrid(*args)37 else:38 return [np.asarray(v) for v in args]39 27 40 28 # TODO: refactor composite model support … … 64 52 """ 65 53 try: s + '' 66 except : return False54 except Exception: return False 67 55 return True 68 56 … … 88 76 parts = model_name.split('*') 89 77 if len(parts) > 1: 90 from . import product91 # Note: currently have circular reference92 78 if len(parts) > 2: 93 79 raise ValueError("use P*S to apply structure factor S to model P") … … 96 82 97 83 kernel_module = generate.load_kernel_module(model_name) 98 return generate.make_model_info(kernel_module)84 return modelinfo.make_model_info(kernel_module) 99 85 100 86 … … 118 104 otherwise it uses the default "ocl". 119 105 """ 120 composition = model_info. get('composition', None)106 composition = model_info.composition 121 107 if composition is not None: 122 108 composition_type, parts = composition … … 137 123 ## 4. rerun "python -m sasmodels.direct_model $MODELNAME" 138 124 ## 5. uncomment open().read() so that source will be regenerated from model 139 # open(model_info ['name']+'.c','w').write(source)140 # source = open(model_info ['name']+'.cl','r').read()125 # open(model_info.name+'.c','w').write(source) 126 # source = open(model_info.name+'.cl','r').read() 141 127 source = generate.make_source(model_info) 142 128 if dtype is None: 143 dtype = 'single' if model_info ['single']else 'double'144 if callable(model_info. get('Iq', None)):129 dtype = 'single' if model_info.single else 'double' 130 if callable(model_info.Iq): 145 131 return kernelpy.PyModel(model_info) 146 132 if (platform == "dll" … … 168 154 source = generate.make_source(model_info) 169 155 return kerneldll.make_dll(source, model_info, dtype=dtype) if source else None 170 171 172 def get_weights(model_info, pars, name):173 """174 Generate the distribution for parameter *name* given the parameter values175 in *pars*.176 177 Uses "name", "name_pd", "name_pd_type", "name_pd_n", "name_pd_sigma"178 from the *pars* dictionary for parameter value and parameter dispersion.179 """180 relative = name in model_info['partype']['pd-rel']181 limits = model_info['limits'][name]182 disperser = pars.get(name+'_pd_type', 'gaussian')183 value = pars.get(name, model_info['defaults'][name])184 npts = pars.get(name+'_pd_n', 0)185 width = pars.get(name+'_pd', 0.0)186 nsigma = pars.get(name+'_pd_nsigma', 3.0)187 value, weight = weights.get_weights(188 disperser, npts, width, nsigma, value, limits, relative)189 return value, weight / np.sum(weight)190 191 def dispersion_mesh(pars):192 """193 Create a mesh grid of dispersion parameters and weights.194 195 Returns [p1,p2,...],w where pj is a vector of values for parameter j196 and w is a vector containing the products for weights for each197 parameter set in the vector.198 """199 value, weight = zip(*pars)200 value = [v.flatten() for v in meshgrid(*value)]201 weight = np.vstack([v.flatten() for v in meshgrid(*weight)])202 weight = np.prod(weight, axis=0)203 return value, weight204 205 def call_kernel(kernel, pars, cutoff=0, mono=False):206 """207 Call *kernel* returned from *model.make_kernel* with parameters *pars*.208 209 *cutoff* is the limiting value for the product of dispersion weights used210 to perform the multidimensional dispersion calculation more quickly at a211 slight cost to accuracy. The default value of *cutoff=0* integrates over212 the entire dispersion cube. Using *cutoff=1e-5* can be 50% faster, but213 with an error of about 1%, which is usually less than the measurement214 uncertainty.215 216 *mono* is True if polydispersity should be set to none on all parameters.217 """218 fixed_pars = [pars.get(name, kernel.info['defaults'][name])219 for name in kernel.fixed_pars]220 if mono:221 pd_pars = [( np.array([pars[name]]), np.array([1.0]) )222 for name in kernel.pd_pars]223 else:224 pd_pars = [get_weights(kernel.info, pars, name) for name in kernel.pd_pars]225 return kernel(fixed_pars, pd_pars, cutoff=cutoff)226 227 def call_ER_VR(model_info, vol_pars):228 """229 Return effect radius and volume ratio for the model.230 231 *info* is either *kernel.info* for *kernel=make_kernel(model,q)*232 or *model.info*.233 234 *pars* are the parameters as expected by :func:`call_kernel`.235 """236 ER = model_info.get('ER', None)237 VR = model_info.get('VR', None)238 value, weight = dispersion_mesh(vol_pars)239 240 individual_radii = ER(*value) if ER else 1.0241 whole, part = VR(*value) if VR else (1.0, 1.0)242 243 effect_radius = np.sum(weight*individual_radii) / np.sum(weight)244 volume_ratio = np.sum(weight*part)/np.sum(weight*whole)245 return effect_radius, volume_ratio246 247 248 def call_ER(model_info, values):249 """250 Call the model ER function using *values*. *model_info* is either251 *model.info* if you have a loaded model, or *kernel.info* if you252 have a model kernel prepared for evaluation.253 """254 ER = model_info.get('ER', None)255 if ER is None:256 return 1.0257 else:258 vol_pars = [get_weights(model_info, values, name)259 for name in model_info['partype']['volume']]260 value, weight = dispersion_mesh(vol_pars)261 individual_radii = ER(*value)262 #print(values[0].shape, weights.shape, fv.shape)263 return np.sum(weight*individual_radii) / np.sum(weight)264 265 def call_VR(model_info, values):266 """267 Call the model VR function using *pars*.268 *info* is either *model.info* if you have a loaded model, or *kernel.info*269 if you have a model kernel prepared for evaluation.270 """271 VR = model_info.get('VR', None)272 if VR is None:273 return 1.0274 else:275 vol_pars = [get_weights(model_info, values, name)276 for name in model_info['partype']['volume']]277 value, weight = dispersion_mesh(vol_pars)278 whole, part = VR(*value)279 return np.sum(weight*part)/np.sum(weight*whole)280 281 # TODO: remove call_ER, call_VR282 -
sasmodels/data.py
rd6f5da6 ree8f734 358 358 try: 359 359 return fn(*args, **kw) 360 except KeyboardInterrupt: 361 raise 362 except: 360 except Exception: 363 361 traceback.print_exc() 364 362 -
sasmodels/direct_model.py
r4d76711 r6d6508e 25 25 import numpy as np 26 26 27 from .core import call_kernel, call_ER_VR28 27 from . import sesans 28 from . import weights 29 29 from . import resolution 30 30 from . import resolution2d 31 from . import details 32 33 34 def call_kernel(kernel, pars, cutoff=0, mono=False): 35 """ 36 Call *kernel* returned from *model.make_kernel* with parameters *pars*. 37 38 *cutoff* is the limiting value for the product of dispersion weights used 39 to perform the multidimensional dispersion calculation more quickly at a 40 slight cost to accuracy. The default value of *cutoff=0* integrates over 41 the entire dispersion cube. Using *cutoff=1e-5* can be 50% faster, but 42 with an error of about 1%, which is usually less than the measurement 43 uncertainty. 44 45 *mono* is True if polydispersity should be set to none on all parameters. 46 """ 47 parameters = kernel.info.parameters 48 if mono: 49 active = lambda name: False 50 elif kernel.dim == '1d': 51 active = lambda name: name in parameters.pd_1d 52 elif kernel.dim == '2d': 53 active = lambda name: name in parameters.pd_2d 54 else: 55 active = lambda name: True 56 57 vw_pairs = [(get_weights(p, pars) if active(p.name) 58 else ([pars.get(p.name, p.default)], [])) 59 for p in parameters.call_parameters] 60 61 call_details, weights, values = details.build_details(kernel, vw_pairs) 62 return kernel(call_details, weights, values, cutoff) 63 64 def get_weights(parameter, values): 65 """ 66 Generate the distribution for parameter *name* given the parameter values 67 in *pars*. 68 69 Uses "name", "name_pd", "name_pd_type", "name_pd_n", "name_pd_sigma" 70 from the *pars* dictionary for parameter value and parameter dispersion. 71 """ 72 value = float(values.get(parameter.name, parameter.default)) 73 relative = parameter.relative_pd 74 limits = parameter.limits 75 disperser = values.get(parameter.name+'_pd_type', 'gaussian') 76 npts = values.get(parameter.name+'_pd_n', 0) 77 width = values.get(parameter.name+'_pd', 0.0) 78 nsigma = values.get(parameter.name+'_pd_nsigma', 3.0) 79 if npts == 0 or width == 0: 80 return [value], [] 81 value, weight = weights.get_weights( 82 disperser, npts, width, nsigma, value, limits, relative) 83 return value, weight / np.sum(weight) 31 84 32 85 class DataMixin(object): … … 67 120 self.data_type = 'Iq' 68 121 69 partype = model.info['partype']70 71 122 if self.data_type == 'sesans': 72 123 … … 82 133 q_mono = sesans.make_all_q(data) 83 134 elif self.data_type == 'Iqxy': 84 if not partype['orientation'] and not partype['magnetic']:85 raise ValueError("not 2D without orientation or magnetic parameters")135 #if not model.info.parameters.has_2d: 136 # raise ValueError("not 2D without orientation or magnetic parameters") 86 137 q = np.sqrt(data.qx_data**2 + data.qy_data**2) 87 138 qmin = getattr(data, 'qmin', 1e-16) … … 172 223 def _calc_theory(self, pars, cutoff=0.0): 173 224 if self._kernel is None: 174 self._kernel = self._model.make_kernel(self._kernel_inputs) # pylint: disable=attribute-dedata_type225 self._kernel = self._model.make_kernel(self._kernel_inputs) 175 226 self._kernel_mono = (self._model.make_kernel(self._kernel_mono_inputs) 176 227 if self._kernel_mono_inputs else None) … … 214 265 return self._calc_theory(pars, cutoff=self.cutoff) 215 266 216 def ER_VR(self, **pars):217 """218 Compute the equivalent radius and volume ratio for the model.219 """220 return call_ER_VR(self.model.info, pars)221 222 267 def simulate_data(self, noise=None, **pars): 223 268 """ … … 243 288 try: 244 289 values = [float(v) for v in call.split(',')] 245 except :290 except Exception: 246 291 values = [] 247 292 if len(values) == 1: -
sasmodels/generate.py
re6408d0 r6d6508e 21 21 22 22 *VR(p1, p2, ...)* returns the volume ratio for core-shell style forms. 23 24 #define INVALID(v) (expr) returns False if v.parameter is invalid 25 for some parameter or other (e.g., v.bell_radius < v.radius). If 26 necessary, the expression can call a function. 23 27 24 28 These functions are defined in a kernel module .py script and an associated … … 65 69 The constructor code will generate the necessary vectors for computing 66 70 them with the desired polydispersity. 67 68 The available kernel parameters are defined as a list, with each parameter69 defined as a sublist with the following elements:70 71 *name* is the name that will be used in the call to the kernel72 function and the name that will be displayed to the user. Names73 should be lower case, with words separated by underscore. If74 acronyms are used, the whole acronym should be upper case.75 76 *units* should be one of *degrees* for angles, *Ang* for lengths,77 *1e-6/Ang^2* for SLDs.78 79 *default value* will be the initial value for the model when it80 is selected, or when an initial value is not otherwise specified.81 82 *limits = [lb, ub]* are the hard limits on the parameter value, used to83 limit the polydispersity density function. In the fit, the parameter limits84 given to the fit are the limits on the central value of the parameter.85 If there is polydispersity, it will evaluate parameter values outside86 the fit limits, but not outside the hard limits specified in the model.87 If there are no limits, use +/-inf imported from numpy.88 89 *type* indicates how the parameter will be used. "volume" parameters90 will be used in all functions. "orientation" parameters will be used91 in *Iqxy* and *Imagnetic*. "magnetic* parameters will be used in92 *Imagnetic* only. If *type* is the empty string, the parameter will93 be used in all of *Iq*, *Iqxy* and *Imagnetic*. "sld" parameters94 can automatically be promoted to magnetic parameters, each of which95 will have a magnitude and a direction, which may be different from96 other sld parameters.97 98 *description* is a short description of the parameter. This will99 be displayed in the parameter table and used as a tool tip for the100 parameter value in the user interface.101 102 71 The kernel module must set variables defining the kernel meta data: 103 72 … … 156 125 An *model_info* dictionary is constructed from the kernel meta data and 157 126 returned to the caller. 158 159 The model evaluator, function call sequence consists of q inputs and the return vector,160 followed by the loop value/weight vector, followed by the values for161 the non-polydisperse parameters, followed by the lengths of the162 polydispersity loops. To construct the call for 1D models, the163 categories *fixed-1d* and *pd-1d* list the names of the parameters164 of the non-polydisperse and the polydisperse parameters respectively.165 Similarly, *fixed-2d* and *pd-2d* provide parameter names for 2D models.166 The *pd-rel* category is a set of those parameters which give167 polydispersitiy as a portion of the value (so a 10% length dispersity168 would use a polydispersity value of 0.1) rather than absolute169 dispersity such as an angle plus or minus 15 degrees.170 171 The *volume* category lists the volume parameters in order for calls172 to volume within the kernel (used for volume normalization) and for173 calls to ER and VR for effective radius and volume ratio respectively.174 175 The *orientation* and *magnetic* categories list the orientation and176 magnetic parameters. These are used by the sasview interface. The177 blank category is for parameters such as scale which don't have any178 other marking.179 127 180 128 The doc string at the start of the kernel module will be used to … … 204 152 from __future__ import print_function 205 153 206 #TODO: identify model files which have changed since loading and reload them.207 154 #TODO: determine which functions are useful outside of generate 208 155 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 209 156 210 import sys 211 from os.path import abspath, dirname, join as joinpath, exists, basename, \ 212 splitext 157 from os.path import abspath, dirname, join as joinpath, exists, getmtime 213 158 import re 214 159 import string 215 160 import warnings 216 from collections import namedtuple217 161 218 162 import numpy as np 219 163 164 from .modelinfo import Parameter 220 165 from .custom import load_custom_kernel_module 221 166 222 PARAMETER_FIELDS = ['name', 'units', 'default', 'limits', 'type', 'description'] 223 Parameter = namedtuple('Parameter', PARAMETER_FIELDS) 224 225 C_KERNEL_TEMPLATE_PATH = joinpath(dirname(__file__), 'kernel_template.c') 167 TEMPLATE_ROOT = dirname(__file__) 226 168 227 169 F16 = np.dtype('float16') … … 232 174 except TypeError: 233 175 F128 = None 234 235 # Scale and background, which are parameters common to every form factor236 COMMON_PARAMETERS = [237 ["scale", "", 1, [0, np.inf], "", "Source intensity"],238 ["background", "1/cm", 1e-3, [0, np.inf], "", "Source background"],239 ]240 176 241 177 # Conversion from units defined in the parameter table for each model … … 331 267 raise ValueError("%r not found in %s" % (filename, search_path)) 332 268 269 333 270 def model_sources(model_info): 334 271 """ 335 272 Return a list of the sources file paths for the module. 336 273 """ 337 search_path = [dirname(model_info ['filename']),274 search_path = [dirname(model_info.filename), 338 275 abspath(joinpath(dirname(__file__), 'models'))] 339 return [_search(search_path, f) for f in model_info['source']] 340 341 # Pragmas for enable OpenCL features. Be sure to protect them so that they 342 # still compile even if OpenCL is not present. 343 _F16_PRAGMA = """\ 344 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp16) 345 # pragma OPENCL EXTENSION cl_khr_fp16: enable 346 #endif 347 """ 348 349 _F64_PRAGMA = """\ 350 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp64) 351 # pragma OPENCL EXTENSION cl_khr_fp64: enable 352 #endif 353 """ 276 return [_search(search_path, f) for f in model_info.source] 277 278 def timestamp(model_info): 279 """ 280 Return a timestamp for the model corresponding to the most recently 281 changed file or dependency. 282 """ 283 source_files = (model_sources(model_info) 284 + model_templates() 285 + [model_info.filename]) 286 newest = max(getmtime(f) for f in source_files) 287 return newest 354 288 355 289 def convert_type(source, dtype): … … 362 296 if dtype == F16: 363 297 fbytes = 2 364 source = _ F16_PRAGMA + _convert_type(source, "half", "f")298 source = _convert_type(source, "half", "f") 365 299 elif dtype == F32: 366 300 fbytes = 4 … … 368 302 elif dtype == F64: 369 303 fbytes = 8 370 source = _F64_PRAGMA + source # Sourceis already double304 # no need to convert if it is already double 371 305 elif dtype == F128: 372 306 fbytes = 16 … … 399 333 Name of the exported kernel symbol. 400 334 """ 401 return model_info ['name']+ "_" + ("Iqxy" if is_2d else "Iq")335 return model_info.name + "_" + ("Iqxy" if is_2d else "Iq") 402 336 403 337 … … 411 345 412 346 413 LOOP_OPEN = """\ 414 for (int %(name)s_i=0; %(name)s_i < N%(name)s; %(name)s_i++) { 415 const double %(name)s = loops[2*(%(name)s_i%(offset)s)]; 416 const double %(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];\ 347 _template_cache = {} 348 def load_template(filename): 349 path = joinpath(TEMPLATE_ROOT, filename) 350 mtime = getmtime(path) 351 if filename not in _template_cache or mtime > _template_cache[filename][0]: 352 with open(path) as fid: 353 _template_cache[filename] = (mtime, fid.read(), path) 354 return _template_cache[filename][1] 355 356 def model_templates(): 357 # TODO: fails DRY; templates are listed in two places. 358 # should instead have model_info contain a list of paths 359 return [joinpath(TEMPLATE_ROOT, filename) 360 for filename in ('kernel_header.c', 'kernel_iq.c')] 361 362 363 _FN_TEMPLATE = """\ 364 double %(name)s(%(pars)s); 365 double %(name)s(%(pars)s) { 366 %(body)s 367 } 368 369 417 370 """ 418 def build_polydispersity_loops(pd_pars): 419 """ 420 Build polydispersity loops 421 422 Returns loop opening and loop closing 423 """ 424 depth = 4 425 offset = "" 426 loop_head = [] 427 loop_end = [] 428 for name in pd_pars: 429 subst = {'name': name, 'offset': offset} 430 loop_head.append(indent(LOOP_OPEN % subst, depth)) 431 loop_end.insert(0, (" "*depth) + "}") 432 offset += '+N' + name 433 depth += 2 434 return "\n".join(loop_head), "\n".join(loop_end) 435 436 C_KERNEL_TEMPLATE = None 371 372 def _gen_fn(name, pars, body): 373 """ 374 Generate a function given pars and body. 375 376 Returns the following string:: 377 378 double fn(double a, double b, ...); 379 double fn(double a, double b, ...) { 380 .... 381 } 382 """ 383 par_decl = ', '.join(p.as_function_argument() for p in pars) if pars else 'void' 384 return _FN_TEMPLATE % {'name': name, 'body': body, 'pars': par_decl} 385 386 def _call_pars(prefix, pars): 387 """ 388 Return a list of *prefix.parameter* from parameter items. 389 """ 390 return [p.as_call_reference(prefix) for p in pars] 391 392 _IQXY_PATTERN = re.compile("^((inline|static) )? *(double )? *Iqxy *([(]|$)", 393 flags=re.MULTILINE) 394 def _have_Iqxy(sources): 395 """ 396 Return true if any file defines Iqxy. 397 398 Note this is not a C parser, and so can be easily confused by 399 non-standard syntax. Also, it will incorrectly identify the following 400 as having Iqxy:: 401 402 /* 403 double Iqxy(qx, qy, ...) { ... fill this in later ... } 404 */ 405 406 If you want to comment out an Iqxy function, use // on the front of the 407 line instead. 408 """ 409 for code in sources: 410 if _IQXY_PATTERN.search(code): 411 return True 412 else: 413 return False 414 437 415 def make_source(model_info): 438 416 """ … … 441 419 Uses source files found in the given search path. 442 420 """ 443 if callable(model_info ['Iq']):421 if callable(model_info.Iq): 444 422 return None 445 423 … … 453 431 # for computing volume even if we allow non-disperse volume parameters. 454 432 455 # Load template 456 global C_KERNEL_TEMPLATE 457 if C_KERNEL_TEMPLATE is None: 458 with open(C_KERNEL_TEMPLATE_PATH) as fid: 459 C_KERNEL_TEMPLATE = fid.read() 460 461 # Load additional sources 462 source = [open(f).read() for f in model_sources(model_info)] 463 464 # Prepare defines 465 defines = [] 466 partype = model_info['partype'] 467 pd_1d = partype['pd-1d'] 468 pd_2d = partype['pd-2d'] 469 fixed_1d = partype['fixed-1d'] 470 fixed_2d = partype['fixed-1d'] 471 472 iq_parameters = [p.name 473 for p in model_info['parameters'][2:] # skip scale, background 474 if p.name in set(fixed_1d + pd_1d)] 475 iqxy_parameters = [p.name 476 for p in model_info['parameters'][2:] # skip scale, background 477 if p.name in set(fixed_2d + pd_2d)] 478 volume_parameters = [p.name 479 for p in model_info['parameters'] 480 if p.type == 'volume'] 481 482 # Fill in defintions for volume parameters 483 if volume_parameters: 484 defines.append(('VOLUME_PARAMETERS', 485 ','.join(volume_parameters))) 486 defines.append(('VOLUME_WEIGHT_PRODUCT', 487 '*'.join(p + '_w' for p in volume_parameters))) 488 489 # Generate form_volume function from body only 490 if model_info['form_volume'] is not None: 491 if volume_parameters: 492 vol_par_decl = ', '.join('double ' + p for p in volume_parameters) 493 else: 494 vol_par_decl = 'void' 495 defines.append(('VOLUME_PARAMETER_DECLARATIONS', 496 vol_par_decl)) 497 fn = """\ 498 double form_volume(VOLUME_PARAMETER_DECLARATIONS); 499 double form_volume(VOLUME_PARAMETER_DECLARATIONS) { 500 %(body)s 501 } 502 """ % {'body':model_info['form_volume']} 503 source.append(fn) 504 505 # Fill in definitions for Iq parameters 506 defines.append(('IQ_KERNEL_NAME', model_info['name'] + '_Iq')) 507 defines.append(('IQ_PARAMETERS', ', '.join(iq_parameters))) 508 if fixed_1d: 509 defines.append(('IQ_FIXED_PARAMETER_DECLARATIONS', 510 ', \\\n '.join('const double %s' % p for p in fixed_1d))) 511 if pd_1d: 512 defines.append(('IQ_WEIGHT_PRODUCT', 513 '*'.join(p + '_w' for p in pd_1d))) 514 defines.append(('IQ_DISPERSION_LENGTH_DECLARATIONS', 515 ', \\\n '.join('const int N%s' % p for p in pd_1d))) 516 defines.append(('IQ_DISPERSION_LENGTH_SUM', 517 '+'.join('N' + p for p in pd_1d))) 518 open_loops, close_loops = build_polydispersity_loops(pd_1d) 519 defines.append(('IQ_OPEN_LOOPS', 520 open_loops.replace('\n', ' \\\n'))) 521 defines.append(('IQ_CLOSE_LOOPS', 522 close_loops.replace('\n', ' \\\n'))) 523 if model_info['Iq'] is not None: 524 defines.append(('IQ_PARAMETER_DECLARATIONS', 525 ', '.join('double ' + p for p in iq_parameters))) 526 fn = """\ 527 double Iq(double q, IQ_PARAMETER_DECLARATIONS); 528 double Iq(double q, IQ_PARAMETER_DECLARATIONS) { 529 %(body)s 530 } 531 """ % {'body':model_info['Iq']} 532 source.append(fn) 533 534 # Fill in definitions for Iqxy parameters 535 defines.append(('IQXY_KERNEL_NAME', model_info['name'] + '_Iqxy')) 536 defines.append(('IQXY_PARAMETERS', ', '.join(iqxy_parameters))) 537 if fixed_2d: 538 defines.append(('IQXY_FIXED_PARAMETER_DECLARATIONS', 539 ', \\\n '.join('const double %s' % p for p in fixed_2d))) 540 if pd_2d: 541 defines.append(('IQXY_WEIGHT_PRODUCT', 542 '*'.join(p + '_w' for p in pd_2d))) 543 defines.append(('IQXY_DISPERSION_LENGTH_DECLARATIONS', 544 ', \\\n '.join('const int N%s' % p for p in pd_2d))) 545 defines.append(('IQXY_DISPERSION_LENGTH_SUM', 546 '+'.join('N' + p for p in pd_2d))) 547 open_loops, close_loops = build_polydispersity_loops(pd_2d) 548 defines.append(('IQXY_OPEN_LOOPS', 549 open_loops.replace('\n', ' \\\n'))) 550 defines.append(('IQXY_CLOSE_LOOPS', 551 close_loops.replace('\n', ' \\\n'))) 552 if model_info['Iqxy'] is not None: 553 defines.append(('IQXY_PARAMETER_DECLARATIONS', 554 ', '.join('double ' + p for p in iqxy_parameters))) 555 fn = """\ 556 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS); 557 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS) { 558 %(body)s 559 } 560 """ % {'body':model_info['Iqxy']} 561 source.append(fn) 562 563 # Need to know if we have a theta parameter for Iqxy; it is not there 564 # for the magnetic sphere model, for example, which has a magnetic 565 # orientation but no shape orientation. 566 if 'theta' in pd_2d: 567 defines.append(('IQXY_HAS_THETA', '1')) 568 569 #for d in defines: print(d) 570 defines = '\n'.join('#define %s %s' % (k, v) for k, v in defines) 571 sources = '\n\n'.join(source) 572 return C_KERNEL_TEMPLATE % { 573 'DEFINES': defines, 574 'SOURCES': sources, 575 } 576 577 def categorize_parameters(pars): 578 """ 579 Build parameter categories out of the the parameter definitions. 580 581 Returns a dictionary of categories. 582 583 Note: these categories are subject to change, depending on the needs of 584 the UI and the needs of the kernel calling function. 585 586 The categories are as follows: 587 588 * *volume* list of volume parameter names 589 * *orientation* list of orientation parameters 590 * *magnetic* list of magnetic parameters 591 * *<empty string>* list of parameters that have no type info 592 593 Each parameter is in one and only one category. 594 595 The following derived categories are created: 596 597 * *fixed-1d* list of non-polydisperse parameters for 1D models 598 * *pd-1d* list of polydisperse parameters for 1D models 599 * *fixed-2d* list of non-polydisperse parameters for 2D models 600 * *pd-d2* list of polydisperse parameters for 2D models 601 """ 602 partype = { 603 'volume': [], 'orientation': [], 'magnetic': [], 'sld': [], '': [], 604 'fixed-1d': [], 'fixed-2d': [], 'pd-1d': [], 'pd-2d': [], 605 'pd-rel': set(), 606 } 607 608 for p in pars: 609 if p.type == 'volume': 610 partype['pd-1d'].append(p.name) 611 partype['pd-2d'].append(p.name) 612 partype['pd-rel'].add(p.name) 613 elif p.type == 'magnetic': 614 partype['fixed-2d'].append(p.name) 615 elif p.type == 'orientation': 616 partype['pd-2d'].append(p.name) 617 elif p.type in ('', 'sld'): 618 partype['fixed-1d'].append(p.name) 619 partype['fixed-2d'].append(p.name) 620 else: 621 raise ValueError("unknown parameter type %r" % p.type) 622 partype[p.type].append(p.name) 623 624 return partype 625 626 def process_parameters(model_info): 627 """ 628 Process parameter block, precalculating parameter details. 629 """ 630 # convert parameters into named tuples 631 for p in model_info['parameters']: 632 if p[4] == '' and (p[0].startswith('sld') or p[0].endswith('sld')): 633 p[4] = 'sld' 634 # TODO: make sure all models explicitly label their sld parameters 635 #raise ValueError("%s.%s needs to be explicitly set to type 'sld'" %(model_info['id'], p[0])) 636 637 pars = [Parameter(*p) for p in model_info['parameters']] 638 # Fill in the derived attributes 639 model_info['parameters'] = pars 640 partype = categorize_parameters(pars) 641 model_info['limits'] = dict((p.name, p.limits) for p in pars) 642 model_info['partype'] = partype 643 model_info['defaults'] = dict((p.name, p.default) for p in pars) 644 if model_info.get('demo', None) is None: 645 model_info['demo'] = model_info['defaults'] 646 model_info['has_2d'] = partype['orientation'] or partype['magnetic'] 647 433 partable = model_info.parameters 434 435 # Identify parameters for Iq, Iqxy, Iq_magnetic and form_volume. 436 # Note that scale and volume are not possible types. 437 438 # Load templates and user code 439 kernel_header = load_template('kernel_header.c') 440 kernel_code = load_template('kernel_iq.c') 441 user_code = [open(f).read() for f in model_sources(model_info)] 442 443 # Build initial sources 444 source = [kernel_header] + user_code 445 446 # Make parameters for q, qx, qy so that we can use them in declarations 447 q, qx, qy = [Parameter(name=v) for v in ('q', 'qx', 'qy')] 448 # Generate form_volume function, etc. from body only 449 if model_info.form_volume is not None: 450 pars = partable.form_volume_parameters 451 source.append(_gen_fn('form_volume', pars, model_info.form_volume)) 452 if model_info.Iq is not None: 453 pars = [q] + partable.iq_parameters 454 source.append(_gen_fn('Iq', pars, model_info.Iq)) 455 if model_info.Iqxy is not None: 456 pars = [qx, qy] + partable.iqxy_parameters 457 source.append(_gen_fn('Iqxy', pars, model_info.Iqxy)) 458 459 # Define the parameter table 460 source.append("#define PARAMETER_TABLE \\") 461 source.append("\\\n".join(p.as_definition() 462 for p in partable.kernel_parameters)) 463 464 # Define the function calls 465 if partable.form_volume_parameters: 466 refs = _call_pars("_v.", partable.form_volume_parameters) 467 call_volume = "#define CALL_VOLUME(_v) form_volume(%s)" % (",".join(refs)) 468 else: 469 # Model doesn't have volume. We could make the kernel run a little 470 # faster by not using/transferring the volume normalizations, but 471 # the ifdef's reduce readability more than is worthwhile. 472 call_volume = "#define CALL_VOLUME(v) 1.0" 473 source.append(call_volume) 474 475 refs = ["_q[_i]"] + _call_pars("_v.", partable.iq_parameters) 476 call_iq = "#define CALL_IQ(_q,_i,_v) Iq(%s)" % (",".join(refs)) 477 if _have_Iqxy(user_code): 478 # Call 2D model 479 refs = ["q[2*i]", "q[2*i+1]"] + _call_pars("_v.", partable.iqxy_parameters) 480 call_iqxy = "#define CALL_IQ(_q,_i,_v) Iqxy(%s)" % (",".join(refs)) 481 else: 482 # Call 1D model with sqrt(qx^2 + qy^2) 483 warnings.warn("Creating Iqxy = Iq(sqrt(qx^2 + qy^2))") 484 # still defined:: refs = ["q[i]"] + _call_pars("v", iq_parameters) 485 pars_sqrt = ["sqrt(_q[2*_i]*_q[2*_i]+_q[2*_i+1]*_q[2*_i+1])"] + refs[1:] 486 call_iqxy = "#define CALL_IQ(_q,_i,_v) Iq(%s)" % (",".join(pars_sqrt)) 487 488 # Fill in definitions for numbers of parameters 489 source.append("#define MAX_PD %s"%partable.max_pd) 490 source.append("#define NPARS %d"%partable.npars) 491 492 # TODO: allow mixed python/opencl kernels? 493 494 # define the Iq kernel 495 source.append("#define KERNEL_NAME %s_Iq"%model_info.name) 496 source.append(call_iq) 497 source.append(kernel_code) 498 source.append("#undef CALL_IQ") 499 source.append("#undef KERNEL_NAME") 500 501 # define the Iqxy kernel from the same source with different #defines 502 source.append("#define KERNEL_NAME %s_Iqxy"%model_info.name) 503 source.append(call_iqxy) 504 source.append(kernel_code) 505 source.append("#undef CALL_IQ") 506 source.append("#undef KERNEL_NAME") 507 508 return '\n'.join(source) 648 509 649 510 def load_kernel_module(model_name): … … 657 518 658 519 659 def make_model_info(kernel_module):660 """661 Interpret the model definition file, categorizing the parameters.662 663 The module can be loaded with a normal python import statement if you664 know which module you need, or with __import__('sasmodels.model.'+name)665 if the name is in a string.666 667 The *model_info* structure contains the following fields:668 669 * *id* is the id of the kernel670 * *name* is the display name of the kernel671 * *filename* is the full path to the module defining the file (if any)672 * *title* is a short description of the kernel673 * *description* is a long description of the kernel (this doesn't seem674 very useful since the Help button on the model page brings you directly675 to the documentation page)676 * *docs* is the docstring from the module. Use :func:`make_doc` to677 * *category* specifies the model location in the docs678 * *parameters* is the model parameter table679 * *single* is True if the model allows single precision680 * *structure_factor* is True if the model is useable in a product681 * *variant_info* contains the information required to select between682 model variants (e.g., the list of cases) or is None if there are no683 model variants684 * *defaults* is the *{parameter: value}* table built from the parameter685 description table.686 * *limits* is the *{parameter: [min, max]}* table built from the687 parameter description table.688 * *partypes* categorizes the model parameters. See689 :func:`categorize_parameters` for details.690 * *demo* contains the *{parameter: value}* map used in compare (and maybe691 for the demo plot, if plots aren't set up to use the default values).692 If *demo* is not given in the file, then the default values will be used.693 * *tests* is a set of tests that must pass694 * *source* is the list of library files to include in the C model build695 * *Iq*, *Iqxy*, *form_volume*, *ER*, *VR* and *sesans* are python functions696 implementing the kernel for the module, or None if they are not697 defined in python698 * *composition* is None if the model is independent, otherwise it is a699 tuple with composition type ('product' or 'mixture') and a list of700 *model_info* blocks for the composition objects. This allows us to701 build complete product and mixture models from just the info.702 703 """704 # TODO: maybe turn model_info into a class ModelDefinition705 parameters = COMMON_PARAMETERS + kernel_module.parameters706 filename = abspath(kernel_module.__file__)707 kernel_id = splitext(basename(filename))[0]708 name = getattr(kernel_module, 'name', None)709 if name is None:710 name = " ".join(w.capitalize() for w in kernel_id.split('_'))711 model_info = dict(712 id=kernel_id, # string used to load the kernel713 filename=abspath(kernel_module.__file__),714 name=name,715 title=kernel_module.title,716 description=kernel_module.description,717 parameters=parameters,718 composition=None,719 docs=kernel_module.__doc__,720 category=getattr(kernel_module, 'category', None),721 single=getattr(kernel_module, 'single', True),722 structure_factor=getattr(kernel_module, 'structure_factor', False),723 variant_info=getattr(kernel_module, 'invariant_info', None),724 demo=getattr(kernel_module, 'demo', None),725 source=getattr(kernel_module, 'source', []),726 tests=getattr(kernel_module, 'tests', []),727 )728 process_parameters(model_info)729 # Check for optional functions730 functions = "ER VR form_volume Iq Iqxy shape sesans".split()731 model_info.update((k, getattr(kernel_module, k, None)) for k in functions)732 return model_info733 520 734 521 section_marker = re.compile(r'\A(?P<first>[%s])(?P=first)*\Z' … … 771 558 Iq_units = "The returned value is scaled to units of |cm^-1| |sr^-1|, absolute scale." 772 559 Sq_units = "The returned value is a dimensionless structure factor, $S(q)$." 773 docs = convert_section_titles_to_boldface(model_info ['docs'])774 subst = dict(id=model_info ['id'].replace('_', '-'),775 name=model_info ['name'],776 title=model_info ['title'],777 parameters=make_partable(model_info ['parameters']),778 returns=Sq_units if model_info ['structure_factor']else Iq_units,560 docs = convert_section_titles_to_boldface(model_info.docs) 561 subst = dict(id=model_info.id.replace('_', '-'), 562 name=model_info.name, 563 title=model_info.title, 564 parameters=make_partable(model_info.parameters), 565 returns=Sq_units if model_info.structure_factor else Iq_units, 779 566 docs=docs) 780 567 return DOC_HEADER % subst … … 785 572 Show how long it takes to process a model. 786 573 """ 574 import datetime 575 from .modelinfo import make_model_info 787 576 from .models import cylinder 788 import datetime 577 789 578 tic = datetime.datetime.now() 790 579 make_source(make_model_info(cylinder)) … … 796 585 Program which prints the source produced by the model. 797 586 """ 587 import sys 588 from .modelinfo import make_model_info 589 798 590 if len(sys.argv) <= 1: 799 591 print("usage: python -m sasmodels.generate modelname") -
sasmodels/kernelcl.py
r4d76711 r6d6508e 48 48 harmless, albeit annoying. 49 49 """ 50 from __future__ import print_function 50 51 import os 51 52 import warnings … … 54 55 55 56 try: 57 raise NotImplementedError("OpenCL not yet implemented for new kernel template") 56 58 import pyopencl as cl 57 59 # Ask OpenCL for the default context so that we know that one exists … … 73 75 # of polydisperse parameters. 74 76 MAX_LOOPS = 2048 77 78 79 # Pragmas for enable OpenCL features. Be sure to protect them so that they 80 # still compile even if OpenCL is not present. 81 _F16_PRAGMA = """\ 82 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp16) 83 # pragma OPENCL EXTENSION cl_khr_fp16: enable 84 #endif 85 """ 86 87 _F64_PRAGMA = """\ 88 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp64) 89 # pragma OPENCL EXTENSION cl_khr_fp64: enable 90 #endif 91 """ 75 92 76 93 … … 142 159 raise RuntimeError("%s not supported for devices"%dtype) 143 160 144 source = generate.convert_type(source, dtype) 161 source_list = [generate.convert_type(source, dtype)] 162 163 if dtype == generate.F16: 164 source_list.insert(0, _F16_PRAGMA) 165 elif dtype == generate.F64: 166 source_list.insert(0, _F64_PRAGMA) 167 145 168 # Note: USE_SINCOS makes the intel cpu slower under opencl 146 169 if context.devices[0].type == cl.device_type.GPU: 147 source = "#define USE_SINCOS\n" + source170 source_list.insert(0, "#define USE_SINCOS\n") 148 171 options = (get_fast_inaccurate_build_options(context.devices[0]) 149 172 if fast else []) 173 source = "\n".join(source_list) 150 174 program = cl.Program(context, source).build(options=options) 151 175 return program … … 220 244 key = "%s-%s-%s"%(name, dtype, fast) 221 245 if key not in self.compiled: 222 #print("compiling",name)246 print("compiling",name) 223 247 dtype = np.dtype(dtype) 224 program = compile_model(self.get_context(dtype), source, dtype, fast) 248 program = compile_model(self.get_context(dtype), 249 str(source), dtype, fast) 225 250 self.compiled[key] = program 226 251 return self.compiled[key] … … 317 342 if self.program is None: 318 343 compiler = environment().compile_program 319 self.program = compiler(self.info ['name'], self.source, self.dtype,320 self. fast)344 self.program = compiler(self.info.name, self.source, 345 self.dtype, self.fast) 321 346 is_2d = len(q_vectors) == 2 322 347 kernel_name = generate.kernel_name(self.info, is_2d) … … 329 354 """ 330 355 if self.program is not None: 331 environment().release_program(self.info ['name'])356 environment().release_program(self.info.name) 332 357 self.program = None 333 358 … … 365 390 # at this point, so instead using 32, which is good on the set of 366 391 # architectures tested so far. 367 self.q_vectors = [_stretch_input(q, self.dtype, 32) for q in q_vectors] 392 if self.is_2d: 393 # Note: 17 rather than 15 because results is 2 elements 394 # longer than input. 395 width = ((self.nq+17)//16)*16 396 self.q = np.empty((width, 2), dtype=dtype) 397 self.q[:self.nq, 0] = q_vectors[0] 398 self.q[:self.nq, 1] = q_vectors[1] 399 else: 400 # Note: 33 rather than 31 because results is 2 elements 401 # longer than input. 402 width = ((self.nq+33)//32)*32 403 self.q = np.empty(width, dtype=dtype) 404 self.q[:self.nq] = q_vectors[0] 405 self.global_size = [self.q.shape[0]] 368 406 context = env.get_context(self.dtype) 369 self.global_size = [self.q_vectors[0].size]370 407 #print("creating inputs of size", self.global_size) 371 self.q_buffers = [ 372 cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=q) 373 for q in self.q_vectors 374 ] 408 self.q_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 409 hostbuf=self.q) 375 410 376 411 def release(self): … … 378 413 Free the memory. 379 414 """ 380 for b in self.q_buffers:381 b.release()382 self.q_buffers = []415 if self.q is not None: 416 self.q.release() 417 self.q = None 383 418 384 419 def __del__(self): … … 406 441 """ 407 442 def __init__(self, kernel, model_info, q_vectors, dtype): 443 max_pd = model_info.max_pd 444 npars = len(model_info.parameters)-2 408 445 q_input = GpuInput(q_vectors, dtype) 446 self.dtype = dtype 447 self.dim = '2d' if q_input.is_2d else '1d' 409 448 self.kernel = kernel 410 449 self.info = model_info 411 self.res = np.empty(q_input.nq, q_input.dtype) 412 dim = '2d' if q_input.is_2d else '1d' 413 self.fixed_pars = model_info['partype']['fixed-' + dim] 414 self.pd_pars = model_info['partype']['pd-' + dim] 450 self.pd_stop_index = 4*max_pd-1 451 # plus three for the normalization values 452 self.result = np.empty(q_input.nq+3, q_input.dtype) 415 453 416 454 # Inputs and outputs for each kernel call … … 418 456 env = environment() 419 457 self.queue = env.get_queue(dtype) 420 self.loops_b = cl.Buffer(self.queue.context, mf.READ_WRITE, 421 2 * MAX_LOOPS * q_input.dtype.itemsize) 422 self.res_b = cl.Buffer(self.queue.context, mf.READ_WRITE, 458 459 # details is int32 data, padded to an 8 integer boundary 460 size = ((max_pd*5 + npars*3 + 2 + 7)//8)*8 461 self.result_b = cl.Buffer(self.queue.context, mf.READ_WRITE, 423 462 q_input.global_size[0] * q_input.dtype.itemsize) 424 self.q_input = q_input 425 426 self._need_release = [ self.loops_b, self.res_b, self.q_input]427 428 def __call__(self, fixed_pars, pd_pars, cutoff):463 self.q_input = q_input # allocated by GpuInput above 464 465 self._need_release = [ self.result_b, self.q_input ] 466 467 def __call__(self, call_details, weights, values, cutoff): 429 468 real = (np.float32 if self.q_input.dtype == generate.F32 430 469 else np.float64 if self.q_input.dtype == generate.F64 431 470 else np.float16 if self.q_input.dtype == generate.F16 432 471 else np.float32) # will never get here, so use np.float32 433 434 #print "pars", fixed_pars, pd_pars 435 res_bi = self.res_b 436 nq = np.uint32(self.q_input.nq) 437 if pd_pars: 438 cutoff = real(cutoff) 439 loops_N = [np.uint32(len(p[0])) for p in pd_pars] 440 loops = np.hstack(pd_pars) \ 441 if pd_pars else np.empty(0, dtype=self.q_input.dtype) 442 loops = np.ascontiguousarray(loops.T, self.q_input.dtype).flatten() 443 #print("loops",Nloops, loops) 444 445 #import sys; print("opencl eval",pars) 446 #print("opencl eval",pars) 447 if len(loops) > 2 * MAX_LOOPS: 448 raise ValueError("too many polydispersity points") 449 450 loops_bi = self.loops_b 451 cl.enqueue_copy(self.queue, loops_bi, loops) 452 loops_l = cl.LocalMemory(len(loops.data)) 453 #ctx = environment().context 454 #loops_bi = cl.Buffer(ctx, mf.READ_ONLY|mf.COPY_HOST_PTR, hostbuf=loops) 455 dispersed = [loops_bi, loops_l, cutoff] + loops_N 456 else: 457 dispersed = [] 458 fixed = [real(p) for p in fixed_pars] 459 args = self.q_input.q_buffers + [res_bi, nq] + dispersed + fixed 472 assert call_details.dtype == np.int32 473 assert weights.dtype == real and values.dtype == real 474 475 context = self.queue.context 476 details_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 477 hostbuf=call_details) 478 weights_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 479 hostbuf=weights) 480 values_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 481 hostbuf=values) 482 483 start, stop = 0, self.details[self.pd_stop_index] 484 args = [ 485 np.uint32(self.q_input.nq), np.uint32(start), np.uint32(stop), 486 self.details_b, self.weights_b, self.values_b, 487 self.q_input.q_b, self.result_b, real(cutoff), 488 ] 460 489 self.kernel(self.queue, self.q_input.global_size, None, *args) 461 cl.enqueue_copy(self.queue, self.res, res_bi) 462 463 return self.res 490 cl.enqueue_copy(self.queue, self.result, self.result_b) 491 [v.release() for v in details_b, weights_b, values_b] 492 493 return self.result[:self.nq] 464 494 465 495 def release(self): -
sasmodels/kerneldll.py
r4d76711 r6d6508e 50 50 import tempfile 51 51 import ctypes as ct 52 from ctypes import c_void_p, c_int, c_longdouble, c_double, c_float 53 import _ctypes 52 from ctypes import c_void_p, c_int32, c_longdouble, c_double, c_float 54 53 55 54 import numpy as np 56 55 57 56 from . import generate 57 from . import details 58 58 from .kernelpy import PyInput, PyModel 59 59 from .exception import annotate_exception … … 81 81 COMPILE = "gcc -shared -fPIC -std=c99 -O2 -Wall %(source)s -o %(output)s -lm" 82 82 if "SAS_OPENMP" in os.environ: 83 COMPILE = COMPILE +" -fopenmp"83 COMPILE += " -fopenmp" 84 84 else: 85 85 COMPILE = "cc -shared -fPIC -fopenmp -std=c99 -O2 -Wall %(source)s -o %(output)s -lm" … … 90 90 91 91 92 def dll_ path(model_info, dtype="double"):93 """ 94 Path to the compiled model defined by *model_info*.95 """96 from os.path import join as joinpath, split as splitpath, splitext97 b asename = splitext(splitpath(model_info['filename'])[1])[0]98 if np.dtype(dtype) == generate.F32:99 basename += "32" 100 elif np.dtype(dtype) == generate.F64:101 basename += "64"102 else:103 basename += "128"104 return joinpath(DLL_PATH, basename+'.so')105 92 def dll_name(model_info, dtype): 93 """ 94 Name of the dll containing the model. This is the base file name without 95 any path or extension, with a form such as 'sas_sphere32'. 96 """ 97 bits = 8*dtype.itemsize 98 return "sas_%s%d"%(model_info.id, bits) 99 100 def dll_path(model_info, dtype): 101 """ 102 Complete path to the dll for the model. Note that the dll may not 103 exist yet if it hasn't been compiled. 104 """ 105 return os.path.join(DLL_PATH, dll_name(model_info, dtype)+".so") 106 106 107 107 def make_dll(source, model_info, dtype="double"): … … 124 124 models are allowed as DLLs. 125 125 """ 126 if callable(model_info. get('Iq', None)):126 if callable(model_info.Iq): 127 127 return PyModel(model_info) 128 128 … … 133 133 dtype = generate.F64 # Force 64-bit dll 134 134 135 if dtype == generate.F32: # 32-bit dll136 tempfile_prefix = 'sas_' + model_info['name'] + '32_'137 elif dtype == generate.F64:138 tempfile_prefix = 'sas_' + model_info['name'] + '64_'139 else:140 tempfile_prefix = 'sas_' + model_info['name'] + '128_'141 142 135 source = generate.convert_type(source, dtype) 143 source_files = generate.model_sources(model_info) + [model_info['filename']]136 newest = generate.timestamp(model_info) 144 137 dll = dll_path(model_info, dtype) 145 newest = max(os.path.getmtime(f) for f in source_files)146 138 if not os.path.exists(dll) or os.path.getmtime(dll) < newest: 147 # Replace with a proper temp file148 fid, filename = tempfile.mkstemp(suffix=".c", prefix= tempfile_prefix)139 basename = dll_name(model_info, dtype) + "_" 140 fid, filename = tempfile.mkstemp(suffix=".c", prefix=basename) 149 141 os.fdopen(fid, "w").write(source) 150 142 command = COMPILE%{"source":filename, "output":dll} … … 171 163 return DllModel(filename, model_info, dtype=dtype) 172 164 173 174 IQ_ARGS = [c_void_p, c_void_p, c_int]175 IQXY_ARGS = [c_void_p, c_void_p, c_void_p, c_int]176 177 165 class DllModel(object): 178 166 """ … … 197 185 198 186 def _load_dll(self): 199 Nfixed1d = len(self.info['partype']['fixed-1d'])200 Nfixed2d = len(self.info['partype']['fixed-2d'])201 Npd1d = len(self.info['partype']['pd-1d'])202 Npd2d = len(self.info['partype']['pd-2d'])203 204 187 #print("dll", self.dllpath) 205 188 try: … … 212 195 else c_double if self.dtype == generate.F64 213 196 else c_longdouble) 214 pd_args_1d = [c_void_p, fp] + [c_int]*Npd1d if Npd1d else [] 215 pd_args_2d = [c_void_p, fp] + [c_int]*Npd2d if Npd2d else [] 197 198 # int, int, int, int*, double*, double*, double*, double*, double*, double 199 argtypes = [c_int32]*3 + [c_void_p]*5 + [fp] 216 200 self.Iq = self.dll[generate.kernel_name(self.info, False)] 217 self.Iq.argtypes = IQ_ARGS + pd_args_1d + [fp]*Nfixed1d218 219 201 self.Iqxy = self.dll[generate.kernel_name(self.info, True)] 220 self.Iqxy.argtypes = IQXY_ARGS + pd_args_2d + [fp]*Nfixed2d 221 222 self.release() 202 self.Iq.argtypes = argtypes 203 self.Iqxy.argtypes = argtypes 223 204 224 205 def __getstate__(self): … … 272 253 """ 273 254 def __init__(self, kernel, model_info, q_input): 255 self.kernel = kernel 274 256 self.info = model_info 275 257 self.q_input = q_input 276 self.kernel = kernel 277 self.res = np.empty(q_input.nq, q_input.dtype) 278 dim = '2d' if q_input.is_2d else '1d' 279 self.fixed_pars = model_info['partype']['fixed-' + dim] 280 self.pd_pars = model_info['partype']['pd-' + dim] 281 282 # In dll kernel, but not in opencl kernel 283 self.p_res = self.res.ctypes.data 284 285 def __call__(self, fixed_pars, pd_pars, cutoff): 258 self.dtype = q_input.dtype 259 self.dim = '2d' if q_input.is_2d else '1d' 260 self.result = np.empty(q_input.nq+3, q_input.dtype) 261 262 def __call__(self, call_details, weights, values, cutoff): 286 263 real = (np.float32 if self.q_input.dtype == generate.F32 287 264 else np.float64 if self.q_input.dtype == generate.F64 288 265 else np.float128) 289 290 nq = c_int(self.q_input.nq) 291 if pd_pars: 292 cutoff = real(cutoff) 293 loops_N = [np.uint32(len(p[0])) for p in pd_pars] 294 loops = np.hstack(pd_pars) 295 loops = np.ascontiguousarray(loops.T, self.q_input.dtype).flatten() 296 p_loops = loops.ctypes.data 297 dispersed = [p_loops, cutoff] + loops_N 298 else: 299 dispersed = [] 300 fixed = [real(p) for p in fixed_pars] 301 args = self.q_input.q_pointers + [self.p_res, nq] + dispersed + fixed 302 #print(pars) 266 assert isinstance(call_details, details.CallDetails) 267 assert weights.dtype == real and values.dtype == real 268 269 start, stop = 0, call_details.total_pd 270 #print("in kerneldll") 271 #print("weights", weights) 272 #print("values", values) 273 args = [ 274 self.q_input.nq, # nq 275 start, # pd_start 276 stop, # pd_stop pd_stride[MAX_PD] 277 call_details.ctypes.data, # problem 278 weights.ctypes.data, # weights 279 values.ctypes.data, #pars 280 self.q_input.q.ctypes.data, #q 281 self.result.ctypes.data, # results 282 real(cutoff), # cutoff 283 ] 303 284 self.kernel(*args) 304 305 return self.res 285 return self.result[:-3] 306 286 307 287 def release(self): -
sasmodels/kernelpy.py
r4d76711 r4bfd277 10 10 from numpy import pi, cos 11 11 12 from . import details 12 13 from .generate import F64 13 14 … … 17 18 """ 18 19 def __init__(self, model_info): 20 # Make sure Iq and Iqxy are available and vectorized 21 _create_default_functions(model_info) 19 22 self.info = model_info 20 23 21 24 def make_kernel(self, q_vectors): 22 25 q_input = PyInput(q_vectors, dtype=F64) 23 kernel = self.info ['Iqxy'] if q_input.is_2d else self.info['Iq']26 kernel = self.info.Iqxy if q_input.is_2d else self.info.Iq 24 27 return PyKernel(kernel, self.info, q_input) 25 28 … … 53 56 self.dtype = dtype 54 57 self.is_2d = (len(q_vectors) == 2) 55 self.q_vectors = [np.ascontiguousarray(q, self.dtype) for q in q_vectors] 56 self.q_pointers = [q.ctypes.data for q in self.q_vectors] 58 if self.is_2d: 59 self.q = np.empty((self.nq, 2), dtype=dtype) 60 self.q[:, 0] = q_vectors[0] 61 self.q[:, 1] = q_vectors[1] 62 else: 63 self.q = np.empty(self.nq, dtype=dtype) 64 self.q[:self.nq] = q_vectors[0] 57 65 58 66 def release(self): … … 60 68 Free resources associated with the model inputs. 61 69 """ 62 self.q _vectors = []70 self.q = None 63 71 64 72 class PyKernel(object): … … 82 90 """ 83 91 def __init__(self, kernel, model_info, q_input): 92 self.dtype = np.dtype('d') 84 93 self.info = model_info 85 94 self.q_input = q_input 86 95 self.res = np.empty(q_input.nq, q_input.dtype) 87 dim = '2d' if q_input.is_2d else '1d' 88 # Loop over q unless user promises that the kernel is vectorized by 89 # taggining it with vectorized=True 90 if not getattr(kernel, 'vectorized', False): 91 if dim == '2d': 92 def vector_kernel(qx, qy, *args): 93 """ 94 Vectorized 2D kernel. 95 """ 96 return np.array([kernel(qxi, qyi, *args) 97 for qxi, qyi in zip(qx, qy)]) 96 self.kernel = kernel 97 self.dim = '2d' if q_input.is_2d else '1d' 98 99 partable = model_info.parameters 100 kernel_parameters = (partable.iqxy_parameters if q_input.is_2d 101 else partable.iq_parameters) 102 volume_parameters = partable.form_volume_parameters 103 104 # Create an array to hold the parameter values. There will be a 105 # single array whose values are updated as the calculator goes 106 # through the loop. Arguments to the kernel and volume functions 107 # will use views into this vector, relying on the fact that a 108 # an array of no dimensions acts like a scalar. 109 parameter_vector = np.empty(len(partable.call_parameters)-2, 'd') 110 111 # Create views into the array to hold the arguments 112 offset = 0 113 kernel_args, volume_args = [], [] 114 for p in partable.kernel_parameters: 115 if p.length == 1: 116 # Scalar values are length 1 vectors with no dimensions. 117 v = parameter_vector[offset:offset+1].reshape(()) 98 118 else: 99 def vector_kernel(q, *args): 100 """ 101 Vectorized 1D kernel. 102 """ 103 return np.array([kernel(qi, *args) 104 for qi in q]) 105 self.kernel = vector_kernel 119 # Vector values are simple views. 120 v = parameter_vector[offset:offset+p.length] 121 offset += p.length 122 if p in kernel_parameters: 123 kernel_args.append(v) 124 if p in volume_parameters: 125 volume_args.append(v) 126 127 # Hold on to the parameter vector so we can use it to call kernel later. 128 # This may also be required to preserve the views into the vector. 129 self._parameter_vector = parameter_vector 130 131 # Generate a closure which calls the kernel with the views into the 132 # parameter array. 133 if q_input.is_2d: 134 form = model_info.Iqxy 135 qx, qy = q_input.q[:,0], q_input.q[:,1] 136 self._form = lambda: form(qx, qy, *kernel_args) 106 137 else: 107 self.kernel = kernel 108 fixed_pars = model_info['partype']['fixed-' + dim] 109 pd_pars = model_info['partype']['pd-' + dim] 110 vol_pars = model_info['partype']['volume'] 111 112 # First two fixed pars are scale and background 113 pars = [p.name for p in model_info['parameters'][2:]] 114 offset = len(self.q_input.q_vectors) 115 self.args = self.q_input.q_vectors + [None] * len(pars) 116 self.fixed_index = np.array([pars.index(p) + offset 117 for p in fixed_pars[2:]]) 118 self.pd_index = np.array([pars.index(p) + offset 119 for p in pd_pars]) 120 self.vol_index = np.array([pars.index(p) + offset 121 for p in vol_pars]) 122 try: self.theta_index = pars.index('theta') + offset 123 except ValueError: self.theta_index = -1 124 125 # Caller needs fixed_pars and pd_pars 126 self.fixed_pars = fixed_pars 127 self.pd_pars = pd_pars 128 129 def __call__(self, fixed, pd, cutoff=1e-5): 130 #print("fixed",fixed) 131 #print("pd", pd) 132 args = self.args[:] # grab a copy of the args 133 form, form_volume = self.kernel, self.info['form_volume'] 134 # First two fixed 135 scale, background = fixed[:2] 136 for index, value in zip(self.fixed_index, fixed[2:]): 137 args[index] = float(value) 138 res = _loops(form, form_volume, cutoff, scale, background, args, 139 pd, self.pd_index, self.vol_index, self.theta_index) 140 138 form = model_info.Iq 139 q = q_input.q 140 self._form = lambda: form(q, *kernel_args) 141 142 # Generate a closure which calls the form_volume if it exists. 143 form_volume = model_info.form_volume 144 self._volume = ((lambda: form_volume(*volume_args)) if form_volume 145 else (lambda: 1.0)) 146 147 def __call__(self, call_details, weights, values, cutoff): 148 assert isinstance(call_details, details.CallDetails) 149 res = _loops(self._parameter_vector, self._form, self._volume, 150 self.q_input.nq, call_details, weights, values, cutoff) 141 151 return res 142 152 … … 147 157 self.q_input = None 148 158 149 def _loops(form, form_volume, cutoff, scale, background, 150 args, pd, pd_index, vol_index, theta_index): 151 """ 152 *form* is the name of the form function, which should be vectorized over 153 q, but otherwise have an interface like the opencl kernels, with the 154 q parameters first followed by the individual parameters in the order 155 given in model.parameters (see :mod:`sasmodels.generate`). 156 157 *form_volume* calculates the volume of the shape. *vol_index* gives 158 the list of volume parameters 159 160 *cutoff* ignores the corners of the dispersion hypercube 161 162 *scale*, *background* multiplies the resulting form and adds an offset 163 164 *args* is the prepopulated set of arguments to the form function, starting 165 with the q vectors, and including slots for all the parameters. The 166 values for the parameters will be substituted with values from the 167 dispersion functions. 168 169 *pd* is the list of dispersion parameters 170 171 *pd_index* are the indices of the dispersion parameters in *args* 172 173 *vol_index* are the indices of the volume parameters in *args* 174 175 *theta_index* is the index of the theta parameter for the sasview 176 spherical correction, or -1 if there is no angular dispersion 177 """ 178 159 def _loops(parameters, # type: np.ndarray 160 form, # type: Callable[[], np.ndarray] 161 form_volume, # type: Callable[[], float] 162 nq, # type: int 163 call_details,# type: details.CallDetails 164 weights, # type: np.ndarray 165 values, # type: np.ndarray 166 cutoff, # type: float 167 ): # type: (...) -> None 179 168 ################################################################ 180 169 # # … … 186 175 # # 187 176 ################################################################ 188 189 weight = np.empty(len(pd), 'd') 190 if weight.size > 0: 191 # weight vector, to be populated by polydispersity loops 192 193 # identify which pd parameters are volume parameters 194 vol_weight_index = np.array([(index in vol_index) for index in pd_index]) 195 196 # Sort parameters in decreasing order of pd length 197 Npd = np.array([len(pdi[0]) for pdi in pd], 'i') 198 order = np.argsort(Npd)[::-1] 199 stride = np.cumprod(Npd[order]) 200 pd = [pd[index] for index in order] 201 pd_index = pd_index[order] 202 vol_weight_index = vol_weight_index[order] 203 204 fast_value = pd[0][0] 205 fast_weight = pd[0][1] 177 parameters[:] = values[call_details.par_offset] 178 scale, background = values[0], values[1] 179 if call_details.num_active == 0: 180 norm = float(form_volume()) 181 if norm > 0.0: 182 return (scale/norm)*form() + background 183 else: 184 return np.ones(nq, 'd')*background 185 186 partial_weight = np.NaN 187 spherical_correction = 1.0 188 pd_stride = call_details.pd_stride[:call_details.num_active] 189 pd_length = call_details.pd_length[:call_details.num_active] 190 pd_offset = call_details.pd_offset[:call_details.num_active] 191 pd_index = np.empty_like(pd_offset) 192 offset = np.empty_like(call_details.par_offset) 193 theta = call_details.theta_par 194 fast_length = pd_length[0] 195 pd_index[0] = fast_length 196 total = np.zeros(nq, 'd') 197 norm = 0.0 198 for loop_index in range(call_details.total_pd): 199 # update polydispersity parameter values 200 if pd_index[0] == fast_length: 201 pd_index[:] = (loop_index/pd_stride)%pd_length 202 partial_weight = np.prod(weights[pd_offset+pd_index][1:]) 203 for k in range(call_details.num_coord): 204 par = call_details.par_coord[k] 205 coord = call_details.pd_coord[k] 206 this_offset = call_details.par_offset[par] 207 block_size = 1 208 for bit in range(len(pd_offset)): 209 if coord&1: 210 this_offset += block_size * pd_index[bit] 211 block_size *= pd_length[bit] 212 coord >>= 1 213 if coord == 0: break 214 offset[par] = this_offset 215 parameters[par] = values[this_offset] 216 if par == theta and not (call_details.par_coord[k]&1): 217 spherical_correction = max(abs(cos(pi/180 * parameters[theta])), 1e-6) 218 for k in range(call_details.num_coord): 219 if call_details.pd_coord[k]&1: 220 #par = call_details.par_coord[k] 221 parameters[par] = values[offset[par]] 222 #print "par",par,offset[par],parameters[par+2] 223 offset[par] += 1 224 if par == theta: 225 spherical_correction = max(abs(cos(pi/180 * parameters[theta])), 1e-6) 226 227 weight = partial_weight * weights[pd_offset[0] + pd_index[0]] 228 pd_index[0] += 1 229 if weight > cutoff: 230 # Call the scattering function 231 # Assume that NaNs are only generated if the parameters are bad; 232 # exclude all q for that NaN. Even better would be to have an 233 # INVALID expression like the C models, but that is too expensive. 234 I = form() 235 if np.isnan(I).any(): continue 236 237 # update value and norm 238 weight *= spherical_correction 239 total += weight * I 240 norm += weight * form_volume() 241 242 if norm > 0.0: 243 return (scale/norm)*total + background 206 244 else: 207 stride = np.array([1]) 208 vol_weight_index = slice(None, None) 209 # keep lint happy 210 fast_value = [None] 211 fast_weight = [None] 212 213 ret = np.zeros_like(args[0]) 214 norm = np.zeros_like(ret) 215 vol = np.zeros_like(ret) 216 vol_norm = np.zeros_like(ret) 217 for k in range(stride[-1]): 218 # update polydispersity parameter values 219 fast_index = k % stride[0] 220 if fast_index == 0: # bottom loop complete ... check all other loops 221 if weight.size > 0: 222 for i, index, in enumerate(k % stride): 223 args[pd_index[i]] = pd[i][0][index] 224 weight[i] = pd[i][1][index] 225 else: 226 args[pd_index[0]] = fast_value[fast_index] 227 weight[0] = fast_weight[fast_index] 228 229 # Computes the weight, and if it is not sufficient then ignore this 230 # parameter set. 231 # Note: could precompute w1*...*wn so we only need to multiply by w0 232 w = np.prod(weight) 233 if w > cutoff: 234 # Note: can precompute spherical correction if theta_index is not 235 # the fast index. Correction factor for spherical integration 236 #spherical_correction = abs(cos(pi*args[phi_index])) if phi_index>=0 else 1.0 237 spherical_correction = (abs(cos(pi * args[theta_index])) * pi / 2 238 if theta_index >= 0 else 1.0) 239 #spherical_correction = 1.0 240 241 # Call the scattering function and adds it to the total. 242 I = form(*args) 243 if np.isnan(I).any(): continue 244 ret += w * I * spherical_correction 245 norm += w 246 247 # Volume normalization. 248 # If there are "volume" polydispersity parameters, then these 249 # will be used to call the form_volume function from the user 250 # supplied kernel, and accumulate a normalized weight. 251 # Note: can precompute volume norm if fast index is not a volume 252 if form_volume: 253 vol_args = [args[index] for index in vol_index] 254 vol_weight = np.prod(weight[vol_weight_index]) 255 vol += vol_weight * form_volume(*vol_args) 256 vol_norm += vol_weight 257 258 positive = (vol * vol_norm != 0.0) 259 ret[positive] *= vol_norm[positive] / vol[positive] 260 result = scale * ret / norm + background 261 return result 245 return np.ones(nq, 'd')*background 246 247 248 def _create_default_functions(model_info): 249 """ 250 Autogenerate missing functions, such as Iqxy from Iq. 251 252 This only works for Iqxy when Iq is written in python. :func:`make_source` 253 performs a similar role for Iq written in C. This also vectorizes 254 any functions that are not already marked as vectorized. 255 """ 256 _create_vector_Iq(model_info) 257 _create_vector_Iqxy(model_info) # call create_vector_Iq() first 258 259 260 def _create_vector_Iq(model_info): 261 """ 262 Define Iq as a vector function if it exists. 263 """ 264 Iq = model_info.Iq 265 if callable(Iq) and not getattr(Iq, 'vectorized', False): 266 #print("vectorizing Iq") 267 def vector_Iq(q, *args): 268 """ 269 Vectorized 1D kernel. 270 """ 271 return np.array([Iq(qi, *args) for qi in q]) 272 vector_Iq.vectorized = True 273 model_info.Iq = vector_Iq 274 275 def _create_vector_Iqxy(model_info): 276 """ 277 Define Iqxy as a vector function if it exists, or default it from Iq(). 278 """ 279 Iq, Iqxy = model_info.Iq, model_info.Iqxy 280 if callable(Iqxy): 281 if not getattr(Iqxy, 'vectorized', False): 282 #print("vectorizing Iqxy") 283 def vector_Iqxy(qx, qy, *args): 284 """ 285 Vectorized 2D kernel. 286 """ 287 return np.array([Iqxy(qxi, qyi, *args) for qxi, qyi in zip(qx, qy)]) 288 vector_Iqxy.vectorized = True 289 model_info.Iqxy = vector_Iqxy 290 elif callable(Iq): 291 #print("defaulting Iqxy") 292 # Iq is vectorized because create_vector_Iq was already called. 293 def default_Iqxy(qx, qy, *args): 294 """ 295 Default 2D kernel. 296 """ 297 return Iq(np.sqrt(qx**2 + qy**2), *args) 298 default_Iqxy.vectorized = True 299 model_info.Iqxy = default_Iqxy 300 -
sasmodels/list_pars.py
ra84a0ca r6d6508e 25 25 for name in sorted(MODELS): 26 26 model_info = load_model_info(name) 27 for p in model_info ['parameters']:27 for p in model_info.parameters.kernel_parameters: 28 28 partable.setdefault(p.name, []) 29 29 partable[p.name].append(name) -
sasmodels/mixture.py
r72a081d r6d6508e 14 14 import numpy as np 15 15 16 from .generate import process_parameters, COMMON_PARAMETERS, Parameter 17 18 SCALE=0 19 BACKGROUND=1 20 EFFECT_RADIUS=2 21 VOLFRACTION=3 16 from .modelinfo import Parameter, ParameterTable, ModelInfo 22 17 23 18 def make_mixture_info(parts): … … 34 29 35 30 # Build new parameter list 36 pars = COMMON_PARAMETERS +[]31 pars = [] 37 32 for k, part in enumerate(parts): 38 33 # Parameter prefix per model, A_, B_, ... 34 # Note that prefix must also be applied to id and length_control 35 # to support vector parameters 39 36 prefix = chr(ord('A')+k) + '_' 40 for p in part['parameters']: 41 # No background on the individual mixture elements 42 if p.name == 'background': 43 continue 44 # TODO: promote Parameter to a full class 45 # this code knows too much about the implementation! 46 p = list(p) 47 if p[0] == 'scale': # allow model subtraction 48 p[3] = [-np.inf, np.inf] # limits 49 p[0] = prefix+p[0] # relabel parameters with A_, ... 37 pars.append(Parameter(prefix+'scale')) 38 for p in part['parameters'].kernel_pars: 39 p = copy(p) 40 p.name = prefix+p.name 41 p.id = prefix+p.id 42 if p.length_control is not None: 43 p.length_control = prefix+p.length_control 50 44 pars.append(p) 45 partable = ParameterTable(pars) 51 46 52 model_info = {}53 model_info ['id']= '+'.join(part['id'])54 model_info ['name']= ' + '.join(part['name'])55 model_info ['filename']= None56 model_info ['title'] = 'Mixture model with ' + model_info['name']57 model_info ['description'] = model_info['title']58 model_info ['docs'] = model_info['title']59 model_info ['category']= "custom"60 model_info ['parameters'] = pars61 #model_info ['single']= any(part['single'] for part in parts)62 model_info ['structure_factor']= False63 model_info ['variant_info']= None64 #model_info ['tests']= []65 #model_info ['source']= []47 model_info = ModelInfo() 48 model_info.id = '+'.join(part['id']) 49 model_info.name = ' + '.join(part['name']) 50 model_info.filename = None 51 model_info.title = 'Mixture model with ' + model_info.name 52 model_info.description = model_info.title 53 model_info.docs = model_info.title 54 model_info.category = "custom" 55 model_info.parameters = partable 56 #model_info.single = any(part['single'] for part in parts) 57 model_info.structure_factor = False 58 model_info.variant_info = None 59 #model_info.tests = [] 60 #model_info.source = [] 66 61 # Iq, Iqxy, form_volume, ER, VR and sesans 67 62 # Remember the component info blocks so we can build the model 68 model_info['composition'] = ('mixture', parts) 69 process_parameters(model_info) 70 return model_info 63 model_info.composition = ('mixture', parts) 71 64 72 65 … … 110 103 if dim == '2d': 111 104 for p in kernels: 112 partype = p.info ['partype']105 partype = p.info.partype 113 106 accumulate(partype['fixed-2d'], partype['pd-2d'], partype['volume']) 114 107 else: 115 108 for p in kernels: 116 partype = p.info ['partype']109 partype = p.info.partype 117 110 accumulate(partype['fixed-1d'], partype['pd-1d'], partype['volume']) 118 111 -
sasmodels/model_test.py
r4d76711 r4bfd277 43 43 Precision defaults to 5 digits (relative). 44 44 """ 45 #TODO: rename to tests so that tab completion works better for models directory 46 45 47 from __future__ import print_function 46 48 … … 51 53 52 54 from .core import list_models, load_model_info, build_model, HAVE_OPENCL 53 from .core import call_kernel, call_ER, call_VR 55 from .details import dispersion_mesh 56 from .direct_model import call_kernel, get_weights 54 57 from .exception import annotate_exception 55 56 #TODO: rename to tests so that tab completion works better for models directory 58 from .modelinfo import expand_pars 59 60 def call_ER(model_info, pars): 61 """ 62 Call the model ER function using *values*. *model_info* is either 63 *model.info* if you have a loaded model, or *kernel.info* if you 64 have a model kernel prepared for evaluation. 65 """ 66 if model_info.ER is None: 67 return 1.0 68 else: 69 value, weight = _vol_pars(model_info, pars) 70 individual_radii = model_info.ER(*value) 71 return np.sum(weight*individual_radii) / np.sum(weight) 72 73 def call_VR(model_info, pars): 74 """ 75 Call the model VR function using *pars*. 76 *info* is either *model.info* if you have a loaded model, or *kernel.info* 77 if you have a model kernel prepared for evaluation. 78 """ 79 if model_info.VR is None: 80 return 1.0 81 else: 82 value, weight = _vol_pars(model_info, pars) 83 whole, part = model_info.VR(*value) 84 return np.sum(weight*part)/np.sum(weight*whole) 85 86 def _vol_pars(model_info, pars): 87 vol_pars = [get_weights(p, pars) 88 for p in model_info.parameters.call_parameters 89 if p.type == 'volume'] 90 value, weight = dispersion_mesh(model_info, vol_pars) 91 return value, weight 92 57 93 58 94 def make_suite(loaders, models): … … 86 122 # don't try to call cl kernel since it will not be 87 123 # available in some environmentes. 88 is_py = callable(model_info ['Iq'])124 is_py = callable(model_info.Iq) 89 125 90 126 if is_py: # kernel implemented in python … … 140 176 self.dtype = dtype 141 177 142 setattr(self, test_method_name, self. _runTest)178 setattr(self, test_method_name, self.run_all) 143 179 unittest.TestCase.__init__(self, test_method_name) 144 180 145 def _runTest(self):181 def run_all(self): 146 182 smoke_tests = [ 147 183 [{}, 0.1, None], … … 151 187 ] 152 188 153 tests = self.info ['tests']189 tests = self.info.tests 154 190 try: 155 191 model = build_model(self.info, dtype=self.dtype, 156 192 platform=self.platform) 157 193 for test in smoke_tests + tests: 158 self. _run_one_test(model, test)194 self.run_one(model, test) 159 195 160 196 if not tests and self.platform == "dll": … … 170 206 raise 171 207 172 def _run_one_test(self, model, test):208 def run_one(self, model, test): 173 209 pars, x, y = test 210 pars = expand_pars(self.info.parameters, pars) 174 211 175 212 if not isinstance(y, list): … … 194 231 actual = call_kernel(kernel, pars) 195 232 196 self.assert Greater(len(actual),0)233 self.assertTrue(len(actual) > 0) 197 234 self.assertEqual(len(y), len(actual)) 198 235 … … 276 313 tests = make_suite(['opencl', 'dll'], ['all']) 277 314 for test_i in tests: 278 yield test_i. _runTest315 yield test_i.run_all 279 316 280 317 -
sasmodels/models/cylinder.c
r26141cb re9b1663d 3 3 double Iqxy(double qx, double qy, double sld, double solvent_sld, 4 4 double radius, double length, double theta, double phi); 5 6 #define INVALID(v) (v.radius<0 || v.length<0) 5 7 6 8 double form_volume(double radius, double length) … … 15 17 double length) 16 18 { 17 // TODO: return NaN if radius<0 or length<0?18 19 // precompute qr and qh to save time in the loop 19 20 const double qr = q*radius; … … 47 48 double phi) 48 49 { 49 // TODO: return NaN if radius<0 or length<0?50 50 double sn, cn; // slots to hold sincos function output 51 51 -
sasmodels/models/flexible_cylinder.c
re6408d0 r4937980 1 double form_volume(double length, double kuhn_length, double radius); 2 double Iq(double q, double length, double kuhn_length, double radius, 3 double sld, double solvent_sld); 4 double Iqxy(double qx, double qy, double length, double kuhn_length, 5 double radius, double sld, double solvent_sld); 6 double flexible_cylinder_kernel(double q, double length, double kuhn_length, 7 double radius, double sld, double solvent_sld); 8 9 10 double form_volume(double length, double kuhn_length, double radius) 1 static double 2 form_volume(length, kuhn_length, radius) 11 3 { 12 4 return 1.0; 13 5 } 14 6 15 double flexible_cylinder_kernel(double q, 16 double length, 17 double kuhn_length, 18 double radius, 19 double sld, 20 double solvent_sld) 7 static double 8 Iq(double q, 9 double length, 10 double kuhn_length, 11 double radius, 12 double sld, 13 double solvent_sld) 21 14 { 22 23 const double cont = sld-solvent_sld; 24 const double qr = q*radius; 25 //const double crossSect = (2.0*J1(qr)/qr)*(2.0*J1(qr)/qr); 26 const double crossSect = sas_J1c(qr); 27 double flex = Sk_WR(q,length,kuhn_length); 28 flex *= crossSect*crossSect; 29 flex *= M_PI*radius*radius*length; 30 flex *= cont*cont; 31 flex *= 1.0e-4; 32 return flex; 15 const double contrast = sld - solvent_sld; 16 const double cross_section = sas_J1c(q*radius); 17 const double volume = M_PI*radius*radius*length; 18 const double flex = Sk_WR(q, length, kuhn_length); 19 return 1.0e-4 * volume * square(contrast*cross_section) * flex; 33 20 } 34 35 double Iq(double q,36 double length,37 double kuhn_length,38 double radius,39 double sld,40 double solvent_sld)41 {42 43 double result = flexible_cylinder_kernel(q, length, kuhn_length, radius, sld, solvent_sld);44 return result;45 }46 47 double Iqxy(double qx, double qy,48 double length,49 double kuhn_length,50 double radius,51 double sld,52 double solvent_sld)53 {54 double q;55 q = sqrt(qx*qx+qy*qy);56 double result = flexible_cylinder_kernel(q, length, kuhn_length, radius, sld, solvent_sld);57 58 return result;59 } -
sasmodels/models/gel_fit.c
r30b4ddf r03cac08 1 double form_volume(void); 2 3 double Iq(double q, 4 double guinier_scale, 5 double lorentzian_scale, 6 double gyration_radius, 7 double fractal_exp, 8 double cor_length); 9 10 double Iqxy(double qx, double qy, 11 double guinier_scale, 12 double lorentzian_scale, 13 double gyration_radius, 14 double fractal_exp, 15 double cor_length); 16 17 static double _gel_fit_kernel(double q, 1 static double Iq(double q, 18 2 double guinier_scale, 19 3 double lorentzian_scale, … … 24 8 // Lorentzian Term 25 9 ////////////////////////double a(x[i]*x[i]*zeta*zeta); 26 double lorentzian_term = q*q*cor_length*cor_length;10 double lorentzian_term = square(q*cor_length); 27 11 lorentzian_term = 1.0 + ((fractal_exp + 1.0)/3.0)*lorentzian_term; 28 12 lorentzian_term = pow(lorentzian_term, (fractal_exp/2.0) ); … … 30 14 // Exponential Term 31 15 ////////////////////////double d(x[i]*x[i]*rg*rg); 32 double exp_term = q*q*gyration_radius*gyration_radius;16 double exp_term = square(q*gyration_radius); 33 17 exp_term = exp(-1.0*(exp_term/3.0)); 34 18 … … 37 21 return result; 38 22 } 39 double form_volume(void){40 // Unused, so free to return garbage.41 return NAN;42 }43 44 double Iq(double q,45 double guinier_scale,46 double lorentzian_scale,47 double gyration_radius,48 double fractal_exp,49 double cor_length)50 {51 return _gel_fit_kernel(q,52 guinier_scale,53 lorentzian_scale,54 gyration_radius,55 fractal_exp,56 cor_length);57 }58 59 // Iqxy is never called since no orientation or magnetic parameters.60 double Iqxy(double qx, double qy,61 double guinier_scale,62 double lorentzian_scale,63 double gyration_radius,64 double fractal_exp,65 double cor_length)66 {67 double q = sqrt(qx*qx + qy*qy);68 return _gel_fit_kernel(q,69 guinier_scale,70 lorentzian_scale,71 gyration_radius,72 fractal_exp,73 cor_length);74 }75 -
sasmodels/models/hardsphere.py
rec45c4f rd2bb604 149 149 """ 150 150 151 Iqxy = """152 // never called since no orientation or magnetic parameters.153 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);154 """155 156 151 # ER defaults to 0.0 157 152 # VR defaults to 1.0 -
sasmodels/models/hayter_msa.py
rec45c4f rd2bb604 87 87 return 1.0; 88 88 """ 89 Iqxy = """90 // never called since no orientation or magnetic parameters.91 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);92 """93 89 # ER defaults to 0.0 94 90 # VR defaults to 1.0 -
sasmodels/models/lamellar.py
rec45c4f rd2bb604 82 82 """ 83 83 84 Iqxy = """85 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);86 """87 88 84 # ER defaults to 0.0 89 85 # VR defaults to 1.0 -
sasmodels/models/lamellar_hg.py
rec45c4f rd2bb604 101 101 """ 102 102 103 Iqxy = """104 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);105 """106 107 103 # ER defaults to 0.0 108 104 # VR defaults to 1.0 -
sasmodels/models/lamellar_hg_stack_caille.py
rec45c4f rd2bb604 120 120 """ 121 121 122 Iqxy = """123 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);124 """125 126 122 # ER defaults to 0.0 127 123 # VR defaults to 1.0 -
sasmodels/models/lamellar_stack_caille.py
rec45c4f rd2bb604 104 104 """ 105 105 106 Iqxy = """107 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);108 """109 110 106 # ER defaults to 0.0 111 107 # VR defaults to 1.0 -
sasmodels/models/lamellar_stack_paracrystal.py
rec45c4f rd2bb604 132 132 """ 133 133 134 Iqxy = """135 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);136 """137 138 134 # ER defaults to 0.0 139 135 # VR defaults to 1.0 -
sasmodels/models/lib/sas_JN.c
re6408d0 r4937980 48 48 */ 49 49 50 static double 51 sas_JN( int n, double x ) { 50 double sas_JN( int n, double x ); 51 52 double sas_JN( int n, double x ) { 52 53 53 54 const double MACHEP = 1.11022302462515654042E-16; -
sasmodels/models/lib/sph_j1c.c
re6f1410 rba32cdd 7 7 * using double precision that are the source. 8 8 */ 9 double sph_j1c(double q); 9 10 10 11 // The choice of the number of terms in the series and the cutoff value for … … 43 44 #endif 44 45 45 double sph_j1c(double q);46 46 double sph_j1c(double q) 47 47 { -
sasmodels/models/lib/sphere_form.c
rad90df9 rba32cdd 1 inline double 2 sphere_volume(double radius) 1 double sphere_volume(double radius); 2 double sphere_form(double q, double radius, double sld, double solvent_sld); 3 4 double sphere_volume(double radius) 3 5 { 4 6 return M_4PI_3*cube(radius); 5 7 } 6 8 7 inline double 8 sphere_form(double q, double radius, double sld, double solvent_sld) 9 double sphere_form(double q, double radius, double sld, double solvent_sld) 9 10 { 10 11 const double fq = sphere_volume(radius) * sph_j1c(q*radius); -
sasmodels/models/lib/wrc_cyl.c
re7678b2 rba32cdd 2 2 Functions for WRC implementation of flexible cylinders 3 3 */ 4 double Sk_WR(double q, double L, double b); 5 6 4 7 static double 5 8 AlphaSquare(double x) … … 363 366 } 364 367 365 double Sk_WR(double q, double L, double b);366 368 double Sk_WR(double q, double L, double b) 367 369 { -
sasmodels/models/onion.c
rfdb1487 rce896fd 4 4 double thickness, double A) 5 5 { 6 const double vol = 4.0/3.0 * M_PI * r * r * r;6 const double vol = M_4PI_3 * cube(r); 7 7 const double qr = q * r; 8 8 const double alpha = A * r/thickness; … … 19 19 double sinqr, cosqr; 20 20 SINCOS(qr, sinqr, cosqr); 21 fun = -3.0 (21 fun = -3.0*( 22 22 ((alphasq - qrsq)*sinqr/qr - 2.0*alpha*cosqr) / sumsq 23 23 - (alpha*sinqr/qr - cosqr) … … 32 32 f_linear(double q, double r, double sld, double slope) 33 33 { 34 const double vol = 4.0/3.0 * M_PI * r * r * r;34 const double vol = M_4PI_3 * cube(r); 35 35 const double qr = q * r; 36 36 const double bes = sph_j1c(qr); … … 52 52 { 53 53 const double bes = sph_j1c(q * r); 54 const double vol = 4.0/3.0 * M_PI * r * r * r;54 const double vol = M_4PI_3 * cube(r); 55 55 return sld * vol * bes; 56 56 } … … 64 64 r += thickness[i]; 65 65 } 66 return 4.0/3.0 * M_PI * r * r * r;66 return M_4PI_3*cube(r); 67 67 } 68 68 69 69 static double 70 Iq(double q, double core_sld, double core_radius, double solvent_sld,71 double n, double in_sld[], double out_sld[], double thickness[],70 Iq(double q, double sld_core, double core_radius, double sld_solvent, 71 double n, double sld_in[], double sld_out[], double thickness[], 72 72 double A[]) 73 73 { 74 74 int i; 75 r = core_radius;76 f = f_constant(q, r, core_sld);75 double r = core_radius; 76 double f = f_constant(q, r, sld_core); 77 77 for (i=0; i<n; i++){ 78 78 const double r0 = r; … … 92 92 } 93 93 } 94 f -= f_constant(q, r, s olvent_sld);95 f2 = f * f * 1.0e-4;94 f -= f_constant(q, r, sld_solvent); 95 const double f2 = f * f * 1.0e-4; 96 96 97 97 return f2; -
sasmodels/models/rpa.c
r13ed84c rd2bb604 1 1 double Iq(double q, double case_num, 2 double Na, double Phia, double va, double a_sld, double ba, 3 double Nb, double Phib, double vb, double b_sld, double bb, 4 double Nc, double Phic, double vc, double c_sld, double bc, 5 double Nd, double Phid, double vd, double d_sld, double bd, 2 double N[], double Phi[], double v[], double L[], double b[], 6 3 double Kab, double Kac, double Kad, 7 4 double Kbc, double Kbd, double Kcd 8 5 ); 9 6 10 double Iqxy(double qx, double qy, double case_num,11 double Na, double Phia, double va, double a_sld, double ba,12 double Nb, double Phib, double vb, double b_sld, double bb,13 double Nc, double Phic, double vc, double c_sld, double bc,14 double Nd, double Phid, double vd, double d_sld, double bd,15 double Kab, double Kac, double Kad,16 double Kbc, double Kbd, double Kcd17 );18 19 double form_volume(void);20 21 double form_volume(void)22 {23 return 1.0;24 }25 26 7 double Iq(double q, double case_num, 27 double Na, double Phia, double va, double La, double ba, 28 double Nb, double Phib, double vb, double Lb, double bb, 29 double Nc, double Phic, double vc, double Lc, double bc, 30 double Nd, double Phid, double vd, double Ld, double bd, 8 double N[], double Phi[], double v[], double L[], double b[], 31 9 double Kab, double Kac, double Kad, 32 10 double Kbc, double Kbd, double Kcd … … 36 14 #if 0 // Sasview defaults 37 15 if (icase <= 1) { 38 N a=Nb=1000.0;39 Phi a=Phib=0.0000001;16 N[0]=N[1]=1000.0; 17 Phi[0]=Phi[1]=0.0000001; 40 18 Kab=Kac=Kad=Kbc=Kbd=-0.0004; 41 L a=Lb=1e-12;42 v a=vb=100.0;43 b a=bb=5.0;19 L[0]=L[1]=1e-12; 20 v[0]=v[1]=100.0; 21 b[0]=b[1]=5.0; 44 22 } else if (icase <= 4) { 45 Phi a=0.0000001;23 Phi[0]=0.0000001; 46 24 Kab=Kac=Kad=-0.0004; 47 L a=1e-12;48 v a=100.0;49 b a=5.0;25 L[0]=1e-12; 26 v[0]=100.0; 27 b[0]=5.0; 50 28 } 51 29 #else 52 30 if (icase <= 1) { 53 N a=Nb=0.0;54 Phi a=Phib=0.0;31 N[0]=N[1]=0.0; 32 Phi[0]=Phi[1]=0.0; 55 33 Kab=Kac=Kad=Kbc=Kbd=0.0; 56 L a=Lb=Ld;57 v a=vb=vd;58 b a=bb=0.0;34 L[0]=L[1]=L[3]; 35 v[0]=v[1]=v[3]; 36 b[0]=b[1]=0.0; 59 37 } else if (icase <= 4) { 60 N a= 0.0;61 Phi a=0.0;38 N[0] = 0.0; 39 Phi[0]=0.0; 62 40 Kab=Kac=Kad=0.0; 63 L a=Ld;64 v a=vd;65 b a=0.0;41 L[0]=L[3]; 42 v[0]=v[3]; 43 b[0]=0.0; 66 44 } 67 45 #endif 68 46 69 const double Xa = q*q*b a*ba*Na/6.0;70 const double Xb = q*q*b b*bb*Nb/6.0;71 const double Xc = q*q*b c*bc*Nc/6.0;72 const double Xd = q*q*b d*bd*Nd/6.0;47 const double Xa = q*q*b[0]*b[0]*N[0]/6.0; 48 const double Xb = q*q*b[1]*b[1]*N[1]/6.0; 49 const double Xc = q*q*b[2]*b[2]*N[2]/6.0; 50 const double Xd = q*q*b[3]*b[3]*N[3]/6.0; 73 51 74 52 // limit as Xa goes to 0 is 1 … … 98 76 #if 0 99 77 const double S0aa = icase<5 100 ? 1.0 : N a*Phia*va*Paa;78 ? 1.0 : N[0]*Phi[0]*v[0]*Paa; 101 79 const double S0bb = icase<2 102 ? 1.0 : N b*Phib*vb*Pbb;103 const double S0cc = N c*Phic*vc*Pcc;104 const double S0dd = N d*Phid*vd*Pdd;80 ? 1.0 : N[1]*Phi[1]*v[1]*Pbb; 81 const double S0cc = N[2]*Phi[2]*v[2]*Pcc; 82 const double S0dd = N[3]*Phi[3]*v[3]*Pdd; 105 83 const double S0ab = icase<8 106 ? 0.0 : sqrt(N a*va*Phia*Nb*vb*Phib)*Pa*Pb;84 ? 0.0 : sqrt(N[0]*v[0]*Phi[0]*N[1]*v[1]*Phi[1])*Pa*Pb; 107 85 const double S0ac = icase<9 108 ? 0.0 : sqrt(N a*va*Phia*Nc*vc*Phic)*Pa*Pc*exp(-Xb);86 ? 0.0 : sqrt(N[0]*v[0]*Phi[0]*N[2]*v[2]*Phi[2])*Pa*Pc*exp(-Xb); 109 87 const double S0ad = icase<9 110 ? 0.0 : sqrt(N a*va*Phia*Nd*vd*Phid)*Pa*Pd*exp(-Xb-Xc);88 ? 0.0 : sqrt(N[0]*v[0]*Phi[0]*N[3]*v[3]*Phi[3])*Pa*Pd*exp(-Xb-Xc); 111 89 const double S0bc = (icase!=4 && icase!=7 && icase!= 9) 112 ? 0.0 : sqrt(N b*vb*Phib*Nc*vc*Phic)*Pb*Pc;90 ? 0.0 : sqrt(N[1]*v[1]*Phi[1]*N[2]*v[2]*Phi[2])*Pb*Pc; 113 91 const double S0bd = (icase!=4 && icase!=7 && icase!= 9) 114 ? 0.0 : sqrt(N b*vb*Phib*Nd*vd*Phid)*Pb*Pd*exp(-Xc);92 ? 0.0 : sqrt(N[1]*v[1]*Phi[1]*N[3]*v[3]*Phi[3])*Pb*Pd*exp(-Xc); 115 93 const double S0cd = (icase==0 || icase==2 || icase==5) 116 ? 0.0 : sqrt(N c*vc*Phic*Nd*vd*Phid)*Pc*Pd;94 ? 0.0 : sqrt(N[2]*v[2]*Phi[2]*N[3]*v[3]*Phi[3])*Pc*Pd; 117 95 #else // sasview equivalent 118 //printf("Xc=%g, S0cc=%g*%g*%g*%g\n",Xc,N c,Phic,vc,Pcc);119 double S0aa = N a*Phia*va*Paa;120 double S0bb = N b*Phib*vb*Pbb;121 double S0cc = N c*Phic*vc*Pcc;122 double S0dd = N d*Phid*vd*Pdd;123 double S0ab = sqrt(N a*va*Phia*Nb*vb*Phib)*Pa*Pb;124 double S0ac = sqrt(N a*va*Phia*Nc*vc*Phic)*Pa*Pc*exp(-Xb);125 double S0ad = sqrt(N a*va*Phia*Nd*vd*Phid)*Pa*Pd*exp(-Xb-Xc);126 double S0bc = sqrt(N b*vb*Phib*Nc*vc*Phic)*Pb*Pc;127 double S0bd = sqrt(N b*vb*Phib*Nd*vd*Phid)*Pb*Pd*exp(-Xc);128 double S0cd = sqrt(N c*vc*Phic*Nd*vd*Phid)*Pc*Pd;96 //printf("Xc=%g, S0cc=%g*%g*%g*%g\n",Xc,N[2],Phi[2],v[2],Pcc); 97 double S0aa = N[0]*Phi[0]*v[0]*Paa; 98 double S0bb = N[1]*Phi[1]*v[1]*Pbb; 99 double S0cc = N[2]*Phi[2]*v[2]*Pcc; 100 double S0dd = N[3]*Phi[3]*v[3]*Pdd; 101 double S0ab = sqrt(N[0]*v[0]*Phi[0]*N[1]*v[1]*Phi[1])*Pa*Pb; 102 double S0ac = sqrt(N[0]*v[0]*Phi[0]*N[2]*v[2]*Phi[2])*Pa*Pc*exp(-Xb); 103 double S0ad = sqrt(N[0]*v[0]*Phi[0]*N[3]*v[3]*Phi[3])*Pa*Pd*exp(-Xb-Xc); 104 double S0bc = sqrt(N[1]*v[1]*Phi[1]*N[2]*v[2]*Phi[2])*Pb*Pc; 105 double S0bd = sqrt(N[1]*v[1]*Phi[1]*N[3]*v[3]*Phi[3])*Pb*Pd*exp(-Xc); 106 double S0cd = sqrt(N[2]*v[2]*Phi[2]*N[3]*v[3]*Phi[3])*Pc*Pd; 129 107 switch(icase){ 130 108 case 0: … … 311 289 // Note: 1e-13 to convert from fm to cm for scattering length 312 290 const double sqrt_Nav=sqrt(6.022045e+23) * 1.0e-13; 313 const double Lad = icase<5 ? 0.0 : (L a/va - Ld/vd)*sqrt_Nav;314 const double Lbd = icase<2 ? 0.0 : (L b/vb - Ld/vd)*sqrt_Nav;315 const double Lcd = (L c/vc - Ld/vd)*sqrt_Nav;291 const double Lad = icase<5 ? 0.0 : (L[0]/v[0] - L[3]/v[3])*sqrt_Nav; 292 const double Lbd = icase<2 ? 0.0 : (L[1]/v[1] - L[3]/v[3])*sqrt_Nav; 293 const double Lcd = (L[2]/v[2] - L[3]/v[3])*sqrt_Nav; 316 294 317 295 const double result=Lad*Lad*S11 + Lbd*Lbd*S22 + Lcd*Lcd*S33 … … 321 299 322 300 } 323 324 double Iqxy(double qx, double qy,325 double case_num,326 double Na, double Phia, double va, double a_sld, double ba,327 double Nb, double Phib, double vb, double b_sld, double bb,328 double Nc, double Phic, double vc, double c_sld, double bc,329 double Nd, double Phid, double vd, double d_sld, double bd,330 double Kab, double Kac, double Kad,331 double Kbc, double Kbd, double Kcd332 )333 {334 double q = sqrt(qx*qx + qy*qy);335 return Iq(q,336 case_num,337 Na, Phia, va, a_sld, ba,338 Nb, Phib, vb, b_sld, bb,339 Nc, Phic, vc, c_sld, bc,340 Nd, Phid, vd, d_sld, bd,341 Kab, Kac, Kad,342 Kbc, Kbd, Kcd);343 } -
sasmodels/models/rpa.py
rec45c4f rea05c87 86 86 # ["name", "units", default, [lower, upper], "type","description"], 87 87 parameters = [ 88 ["case_num", CASES, 0, [0, 10], "", "Component organization"],88 ["case_num", "", 1, CASES, "", "Component organization"], 89 89 90 ["Na", "", 1000.0, [1, inf], "", "Degree of polymerization"], 91 ["Phia", "", 0.25, [0, 1], "", "volume fraction"], 92 ["va", "mL/mol", 100.0, [0, inf], "", "specific volume"], 93 ["La", "fm", 10.0, [-inf, inf], "", "scattering length"], 94 ["ba", "Ang", 5.0, [0, inf], "", "segment length"], 95 96 ["Nb", "", 1000.0, [1, inf], "", "Degree of polymerization"], 97 ["Phib", "", 0.25, [0, 1], "", "volume fraction"], 98 ["vb", "mL/mol", 100.0, [0, inf], "", "specific volume"], 99 ["Lb", "fm", 10.0, [-inf, inf], "", "scattering length"], 100 ["bb", "Ang", 5.0, [0, inf], "", "segment length"], 101 102 ["Nc", "", 1000.0, [1, inf], "", "Degree of polymerization"], 103 ["Phic", "", 0.25, [0, 1], "", "volume fraction"], 104 ["vc", "mL/mol", 100.0, [0, inf], "", "specific volume"], 105 ["Lc", "fm", 10.0, [-inf, inf], "", "scattering length"], 106 ["bc", "Ang", 5.0, [0, inf], "", "segment length"], 107 108 ["Nd", "", 1000.0, [1, inf], "", "Degree of polymerization"], 109 ["Phid", "", 0.25, [0, 1], "", "volume fraction"], 110 ["vd", "mL/mol", 100.0, [0, inf], "", "specific volume"], 111 ["Ld", "fm", 10.0, [-inf, inf], "", "scattering length"], 112 ["bd", "Ang", 5.0, [0, inf], "", "segment length"], 90 ["N[4]", "", 1000.0, [1, inf], "", "Degree of polymerization"], 91 ["Phi[4]", "", 0.25, [0, 1], "", "volume fraction"], 92 ["v[4]", "mL/mol", 100.0, [0, inf], "", "specific volume"], 93 ["L[4]", "fm", 10.0, [-inf, inf], "", "scattering length"], 94 ["b[4]", "Ang", 5.0, [0, inf], "", "segment length"], 113 95 114 96 ["Kab", "", -0.0004, [-inf, inf], "", "Interaction parameter"], -
sasmodels/models/spherical_sld.py
rec45c4f rd2bb604 170 170 # pylint: disable=bad-whitespace, line-too-long 171 171 # ["name", "units", default, [lower, upper], "type", "description"], 172 parameters = [["n _shells","", 1, [0, 9], "", "number of shells"],172 parameters = [["n", "", 1, [0, 9], "", "number of shells"], 173 173 ["radius_core", "Ang", 50.0, [0, inf], "", "intern layer thickness"], 174 174 ["sld_core", "1e-6/Ang^2", 2.07, [-inf, inf], "", "sld function flat"], … … 192 192 193 193 demo = dict( 194 n _shells=4,194 n=4, 195 195 scale=1.0, 196 196 solvent_sld=1.0, -
sasmodels/models/squarewell.py
rec45c4f rd2bb604 123 123 """ 124 124 125 Iqxy = """126 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);127 """128 129 125 # ER defaults to 0.0 130 126 # VR defaults to 1.0 -
sasmodels/models/stickyhardsphere.py
rec45c4f rd2bb604 171 171 """ 172 172 173 Iqxy = """174 return Iq(sqrt(qx*qx+qy*qy), IQ_PARAMETERS);175 """176 177 173 # ER defaults to 0.0 178 174 # VR defaults to 1.0 -
sasmodels/product.py
rf247314 r6d6508e 13 13 import numpy as np 14 14 15 from . core import call_ER_VR16 from . generate import process_parameters15 from .details import dispersion_mesh 16 from .modelinfo import suffix_parameter, ParameterTable, Parameter, ModelInfo 17 17 18 SCALE=0 19 BACKGROUND=1 20 RADIUS_EFFECTIVE=2 21 VOLFRACTION=3 18 # TODO: make estimates available to constraints 19 #ESTIMATED_PARAMETERS = [ 20 # ["est_effect_radius", "A", 0.0, [0, np.inf], "", "Estimated effective radius"], 21 # ["est_volume_ratio", "", 1.0, [0, np.inf], "", "Estimated volume ratio"], 22 #] 22 23 23 24 # TODO: core_shell_sphere model has suppressed the volume ratio calculation … … 27 28 Create info block for product model. 28 29 """ 29 p_id, p_name, p_pars = p_info['id'], p_info['name'], p_info['parameters'] 30 s_id, s_name, s_pars = s_info['id'], s_info['name'], s_info['parameters'] 31 # We require models to start with scale and background 32 assert s_pars[SCALE].name == 'scale' 33 assert s_pars[BACKGROUND].name == 'background' 34 # We require structure factors to start with effect radius and volfraction 35 assert s_pars[RADIUS_EFFECTIVE].name == 'radius_effective' 36 assert s_pars[VOLFRACTION].name == 'volfraction' 37 # Combine the parameter sets. We are skipping the first three 38 # parameters of S since scale, background are defined in P and 39 # effect_radius is set from P.ER(). 40 pars = p_pars + s_pars[3:] 41 # check for duplicates; can't use assertion since they may never be checked 42 if len(set(p.name for p in pars)) != len(pars): 43 raise ValueError("Duplicate parameters in %s and %s"%(p_id)) 44 model_info = {} 45 model_info['id'] = '*'.join((p_id, s_id)) 46 model_info['name'] = ' X '.join((p_name, s_name)) 47 model_info['filename'] = None 48 model_info['title'] = 'Product of %s and structure factor %s'%(p_name, s_name) 49 model_info['description'] = model_info['title'] 50 model_info['docs'] = model_info['title'] 51 model_info['category'] = "custom" 52 model_info['parameters'] = pars 53 #model_info['single'] = p_info['single'] and s_info['single'] 54 model_info['structure_factor'] = False 55 model_info['variant_info'] = None 56 #model_info['tests'] = [] 57 #model_info['source'] = [] 30 p_id, p_name, p_partable = p_info.id, p_info.name, p_info.parameters 31 s_id, s_name, s_partable = s_info.id, s_info.name, s_info.parameters 32 p_set = set(p.id for p in p_partable) 33 s_set = set(p.id for p in s_partable) 34 35 if p_set & s_set: 36 # there is some overlap between the parameter names; tag the 37 # overlapping S parameters with name_S 38 s_pars = [(suffix_parameter(par, "_S") if par.id in p_set else par) 39 for par in s_partable.kernel_parameters] 40 pars = p_partable.kernel_parameters + s_pars 41 else: 42 pars= p_partable.kernel_parameters + s_partable.kernel_parameters 43 44 model_info = ModelInfo() 45 model_info.id = '*'.join((p_id, s_id)) 46 model_info.name = ' X '.join((p_name, s_name)) 47 model_info.filename = None 48 model_info.title = 'Product of %s and %s'%(p_name, s_name) 49 model_info.description = model_info.title 50 model_info.docs = model_info.title 51 model_info.category = "custom" 52 model_info.parameters = ParameterTable(pars) 53 #model_info.single = p_info.single and s_info.single 54 model_info.structure_factor = False 55 model_info.variant_info = None 56 #model_info.tests = [] 57 #model_info.source = [] 58 58 # Iq, Iqxy, form_volume, ER, VR and sesans 59 model_info['composition'] = ('product', [p_info, s_info]) 60 process_parameters(model_info) 59 model_info.composition = ('product', [p_info, s_info]) 61 60 return model_info 62 61 … … 88 87 class ProductKernel(object): 89 88 def __init__(self, model_info, p_kernel, s_kernel): 90 dim = '2d' if p_kernel.q_input.is_2d else '1d'91 92 # Need to know if we want 2D and magnetic parameters when constructing93 # a parameter map.94 par_map = {}95 p_info = p_kernel.info['partype']96 s_info = s_kernel.info['partype']97 vol_pars = set(p_info['volume'])98 if dim == '2d':99 num_p_fixed = len(p_info['fixed-2d'])100 num_p_pd = len(p_info['pd-2d'])101 num_s_fixed = len(s_info['fixed-2d'])102 num_s_pd = len(s_info['pd-2d']) - 1 # exclude effect_radius103 # volume parameters are amongst the pd pars for P, not S104 vol_par_idx = [k for k,v in enumerate(p_info['pd-2d'])105 if v in vol_pars]106 else:107 num_p_fixed = len(p_info['fixed-1d'])108 num_p_pd = len(p_info['pd-1d'])109 num_s_fixed = len(s_info['fixed-1d'])110 num_s_pd = len(s_info['pd-1d']) - 1 # exclude effect_radius111 # volume parameters are amongst the pd pars for P, not S112 vol_par_idx = [k for k,v in enumerate(p_info['pd-1d'])113 if v in vol_pars]114 115 start = 0116 par_map['p_fixed'] = np.arange(start, start+num_p_fixed)117 # User doesn't set scale, background or effect_radius for S in P*S,118 # so borrow values from end of p_fixed. This makes volfraction the119 # first S parameter.120 start += num_p_fixed121 par_map['s_fixed'] = np.hstack(([start,start],122 np.arange(start, start+num_s_fixed-2)))123 par_map['volfraction'] = num_p_fixed124 start += num_s_fixed-2125 # vol pars offset from the start of pd pars126 par_map['vol_pars'] = [start+k for k in vol_par_idx]127 par_map['p_pd'] = np.arange(start, start+num_p_pd)128 start += num_p_pd-1129 par_map['s_pd'] = np.hstack((start,130 np.arange(start, start+num_s_pd-1)))131 132 self.fixed_pars = model_info['partype']['fixed-' + dim]133 self.pd_pars = model_info['partype']['pd-' + dim]134 89 self.info = model_info 135 90 self.p_kernel = p_kernel 136 91 self.s_kernel = s_kernel 137 self.par_map = par_map138 92 139 def __call__(self, fixed_pars, pd_pars, cutoff=1e-5): 140 pars = fixed_pars + pd_pars 141 scale = pars[SCALE] 142 background = pars[BACKGROUND] 143 s_volfraction = pars[self.par_map['volfraction']] 144 p_fixed = [pars[k] for k in self.par_map['p_fixed']] 145 s_fixed = [pars[k] for k in self.par_map['s_fixed']] 146 p_pd = [pars[k] for k in self.par_map['p_pd']] 147 s_pd = [pars[k] for k in self.par_map['s_pd']] 148 vol_pars = [pars[k] for k in self.par_map['vol_pars']] 149 93 def __call__(self, details, weights, values, cutoff): 150 94 effect_radius, vol_ratio = call_ER_VR(self.p_kernel.info, vol_pars) 151 95 … … 157 101 s_pd[0] = [effect_radius], [1.0] 158 102 159 p_res = self.p_kernel(p_ fixed, p_pd, cutoff)160 s_res = self.s_kernel(s_ fixed, s_pd, cutoff)103 p_res = self.p_kernel(p_details, p_weights, p_values, cutoff) 104 s_res = self.s_kernel(s_details, s_weights, s_values, cutoff) 161 105 #print s_fixed, s_pd, p_fixed, p_pd 162 106 … … 167 111 self.q_kernel.release() 168 112 113 def call_ER_VR(model_info, vol_pars): 114 """ 115 Return effect radius and volume ratio for the model. 116 """ 117 value, weight = dispersion_mesh(vol_pars) 118 119 individual_radii = model_info.ER(*value) if model_info.ER else 1.0 120 whole, part = model_info.VR(*value) if model_info.VR else (1.0, 1.0) 121 122 effect_radius = np.sum(weight*individual_radii) / np.sum(weight) 123 volume_ratio = np.sum(weight*part)/np.sum(weight*whole) 124 return effect_radius, volume_ratio -
sasmodels/resolution.py
r4d76711 r6d6508e 476 476 *pars* are the parameter values to use when evaluating. 477 477 """ 478 from sasmodels import core478 from sasmodels import direct_model 479 479 kernel = form.make_kernel([q]) 480 theory = core.call_kernel(kernel, pars)480 theory = direct_model.call_kernel(kernel, pars) 481 481 kernel.release() 482 482 return theory … … 502 502 from scipy.integrate import romberg 503 503 504 if any(k not in form.info['defaults'] for k in pars.keys()): 505 keys = set(form.info['defaults'].keys()) 506 extra = set(pars.keys()) - keys 507 raise ValueError("bad parameters: [%s] not in [%s]"% 508 (", ".join(sorted(extra)), ", ".join(sorted(keys)))) 504 par_set = set([p.name for p in form.info.parameters.call_parameters]) 505 if any(k not in par_set for k in pars.keys()): 506 extra = set(pars.keys()) - par_set 507 raise ValueError("bad parameters: [%s] not in [%s]" 508 % (", ".join(sorted(extra)), 509 ", ".join(sorted(pars.keys())))) 509 510 510 511 if np.isscalar(width): … … 556 557 from scipy.integrate import romberg 557 558 558 if any(k not in form.info['defaults'] for k in pars.keys()): 559 keys = set(form.info['defaults'].keys()) 560 extra = set(pars.keys()) - keys 561 raise ValueError("bad parameters: [%s] not in [%s]"% 562 (", ".join(sorted(extra)), ", ".join(sorted(keys)))) 559 par_set = set([p.name for p in form.info.parameters.call_parameters]) 560 if any(k not in par_set for k in pars.keys()): 561 extra = set(pars.keys()) - par_set 562 raise ValueError("bad parameters: [%s] not in [%s]" 563 % (", ".join(sorted(extra)), 564 ", ".join(sorted(pars.keys())))) 563 565 564 566 _fn = lambda q, q0, dq: eval_form(q, form, pars)*gaussian(q, q0, dq) … … 692 694 693 695 def _eval_sphere(self, pars, resolution): 694 from sasmodels import core696 from sasmodels import direct_model 695 697 kernel = self.model.make_kernel([resolution.q_calc]) 696 theory = core.call_kernel(kernel, pars)698 theory = direct_model.call_kernel(kernel, pars) 697 699 result = resolution.apply(theory) 698 700 kernel.release() … … 1043 1045 import sys 1044 1046 from sasmodels import core 1047 from sasmodels import direct_model 1045 1048 name = sys.argv[1] if len(sys.argv) > 1 else 'cylinder' 1046 1049 … … 1063 1066 1064 1067 kernel = model.make_kernel([resolution.q_calc]) 1065 theory = core.call_kernel(kernel, pars)1068 theory = direct_model.call_kernel(kernel, pars) 1066 1069 Iq = resolution.apply(theory) 1067 1070 -
sasmodels/sasview_model.py
r4d76711 r4bfd277 26 26 from . import custom 27 27 from . import generate 28 from . import weights 29 from . import details 30 from . import modelinfo 28 31 29 32 def load_standard_models(): … … 38 41 try: 39 42 models.append(_make_standard_model(name)) 40 except :43 except Exception: 41 44 logging.error(traceback.format_exc()) 42 45 return models … … 48 51 """ 49 52 kernel_module = custom.load_custom_kernel_module(path) 50 model_info = generate.make_model_info(kernel_module)53 model_info = modelinfo.make_model_info(kernel_module) 51 54 return _make_model_from_info(model_info) 52 55 … … 61 64 """ 62 65 kernel_module = generate.load_kernel_module(name) 63 model_info = generate.make_model_info(kernel_module)66 model_info = modelinfo.make_model_info(kernel_module) 64 67 return _make_model_from_info(model_info) 65 68 … … 72 75 SasviewModel.__init__(self) 73 76 attrs = dict(__init__=__init__, _model_info=model_info) 74 ConstructedModel = type(model_info ['name'], (SasviewModel,), attrs)77 ConstructedModel = type(model_info.name, (SasviewModel,), attrs) 75 78 return ConstructedModel 76 79 … … 80 83 Sasview wrapper for opencl/ctypes model. 81 84 """ 82 _model_info = {}85 _model_info = None # type: modelinfo.ModelInfo 83 86 def __init__(self): 84 87 self._model = None 85 88 model_info = self._model_info 86 87 self.name = model_info['name'] 88 self.description = model_info['description'] 89 parameters = model_info.parameters 90 91 self.name = model_info.name 92 self.description = model_info.description 89 93 self.category = None 90 self.multiplicity_info = None 91 self.is_multifunc = False 94 #self.is_multifunc = False 95 for p in parameters.kernel_parameters: 96 if p.is_control: 97 profile_axes = model_info.profile_axes 98 self.multiplicity_info = [ 99 p.limits[1], p.name, p.choices, profile_axes[0] 100 ] 101 break 102 else: 103 self.multiplicity_info = [] 92 104 93 105 ## interpret the parameters … … 96 108 self.params = collections.OrderedDict() 97 109 self.dispersion = dict() 98 partype = model_info['partype'] 99 100 for p in model_info['parameters']: 110 111 self.orientation_params = [] 112 self.magnetic_params = [] 113 self.fixed = [] 114 for p in parameters.user_parameters(): 101 115 self.params[p.name] = p.default 102 116 self.details[p.name] = [p.units] + p.limits 103 104 for name in partype['pd-2d']: 105 self.dispersion[name] = { 106 'width': 0, 107 'npts': 35, 108 'nsigmas': 3, 109 'type': 'gaussian', 110 } 111 112 self.orientation_params = ( 113 partype['orientation'] 114 + [n + '.width' for n in partype['orientation']] 115 + partype['magnetic']) 116 self.magnetic_params = partype['magnetic'] 117 self.fixed = [n + '.width' for n in partype['pd-2d']] 117 if p.polydisperse: 118 self.dispersion[p.name] = { 119 'width': 0, 120 'npts': 35, 121 'nsigmas': 3, 122 'type': 'gaussian', 123 } 124 if p.type == 'orientation': 125 self.orientation_params.append(p.name) 126 self.orientation_params.append(p.name+".width") 127 self.fixed.append(p.name+".width") 128 if p.type == 'magnetic': 129 self.orientation_params.append(p.name) 130 self.magnetic_params.append(p.name) 131 self.fixed.append(p.name+".width") 132 118 133 self.non_fittable = [] 119 134 120 135 ## independent parameter name and unit [string] 121 self.input_name = model_info.get("input_name", "Q")122 self.input_unit = model_info.get("input_unit", "A^{-1}")123 self.output_name = model_info.get("output_name", "Intensity")124 self.output_unit = model_info.get("output_unit", "cm^{-1}")136 self.input_name = "Q", #model_info.get("input_name", "Q") 137 self.input_unit = "A^{-1}" #model_info.get("input_unit", "A^{-1}") 138 self.output_name = "Intensity" #model_info.get("output_name", "Intensity") 139 self.output_unit = "cm^{-1}" #model_info.get("output_unit", "cm^{-1}") 125 140 126 141 ## _persistency_dict is used by sas.perspectives.fitting.basepage … … 234 249 """ 235 250 # TODO: fix test so that parameter order doesn't matter 236 ret = ['%s.%s' % (d.lower(), p) 237 for d in self._model_info['partype']['pd-2d'] 238 for p in ('npts', 'nsigmas', 'width')] 251 ret = ['%s.%s' % (p.name.lower(), ext) 252 for p in self._model_info.parameters.user_parameters() 253 for ext in ('npts', 'nsigmas', 'width') 254 if p.polydisperse] 239 255 #print(ret) 240 256 return ret … … 309 325 # Check whether we have a list of ndarrays [qx,qy] 310 326 qx, qy = qdist 311 partype = self._model_info['partype'] 312 if not partype['orientation'] and not partype['magnetic']: 327 if not self._model_info.parameters.has_2d: 313 328 return self.calculate_Iq(np.sqrt(qx ** 2 + qy ** 2)) 314 329 else: … … 335 350 self._model = core.build_model(self._model_info) 336 351 q_vectors = [np.asarray(q) for q in args] 337 fn = self._model.make_kernel(q_vectors) 338 pars = [self.params[v] for v in fn.fixed_pars] 339 pd_pars = [self._get_weights(p) for p in fn.pd_pars] 340 result = fn(pars, pd_pars, self.cutoff) 341 fn.q_input.release() 342 fn.release() 352 kernel = self._model.make_kernel(q_vectors) 353 pairs = [self._get_weights(p) 354 for p in self._model_info.parameters.call_parameters] 355 call_details, weight, value = details.build_details(kernel, pairs) 356 result = kernel(call_details, weight, value, cutoff=self.cutoff) 357 kernel.q_input.release() 358 kernel.release() 343 359 return result 344 360 … … 349 365 :return: the value of the effective radius 350 366 """ 351 ER = self._model_info.get('ER', None) 352 if ER is None: 367 if self._model_info.ER is None: 353 368 return 1.0 354 369 else: 355 value s, weights= self._dispersion_mesh()356 fv = ER(*values)370 value, weight = self._dispersion_mesh() 371 fv = self._model_info.ER(*value) 357 372 #print(values[0].shape, weights.shape, fv.shape) 358 return np.sum(weight s * fv) / np.sum(weights)373 return np.sum(weight * fv) / np.sum(weight) 359 374 360 375 def calculate_VR(self): … … 364 379 :return: the value of the volf ratio 365 380 """ 366 VR = self._model_info.get('VR', None) 367 if VR is None: 381 if self._model_info.VR is None: 368 382 return 1.0 369 383 else: 370 value s, weights= self._dispersion_mesh()371 whole, part = VR(*values)372 return np.sum(weight s * part) / np.sum(weights* whole)384 value, weight = self._dispersion_mesh() 385 whole, part = self._model_info.VR(*value) 386 return np.sum(weight * part) / np.sum(weight * whole) 373 387 374 388 def set_dispersion(self, parameter, dispersion): … … 408 422 parameter set in the vector. 409 423 """ 410 pars = self._model_info['partype']['volume'] 411 return core.dispersion_mesh([self._get_weights(p) for p in pars]) 424 pars = [self._get_weights(p) 425 for p in self._model_info.parameters.call_parameters 426 if p.type == 'volume'] 427 return details.dispersion_mesh(self._model_info, pars) 412 428 413 429 def _get_weights(self, par): … … 415 431 Return dispersion weights for parameter 416 432 """ 417 from . import weights 418 relative = self._model_info['partype']['pd-rel'] 419 limits = self._model_info['limits'] 420 dis = self.dispersion[par] 421 value, weight = weights.get_weights( 422 dis['type'], dis['npts'], dis['width'], dis['nsigmas'], 423 self.params[par], limits[par], par in relative) 424 return value, weight / np.sum(weight) 425 433 if par.polydisperse: 434 dis = self.dispersion[par.name] 435 value, weight = weights.get_weights( 436 dis['type'], dis['npts'], dis['width'], dis['nsigmas'], 437 self.params[par.name], par.limits, par.relative_pd) 438 return value, weight / np.sum(weight) 439 else: 440 return [self.params[par.name]], [] 426 441 427 442 def test_model():
Note: See TracChangeset
for help on using the changeset viewer.