Changeset 6dc78e4 in sasmodels


Ignore:
Timestamp:
Aug 17, 2016 5:12:51 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:
deb854f, 300a2f7
Parents:
6b7e2f7
Message:

re-enable structure factor product

Location:
sasmodels
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/convert.py

    r40a87fa r6dc78e4  
    180180        composition_type, parts = model_info.composition 
    181181        if composition_type == 'product': 
    182             oldpars = {'scale':'scale_factor'} 
    183             oldpars.update(_get_old_pars(parts[0])) 
    184             oldpars.update(_get_old_pars(parts[1])) 
     182            translation = {'scale':'scale_factor'} 
     183            translation.update(_get_translation_table(parts[0])) 
     184            translation.update(_get_translation_table(parts[1])) 
    185185        else: 
    186186            raise NotImplementedError("cannot convert to sasview sum") 
    187187    else: 
    188188        translation = _get_translation_table(model_info) 
    189         oldpars = _revert_pars(_unscale_sld(model_info, pars), translation) 
    190         oldpars = _trim_vectors(model_info, pars, oldpars) 
     189    oldpars = _revert_pars(_unscale_sld(model_info, pars), translation) 
     190    oldpars = _trim_vectors(model_info, pars, oldpars) 
    191191 
    192192    # Make sure the control parameter is an integer 
  • sasmodels/details.py

    r725ee36 r6dc78e4  
    177177 
    178178def make_details(model_info, length, offset, num_weights): 
     179    # type: (ModelInfo, np.ndarray, np.ndarray, int) -> CallDetails 
    179180    """ 
    180181    Return a :class:`CallDetails` object for a polydisperse calculation 
     
    186187    array. 
    187188    """ 
    188     # type: (ModelInfo, np.ndarray, np.ndarray, int) -> CallDetails 
    189189    #pars = model_info.parameters.call_parameters[2:model_info.parameters.npars+2] 
    190190    #print(", ".join(str(i)+"-"+p.id for i,p in enumerate(pars))) 
     
    275275    """ 
    276276    value, weight = zip(*pars) 
    277     weight = [w if w else [1.] for w in weight] 
     277    #weight = [w if len(w)>0 else [1.] for w in weight] 
    278278    weight = np.vstack([v.flatten() for v in meshgrid(*weight)]) 
    279279    weight = np.prod(weight, axis=0) 
  • sasmodels/mixture.py

    rfe496dd r6dc78e4  
    116116        self.kernels = kernels 
    117117        self.dtype = self.kernels[0].dtype 
     118        self.results = []  # type: List[np.ndarray] 
    118119 
    119120    def __call__(self, call_details, values, cutoff, magnetic): 
     
    122123        total = 0.0 
    123124        # remember the parts for plotting later 
    124         self.results = [] 
     125        self.results = []  # type: List[np.ndarray] 
    125126        offset = 2 # skip scale & background 
    126127        parts = MixtureParts(self.info, self.kernels, call_details, values) 
  • sasmodels/product.py

    r9eb3632 r6dc78e4  
    99 
    1010To use it, first load form factor P and structure factor S, then create 
    11 *ProductModel(P, S)*. 
     11*make_product_info(P, S)*. 
    1212""" 
     13from __future__ import print_function, division 
     14 
     15from copy import copy 
    1316import numpy as np  # type: ignore 
    1417 
    15 from .modelinfo import suffix_parameter, ParameterTable, ModelInfo 
     18from .modelinfo import Parameter, ParameterTable, ModelInfo 
    1619from .kernel import KernelModel, Kernel 
    17 from .details import dispersion_mesh 
     20from .details import make_details, dispersion_mesh 
    1821 
    1922try: 
    2023    from typing import Tuple 
    21     from .modelinfo import ParameterSet 
    22     from .details import CallDetails 
    2324except ImportError: 
    2425    pass 
     
    2627# TODO: make estimates available to constraints 
    2728#ESTIMATED_PARAMETERS = [ 
    28 #    ["est_effect_radius", "A", 0.0, [0, np.inf], "", "Estimated effective radius"], 
     29#    ["est_radius_effective", "A", 0.0, [0, np.inf], "", "Estimated effective radius"], 
    2930#    ["est_volume_ratio", "", 1.0, [0, np.inf], "", "Estimated volume ratio"], 
    3031#] 
     32 
     33ER_ID = "radius_effective" 
     34VF_ID = "volfraction" 
    3135 
    3236# TODO: core_shell_sphere model has suppressed the volume ratio calculation 
     
    3741    Create info block for product model. 
    3842    """ 
     43    # Make sure effective radius is the first parameter and 
     44    # make sure volume fraction is the second parameter of the 
     45    # structure factor calculator.  Structure factors should not 
     46    # have any magnetic parameters 
     47    assert(s_info.parameters.kernel_parameters[0].id == ER_ID) 
     48    assert(s_info.parameters.kernel_parameters[1].id == VF_ID) 
     49    assert(s_info.parameters.magnetism_index == []) 
    3950    p_id, p_name, p_pars = p_info.id, p_info.name, p_info.parameters 
    4051    s_id, s_name, s_pars = s_info.id, s_info.name, s_info.parameters 
     
    4455    if p_set & s_set: 
    4556        # there is some overlap between the parameter names; tag the 
    46         # overlapping S parameters with name_S 
    47         s_list = [(suffix_parameter(par, "_S") if par.id in p_set else par) 
    48                   for par in s_pars.kernel_parameters] 
    49         combined_pars = p_pars.kernel_parameters + s_list 
     57        # overlapping S parameters with name_S. 
     58        # Skip the first parameter of s, which is effective radius 
     59        s_list = [(suffix_parameter(par) if par.id in p_set else par) 
     60                  for par in s_pars.kernel_parameters[1:]] 
    5061    else: 
    51         combined_pars = p_pars.kernel_parameters + s_pars.kernel_parameters 
     62        # Skip the first parameter of s, which is effective radius 
     63        s_list = s_pars.kernel_parameters[1:] 
     64    translate_name = dict((old.id, new.id) for old, new 
     65                          in zip(s_pars.kernel_parameters[1:], s_list)) 
     66    demo = {} 
     67    demo.update((k, v) for k, v in p_info.demo.items() 
     68                if k not in ("background", "scale")) 
     69    demo.update((translate_name[k], v) for k, v in s_info.demo.items() 
     70                if k not in ("background", "scale") and not k.startswith(ER_ID)) 
     71    combined_pars = p_pars.kernel_parameters + s_list 
    5272    parameters = ParameterTable(combined_pars) 
     73    parameters.max_pd = p_pars.max_pd + s_pars.max_pd 
    5374 
    5475    model_info = ModelInfo() 
    5576    model_info.id = '*'.join((p_id, s_id)) 
    56     model_info.name = ' X '.join((p_name, s_name)) 
     77    model_info.name = '*'.join((p_name, s_name)) 
    5778    model_info.filename = None 
    5879    model_info.title = 'Product of %s and %s'%(p_name, s_name) 
     
    6788    #model_info.source = [] 
    6889    # Iq, Iqxy, form_volume, ER, VR and sesans 
     90    # Remember the component info blocks so we can build the model 
    6991    model_info.composition = ('product', [p_info, s_info]) 
     92    model_info.demo = {} 
    7093    return model_info 
     94 
     95def suffix_parameter(par, suffix): 
     96    par = copy(par) 
     97    par.name = par.name + " S" 
     98    par.id = par.id + "_S" 
     99    return par 
    71100 
    72101class ProductModel(KernelModel): 
     
    77106        self.S = S 
    78107 
    79     def __call__(self, q_vectors): 
     108    def make_kernel(self, q_vectors): 
    80109        # type: (List[np.ndarray]) -> Kernel 
    81110        # Note: may be sending the q_vectors to the GPU twice even though they 
     
    104133        self.p_kernel = p_kernel 
    105134        self.s_kernel = s_kernel 
    106  
    107     def __call__(self, details, weights, values, cutoff): 
    108         # type: (CallDetails, np.ndarray, np.ndarray, float) -> np.ndarray 
    109         effect_radius, vol_ratio = call_ER_VR(self.p_kernel.info, vol_pars) 
    110  
    111         p_details, s_details = details.parts 
    112         p_res = self.p_kernel(p_details, weights, values, cutoff) 
    113         s_res = self.s_kernel(s_details, weights, values, cutoff) 
    114         #print s_fixed, s_pd, p_fixed, p_pd 
    115  
    116         return values[0]*(p_res*s_res) + values[1] 
     135        self.dtype = p_kernel.dtype 
     136        self.results = []  # type: List[np.ndarray] 
     137 
     138    def __call__(self, call_details, values, cutoff, magnetic): 
     139        # type: (CallDetails, np.ndarray, float, bool) -> np.ndarray 
     140        p_info, s_info = self.info.composition[1] 
     141 
     142        # if there are magnetic parameters, they will only be on the 
     143        # form factor P, not the structure factor S. 
     144        nmagnetic = len(self.info.parameters.magnetism_index) 
     145        if nmagnetic: 
     146            spin_index = self.info.parameters.npars + 2 
     147            magnetism = values[spin_index: spin_index+3+3*nmagnetic] 
     148        else: 
     149            magnetism = [] 
     150        nvalues = self.info.parameters.nvalues 
     151        nweights = call_details.num_weights 
     152        weights = values[nvalues:nvalues + 2*nweights] 
     153 
     154        # Construct the calling parameters for P. 
     155        p_npars = p_info.parameters.npars 
     156        p_length = call_details.length[:p_npars] 
     157        p_offset = call_details.offset[:p_npars] 
     158        p_details = make_details(p_info, p_length, p_offset, nweights) 
     159        # Set p scale to the volume fraction in s, which is the first of the 
     160        # 'S' parameters in the parameter list, or 2+np in 0-origin. 
     161        volfrac = values[2+p_npars] 
     162        p_values = [[volfrac, 0.0], values[2:2+p_npars], magnetism, weights] 
     163        spacer = (32 - sum(len(v) for v in p_values)%32)%32 
     164        p_values.append([0.]*spacer) 
     165        p_values = np.hstack(p_values).astype(self.p_kernel.dtype) 
     166 
     167        # Call ER and VR for P since these are needed for S. 
     168        p_er, p_vr = calc_er_vr(p_info, p_details, p_values) 
     169        s_vr = (volfrac/p_vr if p_vr != 0. else volfrac) 
     170        #print("volfrac:%g p_er:%g p_vr:%g s_vr:%g"%(volfrac,p_er,p_vr,s_vr)) 
     171 
     172        # Construct the calling parameters for S. 
     173        # The  effective radius is not in the combined parameter list, so 
     174        # the number of 'S' parameters is one less than expected.  The 
     175        # computed effective radius needs to be added into the weights 
     176        # vector, especially since it is a polydisperse parameter in the 
     177        # stand-alone structure factor models.  We will added it at the 
     178        # end so the remaining offsets don't need to change. 
     179        s_npars = s_info.parameters.npars-1 
     180        s_length = call_details.length[p_npars:p_npars+s_npars] 
     181        s_offset = call_details.offset[p_npars:p_npars+s_npars] 
     182        s_length = np.hstack((1, s_length)) 
     183        s_offset = np.hstack((nweights, s_offset)) 
     184        s_details = make_details(s_info, s_length, s_offset, nweights+1) 
     185        v, w = weights[:nweights], weights[nweights:] 
     186        s_values = [[1., 0., p_er, s_vr], 
     187                    # er and vf already included, so start one past the 
     188                    # volfrac parameter, and go two less than the number 
     189                    # of S parameters. 
     190                    values[2+p_npars+2:2+p_npars+s_npars-1], 
     191                    # no magnetism parameters to include for S 
     192                    # add er into the (value, weights) pairs 
     193                    v, [p_er], w, [1.0]] 
     194        spacer = (32 - sum(len(v) for v in s_values)%32)%32 
     195        s_values.append([0.]*spacer) 
     196        s_values = np.hstack(s_values).astype(self.s_kernel.dtype) 
     197 
     198        # Call the kernels 
     199        p_result = self.p_kernel(p_details, p_values, cutoff, magnetic) 
     200        s_result = self.s_kernel(s_details, s_values, cutoff, False) 
     201 
     202        #call_details.show(values) 
     203        #print("values", values) 
     204        #p_details.show(p_values) 
     205        #print("=>", p_result) 
     206        #print("p val", s_values) 
     207        #s_details.show(s_values) 
     208        #print("=>", s_result) 
     209 
     210        # remember the parts for plotting later 
     211        self.results = [p_result, s_result] 
     212 
     213        #import pylab as plt 
     214        #plt.subplot(211); plt.loglog(self.p_kernel.q_input.q, p_result, '-') 
     215        #plt.subplot(212); plt.loglog(self.s_kernel.q_input.q, s_result, '-') 
     216        #plt.figure() 
     217 
     218        return values[0]*(p_result*s_result) + values[1] 
    117219 
    118220    def release(self): 
     
    121223        self.s_kernel.release() 
    122224 
    123 def call_ER_VR(model_info, pars): 
    124     """ 
    125     Return effect radius and volume ratio for the model. 
    126     """ 
     225 
     226def calc_er_vr(model_info, call_details, values): 
     227    # type: (ModelInfo, ParameterSet) -> Tuple[float, float] 
     228 
    127229    if model_info.ER is None and model_info.VR is None: 
    128230        return 1.0, 1.0 
    129231 
    130     value, weight = _vol_pars(model_info, pars) 
     232    nvalues = model_info.parameters.nvalues 
     233    value = values[nvalues:nvalues + call_details.num_weights] 
     234    weight = values[nvalues + call_details.num_weights: nvalues + 2*call_details.num_weights] 
     235    npars = model_info.parameters.npars 
     236    pairs = [(value[offset:offset+length], weight[offset:offset+length]) 
     237             for p, offset, length 
     238             in zip(model_info.parameters.call_parameters[2:2+npars], 
     239                    call_details.offset, 
     240                    call_details.length) 
     241             if p.type == 'volume'] 
     242    value, weight = dispersion_mesh(model_info, pairs) 
    131243 
    132244    if model_info.ER is not None: 
    133245        individual_radii = model_info.ER(*value) 
    134         effect_radius = np.sum(weight*individual_radii) / np.sum(weight) 
     246        radius_effective = np.sum(weight*individual_radii) / np.sum(weight) 
    135247    else: 
    136         effect_radius = 1.0 
     248        radius_effective = 1.0 
    137249 
    138250    if model_info.VR is not None: 
     
    142254        volume_ratio = 1.0 
    143255 
    144     return effect_radius, volume_ratio 
    145  
    146 def _vol_pars(model_info, pars): 
    147     # type: (ModelInfo, ParameterSet) -> Tuple[np.ndarray, np.ndarray] 
    148     vol_pars = [get_weights(p, pars) 
    149                 for p in model_info.parameters.call_parameters 
    150                 if p.type == 'volume'] 
    151     value, weight = dispersion_mesh(model_info, vol_pars) 
    152     return value, weight 
    153  
     256    return radius_effective, volume_ratio 
Note: See TracChangeset for help on using the changeset viewer.