Changeset 04045f4 in sasmodels


Ignore:
Timestamp:
Apr 12, 2016 6:02:40 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:
793beb3
Parents:
fa5fd8d
Message:

support for rpa style variant models

Location:
sasmodels
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/modelinfo.py

    r7ae2b7f r04045f4  
    77module into the model info block as seen by the rest of the sasmodels library. 
    88""" 
     9from __future__ import print_function 
     10 
    911from copy import copy 
    1012from os.path import abspath, basename, splitext 
     
    1618# Optional typing 
    1719try: 
    18     from typing import Tuple, List, Union, Dict, Optional, Any, Callable 
     20    from typing import Tuple, List, Union, Dict, Optional, Any, Callable, Sequence, Set 
    1921except ImportError: 
    2022    pass 
     
    2224    from .details import CallDetails 
    2325    Limits = Tuple[float, float] 
    24     #LimitsOrChoice = Union[Limits, Tuple[str]] 
     26    LimitsOrChoice = Union[Limits, Tuple[Sequence[str]]] 
    2527    ParameterDef = Tuple[str, str, float, Limits, str, str] 
    2628    ParameterSetUser = Dict[str, Union[float, List[float]]] 
     
    6870 
    6971def parse_parameter(name, units='', default=np.NaN, 
    70                     limits=(-np.inf, np.inf), ptype='', description=''): 
    71     # type: (str, str, float, Limits, str, str) -> Parameter 
     72                    user_limits=None, ptype='', description=''): 
     73    # type: (str, str, float, LimitsOrChoice, str, str) -> Parameter 
    7274    """ 
    7375    Parse an individual parameter from the parameter definition block. 
     
    8183    if not isstr(units): 
    8284        raise ValueError("expected units to be a string for %s"%name) 
    83     # if limits is a list of strings, then this is a choice list 
    84     # field, and limits are 1 to length of string list 
     85 
     86    # Process limits as [float, float] or [[str, str, ...]] 
    8587    choices = []  # type: List[str] 
    86     if isinstance(limits, list) and all(isstr(k) for k in limits): 
    87         choices = limits 
    88         limits = (0., len(choices)-1.) 
    89  
    90     # TODO: maybe allow limits of None for (-inf, inf) 
    91     try: 
    92         low, high = limits 
    93         if not isinstance(low, (int, float)): 
    94             raise TypeError("low is not numeric") 
    95         if not isinstance(high, (int, float)): 
    96             raise TypeError("high is not numeric") 
    97         if low >= high: 
    98             raise ValueError("require low < high") 
    99     except Exception: 
    100         raise ValueError("invalid limits %s for %s"%(limits, name)) 
    101  
     88    if user_limits is None: 
     89        limits = (-np.inf, np.inf) 
     90    elif not isinstance(user_limits, (tuple, list)): 
     91        raise ValueError("invalid limits for %s"%name) 
     92    else: 
     93        # if limits is [[str,...]], then this is a choice list field, 
     94        # and limits are 1 to length of string list 
     95        if isinstance(user_limits[0], (tuple, list)): 
     96            choices = user_limits[0] 
     97            limits = (0., len(choices)-1.) 
     98            if not all(isstr(k) for k in choices): 
     99                raise ValueError("choices must be strings for %s"%name) 
     100        else: 
     101            try: 
     102                low, high = user_limits 
     103                limits = (float(low), float(high)) 
     104            except Exception: 
     105                raise ValueError("invalid limits for %s"%name) 
     106            else: 
     107                if low >= high: 
     108                    raise ValueError("require lower limit < upper limit") 
     109 
     110    # Process default value as float, making sure it is in range 
    102111    if not isinstance(default, (int, float)): 
    103112        raise ValueError("expected default %r to be a number for %s" 
    104113                         % (default, name)) 
    105     if default < low or default > high: 
     114    if default < limits[0] or default > limits[1]: 
    106115        raise ValueError("default value %r not in range for %s" 
    107116                         % (default, name)) 
    108117 
     118    # Check for valid parameter type 
    109119    if ptype not in ("volume", "orientation", "sld", "magnetic", ""): 
    110120        raise ValueError("unexpected type %r for %s" % (ptype, name)) 
    111121 
     122    # Check for valid parameter description 
    112123    if not isstr(description): 
    113124        raise ValueError("expected description to be a string") 
    114  
    115125 
    116126    # Parameter id for name[n] does not include [n] 
     
    122132    else: 
    123133        pid, ref = name, None 
    124  
    125134 
    126135    # automatically identify sld types 
     
    474483                    raise ValueError("expected limits on %s to be within [0, 20]" 
    475484                                     % ref.name) 
    476                 # TODO: may want to make a copy of the parameter before updating 
    477                 # this introduces other potential problems, since the same 
    478                 # parameter may be referenced elsewhere 
    479                 p.length = high 
     485                p.length = int(high) 
    480486 
    481487    def _get_defaults(self): 
     
    544550        early, and rerender the table when it is changed. 
    545551        """ 
     552        # control parameters go first 
    546553        control = [p for p in self.kernel_parameters if p.is_control] 
    547554 
    548555        # Gather entries such as name[n] into groups of the same n 
    549         dependent = dict((p.id, []) for p in control)  # type: Dict[str, List[Parameter]] 
     556        dependent = {} # type: Dict[str, List[Parameter]] 
     557        dependent.update((p.id, []) for p in control) 
    550558        for p in self.kernel_parameters: 
    551559            if p.length_control is not None: 
     
    635643    info.single = getattr(kernel_module, 'single', True) 
    636644    info.structure_factor = getattr(kernel_module, 'structure_factor', False) 
    637     info.profile_axes = getattr(kernel_module, 'profile_axes', ['x','y']) 
     645    info.profile_axes = getattr(kernel_module, 'profile_axes', ['x', 'y']) 
    638646    info.variant_info = getattr(kernel_module, 'variant_info', None) 
    639647    info.source = getattr(kernel_module, 'source', []) 
     
    647655    info.profile = getattr(kernel_module, 'profile', None) # type: ignore 
    648656    info.sesans = getattr(kernel_module, 'sesans', None) # type: ignore 
     657    info.control = getattr(kernel_module, 'control', None) 
     658    info.hidden = getattr(kernel_module, 'hidden', None) # type: ignore 
    649659 
    650660    # Precalculate the monodisperse parameter details 
     
    691701      *model_info* blocks for the composition objects.  This allows us to 
    692702      build complete product and mixture models from just the info. 
     703    * *control* is the name of the control parameter if there is one. 
     704    * *hidden* returns the list of hidden parameters given the value of the 
     705      control parameter 
    693706 
    694707    The structure should be mostly static, other than the delayed definition 
     
    703716    demo = None             # type: Dict[str, float] 
    704717    composition = None      # type: Optional[Tuple[str, List[ModelInfo]]] 
     718    control = None          # type: str 
    705719    docs = None             # type: str 
    706720    category = None         # type: Optional[str] 
     
    718732    profile = None          # type: Optional[Callable[[np.ndarray], None]] 
    719733    sesans = None           # type: Optional[Callable[[np.ndarray], np.ndarray]] 
     734    hidden = None           # type: Optional[Callable[int], Set[str]] 
    720735    mono_details = None     # type: CallDetails 
    721736 
     
    724739        pass 
    725740 
    726  
     741    def get_hidden_parameters(self, control): 
     742        if self.hidden is not None: 
     743            hidden = self.hidden(control) 
     744        else: 
     745            controls = [p for p in self.parameters.kernel_parameters] 
     746            if len(controls) != 1: 
     747                raise ValueError("more than one control parameter") 
     748            hidden = set(p.id+str(k) 
     749                         for p in self.parameters.kernel_parameters 
     750                         for k in range(control+1, p.length+1) 
     751                         if p.length > 1) 
     752        return hidden 
  • sasmodels/sasview_model.py

    rfa5fd8d r04045f4  
    124124    #self.is_multifunc = False 
    125125    non_fittable = []  # type: List[str] 
     126    xlabel = model_info.profile_axes[0] if model_info.profile is not None else "" 
     127    variants = MultiplicityInfo(0, "", [], xlabel) 
    126128    for p in parameters.kernel_parameters: 
    127         if p.is_control: 
     129        if p.name == model_info.control: 
    128130            non_fittable.append(p.name) 
    129             profile_axes = model_info.profile_axes 
    130             multiplicity_info = MultiplicityInfo( 
    131                 p.limits[1], p.name, p.choices, profile_axes[0] 
     131            variants = MultiplicityInfo( 
     132                len(p.choices), p.name, p.choices, xlabel 
    132133            ) 
    133134            break 
    134     else: 
    135         multiplicity_info = MultiplicityInfo(0, "", [], "") 
     135        elif p.is_control: 
     136            non_fittable.append(p.name) 
     137            variants = MultiplicityInfo( 
     138                int(p.limits[1]), p.name, p.choices, xlabel 
     139            ) 
     140            break 
    136141 
    137142    attrs['is_structure_factor'] = model_info.structure_factor 
    138143    attrs['is_form_factor'] = model_info.ER is not None 
    139     attrs['is_multiplicity_model'] = multiplicity_info[0] > 1 
    140     attrs['multiplicity_info'] = multiplicity_info 
    141  
     144    attrs['is_multiplicity_model'] = variants[0] > 1 
     145    attrs['multiplicity_info'] = variants 
    142146 
    143147    orientation_params = [] 
     
    228232        ## _persistency_dict is used by sas.perspectives.fitting.basepage 
    229233        ## to store dispersity reference. 
    230         ## TODO: _persistency_dict to persistency_dict throughout sasview 
    231234        self._persistency_dict = {} 
    232235 
     236        # TODO: _persistency_dict to persistency_dict throughout sasview 
     237        # TODO: refactor multiplicity to encompass variants 
     238        # TODO: dispersion should be a class 
    233239        # TODO: refactor multiplicity info 
    234240        # TODO: separate profile view from multiplicity 
     
    240246        # and lines to plot. 
    241247 
    242         # TODO: refactor multiplicity to encompass variants 
    243         # TODO: dispersion should be a class 
     248        # Get the list of hidden parameters given the mulitplicity 
     249        # Don't include multiplicity in the list of parameters 
    244250        self.multiplicity = multiplicity 
     251        if multiplicity is not None: 
     252            hidden = self._model_info.get_hidden_parameters(multiplicity) 
     253            hidden |= set([self.multiplicity_info.control]) 
     254        else: 
     255            hidden = set() 
     256 
    245257        self.params = collections.OrderedDict() 
    246258        self.dispersion = {} 
    247259        self.details = {} 
    248         config = ({self.multiplicity_info.control: multiplicity} 
    249                   if multiplicity is not None else {}) 
    250         for p in self._model_info.parameters.user_parameters(config): 
    251             # Don't include multiplicity in the list of parameters 
    252             if p.name == self.multiplicity_info.control: 
     260        for p in self._model_info.parameters.user_parameters(): 
     261            if p.name in hidden: 
    253262                continue 
    254263            self.params[p.name] = p.default 
     
    601610    return cylinder.evalDistribution([0.1,0.1]) 
    602611 
     612def test_rpa(): 
     613    # type: () -> float 
     614    """ 
     615    Test that a sasview model (cylinder) can be run. 
     616    """ 
     617    RPA = _make_standard_model('rpa') 
     618    rpa = RPA(3) 
     619    return rpa.evalDistribution([0.1,0.1]) 
     620 
    603621 
    604622def test_model_list(): 
Note: See TracChangeset for help on using the changeset viewer.