Changes in sasmodels/kernelpy.py [108e70e:5399809] in sasmodels


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/kernelpy.py

    r108e70e r5399809  
    1212 
    1313import numpy as np  # type: ignore 
     14from numpy import pi 
     15try: 
     16    from numpy import cbrt 
     17except ImportError: 
     18    def cbrt(x): return x ** (1.0/3.0) 
    1419 
    1520from .generate import F64 
     
    158163 
    159164        # Generate a closure which calls the form_volume if it exists. 
    160         form_volume = model_info.form_volume 
    161         self._volume = ((lambda: form_volume(*volume_args)) if form_volume else 
    162                         (lambda: 1.0)) 
    163  
    164     def __call__(self, call_details, values, cutoff, magnetic): 
     165        self._volume_args = volume_args 
     166        volume = model_info.form_volume 
     167        radius = model_info.effective_radius 
     168        self._volume = ((lambda: volume(*volume_args)) if volume 
     169                        else (lambda: 1.0)) 
     170        self._radius = ((lambda mode: radius(mode, *volume_args)) if radius 
     171                        else (lambda mode: cbrt(0.75/pi*volume(*volume_args))) if volume 
     172                        else (lambda mode: 1.0)) 
     173 
     174 
     175 
     176    def _call_kernel(self, call_details, values, cutoff, magnetic, effective_radius_type): 
    165177        # type: (CallDetails, np.ndarray, np.ndarray, float, bool) -> np.ndarray 
    166178        if magnetic: 
     
    168180        #print("Calling python kernel") 
    169181        #call_details.show(values) 
    170         res = _loops(self._parameter_vector, self._form, self._volume, 
    171                      self.q_input.nq, call_details, values, cutoff) 
    172         return res 
     182        radius = ((lambda: 0.0) if effective_radius_type == 0 
     183                  else (lambda: self._radius(effective_radius_type))) 
     184        self.result = _loops( 
     185            self._parameter_vector, self._form, self._volume, radius, 
     186            self.q_input.nq, call_details, values, cutoff) 
    173187 
    174188    def release(self): 
     
    183197           form,          # type: Callable[[], np.ndarray] 
    184198           form_volume,   # type: Callable[[], float] 
     199           form_radius,   # type: Callable[[], float] 
    185200           nq,            # type: int 
    186201           call_details,  # type: details.CallDetails 
    187202           values,        # type: np.ndarray 
    188            cutoff         # type: float 
     203           cutoff,        # type: float 
    189204          ): 
    190205    # type: (...) -> None 
     
    198213    #                                                              # 
    199214    ################################################################ 
     215 
     216    # WARNING: Trickery ahead 
     217    # The parameters[] vector is embedded in the closures for form(), 
     218    # form_volume() and form_radius().  We set the initial vector from 
     219    # the values for the model parameters. As we loop through the polydispesity 
     220    # mesh, we update the components with the polydispersity values before 
     221    # calling the respective functions. 
    200222    n_pars = len(parameters) 
    201223    parameters[:] = values[2:n_pars+2] 
     224 
    202225    if call_details.num_active == 0: 
    203         pd_norm = float(form_volume()) 
    204         scale = values[0]/(pd_norm if pd_norm != 0.0 else 1.0) 
    205         background = values[1] 
    206         return scale*form() + background 
    207  
    208     pd_value = values[2+n_pars:2+n_pars + call_details.num_weights] 
    209     pd_weight = values[2+n_pars + call_details.num_weights:] 
    210  
    211     pd_norm = 0.0 
    212     partial_weight = np.NaN 
    213     weight = np.NaN 
    214  
    215     p0_par = call_details.pd_par[0] 
    216     p0_length = call_details.pd_length[0] 
    217     p0_index = p0_length 
    218     p0_offset = call_details.pd_offset[0] 
    219  
    220     pd_par = call_details.pd_par[:call_details.num_active] 
    221     pd_offset = call_details.pd_offset[:call_details.num_active] 
    222     pd_stride = call_details.pd_stride[:call_details.num_active] 
    223     pd_length = call_details.pd_length[:call_details.num_active] 
    224  
    225     total = np.zeros(nq, 'd') 
    226     for loop_index in range(call_details.num_eval): 
    227         # update polydispersity parameter values 
    228         if p0_index == p0_length: 
    229             pd_index = (loop_index//pd_stride)%pd_length 
    230             parameters[pd_par] = pd_value[pd_offset+pd_index] 
    231             partial_weight = np.prod(pd_weight[pd_offset+pd_index][1:]) 
    232             p0_index = loop_index%p0_length 
    233  
    234         weight = partial_weight * pd_weight[p0_offset + p0_index] 
    235         parameters[p0_par] = pd_value[p0_offset + p0_index] 
    236         p0_index += 1 
    237         if weight > cutoff: 
    238             # Call the scattering function 
    239             # Assume that NaNs are only generated if the parameters are bad; 
    240             # exclude all q for that NaN.  Even better would be to have an 
    241             # INVALID expression like the C models, but that is too expensive. 
    242             Iq = np.asarray(form(), 'd') 
    243             if np.isnan(Iq).any(): 
    244                 continue 
    245  
    246             # update value and norm 
    247             total += weight * Iq 
    248             pd_norm += weight * form_volume() 
    249  
    250     scale = values[0]/(pd_norm if pd_norm != 0.0 else 1.0) 
    251     background = values[1] 
    252     return scale*total + background 
     226        total = form() 
     227        weight_norm = 1.0 
     228        weighted_volume = form_volume() 
     229        weighted_radius = form_radius() 
     230 
     231    else: 
     232        pd_value = values[2+n_pars:2+n_pars + call_details.num_weights] 
     233        pd_weight = values[2+n_pars + call_details.num_weights:] 
     234 
     235        weight_norm = 0.0 
     236        weighted_volume = 0.0 
     237        weighted_radius = 0.0 
     238        partial_weight = np.NaN 
     239        weight = np.NaN 
     240 
     241        p0_par = call_details.pd_par[0] 
     242        p0_length = call_details.pd_length[0] 
     243        p0_index = p0_length 
     244        p0_offset = call_details.pd_offset[0] 
     245 
     246        pd_par = call_details.pd_par[:call_details.num_active] 
     247        pd_offset = call_details.pd_offset[:call_details.num_active] 
     248        pd_stride = call_details.pd_stride[:call_details.num_active] 
     249        pd_length = call_details.pd_length[:call_details.num_active] 
     250 
     251        total = np.zeros(nq, 'd') 
     252        for loop_index in range(call_details.num_eval): 
     253            # update polydispersity parameter values 
     254            if p0_index == p0_length: 
     255                pd_index = (loop_index//pd_stride)%pd_length 
     256                parameters[pd_par] = pd_value[pd_offset+pd_index] 
     257                partial_weight = np.prod(pd_weight[pd_offset+pd_index][1:]) 
     258                p0_index = loop_index%p0_length 
     259 
     260            weight = partial_weight * pd_weight[p0_offset + p0_index] 
     261            parameters[p0_par] = pd_value[p0_offset + p0_index] 
     262            p0_index += 1 
     263            if weight > cutoff: 
     264                # Call the scattering function 
     265                # Assume that NaNs are only generated if the parameters are bad; 
     266                # exclude all q for that NaN.  Even better would be to have an 
     267                # INVALID expression like the C models, but that is expensive. 
     268                Iq = np.asarray(form(), 'd') 
     269                if np.isnan(Iq).any(): 
     270                    continue 
     271 
     272                # update value and norm 
     273                total += weight * Iq 
     274                weight_norm += weight 
     275                weighted_volume += weight * form_volume() 
     276                weighted_radius += weight * form_radius() 
     277 
     278    result = np.hstack((total, weight_norm, weighted_volume, weighted_radius)) 
     279    return result 
    253280 
    254281 
Note: See TracChangeset for help on using the changeset viewer.