Changes in / [ec45c4f:ea05c87] in sasmodels
- Files:
-
- 7 added
- 27 edited
Legend:
- Unmodified
- Added
- Removed
-
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 rea75043 81 81 from bumps.names import Parameter 82 82 83 pars = {} 83 pars = {} # floating point parameters 84 pd_types = {} # distribution names 84 85 for p in model_info['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 pd_types[p.name+'_pd_type'] = kwargs.pop(name, 'gaussian') 102 98 103 99 if kwargs: # args not corresponding to parameters -
sasmodels/compare.py
rf247314 rf247314 306 306 Format the parameter list for printing. 307 307 """ 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 308 lines = [] 315 for p in model_info['parameters']:316 if exclude(p.name): continue309 parameters = model_info['parameters'] 310 for p in parameters.user_parameters(pars, is2d): 317 311 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'))312 value=pars.get(p.id, p.default), 313 pd=pars.get(p.id+"_pd", 0.), 314 n=int(pars.get(p.id+"_pd_n", 0)), 315 nsigma=pars.get(p.id+"_pd_nsgima", 3.), 316 type=pars.get(p.id+"_pd_type", 'gaussian')) 323 317 lines.append(_format_par(p.name, **fields)) 324 318 return "\n".join(lines) … … 454 448 """ 455 449 # initialize the code so time is more accurate 456 value = calculator(**suppress_pd(pars)) 450 if Nevals > 1: 451 value = calculator(**suppress_pd(pars)) 457 452 toc = tic() 458 453 for _ in range(max(Nevals, 1)): # make sure there is at least one eval … … 661 656 """ 662 657 # 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" 658 pars = {} 659 for p in model_info['parameters'].call_parameters: 660 parts = [('', p.default)] 661 if p.polydisperse: 662 parts.append(('_pd', 0.0)) 663 parts.append(('_pd_n', 0)) 664 parts.append(('_pd_nsigma', 3.0)) 665 parts.append(('_pd_type', "gaussian")) 666 for ext, val in parts: 667 if p.length > 1: 668 dict(("%s%d%s"%(p.id,k,ext), val) for k in range(p.length)) 669 else: 670 pars[p.id+ext] = val 672 671 673 672 # Plug in values given in demo … … 774 773 775 774 if len(engines) == 0: 776 engines.extend(['single', ' sasview'])775 engines.extend(['single', 'double']) 777 776 elif len(engines) == 1: 778 if engines[0][0] != ' sasview':779 engines.append(' sasview')777 if engines[0][0] != 'double': 778 engines.append('double') 780 779 else: 781 780 engines.append('single') … … 880 879 pars, pd_types = bumps_model.create_parameters(model_info, **opts['pars']) 881 880 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)) 881 for p in model_info['parameters'].type['1d']: 882 for ext in ['', '_pd', '_pd_n', '_pd_nsigma']: 883 k = p.name+ext 884 v = pars.get(k, None) 885 if v is not None: 886 v.range(*parameter_range(k, v.value)) 889 887 else: 890 888 for k, v in pars.items(): -
sasmodels/core.py
rb7172bb r68e7f9d 7 7 ] 8 8 9 10 9 from os.path import basename, dirname, join as joinpath, splitext 11 10 from glob import glob 12 import imp13 11 14 12 import numpy as np … … 40 38 return [np.asarray(v) for v in args] 41 39 42 43 40 def list_models(): 44 41 """ … … 63 60 """ 64 61 return build_model(load_model_info(model_name), **kw) 65 66 def load_model_info_from_path(path):67 # Pull off the last .ext if it exists; there may be others68 name = basename(splitext(path)[0])69 70 # Not cleaning name since don't need to be able to reload this71 # model later72 # Should probably turf the model from sys.modules after we are done...73 74 # Placing the model in the 'sasmodels.custom' name space, even75 # though it doesn't actually exist. imp.load_source doesn't seem76 # to care.77 kernel_module = imp.load_source('sasmodels.custom.'+name, path)78 79 # Now that we have the module, we can load it as usual80 model_info = generate.make_model_info(kernel_module)81 return model_info82 62 83 63 def load_model_info(model_name): … … 102 82 return product.make_product_info(P_info, Q_info) 103 83 104 #import sys; print "\n".join(sys.path) 105 __import__('sasmodels.models.'+model_name) 106 kernel_module = getattr(models, model_name, None) 84 kernel_module = generate.load_kernel_module(model_name) 107 85 return generate.make_model_info(kernel_module) 108 86 … … 179 157 180 158 181 def make_kernel(model, q_vectors): 182 """ 183 Return a computation kernel from the model definition and the q input. 184 """ 185 return model(q_vectors) 186 187 def get_weights(model_info, pars, name): 159 def get_weights(parameter, values): 188 160 """ 189 161 Generate the distribution for parameter *name* given the parameter values … … 193 165 from the *pars* dictionary for parameter value and parameter dispersion. 194 166 """ 195 relative = name in model_info['partype']['pd-rel'] 196 limits = model_info['limits'][name] 197 disperser = pars.get(name+'_pd_type', 'gaussian') 198 value = pars.get(name, model_info['defaults'][name]) 199 npts = pars.get(name+'_pd_n', 0) 200 width = pars.get(name+'_pd', 0.0) 201 nsigma = pars.get(name+'_pd_nsigma', 3.0) 167 value = values.get(parameter.name, parameter.default) 168 relative = parameter.relative_pd 169 limits = parameter.limits 170 disperser = values.get(parameter.name+'_pd_type', 'gaussian') 171 npts = values.get(parameter.name+'_pd_n', 0) 172 width = values.get(parameter.name+'_pd', 0.0) 173 nsigma = values.get(parameter.name+'_pd_nsigma', 3.0) 174 if npts == 0 or width == 0: 175 return [value], [] 202 176 value, weight = weights.get_weights( 203 177 disperser, npts, width, nsigma, value, limits, relative) … … 220 194 def call_kernel(kernel, pars, cutoff=0, mono=False): 221 195 """ 222 Call *kernel* returned from :func:`make_kernel`with parameters *pars*.196 Call *kernel* returned from *model.make_kernel* with parameters *pars*. 223 197 224 198 *cutoff* is the limiting value for the product of dispersion weights used … … 228 202 with an error of about 1%, which is usually less than the measurement 229 203 uncertainty. 230 """ 231 fixed_pars = [pars.get(name, kernel.info['defaults'][name]) 232 for name in kernel.fixed_pars] 204 205 *mono* is True if polydispersity should be set to none on all parameters. 206 """ 207 parameters = kernel.info['parameters'] 233 208 if mono: 234 pd_pars = [( np.array([pars[name]]), np.array([1.0]) ) 235 for name in kernel.pd_pars] 236 else: 237 pd_pars = [get_weights(kernel.info, pars, name) for name in kernel.pd_pars] 238 return kernel(fixed_pars, pd_pars, cutoff=cutoff) 209 active = lambda name: False 210 elif kernel.dim == '1d': 211 active = lambda name: name in parameters.pd_1d 212 elif kernel.dim == '2d': 213 active = lambda name: name in parameters.pd_2d 214 else: 215 active = lambda name: True 216 217 vw_pairs = [(get_weights(p, pars) if active(p.name) 218 else ([pars.get(p.name, p.default)], [])) 219 for p in parameters.call_parameters] 220 221 details, weights, values = build_details(kernel, vw_pairs) 222 return kernel(details, weights, values, cutoff) 223 224 def build_details(kernel, pairs): 225 values, weights = zip(*pairs) 226 if max([len(w) for w in weights]) > 1: 227 details = generate.poly_details(kernel.info, weights) 228 else: 229 details = kernel.info['mono_details'] 230 weights, values = [np.hstack(v) for v in (weights, values)] 231 weights = weights.astype(dtype=kernel.dtype) 232 values = values.astype(dtype=kernel.dtype) 233 return details, weights, values 234 239 235 240 236 def call_ER_VR(model_info, vol_pars): … … 259 255 260 256 261 def call_ER( info, pars):262 """ 263 Call the model ER function using * pars*.264 * info* is either *model.info* if you have a loaded model, or *kernel.info*265 if youhave a model kernel prepared for evaluation.266 """ 267 ER = info.get('ER', None)257 def call_ER(model_info, values): 258 """ 259 Call the model ER function using *values*. *model_info* is either 260 *model.info* if you have a loaded model, or *kernel.info* if you 261 have a model kernel prepared for evaluation. 262 """ 263 ER = model_info.get('ER', None) 268 264 if ER is None: 269 265 return 1.0 270 266 else: 271 vol_pars = [get_weights(info, pars, name) 272 for name in info['partype']['volume']] 267 vol_pars = [get_weights(parameter, values) 268 for parameter in model_info['parameters'] 269 if parameter.type == 'volume'] 273 270 value, weight = dispersion_mesh(vol_pars) 274 271 individual_radii = ER(*value) … … 276 273 return np.sum(weight*individual_radii) / np.sum(weight) 277 274 278 def call_VR( info, pars):275 def call_VR(model_info, values): 279 276 """ 280 277 Call the model VR function using *pars*. … … 282 279 if you have a model kernel prepared for evaluation. 283 280 """ 284 VR = info.get('VR', None)281 VR = model_info.get('VR', None) 285 282 if VR is None: 286 283 return 1.0 287 284 else: 288 vol_pars = [get_weights(info, pars, name) 289 for name in info['partype']['volume']] 285 vol_pars = [get_weights(parameter, values) 286 for parameter in model_info['parameters'] 287 if parameter.type == 'volume'] 290 288 value, weight = dispersion_mesh(vol_pars) 291 289 whole, part = VR(*value) -
sasmodels/direct_model.py
rea75043 r68e7f9d 25 25 import numpy as np 26 26 27 from .core import make_kernel28 27 from .core import call_kernel, call_ER_VR 29 28 from . import sesans … … 68 67 self.data_type = 'Iq' 69 68 70 partype = model.info['partype']71 72 69 if self.data_type == 'sesans': 73 70 … … 83 80 q_mono = sesans.make_all_q(data) 84 81 elif self.data_type == 'Iqxy': 85 if not partype['orientation'] and not partype['magnetic']:86 raise ValueError("not 2D without orientation or magnetic parameters")82 #if not model.info['parameters'].has_2d: 83 # raise ValueError("not 2D without orientation or magnetic parameters") 87 84 q = np.sqrt(data.qx_data**2 + data.qy_data**2) 88 85 qmin = getattr(data, 'qmin', 1e-16) … … 173 170 def _calc_theory(self, pars, cutoff=0.0): 174 171 if self._kernel is None: 175 self._kernel = make_kernel(self._model, self._kernel_inputs) # pylint: disable=attribute-dedata_type176 self._kernel_mono = ( make_kernel(self._model,self._kernel_mono_inputs)172 self._kernel = self._model.make_kernel(self._kernel_inputs) 173 self._kernel_mono = (self._model.make_kernel(self._kernel_mono_inputs) 177 174 if self._kernel_mono_inputs else None) 178 175 -
sasmodels/generate.py
rf247314 rf247314 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 … … 204 173 from __future__ import print_function 205 174 206 # TODO: identify model files which have changed since loading and reload them.207 208 import sys 175 #TODO: determine which functions are useful outside of generate 176 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 177 209 178 from os.path import abspath, dirname, join as joinpath, exists, basename, \ 210 splitext 179 splitext, getmtime 211 180 import re 212 181 import string 213 182 import warnings 214 from collections import namedtuple215 183 216 184 import numpy as np 217 185 218 PARAMETER_FIELDS = ['name', 'units', 'default', 'limits', 'type', 'description'] 219 Parameter = namedtuple('Parameter', PARAMETER_FIELDS) 220 221 #TODO: determine which functions are useful outside of generate 222 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 223 224 C_KERNEL_TEMPLATE_PATH = joinpath(dirname(__file__), 'kernel_template.c') 186 from .modelinfo import ModelInfo, Parameter, make_parameter_table, set_demo 187 from .custom import load_custom_kernel_module 188 189 # TODO: identify model files which have changed since loading and reload them. 190 191 TEMPLATE_ROOT = dirname(__file__) 225 192 226 193 F16 = np.dtype('float16') … … 231 198 except TypeError: 232 199 F128 = None 233 234 # Scale and background, which are parameters common to every form factor235 COMMON_PARAMETERS = [236 ["scale", "", 1, [0, np.inf], "", "Source intensity"],237 ["background", "1/cm", 1e-3, [0, np.inf], "", "Source background"],238 ]239 200 240 201 # Conversion from units defined in the parameter table for each model … … 330 291 raise ValueError("%r not found in %s" % (filename, search_path)) 331 292 293 332 294 def model_sources(model_info): 333 295 """ … … 338 300 return [_search(search_path, f) for f in model_info['source']] 339 301 340 # Pragmas for enable OpenCL features. Be sure to protect them so that they 341 # still compile even if OpenCL is not present. 342 _F16_PRAGMA = """\ 343 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp16) 344 # pragma OPENCL EXTENSION cl_khr_fp16: enable 345 #endif 346 """ 347 348 _F64_PRAGMA = """\ 349 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp64) 350 # pragma OPENCL EXTENSION cl_khr_fp64: enable 351 #endif 352 """ 302 def timestamp(model_info): 303 """ 304 Return a timestamp for the model corresponding to the most recently 305 changed file or dependency. 306 """ 307 source_files = (model_sources(model_info) 308 + model_templates() 309 + [model_info['filename']]) 310 newest = max(getmtime(f) for f in source_files) 311 return newest 353 312 354 313 def convert_type(source, dtype): … … 361 320 if dtype == F16: 362 321 fbytes = 2 363 source = _ F16_PRAGMA + _convert_type(source, "half", "f")322 source = _convert_type(source, "float", "f") 364 323 elif dtype == F32: 365 324 fbytes = 4 … … 367 326 elif dtype == F64: 368 327 fbytes = 8 369 source = _F64_PRAGMA + source # Sourceis already double328 # no need to convert if it is already double 370 329 elif dtype == F128: 371 330 fbytes = 16 … … 410 369 411 370 412 LOOP_OPEN = """\ 413 for (int %(name)s_i=0; %(name)s_i < N%(name)s; %(name)s_i++) { 414 const double %(name)s = loops[2*(%(name)s_i%(offset)s)]; 415 const double %(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];\ 371 _template_cache = {} 372 def load_template(filename): 373 path = joinpath(TEMPLATE_ROOT, filename) 374 mtime = getmtime(path) 375 if filename not in _template_cache or mtime > _template_cache[filename][0]: 376 with open(path) as fid: 377 _template_cache[filename] = (mtime, fid.read(), path) 378 return _template_cache[filename][1] 379 380 def model_templates(): 381 # TODO: fails DRY; templates are listed in two places. 382 # should instead have model_info contain a list of paths 383 return [joinpath(TEMPLATE_ROOT, filename) 384 for filename in ('kernel_header.c', 'kernel_iq.c')] 385 386 387 _FN_TEMPLATE = """\ 388 double %(name)s(%(pars)s); 389 double %(name)s(%(pars)s) { 390 %(body)s 391 } 392 393 416 394 """ 417 def build_polydispersity_loops(pd_pars): 418 """ 419 Build polydispersity loops 420 421 Returns loop opening and loop closing 422 """ 423 depth = 4 424 offset = "" 425 loop_head = [] 426 loop_end = [] 427 for name in pd_pars: 428 subst = {'name': name, 'offset': offset} 429 loop_head.append(indent(LOOP_OPEN % subst, depth)) 430 loop_end.insert(0, (" "*depth) + "}") 431 offset += '+N' + name 432 depth += 2 433 return "\n".join(loop_head), "\n".join(loop_end) 434 435 C_KERNEL_TEMPLATE = None 395 396 def _gen_fn(name, pars, body): 397 """ 398 Generate a function given pars and body. 399 400 Returns the following string:: 401 402 double fn(double a, double b, ...); 403 double fn(double a, double b, ...) { 404 .... 405 } 406 """ 407 par_decl = ', '.join(p.as_function_argument() for p in pars) if pars else 'void' 408 return _FN_TEMPLATE % {'name': name, 'body': body, 'pars': par_decl} 409 410 def _call_pars(prefix, pars): 411 """ 412 Return a list of *prefix.parameter* from parameter items. 413 """ 414 return [p.as_call_reference(prefix) for p in pars] 415 416 _IQXY_PATTERN = re.compile("^((inline|static) )? *(double )? *Iqxy *([(]|$)", 417 flags=re.MULTILINE) 418 def _have_Iqxy(sources): 419 """ 420 Return true if any file defines Iqxy. 421 422 Note this is not a C parser, and so can be easily confused by 423 non-standard syntax. Also, it will incorrectly identify the following 424 as having Iqxy:: 425 426 /* 427 double Iqxy(qx, qy, ...) { ... fill this in later ... } 428 */ 429 430 If you want to comment out an Iqxy function, use // on the front of the 431 line instead. 432 """ 433 for code in sources: 434 if _IQXY_PATTERN.search(code): 435 return True 436 else: 437 return False 438 436 439 def make_source(model_info): 437 440 """ … … 452 455 # for computing volume even if we allow non-disperse volume parameters. 453 456 454 # Load template 455 global C_KERNEL_TEMPLATE 456 if C_KERNEL_TEMPLATE is None: 457 with open(C_KERNEL_TEMPLATE_PATH) as fid: 458 C_KERNEL_TEMPLATE = fid.read() 459 460 # Load additional sources 461 source = [open(f).read() for f in model_sources(model_info)] 462 463 # Prepare defines 464 defines = [] 465 partype = model_info['partype'] 466 pd_1d = partype['pd-1d'] 467 pd_2d = partype['pd-2d'] 468 fixed_1d = partype['fixed-1d'] 469 fixed_2d = partype['fixed-1d'] 470 471 iq_parameters = [p.name 472 for p in model_info['parameters'][2:] # skip scale, background 473 if p.name in set(fixed_1d + pd_1d)] 474 iqxy_parameters = [p.name 475 for p in model_info['parameters'][2:] # skip scale, background 476 if p.name in set(fixed_2d + pd_2d)] 477 volume_parameters = [p.name 478 for p in model_info['parameters'] 479 if p.type == 'volume'] 480 481 # Fill in defintions for volume parameters 482 if volume_parameters: 483 defines.append(('VOLUME_PARAMETERS', 484 ','.join(volume_parameters))) 485 defines.append(('VOLUME_WEIGHT_PRODUCT', 486 '*'.join(p + '_w' for p in volume_parameters))) 487 488 # Generate form_volume function from body only 457 partable = model_info['parameters'] 458 459 # Identify parameters for Iq, Iqxy, Iq_magnetic and form_volume. 460 # Note that scale and volume are not possible types. 461 462 # Load templates and user code 463 kernel_header = load_template('kernel_header.c') 464 kernel_code = load_template('kernel_iq.c') 465 user_code = [open(f).read() for f in model_sources(model_info)] 466 467 # Build initial sources 468 source = [kernel_header] + user_code 469 470 # Make parameters for q, qx, qy so that we can use them in declarations 471 q, qx, qy = [Parameter(name=v) for v in ('q', 'qx', 'qy')] 472 # Generate form_volume function, etc. from body only 489 473 if model_info['form_volume'] is not None: 490 if volume_parameters: 491 vol_par_decl = ', '.join('double ' + p for p in volume_parameters) 492 else: 493 vol_par_decl = 'void' 494 defines.append(('VOLUME_PARAMETER_DECLARATIONS', 495 vol_par_decl)) 496 fn = """\ 497 double form_volume(VOLUME_PARAMETER_DECLARATIONS); 498 double form_volume(VOLUME_PARAMETER_DECLARATIONS) { 499 %(body)s 500 } 501 """ % {'body':model_info['form_volume']} 502 source.append(fn) 503 504 # Fill in definitions for Iq parameters 505 defines.append(('IQ_KERNEL_NAME', model_info['name'] + '_Iq')) 506 defines.append(('IQ_PARAMETERS', ', '.join(iq_parameters))) 507 if fixed_1d: 508 defines.append(('IQ_FIXED_PARAMETER_DECLARATIONS', 509 ', \\\n '.join('const double %s' % p for p in fixed_1d))) 510 if pd_1d: 511 defines.append(('IQ_WEIGHT_PRODUCT', 512 '*'.join(p + '_w' for p in pd_1d))) 513 defines.append(('IQ_DISPERSION_LENGTH_DECLARATIONS', 514 ', \\\n '.join('const int N%s' % p for p in pd_1d))) 515 defines.append(('IQ_DISPERSION_LENGTH_SUM', 516 '+'.join('N' + p for p in pd_1d))) 517 open_loops, close_loops = build_polydispersity_loops(pd_1d) 518 defines.append(('IQ_OPEN_LOOPS', 519 open_loops.replace('\n', ' \\\n'))) 520 defines.append(('IQ_CLOSE_LOOPS', 521 close_loops.replace('\n', ' \\\n'))) 474 pars = partable.form_volume_parameters 475 source.append(_gen_fn('form_volume', pars, model_info['form_volume'])) 522 476 if model_info['Iq'] is not None: 523 defines.append(('IQ_PARAMETER_DECLARATIONS', 524 ', '.join('double ' + p for p in iq_parameters))) 525 fn = """\ 526 double Iq(double q, IQ_PARAMETER_DECLARATIONS); 527 double Iq(double q, IQ_PARAMETER_DECLARATIONS) { 528 %(body)s 529 } 530 """ % {'body':model_info['Iq']} 531 source.append(fn) 532 533 # Fill in definitions for Iqxy parameters 534 defines.append(('IQXY_KERNEL_NAME', model_info['name'] + '_Iqxy')) 535 defines.append(('IQXY_PARAMETERS', ', '.join(iqxy_parameters))) 536 if fixed_2d: 537 defines.append(('IQXY_FIXED_PARAMETER_DECLARATIONS', 538 ', \\\n '.join('const double %s' % p for p in fixed_2d))) 539 if pd_2d: 540 defines.append(('IQXY_WEIGHT_PRODUCT', 541 '*'.join(p + '_w' for p in pd_2d))) 542 defines.append(('IQXY_DISPERSION_LENGTH_DECLARATIONS', 543 ', \\\n '.join('const int N%s' % p for p in pd_2d))) 544 defines.append(('IQXY_DISPERSION_LENGTH_SUM', 545 '+'.join('N' + p for p in pd_2d))) 546 open_loops, close_loops = build_polydispersity_loops(pd_2d) 547 defines.append(('IQXY_OPEN_LOOPS', 548 open_loops.replace('\n', ' \\\n'))) 549 defines.append(('IQXY_CLOSE_LOOPS', 550 close_loops.replace('\n', ' \\\n'))) 477 pars = [q] + partable.iq_parameters 478 source.append(_gen_fn('Iq', pars, model_info['Iq'])) 551 479 if model_info['Iqxy'] is not None: 552 defines.append(('IQXY_PARAMETER_DECLARATIONS', 553 ', '.join('double ' + p for p in iqxy_parameters))) 554 fn = """\ 555 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS); 556 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS) { 557 %(body)s 558 } 559 """ % {'body':model_info['Iqxy']} 560 source.append(fn) 561 562 # Need to know if we have a theta parameter for Iqxy; it is not there 563 # for the magnetic sphere model, for example, which has a magnetic 564 # orientation but no shape orientation. 565 if 'theta' in pd_2d: 566 defines.append(('IQXY_HAS_THETA', '1')) 567 568 #for d in defines: print(d) 569 defines = '\n'.join('#define %s %s' % (k, v) for k, v in defines) 570 sources = '\n\n'.join(source) 571 return C_KERNEL_TEMPLATE % { 572 'DEFINES': defines, 573 'SOURCES': sources, 574 } 575 576 def categorize_parameters(pars): 577 """ 578 Build parameter categories out of the the parameter definitions. 579 580 Returns a dictionary of categories. 581 582 Note: these categories are subject to change, depending on the needs of 583 the UI and the needs of the kernel calling function. 584 585 The categories are as follows: 586 587 * *volume* list of volume parameter names 588 * *orientation* list of orientation parameters 589 * *magnetic* list of magnetic parameters 590 * *<empty string>* list of parameters that have no type info 591 592 Each parameter is in one and only one category. 593 594 The following derived categories are created: 595 596 * *fixed-1d* list of non-polydisperse parameters for 1D models 597 * *pd-1d* list of polydisperse parameters for 1D models 598 * *fixed-2d* list of non-polydisperse parameters for 2D models 599 * *pd-d2* list of polydisperse parameters for 2D models 600 """ 601 partype = { 602 'volume': [], 'orientation': [], 'magnetic': [], 'sld': [], '': [], 603 'fixed-1d': [], 'fixed-2d': [], 'pd-1d': [], 'pd-2d': [], 604 'pd-rel': set(), 605 } 606 607 for p in pars: 608 if p.type == 'volume': 609 partype['pd-1d'].append(p.name) 610 partype['pd-2d'].append(p.name) 611 partype['pd-rel'].add(p.name) 612 elif p.type == 'magnetic': 613 partype['fixed-2d'].append(p.name) 614 elif p.type == 'orientation': 615 partype['pd-2d'].append(p.name) 616 elif p.type in ('', 'sld'): 617 partype['fixed-1d'].append(p.name) 618 partype['fixed-2d'].append(p.name) 619 else: 620 raise ValueError("unknown parameter type %r" % p.type) 621 partype[p.type].append(p.name) 622 623 return partype 624 625 def process_parameters(model_info): 626 """ 627 Process parameter block, precalculating parameter details. 628 """ 629 # convert parameters into named tuples 630 for p in model_info['parameters']: 631 if p[4] == '' and (p[0].startswith('sld') or p[0].endswith('sld')): 632 p[4] = 'sld' 633 # TODO: make sure all models explicitly label their sld parameters 634 #raise ValueError("%s.%s needs to be explicitly set to type 'sld'" %(model_info['id'], p[0])) 635 636 pars = [Parameter(*p) for p in model_info['parameters']] 637 # Fill in the derived attributes 638 model_info['parameters'] = pars 639 partype = categorize_parameters(pars) 640 model_info['limits'] = dict((p.name, p.limits) for p in pars) 641 model_info['partype'] = partype 642 model_info['defaults'] = dict((p.name, p.default) for p in pars) 643 if model_info.get('demo', None) is None: 644 model_info['demo'] = model_info['defaults'] 645 model_info['has_2d'] = partype['orientation'] or partype['magnetic'] 480 pars = [qx, qy] + partable.iqxy_parameters 481 source.append(_gen_fn('Iqxy', pars, model_info['Iqxy'])) 482 483 # Define the parameter table 484 source.append("#define PARAMETER_TABLE \\") 485 source.append("\\\n".join(p.as_definition() 486 for p in partable.kernel_parameters)) 487 488 # Define the function calls 489 if partable.form_volume_parameters: 490 refs = _call_pars("v.", partable.form_volume_parameters) 491 call_volume = "#define CALL_VOLUME(v) form_volume(%s)" % (",".join(refs)) 492 else: 493 # Model doesn't have volume. We could make the kernel run a little 494 # faster by not using/transferring the volume normalizations, but 495 # the ifdef's reduce readability more than is worthwhile. 496 call_volume = "#define CALL_VOLUME(v) 1.0" 497 source.append(call_volume) 498 499 refs = ["q[i]"] + _call_pars("v.", partable.iq_parameters) 500 call_iq = "#define CALL_IQ(q,i,v) Iq(%s)" % (",".join(refs)) 501 if _have_Iqxy(user_code): 502 # Call 2D model 503 refs = ["q[2*i]", "q[2*i+1]"] + _call_pars("v.", partable.iqxy_parameters) 504 call_iqxy = "#define CALL_IQ(q,i,v) Iqxy(%s)" % (",".join(refs)) 505 else: 506 # Call 1D model with sqrt(qx^2 + qy^2) 507 warnings.warn("Creating Iqxy = Iq(sqrt(qx^2 + qy^2))") 508 # still defined:: refs = ["q[i]"] + _call_pars("v", iq_parameters) 509 pars_sqrt = ["sqrt(q[2*i]*q[2*i]+q[2*i+1]*q[2*i+1])"] + refs[1:] 510 call_iqxy = "#define CALL_IQ(q,i,v) Iq(%s)" % (",".join(pars_sqrt)) 511 512 # Fill in definitions for numbers of parameters 513 source.append("#define MAX_PD %s"%partable.max_pd) 514 source.append("#define NPARS %d"%partable.npars) 515 516 # TODO: allow mixed python/opencl kernels? 517 518 # define the Iq kernel 519 source.append("#define KERNEL_NAME %s_Iq"%model_info['name']) 520 source.append(call_iq) 521 source.append(kernel_code) 522 source.append("#undef CALL_IQ") 523 source.append("#undef KERNEL_NAME") 524 525 # define the Iqxy kernel from the same source with different #defines 526 source.append("#define KERNEL_NAME %s_Iqxy"%model_info['name']) 527 source.append(call_iqxy) 528 source.append(kernel_code) 529 source.append("#undef CALL_IQ") 530 source.append("#undef KERNEL_NAME") 531 532 return '\n'.join(source) 533 534 class CoordinationDetails(object): 535 def __init__(self, model_info): 536 parameters = model_info['parameters'] 537 max_pd = parameters.max_pd 538 npars = parameters.npars 539 par_offset = 4*max_pd 540 self.details = np.zeros(par_offset + 3*npars + 4, 'i4') 541 542 # generate views on different parts of the array 543 self._pd_par = self.details[0*max_pd:1*max_pd] 544 self._pd_length = self.details[1*max_pd:2*max_pd] 545 self._pd_offset = self.details[2*max_pd:3*max_pd] 546 self._pd_stride = self.details[3*max_pd:4*max_pd] 547 self._par_offset = self.details[par_offset+0*npars:par_offset+1*npars] 548 self._par_coord = self.details[par_offset+1*npars:par_offset+2*npars] 549 self._pd_coord = self.details[par_offset+2*npars:par_offset+3*npars] 550 551 # theta_par is fixed 552 self.details[-1] = parameters.theta_offset 553 554 @property 555 def ctypes(self): return self.details.ctypes 556 @property 557 def pd_par(self): return self._pd_par 558 @property 559 def pd_length(self): return self._pd_length 560 @property 561 def pd_offset(self): return self._pd_offset 562 @property 563 def pd_stride(self): return self._pd_stride 564 @property 565 def pd_coord(self): return self._pd_coord 566 @property 567 def par_coord(self): return self._par_coord 568 @property 569 def par_offset(self): return self._par_offset 570 @property 571 def num_coord(self): return self.details[-2] 572 @num_coord.setter 573 def num_coord(self, v): self.details[-2] = v 574 @property 575 def total_pd(self): return self.details[-3] 576 @total_pd.setter 577 def total_pd(self, v): self.details[-3] = v 578 @property 579 def num_active(self): return self.details[-4] 580 @num_active.setter 581 def num_active(self, v): self.details[-4] = v 582 583 def show(self): 584 print("total_pd", self.total_pd) 585 print("num_active", self.num_active) 586 print("pd_par", self.pd_par) 587 print("pd_length", self.pd_length) 588 print("pd_offset", self.pd_offset) 589 print("pd_stride", self.pd_stride) 590 print("par_offsets", self.par_offset) 591 print("num_coord", self.num_coord) 592 print("par_coord", self.par_coord) 593 print("pd_coord", self.pd_coord) 594 print("theta par", self.details[-1]) 595 596 def mono_details(model_info): 597 details = CoordinationDetails(model_info) 598 # The zero defaults for monodisperse systems are mostly fine 599 details.par_offset[:] = np.arange(2, len(details.par_offset)+2) 600 return details 601 602 def poly_details(model_info, weights): 603 #print("weights",weights) 604 weights = weights[2:] # Skip scale and background 605 606 # Decreasing list of polydispersity lengths 607 # Note: the reversing view, x[::-1], does not require a copy 608 pd_length = np.array([len(w) for w in weights]) 609 num_active = np.sum(pd_length>1) 610 if num_active > model_info['parameters'].max_pd: 611 raise ValueError("Too many polydisperse parameters") 612 613 pd_offset = np.cumsum(np.hstack((0, pd_length))) 614 idx = np.argsort(pd_length)[::-1][:num_active] 615 par_length = np.array([max(len(w),1) for w in weights]) 616 pd_stride = np.cumprod(np.hstack((1, par_length[idx]))) 617 par_offsets = np.cumsum(np.hstack((2, par_length))) 618 619 details = CoordinationDetails(model_info) 620 details.pd_par[:num_active] = idx 621 details.pd_length[:num_active] = pd_length[idx] 622 details.pd_offset[:num_active] = pd_offset[idx] 623 details.pd_stride[:num_active] = pd_stride[:-1] 624 details.par_offset[:] = par_offsets[:-1] 625 details.total_pd = pd_stride[-1] 626 details.num_active = num_active 627 # Without constraints coordinated parameters are just the pd parameters 628 details.par_coord[:num_active] = idx 629 details.pd_coord[:num_active] = 2**np.arange(num_active) 630 details.num_coord = num_active 631 #details.show() 632 return details 633 634 def constrained_poly_details(model_info, weights, constraints): 635 # Need to find the independently varying pars and sort them 636 # Need to build a coordination list for the dependent variables 637 # Need to generate a constraints function which takes values 638 # and weights, returning par blocks 639 raise NotImplementedError("Can't handle constraints yet") 640 641 642 def create_default_functions(model_info): 643 """ 644 Autogenerate missing functions, such as Iqxy from Iq. 645 646 This only works for Iqxy when Iq is written in python. :func:`make_source` 647 performs a similar role for Iq written in C. 648 """ 649 if callable(model_info['Iq']) and model_info['Iqxy'] is None: 650 partable = model_info['parameters'] 651 if partable.has_2d: 652 raise ValueError("Iqxy model is missing") 653 Iq = model_info['Iq'] 654 def Iqxy(qx, qy, **kw): 655 return Iq(np.sqrt(qx**2 + qy**2), **kw) 656 model_info['Iqxy'] = Iqxy 657 658 659 def load_kernel_module(model_name): 660 if model_name.endswith('.py'): 661 kernel_module = load_custom_kernel_module(model_name) 662 else: 663 from sasmodels import models 664 __import__('sasmodels.models.'+model_name) 665 kernel_module = getattr(models, model_name, None) 666 return kernel_module 667 646 668 647 669 def make_model_info(kernel_module): … … 670 692 model variants (e.g., the list of cases) or is None if there are no 671 693 model variants 672 * *defaults* is the *{parameter: value}* table built from the parameter 673 description table. 674 * *limits* is the *{parameter: [min, max]}* table built from the 675 parameter description table. 676 * *partypes* categorizes the model parameters. See 694 * *par_type* categorizes the model parameters. See 677 695 :func:`categorize_parameters` for details. 678 696 * *demo* contains the *{parameter: value}* map used in compare (and maybe … … 688 706 *model_info* blocks for the composition objects. This allows us to 689 707 build complete product and mixture models from just the info. 690 691 708 """ 692 709 # TODO: maybe turn model_info into a class ModelDefinition 693 parameters = COMMON_PARAMETERS + kernel_module.parameters 710 #print("make parameter table", kernel_module.parameters) 711 parameters = make_parameter_table(kernel_module.parameters) 694 712 filename = abspath(kernel_module.__file__) 695 713 kernel_id = splitext(basename(filename))[0] … … 701 719 filename=abspath(kernel_module.__file__), 702 720 name=name, 703 title= kernel_module.title,704 description= kernel_module.description,721 title=getattr(kernel_module, 'title', name+" model"), 722 description=getattr(kernel_module, 'description', 'no description'), 705 723 parameters=parameters, 706 724 composition=None, … … 709 727 single=getattr(kernel_module, 'single', True), 710 728 structure_factor=getattr(kernel_module, 'structure_factor', False), 729 profile_axes=getattr(kernel_module, 'profile_axes', ['x','y']), 711 730 variant_info=getattr(kernel_module, 'invariant_info', None), 712 731 demo=getattr(kernel_module, 'demo', None), … … 714 733 tests=getattr(kernel_module, 'tests', []), 715 734 ) 716 process_parameters(model_info)735 set_demo(model_info, getattr(kernel_module, 'demo', None)) 717 736 # Check for optional functions 718 functions = "ER VR form_volume Iq Iqxy shape sesans".split()737 functions = "ER VR form_volume Iq Iqxy profile sesans".split() 719 738 model_info.update((k, getattr(kernel_module, k, None)) for k in functions) 739 create_default_functions(model_info) 740 # Precalculate the monodisperse parameters 741 # TODO: make this a lazy evaluator 742 # make_model_info is called for every model on sasview startup 743 model_info['mono_details'] = mono_details(model_info) 720 744 return model_info 721 745 … … 769 793 770 794 771 772 795 def demo_time(): 773 796 """ … … 785 808 Program which prints the source produced by the model. 786 809 """ 810 import sys 787 811 if len(sys.argv) <= 1: 788 812 print("usage: python -m sasmodels.generate modelname") 789 813 else: 790 814 name = sys.argv[1] 791 import sasmodels.models 792 __import__('sasmodels.models.' + name) 793 model = getattr(sasmodels.models, name) 794 model_info = make_model_info(model) 815 kernel_module = load_kernel_module(name) 816 model_info = make_model_info(kernel_module) 795 817 source = make_source(model_info) 796 818 print(source) -
sasmodels/kernelcl.py
r8e0d974 rba32cdd 48 48 harmless, albeit annoying. 49 49 """ 50 from __future__ import print_function 50 51 import os 51 52 import warnings … … 73 74 # of polydisperse parameters. 74 75 MAX_LOOPS = 2048 76 77 78 # Pragmas for enable OpenCL features. Be sure to protect them so that they 79 # still compile even if OpenCL is not present. 80 _F16_PRAGMA = """\ 81 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp16) 82 # pragma OPENCL EXTENSION cl_khr_fp16: enable 83 #endif 84 """ 85 86 _F64_PRAGMA = """\ 87 #if defined(__OPENCL_VERSION__) // && !defined(cl_khr_fp64) 88 # pragma OPENCL EXTENSION cl_khr_fp64: enable 89 #endif 90 """ 75 91 76 92 … … 142 158 raise RuntimeError("%s not supported for devices"%dtype) 143 159 144 source = generate.convert_type(source, dtype) 160 source_list = [generate.convert_type(source, dtype)] 161 162 if dtype == generate.F16: 163 source_list.insert(0, _F16_PRAGMA) 164 elif dtype == generate.F64: 165 source_list.insert(0, _F64_PRAGMA) 166 145 167 # Note: USE_SINCOS makes the intel cpu slower under opencl 146 168 if context.devices[0].type == cl.device_type.GPU: 147 source = "#define USE_SINCOS\n" + source169 source_list.insert(0, "#define USE_SINCOS\n") 148 170 options = (get_fast_inaccurate_build_options(context.devices[0]) 149 171 if fast else []) 172 source = "\n".join(source_list) 150 173 program = cl.Program(context, source).build(options=options) 151 174 return program … … 220 243 key = "%s-%s-%s"%(name, dtype, fast) 221 244 if key not in self.compiled: 222 #print("compiling",name)245 print("compiling",name) 223 246 dtype = np.dtype(dtype) 224 program = compile_model(self.get_context(dtype), source, dtype, fast) 247 program = compile_model(self.get_context(dtype), 248 str(source), dtype, fast) 225 249 self.compiled[key] = program 226 250 return self.compiled[key] … … 314 338 self.program = None 315 339 316 def __call__(self, q_vectors):340 def make_kernel(self, q_vectors): 317 341 if self.program is None: 318 342 compiler = environment().compile_program 319 self.program = compiler(self.info['name'], self.source, self.dtype,320 self. fast)343 self.program = compiler(self.info['name'], self.source, 344 self.dtype, self.fast) 321 345 is_2d = len(q_vectors) == 2 322 346 kernel_name = generate.kernel_name(self.info, is_2d) … … 365 389 # at this point, so instead using 32, which is good on the set of 366 390 # architectures tested so far. 367 self.q_vectors = [_stretch_input(q, self.dtype, 32) for q in q_vectors] 391 if self.is_2d: 392 # Note: 17 rather than 15 because results is 2 elements 393 # longer than input. 394 width = ((self.nq+17)//16)*16 395 self.q = np.empty((width, 2), dtype=dtype) 396 self.q[:self.nq, 0] = q_vectors[0] 397 self.q[:self.nq, 1] = q_vectors[1] 398 else: 399 # Note: 33 rather than 31 because results is 2 elements 400 # longer than input. 401 width = ((self.nq+33)//32)*32 402 self.q = np.empty(width, dtype=dtype) 403 self.q[:self.nq] = q_vectors[0] 404 self.global_size = [self.q.shape[0]] 368 405 context = env.get_context(self.dtype) 369 self.global_size = [self.q_vectors[0].size]370 406 #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 ] 407 self.q_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 408 hostbuf=self.q) 375 409 376 410 def release(self): … … 378 412 Free the memory. 379 413 """ 380 for b in self.q_buffers:381 b.release()382 self.q_buffers = []414 if self.q is not None: 415 self.q.release() 416 self.q = None 383 417 384 418 def __del__(self): … … 406 440 """ 407 441 def __init__(self, kernel, model_info, q_vectors, dtype): 442 max_pd = model_info['max_pd'] 443 npars = len(model_info['parameters'])-2 408 444 q_input = GpuInput(q_vectors, dtype) 445 self.dtype = dtype 446 self.dim = '2d' if q_input.is_2d else '1d' 409 447 self.kernel = kernel 410 448 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] 449 self.pd_stop_index = 4*max_pd-1 450 # plus three for the normalization values 451 self.result = np.empty(q_input.nq+3, q_input.dtype) 415 452 416 453 # Inputs and outputs for each kernel call … … 418 455 env = environment() 419 456 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, 457 458 # details is int32 data, padded to an 8 integer boundary 459 size = ((max_pd*5 + npars*3 + 2 + 7)//8)*8 460 self.result_b = cl.Buffer(self.queue.context, mf.READ_WRITE, 423 461 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):462 self.q_input = q_input # allocated by GpuInput above 463 464 self._need_release = [ self.result_b, self.q_input ] 465 466 def __call__(self, details, weights, values, cutoff): 429 467 real = (np.float32 if self.q_input.dtype == generate.F32 430 468 else np.float64 if self.q_input.dtype == generate.F64 431 469 else np.float16 if self.q_input.dtype == generate.F16 432 470 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 471 assert details.dtype == np.int32 472 assert weights.dtype == real and values.dtype == real 473 474 context = self.queue.context 475 details_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 476 hostbuf=details) 477 weights_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 478 hostbuf=weights) 479 values_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 480 hostbuf=values) 481 482 start, stop = 0, self.details[self.pd_stop_index] 483 args = [ 484 np.uint32(self.q_input.nq), np.uint32(start), np.uint32(stop), 485 self.details_b, self.weights_b, self.values_b, 486 self.q_input.q_b, self.result_b, real(cutoff), 487 ] 460 488 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 489 cl.enqueue_copy(self.queue, self.result, self.result_b) 490 [v.release() for v in details_b, weights_b, values_b] 491 492 return self.result[:self.nq] 464 493 465 494 def release(self): -
sasmodels/kerneldll.py
r6ad0e87 rd19962c 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 … … 81 80 COMPILE = "gcc -shared -fPIC -std=c99 -O2 -Wall %(source)s -o %(output)s -lm" 82 81 if "SAS_OPENMP" in os.environ: 83 COMPILE = COMPILE +" -fopenmp"82 COMPILE += " -fopenmp" 84 83 else: 85 84 COMPILE = "cc -shared -fPIC -fopenmp -std=c99 -O2 -Wall %(source)s -o %(output)s -lm" … … 90 89 91 90 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 91 def dll_name(model_info, dtype): 92 """ 93 Name of the dll containing the model. This is the base file name without 94 any path or extension, with a form such as 'sas_sphere32'. 95 """ 96 bits = 8*dtype.itemsize 97 return "sas_%s%d"%(model_info['id'], bits) 98 99 def dll_path(model_info, dtype): 100 """ 101 Complete path to the dll for the model. Note that the dll may not 102 exist yet if it hasn't been compiled. 103 """ 104 return os.path.join(DLL_PATH, dll_name(model_info, dtype)+".so") 106 105 107 106 def make_dll(source, model_info, dtype="double"): … … 133 132 dtype = generate.F64 # Force 64-bit dll 134 133 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 134 source = generate.convert_type(source, dtype) 143 source_files = generate.model_sources(model_info) + [model_info['filename']]135 newest = generate.timestamp(model_info) 144 136 dll = dll_path(model_info, dtype) 145 newest = max(os.path.getmtime(f) for f in source_files)146 137 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)138 basename = dll_name(model_info, dtype) + "_" 139 fid, filename = tempfile.mkstemp(suffix=".c", prefix=basename) 149 140 os.fdopen(fid, "w").write(source) 150 141 command = COMPILE%{"source":filename, "output":dll} … … 171 162 return DllModel(filename, model_info, dtype=dtype) 172 163 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 164 class DllModel(object): 178 165 """ … … 197 184 198 185 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 186 #print("dll", self.dllpath) 205 187 try: … … 212 194 else c_double if self.dtype == generate.F64 213 195 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 [] 196 197 # int, int, int, int*, double*, double*, double*, double*, double*, double 198 argtypes = [c_int32]*3 + [c_void_p]*5 + [fp] 216 199 self.Iq = self.dll[generate.kernel_name(self.info, False)] 217 self.Iq.argtypes = IQ_ARGS + pd_args_1d + [fp]*Nfixed1d218 219 200 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() 201 self.Iq.argtypes = argtypes 202 self.Iqxy.argtypes = argtypes 223 203 224 204 def __getstate__(self): … … 229 209 self.dll = None 230 210 231 def __call__(self, q_vectors):211 def make_kernel(self, q_vectors): 232 212 q_input = PyInput(q_vectors, self.dtype) 233 213 if self.dll is None: self._load_dll() … … 272 252 """ 273 253 def __init__(self, kernel, model_info, q_input): 254 self.kernel = kernel 274 255 self.info = model_info 275 256 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): 257 self.dtype = q_input.dtype 258 self.dim = '2d' if q_input.is_2d else '1d' 259 self.result = np.empty(q_input.nq+3, q_input.dtype) 260 261 def __call__(self, details, weights, values, cutoff): 286 262 real = (np.float32 if self.q_input.dtype == generate.F32 287 263 else np.float64 if self.q_input.dtype == generate.F64 288 264 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) 265 assert isinstance(details, generate.CoordinationDetails) 266 assert weights.dtype == real and values.dtype == real 267 268 start, stop = 0, details.total_pd 269 #print("in kerneldll") 270 #print("weights", weights) 271 #print("values", values) 272 args = [ 273 self.q_input.nq, # nq 274 start, # pd_start 275 stop, # pd_stop pd_stride[MAX_PD] 276 details.ctypes.data, # problem 277 weights.ctypes.data, # weights 278 values.ctypes.data, #pars 279 self.q_input.q.ctypes.data, #q 280 self.result.ctypes.data, # results 281 real(cutoff), # cutoff 282 ] 303 283 self.kernel(*args) 304 305 return self.res 284 return self.result[:-3] 306 285 307 286 def release(self): -
sasmodels/kernelpy.py
ra84a0ca r48fbd50 53 53 self.dtype = dtype 54 54 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] 55 if self.is_2d: 56 self.q = np.empty((self.nq, 2), dtype=dtype) 57 self.q[:, 0] = q_vectors[0] 58 self.q[:, 1] = q_vectors[1] 59 else: 60 self.q = np.empty(self.nq, dtype=dtype) 61 self.q[:self.nq] = q_vectors[0] 57 62 58 63 def release(self): … … 60 65 Free resources associated with the model inputs. 61 66 """ 62 self.q _vectors = []67 self.q = None 63 68 64 69 class PyKernel(object): -
sasmodels/mixture.py
r72a081d r69aa451 14 14 import numpy as np 15 15 16 from . generate import process_parameters, COMMON_PARAMETERS, Parameter16 from .modelinfo import Parameter, ParameterTable 17 17 18 18 SCALE=0 … … 34 34 35 35 # Build new parameter list 36 pars = COMMON_PARAMETERS +[]36 pars = [] 37 37 for k, part in enumerate(parts): 38 38 # Parameter prefix per model, A_, B_, ... 39 # Note that prefix must also be applied to id and length_control 40 # to support vector parameters 39 41 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_, ... 42 pars.append(Parameter(prefix+'scale')) 43 for p in part['parameters'].kernel_pars: 44 p = copy(p) 45 p.name = prefix+p.name 46 p.id = prefix+p.id 47 if p.length_control is not None: 48 p.length_control = prefix+p.length_control 50 49 pars.append(p) 50 partable = ParameterTable(pars) 51 51 52 52 model_info = {} … … 58 58 model_info['docs'] = model_info['title'] 59 59 model_info['category'] = "custom" 60 model_info['parameters'] = par s60 model_info['parameters'] = partable 61 61 #model_info['single'] = any(part['single'] for part in parts) 62 62 model_info['structure_factor'] = False … … 67 67 # Remember the component info blocks so we can build the model 68 68 model_info['composition'] = ('mixture', parts) 69 process_parameters(model_info)70 return model_info71 69 72 70 -
sasmodels/model_test.py
ra84a0ca r48fbd50 51 51 52 52 from .core import list_models, load_model_info, build_model, HAVE_OPENCL 53 from .core import make_kernel,call_kernel, call_ER, call_VR53 from .core import call_kernel, call_ER, call_VR 54 54 from .exception import annotate_exception 55 55 … … 187 187 Qx, Qy = zip(*x) 188 188 q_vectors = [np.array(Qx), np.array(Qy)] 189 kernel = m ake_kernel(model,q_vectors)189 kernel = model.make_kernel(q_vectors) 190 190 actual = call_kernel(kernel, pars) 191 191 else: 192 192 q_vectors = [np.array(x)] 193 kernel = m ake_kernel(model,q_vectors)193 kernel = model.make_kernel(q_vectors) 194 194 actual = call_kernel(kernel, pars) 195 195 -
sasmodels/models/cylinder.c
r26141cb r03cac08 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/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/lib/Si.c
re7678b2 rba32cdd 1 // integral of sin(x)/x Taylor series approximated to w/i 0.1% 1 2 double Si(double x); 2 3 3 // integral of sin(x)/x Taylor series approximated to w/i 0.1%4 4 double Si(double x) 5 5 { -
sasmodels/models/lib/polevl.c
r0b05c24 rba32cdd 51 51 */ 52 52 53 double polevl( double x, constant double *coef, int N ) { 53 double polevl( double x, constant double *coef, int N ); 54 double p1evl( double x, constant double *coef, int N ); 55 56 57 double polevl( double x, constant double *coef, int N ) 58 { 54 59 55 60 int i = 0; … … 70 75 */ 71 76 72 double p1evl( double x, constant double *coef, int N ) { 77 double p1evl( double x, constant double *coef, int N ) 78 { 73 79 int i=0; 74 80 double ans = x+coef[i]; -
sasmodels/models/lib/sas_J0.c
ra776125 rba32cdd 49 49 Copyright 1984, 1987, 1989, 2000 by Stephen L. Moshier 50 50 */ 51 52 double sas_J0(double x); 51 53 52 54 /* Note: all coefficients satisfy the relative error criterion … … 177 179 }; 178 180 179 double sas_J0(double x) { 181 double sas_J0(double x) 182 { 180 183 181 184 //Cephes single precission -
sasmodels/models/lib/sas_J1.c
r19b9005 rba32cdd 39 39 Copyright 1984, 1987, 1989, 2000 by Stephen L. Moshier 40 40 */ 41 double sas_J1(double x); 42 double sas_J1c(double x); 41 43 42 44 constant double RPJ1[8] = { … … 135 137 }; 136 138 137 double sas_J1(double x) { 139 double sas_J1(double x) 140 { 138 141 139 142 //Cephes double pression function -
sasmodels/models/lib/sas_JN.c
ra776125 rba32cdd 47 47 Copyright 1984, 1987, 2000 by Stephen L. Moshier 48 48 */ 49 50 49 double sas_JN( int n, double x ); 51 50 52 double sas_JN( int n, double x ) { 51 double sas_JN( int n, double x ) 52 { 53 53 54 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/onion.py
rec45c4f rec45c4f 293 293 294 294 # ["name", "units", default, [lower, upper], "type","description"], 295 parameters = [[" core_sld", "1e-6/Ang^2", 1.0, [-inf, inf], "",295 parameters = [["sld_core", "1e-6/Ang^2", 1.0, [-inf, inf], "", 296 296 "Core scattering length density"], 297 297 ["core_radius", "Ang", 200., [0, inf], "volume", 298 298 "Radius of the core"], 299 ["s olvent_sld", "1e-6/Ang^2", 6.4, [-inf, inf], "",299 ["sld_solvent", "1e-6/Ang^2", 6.4, [-inf, inf], "", 300 300 "Solvent scattering length density"], 301 301 ["n", "", 1, [0, 10], "volume", 302 302 "number of shells"], 303 [" in_sld[n]", "1e-6/Ang^2", 1.7, [-inf, inf], "",303 ["sld_in[n]", "1e-6/Ang^2", 1.7, [-inf, inf], "", 304 304 "scattering length density at the inner radius of shell k"], 305 [" out_sld[n]", "1e-6/Ang^2", 2.0, [-inf, inf], "",305 ["sld_out[n]", "1e-6/Ang^2", 2.0, [-inf, inf], "", 306 306 "scattering length density at the outer radius of shell k"], 307 307 ["thickness[n]", "Ang", 40., [0, inf], "volume", … … 311 311 ] 312 312 313 #source = ["lib/sph_j1c.c", "onion.c"] 314 315 def Iq(q, *args, **kw): 316 return q 317 318 def Iqxy(qx, *args, **kw): 319 return qx 320 321 322 def shape(core_sld, core_radius, solvent_sld, n, in_sld, out_sld, thickness, A): 313 source = ["lib/sph_j1c.c", "onion.c"] 314 315 #def Iq(q, *args, **kw): 316 # return q 317 318 profile_axes = ['Radius (A)', 'SLD (1e-6/A^2)'] 319 def profile(core_sld, core_radius, solvent_sld, n, in_sld, out_sld, thickness, A): 323 320 """ 324 321 SLD profile … … 374 371 375 372 demo = { 376 "s olvent_sld": 2.2,377 " core_sld": 1.0,373 "sld_solvent": 2.2, 374 "sld_core": 1.0, 378 375 "core_radius": 100, 379 376 "n": 4, 380 " in_sld": [0.5, 1.5, 0.9, 2.0],381 " out_sld": [nan, 0.9, 1.2, 1.6],377 "sld_in": [0.5, 1.5, 0.9, 2.0], 378 "sld_out": [nan, 0.9, 1.2, 1.6], 382 379 "thickness": [50, 75, 150, 75], 383 380 "A": [0, -1, 1e-4, 1], -
sasmodels/models/rpa.py
rec45c4f rec45c4f 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/product.py
rf247314 rf247314 14 14 15 15 from .core import call_ER_VR 16 from .generate import process_parameters17 16 18 17 SCALE=0 … … 58 57 # Iq, Iqxy, form_volume, ER, VR and sesans 59 58 model_info['composition'] = ('product', [p_info, s_info]) 60 process_parameters(model_info)61 59 return model_info 62 60 … … 93 91 # a parameter map. 94 92 par_map = {} 95 p_info = p_kernel.info['par type']96 s_info = s_kernel.info['par type']93 p_info = p_kernel.info['par_type'] 94 s_info = s_kernel.info['par_type'] 97 95 vol_pars = set(p_info['volume']) 98 96 if dim == '2d': -
sasmodels/resolution.py
r486fcf6 r486fcf6 477 477 """ 478 478 from sasmodels import core 479 kernel = core.make_kernel(form,[q])479 kernel = form.make_kernel([q]) 480 480 theory = core.call_kernel(kernel, pars) 481 481 kernel.release() … … 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()) - keys504 par_set = set([p.name for p in form.info['parameters']]) 505 if any(k not in par_set for k in pars.keys()): 506 extra = set(pars.keys()) - par_set 507 507 raise ValueError("bad parameters: [%s] not in [%s]"% 508 508 (", ".join(sorted(extra)), ", ".join(sorted(keys)))) … … 556 556 from scipy.integrate import romberg 557 557 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()) - keys558 par_set = set([p.name for p in form.info['parameters']]) 559 if any(k not in par_set for k in pars.keys()): 560 extra = set(pars.keys()) - par_set 561 561 raise ValueError("bad parameters: [%s] not in [%s]"% 562 (", ".join(sorted(extra)), ", ".join(sorted(keys)))) 562 (", ".join(sorted(extra)), 563 ", ".join(sorted(pars.keys())))) 563 564 564 565 _fn = lambda q, q0, dq: eval_form(q, form, pars)*gaussian(q, q0, dq) … … 693 694 def _eval_sphere(self, pars, resolution): 694 695 from sasmodels import core 695 kernel = core.make_kernel(self.model,[resolution.q_calc])696 kernel = self.model.make_kernel([resolution.q_calc]) 696 697 theory = core.call_kernel(kernel, pars) 697 698 result = resolution.apply(theory) … … 1062 1063 model = core.build_model(model_info) 1063 1064 1064 kernel = core.make_kernel(model,[resolution.q_calc])1065 kernel = model.make_kernel([resolution.q_calc]) 1065 1066 theory = core.call_kernel(kernel, pars) 1066 1067 Iq = resolution.apply(theory) -
sasmodels/sasview_model.py
rf247314 rf247314 21 21 22 22 from . import core 23 from . import generate23 from . import weights 24 24 25 25 def standard_models(): … … 52 52 """ 53 53 def __init__(self): 54 self._ kernel = None54 self._model = None 55 55 model_info = self._model_info 56 parameters = model_info['parameters'] 56 57 57 58 self.name = model_info['name'] 58 59 self.description = model_info['description'] 59 60 self.category = None 60 self.multiplicity_info = None 61 self.is_multifunc = False 61 #self.is_multifunc = False 62 for p in parameters.kernel_parameters: 63 if p.is_control: 64 profile_axes = model_info['profile_axes'] 65 self.multiplicity_info = [ 66 p.limits[1], p.name, p.choices, profile_axes[0] 67 ] 68 break 69 else: 70 self.multiplicity_info = [] 62 71 63 72 ## interpret the parameters … … 66 75 self.params = collections.OrderedDict() 67 76 self.dispersion = dict() 68 partype = model_info['partype'] 69 70 for p in model_info['parameters']: 77 78 self.orientation_params = [] 79 self.magnetic_params = [] 80 self.fixed = [] 81 for p in parameters.user_parameters(): 71 82 self.params[p.name] = p.default 72 83 self.details[p.name] = [p.units] + p.limits 73 74 for name in partype['pd-2d']: 75 self.dispersion[name] = { 76 'width': 0, 77 'npts': 35, 78 'nsigmas': 3, 79 'type': 'gaussian', 80 } 81 82 self.orientation_params = ( 83 partype['orientation'] 84 + [n + '.width' for n in partype['orientation']] 85 + partype['magnetic']) 86 self.magnetic_params = partype['magnetic'] 87 self.fixed = [n + '.width' for n in partype['pd-2d']] 84 if p.polydisperse: 85 self.dispersion[p.name] = { 86 'width': 0, 87 'npts': 35, 88 'nsigmas': 3, 89 'type': 'gaussian', 90 } 91 if p.type == 'orientation': 92 self.orientation_params.append(p.name) 93 self.orientation_params.append(p.name+".width") 94 self.fixed.append(p.name+".width") 95 if p.type == 'magnetic': 96 self.orientation_params.append(p.name) 97 self.magnetic_params.append(p.name) 98 self.fixed.append(p.name+".width") 99 88 100 self.non_fittable = [] 89 101 … … 104 116 def __get_state__(self): 105 117 state = self.__dict__.copy() 106 model_id = self._model_info['id']107 118 state.pop('_kernel') 108 119 # May need to reload model info on set state since it has pointers … … 113 124 def __set_state__(self, state): 114 125 self.__dict__ = state 115 self._ kernel = None126 self._model = None 116 127 117 128 def __str__(self): … … 202 213 def getDispParamList(self): 203 214 """ 204 Return a list of all availableparameters for the model215 Return a list of polydispersity parameters for the model 205 216 """ 206 217 # TODO: fix test so that parameter order doesn't matter 207 ret = ['%s.%s' % (d.lower(), p) 208 for d in self._model_info['partype']['pd-2d'] 209 for p in ('npts', 'nsigmas', 'width')] 218 ret = ['%s.%s' % (p.name.lower(), ext) 219 for p in self._model_info['parameters'].user_parameters() 220 for ext in ('npts', 'nsigmas', 'width') 221 if p.polydisperse] 210 222 #print(ret) 211 223 return ret … … 280 292 # Check whether we have a list of ndarrays [qx,qy] 281 293 qx, qy = qdist 282 partype = self._model_info['partype'] 283 if not partype['orientation'] and not partype['magnetic']: 294 if not self._model_info['parameters'].has_2d: 284 295 return self.calculate_Iq(np.sqrt(qx ** 2 + qy ** 2)) 285 296 else: … … 303 314 to the card for each evaluation. 304 315 """ 305 if self._ kernel is None:306 self._ kernel = core.build_model(self._model_info)316 if self._model is None: 317 self._model = core.build_model(self._model_info, platform='dll') 307 318 q_vectors = [np.asarray(q) for q in args] 308 fn = self._kernel(q_vectors) 309 pars = [self.params[v] for v in fn.fixed_pars] 310 pd_pars = [self._get_weights(p) for p in fn.pd_pars] 311 result = fn(pars, pd_pars, self.cutoff) 312 fn.q_input.release() 313 fn.release() 319 kernel = self._model.make_kernel(q_vectors) 320 pairs = [self._get_weights(p) 321 for p in self._model_info['parameters'].call_parameters] 322 details, weights, values = core.build_details(kernel, pairs) 323 return kernel(details, weights, values, cutoff=self.cutoff) 324 kernel.q_input.release() 325 kernel.release() 314 326 return result 315 327 … … 384 396 def _get_weights(self, par): 385 397 """ 386 Return dispersion weights 387 :param par parameter name 388 """ 389 from . import weights 390 391 relative = self._model_info['partype']['pd-rel'] 392 limits = self._model_info['limits'] 393 dis = self.dispersion[par] 394 value, weight = weights.get_weights( 395 dis['type'], dis['npts'], dis['width'], dis['nsigmas'], 396 self.params[par], limits[par], par in relative) 397 return value, weight / np.sum(weight) 398 398 Return dispersion weights for parameter 399 """ 400 if par.polydisperse: 401 dis = self.dispersion[par.name] 402 value, weight = weights.get_weights( 403 dis['type'], dis['npts'], dis['width'], dis['nsigmas'], 404 self.params[par.name], par.limits, par.relative_pd) 405 return value, weight / np.sum(weight) 406 else: 407 return [self.params[par.name]], [] 408 409 def test_model(): 410 Cylinder = make_class('cylinder') 411 cylinder = Cylinder() 412 return cylinder.evalDistribution([0.1,0.1]) 413 414 if __name__ == "__main__": 415 print("cylinder(0.1,0.1)=%g"%test_model())
Note: See TracChangeset
for help on using the changeset viewer.