Changes in / [22e922e:ce8c388] in sasmodels


Ignore:
Location:
sasmodels
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/core.py

    r60335cc rbb39b4a  
    1010 
    1111import os 
    12 import re 
    1312from os.path import basename, dirname, join as joinpath 
    1413from glob import glob 
     
    2221from . import kernelpy 
    2322from . import kerneldll 
    24 from . import custom 
    2523 
    2624if os.environ.get("SAS_OPENCL", "").lower() == "none": 
     
    3230    except Exception: 
    3331        HAVE_OPENCL = False 
    34  
    35 CUSTOM_MODEL_PATH = os.environ.get('SAS_MODELPATH', "") 
    36 if CUSTOM_MODEL_PATH == "": 
    37     path = joinpath(os.path.expanduser("~"), ".sasmodels", "custom_models") 
    38     if not os.path.isdir(path): 
    39         os.makedirs(path) 
    40     CUSTOM_MODEL_PATH = path 
    4132 
    4233try: 
     
    134125                       dtype=dtype, platform=platform) 
    135126 
    136 def load_model_info(model_string): 
     127 
     128def load_model_info(model_name): 
    137129    # type: (str) -> modelinfo.ModelInfo 
    138130    """ 
    139131    Load a model definition given the model name. 
    140132 
    141     *model_string* is the name of the model, or perhaps a model expression 
    142     such as sphere*cylinder or sphere+cylinder. Use '@' for a structure 
    143     factor product, e.g. sphere@hardsphere. Custom models can be specified by 
    144     prefixing the model name with 'custom.', e.g. 'custom.MyModel+sphere'. 
     133    *model_name* is the name of the model, or perhaps a model expression 
     134    such as sphere*hardsphere or sphere+cylinder. 
    145135 
    146136    This returns a handle to the module defining the model.  This can be 
    147137    used with functions in generate to build the docs or extract model info. 
    148138    """ 
    149     if '@' in model_string: 
    150         parts = model_string.split('@') 
    151         if len(parts) != 2: 
    152             raise ValueError("Use P@S to apply a structure factor S to model P") 
    153         P_info, Q_info = [load_model_info(part) for part in parts] 
     139    parts = model_name.split('+') 
     140    if len(parts) > 1: 
     141        model_info_list = [load_model_info(p) for p in parts] 
     142        return mixture.make_mixture_info(model_info_list) 
     143 
     144    parts = model_name.split('*') 
     145    if len(parts) > 1: 
     146        if len(parts) > 2: 
     147            raise ValueError("use P*S to apply structure factor S to model P") 
     148        P_info, Q_info = [load_model_info(p) for p in parts] 
    154149        return product.make_product_info(P_info, Q_info) 
    155150 
    156     product_parts = [] 
    157     addition_parts = [] 
    158  
    159     addition_parts_names = model_string.split('+') 
    160     if len(addition_parts_names) >= 2: 
    161         addition_parts = [load_model_info(part) for part in addition_parts_names] 
    162     elif len(addition_parts_names) == 1: 
    163         product_parts_names = model_string.split('*') 
    164         if len(product_parts_names) >= 2: 
    165             product_parts = [load_model_info(part) for part in product_parts_names] 
    166         elif len(product_parts_names) == 1: 
    167             if "custom." in product_parts_names[0]: 
    168                 # Extract ModelName from "custom.ModelName" 
    169                 pattern = "custom.([A-Za-z0-9_-]+)" 
    170                 result = re.match(pattern, product_parts_names[0]) 
    171                 if result is None: 
    172                     raise ValueError("Model name in invalid format: " + product_parts_names[0]) 
    173                 model_name = result.group(1) 
    174                 # Use ModelName to find the path to the custom model file 
    175                 model_path = joinpath(CUSTOM_MODEL_PATH, model_name + ".py") 
    176                 if not os.path.isfile(model_path): 
    177                     raise ValueError("The model file {} doesn't exist".format(model_path)) 
    178                 kernel_module = custom.load_custom_kernel_module(model_path) 
    179                 return modelinfo.make_model_info(kernel_module) 
    180             # Model is a core model 
    181             kernel_module = generate.load_kernel_module(product_parts_names[0]) 
    182             return modelinfo.make_model_info(kernel_module) 
    183  
    184     model = None 
    185     if len(product_parts) > 1: 
    186         model = mixture.make_mixture_info(product_parts, operation='*') 
    187     if len(addition_parts) > 1: 
    188         if model is not None: 
    189             addition_parts.append(model) 
    190         model = mixture.make_mixture_info(addition_parts, operation='+') 
    191     return model 
     151    kernel_module = generate.load_kernel_module(model_name) 
     152    return modelinfo.make_model_info(kernel_module) 
    192153 
    193154 
  • sasmodels/mixture.py

    r31ae428 r6dc78e4  
    2525    pass 
    2626 
    27 def make_mixture_info(parts, operation='+'): 
     27def make_mixture_info(parts): 
    2828    # type: (List[ModelInfo]) -> ModelInfo 
    2929    """ 
    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 
    3240    # Build new parameter list 
    3341    combined_pars = [] 
    3442    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     for part in parts: 
     43    for k, part in enumerate(parts): 
    8244        # Parameter prefix per model, A_, B_, ... 
    8345        # Note that prefix must also be applied to id and length_control 
    8446        # to support vector parameters 
    85         prefix = '' 
    86         if not part.composition: 
    87             # Model isn't a composition model, so it's parameters don't have a 
    88             # a prefix. Add the next available prefix 
    89             prefix = chr(ord('A')+len(used_prefixes)) 
    90             used_prefixes.append(prefix) 
    91             prefix += '_' 
    92              
    93         if operation == '+': 
    94             # If model is a sum model, each constituent model gets its own scale parameter 
    95             scale_prefix = prefix 
    96             if prefix == '' and part.operation == '*': 
    97                 # `part` is a composition product model. Find the prefixes of 
    98                 # it's parameters to form a new prefix for the scale, eg: 
    99                 # a model with A*B*C will have ABC_scale 
    100                 sub_prefixes = [] 
    101                 for param in part.parameters.kernel_parameters: 
    102                     # Prefix of constituent model 
    103                     sub_prefix = param.id.split('_')[0] 
    104                     if sub_prefix not in sub_prefixes: 
    105                         sub_prefixes.append(sub_prefix) 
    106                 # Concatenate sub_prefixes to form prefix for the scale 
    107                 scale_prefix = ''.join(sub_prefixes) + '_' 
    108             scale =  Parameter(scale_prefix + 'scale', default=1.0, 
    109                             description="model intensity for " + part.name) 
    110             combined_pars.append(scale) 
     47        prefix = chr(ord('A')+k) + '_' 
     48        scale =  Parameter(prefix+'scale', default=1.0, 
     49                           description="model intensity for " + part.name) 
     50        combined_pars.append(scale) 
    11151        for p in part.parameters.kernel_parameters: 
    11252            p = copy(p) 
     
    12363 
    12464    model_info = ModelInfo() 
    125     model_info.id = operation.join(part.id for part in parts) 
    126     model_info.operation = operation 
    127     model_info.name = '(' + operation.join(part.name for part in parts) + ')' 
     65    model_info.id = '+'.join(part.id for part in parts) 
     66    model_info.name = ' + '.join(part.name for part in parts) 
    12867    model_info.filename = None 
    12968    model_info.title = 'Mixture model with ' + model_info.name 
     
    177116        self.kernels = kernels 
    178117        self.dtype = self.kernels[0].dtype 
    179         self.operation = model_info.operation 
    180118        self.results = []  # type: List[np.ndarray] 
    181119 
     
    186124        # remember the parts for plotting later 
    187125        self.results = []  # type: List[np.ndarray] 
     126        offset = 2 # skip scale & background 
    188127        parts = MixtureParts(self.info, self.kernels, call_details, values) 
    189128        for kernel, kernel_details, kernel_values in parts: 
    190129            #print("calling kernel", kernel.info.name) 
    191130            result = kernel(kernel_details, kernel_values, cutoff, magnetic) 
    192             result = np.array(result).astype(kernel.dtype) 
    193             # print(kernel.info.name, result) 
    194             if self.operation == '+': 
    195                 total += result 
    196             elif self.operation == '*': 
    197                 if np.all(total) == 0.0: 
    198                     total = result 
    199                 else: 
    200                     total *= result 
     131            #print(kernel.info.name, result) 
     132            total += result 
    201133            self.results.append(result) 
    202134 
     
    239171 
    240172        self.part_num += 1 
    241         self.par_index += info.parameters.npars 
    242         if self.model_info.operation == '+': 
    243             self.par_index += 1 # Account for each constituent model's scale param 
     173        self.par_index += info.parameters.npars + 1 
    244174        self.mag_index += 3 * len(info.parameters.magnetism_index) 
    245175 
     
    252182        # which includes the initial scale and background parameters. 
    253183        # We want the index into the weight length/offset for each parameter. 
    254         # Exclude the initial scale and background, so subtract two. If we're 
    255         # building an addition model, each component has its own scale factor 
    256         # which we need to skip when constructing the details for the kernel, so 
    257         # add one, giving a net subtract one. 
    258         diff = 1 if self.model_info.operation == '+' else 2 
    259         index = slice(par_index - diff, par_index - diff + info.parameters.npars) 
     184        # Exclude the initial scale and background, so subtract two, but each 
     185        # component has its own scale factor which we need to skip when 
     186        # constructing the details for the kernel, so add one, giving a 
     187        # net subtract one. 
     188        index = slice(par_index - 1, par_index - 1 + info.parameters.npars) 
    260189        length = full.length[index] 
    261190        offset = full.offset[index] 
     
    267196    def _part_values(self, info, par_index, mag_index): 
    268197        # type: (ModelInfo, int, int) -> np.ndarray 
    269         # Set each constituent model's scale to 1 if this is a multiplication model 
    270         scale = self.values[par_index] if self.model_info.operation == '+' else 1.0 
    271         diff = 1 if self.model_info.operation == '+' else 0 # Skip scale if addition model 
    272         pars = self.values[par_index + diff:par_index + info.parameters.npars + diff] 
     198        #print(info.name, par_index, self.values[par_index:par_index + info.parameters.npars + 1]) 
     199        scale = self.values[par_index] 
     200        pars = self.values[par_index + 1:par_index + info.parameters.npars + 1] 
    273201        nmagnetic = len(info.parameters.magnetism_index) 
    274202        if nmagnetic: 
  • sasmodels/model_test.py

    r65314f7 rbedb9b0  
    201201                ({}, 'VR', None), 
    202202                ] 
    203             tests = smoke_tests 
    204             if self.info.tests is not None: 
    205                 tests += self.info.tests 
     203 
     204            tests = smoke_tests + self.info.tests 
    206205            try: 
    207206                model = build_model(self.info, dtype=self.dtype, 
     
    372371        stream.writeln(traceback.format_exc()) 
    373372        return 
     373 
    374374    # Run the test suite 
    375375    suite.run(result) 
  • sasmodels/modelinfo.py

    r65314f7 r0bdddc2  
    727727    models when the model is first called, not when the model is loaded. 
    728728    """ 
    729     if hasattr(kernel_module, "model_info"): 
    730         # Custom sum/multi models 
    731         return kernel_module.model_info 
    732729    info = ModelInfo() 
    733730    #print("make parameter table", kernel_module.parameters) 
  • sasmodels/product.py

    r6a5ccfb r8f04da4  
    7777 
    7878    model_info = ModelInfo() 
    79     model_info.id = '@'.join((p_id, s_id)) 
    80     model_info.name = '@'.join((p_name, s_name)) 
     79    model_info.id = '*'.join((p_id, s_id)) 
     80    model_info.name = '*'.join((p_name, s_name)) 
    8181    model_info.filename = None 
    8282    model_info.title = 'Product of %s and %s'%(p_name, s_name) 
  • sasmodels/sasview_model.py

    rbcdd6c9 r724257c  
    120120    else: 
    121121        model_info = modelinfo.make_model_info(kernel_module) 
    122         model = make_model_from_info(model_info) 
     122        model = _make_model_from_info(model_info) 
    123123    model.timestamp = getmtime(path) 
    124124 
     
    142142 
    143143 
    144 def make_model_from_info(model_info): 
    145     # type: (ModelInfo) -> SasviewModelType 
    146     """ 
    147     Convert *model_info* into a SasView model wrapper. 
    148     """ 
    149     def __init__(self, multiplicity=None): 
    150         SasviewModel.__init__(self, multiplicity=multiplicity) 
    151     attrs = _generate_model_attributes(model_info) 
    152     attrs['__init__'] = __init__ 
    153     attrs['filename'] = model_info.filename 
    154     ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 
    155     return ConstructedModel 
    156  
    157  
    158144def _make_standard_model(name): 
    159145    # type: (str) -> SasviewModelType 
     
    167153    kernel_module = generate.load_kernel_module(name) 
    168154    model_info = modelinfo.make_model_info(kernel_module) 
    169     return make_model_from_info(model_info) 
    170  
    171      
     155    return _make_model_from_info(model_info) 
     156 
     157 
    172158def _register_old_models(): 
    173159    # type: () -> None 
     
    201187    model_info = product.make_product_info(form_factor._model_info, 
    202188                                           structure_factor._model_info) 
    203     ConstructedModel = make_model_from_info(model_info) 
     189    ConstructedModel = _make_model_from_info(model_info) 
    204190    return ConstructedModel() 
    205191 
     192def _make_model_from_info(model_info): 
     193    # type: (ModelInfo) -> SasviewModelType 
     194    """ 
     195    Convert *model_info* into a SasView model wrapper. 
     196    """ 
     197    def __init__(self, multiplicity=None): 
     198        SasviewModel.__init__(self, multiplicity=multiplicity) 
     199    attrs = _generate_model_attributes(model_info) 
     200    attrs['__init__'] = __init__ 
     201    attrs['filename'] = model_info.filename 
     202    ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 
     203    return ConstructedModel 
    206204 
    207205def _generate_model_attributes(model_info): 
     
    605603        if hasattr(self._model_info, "composition") \ 
    606604           and self._model_info.composition is not None: 
    607             p_model = make_model_from_info(self._model_info.composition[1][0])() 
    608             s_model = make_model_from_info(self._model_info.composition[1][1])() 
     605            p_model = _make_model_from_info(self._model_info.composition[1][0])() 
     606            s_model = _make_model_from_info(self._model_info.composition[1][1])() 
    609607        return p_model, s_model 
    610608 
Note: See TracChangeset for help on using the changeset viewer.