Changeset 69aa451 in sasmodels
- Timestamp:
- Mar 23, 2016 11:39:08 AM (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:
- e9b1663d
- Parents:
- 6fd8de0
- Location:
- sasmodels
- Files:
-
- 1 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/compare.py
ra4e2ae5 r69aa451 870 870 pars, pd_types = bumps_model.create_parameters(model_info, **opts['pars']) 871 871 if not opts['is2d']: 872 for name in model_info['par_type']['1d']:872 for p in model_info['parameters'].type['1d']: 873 873 for ext in ['', '_pd', '_pd_n', '_pd_nsigma']: 874 k = name+ext874 k = p.name+ext 875 875 v = pars.get(k, None) 876 876 if v is not None: -
sasmodels/core.py
rfec69dd r69aa451 218 218 active = lambda name: False 219 219 elif kernel.dim == '1d': 220 active = lambda name: name in set(kernel.info['par_type']['1d']) 220 pars_1d = set(p.name for p in kernel.info['parameters'].type['1d']) 221 active = lambda name: name in pars_1d 221 222 elif kernel.dim == '2d': 222 active = lambda name: name in set(kernel.info['par_type']['2d']) 223 pars_2d = set(p.name for p in kernel.info['parameters'].type['2d']) 224 active = lambda name: name in pars_1d 223 225 else: 224 226 active = lambda name: True -
sasmodels/generate.py
rea1f14d r69aa451 69 69 The constructor code will generate the necessary vectors for computing 70 70 them with the desired polydispersity. 71 72 The available kernel parameters are defined as a list, with each parameter73 defined as a sublist with the following elements:74 75 *name* is the name that will be used in the call to the kernel76 function and the name that will be displayed to the user. Names77 should be lower case, with words separated by underscore. If78 acronyms are used, the whole acronym should be upper case.79 80 *units* should be one of *degrees* for angles, *Ang* for lengths,81 *1e-6/Ang^2* for SLDs.82 83 *default value* will be the initial value for the model when it84 is selected, or when an initial value is not otherwise specified.85 86 *limits = [lb, ub]* are the hard limits on the parameter value, used to87 limit the polydispersity density function. In the fit, the parameter limits88 given to the fit are the limits on the central value of the parameter.89 If there is polydispersity, it will evaluate parameter values outside90 the fit limits, but not outside the hard limits specified in the model.91 If there are no limits, use +/-inf imported from numpy.92 93 *type* indicates how the parameter will be used. "volume" parameters94 will be used in all functions. "orientation" parameters will be used95 in *Iqxy* and *Imagnetic*. "magnetic* parameters will be used in96 *Imagnetic* only. If *type* is the empty string, the parameter will97 be used in all of *Iq*, *Iqxy* and *Imagnetic*. "sld" parameters98 can automatically be promoted to magnetic parameters, each of which99 will have a magnitude and a direction, which may be different from100 other sld parameters.101 102 *description* is a short description of the parameter. This will103 be displayed in the parameter table and used as a tool tip for the104 parameter value in the user interface.105 106 71 The kernel module must set variables defining the kernel meta data: 107 72 … … 216 181 from __future__ import print_function 217 182 218 # TODO: identify model files which have changed since loading and reload them. 183 #TODO: determine which functions are useful outside of generate 184 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 219 185 220 186 import sys … … 224 190 import string 225 191 import warnings 226 from collections import namedtuple227 192 228 193 import numpy as np 229 194 230 # TODO: promote Parameter and model_info to classes 231 PARAMETER_FIELDS = ['name', 'units', 'default', 'limits', 'type', 'description'] 232 Parameter = namedtuple('Parameter', PARAMETER_FIELDS) 233 234 #TODO: determine which functions are useful outside of generate 235 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 195 from .modelinfo import ModelInfo, Parameter, make_parameter_table 196 197 # TODO: identify model files which have changed since loading and reload them. 236 198 237 199 TEMPLATE_ROOT = dirname(__file__) … … 246 208 except TypeError: 247 209 F128 = None 248 249 # Scale and background, which are parameters common to every form factor250 COMMON_PARAMETERS = [251 ["scale", "", 1, [0, np.inf], "", "Source intensity"],252 ["background", "1/cm", 1e-3, [0, np.inf], "", "Source background"],253 ]254 210 255 211 # Conversion from units defined in the parameter table for each model … … 354 310 return [_search(search_path, f) for f in model_info['source']] 355 311 312 def timestamp(model_info): 313 """ 314 Return a timestamp for the model corresponding to the most recently 315 changed file or dependency. 316 """ 317 source_files = (model_sources(model_info) 318 + model_templates() 319 + [model_info['filename']]) 320 newest = max(getmtime(f) for f in source_files) 321 return newest 322 356 323 def convert_type(source, dtype): 357 324 """ … … 448 415 } 449 416 """ 450 par_decl = ', '.join( 'double ' + pfor p in pars) if pars else 'void'417 par_decl = ', '.join(p.as_argument() for p in pars) if pars else 'void' 451 418 return _FN_TEMPLATE % {'name': name, 'body': body, 'pars': par_decl} 452 419 … … 455 422 Return a list of *prefix.parameter* from parameter items. 456 423 """ 457 prefix += "." 458 return [prefix+p for p in pars] 424 return [p.as_call_reference(prefix) for p in pars] 459 425 460 426 _IQXY_PATTERN = re.compile("^((inline|static) )? *(double )? *Iqxy *([(]|$)", … … 499 465 # for computing volume even if we allow non-disperse volume parameters. 500 466 501 # kernel_iq assumes scale and background are the first parameters; 502 # they should be first for 1d and 2d parameter lists as well. 503 assert model_info['parameters'][0].name == 'scale' 504 assert model_info['parameters'][1].name == 'background' 505 506 # Identify parameter types 507 iq_parameters = model_info['par_type']['1d'][2:] 508 iqxy_parameters = model_info['par_type']['2d'][2:] 509 vol_parameters = model_info['par_type']['volume'] 467 partable = model_info['parameters'] 468 469 # Identify parameters for Iq, Iqxy, Iq_magnetic and form_volume. 470 # Note that scale and volume are not possible types. 510 471 511 472 # Load templates and user code … … 517 478 source = [kernel_header] + user_code 518 479 480 vol_parameters = partable.kernel_pars('volume') 481 iq_parameters = partable.kernel_pars('1d') 482 iqxy_parameters = partable.kernel_pars('2d') 483 484 # Make parameters for q, qx, qy so that we can use them in declarations 485 q, qx, qy = [Parameter(name=v) for v in ('q', 'qx', 'qy')] 519 486 # Generate form_volume function, etc. from body only 520 487 if model_info['form_volume'] is not None: 521 p names = [p for p in vol_parameters]522 source.append(_gen_fn('form_volume', p names, model_info['form_volume']))488 pars = vol_parameters 489 source.append(_gen_fn('form_volume', pars, model_info['form_volume'])) 523 490 if model_info['Iq'] is not None: 524 p names = ['q'] + [p for p in iq_parameters]525 source.append(_gen_fn('Iq', p names, model_info['Iq']))491 pars = [q] + iq_parameters 492 source.append(_gen_fn('Iq', pars, model_info['Iq'])) 526 493 if model_info['Iqxy'] is not None: 527 p names = ['qx', 'qy'] + [p for p in iqxy_parameters]528 source.append(_gen_fn('Iqxy', p names, model_info['Iqxy']))494 pars = [qx, qy] + iqxy_parameters 495 source.append(_gen_fn('Iqxy', pars, model_info['Iqxy'])) 529 496 530 497 # Define the parameter table 531 498 source.append("#define PARAMETER_TABLE \\") 532 source.append("\\\n ".join("double %s;"%p.name533 499 source.append("\\\n".join(p.as_definition() 500 for p in model_info['parameters'][2:])) 534 501 535 502 # Define the function calls 536 503 if vol_parameters: 537 refs = ",".join(_call_pars("v", vol_parameters))538 call_volume = "#define CALL_VOLUME(v) form_volume(%s)" %refs504 refs = _call_pars("v.", vol_parameters) 505 call_volume = "#define CALL_VOLUME(v) form_volume(%s)" % (",".join(refs)) 539 506 else: 540 507 # Model doesn't have volume. We could make the kernel run a little … … 544 511 source.append(call_volume) 545 512 546 refs = ["q[i]"] + _call_pars("v ", iq_parameters)513 refs = ["q[i]"] + _call_pars("v.", iq_parameters) 547 514 call_iq = "#define CALL_IQ(q,i,v) Iq(%s)" % (",".join(refs)) 548 515 if _have_Iqxy(user_code): 549 516 # Call 2D model 550 refs = ["q[2*i]", "q[2*i+1]"] + _call_pars("v ", iqxy_parameters)517 refs = ["q[2*i]", "q[2*i+1]"] + _call_pars("v.", iqxy_parameters) 551 518 call_iqxy = "#define CALL_IQ(q,i,v) Iqxy(%s)" % (",".join(refs)) 552 519 else: … … 559 526 # Fill in definitions for numbers of parameters 560 527 source.append("#define MAX_PD %s"%model_info['max_pd']) 561 source.append("#define NPARS %d"%(len( model_info['parameters'])-2))528 source.append("#define NPARS %d"%(len(partable.kernel_pars()))) 562 529 563 530 # TODO: allow mixed python/opencl kernels? … … 597 564 """ 598 565 par_set = {} 599 par_set['1d'] = [p.name for p in pars if p.type not in ('orientation', 'magnetic')]600 par_set['2d'] = [p.name for p in pars if p.type != 'magnetic']601 par_set['magnetic'] = [p.name for p in pars]602 par_set['pd'] = [p.name for p in pars if p.type in ('volume', 'orientation')]603 par_set['pd_relative'] = [p.name for p in pars if p.type == 'volume']604 if 'theta' in par_set['2d']:605 # find id of theta in parameter set (or whatever variable is606 # used for spherical normalization during polydispersity...607 par_set['theta_par'] = [k for k,p in enumerate(pars) if p.name=='theta'][0]608 else:609 par_set['theta_par'] = -1610 return par_set611 612 def collect_types(pars):613 """614 Build parameter categories out of the the parameter definitions.615 616 Returns a dictionary of categories.617 618 Note: these categories are subject to change, depending on the needs of619 the UI and the needs of the kernel calling function.620 621 The categories are as follows:622 623 * *volume* list of volume parameter names624 * *orientation* list of orientation parameters625 * *magnetic* list of magnetic parameters626 * *sld* list of parameters that have no type info627 * *other* list of parameters that have no type info628 629 Each parameter is in one and only one category.630 """631 par_type = {632 'volume': [], 'orientation': [], 'magnetic': [], 'sld': [], 'other': [],633 }634 for p in pars:635 par_type[p.type if p.type else 'other'].append(p.name)636 return par_type637 638 566 639 567 def process_parameters(model_info): … … 641 569 Process parameter block, precalculating parameter details. 642 570 """ 643 # convert parameters into named tuples 644 for p in model_info['parameters']: 645 if p[4] == '' and (p[0].startswith('sld') or p[0].endswith('sld')): 646 p[4] = 'sld' 647 # TODO: make sure all models explicitly label their sld parameters 648 #raise ValueError("%s.%s needs to be explicitly set to type 'sld'" %(model_info['id'], p[0])) 649 650 pars = [Parameter(*p) for p in model_info['parameters']] 651 # Fill in the derived attributes 652 par_type = collect_types(pars) 653 par_type.update(categorize_parameters(pars)) 654 model_info['parameters'] = pars 655 model_info['par_type'] = par_type 571 partable = model_info['parameters'] 656 572 if model_info.get('demo', None) is None: 657 model_info['demo'] = dict((p.name, p.default) for p in pars)658 model_info['has_2d'] = par_type['orientation'] or par_type['magnetic'] 573 model_info['demo'] = partable.defaults 574 659 575 # Don't use more polydisperse parameters than are available in the model 660 576 # Note: we can do polydispersity on arbitrary parameters, so it is not 661 577 # clear that this is a good idea; it does however make the poly_details 662 578 # code easier to write, so we will leave it in for now. 663 model_info['max_pd'] = min( len(par_type['pd']), MAX_PD)579 model_info['max_pd'] = min(partable.num_pd, MAX_PD) 664 580 665 581 def mono_details(model_info): 582 # TODO: move max_pd into ParameterTable? 666 583 max_pd = model_info['max_pd'] 667 npars = len(model_info['parameters']) - 2 668 p = 5*max_pd 669 c = p + 3*npars 670 671 details = np.zeros(c + 2, 'int32') 584 pars = model_info['parameters'].kernel_pars() 585 npars = len(pars) 586 par_offset = 5*max_pd 587 constants_offset = par_offset + 3*npars 588 589 details = np.zeros(constants_offset + 2, 'int32') 672 590 details[0*max_pd:1*max_pd] = range(max_pd) # pd_par: arbitrary order; use first 673 591 details[1*max_pd:2*max_pd] = [1]*max_pd # pd_length: only one element … … 675 593 details[3*max_pd:4*max_pd] = [1]*max_pd # pd_stride: vectors of length 1 676 594 details[4*max_pd:5*max_pd] = [0]*max_pd # pd_isvol: doens't matter if no norm 677 details[p +0*npars:p+1*npars] = range(2, npars+2) # par_offset: skip scale and background678 details[p +1*npars:p+2*npars] = [0]*npars # no coordination595 details[par_offset+0*npars:par_offset+1*npars] = range(2, npars+2) # par_offset: skip scale and background 596 details[par_offset+1*npars:par_offset+2*npars] = [0]*npars # no coordination 679 597 #details[p+npars] = 1 # par_coord[0] is coordinated with the first par? 680 details[p +2*npars:p+3*npars] = 0 # fast coord with 0681 details[c ] = 1 # fast_coord_count: one fast index682 details[c +1] = -1 # theta_par: None598 details[par_offset+2*npars:par_offset+3*npars] = 0 # fast coord with 0 599 details[constants_offset] = 1 # fast_coord_count: one fast index 600 details[constants_offset+1] = -1 # theta_par: None 683 601 return details 684 602 685 603 def poly_details(model_info, weights): 686 print("entering poly",weights)687 688 print([p.name for p in model_info['parameters']])689 pars = model_info['parameters'][2:] # skip scale and background690 604 weights = weights[2:] 605 606 # TODO: move max_pd into ParameterTable? 691 607 max_pd = model_info['max_pd'] 692 npars = len(pars) # scale and background already removed 608 pars = model_info['parameters'].kernel_pars 609 npars = len(pars) 693 610 par_offset = 5*max_pd 694 611 constants_offset = par_offset + 3*npars … … 745 662 """ 746 663 if model_info['Iq'] is not None and model_info['Iqxy'] is None: 747 if model_info['par_type']['1d'] != model_info['par_type']['2d']: 664 partable = model_info['parameters'] 665 if partable.type['1d'] != partable.type['2d']: 748 666 raise ValueError("Iqxy model is missing") 749 667 Iq = model_info['Iq'] … … 751 669 return Iq(np.sqrt(qx**2 + qy**2), **kw) 752 670 model_info['Iqxy'] = Iqxy 671 753 672 754 673 def make_model_info(kernel_module): … … 800 719 """ 801 720 # TODO: maybe turn model_info into a class ModelDefinition 802 parameters = COMMON_PARAMETERS + kernel_module.parameters 721 #print("make parameter table", kernel_module.parameters) 722 parameters = make_parameter_table(kernel_module.parameters) 803 723 filename = abspath(kernel_module.__file__) 804 724 kernel_id = splitext(basename(filename))[0] -
sasmodels/kernel_iq.c
rfec69dd r69aa451 14 14 #ifndef _PAR_BLOCK_ // protected block so we can include this code twice. 15 15 #define _PAR_BLOCK_ 16 17 #define MAX_PD 4 // MAX_PD is the max number of polydisperse parameters18 16 19 17 typedef struct { -
sasmodels/kerneldll.py
rfec69dd r69aa451 140 140 141 141 source = generate.convert_type(source, dtype) 142 source_files = (generate.model_sources(model_info) 143 + [model_info['filename']] 144 + generate.model_templates()) 142 newest = generate.timestamp(model_info) 145 143 dll = dll_path(model_info, dtype) 146 newest = max(os.path.getmtime(f) for f in source_files)147 144 if not os.path.exists(dll) or os.path.getmtime(dll) < newest: 148 145 # Replace with a proper temp file -
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/models/rpa.py
raa2edb2 r69aa451 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"],
Note: See TracChangeset
for help on using the changeset viewer.