Changeset e526a9d in sasmodels for sasmodels


Ignore:
Timestamp:
Feb 4, 2018 9:40:01 PM (6 years ago)
Author:
GitHub <noreply@…>
Branches:
master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
aadec17
Parents:
032646d (diff), 72e41a0 (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 Butler <butlerpd@…> (02/04/18 21:40:01)
git-committer:
GitHub <noreply@…> (02/04/18 21:40:01)
Message:

Merge pull request #59 from SasView?/boltzmann

add BoltzmannDistribution? and UniformDistribution?

Since Paul K approved the code and the only issue found by R. Heenan and P. Butler has been resolved by sasview PR 141 we should be able to merge this now.

Location:
sasmodels
Files:
4 edited
2 moved

Legend:

Unmodified
Added
Removed
  • sasmodels/weights.py

    r2d81cfe r3d58247  
    9797        return x, px 
    9898 
     99class UniformDispersion(Dispersion): 
     100    r""" 
     101    Uniform dispersion, with width $\sigma$. 
     102 
     103    .. math:: 
     104 
     105        w = 1 
     106    """ 
     107    type = "uniform" 
     108    default = dict(npts=35, width=0, nsigmas=None) 
     109    def _weights(self, center, sigma, lb, ub): 
     110        x = np.linspace(center-sigma, center+sigma, self.npts) 
     111        x = x[(x >= lb) & (x <= ub)] 
     112        return x, np.ones_like(x) 
    99113 
    100114class RectangleDispersion(Dispersion): 
     
    107121    """ 
    108122    type = "rectangle" 
    109     default = dict(npts=35, width=0, nsigmas=1.70325) 
    110     def _weights(self, center, sigma, lb, ub): 
    111         x = self._linspace(center, sigma, lb, ub) 
    112         x = x[np.fabs(x-center) <= np.fabs(sigma)*sqrt(3.0)] 
    113         return x, np.ones_like(x) 
    114  
     123    default = dict(npts=35, width=0, nsigmas=1.73205) 
     124    def _weights(self, center, sigma, lb, ub): 
     125         x = self._linspace(center, sigma, lb, ub) 
     126         x = x[np.fabs(x-center) <= np.fabs(sigma)*sqrt(3.0)] 
     127         return x, np.ones_like(x) 
    115128 
    116129class LogNormalDispersion(Dispersion): 
     
    190203        return x, px 
    191204 
     205class BoltzmannDispersion(Dispersion): 
     206    r""" 
     207    Boltzmann dispersion, with $\sigma=k T/E$. 
     208 
     209    .. math:: 
     210 
     211        w = \exp\left( -|x - c|/\sigma\right) 
     212    """ 
     213    type = "boltzmann" 
     214    default = dict(npts=35, width=0, nsigmas=3) 
     215    def _weights(self, center, sigma, lb, ub): 
     216        x = self._linspace(center, sigma, lb, ub) 
     217        px = np.exp(-np.abs(x-center) / np.abs(sigma)) 
     218        return x, px 
    192219 
    193220# dispersion name -> disperser lookup table. 
     
    196223MODELS = OrderedDict((d.type, d) for d in ( 
    197224    RectangleDispersion, 
     225    UniformDispersion, 
    198226    ArrayDispersion, 
    199227    LogNormalDispersion, 
    200228    GaussianDispersion, 
    201229    SchulzDispersion, 
     230    BoltzmannDispersion 
    202231)) 
    203232 
  • sasmodels/core.py

    r2d81cfe r7a516d0  
    148148    used with functions in generate to build the docs or extract model info. 
    149149    """ 
    150     if '@' in model_string: 
    151         parts = model_string.split('@') 
    152         if len(parts) != 2: 
    153             raise ValueError("Use P@S to apply a structure factor S to model P") 
    154         P_info, Q_info = [load_model_info(part) for part in parts] 
    155         return product.make_product_info(P_info, Q_info) 
    156  
    157     product_parts = [] 
    158     addition_parts = [] 
    159  
    160     addition_parts_names = model_string.split('+') 
    161     if len(addition_parts_names) >= 2: 
    162         addition_parts = [load_model_info(part) for part in addition_parts_names] 
    163     elif len(addition_parts_names) == 1: 
    164         product_parts_names = model_string.split('*') 
    165         if len(product_parts_names) >= 2: 
    166             product_parts = [load_model_info(part) for part in product_parts_names] 
    167         elif len(product_parts_names) == 1: 
    168             if "custom." in product_parts_names[0]: 
    169                 # Extract ModelName from "custom.ModelName" 
    170                 pattern = "custom.([A-Za-z0-9_-]+)" 
    171                 result = re.match(pattern, product_parts_names[0]) 
    172                 if result is None: 
    173                     raise ValueError("Model name in invalid format: " + product_parts_names[0]) 
    174                 model_name = result.group(1) 
    175                 # Use ModelName to find the path to the custom model file 
    176                 model_path = joinpath(CUSTOM_MODEL_PATH, model_name + ".py") 
    177                 if not os.path.isfile(model_path): 
    178                     raise ValueError("The model file {} doesn't exist".format(model_path)) 
    179                 kernel_module = custom.load_custom_kernel_module(model_path) 
    180                 return modelinfo.make_model_info(kernel_module) 
    181             # Model is a core model 
    182             kernel_module = generate.load_kernel_module(product_parts_names[0]) 
    183             return modelinfo.make_model_info(kernel_module) 
    184  
    185     model = None 
    186     if len(product_parts) > 1: 
    187         model = mixture.make_mixture_info(product_parts, operation='*') 
    188     if len(addition_parts) > 1: 
    189         if model is not None: 
    190             addition_parts.append(model) 
    191         model = mixture.make_mixture_info(addition_parts, operation='+') 
    192     return model 
     150    if "+" in model_string: 
     151        parts = [load_model_info(part) 
     152                 for part in model_string.split("+")] 
     153        return mixture.make_mixture_info(parts, operation='+') 
     154    elif "*" in model_string: 
     155        parts = [load_model_info(part) 
     156                 for part in model_string.split("*")] 
     157        return mixture.make_mixture_info(parts, operation='*') 
     158    elif "@" in model_string: 
     159        p_info, q_info = [load_model_info(part) 
     160                          for part in model_string.split("@")] 
     161        return product.make_product_info(p_info, q_info) 
     162    # We are now dealing with a pure model 
     163    elif "custom." in model_string: 
     164        pattern = "custom.([A-Za-z0-9_-]+)" 
     165        result = re.match(pattern, model_string) 
     166        if result is None: 
     167            raise ValueError("Model name in invalid format: " + model_string) 
     168        model_name = result.group(1) 
     169        # Use ModelName to find the path to the custom model file 
     170        model_path = joinpath(CUSTOM_MODEL_PATH, model_name + ".py") 
     171        if not os.path.isfile(model_path): 
     172            raise ValueError("The model file {} doesn't exist".format(model_path)) 
     173        kernel_module = custom.load_custom_kernel_module(model_path) 
     174        return modelinfo.make_model_info(kernel_module) 
     175    kernel_module = generate.load_kernel_module(model_string) 
     176    return modelinfo.make_model_info(kernel_module) 
    193177 
    194178 
     
    336320    print("\n".join(list_models(kind))) 
    337321 
     322def test_load(): 
     323    # type: () -> None 
     324    """Check that model load works""" 
     325    def test_models(fst, snd): 
     326        """Confirm that two models produce the same parameters""" 
     327        fst = load_model(fst) 
     328        snd = load_model(snd) 
     329        # Remove the upper case characters frin the parameters, since 
     330        # they lasndel the order of events and we specfically are 
     331        # changin that aspect 
     332        fst = [[x for x in p.name if x == x.lower()] for p in fst.info.parameters.kernel_parameters] 
     333        snd = [[x for x in p.name if x == x.lower()] for p in snd.info.parameters.kernel_parameters] 
     334        assert sorted(fst) == sorted(snd), "{} != {}".format(fst, snd) 
     335 
     336 
     337    test_models( 
     338        "cylinder+sphere", 
     339        "sphere+cylinder") 
     340    test_models( 
     341        "cylinder*sphere", 
     342        "sphere*cylinder") 
     343    test_models( 
     344        "cylinder@hardsphere*sphere", 
     345        "sphere*cylinder@hardsphere") 
     346    test_models( 
     347        "barbell+sphere*cylinder@hardsphere", 
     348        "sphere*cylinder@hardsphere+barbell") 
     349    test_models( 
     350        "barbell+cylinder@hardsphere*sphere", 
     351        "cylinder@hardsphere*sphere+barbell") 
     352    test_models( 
     353        "barbell+sphere*cylinder@hardsphere", 
     354        "barbell+cylinder@hardsphere*sphere") 
     355    test_models( 
     356        "sphere*cylinder@hardsphere+barbell", 
     357        "cylinder@hardsphere*sphere+barbell") 
     358    test_models( 
     359        "barbell+sphere*cylinder@hardsphere", 
     360        "cylinder@hardsphere*sphere+barbell") 
     361    test_models( 
     362        "barbell+cylinder@hardsphere*sphere", 
     363        "sphere*cylinder@hardsphere+barbell") 
     364 
     365    #Test the the model produces the parameters that we would expect 
     366    model = load_model("cylinder@hardsphere*sphere") 
     367    actual = [p.name for p in model.info.parameters.kernel_parameters] 
     368    target = ("sld sld_solvent radius length theta phi volfraction" 
     369              " A_sld A_sld_solvent A_radius").split() 
     370    assert target == actual, "%s != %s"%(target, actual) 
     371 
    338372if __name__ == "__main__": 
    339373    list_models_main() 
  • sasmodels/jitter.py

    r8cfb486 r0d5a655  
    11#!/usr/bin/env python 
    22""" 
    3 Application to explore the difference between sasview 3.x orientation 
    4 dispersity and possible replacement algorithms. 
     3Jitter Explorer 
     4=============== 
     5 
     6Application to explore orientation angle and angular dispersity. 
    57""" 
    68from __future__ import division, print_function 
     
    124126def draw_parallelepiped(ax, size, view, jitter, steps=None, alpha=1): 
    125127    """Draw a parallelepiped.""" 
    126     a,b,c = size 
     128    a, b, c = size 
    127129    x = a*np.array([ 1,-1, 1,-1, 1,-1, 1,-1]) 
    128130    y = b*np.array([ 1, 1,-1,-1, 1, 1,-1,-1]) 
     
    141143    x, y, z = transform_xyz(view, jitter, x, y, z) 
    142144    ax.plot_trisurf(x, y, triangles=tri, Z=z, color='w', alpha=alpha) 
     145 
     146    # Draw pink face on box. 
     147    # Since I can't control face color, instead draw a thin box situated just 
     148    # in front of the "a+" face. 
     149    if 1: 
     150        x = a*np.array([ 1,-1, 1,-1, 1,-1, 1,-1]) 
     151        y = b*np.array([ 1, 1,-1,-1, 1, 1,-1,-1]) 
     152        z = c*np.array([ 1, 1, 1, 1,-1,-1,-1,-1]) 
     153        x, y, z = transform_xyz(view, jitter, abs(x)*1.05, y, z) 
     154        ax.plot_trisurf(x, y, triangles=tri, Z=z, color=[1,0.6,0.6], alpha=alpha) 
    143155 
    144156    draw_labels(ax, view, jitter, [ 
     
    608620    Show an interactive orientation and jitter demo. 
    609621 
    610     *model_name* is one of the models available in :func:`select_model`. 
     622    *model_name* is one of: sphere, cylinder, ellipsoid, parallelepiped, 
     623    triaxial_ellipsoid, or bcc_paracrystal. 
     624 
     625    *size* gives the dimensions (a, b, c) of the shape. 
     626 
     627    *dist* is the type of dispersition: gaussian, rectangle, or uniform. 
     628 
     629    *mesh* is the number of points in the dispersion mesh. 
     630 
     631    *projection* is the map projection to use for the mesh: equirectangular, 
     632    sinusoidal, guyou, azimuthal_equidistance, or azimuthal_equal_area. 
    611633    """ 
    612634    # projection number according to 1-order position in list, but 
  • sasmodels/mixture.py

    r2d81cfe recf895e  
    9494            # If model is a sum model, each constituent model gets its own scale parameter 
    9595            scale_prefix = prefix 
    96             if prefix == '' and part.operation == '*': 
     96            if prefix == '' and getattr(part, "operation", '') == '*': 
    9797                # `part` is a composition product model. Find the prefixes of 
    98                 # it's parameters to form a new prefix for the scale, eg: 
    99                 # a model with A*B*C will have ABC_scale 
     98                # it's parameters to form a new prefix for the scale. 
     99                # For example, a model with A*B*C will have ABC_scale. 
    100100                sub_prefixes = [] 
    101101                for param in part.parameters.kernel_parameters: 
     
    233233        return self 
    234234 
    235     def next(self): 
     235    def __next__(self): 
    236236        # type: () -> Tuple[List[Callable], CallDetails, np.ndarray] 
    237237        if self.part_num >= len(self.parts): 
     
    251251 
    252252        return kernel, call_details, values 
     253 
     254    # CRUFT: py2 support 
     255    next = __next__ 
    253256 
    254257    def _part_details(self, info, par_index): 
  • sasmodels/modelinfo.py

    r108e70e r95498a3  
    6969        processed.append(parse_parameter(*p)) 
    7070    partable = ParameterTable(processed) 
     71    partable.check_angles() 
    7172    return partable 
    7273 
     
    421422        # type: (List[Parameter]) -> None 
    422423        self.kernel_parameters = parameters 
    423         self._check_angles() 
    424424        self._set_vector_lengths() 
    425425 
     
    471471        self.pd_2d = set(p.name for p in self.call_parameters if p.polydisperse) 
    472472 
    473     def _check_angles(self): 
     473    def check_angles(self): 
     474        """ 
     475        Check that orientation angles are theta, phi and possibly psi. 
     476        """ 
    474477        theta = phi = psi = -1 
    475478        for k, p in enumerate(self.kernel_parameters): 
     
    494497            if psi >= 0 and psi != phi+1: 
    495498                raise TypeError("psi must follow phi") 
    496             #if (psi >= 0 and psi != last_par) or (psi < 0 and phi != last_par): 
    497             #    raise TypeError("orientation parameters must appear at the " 
    498             #                    "end of the parameter table") 
     499            if (psi >= 0 and psi != last_par) or (psi < 0 and phi != last_par): 
     500                raise TypeError("orientation parameters must appear at the " 
     501                                "end of the parameter table") 
    499502        elif theta >= 0 or phi >= 0 or psi >= 0: 
    500503            raise TypeError("oriented shapes must have both theta and phi and maybe psi") 
Note: See TracChangeset for help on using the changeset viewer.