source: sasmodels/sasmodels/sasview_model.py @ 07c8d46

core_shell_microgelscostrafo411magnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 07c8d46 was 790b16bb, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

Merge branch 'ticket-795' of github.com:sasview/sasmodels into ticket-795

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