Changes in sasmodels/product.py [c0131d44:33d7be3] in sasmodels


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/product.py

    rc0131d44 r33d7be3  
    2424# pylint: disable=unused-import 
    2525try: 
    26     from typing import Tuple, Callable 
     26    from typing import Tuple, Callable, Union 
    2727except ImportError: 
    2828    pass 
     
    3737#] 
    3838 
    39 ER_ID = "radius_effective" 
    40 VF_ID = "volfraction" 
    41  
     39RADIUS_ID = "radius_effective" 
     40VOLFRAC_ID = "volfraction" 
    4241def make_extra_pars(p_info): 
    4342    pars = [] 
    4443    if p_info.have_Fq: 
    45         par = parse_parameter( 
    46                 "structure_factor_mode", 
    47                 "", 
    48                 0, 
    49                 [["P*S","P*(1+beta*(S-1))"]], 
    50                 "", 
    51                 "Structure factor calculation") 
     44        par = parse_parameter("structure_factor_mode", "", 0, [["P*S","P*(1+beta*(S-1))"]], "", 
     45                        "Structure factor calculation") 
     46        pars.append(par) 
     47    if p_info.effective_radius_type is not None: 
     48        par = parse_parameter("radius_effective_mode", "", 0, [["unconstrained"] + p_info.effective_radius_type], "", 
     49                        "Effective radius calculation") 
    5250        pars.append(par) 
    5351    return pars 
     
    6563    # have any magnetic parameters 
    6664    if not len(s_info.parameters.kernel_parameters) >= 2: 
    67         raise TypeError("S needs {} and {} as its first parameters".format(ER_ID, VF_ID)) 
    68     if not s_info.parameters.kernel_parameters[0].id == ER_ID: 
    69         raise TypeError("S needs {} as first parameter".format(ER_ID)) 
    70     if not s_info.parameters.kernel_parameters[1].id == VF_ID: 
    71         raise TypeError("S needs {} as second parameter".format(VF_ID)) 
     65        raise TypeError("S needs {} and {} as its first parameters".format(RADIUS_ID, VOLFRAC_ID)) 
     66    if not s_info.parameters.kernel_parameters[0].id == RADIUS_ID: 
     67        raise TypeError("S needs {} as first parameter".format(RADIUS_ID)) 
     68    if not s_info.parameters.kernel_parameters[1].id == VOLFRAC_ID: 
     69        raise TypeError("S needs {} as second parameter".format(VOLFRAC_ID)) 
    7270    if not s_info.parameters.magnetism_index == []: 
    7371        raise TypeError("S should not have SLD parameters") 
     
    7573    s_id, s_name, s_pars = s_info.id, s_info.name, s_info.parameters 
    7674 
    77     # Create list of parameters for the combined model.  Skip the first 
    78     # parameter of S, which we verified above is effective radius.  If there 
     75    # Create list of parameters for the combined model.  If there 
    7976    # are any names in P that overlap with those in S, modify the name in S 
    8077    # to distinguish it. 
    8178    p_set = set(p.id for p in p_pars.kernel_parameters) 
    8279    s_list = [(_tag_parameter(par) if par.id in p_set else par) 
    83               for par in s_pars.kernel_parameters[1:]] 
     80              for par in s_pars.kernel_parameters] 
    8481    # Check if still a collision after renaming.  This could happen if for 
    8582    # example S has volfrac and P has both volfrac and volfrac_S. 
     
    8784        raise TypeError("name collision: P has P.name and P.name_S while S has S.name") 
    8885 
     86    # make sure effective radius is not a polydisperse parameter in product 
     87    s_list[0] = copy(s_list[0]) 
     88    s_list[0].polydisperse = False 
     89 
    8990    translate_name = dict((old.id, new.id) for old, new 
    90                           in zip(s_pars.kernel_parameters[1:], s_list)) 
     91                          in zip(s_pars.kernel_parameters, s_list)) 
    9192    combined_pars = p_pars.kernel_parameters + s_list + make_extra_pars(p_info) 
    9293    parameters = ParameterTable(combined_pars) 
     
    9495    def random(): 
    9596        combined_pars = p_info.random() 
    96         s_names = set(par.id for par in s_pars.kernel_parameters[1:]) 
     97        s_names = set(par.id for par in s_pars.kernel_parameters) 
    9798        combined_pars.update((translate_name[k], v) 
    9899                             for k, v in s_info.random().items() 
     
    157158    return par 
    158159 
    159 def _intermediates(P, S): 
    160     # type: (np.ndarray, np.ndarray) -> OrderedDict[str, np.ndarray] 
     160def _intermediates(P, S, effective_radius): 
     161    # type: (np.ndarray, np.ndarray, float) -> OrderedDict[str, np.ndarray] 
    161162    """ 
    162163    Returns intermediate results for standard product (P(Q)*S(Q)) 
     
    165166        ("P(Q)", P), 
    166167        ("S(Q)", S), 
     168        ("effective_radius", effective_radius), 
    167169    )) 
    168170 
    169 def _intermediates_beta(F1, F2, S, scale, bg): 
    170     # type: (np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray) -> OrderedDict[str, np.ndarray] 
    171     """ 
    172     Returns intermediate results for beta approximation-enabled product 
     171def _intermediates_beta(F1, F2, S, scale, bg, effective_radius): 
     172    # type: (np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, float) -> OrderedDict[str, Union[np.ndarray, float]] 
     173    """ 
     174    Returns intermediate results for beta approximation-enabled product. The result may be an array or a float. 
    173175    """ 
    174176    # TODO: 1. include calculated Q vector 
     
    179181        ("beta(Q)", F1**2 / F2), 
    180182        ("S_eff(Q)", 1 + (F1**2 / F2)*(S-1)), 
     183        ("effective_radius", effective_radius), 
    181184        # ("I(Q)", scale*(F2 + (F1**2)*(S-1)) + bg), 
    182185    )) 
     
    262265        p_values = np.hstack(p_values).astype(self.p_kernel.dtype) 
    263266 
    264         # Call ER and VR for P since these are needed for S. 
    265         p_er, p_vr = calc_er_vr(p_info, p_details, p_values) 
    266         s_vr = (volfrac/p_vr if p_vr != 0. else volfrac) 
    267         #print("volfrac:%g p_er:%g p_vr:%g s_vr:%g"%(volfrac,p_er,p_vr,s_vr)) 
    268  
    269267        # Construct the calling parameters for S. 
    270         # The  effective radius is not in the combined parameter list, so 
    271         # the number of 'S' parameters is one less than expected.  The 
    272         # computed effective radius needs to be added into the weights 
    273         # vector, especially since it is a polydisperse parameter in the 
    274         # stand-alone structure factor models.  We will added it at the 
    275         # end so the remaining offsets don't need to change. 
    276         s_npars = s_info.parameters.npars-1 
     268        s_npars = s_info.parameters.npars 
    277269        s_length = call_details.length[p_npars:p_npars+s_npars] 
    278270        s_offset = call_details.offset[p_npars:p_npars+s_npars] 
     
    280272        s_offset = np.hstack((nweights, s_offset)) 
    281273        s_details = make_details(s_info, s_length, s_offset, nweights+1) 
    282         v, w = weights[:nweights], weights[nweights:] 
    283274        s_values = [ 
    284             # scale=1, background=0, radius_effective=p_er, volfraction=s_vr 
    285             [1., 0., p_er, s_vr], 
    286             # structure factor parameters start after scale, background and 
    287             # all the form factor parameters.  Skip the volfraction parameter 
    288             # as well, since it is computed elsewhere, and go to the end of the 
    289             # parameter list. 
    290             values[2+p_npars+1:2+p_npars+s_npars], 
    291             # no magnetism parameters to include for S 
    292             # add er into the (value, weights) pairs 
    293             v, [p_er], w, [1.0] 
     275            # scale=1, background=0, 
     276            [1., 0.], 
     277            values[2+p_npars:2+p_npars+s_npars], 
     278            weights, 
    294279        ] 
    295280        spacer = (32 - sum(len(v) for v in s_values)%32)%32 
     
    298283 
    299284        # beta mode is the first parameter after the structure factor pars 
    300         beta_index = 2+p_npars+s_npars 
    301         beta_mode = values[beta_index] 
     285        extra_offset = 2+p_npars+s_npars 
     286        if p_info.have_Fq: 
     287            beta_mode = values[extra_offset] 
     288            extra_offset += 1 
     289        else: 
     290            beta_mode = 0 
     291        if p_info.effective_radius_type is not None: 
     292            effective_radius_type = int(values[extra_offset]) 
     293            extra_offset += 1 
     294        else: 
     295            effective_radius_type = 0 
    302296 
    303297        # Call the kernels 
    304         s_result = self.s_kernel.Iq(s_details, s_values, cutoff, False) 
    305298        scale, background = values[0], values[1] 
    306299        if beta_mode: 
    307             F1, F2, volume_avg = self.p_kernel.beta(p_details, p_values, cutoff, magnetic) 
     300            F1, F2, volume_avg, effective_radius = self.p_kernel.beta( 
     301                p_details, p_values, cutoff, magnetic, effective_radius_type) 
     302            if effective_radius_type > 0: 
     303                # Plug R_eff from p into S model (initial value and pd value) 
     304                s_values[2] = s_values[2+s_npars+s_offset[0]] = effective_radius 
     305            s_result = self.s_kernel.Iq(s_details, s_values, cutoff, False) 
    308306            combined_scale = scale*volfrac/volume_avg 
    309307 
    310             self.results = lambda: _intermediates_beta(F1, F2, s_result, volfrac/volume_avg, background) 
     308            self.results = lambda: _intermediates_beta(F1, F2, s_result, volfrac/volume_avg, background, effective_radius) 
    311309            final_result = combined_scale*(F2 + (F1**2)*(s_result - 1)) + background 
    312310 
    313311        else: 
    314             p_result = self.p_kernel.Iq(p_details, p_values, cutoff, magnetic) 
    315  
    316             self.results = lambda: _intermediates(p_result, s_result) 
     312            p_result, effective_radius = self.p_kernel.Pq_Reff( 
     313                p_details, p_values, cutoff, magnetic, effective_radius_type) 
     314            if effective_radius_type > 0: 
     315                # Plug R_eff from p into S model (initial value and pd value) 
     316                s_values[2] = s_values[2+s_npars+s_offset[0]] = effective_radius 
     317            s_result = self.s_kernel.Iq(s_details, s_values, cutoff, False) 
     318            # remember the parts for plotting later 
     319            self.results = lambda: _intermediates(p_result, s_result, effective_radius) 
    317320            final_result = scale*(p_result*s_result) + background 
    318321 
Note: See TracChangeset for help on using the changeset viewer.