Changeset 6d6508e in sasmodels for sasmodels/kernelpy.py


Ignore:
Timestamp:
Apr 7, 2016 4: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/kernelpy.py

    ra8a7f08 r6d6508e  
    1717    """ 
    1818    def __init__(self, model_info): 
     19        # Make sure Iq and Iqxy are available and vectorized 
     20        _create_default_functions(model_info) 
    1921        self.info = model_info 
    2022 
    2123    def make_kernel(self, q_vectors): 
    2224        q_input = PyInput(q_vectors, dtype=F64) 
    23         kernel = self.info['Iqxy'] if q_input.is_2d else self.info['Iq'] 
     25        kernel = self.info.Iqxy if q_input.is_2d else self.info.Iq 
    2426        return PyKernel(kernel, self.info, q_input) 
    2527 
     
    9496        self.dim = '2d' if q_input.is_2d else '1d' 
    9597 
    96         partable = model_info['parameters'] 
     98        partable = model_info.parameters 
    9799        kernel_parameters = (partable.iqxy_parameters if q_input.is_2d 
    98100                             else partable.iq_parameters) 
     
    129131        # parameter array. 
    130132        if q_input.is_2d: 
    131             form = model_info['Iqxy'] 
     133            form = model_info.Iqxy 
    132134            qx, qy = q_input.q[:,0], q_input.q[:,1] 
    133135            self._form = lambda: form(qx, qy, *kernel_args) 
    134136        else: 
    135             form = model_info['Iq'] 
     137            form = model_info.Iq 
    136138            q = q_input.q 
    137139            self._form = lambda: form(q, *kernel_args) 
    138140 
    139141        # Generate a closure which calls the form_volume if it exists. 
    140         form_volume = model_info['form_volume'] 
     142        form_volume = model_info.form_volume 
    141143        self._volume = ((lambda: form_volume(*volume_args)) if form_volume 
    142144                        else (lambda: 1.0)) 
    143145 
    144     def __call__(self, details, weights, values, cutoff): 
    145         # type: (.generate.CoordinationDetails, np.ndarray, np.ndarray, float) -> np.ndarray 
     146    def __call__(self, call_details, weights, values, cutoff): 
     147        # type: (.generate.CallDetails, np.ndarray, np.ndarray, float) -> np.ndarray 
    146148        res = _loops(self._parameter_vector, self._form, self._volume, 
    147                      self.q_input.nq, details, weights, values, cutoff) 
     149                     self.q_input.nq, call_details, weights, values, cutoff) 
    148150        return res 
    149151 
     
    158160           form_volume, # type: Callable[[], float] 
    159161           nq,          # type: int 
    160            details,     # type: .generate.CoordinationDetails 
     162           call_details,# type: .generate.CallDetails 
    161163           weights,     # type: np.ndarray 
    162164           values,      # type: np.ndarray 
     
    172174    #                                                              # 
    173175    ################################################################ 
    174     parameters[:] = values[details.par_offset] 
     176    parameters[:] = values[call_details.par_offset] 
    175177    scale, background = values[0], values[1] 
    176     if details.num_active == 0: 
     178    if call_details.num_active == 0: 
    177179        norm = float(form_volume()) 
    178180        if norm > 0.0: 
     
    183185    partial_weight = np.NaN 
    184186    spherical_correction = 1.0 
    185     pd_stride = details.pd_stride[:details.num_active] 
    186     pd_length = details.pd_length[:details.num_active] 
    187     pd_offset = details.pd_offset[:details.num_active] 
     187    pd_stride = call_details.pd_stride[:call_details.num_active] 
     188    pd_length = call_details.pd_length[:call_details.num_active] 
     189    pd_offset = call_details.pd_offset[:call_details.num_active] 
    188190    pd_index = np.empty_like(pd_offset) 
    189     offset = np.empty_like(details.par_offset) 
    190     theta = details.theta_par 
     191    offset = np.empty_like(call_details.par_offset) 
     192    theta = call_details.theta_par 
    191193    fast_length = pd_length[0] 
    192194    pd_index[0] = fast_length 
    193195    total = np.zeros(nq, 'd') 
    194196    norm = 0.0 
    195     for loop_index in range(details.total_pd): 
     197    for loop_index in range(call_details.total_pd): 
    196198        # update polydispersity parameter values 
    197199        if pd_index[0] == fast_length: 
    198200            pd_index[:] = (loop_index/pd_stride)%pd_length 
    199201            partial_weight = np.prod(weights[pd_offset+pd_index][1:]) 
    200             for k in range(details.num_coord): 
    201                 par = details.par_coord[k] 
    202                 coord = details.pd_coord[k] 
    203                 this_offset = details.par_offset[par] 
     202            for k in range(call_details.num_coord): 
     203                par = call_details.par_coord[k] 
     204                coord = call_details.pd_coord[k] 
     205                this_offset = call_details.par_offset[par] 
    204206                block_size = 1 
    205207                for bit in xrange(32): 
     
    211213                offset[par] = this_offset 
    212214                parameters[par] = values[this_offset] 
    213                 if par == theta and not (details.par_coord[k]&1): 
     215                if par == theta and not (call_details.par_coord[k]&1): 
    214216                    spherical_correction = max(abs(cos(pi/180 * parameters[theta])), 1e-6) 
    215         for k in range(details.num_coord): 
    216             if details.pd_coord[k]&1: 
    217                 #par = details.par_coord[k] 
     217        for k in range(call_details.num_coord): 
     218            if call_details.pd_coord[k]&1: 
     219                #par = call_details.par_coord[k] 
    218220                parameters[par] = values[offset[par]] 
    219221                #print "par",par,offset[par],parameters[par+2] 
     
    241243    else: 
    242244        return np.ones(nq, 'd')*background 
     245 
     246 
     247 
     248def _create_vector_Iq(model_info): 
     249    """ 
     250    Define Iq as a vector function if it exists. 
     251    """ 
     252    Iq = model_info.Iq 
     253    if callable(Iq) and not getattr(Iq, 'vectorized', False): 
     254        def vector_Iq(q, *args): 
     255            """ 
     256            Vectorized 1D kernel. 
     257            """ 
     258            return np.array([Iq(qi, *args) for qi in q]) 
     259        vector_Iq.vectorized = True 
     260        model_info.Iq = vector_Iq 
     261 
     262def _create_vector_Iqxy(model_info): 
     263    """ 
     264    Define Iqxy as a vector function if it exists, or default it from Iq(). 
     265    """ 
     266    Iq, Iqxy = model_info.Iq, model_info.Iqxy 
     267    if callable(Iqxy) and not getattr(Iqxy, 'vectorized', False): 
     268        def vector_Iqxy(qx, qy, *args): 
     269            """ 
     270            Vectorized 2D kernel. 
     271            """ 
     272            return np.array([Iqxy(qxi, qyi, *args) for qxi, qyi in zip(qx, qy)]) 
     273        vector_Iqxy.vectorized = True 
     274        model_info.Iqxy = vector_Iqxy 
     275    elif callable(Iq): 
     276        # Iq is vectorized because create_vector_Iq was already called. 
     277        def default_Iqxy(qx, qy, *args): 
     278            """ 
     279            Default 2D kernel. 
     280            """ 
     281            return Iq(np.sqrt(qx**2 + qy**2), *args) 
     282        default_Iqxy.vectorized = True 
     283        model_info.Iqxy = default_Iqxy 
     284 
     285def _create_default_functions(model_info): 
     286    """ 
     287    Autogenerate missing functions, such as Iqxy from Iq. 
     288 
     289    This only works for Iqxy when Iq is written in python. :func:`make_source` 
     290    performs a similar role for Iq written in C.  This also vectorizes 
     291    any functions that are not already marked as vectorized. 
     292    """ 
     293    _create_vector_Iq(model_info) 
     294    _create_vector_Iqxy(model_info)  # call create_vector_Iq() first 
     295 
Note: See TracChangeset for help on using the changeset viewer.