Changeset eeb772e in sasmodels


Ignore:
Timestamp:
Apr 1, 2019 10:54:03 AM (5 years ago)
Author:
GitHub <noreply@…>
Parents:
3448301 (diff), 7eb2a4d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
git-author:
Paul Kienzle <pkienzle@…> (04/01/19 10:54:03)
git-committer:
GitHub <noreply@…> (04/01/19 10:54:03)
Message:

Merge 7eb2a4d86ee338d6e6039653d7b0020d13b0573b into 34483013ffb02edbec666039a8c85e18f605b690

Files:
6 added
17 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/mixture.py

    rb297ba9 rb2f0e5d  
    117117            combined_pars.append(p) 
    118118    parameters = ParameterTable(combined_pars) 
     119    # Allow for the scenario in which each component has all its PD parameters 
     120    # active simultaneously.  details.make_details() will throw an error if 
     121    # too many are used from any one component. 
    119122    parameters.max_pd = sum(part.parameters.max_pd for part in parts) 
    120123 
  • sasmodels/modelinfo.py

    ra34b811 r7eb2a4d  
    7070        processed.append(parse_parameter(*p)) 
    7171    partable = ParameterTable(processed) 
    72     partable.check_angles() 
     72    partable.check_angles(strict=True) 
    7373    return partable 
    7474 
     
    446446        # properties, such as default=0.0 for structure factor backgrounds. 
    447447        self.common_parameters = [Parameter(*p) for p in COMMON_PARAMETERS] 
    448  
    449448        self.kernel_parameters = parameters 
    450449        self._set_vector_lengths() 
     
    495494        self.pd_2d = set(p.name for p in self.call_parameters if p.polydisperse) 
    496495 
     496        # Final checks 
     497        self.check_duplicates() 
     498        self.check_angles() 
     499 
    497500    def set_zero_background(self): 
    498501        """ 
     
    506509        self.defaults = self._get_defaults() 
    507510 
    508     def check_angles(self): 
     511    def check_angles(self, strict=False): 
    509512        """ 
    510513        Check that orientation angles are theta, phi and possibly psi. 
     514 
     515        *strict* should be True when checking a parameter table defined 
     516        in a model file, but False when checking from mixture models, etc., 
     517        where the parameters aren't being passed to a calculator directly. 
    511518        """ 
    512519        theta = phi = psi = -1 
     
    524531                if p.type != 'orientation': 
    525532                    raise TypeError("psi must be an orientation parameter") 
    526             elif p.type == 'orientation': 
     533            elif strict and p.type == 'orientation': 
    527534                raise TypeError("only theta, phi and psi can be orientation parameters") 
    528535        if theta >= 0 and phi >= 0: 
     
    532539            if psi >= 0 and psi != phi+1: 
    533540                raise TypeError("psi must follow phi") 
    534             if (psi >= 0 and psi != last_par) or (psi < 0 and phi != last_par): 
     541            # TODO: Why must theta/phi/psi be at the end?  Consistency only? 
     542            if strict and phi != last_par and psi != last_par: 
    535543                raise TypeError("orientation parameters must appear at the " 
    536544                                "end of the parameter table") 
    537545        elif theta >= 0 or phi >= 0 or psi >= 0: 
    538546            raise TypeError("oriented shapes must have both theta and phi and maybe psi") 
     547 
     548    def check_duplicates(self): 
     549        """ 
     550        Check for duplicate parameter names 
     551        """ 
     552        checked, dups = set(), set() 
     553        for p in self.call_parameters: 
     554            if p.id in checked: 
     555                dups.add(p.id) 
     556            else: 
     557                checked.add(p.id) 
     558        if dups: 
     559            raise TypeError("Duplicate parameters: {}" 
     560                            .format(", ".join(sorted(dups)))) 
    539561 
    540562    def __getitem__(self, key): 
  • sasmodels/product.py

    r065d77d r93cac17  
    1010To use it, first load form factor P and structure factor S, then create 
    1111*make_product_info(P, S)*. 
     12 
     13The P@S models is somewhat complicated because there are many special 
     14parameters that need to be handled in particular ways.  Much of the 
     15code is used to figure out what special parameters we have, where to 
     16find them in the P@S model inputs and how to distribute them to the underlying 
     17P and S model calculators. 
     18 
     19The parameter packet received by the P@S is a :class:`details.CallDetails` 
     20structure along with a data vector. The CallDetails structure indicates which 
     21parameters are polydisperse, the length of the distribution, and where to 
     22find it in the data vector.  The distributions are ordered from longest to 
     23shortest, with length 1 distributions filling out the distribution set.  That 
     24way the kernel calculator doesn't have to check if it needs another nesting 
     25level since it is always there.  The data vector consists of a list of target 
     26values for the parameters, followed by a concatenation of the distribution 
     27values, and then followed by a concatenation of the distribution weights. 
     28Given the combined details and data for P@S, we must decompose them in to 
     29details for P and details for S separately, which unfortunately requires 
     30intimate knowledge of the data structures and tricky code. 
     31 
     32The special parameters are: 
     33 
     34* *scale* and *background*: 
     35    First two parameters of the value list in each of P, S and P@S. 
     36    When decomposing P@S parameters, ignore *scale* and *background*, 
     37    instead using 1 and 0 for the first two slots of both P and S. 
     38    After calling P and S individually, the results are combined as 
     39    :code:`volfraction*scale*P*S + background`.  The *scale* and 
     40    *background* do not show up in the polydispersity structure so 
     41    they are easy to handle. 
     42 
     43* *volfraction*: 
     44    Always the first parameter of S, but it may also be in P. If it is in P, 
     45    then *P.volfraction* is used in the combined P@S list, and 
     46    *S.volfraction* is elided, otherwise *S.volfraction* is used. If we 
     47    are using *volfraction* from P we can treat it like all the other P 
     48    parameters when calling P, but when calling S we need to insert the 
     49    *P.volfraction* into data vector for S and assign a slot of length 1 
     50    in the distribution. Because we are using the original layout of the 
     51    distribution vectors from P@S, but copying it into private data 
     52    vectors for S and P, we are free to "borrow" a P slots to store the 
     53    missing *S.volfraction* distribution.  We use the *P.volfraction* 
     54    slot itself but any slot will work. 
     55 
     56    For hollow shapes, *volfraction* represents the volume fraction of 
     57    material but S needs the volume fraction enclosed by the shape. The 
     58    answer is to scale the user specified volume fraction by the form:shell 
     59    ratio computed from the average form volume and average shell volume 
     60    returned from P. Use the original *volfraction* divided by *shell_volume* 
     61    to compute the number density, and scale P@S by that to get absolute 
     62    scaling on the final *I(q)*. The *scale* for P@S should therefore usually 
     63    be one. 
     64 
     65* *radius_effective*: 
     66    Always the second parameter of S and always part of P@S, but never in P. 
     67    The value may be calculated using *P.radius_effective()* or it 
     68    may be set to the *radius_effective* value in P@S, depending on 
     69    *radius_effective_mode*.  If part of S, the value may be polydisperse. 
     70    If calculated by P, then it will be the weighted average of effective 
     71    radii computed for the polydisperse shape parameters. 
     72 
     73* *structure_factor_mode* 
     74    If P@S supports beta approximation (i.e., if it has the *Fq* function that 
     75    returns <FF*> and <F><F*>), then *structure_factor_mode* will be added 
     76    to the P@S parameters right after the S parameters.  This mode may be 0 
     77    for the monodisperse approximation or 1 for the beta approximation.  We 
     78    will add more values here as we implemented more complicated operations, 
     79    but for now P and S must be computed separately.  If beta, then we 
     80    return *I = scale volfrac/volume ( <FF> + <F>^2 (S-1)) + background*. 
     81    If not beta then return *I = scale/volume P S + background* .  In both 
     82    cases, return the appropriate immediate values. 
     83 
     84* *radius_effective_mode* 
     85    If P defines the *radius_effective* function (and therefore 
     86    *P.info.radius_effective_modes* is a list of effective radius modes), 
     87    then *radius_effective_mode* will be the final parameter in P@S.  Mode 
     88    will be zero if *radius_effective* is defined by the user using the S 
     89    parameter; any other value and the *radius_effective* parameter will be 
     90    filled in from the value computed in P.  In the latter case, the 
     91    polydispersity information for *S.radius_effective* will need to be 
     92    suppressed, with pd length set to 1, the first value set to the 
     93    effective radius and the first weight set to 1.  Do this after composing 
     94    the S data vector so the inputs are left untouched. 
     95 
     96* *regular parameters* 
     97    The regular P parameters form a block of length *P.info.npars* at the 
     98    start of the data vector (after scale and background).  These will be 
     99    followed by *S.effective_radius*, and *S.volfraction* (if *P.volfraction* 
     100    is absent), and then the regular S parameters.  The P and S blocks can 
     101    be copied as a group into the respective P and S data vectors. 
     102    We can copy the distribution value and weight vectors untouched to both 
     103    the P and S data vectors since they are referenced by offset and length. 
     104    We can update the radius_effective slots in the P data vector with 
     105    *P.radius_effective()* if needed. 
     106 
     107* *magnetic parameters* 
     108    For each P parameter that is an SLD there will be a set of three magnetic 
     109    parameters tacked on to P@S after the regular P and S and after the 
     110    special *structure_factor_mode* and *radius_effective_mode*.  These 
     111    can be copied as a group after the regular P parameters.  There won't 
     112    be any magnetic S parameters. 
     113 
    12114""" 
    13115from __future__ import print_function, division 
     
    84186    if not s_info.parameters.magnetism_index == []: 
    85187        raise TypeError("S should not have SLD parameters") 
     188    if RADIUS_ID in p_info.parameters: 
     189        raise TypeError("P should not have {}".format(RADIUS_ID)) 
    86190    p_id, p_name, p_pars = p_info.id, p_info.name, p_info.parameters 
    87191    s_id, s_name, s_pars = s_info.id, s_info.name, s_info.parameters 
    88  
    89     # Create list of parameters for the combined model.  If there 
    90     # are any names in P that overlap with those in S, modify the name in S 
    91     # to distinguish it. 
     192    p_has_volfrac = VOLFRAC_ID in p_info.parameters 
     193 
     194    # Create list of parameters for the combined model.  If a name in 
     195    # S overlaps a name in P, tag the S parameter name to distinguish it. 
     196    # If the tagged name also collides it will be caught by the parameter 
     197    # table builder.  Similarly if any special names are abused.  Need the 
     198    # pairs to create the translation table for random model generation. 
    92199    p_set = set(p.id for p in p_pars.kernel_parameters) 
    93     s_list = [(_tag_parameter(par) if par.id in p_set else par) 
    94               for par in s_pars.kernel_parameters] 
    95     # Check if still a collision after renaming.  This could happen if for 
    96     # example S has volfrac and P has both volfrac and volfrac_S. 
    97     if any(p.id in p_set for p in s_list): 
    98         raise TypeError("name collision: P has P.name and P.name_S while S has S.name") 
    99  
    100     # make sure effective radius is not a polydisperse parameter in product 
    101     s_list[0] = copy(s_list[0]) 
    102     s_list[0].polydisperse = False 
    103  
    104     translate_name = dict((old.id, new.id) for old, new 
    105                           in zip(s_pars.kernel_parameters, s_list)) 
     200    s_pairs = [(par, (_tag_parameter(par) if par.id in p_set else par)) 
     201               for par in s_pars.kernel_parameters 
     202               # Note: exclude volfraction from s_list if volfraction in p 
     203               if par.id != VOLFRAC_ID or not p_has_volfrac] 
     204    s_list = [pair[0] for pair in s_pairs] 
     205 
     206    # Build combined parameter table 
    106207    combined_pars = p_pars.kernel_parameters + s_list + make_extra_pars(p_info) 
    107208    parameters = ParameterTable(combined_pars) 
    108     parameters.max_pd = p_pars.max_pd + s_pars.max_pd 
     209    # Allow for the scenario in which each component has all its PD parameters 
     210    # active simultaneously.  details.make_details() will throw an error if 
     211    # too many are used from any one component. 
     212    parameters.Pumax_pd = p_pars.max_pd + s_pars.max_pd 
     213 
     214    # TODO: does user-defined polydisperse S.radius_effective make sense? 
     215    # make sure effective radius is not a polydisperse parameter in product 
     216    #s_list[0] = copy(s_list[0]) 
     217    #s_list[0].polydisperse = False 
     218 
     219    s_translate = {old.id: new.id for old, new in s_pairs} 
    109220    def random(): 
    110221        """Random set of model parameters for product model""" 
    111222        combined_pars = p_info.random() 
    112         s_names = set(par.id for par in s_pars.kernel_parameters) 
    113         combined_pars.update((translate_name[k], v) 
     223        combined_pars.update((s_translate[k], v) 
    114224                             for k, v in s_info.random().items() 
    115                              if k in s_names) 
     225                             if k in s_translate) 
    116226        return combined_pars 
    117227 
     
    173283 
    174284def _intermediates( 
    175         F1,               # type: np.ndarray 
    176         F2,               # type: np.ndarray 
     285        F,                # type: np.ndarray 
     286        Fsq,              # type: np.ndarray 
    177287        S,                # type: np.ndarray 
    178288        scale,            # type: float 
     
    190300        # TODO: 2. consider implications if there are intermediate results in P(Q) 
    191301        parts = OrderedDict(( 
    192             ("P(Q)", scale*F2), 
     302            ("P(Q)", scale*Fsq), 
    193303            ("S(Q)", S), 
    194             ("beta(Q)", F1**2 / F2), 
    195             ("S_eff(Q)", 1 + (F1**2 / F2)*(S-1)), 
     304            ("beta(Q)", F**2 / Fsq), 
     305            ("S_eff(Q)", 1 + (F**2 / Fsq)*(S-1)), 
    196306            ("effective_radius", radius_effective), 
    197307            ("radius_effective", radius_effective), 
    198             # ("I(Q)", scale*(F2 + (F1**2)*(S-1)) + bg), 
     308            # ("I(Q)", scale*(Fsq + (F**2)*(S-1)) + bg), 
    199309        )) 
    200310    else: 
    201311        parts = OrderedDict(( 
    202             ("P(Q)", scale*F2), 
     312            ("P(Q)", scale*Fsq), 
    203313            ("S(Q)", S), 
    204314            ("effective_radius", radius_effective), 
     
    264374        self.results = []  # type: List[np.ndarray] 
    265375 
     376        # Find index of volfraction parameter in parameter list 
     377        for k, p in enumerate(model_info.parameters.call_parameters): 
     378            if p.id == VOLFRAC_ID: 
     379                self._volfrac_index = k 
     380                break 
     381        else: 
     382            raise RuntimeError("no %s parameter in %s"%(VOLFRAC_ID, self)) 
     383 
     384        p_info, s_info = self.info.composition[1] 
     385        p_npars = p_info.parameters.npars 
     386        s_npars = s_info.parameters.npars 
     387 
     388        have_beta_mode = p_info.have_Fq 
     389        have_er_mode = p_info.radius_effective_modes is not None 
     390        volfrac_in_p = self._volfrac_index < p_npars + 2  # scale & background 
     391 
     392        # Slices into the details length/offset structure for P@S. 
     393        # Made complicated by the possibly missing volfraction in S. 
     394        self._p_detail_slice = slice(0, p_npars) 
     395        self._s_detail_slice = slice(p_npars, p_npars+s_npars-volfrac_in_p) 
     396        self._volfrac_in_p = volfrac_in_p 
     397 
     398        # P block from data vector, without scale and background 
     399        first_p = 2 
     400        last_p = p_npars + 2 
     401        self._p_value_slice = slice(first_p, last_p) 
     402 
     403        # radius_effective is the first parameter in S from the data vector. 
     404        self._er_index = last_p 
     405 
     406        # S block from data vector, without scale, background, volfrac or er. 
     407        first_s = last_p + 2 - volfrac_in_p 
     408        last_s = first_s + s_npars - 2 
     409        self._s_value_slice = slice(first_s, last_s) 
     410 
     411        # S distribution block in S data vector starts after all S values 
     412        self._s_dist_slice = slice(2 + s_npars, None) 
     413 
     414        # structure_factor_mode is the first parameter after P and S.  Skip 
     415        # 2 for scale and background, and subtract 1 in case there is no 
     416        # volfraction in S. 
     417        self._beta_mode_index = last_s if have_beta_mode else 0 
     418 
     419        # radius_effective_mode is the second parameter after P and S 
     420        # unless structure_factor_mode isn't available, in which case it 
     421        # is first. 
     422        self._er_mode_index = last_s + have_beta_mode if have_er_mode else 0 
     423 
     424        # Magnetic parameters are after everything else.  If they exist, 
     425        # they will only be for form factor P, not structure factor S. 
     426        first_mag = last_s + have_beta_mode + have_er_mode 
     427        mag_pars = 3*p_info.parameters.nmagnetic 
     428        last_mag = first_mag + (mag_pars + 3 if mag_pars else 0) 
     429        self._magentic_slice = slice(first_mag, last_mag) 
     430 
    266431    def Iq(self, call_details, values, cutoff, magnetic): 
    267432        # type: (CallDetails, np.ndarray, float, bool) -> np.ndarray 
    268  
    269433        p_info, s_info = self.info.composition[1] 
    270         p_npars = p_info.parameters.npars 
    271         p_length = call_details.length[:p_npars] 
    272         p_offset = call_details.offset[:p_npars] 
    273         s_npars = s_info.parameters.npars 
    274         s_length = call_details.length[p_npars:p_npars+s_npars] 
    275         s_offset = call_details.offset[p_npars:p_npars+s_npars] 
    276  
    277         # Beta mode parameter is the first parameter after P and S parameters 
    278         have_beta_mode = p_info.have_Fq 
    279         beta_mode_offset = 2+p_npars+s_npars 
    280         beta_mode = (values[beta_mode_offset] > 0) if have_beta_mode else False 
    281         if beta_mode and self.p_kernel.dim == '2d': 
    282             raise NotImplementedError("beta not yet supported for 2D") 
    283  
    284         # R_eff type parameter is the second parameter after P and S parameters 
    285         # unless the model doesn't support beta mode, in which case it is first 
    286         have_radius_type = p_info.radius_effective_modes is not None 
    287         #print(p_npars,s_npars) 
    288         radius_type_offset = 2+p_npars+s_npars + (1 if have_beta_mode else 0) 
    289         #print(values[radius_type_offset]) 
    290         radius_type = int(values[radius_type_offset]) if have_radius_type else 0 
    291  
    292         # Retrieve the volume fraction, which is the second of the 
    293         # 'S' parameters in the parameter list, or 2+np in 0-origin, 
    294         # as well as the scale and background. 
    295         volfrac = values[3+p_npars] 
     434 
     435        # Retrieve values from the data vector 
    296436        scale, background = values[0], values[1] 
    297  
    298         # if there are magnetic parameters, they will only be on the 
    299         # form factor P, not the structure factor S. 
    300         nmagnetic = len(self.info.parameters.magnetism_index) 
    301         if nmagnetic: 
    302             spin_index = self.info.parameters.npars + 2 
    303             magnetism = values[spin_index: spin_index+3+3*nmagnetic] 
    304         else: 
    305             magnetism = [] 
     437        volfrac = values[self._volfrac_index] 
     438        er_mode = (int(values[self._er_mode_index]) 
     439                   if self._er_mode_index > 0 else 0) 
     440        beta_mode = (values[self._beta_mode_index] > 0 
     441                     if self._beta_mode_index > 0 else False) 
     442 
    306443        nvalues = self.info.parameters.nvalues 
    307444        nweights = call_details.num_weights 
    308445        weights = values[nvalues:nvalues + 2*nweights] 
    309446 
     447        # Can't do 2d and beta_mode just yet 
     448        if beta_mode and self.p_kernel.dim == '2d': 
     449            raise NotImplementedError("beta not yet supported for 2D") 
     450 
    310451        # Construct the calling parameters for P. 
     452        p_length = call_details.length[self._p_detail_slice] 
     453        p_offset = call_details.offset[self._p_detail_slice] 
    311454        p_details = make_details(p_info, p_length, p_offset, nweights) 
    312455        p_values = [ 
    313456            [1., 0.], # scale=1, background=0, 
    314             values[2:2+p_npars], 
    315             magnetism, 
     457            values[self._p_value_slice], 
     458            values[self._magentic_slice], 
    316459            weights] 
    317460        spacer = (32 - sum(len(v) for v in p_values)%32)%32 
     
    319462        p_values = np.hstack(p_values).astype(self.p_kernel.dtype) 
    320463 
     464        # Call the form factor kernel to compute <F> and <F^2>. 
     465        # If the model doesn't support Fq the returned <F> will be None. 
     466        F, Fsq, radius_effective, shell_volume, volume_ratio \ 
     467            = self.p_kernel.Fq(p_details, p_values, cutoff, magnetic, er_mode) 
     468 
     469        # TODO: async call to the GPU 
     470 
    321471        # Construct the calling parameters for S. 
    322         if radius_type > 0: 
    323             # If R_eff comes from form factor, make sure it is monodisperse. 
    324             # weight is set to 1 later, after the value array is created 
     472        s_length = call_details.length[self._s_detail_slice] 
     473        s_offset = call_details.offset[self._s_detail_slice] 
     474        if self._volfrac_in_p: 
     475            # Volfrac is in P and missing from S so insert a slot for it.  Say 
     476            # the distribution is length 1 and use the slot for volfraction 
     477            # from the P distribution. 
     478            s_length = np.insert(s_length, 1, 1) 
     479            s_offset = np.insert(s_offset, 1, p_offset[self._volfrac_index - 2]) 
     480        if er_mode > 0: 
     481            # If effective_radius comes from P, make sure it is monodisperse. 
     482            # Weight is set to 1 later, after the value array is created 
    325483            s_length[0] = 1 
    326484        s_details = make_details(s_info, s_length, s_offset, nweights) 
    327485        s_values = [ 
    328             [1., 0.], # scale=1, background=0, 
    329             values[2+p_npars:2+p_npars+s_npars], 
     486            [1., # scale=1 
     487             0., # background=0, 
     488             values[self._er_index], # S.radius_effective; may be replaced by P 
     489             0.], # volfraction; will be replaced by volfrac * volume_ratio 
     490            # followed by S parameters after effective_radius and volfraction 
     491            values[self._s_value_slice], 
    330492            weights, 
    331493        ] 
     
    334496        s_values = np.hstack(s_values).astype(self.s_kernel.dtype) 
    335497 
    336         # Call the form factor kernel to compute <F> and <F^2>. 
    337         # If the model doesn't support Fq the returned <F> will be None. 
    338         F1, F2, radius_effective, shell_volume, volume_ratio = self.p_kernel.Fq( 
    339             p_details, p_values, cutoff, magnetic, radius_type) 
    340  
    341         # Call the structure factor kernel to compute S. 
    342498        # Plug R_eff from the form factor into structure factor parameters 
    343499        # and scale volume fraction by form:shell volume ratio. These changes 
     
    347503        #print("R_eff=%d:%g, volfrac=%g, volume ratio=%g" 
    348504        #      % (radius_type, radius_effective, volfrac, volume_ratio)) 
    349         if radius_type > 0: 
     505        s_dist = s_values[self._s_dist_slice] 
     506        if er_mode > 0: 
    350507            # set the value to the model R_eff and set the weight to 1 
    351             s_values[2] = s_values[2+s_npars+s_offset[0]] = radius_effective 
    352             s_values[2+s_npars+s_offset[0]+nweights] = 1.0 
    353         s_values[3] = s_values[2+s_npars+s_offset[1]] = volfrac*volume_ratio 
     508            s_values[2] = s_dist[s_offset[0]] = radius_effective 
     509            s_dist[s_offset[0]+nweights] = 1.0 
     510        s_values[3] = s_dist[s_offset[1]] = volfrac*volume_ratio 
     511        s_dist[s_offset[1]+nweights] = 1.0 
     512 
     513        # Call the structure factor kernel to compute S. 
    354514        S = self.s_kernel.Iq(s_details, s_values, cutoff, False) 
     515        #print("P", Fsq[:10]) 
     516        #print("S", S[:10]) 
     517        #print(radius_effective, volfrac*volume_ratio) 
     518 
     519        # Combine form factor and structure factor 
     520        #print("beta", beta_mode, F, Fsq, S) 
     521        PS = Fsq + F**2*(S-1) if beta_mode else Fsq*S 
    355522 
    356523        # Determine overall scale factor. Hollow shapes are weighted by 
    357         # shell_volume, so that is needed for volume normalization.  For 
    358         # solid shapes we can use shell_volume as well since it is equal 
    359         # to form volume. 
    360         combined_scale = scale*volfrac/shell_volume 
    361  
    362         # Combine form factor and structure factor 
    363         #print("beta", beta_mode, F1, F2, S) 
    364         PS = F2 + F1**2*(S-1) if beta_mode else F2*S 
    365         final_result = combined_scale*PS + background 
     524        # shell_volume, so that is needed for number density estimation. 
     525        # For solid shapes we can use shell_volume as well since it is 
     526        # equal to form volume.  If P already has a volfraction parameter, 
     527        # then assume that it is already on absolute scale, and don't 
     528        # include volfrac in the combined_scale. 
     529        combined_scale = scale*(volfrac if not self._volfrac_in_p else 1.0) 
     530        final_result = combined_scale/shell_volume*PS + background 
    366531 
    367532        # Capture intermediate values so user can see them.  These are 
     
    375540        # the results directly rather than through a lazy evaluator. 
    376541        self.results = lambda: _intermediates( 
    377             F1, F2, S, combined_scale, radius_effective, beta_mode) 
     542            F, Fsq, S, combined_scale, radius_effective, beta_mode) 
    378543 
    379544        return final_result 
  • doc/guide/index.rst

    rda5536f rbc69321  
    1212   pd/polydispersity.rst 
    1313   resolution.rst 
     14   plugin.rst 
     15   fitting_sq.rst 
    1416   magnetism/magnetism.rst 
    1517   orientation/orientation.rst 
    1618   sesans/sans_to_sesans.rst 
    1719   sesans/sesans_fitting.rst 
    18    plugin.rst 
    1920   scripting.rst 
    2021   refs.rst 
  • doc/guide/pd/polydispersity.rst

    rd089a00 ra5cb9bc  
    1111-------------------------------------------- 
    1212 
    13 For some models we can calculate the average intensity for a population of  
    14 particles that possess size and/or orientational (ie, angular) distributions.  
    15 In SasView we call the former *polydispersity* but use the parameter *PD* to  
    16 parameterise both. In other words, the meaning of *PD* in a model depends on  
     13For some models we can calculate the average intensity for a population of 
     14particles that possess size and/or orientational (ie, angular) distributions. 
     15In SasView we call the former *polydispersity* but use the parameter *PD* to 
     16parameterise both. In other words, the meaning of *PD* in a model depends on 
    1717the actual parameter it is being applied too. 
    1818 
    19 The resultant intensity is then normalized by the average particle volume such  
     19The resultant intensity is then normalized by the average particle volume such 
    2020that 
    2121 
     
    2424  P(q) = \text{scale} \langle F^* F \rangle / V + \text{background} 
    2525 
    26 where $F$ is the scattering amplitude and $\langle\cdot\rangle$ denotes an  
     26where $F$ is the scattering amplitude and $\langle\cdot\rangle$ denotes an 
    2727average over the distribution $f(x; \bar x, \sigma)$, giving 
    2828 
    2929.. math:: 
    3030 
    31   P(q) = \frac{\text{scale}}{V} \int_\mathbb{R}  
     31  P(q) = \frac{\text{scale}}{V} \int_\mathbb{R} 
    3232  f(x; \bar x, \sigma) F^2(q, x)\, dx + \text{background} 
    3333 
    3434Each distribution is characterized by a center value $\bar x$ or 
    3535$x_\text{med}$, a width parameter $\sigma$ (note this is *not necessarily* 
    36 the standard deviation, so read the description of the distribution carefully),  
    37 the number of sigmas $N_\sigma$ to include from the tails of the distribution,  
    38 and the number of points used to compute the average. The center of the  
    39 distribution is set by the value of the model parameter. 
    40  
    41 The distribution width applied to *volume* (ie, shape-describing) parameters  
    42 is relative to the center value such that $\sigma = \mathrm{PD} \cdot \bar x$.  
    43 However, the distribution width applied to *orientation* parameters is just  
    44 $\sigma = \mathrm{PD}$. 
     36the standard deviation, so read the description carefully), the number of 
     37sigmas $N_\sigma$ to include from the tails of the distribution, and the 
     38number of points used to compute the average. The center of the distribution 
     39is set by the value of the model parameter. The meaning of a polydispersity 
     40parameter *PD* (not to be confused with a molecular weight distributions 
     41in polymer science) in a model depends on the type of parameter it is being 
     42applied too. 
     43 
     44The distribution width applied to *volume* (ie, shape-describing) parameters 
     45is relative to the center value such that $\sigma = \mathrm{PD} \cdot \bar x$. 
     46However, the distribution width applied to *orientation* (ie, angle-describing) 
     47parameters is just $\sigma = \mathrm{PD}$. 
    4548 
    4649$N_\sigma$ determines how far into the tails to evaluate the distribution, 
     
    5255 
    5356Users should note that the averaging computation is very intensive. Applying 
    54 polydispersion and/or orientational distributions to multiple parameters at  
    55 the same time, or increasing the number of points in the distribution, will  
    56 require patience! However, the calculations are generally more robust with  
     57polydispersion and/or orientational distributions to multiple parameters at 
     58the same time, or increasing the number of points in the distribution, will 
     59require patience! However, the calculations are generally more robust with 
    5760more data points or more angles. 
    5861 
     
    6669*  *Schulz Distribution* 
    6770*  *Array Distribution* 
     71*  *User-defined Distributions* 
    6872 
    6973These are all implemented as *number-average* distributions. 
    7074 
    71 Additional distributions are under consideration. 
    7275 
    7376**Beware: when the Polydispersity & Orientational Distribution panel in SasView is** 
     
    7578**This may not be suitable. See Suggested Applications below.** 
    7679 
    77 .. note:: In 2009 IUPAC decided to introduce the new term 'dispersity' to replace  
    78            the term 'polydispersity' (see `Pure Appl. Chem., (2009), 81(2),  
    79            351-353 <http://media.iupac.org/publications/pac/2009/pdf/8102x0351.pdf>`_  
    80            in order to make the terminology describing distributions of chemical  
    81            properties unambiguous. However, these terms are unrelated to the  
    82            proportional size distributions and orientational distributions used in  
     80.. note:: In 2009 IUPAC decided to introduce the new term 'dispersity' to replace 
     81           the term 'polydispersity' (see `Pure Appl. Chem., (2009), 81(2), 
     82           351-353 <http://media.iupac.org/publications/pac/2009/pdf/8102x0351.pdf>`_ 
     83           in order to make the terminology describing distributions of chemical 
     84           properties unambiguous. However, these terms are unrelated to the 
     85           proportional size distributions and orientational distributions used in 
    8386           SasView models. 
    8487 
     
    9295or angular orientations, consider using the Gaussian or Boltzmann distributions. 
    9396 
    94 If applying polydispersion to parameters describing angles, use the Uniform  
    95 distribution. Beware of using distributions that are always positive (eg, the  
     97If applying polydispersion to parameters describing angles, use the Uniform 
     98distribution. Beware of using distributions that are always positive (eg, the 
    9699Lognormal) because angles can be negative! 
    97100 
    98 The array distribution allows a user-defined distribution to be applied. 
     101The array distribution provides a very simple means of implementing a user- 
     102defined distribution, but without any fittable parameters. Greater flexibility 
     103is conferred by the user-defined distribution. 
    99104 
    100105.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
     
    334339.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
    335340 
     341User-defined Distributions 
     342^^^^^^^^^^^^^^^^^^^^^^^^^^ 
     343 
     344You can also define your own distribution by creating a python file defining a 
     345*Distribution* object with a *_weights* method.  The *_weights* method takes 
     346*center*, *sigma*, *lb* and *ub* as arguments, and can access *self.npts* 
     347and *self.nsigmas* from the distribution.  They are interpreted as follows: 
     348 
     349* *center* the value of the shape parameter (for size dispersity) or zero 
     350  if it is an angular dispersity.  This parameter may be fitted. 
     351 
     352* *sigma* the width of the distribution, which is the polydispersity parameter 
     353  times the center for size dispersity, or the polydispersity parameter alone 
     354  for angular dispersity.  This parameter may be fitted. 
     355 
     356* *lb*, *ub* are the parameter limits (lower & upper bounds) given in the model 
     357  definition file.  For example, a radius parameter has *lb* equal to zero.  A 
     358  volume fraction parameter would have *lb* equal to zero and *ub* equal to one. 
     359 
     360* *self.nsigmas* the distance to go into the tails when evaluating the 
     361  distribution.  For a two parameter distribution, this value could be 
     362  co-opted to use for the second parameter, though it will not be available 
     363  for fitting. 
     364 
     365* *self.npts* the number of points to use when evaluating the distribution. 
     366  The user will adjust this to trade calculation time for accuracy, but the 
     367  distribution code is free to return more or fewer, or use it for the third 
     368  parameter in a three parameter distribution. 
     369 
     370As an example, the code following wraps the Laplace distribution from scipy stats:: 
     371 
     372    import numpy as np 
     373    from scipy.stats import laplace 
     374 
     375    from sasmodels import weights 
     376 
     377    class Dispersion(weights.Dispersion): 
     378        r""" 
     379        Laplace distribution 
     380 
     381        .. math:: 
     382 
     383            w(x) = e^{-\sigma |x - \mu|} 
     384        """ 
     385        type = "laplace" 
     386        default = dict(npts=35, width=0, nsigmas=3)  # default values 
     387        def _weights(self, center, sigma, lb, ub): 
     388            x = self._linspace(center, sigma, lb, ub) 
     389            wx = laplace.pdf(x, center, sigma) 
     390            return x, wx 
     391 
     392You can plot the weights for a given value and width using the following:: 
     393 
     394    from numpy import inf 
     395    from matplotlib import pyplot as plt 
     396    from sasmodels import weights 
     397 
     398    # reload the user-defined weights 
     399    weights.load_weights() 
     400    x, wx = weights.get_weights('laplace', n=35, width=0.1, nsigmas=3, value=50, 
     401                                limits=[0, inf], relative=True) 
     402 
     403    # plot the weights 
     404    plt.interactive(True) 
     405    plt.plot(x, wx, 'x') 
     406 
     407The *self.nsigmas* and *self.npts* parameters are normally used to control 
     408the accuracy of the distribution integral. The *self._linspace* function 
     409uses them to define the *x* values (along with the *center*, *sigma*, 
     410*lb*, and *ub* which are passed as parameters).  If you repurpose npts or 
     411nsigmas you will need to generate your own *x*.  Be sure to honour the 
     412limits *lb* and *ub*, for example to disallow a negative radius or constrain 
     413the volume fraction to lie between zero and one. 
     414 
     415To activate a user-defined distribution, put it in a file such as *distname.py* 
     416in the *SAS_WEIGHTS_PATH* folder.  This is defined with an environment 
     417variable, defaulting to:: 
     418 
     419    SAS_WEIGHTS_PATH=~/.sasview/weights 
     420 
     421The weights path is loaded on startup.  To update the distribution definition 
     422in a running application you will need to enter the following python commands:: 
     423 
     424    import sasmodels.weights 
     425    sasmodels.weights.load_weights('path/to/distname.py') 
     426 
     427.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
     428 
    336429Note about DLS polydispersity 
    337430^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
    338431 
    339 Several measures of polydispersity abound in Dynamic Light Scattering (DLS) and  
    340 it should not be assumed that any of the following can be simply equated with  
     432Several measures of polydispersity abound in Dynamic Light Scattering (DLS) and 
     433it should not be assumed that any of the following can be simply equated with 
    341434the polydispersity *PD* parameter used in SasView. 
    342435 
    343 The dimensionless **Polydispersity Index (PI)** is a measure of the width of the  
    344 distribution of autocorrelation function decay rates (*not* the distribution of  
    345 particle sizes itself, though the two are inversely related) and is defined by  
     436The dimensionless **Polydispersity Index (PI)** is a measure of the width of the 
     437distribution of autocorrelation function decay rates (*not* the distribution of 
     438particle sizes itself, though the two are inversely related) and is defined by 
    346439ISO 22412:2017 as 
    347440 
     
    350443    PI = \mu_{2} / \bar \Gamma^2 
    351444 
    352 where $\mu_\text{2}$ is the second cumulant, and $\bar \Gamma^2$ is the  
     445where $\mu_\text{2}$ is the second cumulant, and $\bar \Gamma^2$ is the 
    353446intensity-weighted average value, of the distribution of decay rates. 
    354447 
     
    359452    PI = \sigma^2 / 2\bar \Gamma^2 
    360453 
    361 where $\sigma$ is the standard deviation, allowing a **Relative Polydispersity (RP)**  
     454where $\sigma$ is the standard deviation, allowing a **Relative Polydispersity (RP)** 
    362455to be defined as 
    363456 
     
    366459    RP = \sigma / \bar \Gamma = \sqrt{2 \cdot PI} 
    367460 
    368 PI values smaller than 0.05 indicate a highly monodisperse system. Values  
     461PI values smaller than 0.05 indicate a highly monodisperse system. Values 
    369462greater than 0.7 indicate significant polydispersity. 
    370463 
    371 The **size polydispersity P-parameter** is defined as the relative standard  
    372 deviation coefficient of variation   
     464The **size polydispersity P-parameter** is defined as the relative standard 
     465deviation coefficient of variation 
    373466 
    374467.. math:: 
     
    377470 
    378471where $\nu$ is the variance of the distribution and $\bar R$ is the mean 
    379 value of $R$. Here, the product $P \bar R$ is *equal* to the standard  
     472value of $R$. Here, the product $P \bar R$ is *equal* to the standard 
    380473deviation of the Lognormal distribution. 
    381474 
  • doc/guide/plugin.rst

    rdb1d9d5 re15a822  
    456456............. 
    457457 
     458.. note:: 
     459 
     460   Pure python models do not yet support direct computation of $<F(Q)^2>$ or 
     461   $<F(Q)>^2$. Neither do they support orientational distributions or magnetism 
     462   (use C models if these are required). 
     463 
    458464For pure python models, define the *Iq* function:: 
    459465 
     
    516522is automatically scaled by *form_volume/shell_volume* prior to calling the 
    517523structure factor. 
    518  
    519 **Note: Pure python models do not yet support direct computation of the** 
    520 **average of $F(q)$ and $F^2(q)$. Neither do they support orientational** 
    521 **distributions or magnetism (use C models if these are required).** 
    522524 
    523525Embedded C Models 
  • sasmodels/compare.py

    rb297ba9 rd0b0f5d  
    3939 
    4040from . import core 
     41from . import weights 
    4142from . import kerneldll 
    4243from . import kernelcl 
     
    4546from .direct_model import DirectModel, get_mesh 
    4647from .generate import FLOAT_RE, set_integration_size 
    47 from .weights import plot_weights 
    4848 
    4949# pylint: disable=unused-import 
     
    115115 
    116116    === environment variables === 
    117     -DSAS_MODELPATH=path sets directory containing custom models 
     117    -DSAS_MODELPATH=~/.sasmodels/custom_models sets path to custom models 
     118    -DSAS_WEIGHTS_PATH=~/.sasview/weights sets path to custom distributions 
    118119    -DSAS_OPENCL=vendor:device|cuda:device|none sets the target GPU device 
    119120    -DXDG_CACHE_HOME=~/.cache sets the pyopencl cache root (linux only) 
    120121    -DSAS_COMPILER=tinycc|msvc|mingw|unix sets the DLL compiler 
    121     -DSAS_OPENMP=1 turns on OpenMP for the DLLs 
    122     -DSAS_DLL_PATH=path sets the path to the compiled modules 
     122    -DSAS_OPENMP=0 set to 1 to turn on OpenMP for the DLLs 
     123    -DSAS_DLL_PATH=~/.sasmodels/compiled_models sets the DLL cache 
    123124 
    124125The interpretation of quad precision depends on architecture, and may 
     
    784785            model_info = base._kernel.info 
    785786            dim = base._kernel.dim 
    786             plot_weights(model_info, get_mesh(model_info, base_pars, dim=dim)) 
     787            weights.plot_weights(model_info, get_mesh(model_info, base_pars, dim=dim)) 
    787788        if opts['show_profile']: 
    788789            import pylab 
     
    14411442    #import pprint; pprint.pprint(model_info) 
    14421443 
     1444    # Hack to load user-defined distributions; run through all parameters 
     1445    # and make sure any pd_type parameter is a defined distribution. 
     1446    if (any(p.endswith('pd_type') and v not in weights.MODELS 
     1447            for p, v in pars.items()) or 
     1448        any(p.endswith('pd_type') and v not in weights.MODELS 
     1449            for p, v in pars2.items())): 
     1450       weights.load_weights() 
     1451 
    14431452    if opts['show_pars']: 
    14441453        if model_info.name != model_info2.name or pars != pars2: 
  • sasmodels/kernelcl.py

    ra34b811 r9fac5f5  
    158158ENV = None 
    159159def reset_environment(): 
    160     # type: () -> None 
    161     """ 
    162     Call to create a new OpenCL context, such as after a change to SAS_OPENCL. 
     160    # type: () -> "GpuEnvironment" 
     161    """ 
     162    Return a new OpenCL context, such as after a change to SAS_OPENCL. 
    163163    """ 
    164164    global ENV 
    165165    ENV = GpuEnvironment() if use_opencl() else None 
    166  
     166    return ENV 
    167167 
    168168def environment(): 
  • sasmodels/models/__init__.py

    r2d81cfe rd827c5e  
    11""" 
    2 1D Modeling for SAS 
     2Model definition files 
     3---------------------- 
     4 
     5The models below are grouped by type.  The list is a snapshot at a particular 
     6time and may be out of date. 
     7 
     8Models with pure form factor (all of which define *F(Q)*): 
     9 
     10    :mod:`barbell` 
     11    :mod:`capped_cylinder` 
     12    :mod:`core_multi_shell` 
     13    :mod:`core_shell_bicelle` 
     14    :mod:`core_shell_bicelle_elliptical` 
     15    :mod:`core_shell_bicelle_elliptical_belt_rough` 
     16    :mod:`core_shell_cylinder` 
     17    :mod:`core_shell_ellipsoid` 
     18    :mod:`core_shell_parallelepipied` 
     19    :mod:`core_shell_sphere` 
     20    :mod:`cylinder` [limiting conditions (long rods, thin disks) not implemented] 
     21    :mod:`ellipsoid` 
     22    :mod:`elliptical_cylinder` 
     23    :mod:`fuzzy_sphere` 
     24    :mod:`hollow_cylinder` 
     25    :mod:`hollow_rectangular_prism` 
     26    :mod:`hollow_rectangular_prism_thin_walls` 
     27    :mod:`multilayer_vesicle` 
     28    :mod:`onion` 
     29    :mod:`parallelepiped` 
     30    :mod:`rectangular_prism` 
     31    :mod:`sphere` 
     32    :mod:`spherical_sld` 
     33    :mod:`triaxial_ellipsoid` 
     34    :mod:`vesicle` 
     35 
     36Models with local structure factor: 
     37 
     38    :mod:`flexible_cylinder` 
     39    :mod:`flexible_cylinder_elliptical` 
     40    :mod:`linear_pearls` 
     41    :mod:`mono_gauss_coil` 
     42    :mod:`pearl_necklace` 
     43    :mod:`poly_gauss_coil` 
     44    :mod:`polymer_micelle` 
     45    :mod:`pringle` 
     46    :mod:`raspberry` 
     47    :mod:`stacked_disks` 
     48    :mod:`star_polymer` 
     49 
     50Models with long range structure factor: 
     51 
     52    :mod:`binary_hard_sphere` 
     53    :mod:`bcc_paracrystal` 
     54    :mod:`fcc_paracrystal` 
     55    :mod:`fractal` 
     56    :mod:`fractal_core_shell` 
     57    :mod:`lamellar` 
     58    :mod:`lamellar_hg` 
     59    :mod:`lamellar_hg_stack_caille` 
     60    :mod:`lamellar_stack_caille` 
     61    :mod:`lamellar_stack_paracrystal` 
     62    :mod:`mass_fractal` 
     63    :mod:`mass_surface_fractal` 
     64    :mod:`rpa` 
     65    :mod:`sc_paracrystal` 
     66    :mod:`surface_fractal` 
     67 
     68Models which are pure structure factors:: 
     69 
     70    :mod:`hardsphere` 
     71    :mod:`hayter_msa` 
     72    :mod:`squarewell` 
     73    :mod:`stickyhardsphere` 
     74 
     75Other models: 
     76 
     77    :mod:`adsorbed_layer` 
     78    :mod:`be_polyelectrolyte` 
     79    :mod:`broad_peak` 
     80    :mod:`correlation_length` 
     81    :mod:`dab` 
     82    :mod:`gauss_lorentz_gel` 
     83    :mod:`gaussian_peak` 
     84    :mod:`gel_fit` 
     85    :mod:`guinier_porod` 
     86    :mod:`guinier` 
     87    :mod:`line` 
     88    :mod:`lorentz` 
     89    :mod:`peak_lorentz` 
     90    :mod:`polymer_excl_volume` 
     91    :mod:`porod` 
     92    :mod:`power_law` 
     93    :mod:`spinodal` 
     94    :mod:`teubner_strey` 
     95    :mod:`two_lorentzian` 
     96    :mod:`unified_power_Rg` 
     97 
    398""" 
  • sasmodels/models/hardsphere.py

    rdb1d9d5 r4d00de6  
    1616   Earlier versions of SasView did not incorporate the so-called 
    1717   $\beta(q)$ ("beta") correction [1] for polydispersity and non-sphericity. 
    18    This is only available in SasView versions 4.2.2 and higher. 
     18   This is only available in SasView versions 5.0 and higher. 
    1919 
    2020radius_effective is the effective hard sphere radius. 
  • sasmodels/models/hayter_msa.py

    rdb1d9d5 r4d00de6  
    1818   Earlier versions of SasView did not incorporate the so-called 
    1919   $\beta(q)$ ("beta") correction [3] for polydispersity and non-sphericity. 
    20    This is only available in SasView versions 4.2.2 and higher. 
     20   This is only available in SasView versions 5.0 and higher. 
    2121 
    2222The salt concentration is used to compute the ionic strength of the solution 
  • sasmodels/models/sphere.py

    ra34b811 r934a001  
    3636References 
    3737---------- 
    38  
    39 .. [#] A Guinier and G. Fournet, *Small-Angle Scattering of X-Rays*, John Wiley and Sons, New York, (1955) 
     38  
     39.. [#] A Guinier and G. Fournet, *Small-Angle Scattering of X-Rays*, 
     40   John Wiley and Sons, New York, (1955) 
    4041 
    4142Source 
     
    9091    ) 
    9192    return pars 
    92  
     93#2345678901234567890123456789012345678901234567890123456789012345678901234567890 
    9394tests = [ 
    94      [{}, 0.2, 0.726362], # each test starts with default parameter values inside { }, unless modified. Then Q and expected value of I(Q) 
     95     [{}, 0.2, 0.726362], # each test starts with default parameter values  
     96     #            inside { }, unless modified. Then Q and expected value of I(Q) 
     97     # putting None for an expected result will pass the test if there are no  
     98     # errors from the routine, but without any check on the value of the result 
     99    [{"scale": 1., "background": 0., "sld": 6., "sld_solvent": 1., 
     100       "radius": 120.}, [0.01,0.1,0.2],  
     101     [1.34836265e+04, 6.20114062e+00, 1.04733914e-01]], 
    95102     [{"scale": 1., "background": 0., "sld": 6., "sld_solvent": 1., 
     103     #  careful tests here R=120 Pd=.2, then with S(Q) at default Reff=50  
     104     #  (but this gets changed to 120) phi=0,2 
    96105       "radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 
    97       0.2, 0.2288431], 
    98     [{"radius": 120., "radius_pd": 0.02, "radius_pd_n":45}, 
    99       0.2, 792.0646662454202, 1166737.0473152, 120.0, 7246723.820358589, 1.0], # the longer list here checks  F1, F2, R_eff, volume, volume_ratio = call_Fq(kernel, pars) 
     106      [0.01,0.1,0.2], [1.74395295e+04, 3.68016987e+00, 2.28843099e-01]],   
     107     # a list of Q values and list of expected results is also possible 
     108    [{"scale": 1., "background": 0., "sld": 6., "sld_solvent": 1., 
     109     "radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 
     110      0.01, 335839.88055473, 1.41045057e+11, 120.0, 8087664.122641933, 1.0],  
     111     # the longer list here checks  F1, F2, R_eff, volume, volume_ratio  
     112    [{"radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 
     113      0.1, 482.93824329, 29763977.79867414, 120.0, 8087664.122641933, 1.0],  
     114    [{"radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 
     115      0.2, 1.23330406, 1850806.1197361, 120.0, 8087664.122641933, 1.0], 
    100116   #  But note P(Q) = F2/volume 
    101    #  F and F^2 are "unscaled", with for  n <F F*>S(q) or for beta approx I(q) = n [<F F*> + <F><F*> (S(q) - 1)] 
    102    #  for n the number density and <.> the orientation average, and F = integral rho(r) exp(i q . r) dr. 
     117   #  F and F^2 are "unscaled", with for  n <F F*>S(q) or for beta approx  
     118   #          I(q) = n [<F F*> + <F><F*> (S(q) - 1)] 
     119   #  for n the number density and <.> the orientation average, and  
     120   #  F = integral rho(r) exp(i q . r) dr. 
    103121   #  The number density is volume fraction divided by particle volume. 
    104    #  Effectively, this leaves F = V drho form, where form is the usual 3 j1(qr)/(qr) or whatever depending on the shape. 
    105    # [{"@S": "hardsphere"}, 
    106    #    0.01, 55.881884232102124], # this is current value, not verified elsewhere yet 
    107    # [{"radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 
    108    #   0.2, 1.233304061, [1850806.119736], 120.0, 8087664.1226, 1.0], # the longer list here checks  F1, F2, R_eff, volume, volume_ratio = call_Fq(kernel, pars) 
    109    # [{"@S": "hardsphere"}, 
    110    #     0.2, 0.14730859242492958], #  this is current value, not verified elsewhere yet 
    111     # [{"@S": "hardsphere"}, 
    112     #    0.1, 0.7940350343811906], #  this is current value, not verified elsewhere yet 
     122   #  Effectively, this leaves F = V drho form, where form is the usual  
     123   #  3 j1(qr)/(qr) or whatever depending on the shape. 
     124   # @S RESULTS using F1 and F2 from the longer test strng above: 
     125   # 
     126   # I(Q) = (F2 + F1^2*(S(Q) -1))*volfraction*scale/Volume  + background 
     127   # 
     128   # with by default scale=1.0, background=0.001 
     129   # NOTE currently S(Q) volfraction is also included in scaling 
     130   #  structure_factor_mode 0 = normal decoupling approx,  
     131   #                        1 = beta(Q) approx 
     132   # radius_effective_mode  0 is for free choice,  
     133   #                        1 is use radius from F2(Q) 
     134   #    (sphere only has two choices, other models may have more) 
     135    [{"@S": "hardsphere", 
     136     "radius": 120., "radius_pd": 0.2, "radius_pd_n":45,"volfraction":0.2, 
     137     #"radius_effective":50.0,    # hard sphere structure factor 
     138     "structure_factor_mode": 1,  # mode 0 = normal decoupling approx,  
     139     #                                   1 = beta(Q) approx 
     140     "radius_effective_mode": 0   # this used default hardsphere Reff=50     
     141     }, [0.01,0.1,0.2], [1.32473756e+03, 7.36633631e-01, 4.67686201e-02]  ], 
    113142    [{"@S": "hardsphere", 
    114143     "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 
    115144     "volfraction":0.2, 
    116      "radius_effective":45.0,     # uses this (check) 
    117      "structure_factor_mode": 1,  # 0 = normal decoupling approximation, 1 = beta(Q) approx 
    118      "radius_effective_mode": 0   # equivalent sphere, there is only one valid mode for sphere. says -this used r_eff =0 or default 50? 
     145     "radius_effective":45.0,     # explicit Reff over rides either 50 or 120 
     146     "structure_factor_mode": 1,  # beta approx 
     147     "radius_effective_mode": 0   #  
    119148     }, 0.01, 1316.2990966463444 ], 
    120149    [{"@S": "hardsphere", 
    121150     "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 
    122151     "volfraction":0.2, 
    123      "radius_effective":50.0,        # hard sphere structure factor 
    124      "structure_factor_mode": 1,  # 0 = normal decoupling approximation, 1 = beta(Q) approx 
    125      "radius_effective_mode": 0   # this used r_eff =0 or default 50? 
    126      }, 0.01, 1324.7375636587356 ], 
     152     "radius_effective":120.0,    # over ride Reff 
     153     "structure_factor_mode": 1,  # beta approx 
     154     "radius_effective_mode": 0   # (mode=1 here also uses 120)  
     155     }, [0.01,0.1,0.2], [1.57928589e+03, 7.37067923e-01, 4.67686197e-02  ]], 
    127156    [{"@S": "hardsphere", 
    128157     "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 
    129158     "volfraction":0.2, 
    130      "radius_effective":50.0,        # hard sphere structure factor 
    131      "structure_factor_mode": 1,  # 0 = normal decoupling approximation, 1 = beta(Q) approx 
    132      "radius_effective_mode": 1   # this used 120 ??? 
    133      }, 0.01, 1579.2858949296565 ] 
     159     #"radius_effective":120.0,   # hard sphere structure factor 
     160     "structure_factor_mode": 0,  # normal decoupling approximation 
     161     "radius_effective_mode": 1   # this uses 120 from the form factor 
     162     }, [0.01,0.1,0.2], [1.10112335e+03, 7.41366536e-01, 4.66630207e-02]], 
     163    [{"@S": "hardsphere", 
     164     "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 
     165     "volfraction":0.2, 
     166     #"radius_effective":50.0,    # hard sphere structure factor 
     167     "structure_factor_mode": 0,  # normal decoupling approximation 
     168     "radius_effective_mode": 0   # this used 50 the default for hardsphere 
     169     }, [0.01,0.1,0.2], [7.82803598e+02, 6.85943611e-01, 4.71586457e-02 ]] 
    134170] 
    135 # putting None for expected result will pass the test if there are no errors from the routine, but without any check on the value of the result 
     171# 
  • sasmodels/models/squarewell.py

    rdb1d9d5 r4d00de6  
    2020   Earlier versions of SasView did not incorporate the so-called 
    2121   $\beta(q)$ ("beta") correction [2] for polydispersity and non-sphericity. 
    22    This is only available in SasView versions 4.2.2 and higher. 
     22   This is only available in SasView versions 5.0 and higher. 
    2323 
    2424The well width $(\lambda)$ is defined as multiples of the particle diameter 
  • sasmodels/models/stickyhardsphere.py

    rdb1d9d5 r4d00de6  
    5656   Earlier versions of SasView did not incorporate the so-called 
    5757   $\beta(q)$ ("beta") correction [3] for polydispersity and non-sphericity. 
    58    This is only available in SasView versions 4.2.2 and higher. 
     58   This is only available in SasView versions 5.0 and higher. 
    5959 
    6060In SasView the effective radius may be calculated from the parameters 
  • sasmodels/sasview_model.py

    ra34b811 rd0b0f5d  
    3131from . import modelinfo 
    3232from .details import make_kernel_args, dispersion_mesh 
     33 
     34# Hack: load in any custom distributions 
     35# Uses ~/.sasview/weights/*.py unless SASMODELS_WEIGHTS is set in the environ. 
     36# Override with weights.load_weights(pattern="<weights_path>/*.py") 
     37weights.load_weights() 
    3338 
    3439# pylint: disable=unused-import 
  • sasmodels/sesans.py

    rb297ba9 rda33725  
    1515from numpy import pi  # type: ignore 
    1616from scipy.special import j0 
     17 
    1718 
    1819class SesansTransform(object): 
     
    3839 
    3940    # transform arrays 
    40     _H = None  # type: np.ndarray 
    41     _H0 = None # type: np.ndarray 
     41    _H = None   # type: np.ndarray 
     42    _H0 = None  # type: np.ndarray 
    4243 
    43     def __init__(self, z, SElength, lam, zaccept, Rmax): 
     44    def __init__(self, z, SElength, lam, zaccept, Rmax, log_spacing=1.0003): 
    4445        # type: (np.ndarray, float, float) -> None 
    45         #import logging; logging.info("creating SESANS transform") 
    4646        self.q = z 
     47        self.log_spacing = log_spacing 
    4748        self._set_hankel(SElength, lam, zaccept, Rmax) 
    4849 
     
    5960    def _set_hankel(self, SElength, lam, zaccept, Rmax): 
    6061        # type: (np.ndarray, float, float) -> None 
    61         # Force float32 arrays, otherwise run into memory problems on some machines 
    62         SElength = np.asarray(SElength, dtype='float32') 
    63  
    64         #Rmax = #value in text box somewhere in FitPage? 
     62        SElength = np.asarray(SElength) 
    6563        q_max = 2*pi / (SElength[1] - SElength[0]) 
    6664        q_min = 0.1 * 2*pi / (np.size(SElength) * SElength[-1]) 
    67         q = np.arange(q_min, q_max, q_min, dtype='float32') 
    68         dq = q_min 
     65        q = np.exp(np.arange(np.log(q_min), np.log(q_max), 
     66                             np.log(self.log_spacing))) 
    6967 
    70         H0 = np.float32(dq/(2*pi)) * q 
     68        dq = np.diff(q) 
     69        dq = np.insert(dq, 0, dq[0]) 
    7170 
    72         repq = np.tile(q, (SElength.size, 1)).T 
    73         repSE = np.tile(SElength, (q.size, 1)) 
    74         H = np.float32(dq/(2*pi)) * j0(repSE*repq) * repq 
     71        H0 = dq/(2*pi) * q 
    7572 
    76         replam = np.tile(lam, (q.size, 1)) 
    77         reptheta = np.arcsin(repq*replam/2*np.pi) 
     73        H = np.outer(q, SElength) 
     74        j0(H, out=H) 
     75        H *= (dq * q / (2*pi)).reshape((-1, 1)) 
     76 
     77        reptheta = np.outer(q, lam/(2*pi)) 
     78        np.arcsin(reptheta, out=reptheta) 
    7879        mask = reptheta > zaccept 
    7980        H[mask] = 0 
  • sasmodels/weights.py

    rb297ba9 rd0b0f5d  
    230230)) 
    231231 
     232SAS_WEIGHTS_PATH = "~/.sasview/weights" 
     233def load_weights(pattern=None): 
     234    # type: (str) -> None 
     235    """ 
     236    Load dispersion distributions matching the given glob pattern 
     237    """ 
     238    import logging 
     239    import os 
     240    import os.path 
     241    import glob 
     242    import traceback 
     243    from .custom import load_custom_kernel_module 
     244    if pattern is None: 
     245        path = os.environ.get("SAS_WEIGHTS_PATH", SAS_WEIGHTS_PATH) 
     246        pattern = os.path.join(path, "*.py") 
     247    for filename in sorted(glob.glob(os.path.expanduser(pattern))): 
     248        try: 
     249            #print("loading weights from", filename) 
     250            module = load_custom_kernel_module(filename) 
     251            MODELS[module.Dispersion.type] = module.Dispersion 
     252        except Exception as exc: 
     253            logging.error(traceback.format_exc(exc)) 
    232254 
    233255def get_weights(disperser, n, width, nsigmas, value, limits, relative): 
Note: See TracChangeset for help on using the changeset viewer.