Changeset 6d6508e in sasmodels
- Timestamp:
- Apr 7, 2016 6:57:33 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:
- d2fc9a4
- Parents:
- 3707eee
- Location:
- sasmodels
- Files:
-
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/bumps_model.py
r21b116f r6d6508e 83 83 pars = {} # floating point parameters 84 84 pd_types = {} # distribution names 85 for p in model_info ['parameters'].call_parameters:85 for p in model_info.parameters.call_parameters: 86 86 value = kwargs.pop(p.name, p.default) 87 87 pars[p.name] = Parameter.default(value, name=p.name, limits=p.limits) -
sasmodels/compare.py
r8bd7b77 r6d6508e 244 244 245 245 # Find the parameter definition 246 for par in model_info ['parameters'].call_parameters:246 for par in model_info.parameters.call_parameters: 247 247 if par.name == p: 248 248 break … … 284 284 cylinder radius in this case). 285 285 """ 286 name = model_info ['id']286 name = model_info.id 287 287 # if it is a product model, then just look at the form factor since 288 288 # none of the structure factors need any constraints. … … 318 318 """ 319 319 lines = [] 320 parameters = model_info ['parameters']320 parameters = model_info.parameters 321 321 for p in parameters.user_parameters(pars, is2d): 322 322 fields = dict( … … 368 368 369 369 # grab the sasview model, or create it if it is a product model 370 if model_info ['composition']:371 composition_type, parts = model_info ['composition']370 if model_info.composition: 371 composition_type, parts = model_info.composition 372 372 if composition_type == 'product': 373 373 from sas.models.MultiplicationModel import MultiplicationModel … … 668 668 # Get the default values for the parameters 669 669 pars = {} 670 for p in model_info ['parameters'].call_parameters:670 for p in model_info.parameters.call_parameters: 671 671 parts = [('', p.default)] 672 672 if p.polydisperse: … … 683 683 # Plug in values given in demo 684 684 if use_demo: 685 pars.update(model_info ['demo'])685 pars.update(model_info.demo) 686 686 return pars 687 687 … … 891 891 # Initialize parameter ranges, fixing the 2D parameters for 1D data. 892 892 if not opts['is2d']: 893 for p in model_info ['parameters'].user_parameters(is2d=False):893 for p in model_info.parameters.user_parameters(is2d=False): 894 894 for ext in ['', '_pd', '_pd_n', '_pd_nsigma']: 895 895 k = p.name+ext -
sasmodels/convert.py
r8bd7b77 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) … … 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
ree8f734 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 … … 26 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 … … 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(parameter, values):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 value = float(values.get(parameter.name, parameter.default))181 relative = parameter.relative_pd182 limits = parameter.limits183 disperser = values.get(parameter.name+'_pd_type', 'gaussian')184 npts = values.get(parameter.name+'_pd_n', 0)185 width = values.get(parameter.name+'_pd', 0.0)186 nsigma = values.get(parameter.name+'_pd_nsigma', 3.0)187 if npts == 0 or width == 0:188 return [value], []189 value, weight = weights.get_weights(190 disperser, npts, width, nsigma, value, limits, relative)191 return value, weight / np.sum(weight)192 193 def dispersion_mesh(pars):194 """195 Create a mesh grid of dispersion parameters and weights.196 197 Returns [p1,p2,...],w where pj is a vector of values for parameter j198 and w is a vector containing the products for weights for each199 parameter set in the vector.200 """201 value, weight = zip(*pars)202 weight = [w if w else [1.] for w in weight]203 value = [v.flatten() for v in meshgrid(*value)]204 weight = np.vstack([v.flatten() for v in meshgrid(*weight)])205 weight = np.prod(weight, axis=0)206 return value, weight207 208 def call_kernel(kernel, pars, cutoff=0, mono=False):209 """210 Call *kernel* returned from *model.make_kernel* with parameters *pars*.211 212 *cutoff* is the limiting value for the product of dispersion weights used213 to perform the multidimensional dispersion calculation more quickly at a214 slight cost to accuracy. The default value of *cutoff=0* integrates over215 the entire dispersion cube. Using *cutoff=1e-5* can be 50% faster, but216 with an error of about 1%, which is usually less than the measurement217 uncertainty.218 219 *mono* is True if polydispersity should be set to none on all parameters.220 """221 parameters = kernel.info['parameters']222 if mono:223 active = lambda name: False224 elif kernel.dim == '1d':225 active = lambda name: name in parameters.pd_1d226 elif kernel.dim == '2d':227 active = lambda name: name in parameters.pd_2d228 else:229 active = lambda name: True230 231 vw_pairs = [(get_weights(p, pars) if active(p.name)232 else ([pars.get(p.name, p.default)], []))233 for p in parameters.call_parameters]234 235 details, weights, values = build_details(kernel, vw_pairs)236 return kernel(details, weights, values, cutoff)237 238 def build_details(kernel, pairs):239 values, weights = zip(*pairs)240 if max([len(w) for w in weights]) > 1:241 details = generate.poly_details(kernel.info, weights)242 else:243 details = kernel.info['mono_details']244 weights, values = [np.hstack(v) for v in (weights, values)]245 weights = weights.astype(dtype=kernel.dtype)246 values = values.astype(dtype=kernel.dtype)247 return details, weights, values248 249 250 def call_ER_VR(model_info, vol_pars):251 """252 Return effect radius and volume ratio for the model.253 254 *info* is either *kernel.info* for *kernel=make_kernel(model,q)*255 or *model.info*.256 257 *pars* are the parameters as expected by :func:`call_kernel`.258 """259 ER = model_info.get('ER', None)260 VR = model_info.get('VR', None)261 value, weight = dispersion_mesh(vol_pars)262 263 individual_radii = ER(*value) if ER else 1.0264 whole, part = VR(*value) if VR else (1.0, 1.0)265 266 effect_radius = np.sum(weight*individual_radii) / np.sum(weight)267 volume_ratio = np.sum(weight*part)/np.sum(weight*whole)268 return effect_radius, volume_ratio269 270 271 def call_ER(model_info, values):272 """273 Call the model ER function using *values*. *model_info* is either274 *model.info* if you have a loaded model, or *kernel.info* if you275 have a model kernel prepared for evaluation.276 """277 ER = model_info.get('ER', None)278 if ER is None:279 return 1.0280 else:281 vol_pars = [get_weights(parameter, values)282 for parameter in model_info['parameters'].call_parameters283 if parameter.type == 'volume']284 value, weight = dispersion_mesh(vol_pars)285 individual_radii = ER(*value)286 return np.sum(weight*individual_radii) / np.sum(weight)287 288 def call_VR(model_info, values):289 """290 Call the model VR function using *pars*.291 *info* is either *model.info* if you have a loaded model, or *kernel.info*292 if you have a model kernel prepared for evaluation.293 """294 VR = model_info.get('VR', None)295 if VR is None:296 return 1.0297 else:298 vol_pars = [get_weights(parameter, values)299 for parameter in model_info['parameters'].call_parameters300 if parameter.type == 'volume']301 value, weight = dispersion_mesh(vol_pars)302 whole, part = VR(*value)303 return np.sum(weight*part)/np.sum(weight*whole)304 305 # TODO: remove call_ER, call_VR306 -
sasmodels/direct_model.py
ree8f734 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): … … 80 133 q_mono = sesans.make_all_q(data) 81 134 elif self.data_type == 'Iqxy': 82 #if not model.info ['parameters'].has_2d:135 #if not model.info.parameters.has_2d: 83 136 # raise ValueError("not 2D without orientation or magnetic parameters") 84 137 q = np.sqrt(data.qx_data**2 + data.qy_data**2) … … 211 264 def __call__(self, **pars): 212 265 return self._calc_theory(pars, cutoff=self.cutoff) 213 214 def ER_VR(self, **pars):215 """216 Compute the equivalent radius and volume ratio for the model.217 """218 return call_ER_VR(self.model.info, pars)219 266 220 267 def simulate_data(self, noise=None, **pars): -
sasmodels/generate.py
r6e7ff6d r6d6508e 155 155 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 156 156 157 from os.path import abspath, dirname, join as joinpath, exists, basename, \ 158 splitext, getmtime 157 from os.path import abspath, dirname, join as joinpath, exists, getmtime 159 158 import re 160 159 import string … … 163 162 import numpy as np 164 163 165 from .modelinfo import ModelInfo, Parameter, make_parameter_table, set_demo164 from .modelinfo import Parameter 166 165 from .custom import load_custom_kernel_module 167 166 … … 273 272 Return a list of the sources file paths for the module. 274 273 """ 275 search_path = [dirname(model_info ['filename']),274 search_path = [dirname(model_info.filename), 276 275 abspath(joinpath(dirname(__file__), 'models'))] 277 return [_search(search_path, f) for f in model_info ['source']]276 return [_search(search_path, f) for f in model_info.source] 278 277 279 278 def timestamp(model_info): … … 284 283 source_files = (model_sources(model_info) 285 284 + model_templates() 286 + [model_info ['filename']])285 + [model_info.filename]) 287 286 newest = max(getmtime(f) for f in source_files) 288 287 return newest … … 334 333 Name of the exported kernel symbol. 335 334 """ 336 return model_info ['name']+ "_" + ("Iqxy" if is_2d else "Iq")335 return model_info.name + "_" + ("Iqxy" if is_2d else "Iq") 337 336 338 337 … … 420 419 Uses source files found in the given search path. 421 420 """ 422 if callable(model_info ['Iq']):421 if callable(model_info.Iq): 423 422 return None 424 423 … … 432 431 # for computing volume even if we allow non-disperse volume parameters. 433 432 434 partable = model_info ['parameters']433 partable = model_info.parameters 435 434 436 435 # Identify parameters for Iq, Iqxy, Iq_magnetic and form_volume. … … 448 447 q, qx, qy = [Parameter(name=v) for v in ('q', 'qx', 'qy')] 449 448 # Generate form_volume function, etc. from body only 450 if model_info ['form_volume']is not None:449 if model_info.form_volume is not None: 451 450 pars = partable.form_volume_parameters 452 source.append(_gen_fn('form_volume', pars, model_info ['form_volume']))453 if model_info ['Iq']is not None:451 source.append(_gen_fn('form_volume', pars, model_info.form_volume)) 452 if model_info.Iq is not None: 454 453 pars = [q] + partable.iq_parameters 455 source.append(_gen_fn('Iq', pars, model_info ['Iq']))456 if model_info ['Iqxy']is not None:454 source.append(_gen_fn('Iq', pars, model_info.Iq)) 455 if model_info.Iqxy is not None: 457 456 pars = [qx, qy] + partable.iqxy_parameters 458 source.append(_gen_fn('Iqxy', pars, model_info ['Iqxy']))457 source.append(_gen_fn('Iqxy', pars, model_info.Iqxy)) 459 458 460 459 # Define the parameter table … … 494 493 495 494 # define the Iq kernel 496 source.append("#define KERNEL_NAME %s_Iq"%model_info ['name'])495 source.append("#define KERNEL_NAME %s_Iq"%model_info.name) 497 496 source.append(call_iq) 498 497 source.append(kernel_code) … … 501 500 502 501 # define the Iqxy kernel from the same source with different #defines 503 source.append("#define KERNEL_NAME %s_Iqxy"%model_info ['name'])502 source.append("#define KERNEL_NAME %s_Iqxy"%model_info.name) 504 503 source.append(call_iqxy) 505 504 source.append(kernel_code) … … 508 507 509 508 return '\n'.join(source) 510 511 class CoordinationDetails(object):512 def __init__(self, model_info):513 parameters = model_info['parameters']514 max_pd = parameters.max_pd515 npars = parameters.npars516 par_offset = 4*max_pd517 self.details = np.zeros(par_offset + 3*npars + 4, 'i4')518 519 # generate views on different parts of the array520 self._pd_par = self.details[0*max_pd:1*max_pd]521 self._pd_length = self.details[1*max_pd:2*max_pd]522 self._pd_offset = self.details[2*max_pd:3*max_pd]523 self._pd_stride = self.details[3*max_pd:4*max_pd]524 self._par_offset = self.details[par_offset+0*npars:par_offset+1*npars]525 self._par_coord = self.details[par_offset+1*npars:par_offset+2*npars]526 self._pd_coord = self.details[par_offset+2*npars:par_offset+3*npars]527 528 # theta_par is fixed529 self.details[-1] = parameters.theta_offset530 531 @property532 def ctypes(self): return self.details.ctypes533 534 @property535 def pd_par(self): return self._pd_par536 537 @property538 def pd_length(self): return self._pd_length539 540 @property541 def pd_offset(self): return self._pd_offset542 543 @property544 def pd_stride(self): return self._pd_stride545 546 @property547 def pd_coord(self): return self._pd_coord548 549 @property550 def par_coord(self): return self._par_coord551 552 @property553 def par_offset(self): return self._par_offset554 555 @property556 def num_active(self): return self.details[-4]557 @num_active.setter558 def num_active(self, v): self.details[-4] = v559 560 @property561 def total_pd(self): return self.details[-3]562 @total_pd.setter563 def total_pd(self, v): self.details[-3] = v564 565 @property566 def num_coord(self): return self.details[-2]567 @num_coord.setter568 def num_coord(self, v): self.details[-2] = v569 570 @property571 def theta_par(self): return self.details[-1]572 573 def show(self):574 print("total_pd", self.total_pd)575 print("num_active", self.num_active)576 print("pd_par", self.pd_par)577 print("pd_length", self.pd_length)578 print("pd_offset", self.pd_offset)579 print("pd_stride", self.pd_stride)580 print("par_offsets", self.par_offset)581 print("num_coord", self.num_coord)582 print("par_coord", self.par_coord)583 print("pd_coord", self.pd_coord)584 print("theta par", self.details[-1])585 586 def mono_details(model_info):587 details = CoordinationDetails(model_info)588 # The zero defaults for monodisperse systems are mostly fine589 details.par_offset[:] = np.arange(2, len(details.par_offset)+2)590 return details591 592 def poly_details(model_info, weights):593 #print("weights",weights)594 weights = weights[2:] # Skip scale and background595 596 # Decreasing list of polydispersity lengths597 # Note: the reversing view, x[::-1], does not require a copy598 pd_length = np.array([len(w) for w in weights])599 num_active = np.sum(pd_length>1)600 if num_active > model_info['parameters'].max_pd:601 raise ValueError("Too many polydisperse parameters")602 603 pd_offset = np.cumsum(np.hstack((0, pd_length)))604 idx = np.argsort(pd_length)[::-1][:num_active]605 par_length = np.array([max(len(w),1) for w in weights])606 pd_stride = np.cumprod(np.hstack((1, par_length[idx])))607 par_offsets = np.cumsum(np.hstack((2, par_length)))608 609 details = CoordinationDetails(model_info)610 details.pd_par[:num_active] = idx611 details.pd_length[:num_active] = pd_length[idx]612 details.pd_offset[:num_active] = pd_offset[idx]613 details.pd_stride[:num_active] = pd_stride[:-1]614 details.par_offset[:] = par_offsets[:-1]615 details.total_pd = pd_stride[-1]616 details.num_active = num_active617 # Without constraints coordinated parameters are just the pd parameters618 details.par_coord[:num_active] = idx619 details.pd_coord[:num_active] = 2**np.arange(num_active)620 details.num_coord = num_active621 #details.show()622 return details623 624 def constrained_poly_details(model_info, weights, constraints):625 # Need to find the independently varying pars and sort them626 # Need to build a coordination list for the dependent variables627 # Need to generate a constraints function which takes values628 # and weights, returning par blocks629 raise NotImplementedError("Can't handle constraints yet")630 631 632 def create_vector_Iq(model_info):633 """634 Define Iq as a vector function if it exists.635 """636 Iq = model_info['Iq']637 if callable(Iq) and not getattr(Iq, 'vectorized', False):638 def vector_Iq(q, *args):639 """640 Vectorized 1D kernel.641 """642 return np.array([Iq(qi, *args) for qi in q])643 model_info['Iq'] = vector_Iq644 645 def create_vector_Iqxy(model_info):646 """647 Define Iqxy as a vector function if it exists, or default it from Iq().648 """649 Iq, Iqxy = model_info['Iq'], model_info['Iqxy']650 if callable(Iqxy) and not getattr(Iqxy, 'vectorized', False):651 def vector_Iqxy(qx, qy, *args):652 """653 Vectorized 2D kernel.654 """655 return np.array([Iqxy(qxi, qyi, *args) for qxi, qyi in zip(qx, qy)])656 model_info['Iqxy'] = vector_Iqxy657 elif callable(Iq):658 # Iq is vectorized because create_vector_Iq was already called.659 def default_Iqxy(qx, qy, *args):660 """661 Default 2D kernel.662 """663 return Iq(np.sqrt(qx**2 + qy**2), *args)664 model_info['Iqxy'] = default_Iqxy665 666 def create_default_functions(model_info):667 """668 Autogenerate missing functions, such as Iqxy from Iq.669 670 This only works for Iqxy when Iq is written in python. :func:`make_source`671 performs a similar role for Iq written in C. This also vectorizes672 any functions that are not already marked as vectorized.673 """674 create_vector_Iq(model_info)675 create_vector_Iqxy(model_info) # call create_vector_Iq() first676 509 677 510 def load_kernel_module(model_name): … … 685 518 686 519 687 def make_model_info(kernel_module):688 """689 Interpret the model definition file, categorizing the parameters.690 691 The module can be loaded with a normal python import statement if you692 know which module you need, or with __import__('sasmodels.model.'+name)693 if the name is in a string.694 695 The *model_info* structure contains the following fields:696 697 * *id* is the id of the kernel698 * *name* is the display name of the kernel699 * *filename* is the full path to the module defining the file (if any)700 * *title* is a short description of the kernel701 * *description* is a long description of the kernel (this doesn't seem702 very useful since the Help button on the model page brings you directly703 to the documentation page)704 * *docs* is the docstring from the module. Use :func:`make_doc` to705 * *category* specifies the model location in the docs706 * *parameters* is the model parameter table707 * *single* is True if the model allows single precision708 * *structure_factor* is True if the model is useable in a product709 * *variant_info* contains the information required to select between710 model variants (e.g., the list of cases) or is None if there are no711 model variants712 * *par_type* categorizes the model parameters. See713 :func:`categorize_parameters` for details.714 * *demo* contains the *{parameter: value}* map used in compare (and maybe715 for the demo plot, if plots aren't set up to use the default values).716 If *demo* is not given in the file, then the default values will be used.717 * *tests* is a set of tests that must pass718 * *source* is the list of library files to include in the C model build719 * *Iq*, *Iqxy*, *form_volume*, *ER*, *VR* and *sesans* are python functions720 implementing the kernel for the module, or None if they are not721 defined in python722 * *composition* is None if the model is independent, otherwise it is a723 tuple with composition type ('product' or 'mixture') and a list of724 *model_info* blocks for the composition objects. This allows us to725 build complete product and mixture models from just the info.726 """727 # TODO: maybe turn model_info into a class ModelDefinition728 #print("make parameter table", kernel_module.parameters)729 parameters = make_parameter_table(kernel_module.parameters)730 filename = abspath(kernel_module.__file__)731 kernel_id = splitext(basename(filename))[0]732 name = getattr(kernel_module, 'name', None)733 if name is None:734 name = " ".join(w.capitalize() for w in kernel_id.split('_'))735 model_info = dict(736 id=kernel_id, # string used to load the kernel737 filename=abspath(kernel_module.__file__),738 name=name,739 title=getattr(kernel_module, 'title', name+" model"),740 description=getattr(kernel_module, 'description', 'no description'),741 parameters=parameters,742 composition=None,743 docs=kernel_module.__doc__,744 category=getattr(kernel_module, 'category', None),745 single=getattr(kernel_module, 'single', True),746 structure_factor=getattr(kernel_module, 'structure_factor', False),747 profile_axes=getattr(kernel_module, 'profile_axes', ['x','y']),748 variant_info=getattr(kernel_module, 'invariant_info', None),749 demo=getattr(kernel_module, 'demo', None),750 source=getattr(kernel_module, 'source', []),751 tests=getattr(kernel_module, 'tests', []),752 )753 set_demo(model_info, getattr(kernel_module, 'demo', None))754 # Check for optional functions755 functions = "ER VR form_volume Iq Iqxy profile sesans".split()756 model_info.update((k, getattr(kernel_module, k, None)) for k in functions)757 create_default_functions(model_info)758 # Precalculate the monodisperse parameters759 # TODO: make this a lazy evaluator760 # make_model_info is called for every model on sasview startup761 model_info['mono_details'] = mono_details(model_info)762 return model_info763 520 764 521 section_marker = re.compile(r'\A(?P<first>[%s])(?P=first)*\Z' … … 801 558 Iq_units = "The returned value is scaled to units of |cm^-1| |sr^-1|, absolute scale." 802 559 Sq_units = "The returned value is a dimensionless structure factor, $S(q)$." 803 docs = convert_section_titles_to_boldface(model_info ['docs'])804 subst = dict(id=model_info ['id'].replace('_', '-'),805 name=model_info ['name'],806 title=model_info ['title'],807 parameters=make_partable(model_info ['parameters']),808 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, 809 566 docs=docs) 810 567 return DOC_HEADER % subst … … 815 572 Show how long it takes to process a model. 816 573 """ 574 import datetime 575 from .modelinfo import make_model_info 817 576 from .models import cylinder 818 import datetime 577 819 578 tic = datetime.datetime.now() 820 579 make_source(make_model_info(cylinder)) … … 827 586 """ 828 587 import sys 588 from .modelinfo import make_model_info 589 829 590 if len(sys.argv) <= 1: 830 591 print("usage: python -m sasmodels.generate modelname") -
sasmodels/kernelcl.py
rd2bb604 r6d6508e 342 342 if self.program is None: 343 343 compiler = environment().compile_program 344 self.program = compiler(self.info ['name'], self.source,344 self.program = compiler(self.info.name, self.source, 345 345 self.dtype, self.fast) 346 346 is_2d = len(q_vectors) == 2 … … 354 354 """ 355 355 if self.program is not None: 356 environment().release_program(self.info ['name'])356 environment().release_program(self.info.name) 357 357 self.program = None 358 358 … … 441 441 """ 442 442 def __init__(self, kernel, model_info, q_vectors, dtype): 443 max_pd = model_info ['max_pd']444 npars = len(model_info ['parameters'])-2443 max_pd = model_info.max_pd 444 npars = len(model_info.parameters)-2 445 445 q_input = GpuInput(q_vectors, dtype) 446 446 self.dtype = dtype … … 465 465 self._need_release = [ self.result_b, self.q_input ] 466 466 467 def __call__(self, details, weights, values, cutoff):467 def __call__(self, call_details, weights, values, cutoff): 468 468 real = (np.float32 if self.q_input.dtype == generate.F32 469 469 else np.float64 if self.q_input.dtype == generate.F64 470 470 else np.float16 if self.q_input.dtype == generate.F16 471 471 else np.float32) # will never get here, so use np.float32 472 assert details.dtype == np.int32472 assert call_details.dtype == np.int32 473 473 assert weights.dtype == real and values.dtype == real 474 474 475 475 context = self.queue.context 476 476 details_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 477 hostbuf= details)477 hostbuf=call_details) 478 478 weights_b = cl.Buffer(context, mf.READ_ONLY | mf.COPY_HOST_PTR, 479 479 hostbuf=weights) -
sasmodels/kerneldll.py
r1e2a1ba r6d6508e 55 55 56 56 from . import generate 57 from . import details 57 58 from .kernelpy import PyInput, PyModel 58 59 from .exception import annotate_exception … … 95 96 """ 96 97 bits = 8*dtype.itemsize 97 return "sas_%s%d"%(model_info ['id'], bits)98 return "sas_%s%d"%(model_info.id, bits) 98 99 99 100 def dll_path(model_info, dtype): … … 123 124 models are allowed as DLLs. 124 125 """ 125 if callable(model_info. get('Iq', None)):126 if callable(model_info.Iq): 126 127 return PyModel(model_info) 127 128 … … 259 260 self.result = np.empty(q_input.nq+3, q_input.dtype) 260 261 261 def __call__(self, details, weights, values, cutoff):262 def __call__(self, call_details, weights, values, cutoff): 262 263 real = (np.float32 if self.q_input.dtype == generate.F32 263 264 else np.float64 if self.q_input.dtype == generate.F64 264 265 else np.float128) 265 assert isinstance( details, generate.CoordinationDetails)266 assert isinstance(call_details, details.CallDetails) 266 267 assert weights.dtype == real and values.dtype == real 267 268 268 start, stop = 0, details.total_pd269 start, stop = 0, call_details.total_pd 269 270 #print("in kerneldll") 270 271 #print("weights", weights) … … 274 275 start, # pd_start 275 276 stop, # pd_stop pd_stride[MAX_PD] 276 details.ctypes.data, # problem277 call_details.ctypes.data, # problem 277 278 weights.ctypes.data, # weights 278 279 values.ctypes.data, #pars -
sasmodels/kernelpy.py
ra8a7f08 r6d6508e 17 17 """ 18 18 def __init__(self, model_info): 19 # Make sure Iq and Iqxy are available and vectorized 20 _create_default_functions(model_info) 19 21 self.info = model_info 20 22 21 23 def make_kernel(self, q_vectors): 22 24 q_input = PyInput(q_vectors, dtype=F64) 23 kernel = self.info ['Iqxy'] if q_input.is_2d else self.info['Iq']25 kernel = self.info.Iqxy if q_input.is_2d else self.info.Iq 24 26 return PyKernel(kernel, self.info, q_input) 25 27 … … 94 96 self.dim = '2d' if q_input.is_2d else '1d' 95 97 96 partable = model_info ['parameters']98 partable = model_info.parameters 97 99 kernel_parameters = (partable.iqxy_parameters if q_input.is_2d 98 100 else partable.iq_parameters) … … 129 131 # parameter array. 130 132 if q_input.is_2d: 131 form = model_info ['Iqxy']133 form = model_info.Iqxy 132 134 qx, qy = q_input.q[:,0], q_input.q[:,1] 133 135 self._form = lambda: form(qx, qy, *kernel_args) 134 136 else: 135 form = model_info ['Iq']137 form = model_info.Iq 136 138 q = q_input.q 137 139 self._form = lambda: form(q, *kernel_args) 138 140 139 141 # Generate a closure which calls the form_volume if it exists. 140 form_volume = model_info ['form_volume']142 form_volume = model_info.form_volume 141 143 self._volume = ((lambda: form_volume(*volume_args)) if form_volume 142 144 else (lambda: 1.0)) 143 145 144 def __call__(self, details, weights, values, cutoff):145 # type: (.generate.C oordinationDetails, np.ndarray, np.ndarray, float) -> np.ndarray146 def __call__(self, call_details, weights, values, cutoff): 147 # type: (.generate.CallDetails, np.ndarray, np.ndarray, float) -> np.ndarray 146 148 res = _loops(self._parameter_vector, self._form, self._volume, 147 self.q_input.nq, details, weights, values, cutoff)149 self.q_input.nq, call_details, weights, values, cutoff) 148 150 return res 149 151 … … 158 160 form_volume, # type: Callable[[], float] 159 161 nq, # type: int 160 details, # type: .generate.CoordinationDetails162 call_details,# type: .generate.CallDetails 161 163 weights, # type: np.ndarray 162 164 values, # type: np.ndarray … … 172 174 # # 173 175 ################################################################ 174 parameters[:] = values[ details.par_offset]176 parameters[:] = values[call_details.par_offset] 175 177 scale, background = values[0], values[1] 176 if details.num_active == 0:178 if call_details.num_active == 0: 177 179 norm = float(form_volume()) 178 180 if norm > 0.0: … … 183 185 partial_weight = np.NaN 184 186 spherical_correction = 1.0 185 pd_stride = details.pd_stride[:details.num_active]186 pd_length = details.pd_length[:details.num_active]187 pd_offset = details.pd_offset[:details.num_active]187 pd_stride = call_details.pd_stride[:call_details.num_active] 188 pd_length = call_details.pd_length[:call_details.num_active] 189 pd_offset = call_details.pd_offset[:call_details.num_active] 188 190 pd_index = np.empty_like(pd_offset) 189 offset = np.empty_like( details.par_offset)190 theta = details.theta_par191 offset = np.empty_like(call_details.par_offset) 192 theta = call_details.theta_par 191 193 fast_length = pd_length[0] 192 194 pd_index[0] = fast_length 193 195 total = np.zeros(nq, 'd') 194 196 norm = 0.0 195 for loop_index in range( details.total_pd):197 for loop_index in range(call_details.total_pd): 196 198 # update polydispersity parameter values 197 199 if pd_index[0] == fast_length: 198 200 pd_index[:] = (loop_index/pd_stride)%pd_length 199 201 partial_weight = np.prod(weights[pd_offset+pd_index][1:]) 200 for k in range( details.num_coord):201 par = details.par_coord[k]202 coord = details.pd_coord[k]203 this_offset = details.par_offset[par]202 for k in range(call_details.num_coord): 203 par = call_details.par_coord[k] 204 coord = call_details.pd_coord[k] 205 this_offset = call_details.par_offset[par] 204 206 block_size = 1 205 207 for bit in xrange(32): … … 211 213 offset[par] = this_offset 212 214 parameters[par] = values[this_offset] 213 if par == theta and not ( details.par_coord[k]&1):215 if par == theta and not (call_details.par_coord[k]&1): 214 216 spherical_correction = max(abs(cos(pi/180 * parameters[theta])), 1e-6) 215 for k in range( details.num_coord):216 if details.pd_coord[k]&1:217 #par = details.par_coord[k]217 for k in range(call_details.num_coord): 218 if call_details.pd_coord[k]&1: 219 #par = call_details.par_coord[k] 218 220 parameters[par] = values[offset[par]] 219 221 #print "par",par,offset[par],parameters[par+2] … … 241 243 else: 242 244 return np.ones(nq, 'd')*background 245 246 247 248 def _create_vector_Iq(model_info): 249 """ 250 Define Iq as a vector function if it exists. 251 """ 252 Iq = model_info.Iq 253 if callable(Iq) and not getattr(Iq, 'vectorized', False): 254 def vector_Iq(q, *args): 255 """ 256 Vectorized 1D kernel. 257 """ 258 return np.array([Iq(qi, *args) for qi in q]) 259 vector_Iq.vectorized = True 260 model_info.Iq = vector_Iq 261 262 def _create_vector_Iqxy(model_info): 263 """ 264 Define Iqxy as a vector function if it exists, or default it from Iq(). 265 """ 266 Iq, Iqxy = model_info.Iq, model_info.Iqxy 267 if callable(Iqxy) and not getattr(Iqxy, 'vectorized', False): 268 def vector_Iqxy(qx, qy, *args): 269 """ 270 Vectorized 2D kernel. 271 """ 272 return np.array([Iqxy(qxi, qyi, *args) for qxi, qyi in zip(qx, qy)]) 273 vector_Iqxy.vectorized = True 274 model_info.Iqxy = vector_Iqxy 275 elif callable(Iq): 276 # Iq is vectorized because create_vector_Iq was already called. 277 def default_Iqxy(qx, qy, *args): 278 """ 279 Default 2D kernel. 280 """ 281 return Iq(np.sqrt(qx**2 + qy**2), *args) 282 default_Iqxy.vectorized = True 283 model_info.Iqxy = default_Iqxy 284 285 def _create_default_functions(model_info): 286 """ 287 Autogenerate missing functions, such as Iqxy from Iq. 288 289 This only works for Iqxy when Iq is written in python. :func:`make_source` 290 performs a similar role for Iq written in C. This also vectorizes 291 any functions that are not already marked as vectorized. 292 """ 293 _create_vector_Iq(model_info) 294 _create_vector_Iqxy(model_info) # call create_vector_Iq() first 295 -
sasmodels/list_pars.py
r21b116f r6d6508e 25 25 for name in sorted(MODELS): 26 26 model_info = load_model_info(name) 27 for p in model_info ['parameters'].kernel_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
r69aa451 r6d6508e 14 14 import numpy as np 15 15 16 from .modelinfo import Parameter, ParameterTable 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): … … 50 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']= partable61 #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)63 model_info.composition = ('mixture', parts) 69 64 70 65 … … 108 103 if dim == '2d': 109 104 for p in kernels: 110 partype = p.info ['partype']105 partype = p.info.partype 111 106 accumulate(partype['fixed-2d'], partype['pd-2d'], partype['volume']) 112 107 else: 113 108 for p in kernels: 114 partype = p.info ['partype']109 partype = p.info.partype 115 110 accumulate(partype['fixed-1d'], partype['pd-1d'], partype['volume']) 116 111 -
sasmodels/model_test.py
r6e7ff6d r6d6508e 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 58 56 #TODO: rename to tests so that tab completion works better for models directory 59 60 def call_ER(model_info, values): 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 vol_pars = [get_weights(parameter, values) 70 for parameter in model_info.parameters.call_parameters 71 if parameter.type == 'volume'] 72 value, weight = dispersion_mesh(vol_pars) 73 individual_radii = model_info.ER(*value) 74 return np.sum(weight*individual_radii) / np.sum(weight) 75 76 def call_VR(model_info, values): 77 """ 78 Call the model VR function using *pars*. 79 *info* is either *model.info* if you have a loaded model, or *kernel.info* 80 if you have a model kernel prepared for evaluation. 81 """ 82 if model_info.VR is None: 83 return 1.0 84 else: 85 vol_pars = [get_weights(parameter, values) 86 for parameter in model_info.parameters.call_parameters 87 if parameter.type == 'volume'] 88 value, weight = dispersion_mesh(vol_pars) 89 whole, part = model_info.VR(*value) 90 return np.sum(weight*part)/np.sum(weight*whole) 91 57 92 58 93 def make_suite(loaders, models): … … 86 121 # don't try to call cl kernel since it will not be 87 122 # available in some environmentes. 88 is_py = callable(model_info ['Iq'])123 is_py = callable(model_info.Iq) 89 124 90 125 if is_py: # kernel implemented in python … … 151 186 ] 152 187 153 tests = self.info ['tests']188 tests = self.info.tests 154 189 try: 155 190 model = build_model(self.info, dtype=self.dtype, -
sasmodels/modelinfo.py
r6e7ff6d r6d6508e 1 from copy import copy 2 from os.path import abspath, basename, splitext 1 3 2 4 import numpy as np 3 5 4 # TODO: turn ModelInfo into a proper class 5 ModelInfo = dict 6 from .details import mono_details 6 7 7 8 MAX_PD = 4 … … 116 117 117 118 118 def set_demo(model_info, demo): 119 """ 120 Assign demo parameters to model_info['demo'] 121 122 If demo is not defined, then use defaults. 123 124 If demo is defined, override defaults with value from demo. 125 126 If demo references vector fields, such as thickness[n], then support 119 def make_demo_pars(partable, demo): 120 """ 121 Create demo parameter set from key-value pairs. 122 123 *demo* are the key-value pairs to use for the demo parameters. Any 124 parameters not specified in *demo* are set from the *partable* defaults. 125 126 If *demo* references vector fields, such as thickness[n], then support 127 127 different ways of assigning the demo values, including assigning a 128 128 specific value (e.g., thickness3=50.0), assigning a new value to all 129 129 (e.g., thickness=50.0) or assigning values using list notation. 130 130 """ 131 partable = model_info['parameters']132 131 if demo is None: 133 132 result = partable.defaults … … 157 156 result.update(demo) 158 157 159 model_info['demo'] = result 158 return result 159 160 def prefix_parameter(par, prefix): 161 """ 162 Return a copy of the parameter with its name prefixed. 163 """ 164 new_par = copy(par) 165 new_par.name = prefix + par.name 166 new_par.id = prefix + par.id 167 168 def suffix_parameter(par, suffix): 169 """ 170 Return a copy of the parameter with its name prefixed. 171 """ 172 new_par = copy(par) 173 # If name has the form x[n], replace with x_suffix[n] 174 new_par.name = par.id + suffix + par.name[len(par.id):] 175 new_par.id = par.id + suffix 160 176 161 177 class Parameter(object): … … 517 533 return isinstance(x, str) 518 534 535 def make_model_info(kernel_module): 536 info = ModelInfo() 537 #print("make parameter table", kernel_module.parameters) 538 parameters = make_parameter_table(kernel_module.parameters) 539 demo = make_demo_pars(parameters, getattr(kernel_module, 'demo', None)) 540 filename = abspath(kernel_module.__file__) 541 kernel_id = splitext(basename(filename))[0] 542 name = getattr(kernel_module, 'name', None) 543 if name is None: 544 name = " ".join(w.capitalize() for w in kernel_id.split('_')) 545 546 info.id = kernel_id # string used to load the kernel 547 info.filename = abspath(kernel_module.__file__) 548 info.name = name 549 info.title = getattr(kernel_module, 'title', name+" model") 550 info.description = getattr(kernel_module, 'description', 'no description') 551 info.parameters = parameters 552 info.demo = demo 553 info.composition = None 554 info.docs = kernel_module.__doc__ 555 info.category = getattr(kernel_module, 'category', None) 556 info.single = getattr(kernel_module, 'single', True) 557 info.structure_factor = getattr(kernel_module, 'structure_factor', False) 558 info.profile_axes = getattr(kernel_module, 'profile_axes', ['x','y']) 559 info.variant_info = getattr(kernel_module, 'invariant_info', None) 560 info.demo = getattr(kernel_module, 'demo', None) 561 info.source = getattr(kernel_module, 'source', []) 562 info.tests = getattr(kernel_module, 'tests', []) 563 info.ER = getattr(kernel_module, 'ER', None) 564 info.VR = getattr(kernel_module, 'VR', None) 565 info.form_volume = getattr(kernel_module, 'form_volume', None) 566 info.Iq = getattr(kernel_module, 'Iq', None) 567 info.Iqxy = getattr(kernel_module, 'Iqxy', None) 568 info.profile = getattr(kernel_module, 'profile', None) 569 info.sesans = getattr(kernel_module, 'sesans', None) 570 571 # Precalculate the monodisperse parameter details 572 info.mono_details = mono_details(info) 573 return info 574 575 class ModelInfo(object): 576 """ 577 Interpret the model definition file, categorizing the parameters. 578 579 The module can be loaded with a normal python import statement if you 580 know which module you need, or with __import__('sasmodels.model.'+name) 581 if the name is in a string. 582 583 The *model_info* structure contains the following fields: 584 585 * *id* is the id of the kernel 586 * *name* is the display name of the kernel 587 * *filename* is the full path to the module defining the file (if any) 588 * *title* is a short description of the kernel 589 * *description* is a long description of the kernel (this doesn't seem 590 very useful since the Help button on the model page brings you directly 591 to the documentation page) 592 * *docs* is the docstring from the module. Use :func:`make_doc` to 593 * *category* specifies the model location in the docs 594 * *parameters* is the model parameter table 595 * *single* is True if the model allows single precision 596 * *structure_factor* is True if the model is useable in a product 597 * *variant_info* contains the information required to select between 598 model variants (e.g., the list of cases) or is None if there are no 599 model variants 600 * *par_type* categorizes the model parameters. See 601 :func:`categorize_parameters` for details. 602 * *demo* contains the *{parameter: value}* map used in compare (and maybe 603 for the demo plot, if plots aren't set up to use the default values). 604 If *demo* is not given in the file, then the default values will be used. 605 * *tests* is a set of tests that must pass 606 * *source* is the list of library files to include in the C model build 607 * *Iq*, *Iqxy*, *form_volume*, *ER*, *VR* and *sesans* are python functions 608 implementing the kernel for the module, or None if they are not 609 defined in python 610 * *composition* is None if the model is independent, otherwise it is a 611 tuple with composition type ('product' or 'mixture') and a list of 612 *model_info* blocks for the composition objects. This allows us to 613 build complete product and mixture models from just the info. 614 """ 615 id = None 616 filename = None 617 name = None 618 title = None 619 description = None 620 parameters = None 621 demo = None 622 composition = None 623 docs = None 624 category = None 625 single = None 626 structure_factor = None 627 profile_axes = None 628 variant_info = None 629 demo = None 630 source = None 631 tests = None 632 ER = None 633 VR = None 634 form_volume = None 635 Iq = None 636 Iqxy = None 637 profile = None 638 sesans = None 639 mono_details = None 640 641 def __init__(self): 642 pass 643 644 -
sasmodels/product.py
rea05c87 r6d6508e 13 13 import numpy as np 14 14 15 from .core import call_ER_VR 15 from .details import dispersion_mesh 16 from .modelinfo import suffix_parameter, ParameterTable, Parameter, ModelInfo 16 17 17 SCALE=0 18 BACKGROUND=1 19 RADIUS_EFFECTIVE=2 20 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 #] 21 23 22 24 # TODO: core_shell_sphere model has suppressed the volume ratio calculation … … 26 28 Create info block for product model. 27 29 """ 28 p_id, p_name, p_pars = p_info['id'], p_info['name'], p_info['parameters'] 29 s_id, s_name, s_pars = s_info['id'], s_info['name'], s_info['parameters'] 30 # We require models to start with scale and background 31 assert s_pars[SCALE].name == 'scale' 32 assert s_pars[BACKGROUND].name == 'background' 33 # We require structure factors to start with effect radius and volfraction 34 assert s_pars[RADIUS_EFFECTIVE].name == 'radius_effective' 35 assert s_pars[VOLFRACTION].name == 'volfraction' 36 # Combine the parameter sets. We are skipping the first three 37 # parameters of S since scale, background are defined in P and 38 # effect_radius is set from P.ER(). 39 pars = p_pars + s_pars[3:] 40 # check for duplicates; can't use assertion since they may never be checked 41 if len(set(p.name for p in pars)) != len(pars): 42 raise ValueError("Duplicate parameters in %s and %s"%(p_id)) 43 model_info = {} 44 model_info['id'] = '*'.join((p_id, s_id)) 45 model_info['name'] = ' X '.join((p_name, s_name)) 46 model_info['filename'] = None 47 model_info['title'] = 'Product of %s and structure factor %s'%(p_name, s_name) 48 model_info['description'] = model_info['title'] 49 model_info['docs'] = model_info['title'] 50 model_info['category'] = "custom" 51 model_info['parameters'] = pars 52 #model_info['single'] = p_info['single'] and s_info['single'] 53 model_info['structure_factor'] = False 54 model_info['variant_info'] = None 55 #model_info['tests'] = [] 56 #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 = [] 57 58 # Iq, Iqxy, form_volume, ER, VR and sesans 58 model_info ['composition']= ('product', [p_info, s_info])59 model_info.composition = ('product', [p_info, s_info]) 59 60 return model_info 60 61 … … 86 87 class ProductKernel(object): 87 88 def __init__(self, model_info, p_kernel, s_kernel): 88 dim = '2d' if p_kernel.q_input.is_2d else '1d'89 90 # Need to know if we want 2D and magnetic parameters when constructing91 # a parameter map.92 par_map = {}93 p_info = p_kernel.info['par_type']94 s_info = s_kernel.info['par_type']95 vol_pars = set(p_info['volume'])96 if dim == '2d':97 num_p_fixed = len(p_info['fixed-2d'])98 num_p_pd = len(p_info['pd-2d'])99 num_s_fixed = len(s_info['fixed-2d'])100 num_s_pd = len(s_info['pd-2d']) - 1 # exclude effect_radius101 # volume parameters are amongst the pd pars for P, not S102 vol_par_idx = [k for k,v in enumerate(p_info['pd-2d'])103 if v in vol_pars]104 else:105 num_p_fixed = len(p_info['fixed-1d'])106 num_p_pd = len(p_info['pd-1d'])107 num_s_fixed = len(s_info['fixed-1d'])108 num_s_pd = len(s_info['pd-1d']) - 1 # exclude effect_radius109 # volume parameters are amongst the pd pars for P, not S110 vol_par_idx = [k for k,v in enumerate(p_info['pd-1d'])111 if v in vol_pars]112 113 start = 0114 par_map['p_fixed'] = np.arange(start, start+num_p_fixed)115 # User doesn't set scale, background or effect_radius for S in P*S,116 # so borrow values from end of p_fixed. This makes volfraction the117 # first S parameter.118 start += num_p_fixed119 par_map['s_fixed'] = np.hstack(([start,start],120 np.arange(start, start+num_s_fixed-2)))121 par_map['volfraction'] = num_p_fixed122 start += num_s_fixed-2123 # vol pars offset from the start of pd pars124 par_map['vol_pars'] = [start+k for k in vol_par_idx]125 par_map['p_pd'] = np.arange(start, start+num_p_pd)126 start += num_p_pd-1127 par_map['s_pd'] = np.hstack((start,128 np.arange(start, start+num_s_pd-1)))129 130 self.fixed_pars = model_info['partype']['fixed-' + dim]131 self.pd_pars = model_info['partype']['pd-' + dim]132 89 self.info = model_info 133 90 self.p_kernel = p_kernel 134 91 self.s_kernel = s_kernel 135 self.par_map = par_map136 92 137 def __call__(self, fixed_pars, pd_pars, cutoff=1e-5): 138 pars = fixed_pars + pd_pars 139 scale = pars[SCALE] 140 background = pars[BACKGROUND] 141 s_volfraction = pars[self.par_map['volfraction']] 142 p_fixed = [pars[k] for k in self.par_map['p_fixed']] 143 s_fixed = [pars[k] for k in self.par_map['s_fixed']] 144 p_pd = [pars[k] for k in self.par_map['p_pd']] 145 s_pd = [pars[k] for k in self.par_map['s_pd']] 146 vol_pars = [pars[k] for k in self.par_map['vol_pars']] 147 93 def __call__(self, details, weights, values, cutoff): 148 94 effect_radius, vol_ratio = call_ER_VR(self.p_kernel.info, vol_pars) 149 95 … … 155 101 s_pd[0] = [effect_radius], [1.0] 156 102 157 p_res = self.p_kernel(p_ fixed, p_pd, cutoff)158 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) 159 105 #print s_fixed, s_pd, p_fixed, p_pd 160 106 … … 165 111 self.q_kernel.release() 166 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
rd2bb604 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 par_set = set([p.name for p in form.info ['parameters'].call_parameters])504 par_set = set([p.name for p in form.info.parameters.call_parameters]) 505 505 if any(k not in par_set for k in pars.keys()): 506 506 extra = set(pars.keys()) - par_set … … 557 557 from scipy.integrate import romberg 558 558 559 par_set = set([p.name for p in form.info ['parameters'].call_parameters])559 par_set = set([p.name for p in form.info.parameters.call_parameters]) 560 560 if any(k not in par_set for k in pars.keys()): 561 561 extra = set(pars.keys()) - par_set … … 694 694 695 695 def _eval_sphere(self, pars, resolution): 696 from sasmodels import core696 from sasmodels import direct_model 697 697 kernel = self.model.make_kernel([resolution.q_calc]) 698 theory = core.call_kernel(kernel, pars)698 theory = direct_model.call_kernel(kernel, pars) 699 699 result = resolution.apply(theory) 700 700 kernel.release() … … 1045 1045 import sys 1046 1046 from sasmodels import core 1047 from sasmodels import direct_model 1047 1048 name = sys.argv[1] if len(sys.argv) > 1 else 'cylinder' 1048 1049 … … 1065 1066 1066 1067 kernel = model.make_kernel([resolution.q_calc]) 1067 theory = core.call_kernel(kernel, pars)1068 theory = direct_model.call_kernel(kernel, pars) 1068 1069 Iq = resolution.apply(theory) 1069 1070 -
sasmodels/sasview_model.py
ree8f734 r6d6508e 27 27 from . import generate 28 28 from . import weights 29 from . import details 30 from . import modelinfo 29 31 30 32 def load_standard_models(): … … 49 51 """ 50 52 kernel_module = custom.load_custom_kernel_module(path) 51 model_info = generate.make_model_info(kernel_module)53 model_info = modelinfo.make_model_info(kernel_module) 52 54 return _make_model_from_info(model_info) 53 55 … … 62 64 """ 63 65 kernel_module = generate.load_kernel_module(name) 64 model_info = generate.make_model_info(kernel_module)66 model_info = modelinfo.make_model_info(kernel_module) 65 67 return _make_model_from_info(model_info) 66 68 … … 73 75 SasviewModel.__init__(self) 74 76 attrs = dict(__init__=__init__, _model_info=model_info) 75 ConstructedModel = type(model_info ['name'], (SasviewModel,), attrs)77 ConstructedModel = type(model_info.name, (SasviewModel,), attrs) 76 78 return ConstructedModel 77 79 … … 85 87 self._model = None 86 88 model_info = self._model_info 87 parameters = model_info ['parameters']88 89 self.name = model_info ['name']90 self.description = model_info ['description']89 parameters = model_info.parameters 90 91 self.name = model_info.name 92 self.description = model_info.description 91 93 self.category = None 92 94 #self.is_multifunc = False 93 95 for p in parameters.kernel_parameters: 94 96 if p.is_control: 95 profile_axes = model_info ['profile_axes']97 profile_axes = model_info.profile_axes 96 98 self.multiplicity_info = [ 97 99 p.limits[1], p.name, p.choices, profile_axes[0] … … 132 134 133 135 ## independent parameter name and unit [string] 134 self.input_name = model_info.get("input_name", "Q")135 self.input_unit = model_info.get("input_unit", "A^{-1}")136 self.output_name = model_info.get("output_name", "Intensity")137 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}") 138 140 139 141 ## _persistency_dict is used by sas.perspectives.fitting.basepage … … 248 250 # TODO: fix test so that parameter order doesn't matter 249 251 ret = ['%s.%s' % (p.name.lower(), ext) 250 for p in self._model_info ['parameters'].user_parameters()252 for p in self._model_info.parameters.user_parameters() 251 253 for ext in ('npts', 'nsigmas', 'width') 252 254 if p.polydisperse] … … 323 325 # Check whether we have a list of ndarrays [qx,qy] 324 326 qx, qy = qdist 325 if not self._model_info ['parameters'].has_2d:327 if not self._model_info.parameters.has_2d: 326 328 return self.calculate_Iq(np.sqrt(qx ** 2 + qy ** 2)) 327 329 else: … … 350 352 kernel = self._model.make_kernel(q_vectors) 351 353 pairs = [self._get_weights(p) 352 for p in self._model_info ['parameters'].call_parameters]353 details, weights, values = core.build_details(kernel, pairs)354 result = kernel( details, weights, values, cutoff=self.cutoff)354 for p in self._model_info.parameters.call_parameters] 355 call_details, weights, values = details.build_details(kernel, pairs) 356 result = kernel(call_details, weights, values, cutoff=self.cutoff) 355 357 kernel.q_input.release() 356 358 kernel.release() … … 363 365 :return: the value of the effective radius 364 366 """ 365 ER = self._model_info.get('ER', None) 366 if ER is None: 367 if model_info.ER is None: 367 368 return 1.0 368 369 else: 369 370 values, weights = self._dispersion_mesh() 370 fv = ER(*values)371 fv = model_info.ER(*values) 371 372 #print(values[0].shape, weights.shape, fv.shape) 372 373 return np.sum(weights * fv) / np.sum(weights) … … 378 379 :return: the value of the volf ratio 379 380 """ 380 VR = self._model_info.get('VR', None) 381 if VR is None: 381 if model_info.VR is None: 382 382 return 1.0 383 383 else: 384 384 values, weights = self._dispersion_mesh() 385 whole, part = VR(*values)385 whole, part = model_info.VR(*values) 386 386 return np.sum(weights * part) / np.sum(weights * whole) 387 387 … … 422 422 parameter set in the vector. 423 423 """ 424 pars = self._model_info ['partype']['volume']425 return core.dispersion_mesh([self._get_weights(p) for p in pars])424 pars = self._model_info.partype['volume'] 425 return details.dispersion_mesh([self._get_weights(p) for p in pars]) 426 426 427 427 def _get_weights(self, par):
Note: See TracChangeset
for help on using the changeset viewer.