source: sasmodels/sasmodels/sasview_model.py @ 01ecc31

core_shell_microgelscostrafo411magnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 01ecc31 was 01ecc31, checked in by krzywon, 7 years ago

All old versions of models are run through in sasview_model.

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