Changeset fb9a3b6 in sasmodels


Ignore:
Timestamp:
Aug 31, 2017 12:07:59 PM (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:
bcdd6c9
Parents:
573ffab
Message:

Allow mixture models to do multiplication as well as addition

Location:
sasmodels
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/core.py

    r2e66ef5 rfb9a3b6  
    126126 
    127127 
    128 def load_model_info(model_name): 
     128def load_model_info(model_name, force_mixture=False): 
    129129    # type: (str) -> modelinfo.ModelInfo 
    130130    """ 
     
    134134    such as sphere*hardsphere or sphere+cylinder. 
    135135 
     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 
    136140    This returns a handle to the module defining the model.  This can be 
    137141    used with functions in generate to build the docs or extract model info. 
     
    139143    parts = model_name.split('+') 
    140144    if len(parts) > 1: 
     145        # Always use MixtureModel for addition 
    141146        model_info_list = [load_model_info(p) for p in parts] 
    142147        return mixture.make_mixture_info(model_info_list) 
     
    144149    parts = model_name.split('*') 
    145150    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='*') 
    146155        if len(parts) > 2: 
    147156            raise ValueError("use P*S to apply structure factor S to model P") 
     157        # Use ProductModel 
    148158        P_info, Q_info = [load_model_info(p) for p in parts] 
    149159        return product.make_product_info(P_info, Q_info) 
  • sasmodels/mixture.py

    r6dc78e4 rfb9a3b6  
    2525    pass 
    2626 
    27 def make_mixture_info(parts): 
     27def make_mixture_info(parts, operation='+'): 
    2828    # type: (List[ModelInfo]) -> ModelInfo 
    2929    """ 
     
    4646        # to support vector parameters 
    4747        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) 
     48        if operation == '+': 
     49            # If model is a sum model, each constituent model gets its own scale parameter 
     50            scale =  Parameter(prefix+'scale', default=1.0, 
     51                            description="model intensity for " + part.name) 
     52            combined_pars.append(scale) 
    5153        for p in part.parameters.kernel_parameters: 
    5254            p = copy(p) 
     
    6365 
    6466    model_info = ModelInfo() 
    65     model_info.id = '+'.join(part.id for part in parts) 
    66     model_info.name = ' + '.join(part.name for part in parts) 
     67    model_info.id = operation.join(part.id for part in parts) 
     68    model_info.operation = operation 
     69    model_info.name = operation.join(part.name for part in parts) 
    6770    model_info.filename = None 
    6871    model_info.title = 'Mixture model with ' + model_info.name 
     
    116119        self.kernels = kernels 
    117120        self.dtype = self.kernels[0].dtype 
     121        self.operation = model_info.operation 
    118122        self.results = []  # type: List[np.ndarray] 
    119123 
     
    124128        # remember the parts for plotting later 
    125129        self.results = []  # type: List[np.ndarray] 
    126         offset = 2 # skip scale & background 
    127130        parts = MixtureParts(self.info, self.kernels, call_details, values) 
    128131        for kernel, kernel_details, kernel_values in parts: 
    129132            #print("calling kernel", kernel.info.name) 
    130133            result = kernel(kernel_details, kernel_values, cutoff, magnetic) 
    131             #print(kernel.info.name, result) 
    132             total += result 
     134            result = np.array(result).astype(kernel.dtype) 
     135            # print(kernel.info.name, result) 
     136            if self.operation == '+': 
     137                total += result 
     138            elif self.operation == '*': 
     139                if np.all(total) == 0.0: 
     140                    total = result 
     141                else: 
     142                    total *= result 
    133143            self.results.append(result) 
    134144 
     
    171181 
    172182        self.part_num += 1 
    173         self.par_index += info.parameters.npars + 1 
     183        self.par_index += info.parameters.npars 
     184        if self.model_info.operation == '+': 
     185            self.par_index += 1 # Account for each constituent model's scale param 
    174186        self.mag_index += 3 * len(info.parameters.magnetism_index) 
    175187 
     
    182194        # which includes the initial scale and background parameters. 
    183195        # We want the index into the weight length/offset for each parameter. 
    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) 
     196        # Exclude the initial scale and background, so subtract two. If we're 
     197        # building an addition model, each component has its own scale factor 
     198        # which we need to skip when constructing the details for the kernel, so 
     199        # add one, giving a net subtract one. 
     200        diff = 1 if self.model_info.operation == '+' else 2 
     201        index = slice(par_index - diff, par_index - diff + info.parameters.npars) 
    189202        length = full.length[index] 
    190203        offset = full.offset[index] 
     
    196209    def _part_values(self, info, par_index, mag_index): 
    197210        # type: (ModelInfo, int, int) -> np.ndarray 
    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] 
     211        # Set each constituent model's scale to 1 if this is a multiplication model 
     212        scale = self.values[par_index] if self.model_info.operation == '+' else 1.0 
     213        diff = 1 if self.model_info.operation == '+' else 0 # Skip scale if addition model 
     214        pars = self.values[par_index + diff:par_index + info.parameters.npars + diff] 
    201215        nmagnetic = len(info.parameters.magnetism_index) 
    202216        if nmagnetic: 
Note: See TracChangeset for help on using the changeset viewer.