source: sasmodels/sasmodels/sasview_model.py @ 50ec515

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 50ec515 was 50ec515, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

spherical sld: document interface shape number→interface relationship since UI doesn't show dropdown list yet

  • Property mode set to 100644
File size: 22.3 KB
RevLine 
[87985ca]1"""
2Sasview model constructor.
3
4Given a module defining an OpenCL kernel such as sasmodels.models.cylinder,
5create a sasview model class to run that kernel as follows::
6
[92d38285]7    from sasmodels.sasview_model import load_custom_model
8    CylinderModel = load_custom_model('sasmodels/models/cylinder.py')
[87985ca]9"""
[4d76711]10from __future__ import print_function
[87985ca]11
[ce27e21]12import math
13from copy import deepcopy
[2622b3f]14import collections
[4d76711]15import traceback
16import logging
[ce27e21]17
[7ae2b7f]18import numpy as np  # type: ignore
[ce27e21]19
[aa4946b]20from . import core
[4d76711]21from . import custom
[72a081d]22from . import generate
[fb5914f]23from . import weights
[6d6508e]24from . import modelinfo
[9eb3632]25from .details import build_details, dispersion_mesh
[ff7119b]26
[fa5fd8d]27try:
[60f03de]28    from typing import Dict, Mapping, Any, Sequence, Tuple, NamedTuple, List, Optional, Union, Callable
[fa5fd8d]29    from .modelinfo import ModelInfo, Parameter
30    from .kernel import KernelModel
31    MultiplicityInfoType = NamedTuple(
32        'MuliplicityInfo',
33        [("number", int), ("control", str), ("choices", List[str]),
34         ("x_axis_label", str)])
[60f03de]35    SasviewModelType = Callable[[int], "SasviewModel"]
[fa5fd8d]36except ImportError:
37    pass
38
39# TODO: separate x_axis_label from multiplicity info
40MultiplicityInfo = collections.namedtuple(
41    'MultiplicityInfo',
42    ["number", "control", "choices", "x_axis_label"],
43)
44
[92d38285]45MODELS = {}
46def find_model(modelname):
47    # TODO: used by sum/product model to load an existing model
48    # TODO: doesn't handle custom models properly
49    if modelname.endswith('.py'):
50        return load_custom_model(modelname)
51    elif modelname in MODELS:
52        return MODELS[modelname]
53    else:
54        raise ValueError("unknown model %r"%modelname)
55
[56b2687]56
[fa5fd8d]57# TODO: figure out how to say that the return type is a subclass
[4d76711]58def load_standard_models():
[60f03de]59    # type: () -> List[SasviewModelType]
[4d76711]60    """
61    Load and return the list of predefined models.
62
63    If there is an error loading a model, then a traceback is logged and the
64    model is not returned.
65    """
66    models = []
67    for name in core.list_models():
68        try:
[92d38285]69            MODELS[name] = _make_standard_model(name)
70            models.append(MODELS[name])
[ee8f734]71        except Exception:
[4d76711]72            logging.error(traceback.format_exc())
73    return models
[de97440]74
[4d76711]75
76def load_custom_model(path):
[60f03de]77    # type: (str) -> SasviewModelType
[4d76711]78    """
79    Load a custom model given the model path.
[ff7119b]80    """
[92d38285]81    #print("load custom model", path)
[4d76711]82    kernel_module = custom.load_custom_kernel_module(path)
[92d38285]83    try:
84        model = kernel_module.Model
85    except AttributeError:
[56b2687]86        model_info = modelinfo.make_model_info(kernel_module)
[92d38285]87        model = _make_model_from_info(model_info)
88    MODELS[model.name] = model
89    return model
[4d76711]90
[87985ca]91
[4d76711]92def _make_standard_model(name):
[60f03de]93    # type: (str) -> SasviewModelType
[ff7119b]94    """
[4d76711]95    Load the sasview model defined by *name*.
[72a081d]96
[4d76711]97    *name* can be a standard model name or a path to a custom model.
[87985ca]98
[4d76711]99    Returns a class that can be used directly as a sasview model.
[ff7119b]100    """
[4d76711]101    kernel_module = generate.load_kernel_module(name)
[fa5fd8d]102    model_info = modelinfo.make_model_info(kernel_module)
[4d76711]103    return _make_model_from_info(model_info)
[72a081d]104
105
[4d76711]106def _make_model_from_info(model_info):
[60f03de]107    # type: (ModelInfo) -> SasviewModelType
[4d76711]108    """
109    Convert *model_info* into a SasView model wrapper.
110    """
[fa5fd8d]111    def __init__(self, multiplicity=None):
112        SasviewModel.__init__(self, multiplicity=multiplicity)
113    attrs = _generate_model_attributes(model_info)
114    attrs['__init__'] = __init__
[60f03de]115    ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType
[ce27e21]116    return ConstructedModel
117
[fa5fd8d]118def _generate_model_attributes(model_info):
119    # type: (ModelInfo) -> Dict[str, Any]
120    """
121    Generate the class attributes for the model.
122
123    This should include all the information necessary to query the model
124    details so that you do not need to instantiate a model to query it.
125
126    All the attributes should be immutable to avoid accidents.
127    """
128
129    # TODO: allow model to override axis labels input/output name/unit
130
[a18c5b3]131    # Process multiplicity
[fa5fd8d]132    non_fittable = []  # type: List[str]
[04045f4]133    xlabel = model_info.profile_axes[0] if model_info.profile is not None else ""
134    variants = MultiplicityInfo(0, "", [], xlabel)
[a18c5b3]135    for p in model_info.parameters.kernel_parameters:
[04045f4]136        if p.name == model_info.control:
[fa5fd8d]137            non_fittable.append(p.name)
[04045f4]138            variants = MultiplicityInfo(
[ce176ca]139                len(p.choices) if p.choices else int(p.limits[1]),
140                p.name, p.choices, xlabel
[fa5fd8d]141            )
142            break
143
[50ec515]144    # Only a single drop-down list parameter available
145    fun_list = []
146    for p in model_info.parameters.kernel_parameters:
147        if p.choices:
148            fun_list = p.choices
149            if p.length > 1:
150                non_fittable.extend(p.id+str(k) for k in range(1, p.length+1))
151            break
152
[a18c5b3]153    # Organize parameter sets
[fa5fd8d]154    orientation_params = []
155    magnetic_params = []
156    fixed = []
[a18c5b3]157    for p in model_info.parameters.user_parameters():
[fa5fd8d]158        if p.type == 'orientation':
159            orientation_params.append(p.name)
160            orientation_params.append(p.name+".width")
161            fixed.append(p.name+".width")
[32e3c9b]162        elif p.type == 'magnetic':
[fa5fd8d]163            orientation_params.append(p.name)
164            magnetic_params.append(p.name)
165            fixed.append(p.name+".width")
[a18c5b3]166
[32e3c9b]167
[a18c5b3]168    # Build class dictionary
169    attrs = {}  # type: Dict[str, Any]
170    attrs['_model_info'] = model_info
171    attrs['name'] = model_info.name
172    attrs['id'] = model_info.id
173    attrs['description'] = model_info.description
174    attrs['category'] = model_info.category
175    attrs['is_structure_factor'] = model_info.structure_factor
176    attrs['is_form_factor'] = model_info.ER is not None
177    attrs['is_multiplicity_model'] = variants[0] > 1
178    attrs['multiplicity_info'] = variants
[fa5fd8d]179    attrs['orientation_params'] = tuple(orientation_params)
180    attrs['magnetic_params'] = tuple(magnetic_params)
181    attrs['fixed'] = tuple(fixed)
182    attrs['non_fittable'] = tuple(non_fittable)
[50ec515]183    attrs['fun_list'] = tuple(fun_list)
[fa5fd8d]184
185    return attrs
[4d76711]186
[ce27e21]187class SasviewModel(object):
188    """
189    Sasview wrapper for opencl/ctypes model.
190    """
[fa5fd8d]191    # Model parameters for the specific model are set in the class constructor
192    # via the _generate_model_attributes function, which subclasses
193    # SasviewModel.  They are included here for typing and documentation
194    # purposes.
195    _model = None       # type: KernelModel
196    _model_info = None  # type: ModelInfo
197    #: load/save name for the model
198    id = None           # type: str
199    #: display name for the model
200    name = None         # type: str
201    #: short model description
202    description = None  # type: str
203    #: default model category
204    category = None     # type: str
205
206    #: names of the orientation parameters in the order they appear
207    orientation_params = None # type: Sequence[str]
208    #: names of the magnetic parameters in the order they appear
209    magnetic_params = None    # type: Sequence[str]
210    #: names of the fittable parameters
211    fixed = None              # type: Sequence[str]
212    # TODO: the attribute fixed is ill-named
213
214    # Axis labels
215    input_name = "Q"
216    input_unit = "A^{-1}"
217    output_name = "Intensity"
218    output_unit = "cm^{-1}"
219
220    #: default cutoff for polydispersity
221    cutoff = 1e-5
222
223    # Note: Use non-mutable values for class attributes to avoid errors
224    #: parameters that are not fitted
225    non_fittable = ()        # type: Sequence[str]
226
227    #: True if model should appear as a structure factor
228    is_structure_factor = False
229    #: True if model should appear as a form factor
230    is_form_factor = False
231    #: True if model has multiplicity
232    is_multiplicity_model = False
233    #: Mulitplicity information
234    multiplicity_info = None # type: MultiplicityInfoType
235
236    # Per-instance variables
237    #: parameter {name: value} mapping
238    params = None      # type: Dict[str, float]
239    #: values for dispersion width, npts, nsigmas and type
240    dispersion = None  # type: Dict[str, Any]
241    #: units and limits for each parameter
[60f03de]242    details = None     # type: Dict[str, Sequence[Any]]
243    #                  # actual type is Dict[str, List[str, float, float]]
[04dc697]244    #: multiplicity value, or None if no multiplicity on the model
[fa5fd8d]245    multiplicity = None     # type: Optional[int]
[04dc697]246    #: memory for polydispersity array if using ArrayDispersion (used by sasview).
247    _persistency_dict = None # type: Dict[str, Tuple[np.ndarray, np.ndarray]]
[fa5fd8d]248
249    def __init__(self, multiplicity=None):
[04dc697]250        # type: (Optional[int]) -> None
[2622b3f]251
[04045f4]252        # TODO: _persistency_dict to persistency_dict throughout sasview
253        # TODO: refactor multiplicity to encompass variants
254        # TODO: dispersion should be a class
[fa5fd8d]255        # TODO: refactor multiplicity info
256        # TODO: separate profile view from multiplicity
257        # The button label, x and y axis labels and scale need to be under
258        # the control of the model, not the fit page.  Maximum flexibility,
259        # the fit page would supply the canvas and the profile could plot
260        # how it wants, but this assumes matplotlib.  Next level is that
261        # we provide some sort of data description including title, labels
262        # and lines to plot.
263
[04045f4]264        # Get the list of hidden parameters given the mulitplicity
265        # Don't include multiplicity in the list of parameters
[fa5fd8d]266        self.multiplicity = multiplicity
[04045f4]267        if multiplicity is not None:
268            hidden = self._model_info.get_hidden_parameters(multiplicity)
269            hidden |= set([self.multiplicity_info.control])
270        else:
271            hidden = set()
272
[04dc697]273        self._persistency_dict = {}
[fa5fd8d]274        self.params = collections.OrderedDict()
[b3a85cd]275        self.dispersion = collections.OrderedDict()
[fa5fd8d]276        self.details = {}
[04045f4]277        for p in self._model_info.parameters.user_parameters():
278            if p.name in hidden:
[fa5fd8d]279                continue
[fcd7bbd]280            self.params[p.name] = p.default
[fa5fd8d]281            self.details[p.id] = [p.units, p.limits[0], p.limits[1]]
[fb5914f]282            if p.polydisperse:
[fa5fd8d]283                self.details[p.id+".width"] = [
284                    "", 0.0, 1.0 if p.relative_pd else np.inf
285                ]
[fb5914f]286                self.dispersion[p.name] = {
287                    'width': 0,
288                    'npts': 35,
289                    'nsigmas': 3,
290                    'type': 'gaussian',
291                }
[ce27e21]292
[de97440]293    def __get_state__(self):
[fa5fd8d]294        # type: () -> Dict[str, Any]
[de97440]295        state = self.__dict__.copy()
[4d76711]296        state.pop('_model')
[de97440]297        # May need to reload model info on set state since it has pointers
298        # to python implementations of Iq, etc.
299        #state.pop('_model_info')
300        return state
301
302    def __set_state__(self, state):
[fa5fd8d]303        # type: (Dict[str, Any]) -> None
[de97440]304        self.__dict__ = state
[fb5914f]305        self._model = None
[de97440]306
[ce27e21]307    def __str__(self):
[fa5fd8d]308        # type: () -> str
[ce27e21]309        """
310        :return: string representation
311        """
312        return self.name
313
314    def is_fittable(self, par_name):
[fa5fd8d]315        # type: (str) -> bool
[ce27e21]316        """
317        Check if a given parameter is fittable or not
318
319        :param par_name: the parameter name to check
320        """
[e758662]321        return par_name in self.fixed
[ce27e21]322        #For the future
323        #return self.params[str(par_name)].is_fittable()
324
325
326    def getProfile(self):
[fa5fd8d]327        # type: () -> (np.ndarray, np.ndarray)
[ce27e21]328        """
329        Get SLD profile
330
331        : return: (z, beta) where z is a list of depth of the transition points
332                beta is a list of the corresponding SLD values
333        """
[745b7bb]334        args = {} # type: Dict[str, Any]
[fa5fd8d]335        for p in self._model_info.parameters.kernel_parameters:
336            if p.id == self.multiplicity_info.control:
[745b7bb]337                value = float(self.multiplicity)
[fa5fd8d]338            elif p.length == 1:
[745b7bb]339                value = self.params.get(p.id, np.NaN)
[fa5fd8d]340            else:
[745b7bb]341                value = np.array([self.params.get(p.id+str(k), np.NaN)
[4e0968b]342                                  for k in range(1,p.length+1)])
[745b7bb]343            args[p.id] = value
344
[e7fe459]345        x, y = self._model_info.profile(**args)
346        return x, 1e-6*y
[ce27e21]347
348    def setParam(self, name, value):
[fa5fd8d]349        # type: (str, float) -> None
[ce27e21]350        """
351        Set the value of a model parameter
352
353        :param name: name of the parameter
354        :param value: value of the parameter
355
356        """
357        # Look for dispersion parameters
358        toks = name.split('.')
[de0c4ba]359        if len(toks) == 2:
[ce27e21]360            for item in self.dispersion.keys():
[e758662]361                if item == toks[0]:
[ce27e21]362                    for par in self.dispersion[item]:
[e758662]363                        if par == toks[1]:
[ce27e21]364                            self.dispersion[item][par] = value
365                            return
366        else:
367            # Look for standard parameter
368            for item in self.params.keys():
[e758662]369                if item == name:
[ce27e21]370                    self.params[item] = value
371                    return
372
[63b32bb]373        raise ValueError("Model does not contain parameter %s" % name)
[ce27e21]374
375    def getParam(self, name):
[fa5fd8d]376        # type: (str) -> float
[ce27e21]377        """
378        Set the value of a model parameter
379
380        :param name: name of the parameter
381
382        """
383        # Look for dispersion parameters
384        toks = name.split('.')
[de0c4ba]385        if len(toks) == 2:
[ce27e21]386            for item in self.dispersion.keys():
[e758662]387                if item == toks[0]:
[ce27e21]388                    for par in self.dispersion[item]:
[e758662]389                        if par == toks[1]:
[ce27e21]390                            return self.dispersion[item][par]
391        else:
392            # Look for standard parameter
393            for item in self.params.keys():
[e758662]394                if item == name:
[ce27e21]395                    return self.params[item]
396
[63b32bb]397        raise ValueError("Model does not contain parameter %s" % name)
[ce27e21]398
399    def getParamList(self):
[04dc697]400        # type: () -> Sequence[str]
[ce27e21]401        """
402        Return a list of all available parameters for the model
403        """
[04dc697]404        param_list = list(self.params.keys())
[ce27e21]405        # WARNING: Extending the list with the dispersion parameters
[de0c4ba]406        param_list.extend(self.getDispParamList())
407        return param_list
[ce27e21]408
409    def getDispParamList(self):
[04dc697]410        # type: () -> Sequence[str]
[ce27e21]411        """
[fb5914f]412        Return a list of polydispersity parameters for the model
[ce27e21]413        """
[1780d59]414        # TODO: fix test so that parameter order doesn't matter
[56b2687]415        ret = ['%s.%s' % (p.name, ext)
[6d6508e]416               for p in self._model_info.parameters.user_parameters()
[fb5914f]417               for ext in ('npts', 'nsigmas', 'width')
418               if p.polydisperse]
[9404dd3]419        #print(ret)
[1780d59]420        return ret
[ce27e21]421
422    def clone(self):
[04dc697]423        # type: () -> "SasviewModel"
[ce27e21]424        """ Return a identical copy of self """
425        return deepcopy(self)
426
427    def run(self, x=0.0):
[fa5fd8d]428        # type: (Union[float, (float, float), List[float]]) -> float
[ce27e21]429        """
430        Evaluate the model
431
432        :param x: input q, or [q,phi]
433
434        :return: scattering function P(q)
435
436        **DEPRECATED**: use calculate_Iq instead
437        """
[de0c4ba]438        if isinstance(x, (list, tuple)):
[3c56da87]439            # pylint: disable=unpacking-non-sequence
[ce27e21]440            q, phi = x
[60f03de]441            return self.calculate_Iq([q*math.cos(phi)], [q*math.sin(phi)])[0]
[ce27e21]442        else:
[60f03de]443            return self.calculate_Iq([x])[0]
[ce27e21]444
445
446    def runXY(self, x=0.0):
[fa5fd8d]447        # type: (Union[float, (float, float), List[float]]) -> float
[ce27e21]448        """
449        Evaluate the model in cartesian coordinates
450
451        :param x: input q, or [qx, qy]
452
453        :return: scattering function P(q)
454
455        **DEPRECATED**: use calculate_Iq instead
456        """
[de0c4ba]457        if isinstance(x, (list, tuple)):
[60f03de]458            return self.calculate_Iq([x[0]], [x[1]])[0]
[ce27e21]459        else:
[60f03de]460            return self.calculate_Iq([x])[0]
[ce27e21]461
462    def evalDistribution(self, qdist):
[04dc697]463        # type: (Union[np.ndarray, Tuple[np.ndarray, np.ndarray], List[np.ndarray]]) -> np.ndarray
[d138d43]464        r"""
[ce27e21]465        Evaluate a distribution of q-values.
466
[d138d43]467        :param qdist: array of q or a list of arrays [qx,qy]
[ce27e21]468
[d138d43]469        * For 1D, a numpy array is expected as input
[ce27e21]470
[d138d43]471        ::
[ce27e21]472
[d138d43]473            evalDistribution(q)
[ce27e21]474
[d138d43]475          where *q* is a numpy array.
[ce27e21]476
[d138d43]477        * For 2D, a list of *[qx,qy]* is expected with 1D arrays as input
[ce27e21]478
[d138d43]479        ::
[ce27e21]480
[d138d43]481              qx = [ qx[0], qx[1], qx[2], ....]
482              qy = [ qy[0], qy[1], qy[2], ....]
[ce27e21]483
[d138d43]484        If the model is 1D only, then
[ce27e21]485
[d138d43]486        .. math::
[ce27e21]487
[d138d43]488            q = \sqrt{q_x^2+q_y^2}
[ce27e21]489
490        """
[de0c4ba]491        if isinstance(qdist, (list, tuple)):
[ce27e21]492            # Check whether we have a list of ndarrays [qx,qy]
493            qx, qy = qdist
[6d6508e]494            if not self._model_info.parameters.has_2d:
[de0c4ba]495                return self.calculate_Iq(np.sqrt(qx ** 2 + qy ** 2))
[5d4777d]496            else:
497                return self.calculate_Iq(qx, qy)
[ce27e21]498
499        elif isinstance(qdist, np.ndarray):
500            # We have a simple 1D distribution of q-values
501            return self.calculate_Iq(qdist)
502
503        else:
[3c56da87]504            raise TypeError("evalDistribution expects q or [qx, qy], not %r"
505                            % type(qdist))
[ce27e21]506
[fa5fd8d]507    def calculate_Iq(self, qx, qy=None):
508        # type: (Sequence[float], Optional[Sequence[float]]) -> np.ndarray
[ff7119b]509        """
510        Calculate Iq for one set of q with the current parameters.
511
512        If the model is 1D, use *q*.  If 2D, use *qx*, *qy*.
513
514        This should NOT be used for fitting since it copies the *q* vectors
515        to the card for each evaluation.
516        """
[6a0d6aa]517        #core.HAVE_OPENCL = False
[fb5914f]518        if self._model is None:
[d2bb604]519            self._model = core.build_model(self._model_info)
[fa5fd8d]520        if qy is not None:
521            q_vectors = [np.asarray(qx), np.asarray(qy)]
522        else:
523            q_vectors = [np.asarray(qx)]
[a738209]524        calculator = self._model.make_kernel(q_vectors)
[6a0d6aa]525        parameters = self._model_info.parameters
526        pairs = [self._get_weights(p) for p in parameters.call_parameters]
[9eb3632]527        call_details, values, is_magnetic = build_details(calculator, pairs)
[4edec6f]528        #call_details.show()
529        #print("pairs", pairs)
530        #print("params", self.params)
531        #print("values", values)
532        #print("is_mag", is_magnetic)
[6a0d6aa]533        result = calculator(call_details, values, cutoff=self.cutoff,
[9eb3632]534                            magnetic=is_magnetic)
[a738209]535        calculator.release()
[ce27e21]536        return result
537
538    def calculate_ER(self):
[fa5fd8d]539        # type: () -> float
[ce27e21]540        """
541        Calculate the effective radius for P(q)*S(q)
542
543        :return: the value of the effective radius
544        """
[4bfd277]545        if self._model_info.ER is None:
[ce27e21]546            return 1.0
547        else:
[4bfd277]548            value, weight = self._dispersion_mesh()
549            fv = self._model_info.ER(*value)
[9404dd3]550            #print(values[0].shape, weights.shape, fv.shape)
[4bfd277]551            return np.sum(weight * fv) / np.sum(weight)
[ce27e21]552
553    def calculate_VR(self):
[fa5fd8d]554        # type: () -> float
[ce27e21]555        """
556        Calculate the volf ratio for P(q)*S(q)
557
558        :return: the value of the volf ratio
559        """
[4bfd277]560        if self._model_info.VR is None:
[ce27e21]561            return 1.0
562        else:
[4bfd277]563            value, weight = self._dispersion_mesh()
564            whole, part = self._model_info.VR(*value)
565            return np.sum(weight * part) / np.sum(weight * whole)
[ce27e21]566
567    def set_dispersion(self, parameter, dispersion):
[fa5fd8d]568        # type: (str, weights.Dispersion) -> Dict[str, Any]
[ce27e21]569        """
570        Set the dispersion object for a model parameter
571
572        :param parameter: name of the parameter [string]
573        :param dispersion: dispersion object of type Dispersion
574        """
[fa800e72]575        if parameter in self.params:
[1780d59]576            # TODO: Store the disperser object directly in the model.
[56b2687]577            # The current method of relying on the sasview GUI to
[fa800e72]578            # remember them is kind of funky.
[1780d59]579            # Note: can't seem to get disperser parameters from sasview
580            # (1) Could create a sasview model that has not yet # been
581            # converted, assign the disperser to one of its polydisperse
582            # parameters, then retrieve the disperser parameters from the
583            # sasview model.  (2) Could write a disperser parameter retriever
584            # in sasview.  (3) Could modify sasview to use sasmodels.weights
585            # dispersers.
586            # For now, rely on the fact that the sasview only ever uses
587            # new dispersers in the set_dispersion call and create a new
588            # one instead of trying to assign parameters.
589            from . import weights
590            disperser = weights.dispersers[dispersion.__class__.__name__]
[a18c5b3]591            dispersion = weights.MODELS[disperser]()
[ce27e21]592            self.dispersion[parameter] = dispersion.get_pars()
593        else:
594            raise ValueError("%r is not a dispersity or orientation parameter")
595
[aa4946b]596    def _dispersion_mesh(self):
[fa5fd8d]597        # type: () -> List[Tuple[np.ndarray, np.ndarray]]
[ce27e21]598        """
599        Create a mesh grid of dispersion parameters and weights.
600
601        Returns [p1,p2,...],w where pj is a vector of values for parameter j
602        and w is a vector containing the products for weights for each
603        parameter set in the vector.
604        """
[4bfd277]605        pars = [self._get_weights(p)
606                for p in self._model_info.parameters.call_parameters
607                if p.type == 'volume']
[9eb3632]608        return dispersion_mesh(self._model_info, pars)
[ce27e21]609
610    def _get_weights(self, par):
[fa5fd8d]611        # type: (Parameter) -> Tuple[np.ndarray, np.ndarray]
[de0c4ba]612        """
[fb5914f]613        Return dispersion weights for parameter
[de0c4ba]614        """
[fa5fd8d]615        if par.name not in self.params:
616            if par.name == self.multiplicity_info.control:
[4edec6f]617                return [self.multiplicity], [1.0]
[fa5fd8d]618            else:
[4edec6f]619                return [np.NaN], [1.0]
[fa5fd8d]620        elif par.polydisperse:
[fb5914f]621            dis = self.dispersion[par.name]
622            value, weight = weights.get_weights(
623                dis['type'], dis['npts'], dis['width'], dis['nsigmas'],
624                self.params[par.name], par.limits, par.relative_pd)
625            return value, weight / np.sum(weight)
626        else:
[4edec6f]627            return [self.params[par.name]], [1.0]
[ce27e21]628
[fb5914f]629def test_model():
[fa5fd8d]630    # type: () -> float
[4d76711]631    """
632    Test that a sasview model (cylinder) can be run.
633    """
634    Cylinder = _make_standard_model('cylinder')
[fb5914f]635    cylinder = Cylinder()
636    return cylinder.evalDistribution([0.1,0.1])
[de97440]637
[04045f4]638def test_rpa():
639    # type: () -> float
640    """
641    Test that a sasview model (cylinder) can be run.
642    """
643    RPA = _make_standard_model('rpa')
644    rpa = RPA(3)
645    return rpa.evalDistribution([0.1,0.1])
646
[4d76711]647
648def test_model_list():
[fa5fd8d]649    # type: () -> None
[4d76711]650    """
651    Make sure that all models build as sasview models.
652    """
653    from .exception import annotate_exception
654    for name in core.list_models():
655        try:
656            _make_standard_model(name)
657        except:
658            annotate_exception("when loading "+name)
659            raise
660
[fb5914f]661if __name__ == "__main__":
[ea05c87]662    print("cylinder(0.1,0.1)=%g"%test_model())
Note: See TracBrowser for help on using the repository browser.