source: sasmodels/sasmodels/sasview_model.py @ fe8ff99

core_shell_microgelscostrafo411magnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since fe8ff99 was fe8ff99, checked in by GitHub <noreply@…>, 4 years ago

Merge pull request #14 from SasView?/ticket-795

Ticket 795

  • Property mode set to 100644
File size: 27.1 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 get_composition_models(self):
586        """
587            Returns usable models that compose this model
588        """
589        s_model = None
590        p_model = None
591        if hasattr(self._model_info, "composition") \
592           and self._model_info.composition is not None:
593            p_model = _make_model_from_info(self._model_info.composition[1][0])()
594            s_model = _make_model_from_info(self._model_info.composition[1][1])()
595        return p_model, s_model
596
597    def calculate_Iq(self, qx, qy=None):
598        # type: (Sequence[float], Optional[Sequence[float]]) -> np.ndarray
599        """
600        Calculate Iq for one set of q with the current parameters.
601
602        If the model is 1D, use *q*.  If 2D, use *qx*, *qy*.
603
604        This should NOT be used for fitting since it copies the *q* vectors
605        to the card for each evaluation.
606        """
607        #core.HAVE_OPENCL = False
608        if self._model is None:
609            self._model = core.build_model(self._model_info)
610        if qy is not None:
611            q_vectors = [np.asarray(qx), np.asarray(qy)]
612        else:
613            q_vectors = [np.asarray(qx)]
614        calculator = self._model.make_kernel(q_vectors)
615        parameters = self._model_info.parameters
616        pairs = [self._get_weights(p) for p in parameters.call_parameters]
617        #weights.plot_weights(self._model_info, pairs)
618        call_details, values, is_magnetic = make_kernel_args(calculator, pairs)
619        #call_details.show()
620        #print("pairs", pairs)
621        #print("params", self.params)
622        #print("values", values)
623        #print("is_mag", is_magnetic)
624        result = calculator(call_details, values, cutoff=self.cutoff,
625                            magnetic=is_magnetic)
626        calculator.release()
627        self._model.release()
628        return result
629
630    def calculate_ER(self):
631        # type: () -> float
632        """
633        Calculate the effective radius for P(q)*S(q)
634
635        :return: the value of the effective radius
636        """
637        if self._model_info.ER is None:
638            return 1.0
639        else:
640            value, weight = self._dispersion_mesh()
641            fv = self._model_info.ER(*value)
642            #print(values[0].shape, weights.shape, fv.shape)
643            return np.sum(weight * fv) / np.sum(weight)
644
645    def calculate_VR(self):
646        # type: () -> float
647        """
648        Calculate the volf ratio for P(q)*S(q)
649
650        :return: the value of the volf ratio
651        """
652        if self._model_info.VR is None:
653            return 1.0
654        else:
655            value, weight = self._dispersion_mesh()
656            whole, part = self._model_info.VR(*value)
657            return np.sum(weight * part) / np.sum(weight * whole)
658
659    def set_dispersion(self, parameter, dispersion):
660        # type: (str, weights.Dispersion) -> Dict[str, Any]
661        """
662        Set the dispersion object for a model parameter
663
664        :param parameter: name of the parameter [string]
665        :param dispersion: dispersion object of type Dispersion
666        """
667        if parameter in self.params:
668            # TODO: Store the disperser object directly in the model.
669            # The current method of relying on the sasview GUI to
670            # remember them is kind of funky.
671            # Note: can't seem to get disperser parameters from sasview
672            # (1) Could create a sasview model that has not yet been
673            # converted, assign the disperser to one of its polydisperse
674            # parameters, then retrieve the disperser parameters from the
675            # sasview model.
676            # (2) Could write a disperser parameter retriever in sasview.
677            # (3) Could modify sasview to use sasmodels.weights dispersers.
678            # For now, rely on the fact that the sasview only ever uses
679            # new dispersers in the set_dispersion call and create a new
680            # one instead of trying to assign parameters.
681            self.dispersion[parameter] = dispersion.get_pars()
682        else:
683            raise ValueError("%r is not a dispersity or orientation parameter")
684
685    def _dispersion_mesh(self):
686        # type: () -> List[Tuple[np.ndarray, np.ndarray]]
687        """
688        Create a mesh grid of dispersion parameters and weights.
689
690        Returns [p1,p2,...],w where pj is a vector of values for parameter j
691        and w is a vector containing the products for weights for each
692        parameter set in the vector.
693        """
694        pars = [self._get_weights(p)
695                for p in self._model_info.parameters.call_parameters
696                if p.type == 'volume']
697        return dispersion_mesh(self._model_info, pars)
698
699    def _get_weights(self, par):
700        # type: (Parameter) -> Tuple[np.ndarray, np.ndarray]
701        """
702        Return dispersion weights for parameter
703        """
704        if par.name not in self.params:
705            if par.name == self.multiplicity_info.control:
706                return [self.multiplicity], [1.0]
707            else:
708                # For hidden parameters use the default value.
709                value = self._model_info.parameters.defaults.get(par.name, np.NaN)
710                return [value], [1.0]
711        elif par.polydisperse:
712            dis = self.dispersion[par.name]
713            if dis['type'] == 'array':
714                value, weight = dis['values'], dis['weights']
715            else:
716                value, weight = weights.get_weights(
717                    dis['type'], dis['npts'], dis['width'], dis['nsigmas'],
718                    self.params[par.name], par.limits, par.relative_pd)
719            return value, weight / np.sum(weight)
720        else:
721            return [self.params[par.name]], [1.0]
722
723def test_model():
724    # type: () -> float
725    """
726    Test that a sasview model (cylinder) can be run.
727    """
728    Cylinder = _make_standard_model('cylinder')
729    cylinder = Cylinder()
730    return cylinder.evalDistribution([0.1, 0.1])
731
732def test_structure_factor():
733    # type: () -> float
734    """
735    Test that a sasview model (cylinder) can be run.
736    """
737    Model = _make_standard_model('hardsphere')
738    model = Model()
739    value = model.evalDistribution([0.1, 0.1])
740    if np.isnan(value):
741        raise ValueError("hardsphere returns null")
742
743def test_rpa():
744    # type: () -> float
745    """
746    Test that a sasview model (cylinder) can be run.
747    """
748    RPA = _make_standard_model('rpa')
749    rpa = RPA(3)
750    return rpa.evalDistribution([0.1, 0.1])
751
752
753def test_model_list():
754    # type: () -> None
755    """
756    Make sure that all models build as sasview models.
757    """
758    from .exception import annotate_exception
759    for name in core.list_models():
760        try:
761            _make_standard_model(name)
762        except:
763            annotate_exception("when loading "+name)
764            raise
765
766def test_old_name():
767    # type: () -> None
768    """
769    Load and run cylinder model from sas.models.CylinderModel
770    """
771    if not SUPPORT_OLD_STYLE_PLUGINS:
772        return
773    try:
774        # if sasview is not on the path then don't try to test it
775        import sas
776    except ImportError:
777        return
778    load_standard_models()
779    from sas.models.CylinderModel import CylinderModel
780    CylinderModel().evalDistribution([0.1, 0.1])
781
782if __name__ == "__main__":
783    print("cylinder(0.1,0.1)=%g"%test_model())
Note: See TracBrowser for help on using the repository browser.