Changeset d19962c in sasmodels
- Timestamp:
- Mar 27, 2016 6:57:03 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:
- 5c028e3
- Parents:
- c499331
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
example/polynomial.py
rc499331 rd19962c 1 1 from numpy import inf 2 2 parameters = [ 3 ["n", "", 0, [0,5], "", "polynomial degree"],3 ["n", "", 1, [1,5], "", "number of coefficients (or degree+1)"], 4 4 ["c[n]", "", 0, [-inf, inf], "", "coefficients to c_n x^n"], 5 5 ] 6 6 7 Iq = """7 Iq = r""" 8 8 int int_n = (int)n; 9 double result = c[int_n ];10 for (int k=int_n- 1; k >= 0; k--) { result = result*q + c[k]; }9 double result = c[int_n-1]; 10 for (int k=int_n-2; k >= 0; k--) { result = result*q + c[k]; } 11 11 return result; 12 12 """ -
sasmodels/bumps_model.py
re9b1663d rd19962c 81 81 from bumps.names import Parameter 82 82 83 pars = {} # => floating point parameters 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['par_type']['pd']: 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 = {} # => distribution names 98 for name in model_info['par_type']['pd']: 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
rc499331 rd19962c 308 308 lines = [] 309 309 parameters = model_info['parameters'] 310 for p in parameters.type['2d' if is2d else '1d']: 311 if p.length > 1: 312 for k in range(p.length): 313 ext = "[%d]"%k 314 fields = dict( 315 value=pars.get(p.id+ext, p.default), 316 pd=pars.get(p.id+"_pd"+ext, 0.), 317 n=int(pars.get(p.id+"_pd_n"+ext, 0)), 318 nsigma=pars.get(p.id+"_pd_nsgima"+ext, 3.), 319 type=pars.get(p.id+"_pd_type"+ext, 'gaussian')) 320 lines.append(_format_par(p.id+ext, **fields)) 321 else: 322 fields = dict( 323 value=pars.get(p.id, p.default), 324 pd=pars.get(p.id+"_pd", 0.), 325 n=int(pars.get(p.id+"_pd_n", 0)), 326 nsigma=pars.get(p.id+"_pd_nsgima", 3.), 327 type=pars.get(p.id+"_pd_type", 'gaussian')) 328 lines.append(_format_par(p.name, **fields)) 310 for p in parameters.user_parameters(pars, is2d): 311 fields = dict( 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')) 317 lines.append(_format_par(p.name, **fields)) 329 318 return "\n".join(lines) 330 319 … … 668 657 # Get the default values for the parameters 669 658 pars = {} 670 for p in model_info['parameters'] :659 for p in model_info['parameters'].call_parameters: 671 660 parts = [('', p.default)] 672 661 if p.polydisperse: … … 677 666 for ext, val in parts: 678 667 if p.length > 1: 679 dict(("%s% s[%d]"%(p.id,ext,k), val) for k in range(p.length))668 dict(("%s%d%s"%(p.id,k,ext), val) for k in range(p.length)) 680 669 else: 681 670 pars[p.id+ext] = val … … 709 698 name = args[0] 710 699 try: 711 if name.endswith('.py'): 712 model_info = core.load_model_info_from_path(name) 713 else: 714 model_info = core.load_model_info(name) 700 model_info = core.load_model_info(name) 715 701 except ImportError, exc: 716 702 print(str(exc)) -
sasmodels/core.py
rc499331 rd19962c 24 24 HAVE_OPENCL = False 25 25 26 __all__ = [27 "list_models", "load_model_info", "precompile_dll",28 "build_model", "call_kernel", "call_ER_VR",29 ]30 31 26 try: 32 27 # Python 3.5 and up … … 44 39 return module 45 40 41 42 43 __all__ = [ 44 "list_models", "load_model_info", "precompile_dll", 45 "build_model", "call_kernel", "call_ER_VR", 46 ] 47 46 48 def list_models(): 47 49 """ … … 65 67 Load model info and build model. 66 68 """ 67 if model_name.endswith('.py'): 68 model_info = load_model_info_from_path(model_name) 69 else: 70 model_info = load_model_info(model_name) 71 return build_model(model_info, **kw) 72 73 def load_model_info_from_path(path): 74 # Pull off the last .ext if it exists; there may be others 75 name = basename(splitext(path)[0]) 76 77 # Not cleaning name since don't need to be able to reload this 78 # model later 79 # Should probably turf the model from sys.modules after we are done... 80 81 # Placing the model in the 'sasmodels.custom' name space, even 82 # though it doesn't actually exist. imp.load_source doesn't seem 83 # to care. 84 import sasmodels.custom 85 kernel_module = load_module('sasmodels.custom.'+name, path) 86 87 # Now that we have the module, we can load it as usual 88 model_info = generate.make_model_info(kernel_module) 89 return model_info 69 return build_model(load_model_info(model_name), **kw) 90 70 91 71 def load_model_info(model_name): … … 110 90 return product.make_product_info(P_info, Q_info) 111 91 92 return make_model_by_name(model_name) 93 94 95 def make_model_by_name(model_name): 96 if model_name.endswith('.py'): 97 path = model_name 98 # Pull off the last .ext if it exists; there may be others 99 name = basename(splitext(path)[0]) 100 # Placing the model in the 'sasmodels.custom' name space. 101 from sasmodels import custom 102 kernel_module = load_module('sasmodels.custom.'+name, path) 103 else: 104 from sasmodels import models 105 __import__('sasmodels.models.'+model_name) 106 kernel_module = getattr(models, model_name, None) 112 107 #import sys; print "\n".join(sys.path) 113 __import__('sasmodels.models.'+model_name)114 kernel_module = getattr(models, model_name, None)115 108 return generate.make_model_info(kernel_module) 116 109 … … 196 189 """ 197 190 value = values.get(parameter.name, parameter.default) 198 if parameter.type not in ('volume', 'orientation'): 199 return [value], [] 200 relative = parameter.type == 'volume' 191 relative = parameter.relative_pd 201 192 limits = parameter.limits 202 193 disperser = values.get(parameter.name+'_pd_type', 'gaussian') … … 204 195 width = values.get(parameter.name+'_pd', 0.0) 205 196 nsigma = values.get(parameter.name+'_pd_nsigma', 3.0) 197 if npts == 0 or width == 0: 198 return [value], [] 206 199 value, weight = weights.get_weights( 207 200 disperser, npts, width, nsigma, value, limits, relative) … … 235 228 *mono* is True if polydispersity should be set to none on all parameters. 236 229 """ 230 parameters = kernel.info['parameters'] 237 231 if mono: 238 232 active = lambda name: False 239 233 elif kernel.dim == '1d': 240 pars_1d = set(p.name for p in kernel.info['parameters'].type['1d']) 241 active = lambda name: name in pars_1d 234 active = lambda name: name in parameters.pd_1d 242 235 elif kernel.dim == '2d': 243 pars_2d = set(p.name for p in kernel.info['parameters'].type['2d']) 244 active = lambda name: name in pars_2d 236 active = lambda name: name in parameters.pd_2d 245 237 else: 246 238 active = lambda name: True 247 239 248 vw_pairs = [(get_weights(p, pars) if active(p.name) else ([p.default], [])) 249 for p in kernel.info['parameters']] 240 vw_pairs = [(get_weights(p, pars) if active(p.name) 241 else ([pars.get(p.name, p.default)], [])) 242 for p in parameters.call_parameters] 250 243 values, weights = zip(*vw_pairs) 251 244 -
sasmodels/generate.py
r5ff1b03 rd19962c 184 184 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 185 185 186 import sys187 186 from os.path import abspath, dirname, join as joinpath, exists, basename, \ 188 187 splitext, getmtime … … 198 197 199 198 TEMPLATE_ROOT = dirname(__file__) 200 201 MAX_PD = 4202 199 203 200 F16 = np.dtype('float16') … … 478 475 source = [kernel_header] + user_code 479 476 480 vol_parameters = partable.kernel_pars('volume')481 iq_parameters = partable.kernel_pars('1d')482 iqxy_parameters = partable.kernel_pars('2d')483 484 477 # Make parameters for q, qx, qy so that we can use them in declarations 485 478 q, qx, qy = [Parameter(name=v) for v in ('q', 'qx', 'qy')] 486 479 # Generate form_volume function, etc. from body only 487 480 if model_info['form_volume'] is not None: 488 pars = vol_parameters481 pars = partable.form_volume_parameters 489 482 source.append(_gen_fn('form_volume', pars, model_info['form_volume'])) 490 483 if model_info['Iq'] is not None: 491 pars = [q] + iq_parameters484 pars = [q] + partable.iq_parameters 492 485 source.append(_gen_fn('Iq', pars, model_info['Iq'])) 493 486 if model_info['Iqxy'] is not None: 494 pars = [qx, qy] + iqxy_parameters487 pars = [qx, qy] + partable.iqxy_parameters 495 488 source.append(_gen_fn('Iqxy', pars, model_info['Iqxy'])) 496 489 … … 498 491 source.append("#define PARAMETER_TABLE \\") 499 492 source.append("\\\n".join(p.as_definition() 500 for p in model_info['parameters'][2:]))493 for p in partable.kernel_parameters)) 501 494 502 495 # Define the function calls 503 if vol_parameters:504 refs = _call_pars("v.", vol_parameters)496 if partable.form_volume_parameters: 497 refs = _call_pars("v.", partable.form_volume_parameters) 505 498 call_volume = "#define CALL_VOLUME(v) form_volume(%s)" % (",".join(refs)) 506 499 else: … … 511 504 source.append(call_volume) 512 505 513 refs = ["q[i]"] + _call_pars("v.", iq_parameters)506 refs = ["q[i]"] + _call_pars("v.", partable.iq_parameters) 514 507 call_iq = "#define CALL_IQ(q,i,v) Iq(%s)" % (",".join(refs)) 515 508 if _have_Iqxy(user_code): 516 509 # Call 2D model 517 refs = ["q[2*i]", "q[2*i+1]"] + _call_pars("v.", iqxy_parameters)510 refs = ["q[2*i]", "q[2*i+1]"] + _call_pars("v.", partable.iqxy_parameters) 518 511 call_iqxy = "#define CALL_IQ(q,i,v) Iqxy(%s)" % (",".join(refs)) 519 512 else: … … 525 518 526 519 # Fill in definitions for numbers of parameters 527 source.append("#define MAX_PD %s"% model_info['max_pd'])528 source.append("#define NPARS %d"% (len(partable.kernel_pars())))520 source.append("#define MAX_PD %s"%partable.max_pd) 521 source.append("#define NPARS %d"%partable.npars) 529 522 530 523 # TODO: allow mixed python/opencl kernels? … … 546 539 return '\n'.join(source) 547 540 548 def categorize_parameters(pars):549 """550 Categorize the parameters by use:551 552 * *pd* list of polydisperse parameters in order; gui should test whether553 they are in *2d* or *magnetic* as appropriate for the data554 * *1d* set of parameters that are used to compute 1D patterns555 * *2d* set of parameters that are used to compute 2D patterns (which556 includes all 1D parameters)557 * *magnetic* set of parameters that are used to compute magnetic558 patterns (which includes all 1D and 2D parameters)559 * *pd_relative* is the set of parameters with relative distribution560 width (e.g., radius +/- 10%) rather than absolute distribution561 width (e.g., theta +/- 6 degrees).562 * *theta_par* is the index of the polar angle polydispersion parameter563 or -1 if no such parameter exists564 """565 par_set = {}566 567 541 def process_parameters(model_info): 568 542 """ … … 573 547 model_info['demo'] = partable.defaults 574 548 575 # Don't use more polydisperse parameters than are available in the model576 # Note: we can do polydispersity on arbitrary parameters, so it is not577 # clear that this is a good idea; it does however make the poly_details578 # code easier to write, so we will leave it in for now.579 model_info['max_pd'] = min(partable.num_pd, MAX_PD)580 581 549 class CoordinationDetails(object): 582 550 def __init__(self, model_info): 583 max_pd = model_info['max_pd'] 584 npars = len(model_info['parameters'].kernel_pars()) 551 parameters = model_info['parameters'] 552 max_pd = parameters.max_pd 553 npars = parameters.npars 585 554 par_offset = 4*max_pd 586 555 self.details = np.zeros(par_offset + 3*npars + 4, 'i4') … … 596 565 597 566 # theta_par is fixed 598 self.details[-1] = model_info['parameters'].theta_par567 self.details[-1] = parameters.theta_offset 599 568 600 569 @property … … 647 616 648 617 def poly_details(model_info, weights): 649 weights = weights[2:]650 max_pd = model_info['max_pd']618 #print("weights",weights) 619 weights = weights[2:] # Skip scale and background 651 620 652 621 # Decreasing list of polydispersity lengths … … 654 623 pd_length = np.array([len(w) for w in weights]) 655 624 num_active = np.sum(pd_length>1) 656 if num_active > m ax_pd:625 if num_active > model_info['parameters'].max_pd: 657 626 raise ValueError("Too many polydisperse parameters") 658 627 … … 745 714 *model_info* blocks for the composition objects. This allows us to 746 715 build complete product and mixture models from just the info. 747 * *max_pd* is the max polydispersity dimension. This is constant and748 should not be reset. You may be able to change it when the program749 starts by setting *sasmodels.generate.MAX_PD*.750 751 716 """ 752 717 # TODO: maybe turn model_info into a class ModelDefinition … … 837 802 838 803 839 840 804 def demo_time(): 841 805 """ … … 853 817 Program which prints the source produced by the model. 854 818 """ 819 import sys 820 from sasmodels.core import make_model_by_name 855 821 if len(sys.argv) <= 1: 856 822 print("usage: python -m sasmodels.generate modelname") 857 823 else: 858 824 name = sys.argv[1] 859 import sasmodels.models 860 __import__('sasmodels.models.' + name) 861 model = getattr(sasmodels.models, name) 862 model_info = make_model_info(model) 825 model_info = make_model_by_name(name) 863 826 source = make_source(model_info) 864 827 print(source) -
sasmodels/kerneldll.py
r5ff1b03 rd19962c 266 266 assert weights.dtype == real and values.dtype == real 267 267 268 max_pd = self.info['max_pd']269 268 start, stop = 0, details.total_pd 270 269 #print("in kerneldll") -
sasmodels/modelinfo.py
rc499331 rd19962c 4 4 # TODO: turn ModelInfo into a proper class 5 5 ModelInfo = dict 6 7 MAX_PD = 4 6 8 7 9 COMMON_PARAMETERS = [ … … 153 155 *length* is the length of the field if it is a vector field 154 156 *length_control* is the parameter which sets the vector length 157 *is_control* is True if the parameter is a control parameter for a vector 155 158 *polydisperse* is true if the parameter accepts a polydispersity 156 159 *relative_pd* is true if that polydispersity is relative … … 163 166 self.id = name.split('[')[0].strip() 164 167 self.name = name 168 self.units = units 165 169 self.default = default 166 170 self.limits = limits … … 169 173 self.choices = None 170 174 171 # Length and length_control will be filled in by 172 # set_vector_length_from_reference(partable) once the complete 175 # Length and length_control will be filled in once the complete 173 176 # parameter table is available. 174 177 self.length = 1 175 178 self.length_control = None 179 self.is_control = False 176 180 177 181 # TODO: need better control over whether a parameter is polydisperse … … 216 220 return "P<%s>"%self.name 217 221 222 218 223 class ParameterTable(object): 224 """ 225 ParameterTable manages the list of available parameters. 226 227 There are a couple of complications which mean that the list of parameters 228 for the kernel differs from the list of parameters that the user sees. 229 230 (1) Common parameters. Scale and background are implicit to every model, 231 but are not passed to the kernel. 232 233 (2) Vector parameters. Vector parameters are passed to the kernel as a 234 pointer to an array, e.g., thick[], but they are seen by the user as n 235 separate parameters thick1, thick2, ... 236 237 Therefore, the parameter table is organized by how it is expected to be 238 used. The following information is needed to set up the kernel functions: 239 240 * *kernel_parameters* is the list of parameters in the kernel parameter 241 table, with vector parameter p declared as p[]. 242 243 * *iq_parameters* is the list of parameters to the Iq(q, ...) function, 244 with vector parameter p sent as p[]. 245 246 * *iqxy_parameters* is the list of parameters to the Iqxy(qx, qy, ...) 247 function, with vector parameter p sent as p[]. 248 249 * *form_volume_parameters* is the list of parameters to the form_volume(...) 250 function, with vector parameter p sent as p[]. 251 252 Problem details, which sets up the polydispersity loops, requires the 253 following: 254 255 * *theta_offset* is the offset of the theta parameter in the kernel parameter 256 table, with vector parameters counted as n individual parameters 257 p1, p2, ..., or offset is -1 if there is no theta parameter. 258 259 * *max_pd* is the maximum number of polydisperse parameters, with vector 260 parameters counted as n individual parameters p1, p2, ... Note that 261 this number is limited to sasmodels.modelinfo.MAX_PD. 262 263 * *npars* is the total number of parameters to the kernel, with vector 264 parameters counted as n individual parameters p1, p2, ... 265 266 * *call_parameters* is the complete list of parameters to the kernel, 267 including scale and background, with vector parameters recorded as 268 individual parameters p1, p2, ... 269 270 * *active_1d* is the set of names that may be polydisperse for 1d data 271 272 * *active_2d* is the set of names that may be polydisperse for 2d data 273 274 User parameters are the set of parameters visible to the user, including 275 the scale and background parameters that the kernel does not see. User 276 parameters don't use vector notation, and instead use p1, p2, ... 277 278 * *control_parameters* is the 279 280 """ 219 281 # scale and background are implicit parameters 220 282 COMMON = [Parameter(*p) for p in COMMON_PARAMETERS] 221 283 222 284 def __init__(self, parameters): 223 self.parameters = self.COMMON + parameters 224 self._name_table= dict((p.name, p) for p in parameters) 285 self.kernel_parameters = parameters 286 self._set_vector_lengths() 287 self._make_call_parameter_list() 225 288 self._categorize_parameters() 226 227 self._set_vector_lengths()228 289 self._set_defaults() 290 #self._name_table= dict((p.id, p) for p in parameters) 229 291 230 292 def _set_vector_lengths(self): 231 293 # Sort out the length of the vector parameters such as thickness[n] 232 for p in self. parameters:294 for p in self.kernel_parameters: 233 295 if p.length_control: 234 ref = self._name_table[p.length_control] 296 for ref in self.kernel_parameters: 297 if ref.id == p.length_control: 298 break 299 else: 300 raise ValueError("no reference variable %r for %s" 301 % (p.length_control, p.name)) 302 ref.is_control = True 235 303 low, high = ref.limits 236 304 if int(low) != low or int(high) != high or low<0 or high>20: 237 raise ValueError("expected limits on %s to be within [0, 20]"%ref.name) 305 raise ValueError("expected limits on %s to be within [0, 20]" 306 % ref.name) 238 307 p.length = high 239 308 … … 241 310 # Construct default values, including vector defaults 242 311 defaults = {} 243 for p in self. parameters:312 for p in self.call_parameters: 244 313 if p.length == 1: 245 314 defaults[p.id] = p.default 246 315 else: 247 for k in range( p.length):248 defaults["%s [%d]"%(p.id, k)] = p.default316 for k in range(1, p.length+1): 317 defaults["%s%d"%(p.id, k)] = p.default 249 318 self.defaults = defaults 250 319 320 def _make_call_parameter_list(self): 321 full_list = self.COMMON[:] 322 for p in self.kernel_parameters: 323 if p.length == 1: 324 full_list.append(p) 325 else: 326 for k in range(1, p.length+1): 327 pk = Parameter(p.id+str(k), p.units, p.default, 328 p.limits, p.type, p.description) 329 pk.polydisperse = p.polydisperse 330 pk.relative_pd = p.relative_pd 331 full_list.append(pk) 332 self.call_parameters = full_list 333 334 """ # Suppress these for now until we see how they are used 251 335 def __getitem__(self, k): 252 336 if isinstance(k, (int, slice)): … … 259 343 260 344 def __iter__(self): 261 return iter(self.parameters) 262 263 def kernel_pars(self, ptype=None): 264 """ 265 Return the parameters to the user kernel which match the given type. 266 267 Types include '1d' for Iq kernels, '2d' for Iqxy kernels and 268 'volume' for form_volume kernels. 269 """ 270 # Assumes background and scale are the first two parameters 271 if ptype is None: 272 return self.parameters[2:] 345 return iter(self.expanded_parameters) 346 """ 347 348 def _categorize_parameters(self): 349 # Set the kernel parameters. Assumes background and scale are the 350 # first two parameters in the parameter list, but these are not sent 351 # to the underlying kernel functions. 352 self.iq_parameters = [p for p in self.kernel_parameters 353 if p.type not in ('orientation', 'magnetic')] 354 self.iqxy_parameters = [p for p in self.kernel_parameters 355 if p.type != 'magnetic'] 356 self.form_volume_parameters = [p for p in self.kernel_parameters 357 if p.type == 'volume'] 358 359 # Theta offset 360 offset = 0 361 for p in self.kernel_parameters: 362 if p.name == 'theta': 363 self.theta_offset = offset 364 break 365 offset += p.length 273 366 else: 274 return [p for p in self.parameters[2:] if p in self.type[ptype]] 275 276 def _categorize_parameters(self): 277 """ 278 Build parameter categories out of the the parameter definitions. 279 280 Returns a dictionary of categories. 281 282 Note: these categories are subject to change, depending on the needs of 283 the UI and the needs of the kernel calling function. 284 285 The categories are as follows: 286 287 * *volume* list of volume parameter names 288 * *orientation* list of orientation parameters 289 * *magnetic* list of magnetic parameters 290 * *sld* list of parameters that have no type info 291 * *other* list of parameters that have no type info 292 293 Each parameter is in one and only one category. 294 """ 295 pars = self.parameters 296 297 par_type = { 298 'volume': [], 'orientation': [], 'magnetic': [], 'sld': [], 'other': [], 299 } 300 for p in self.parameters: 301 par_type[p.type if p.type else 'other'].append(p) 302 par_type['1d'] = [p for p in pars if p.type not in ('orientation', 'magnetic')] 303 par_type['2d'] = [p for p in pars if p.type != 'magnetic'] 304 par_type['pd'] = [p for p in pars if p.polydisperse] 305 par_type['pd_relative'] = [p for p in pars if p.relative_pd] 306 self.type = par_type 307 308 # find index of theta (or whatever variable is used for spherical 309 # normalization during polydispersity... 310 if 'theta' in par_type['2d']: 311 # TODO: may be an off-by 2 bug due to background and scale 312 # TODO: is theta always the polar coordinate? 313 self.theta_par = [k for k,p in enumerate(pars) if p.name=='theta'][0] 314 else: 315 self.theta_par = -1 316 317 @property 318 def num_pd(self): 319 """ 320 Number of distributional parameters in the model (polydispersity in 321 shape dimensions and orientational distributions). 322 """ 323 return sum(p.length for p in self.type['pd']) 324 325 @property 326 def has_2d(self): 327 return self.type['orientation'] or self.type['magnetic'] 367 self.theta_offset = -1 368 369 # number of polydisperse parameters 370 num_pd = sum(p.length for p in self.kernel_parameters if p.polydisperse) 371 # Don't use more polydisperse parameters than are available in the model 372 # Note: we can do polydispersity on arbitrary parameters, so it is not 373 # clear that this is a good idea; it does however make the poly_details 374 # code easier to write, so we will leave it in for now. 375 self.max_pd = min(num_pd, MAX_PD) 376 377 self.npars = sum(p.length for p in self.kernel_parameters) 378 379 # true if has 2D parameters 380 self.has_2d = any(p.type in ('orientation', 'magnetic') 381 for p in self.kernel_parameters) 382 383 self.pd_1d = set(p.name for p in self.call_parameters 384 if p.polydisperse and p.type not in ('orientation', 'magnetic')) 385 self.pd_2d = set(p.name for p in self.call_parameters 386 if p.polydisperse and p.type != 'magnetic') 387 388 def user_parameters(self, pars, is2d): 389 """ 390 Return the list of parameters for the given data type. 391 392 Vector parameters are expanded as in place. If multiple parameters 393 share the same vector length, then the parameters will be interleaved 394 in the result. The control parameters come first. For example, 395 if the parameter table is ordered as:: 396 397 sld_core 398 sld_shell[num_shells] 399 sld_solvent 400 thickness[num_shells] 401 num_shells 402 403 and *pars[num_shells]=2* then the returned list will be:: 404 405 num_shells 406 scale 407 background 408 sld_core 409 sld_shell1 410 thickness1 411 sld_shell2 412 thickness2 413 sld_solvent 414 415 Note that shell/thickness pairs are grouped together in the result 416 even though they were not grouped in the incoming table. The control 417 parameter is always returned first since the GUI will want to set it 418 early, and rerender the table when it is changed. 419 """ 420 control = [p for p in self.kernel_parameters if p.is_control] 421 422 # Gather entries such as name[n] into groups of the same n 423 dependent = dict((p.id, []) for p in control) 424 for p in self.kernel_parameters: 425 if p.length_control is not None: 426 dependent[p.length_control].append(p) 427 428 # Gather entries such as name[4] into groups of the same length 429 fixed = {} 430 for p in self.kernel_parameters: 431 if p.length > 1 and p.length_control is None: 432 fixed.setdefault(p.length, []).append(p) 433 434 # Using the call_parameters table, we already have expanded forms 435 # for each of the vector parameters; put them in a lookup table 436 expanded_pars = dict((p.name, p) for p in self.call_parameters) 437 438 # Gather the user parameters in order 439 result = control + self.COMMON 440 for p in self.kernel_parameters: 441 if not is2d and p.type in ('orientation', 'magnetic'): 442 pass 443 elif p.is_control: 444 pass # already added 445 elif p.length_control is not None: 446 table = dependent.get(p.length_control, []) 447 if table: 448 # look up length from incoming parameters 449 table_length = int(pars[p.length_control]) 450 del dependent[p.length_control] # first entry seen 451 for k in range(1, table_length+1): 452 for entry in table: 453 result.append(expanded_pars[entry.id+str(k)]) 454 else: 455 pass # already processed all entries 456 elif p.length > 1: 457 table = fixed.get(p.length, []) 458 if table: 459 table_length = p.length 460 del fixed[p.length] 461 for k in range(1, table_length+1): 462 for entry in table: 463 result.append(expanded_pars[entry.id+str(k)]) 464 else: 465 pass # already processed all entries 466 else: 467 result.append(p) 468 469 return result 470 328 471 329 472
Note: See TracChangeset
for help on using the changeset viewer.