Changeset 61a4bd4 in sasmodels


Ignore:
Timestamp:
Sep 4, 2017 10:09:27 AM (7 years ago)
Author:
lewis
Branches:
master, core_shell_microgels, costrafo411, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
481ff64
Parents:
65314f7
Message:

Refactor load_model_info to parse more complex model strings

Location:
sasmodels
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/core.py

    rfb9a3b6 r61a4bd4  
    126126 
    127127 
    128 def load_model_info(model_name, force_mixture=False): 
     128# def load_model_info(model_name, force_mixture=False): 
     129#     # type: (str) -> modelinfo.ModelInfo 
     130#     """ 
     131#     Load a model definition given the model name. 
     132 
     133#     *model_name* is the name of the model, or perhaps a model expression 
     134#     such as sphere*hardsphere or sphere+cylinder. 
     135 
     136#     *force_mixture* if true, MixtureModel will be used for combining models. 
     137#     Otherwise either MixtureModel will be used for addition and ProductModel 
     138#     will be used for multiplication 
     139 
     140#     This returns a handle to the module defining the model.  This can be 
     141#     used with functions in generate to build the docs or extract model info. 
     142#     """ 
     143#     parts = model_name.split('+') 
     144#     if len(parts) > 1: 
     145#         # Always use MixtureModel for addition 
     146#         model_info_list = [load_model_info(p) for p in parts] 
     147#         return mixture.make_mixture_info(model_info_list) 
     148 
     149#     parts = model_name.split('*') 
     150#     if len(parts) > 1: 
     151#         if force_mixture: 
     152#             # Use MixtureModel for multiplication if forced 
     153#             model_info_list = [load_model_info(p) for p in parts] 
     154#             return mixture.make_mixture_info(model_info_list, operation='*') 
     155#         if len(parts) > 2: 
     156#             raise ValueError("use P*S to apply structure factor S to model P") 
     157#         # Use ProductModel 
     158#         P_info, Q_info = [load_model_info(p) for p in parts] 
     159#         return product.make_product_info(P_info, Q_info) 
     160 
     161#     kernel_module = generate.load_kernel_module(model_name) 
     162#     return modelinfo.make_model_info(kernel_module) 
     163 
     164def load_model_info(model_string): 
    129165    # type: (str) -> modelinfo.ModelInfo 
    130166    """ 
     
    134170    such as sphere*hardsphere or sphere+cylinder. 
    135171 
    136     *force_mixture* if true, MixtureModel will be used for combining models. 
    137     Otherwise either MixtureModel will be used for addition and ProductModel 
    138     will be used for multiplication 
    139  
    140172    This returns a handle to the module defining the model.  This can be 
    141173    used with functions in generate to build the docs or extract model info. 
    142174    """ 
    143     parts = model_name.split('+') 
    144     if len(parts) > 1: 
    145         # Always use MixtureModel for addition 
    146         model_info_list = [load_model_info(p) for p in parts] 
    147         return mixture.make_mixture_info(model_info_list) 
    148  
    149     parts = model_name.split('*') 
    150     if len(parts) > 1: 
    151         if force_mixture: 
    152             # Use MixtureModel for multiplication if forced 
    153             model_info_list = [load_model_info(p) for p in parts] 
    154             return mixture.make_mixture_info(model_info_list, operation='*') 
    155         if len(parts) > 2: 
    156             raise ValueError("use P*S to apply structure factor S to model P") 
    157         # Use ProductModel 
    158         P_info, Q_info = [load_model_info(p) for p in parts] 
    159         return product.make_product_info(P_info, Q_info) 
    160  
    161     kernel_module = generate.load_kernel_module(model_name) 
    162     return modelinfo.make_model_info(kernel_module) 
     175    # TODO: parse an expression like form@structure to create a P(Q)*S(Q) model 
     176    product_parts = [] 
     177    addition_parts = [] 
     178 
     179    addition_parts_names = model_string.split('+') 
     180    if len(addition_parts_names) >= 2: 
     181        addition_parts = [load_model_info(part) for part in addition_parts_names] 
     182    elif len(addition_parts_names) == 1: 
     183        product_parts_names = model_string.split('*') 
     184        if len(product_parts_names) >= 2: 
     185            product_parts = [load_model_info(part) for part in product_parts_names] 
     186        elif len(product_parts_names) == 1: 
     187            kernel_module = generate.load_kernel_module(product_parts_names[0]) 
     188            return modelinfo.make_model_info(kernel_module) 
     189 
     190    model = None 
     191    if len(product_parts) > 1: 
     192        model = mixture.make_mixture_info(product_parts, operation='*') 
     193    if len(addition_parts) > 1: 
     194        if model is not None: 
     195            addition_parts.append(model) 
     196        model = mixture.make_mixture_info(addition_parts, operation='+') 
     197    return model 
    163198 
    164199 
  • sasmodels/mixture.py

    rfb9a3b6 r61a4bd4  
    3030    Create info block for mixture model. 
    3131    """ 
    32     flatten = [] 
    33     for part in parts: 
    34         if part.composition and part.composition[0] == 'mixture': 
    35             flatten.extend(part.composition[1]) 
    36         else: 
    37             flatten.append(part) 
    38     parts = flatten 
    39  
    4032    # Build new parameter list 
    4133    combined_pars = [] 
    4234    demo = {} 
     35 
     36    model_num = 0 
     37    all_parts = copy(parts) 
     38    is_flat = False 
     39    while not is_flat: 
     40        is_flat = True 
     41        for part in all_parts: 
     42            if part.composition and part.composition[0] == 'mixture' and \ 
     43                len(part.composition[1]) > 1: 
     44                all_parts += part.composition[1] 
     45                all_parts.remove(part) 
     46                is_flat = False 
     47 
     48    # When creating a mixture model that is a sum of product models (ie (1*2)+(3*4)) 
     49    # the parameters for models 1 & 2 will be prefixed with A & B respectively, 
     50    # but so will the parameters for models 3 & 4. We need to rename models 3 & 4 
     51    # so that they are prefixed with C & D to avoid overlap of parameter names. 
     52    used_prefixes = [] 
     53    for part in parts: 
     54        i = 0 
     55        if part.composition and part.composition[0] == 'mixture': 
     56            npars_list = [info.parameters.npars for info in part.composition[1]] 
     57            for npars in npars_list: 
     58                # List of params of one of the constituent models of part 
     59                submodel_pars = part.parameters.kernel_parameters[i:i+npars] 
     60                # Prefix of the constituent model 
     61                prefix = submodel_pars[0].name[0] 
     62                if prefix not in used_prefixes: # Haven't seen this prefix so far 
     63                    used_prefixes.append(prefix) 
     64                    i += npars 
     65                    continue 
     66                while prefix in used_prefixes: 
     67                    # This prefix has been already used, so change it to the 
     68                    # next letter that hasn't been used 
     69                    prefix = chr(ord(prefix) + 1) 
     70                used_prefixes.append(prefix) 
     71                prefix += "_" 
     72                # Update the parameters of this constituent model to use the 
     73                # new prefix 
     74                for par in submodel_pars: 
     75                    par.id = prefix + par.id[2:] 
     76                    par.name = prefix + par.name[2:] 
     77                    if par.length_control is not None: 
     78                        par.length_control = prefix + par.length_control[2:] 
     79                i += npars 
     80 
     81    model_num = len(all_parts) - len(parts) 
     82    if model_num != 0: 
     83        model_num += 1 
     84 
    4385    for k, part in enumerate(parts): 
    4486        # Parameter prefix per model, A_, B_, ... 
    4587        # Note that prefix must also be applied to id and length_control 
    4688        # to support vector parameters 
    47         prefix = chr(ord('A')+k) + '_' 
     89        prefix = chr(ord('A')+k+model_num) + '_' 
     90        if part.composition and part.composition[0] == 'mixture': 
     91            # Parameter already has a prefix as it's part of a composition model 
     92            prefix = '' 
     93            model_num -= 1 
     94             
    4895        if operation == '+': 
    4996            # If model is a sum model, each constituent model gets its own scale parameter 
    50             scale =  Parameter(prefix+'scale', default=1.0, 
     97            scale_prefix = prefix 
     98            if prefix == '' and part.operation == '*': 
     99                # `part` is a composition product model. Find the prefixes of 
     100                # it's parameters to form a new prefix for the scale, eg: 
     101                # a model with A*B*C will have ABC_scale 
     102                sub_prefixes = [] 
     103                for param in part.parameters.kernel_parameters: 
     104                    # Prefix of constituent model 
     105                    sub_prefix = param.id.split('_')[0] 
     106                    if sub_prefix not in sub_prefixes: 
     107                        sub_prefixes.append(sub_prefix) 
     108                # Concatenate sub_prefixes to form prefix for the scale 
     109                scale_prefix = ''.join(sub_prefixes) + '_' 
     110            scale =  Parameter(scale_prefix + 'scale', default=1.0, 
    51111                            description="model intensity for " + part.name) 
    52112            combined_pars.append(scale) 
     
    67127    model_info.id = operation.join(part.id for part in parts) 
    68128    model_info.operation = operation 
    69     model_info.name = operation.join(part.name for part in parts) 
     129    model_info.name = '(' + operation.join(part.name for part in parts) + ')' 
    70130    model_info.filename = None 
    71131    model_info.title = 'Mixture model with ' + model_info.name 
Note: See TracChangeset for help on using the changeset viewer.