Changeset d19962c in sasmodels


Ignore:
Timestamp:
Mar 27, 2016 4:57:03 PM (8 years ago)
Author:
Paul Kienzle <pkienzle@…>
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
Message:

working vector parameter example using dll engine

Files:
7 edited

Legend:

Unmodified
Added
Removed
  • example/polynomial.py

    rc499331 rd19962c  
    11from numpy import inf 
    22parameters = [ 
    3     ["n", "", 0, [0,5], "", "polynomial degree"], 
     3    ["n", "", 1, [1,5], "", "number of coefficients (or degree+1)"], 
    44    ["c[n]", "", 0, [-inf, inf], "", "coefficients to c_n x^n"], 
    55] 
    66 
    7 Iq = """ 
     7Iq = r""" 
    88    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]; } 
    1111    return result; 
    1212    """ 
  • sasmodels/bumps_model.py

    re9b1663d rd19962c  
    8181    from bumps.names import Parameter 
    8282 
    83     pars = {} # => floating point parameters 
     83    pars = {}     # floating point parameters 
     84    pd_types = {} # distribution names 
    8485    for p in model_info['parameters']: 
    8586        value = kwargs.pop(p.name, p.default) 
    8687        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') 
    10298 
    10399    if kwargs:  # args not corresponding to parameters 
  • sasmodels/compare.py

    rc499331 rd19962c  
    308308    lines = [] 
    309309    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)) 
    329318    return "\n".join(lines) 
    330319 
     
    668657    # Get the default values for the parameters 
    669658    pars = {} 
    670     for p in model_info['parameters']: 
     659    for p in model_info['parameters'].call_parameters: 
    671660        parts = [('', p.default)] 
    672661        if p.polydisperse: 
     
    677666        for ext, val in parts: 
    678667            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)) 
    680669            else: 
    681670                pars[p.id+ext] = val 
     
    709698    name = args[0] 
    710699    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) 
    715701    except ImportError, exc: 
    716702        print(str(exc)) 
  • sasmodels/core.py

    rc499331 rd19962c  
    2424    HAVE_OPENCL = False 
    2525 
    26 __all__ = [ 
    27     "list_models", "load_model_info", "precompile_dll", 
    28     "build_model", "call_kernel", "call_ER_VR", 
    29 ] 
    30  
    3126try: 
    3227    # Python 3.5 and up 
     
    4439        return module 
    4540 
     41 
     42 
     43__all__ = [ 
     44    "list_models", "load_model_info", "precompile_dll", 
     45    "build_model", "call_kernel", "call_ER_VR", 
     46] 
     47 
    4648def list_models(): 
    4749    """ 
     
    6567    Load model info and build model. 
    6668    """ 
    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) 
    9070 
    9171def load_model_info(model_name): 
     
    11090        return product.make_product_info(P_info, Q_info) 
    11191 
     92    return make_model_by_name(model_name) 
     93 
     94 
     95def 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) 
    112107    #import sys; print "\n".join(sys.path) 
    113     __import__('sasmodels.models.'+model_name) 
    114     kernel_module = getattr(models, model_name, None) 
    115108    return generate.make_model_info(kernel_module) 
    116109 
     
    196189    """ 
    197190    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 
    201192    limits = parameter.limits 
    202193    disperser = values.get(parameter.name+'_pd_type', 'gaussian') 
     
    204195    width = values.get(parameter.name+'_pd', 0.0) 
    205196    nsigma = values.get(parameter.name+'_pd_nsigma', 3.0) 
     197    if npts == 0 or width == 0: 
     198        return [value], [] 
    206199    value, weight = weights.get_weights( 
    207200        disperser, npts, width, nsigma, value, limits, relative) 
     
    235228    *mono* is True if polydispersity should be set to none on all parameters. 
    236229    """ 
     230    parameters = kernel.info['parameters'] 
    237231    if mono: 
    238232        active = lambda name: False 
    239233    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 
    242235    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 
    245237    else: 
    246238        active = lambda name: True 
    247239 
    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] 
    250243    values, weights = zip(*vw_pairs) 
    251244 
  • sasmodels/generate.py

    r5ff1b03 rd19962c  
    184184#__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 
    185185 
    186 import sys 
    187186from os.path import abspath, dirname, join as joinpath, exists, basename, \ 
    188187    splitext, getmtime 
     
    198197 
    199198TEMPLATE_ROOT = dirname(__file__) 
    200  
    201 MAX_PD = 4 
    202199 
    203200F16 = np.dtype('float16') 
     
    478475    source = [kernel_header] + user_code 
    479476 
    480     vol_parameters = partable.kernel_pars('volume') 
    481     iq_parameters = partable.kernel_pars('1d') 
    482     iqxy_parameters = partable.kernel_pars('2d') 
    483  
    484477    # Make parameters for q, qx, qy so that we can use them in declarations 
    485478    q, qx, qy = [Parameter(name=v) for v in ('q', 'qx', 'qy')] 
    486479    # Generate form_volume function, etc. from body only 
    487480    if model_info['form_volume'] is not None: 
    488         pars = vol_parameters 
     481        pars = partable.form_volume_parameters 
    489482        source.append(_gen_fn('form_volume', pars, model_info['form_volume'])) 
    490483    if model_info['Iq'] is not None: 
    491         pars = [q] + iq_parameters 
     484        pars = [q] + partable.iq_parameters 
    492485        source.append(_gen_fn('Iq', pars, model_info['Iq'])) 
    493486    if model_info['Iqxy'] is not None: 
    494         pars = [qx, qy] + iqxy_parameters 
     487        pars = [qx, qy] + partable.iqxy_parameters 
    495488        source.append(_gen_fn('Iqxy', pars, model_info['Iqxy'])) 
    496489 
     
    498491    source.append("#define PARAMETER_TABLE \\") 
    499492    source.append("\\\n".join(p.as_definition() 
    500                                   for p in model_info['parameters'][2:])) 
     493                              for p in partable.kernel_parameters)) 
    501494 
    502495    # 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) 
    505498        call_volume = "#define CALL_VOLUME(v) form_volume(%s)" % (",".join(refs)) 
    506499    else: 
     
    511504    source.append(call_volume) 
    512505 
    513     refs = ["q[i]"] + _call_pars("v.", iq_parameters) 
     506    refs = ["q[i]"] + _call_pars("v.", partable.iq_parameters) 
    514507    call_iq = "#define CALL_IQ(q,i,v) Iq(%s)" % (",".join(refs)) 
    515508    if _have_Iqxy(user_code): 
    516509        # 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) 
    518511        call_iqxy = "#define CALL_IQ(q,i,v) Iqxy(%s)" % (",".join(refs)) 
    519512    else: 
     
    525518 
    526519    # 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) 
    529522 
    530523    # TODO: allow mixed python/opencl kernels? 
     
    546539    return '\n'.join(source) 
    547540 
    548 def categorize_parameters(pars): 
    549     """ 
    550     Categorize the parameters by use: 
    551  
    552     * *pd* list of polydisperse parameters in order; gui should test whether 
    553       they are in *2d* or *magnetic* as appropriate for the data 
    554     * *1d* set of parameters that are used to compute 1D patterns 
    555     * *2d* set of parameters that are used to compute 2D patterns (which 
    556       includes all 1D parameters) 
    557     * *magnetic* set of parameters that are used to compute magnetic 
    558       patterns (which includes all 1D and 2D parameters) 
    559     * *pd_relative* is the set of parameters with relative distribution 
    560       width (e.g., radius +/- 10%) rather than absolute distribution 
    561       width (e.g., theta +/- 6 degrees). 
    562     * *theta_par* is the index of the polar angle polydispersion parameter 
    563       or -1 if no such parameter exists 
    564     """ 
    565     par_set = {} 
    566  
    567541def process_parameters(model_info): 
    568542    """ 
     
    573547        model_info['demo'] = partable.defaults 
    574548 
    575     # Don't use more polydisperse parameters than are available in the model 
    576     # Note: we can do polydispersity on arbitrary parameters, so it is not 
    577     # clear that this is a good idea; it does however make the poly_details 
    578     # code easier to write, so we will leave it in for now. 
    579     model_info['max_pd'] = min(partable.num_pd, MAX_PD) 
    580  
    581549class CoordinationDetails(object): 
    582550    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 
    585554        par_offset = 4*max_pd 
    586555        self.details = np.zeros(par_offset + 3*npars + 4, 'i4') 
     
    596565 
    597566        # theta_par is fixed 
    598         self.details[-1] = model_info['parameters'].theta_par 
     567        self.details[-1] = parameters.theta_offset 
    599568 
    600569    @property 
     
    647616 
    648617def 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 
    651620 
    652621    # Decreasing list of polydispersity lengths 
     
    654623    pd_length = np.array([len(w) for w in weights]) 
    655624    num_active = np.sum(pd_length>1) 
    656     if num_active > max_pd: 
     625    if num_active > model_info['parameters'].max_pd: 
    657626        raise ValueError("Too many polydisperse parameters") 
    658627 
     
    745714      *model_info* blocks for the composition objects.  This allows us to 
    746715      build complete product and mixture models from just the info. 
    747     * *max_pd* is the max polydispersity dimension.  This is constant and 
    748       should not be reset.  You may be able to change it when the program 
    749       starts by setting *sasmodels.generate.MAX_PD*. 
    750  
    751716    """ 
    752717    # TODO: maybe turn model_info into a class ModelDefinition 
     
    837802 
    838803 
    839  
    840804def demo_time(): 
    841805    """ 
     
    853817    Program which prints the source produced by the model. 
    854818    """ 
     819    import sys 
     820    from sasmodels.core import make_model_by_name 
    855821    if len(sys.argv) <= 1: 
    856822        print("usage: python -m sasmodels.generate modelname") 
    857823    else: 
    858824        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) 
    863826        source = make_source(model_info) 
    864827        print(source) 
  • sasmodels/kerneldll.py

    r5ff1b03 rd19962c  
    266266        assert weights.dtype == real and values.dtype == real 
    267267 
    268         max_pd = self.info['max_pd'] 
    269268        start, stop = 0, details.total_pd 
    270269        #print("in kerneldll") 
  • sasmodels/modelinfo.py

    rc499331 rd19962c  
    44# TODO: turn ModelInfo into a proper class 
    55ModelInfo = dict 
     6 
     7MAX_PD = 4 
    68 
    79COMMON_PARAMETERS = [ 
     
    153155    *length* is the length of the field if it is a vector field 
    154156    *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 
    155158    *polydisperse* is true if the parameter accepts a polydispersity 
    156159    *relative_pd* is true if that polydispersity is relative 
     
    163166        self.id = name.split('[')[0].strip() 
    164167        self.name = name 
     168        self.units = units 
    165169        self.default = default 
    166170        self.limits = limits 
     
    169173        self.choices = None 
    170174 
    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 
    173176        # parameter table is available. 
    174177        self.length = 1 
    175178        self.length_control = None 
     179        self.is_control = False 
    176180 
    177181        # TODO: need better control over whether a parameter is polydisperse 
     
    216220        return "P<%s>"%self.name 
    217221 
     222 
    218223class 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    """ 
    219281    # scale and background are implicit parameters 
    220282    COMMON = [Parameter(*p) for p in COMMON_PARAMETERS] 
    221283 
    222284    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() 
    225288        self._categorize_parameters() 
    226  
    227         self._set_vector_lengths() 
    228289        self._set_defaults() 
     290        #self._name_table= dict((p.id, p) for p in parameters) 
    229291 
    230292    def _set_vector_lengths(self): 
    231293        # 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: 
    233295            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 
    235303                low, high = ref.limits 
    236304                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) 
    238307                p.length = high 
    239308 
     
    241310        # Construct default values, including vector defaults 
    242311        defaults = {} 
    243         for p in self.parameters: 
     312        for p in self.call_parameters: 
    244313            if p.length == 1: 
    245314                defaults[p.id] = p.default 
    246315            else: 
    247                 for k in range(p.length): 
    248                     defaults["%s[%d]"%(p.id, k)] = p.default 
     316                for k in range(1, p.length+1): 
     317                    defaults["%s%d"%(p.id, k)] = p.default 
    249318        self.defaults = defaults 
    250319 
     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 
    251335    def __getitem__(self, k): 
    252336        if isinstance(k, (int, slice)): 
     
    259343 
    260344    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 
    273366        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 
    328471 
    329472 
Note: See TracChangeset for help on using the changeset viewer.