Changeset 6d6508e in sasmodels for sasmodels/product.py


Ignore:
Timestamp:
Apr 7, 2016 6:57:33 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:
d2fc9a4
Parents:
3707eee
Message:

refactor model_info from dictionary to class

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/product.py

    rea05c87 r6d6508e  
    1313import numpy as np 
    1414 
    15 from .core import call_ER_VR 
     15from .details import dispersion_mesh 
     16from .modelinfo import suffix_parameter, ParameterTable, Parameter, ModelInfo 
    1617 
    17 SCALE=0 
    18 BACKGROUND=1 
    19 RADIUS_EFFECTIVE=2 
    20 VOLFRACTION=3 
     18# TODO: make estimates available to constraints 
     19#ESTIMATED_PARAMETERS = [ 
     20#    ["est_effect_radius", "A", 0.0, [0, np.inf], "", "Estimated effective radius"], 
     21#    ["est_volume_ratio", "", 1.0, [0, np.inf], "", "Estimated volume ratio"], 
     22#] 
    2123 
    2224# TODO: core_shell_sphere model has suppressed the volume ratio calculation 
     
    2628    Create info block for product model. 
    2729    """ 
    28     p_id, p_name, p_pars = p_info['id'], p_info['name'], p_info['parameters'] 
    29     s_id, s_name, s_pars = s_info['id'], s_info['name'], s_info['parameters'] 
    30     # We require models to start with scale and background 
    31     assert s_pars[SCALE].name == 'scale' 
    32     assert s_pars[BACKGROUND].name == 'background' 
    33     # We require structure factors to start with effect radius and volfraction 
    34     assert s_pars[RADIUS_EFFECTIVE].name == 'radius_effective' 
    35     assert s_pars[VOLFRACTION].name == 'volfraction' 
    36     # Combine the parameter sets.  We are skipping the first three 
    37     # parameters of S since scale, background are defined in P and 
    38     # effect_radius is set from P.ER(). 
    39     pars = p_pars + s_pars[3:] 
    40     # check for duplicates; can't use assertion since they may never be checked 
    41     if len(set(p.name for p in pars)) != len(pars): 
    42         raise ValueError("Duplicate parameters in %s and %s"%(p_id)) 
    43     model_info = {} 
    44     model_info['id'] = '*'.join((p_id, s_id)) 
    45     model_info['name'] = ' X '.join((p_name, s_name)) 
    46     model_info['filename'] = None 
    47     model_info['title'] = 'Product of %s and structure factor %s'%(p_name, s_name) 
    48     model_info['description'] = model_info['title'] 
    49     model_info['docs'] = model_info['title'] 
    50     model_info['category'] = "custom" 
    51     model_info['parameters'] = pars 
    52     #model_info['single'] = p_info['single'] and s_info['single'] 
    53     model_info['structure_factor'] = False 
    54     model_info['variant_info'] = None 
    55     #model_info['tests'] = [] 
    56     #model_info['source'] = [] 
     30    p_id, p_name, p_partable = p_info.id, p_info.name, p_info.parameters 
     31    s_id, s_name, s_partable = s_info.id, s_info.name, s_info.parameters 
     32    p_set = set(p.id for p in p_partable) 
     33    s_set = set(p.id for p in s_partable) 
     34 
     35    if p_set & s_set: 
     36        # there is some overlap between the parameter names; tag the 
     37        # overlapping S parameters with name_S 
     38        s_pars = [(suffix_parameter(par, "_S") if par.id in p_set else par) 
     39                  for par in s_partable.kernel_parameters] 
     40        pars = p_partable.kernel_parameters + s_pars 
     41    else: 
     42        pars= p_partable.kernel_parameters + s_partable.kernel_parameters 
     43 
     44    model_info = ModelInfo() 
     45    model_info.id = '*'.join((p_id, s_id)) 
     46    model_info.name = ' X '.join((p_name, s_name)) 
     47    model_info.filename = None 
     48    model_info.title = 'Product of %s and %s'%(p_name, s_name) 
     49    model_info.description = model_info.title 
     50    model_info.docs = model_info.title 
     51    model_info.category = "custom" 
     52    model_info.parameters = ParameterTable(pars) 
     53    #model_info.single = p_info.single and s_info.single 
     54    model_info.structure_factor = False 
     55    model_info.variant_info = None 
     56    #model_info.tests = [] 
     57    #model_info.source = [] 
    5758    # Iq, Iqxy, form_volume, ER, VR and sesans 
    58     model_info['composition'] = ('product', [p_info, s_info]) 
     59    model_info.composition = ('product', [p_info, s_info]) 
    5960    return model_info 
    6061 
     
    8687class ProductKernel(object): 
    8788    def __init__(self, model_info, p_kernel, s_kernel): 
    88         dim = '2d' if p_kernel.q_input.is_2d else '1d' 
    89  
    90         # Need to know if we want 2D and magnetic parameters when constructing 
    91         # a parameter map. 
    92         par_map = {} 
    93         p_info = p_kernel.info['par_type'] 
    94         s_info = s_kernel.info['par_type'] 
    95         vol_pars = set(p_info['volume']) 
    96         if dim == '2d': 
    97             num_p_fixed = len(p_info['fixed-2d']) 
    98             num_p_pd = len(p_info['pd-2d']) 
    99             num_s_fixed = len(s_info['fixed-2d']) 
    100             num_s_pd = len(s_info['pd-2d']) - 1 # exclude effect_radius 
    101             # volume parameters are amongst the pd pars for P, not S 
    102             vol_par_idx = [k for k,v in enumerate(p_info['pd-2d']) 
    103                            if v in vol_pars] 
    104         else: 
    105             num_p_fixed = len(p_info['fixed-1d']) 
    106             num_p_pd = len(p_info['pd-1d']) 
    107             num_s_fixed = len(s_info['fixed-1d']) 
    108             num_s_pd = len(s_info['pd-1d']) - 1  # exclude effect_radius 
    109             # volume parameters are amongst the pd pars for P, not S 
    110             vol_par_idx = [k for k,v in enumerate(p_info['pd-1d']) 
    111                            if v in vol_pars] 
    112  
    113         start = 0 
    114         par_map['p_fixed'] = np.arange(start, start+num_p_fixed) 
    115         # User doesn't set scale, background or effect_radius for S in P*S, 
    116         # so borrow values from end of p_fixed.  This makes volfraction the 
    117         # first S parameter. 
    118         start += num_p_fixed 
    119         par_map['s_fixed'] = np.hstack(([start,start], 
    120                                         np.arange(start, start+num_s_fixed-2))) 
    121         par_map['volfraction'] = num_p_fixed 
    122         start += num_s_fixed-2 
    123         # vol pars offset from the start of pd pars 
    124         par_map['vol_pars'] = [start+k for k in vol_par_idx] 
    125         par_map['p_pd'] = np.arange(start, start+num_p_pd) 
    126         start += num_p_pd-1 
    127         par_map['s_pd'] = np.hstack((start, 
    128                                      np.arange(start, start+num_s_pd-1))) 
    129  
    130         self.fixed_pars = model_info['partype']['fixed-' + dim] 
    131         self.pd_pars = model_info['partype']['pd-' + dim] 
    13289        self.info = model_info 
    13390        self.p_kernel = p_kernel 
    13491        self.s_kernel = s_kernel 
    135         self.par_map = par_map 
    13692 
    137     def __call__(self, fixed_pars, pd_pars, cutoff=1e-5): 
    138         pars = fixed_pars + pd_pars 
    139         scale = pars[SCALE] 
    140         background = pars[BACKGROUND] 
    141         s_volfraction = pars[self.par_map['volfraction']] 
    142         p_fixed = [pars[k] for k in self.par_map['p_fixed']] 
    143         s_fixed = [pars[k] for k in self.par_map['s_fixed']] 
    144         p_pd = [pars[k] for k in self.par_map['p_pd']] 
    145         s_pd = [pars[k] for k in self.par_map['s_pd']] 
    146         vol_pars = [pars[k] for k in self.par_map['vol_pars']] 
    147  
     93    def __call__(self, details, weights, values, cutoff): 
    14894        effect_radius, vol_ratio = call_ER_VR(self.p_kernel.info, vol_pars) 
    14995 
     
    155101        s_pd[0] = [effect_radius], [1.0] 
    156102 
    157         p_res = self.p_kernel(p_fixed, p_pd, cutoff) 
    158         s_res = self.s_kernel(s_fixed, s_pd, cutoff) 
     103        p_res = self.p_kernel(p_details, p_weights, p_values, cutoff) 
     104        s_res = self.s_kernel(s_details, s_weights, s_values, cutoff) 
    159105        #print s_fixed, s_pd, p_fixed, p_pd 
    160106 
     
    165111        self.q_kernel.release() 
    166112 
     113def call_ER_VR(model_info, vol_pars): 
     114    """ 
     115    Return effect radius and volume ratio for the model. 
     116    """ 
     117    value, weight = dispersion_mesh(vol_pars) 
     118 
     119    individual_radii = model_info.ER(*value) if model_info.ER else 1.0 
     120    whole, part = model_info.VR(*value) if model_info.VR else (1.0, 1.0) 
     121 
     122    effect_radius = np.sum(weight*individual_radii) / np.sum(weight) 
     123    volume_ratio = np.sum(weight*part)/np.sum(weight*whole) 
     124    return effect_radius, volume_ratio 
Note: See TracChangeset for help on using the changeset viewer.