source: sasmodels/sasmodels/modelinfo.py @ a86f6c0

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

optional typing for modelinfo module

  • Property mode set to 100644
File size: 28.2 KB
Line 
1from copy import copy
2from os.path import abspath, basename, splitext
3
4import numpy as np
5
6from .details import mono_details
7
8# Optional typing
9try:
10    from typing import Tuple, List, Union, Dict, Optional, Any, Callable
11except ImportError:
12    pass
13else:
14    from .details import CallDetails
15    Limits = Tuple[float, float]
16    LimitsOrChoice = Union[Limits, Tuple[str]]
17    ParameterDef = Tuple[str, str, float, LimitsOrChoice, str, str]
18    ParameterSetUser = Dict[str, Union[float, List[float]]]
19    ParameterSet = Dict[str, float]
20    TestInput = Union[str, float, List[float], Tuple[float, float], List[Tuple[float, float]]]
21    TestValue = Union[float, List[float]]
22    TestCondition = Tuple[ParameterSetUser, TestInput, TestValue]
23
24MAX_PD = 4
25
26COMMON_PARAMETERS = [
27    ["scale", "", 1, [0, np.inf], "", "Source intensity"],
28    ["background", "1/cm", 1e-3, [0, np.inf], "", "Source background"],
29]
30assert (len(COMMON_PARAMETERS) == 2
31        and COMMON_PARAMETERS[0][0]=="scale"
32        and COMMON_PARAMETERS[1][0]=="background"), "don't change common parameters"
33# assumptions about common parameters exist throughout the code, such as:
34# (1) kernel functions Iq, Iqxy, form_volume, ... don't see them
35# (2) kernel drivers assume scale is par[0] and background is par[1]
36# (3) mixture models drop the background on components and replace the scale
37#     with a scale that varies from [-inf, inf]
38# (4) product models drop the background and reassign scale
39# and maybe other places.
40# Note that scale and background cannot be coordinated parameters whose value
41# depends on the some polydisperse parameter with the current implementation
42
43
44def make_parameter_table(pars):
45    # type: (List[ParameterDefinition) -> ParameterTable
46    processed = []
47    for p in pars:
48        if not isinstance(p, (list, tuple)) or len(p) != 6:
49            raise ValueError("Parameter should be [name, units, default, limits, type, desc], but got %r"
50                             %str(p))
51        processed.append(parse_parameter(*p))
52    partable = ParameterTable(processed)
53    return partable
54
55def parse_parameter(name, units='', default=np.NaN,
56                    limits=(-np.inf, np.inf), ptype='', description=''):
57    # type: (str, str, float, LimitsOrChoice, str, str) -> Parameter
58    # Parameter is a user facing class.  Do robust type checking.
59    if not isstr(name):
60        raise ValueError("expected string for parameter name %r"%name)
61    if not isstr(units):
62        raise ValueError("expected units to be a string for %s"%name)
63    # if limits is a list of strings, then this is a choice list
64    # field, and limits are 1 to length of string list
65    if isinstance(limits, list) and all(isstr(k) for k in limits):
66        choices = limits
67        limits = [0, len(choices)-1]
68    else:
69        choices = []
70    # TODO: maybe allow limits of None for (-inf, inf)
71    try:
72        low, high = limits
73        if not isinstance(low, (int, float)):
74            raise TypeError("low is not numeric")
75        if not isinstance(high, (int, float)):
76            raise TypeError("high is not numeric")
77        if low >= high:
78            raise ValueError("require low < high")
79    except Exception:
80        raise ValueError("invalid limits %s for %s"%(limits, name))
81
82    if not isinstance(default, (int, float)):
83        raise ValueError("expected default %r to be a number for %s"
84                         % (default, name))
85    if default < low or default > high:
86        raise ValueError("default value %r not in range for %s"
87                         % (default, name))
88
89    if ptype not in ("volume", "orientation", "sld", "magnetic", ""):
90        raise ValueError("unexpected type %r for %s" % (ptype, name))
91
92    if not isstr(description):
93        raise ValueError("expected description to be a string")
94
95
96    # Parameter id for name[n] does not include [n]
97    if "[" in name:
98        if not name.endswith(']'):
99            raise ValueError("Expected name[len] for vector parameter %s"%name)
100        pid, ref = name[:-1].split('[', 1)
101        ref = ref.strip()
102    else:
103        pid, ref = name, None
104
105
106    # automatically identify sld types
107    if ptype== '' and (pid.startswith('sld') or pid.endswith('sld')):
108        ptype = 'sld'
109
110    # Check if using a vector definition, name[k], as the parameter name
111    if ref:
112        if ref == '':
113            raise ValueError("Need to specify vector length for %s"%name)
114        try:
115            length = int(ref)
116            control = None
117        except ValueError:
118            length = None
119            control = ref
120    else:
121        length = 1
122        control = None
123
124    # Build the parameter
125    parameter = Parameter(name=name, units=units, default=default,
126                          limits=limits, ptype=ptype, description=description)
127
128    # TODO: need better control over whether a parameter is polydisperse
129    parameter.polydisperse = ptype in ('orientation', 'volume')
130    parameter.relative_pd = ptype == 'volume'
131    parameter.choices = choices
132    parameter.length = length
133    parameter.length_control = control
134
135    return parameter
136
137
138def expand_pars(partable, pars):
139    # type: (ParameterTable, ParameterSetUser) ->  ParameterSet
140    """
141    Create demo parameter set from key-value pairs.
142
143    *pars* are the key-value pairs to use for the parameters.  Any
144    parameters not specified in *pars* are set from the *partable* defaults.
145
146    If *pars* references vector fields, such as thickness[n], then support
147    different ways of assigning the demo values, including assigning a
148    specific value (e.g., thickness3=50.0), assigning a new value to all
149    (e.g., thickness=50.0) or assigning values using list notation.
150    """
151    if pars is None:
152        result = partable.defaults
153    else:
154        lookup = dict((p.id, p) for p in partable.kernel_parameters)
155        result = partable.defaults.copy()
156        vectors = dict((name,value) for name,value in pars.items()
157                       if name in lookup and lookup[name].length > 1)
158        if vectors:
159            scalars = dict((name, value) for name, value in pars.items()
160                           if name not in lookup or lookup[name].length == 1)
161            for name, value in vectors.items():
162                if np.isscalar(value):
163                    # support for the form
164                    #    dict(thickness=0, thickness2=50)
165                    for k in range(1, lookup[name].length+1):
166                        key = name+str(k)
167                        if key not in scalars:
168                            scalars[key] = vectors
169                else:
170                    # supoprt for the form
171                    #    dict(thickness=[20,10,3])
172                    for (k,v) in enumerate(value):
173                        scalars[name+str(k)] = v
174            result.update(scalars)
175        else:
176            result.update(pars)
177
178    return result
179
180def prefix_parameter(par, prefix):
181    # type: (Parameter, str) -> Parameter
182    """
183    Return a copy of the parameter with its name prefixed.
184    """
185    new_par = copy(par)
186    new_par.name = prefix + par.name
187    new_par.id = prefix + par.id
188
189def suffix_parameter(par, suffix):
190    # type: (Parameter, str) -> Parameter
191    """
192    Return a copy of the parameter with its name prefixed.
193    """
194    new_par = copy(par)
195    # If name has the form x[n], replace with x_suffix[n]
196    new_par.name = par.id + suffix + par.name[len(par.id):]
197    new_par.id = par.id + suffix
198
199class Parameter(object):
200    """
201    The available kernel parameters are defined as a list, with each parameter
202    defined as a sublist with the following elements:
203
204    *name* is the name that will be used in the call to the kernel
205    function and the name that will be displayed to the user.  Names
206    should be lower case, with words separated by underscore.  If
207    acronyms are used, the whole acronym should be upper case.
208
209    *units* should be one of *degrees* for angles, *Ang* for lengths,
210    *1e-6/Ang^2* for SLDs.
211
212    *default value* will be the initial value for  the model when it
213    is selected, or when an initial value is not otherwise specified.
214
215    *limits = [lb, ub]* are the hard limits on the parameter value, used to
216    limit the polydispersity density function.  In the fit, the parameter limits
217    given to the fit are the limits  on the central value of the parameter.
218    If there is polydispersity, it will evaluate parameter values outside
219    the fit limits, but not outside the hard limits specified in the model.
220    If there are no limits, use +/-inf imported from numpy.
221
222    *type* indicates how the parameter will be used.  "volume" parameters
223    will be used in all functions.  "orientation" parameters will be used
224    in *Iqxy* and *Imagnetic*.  "magnetic* parameters will be used in
225    *Imagnetic* only.  If *type* is the empty string, the parameter will
226    be used in all of *Iq*, *Iqxy* and *Imagnetic*.  "sld" parameters
227    can automatically be promoted to magnetic parameters, each of which
228    will have a magnitude and a direction, which may be different from
229    other sld parameters. The volume parameters are used for calls
230    to form_volume within the kernel (required for volume normalization)
231    and for calls to ER and VR for effective radius and volume ratio
232    respectively.
233
234    *description* is a short description of the parameter.  This will
235    be displayed in the parameter table and used as a tool tip for the
236    parameter value in the user interface.
237
238    Additional values can be set after the parameter is created:
239
240    * *length* is the length of the field if it is a vector field
241
242    * *length_control* is the parameter which sets the vector length
243
244    * *is_control* is True if the parameter is a control parameter for a vector
245
246    * *polydisperse* is true if the parameter accepts a polydispersity
247
248    * *relative_pd* is true if that polydispersity is a portion of the
249    value (so a 10% length dipsersity would use a polydispersity value of 0.1)
250    rather than absolute dispersisity (such as an angle plus or minus
251    15 degrees).
252
253    In the usual process these values are set by :func:`make_parameter_table`
254    and :func:`parse_parameter` therein.
255    """
256    def __init__(self, name, units='', default=None, limits=(-np.inf, np.inf),
257                 ptype='', description=''):
258        # type: (str, str, float, Limits, str, str)
259        self.id = name.split('[')[0].strip() # type: str
260        self.name = name                     # type: str
261        self.units = units                   # type: str
262        self.default = default               # type: float
263        self.limits = limits                 # type: Limits
264        self.type = ptype                    # type: str
265        self.description = description       # type: str
266
267        # Length and length_control will be filled in once the complete
268        # parameter table is available.
269        self.length = 1                      # type: int
270        self.length_control = None           # type: Optional[str]
271        self.is_control = False              # type: bool
272
273        # TODO: need better control over whether a parameter is polydisperse
274        self.polydisperse = False            # type: bool
275        self.relative_pd = False             # type: bool
276
277        # choices are also set externally.
278        self.choices = []                    # type: List[str]
279
280    def as_definition(self):
281        # type: () -> str
282        """
283        Declare space for the variable in a parameter structure.
284
285        For example, the parameter thickness with length 3 will
286        return "double thickness[3];", with no spaces before and
287        no new line character afterward.
288        """
289        if self.length == 1:
290            return "double %s;"%self.id
291        else:
292            return "double %s[%d];"%(self.id, self.length)
293
294    def as_function_argument(self):
295        # type: () -> str
296        """
297        Declare the variable as a function argument.
298
299        For example, the parameter thickness with length 3 will
300        return "double *thickness", with no spaces before and
301        no comma afterward.
302        """
303        if self.length == 1:
304            return "double %s"%self.id
305        else:
306            return "double *%s"%self.id
307
308    def as_call_reference(self, prefix=""):
309        # type: () -> str
310        # Note: if the parameter is a struct type, then we will need to use
311        # &prefix+id.  For scalars and vectors we can just use prefix+id.
312        return prefix + self.id
313
314    def __str__(self):
315        # type: () -> str
316        return "<%s>"%self.name
317
318    def __repr__(self):
319        # type: () -> str
320        return "P<%s>"%self.name
321
322
323class ParameterTable(object):
324    """
325    ParameterTable manages the list of available parameters.
326
327    There are a couple of complications which mean that the list of parameters
328    for the kernel differs from the list of parameters that the user sees.
329
330    (1) Common parameters.  Scale and background are implicit to every model,
331    but are not passed to the kernel.
332
333    (2) Vector parameters.  Vector parameters are passed to the kernel as a
334    pointer to an array, e.g., thick[], but they are seen by the user as n
335    separate parameters thick1, thick2, ...
336
337    Therefore, the parameter table is organized by how it is expected to be
338    used. The following information is needed to set up the kernel functions:
339
340    * *kernel_parameters* is the list of parameters in the kernel parameter
341    table, with vector parameter p declared as p[].
342
343    * *iq_parameters* is the list of parameters to the Iq(q, ...) function,
344    with vector parameter p sent as p[].
345
346    * *iqxy_parameters* is the list of parameters to the Iqxy(qx, qy, ...)
347    function, with vector parameter p sent as p[].
348
349    * *form_volume_parameters* is the list of parameters to the form_volume(...)
350    function, with vector parameter p sent as p[].
351
352    Problem details, which sets up the polydispersity loops, requires the
353    following:
354
355    * *theta_offset* is the offset of the theta parameter in the kernel parameter
356    table, with vector parameters counted as n individual parameters
357    p1, p2, ..., or offset is -1 if there is no theta parameter.
358
359    * *max_pd* is the maximum number of polydisperse parameters, with vector
360    parameters counted as n individual parameters p1, p2, ...  Note that
361    this number is limited to sasmodels.modelinfo.MAX_PD.
362
363    * *npars* is the total number of parameters to the kernel, with vector
364    parameters counted as n individual parameters p1, p2, ...
365
366    * *call_parameters* is the complete list of parameters to the kernel,
367    including scale and background, with vector parameters recorded as
368    individual parameters p1, p2, ...
369
370    * *active_1d* is the set of names that may be polydisperse for 1d data
371
372    * *active_2d* is the set of names that may be polydisperse for 2d data
373
374    User parameters are the set of parameters visible to the user, including
375    the scale and background parameters that the kernel does not see.  User
376    parameters don't use vector notation, and instead use p1, p2, ...
377
378    * *control_parameters* is the
379
380    """
381    # scale and background are implicit parameters
382    COMMON = [Parameter(*p) for p in COMMON_PARAMETERS]
383
384    def __init__(self, parameters):
385        # type: (List[Parameter]) -> None
386        self.kernel_parameters = parameters
387        self._set_vector_lengths()
388        self.call_parameters = self._get_call_parameters()
389        self.defaults = self._get_defaults()
390        #self._name_table= dict((p.id, p) for p in parameters)
391
392        # Set the kernel parameters.  Assumes background and scale are the
393        # first two parameters in the parameter list, but these are not sent
394        # to the underlying kernel functions.
395        self.iq_parameters = [p for p in self.kernel_parameters
396                              if p.type not in ('orientation', 'magnetic')]
397        self.iqxy_parameters = [p for p in self.kernel_parameters
398                                if p.type != 'magnetic']
399        self.form_volume_parameters = [p for p in self.kernel_parameters
400                                       if p.type == 'volume']
401
402        # Theta offset
403        offset = 0
404        for p in self.kernel_parameters:
405            if p.name == 'theta':
406                self.theta_offset = offset
407                break
408            offset += p.length
409        else:
410            self.theta_offset = -1
411
412        # number of polydisperse parameters
413        num_pd = sum(p.length for p in self.kernel_parameters if p.polydisperse)
414        # Don't use more polydisperse parameters than are available in the model
415        # Note: we can do polydispersity on arbitrary parameters, so it is not
416        # clear that this is a good idea; it does however make the poly_details
417        # code easier to write, so we will leave it in for now.
418        self.max_pd = min(num_pd, MAX_PD)
419
420        self.npars = sum(p.length for p in self.kernel_parameters)
421
422        # true if has 2D parameters
423        self.has_2d = any(p.type in ('orientation', 'magnetic')
424                          for p in self.kernel_parameters)
425
426        self.pd_1d = set(p.name for p in self.call_parameters
427                         if p.polydisperse and p.type not in ('orientation', 'magnetic'))
428        self.pd_2d = set(p.name for p in self.call_parameters
429                         if p.polydisperse and p.type != 'magnetic')
430
431
432    def _set_vector_lengths(self):
433        # type: () -> None
434        """
435        Walk the list of kernel parameters, setting the length field of the
436        vector parameters from the upper limit of the reference parameter.
437
438        This needs to be done once the entire parameter table is available
439        since the reference may still be undefined when the parameter is
440        initially created.
441
442        Note: This modifies the underlying parameter object.
443        """
444        # Sort out the length of the vector parameters such as thickness[n]
445        for p in self.kernel_parameters:
446            if p.length_control:
447                for ref in self.kernel_parameters:
448                    if ref.id == p.length_control:
449                        break
450                else:
451                    raise ValueError("no reference variable %r for %s"
452                                     % (p.length_control, p.name))
453                ref.is_control = True
454                low, high = ref.limits
455                if int(low) != low or int(high) != high or low < 0 or high > 20:
456                    raise ValueError("expected limits on %s to be within [0, 20]"
457                                     % ref.name)
458                # TODO: may want to make a copy of the parameter before updating
459                # this introduces other potential problems, since the same
460                # parameter may be referenced elsewhere
461                p.length = high
462
463    def _get_defaults(self):
464        # type: () -> ParameterSet
465        """
466        Get a list of parameter defaults from the parameters.
467
468        Expands vector parameters into parameter id+number.
469        """
470        # Construct default values, including vector defaults
471        defaults = {}
472        for p in self.call_parameters:
473            if p.length == 1:
474                defaults[p.id] = p.default
475            else:
476                for k in range(1, p.length+1):
477                    defaults["%s%d"%(p.id, k)] = p.default
478        return defaults
479
480    def _get_call_parameters(self):
481        # type: () -> List[Parameter]
482        full_list = self.COMMON[:]
483        for p in self.kernel_parameters:
484            if p.length == 1:
485                full_list.append(p)
486            else:
487                for k in range(1, p.length+1):
488                    pk = Parameter(p.id+str(k), p.units, p.default,
489                                   p.limits, p.type, p.description)
490                    pk.polydisperse = p.polydisperse
491                    pk.relative_pd = p.relative_pd
492                    full_list.append(pk)
493        return full_list
494
495    def user_parameters(self, pars={}, is2d=True):
496        # type: (Dict[str, float], bool) -> List[str]
497        """
498        Return the list of parameters for the given data type.
499
500        Vector parameters are expanded as in place.  If multiple parameters
501        share the same vector length, then the parameters will be interleaved
502        in the result.  The control parameters come first.  For example,
503        if the parameter table is ordered as::
504
505            sld_core
506            sld_shell[num_shells]
507            sld_solvent
508            thickness[num_shells]
509            num_shells
510
511        and *pars[num_shells]=2* then the returned list will be::
512
513            num_shells
514            scale
515            background
516            sld_core
517            sld_shell1
518            thickness1
519            sld_shell2
520            thickness2
521            sld_solvent
522
523        Note that shell/thickness pairs are grouped together in the result
524        even though they were not grouped in the incoming table.  The control
525        parameter is always returned first since the GUI will want to set it
526        early, and rerender the table when it is changed.
527        """
528        control = [p for p in self.kernel_parameters if p.is_control]
529
530        # Gather entries such as name[n] into groups of the same n
531        dependent = dict((p.id, []) for p in control)
532        for p in self.kernel_parameters:
533            if p.length_control is not None:
534                dependent[p.length_control].append(p)
535
536        # Gather entries such as name[4] into groups of the same length
537        fixed = {}
538        for p in self.kernel_parameters:
539            if p.length > 1 and p.length_control is None:
540                fixed.setdefault(p.length, []).append(p)
541
542        # Using the call_parameters table, we already have expanded forms
543        # for each of the vector parameters; put them in a lookup table
544        expanded_pars = dict((p.name, p) for p in self.call_parameters)
545
546        # Gather the user parameters in order
547        result = control + self.COMMON
548        for p in self.kernel_parameters:
549            if not is2d and p.type in ('orientation', 'magnetic'):
550                pass
551            elif p.is_control:
552                pass # already added
553            elif p.length_control is not None:
554                table = dependent.get(p.length_control, [])
555                if table:
556                    # look up length from incoming parameters
557                    table_length = int(pars.get(p.length_control, p.length))
558                    del dependent[p.length_control] # first entry seen
559                    for k in range(1, table_length+1):
560                        for entry in table:
561                            result.append(expanded_pars[entry.id+str(k)])
562                else:
563                    pass # already processed all entries
564            elif p.length > 1:
565                table = fixed.get(p.length, [])
566                if table:
567                    table_length = p.length
568                    del fixed[p.length]
569                    for k in range(1, table_length+1):
570                        for entry in table:
571                            result.append(expanded_pars[entry.id+str(k)])
572                else:
573                    pass # already processed all entries
574            else:
575                result.append(p)
576
577        return result
578
579
580
581def isstr(x):
582    # type: (Any) -> bool
583    # TODO: 2-3 compatible tests for str, including unicode strings
584    return isinstance(x, str)
585
586def make_model_info(kernel_module):
587    info = ModelInfo()
588    #print("make parameter table", kernel_module.parameters)
589    parameters = make_parameter_table(kernel_module.parameters)
590    demo = expand_pars(parameters, getattr(kernel_module, 'demo', None))
591    filename = abspath(kernel_module.__file__)
592    kernel_id = splitext(basename(filename))[0]
593    name = getattr(kernel_module, 'name', None)
594    if name is None:
595        name = " ".join(w.capitalize() for w in kernel_id.split('_'))
596
597    info.id = kernel_id  # string used to load the kernel
598    info.filename = abspath(kernel_module.__file__)
599    info.name = name
600    info.title = getattr(kernel_module, 'title', name+" model")
601    info.description = getattr(kernel_module, 'description', 'no description')
602    info.parameters = parameters
603    info.demo = demo
604    info.composition = None
605    info.docs = kernel_module.__doc__
606    info.category = getattr(kernel_module, 'category', None)
607    info.single = getattr(kernel_module, 'single', True)
608    info.structure_factor = getattr(kernel_module, 'structure_factor', False)
609    info.profile_axes = getattr(kernel_module, 'profile_axes', ['x','y'])
610    info.variant_info = getattr(kernel_module, 'variant_info', None)
611    info.source = getattr(kernel_module, 'source', [])
612    info.tests = getattr(kernel_module, 'tests', [])
613    info.ER = getattr(kernel_module, 'ER', None)
614    info.VR = getattr(kernel_module, 'VR', None)
615    info.form_volume = getattr(kernel_module, 'form_volume', None)
616    info.Iq = getattr(kernel_module, 'Iq', None)
617    info.Iqxy = getattr(kernel_module, 'Iqxy', None)
618    info.profile = getattr(kernel_module, 'profile', None)
619    info.sesans = getattr(kernel_module, 'sesans', None)
620
621    # Precalculate the monodisperse parameter details
622    info.mono_details = mono_details(info)
623    return info
624
625class ModelInfo(object):
626    """
627    Interpret the model definition file, categorizing the parameters.
628
629    The module can be loaded with a normal python import statement if you
630    know which module you need, or with __import__('sasmodels.model.'+name)
631    if the name is in a string.
632
633    The *model_info* structure contains the following fields:
634
635    * *id* is the id of the kernel
636    * *name* is the display name of the kernel
637    * *filename* is the full path to the module defining the file (if any)
638    * *title* is a short description of the kernel
639    * *description* is a long description of the kernel (this doesn't seem
640      very useful since the Help button on the model page brings you directly
641      to the documentation page)
642    * *docs* is the docstring from the module.  Use :func:`make_doc` to
643    * *category* specifies the model location in the docs
644    * *parameters* is the model parameter table
645    * *single* is True if the model allows single precision
646    * *structure_factor* is True if the model is useable in a product
647    * *variant_info* contains the information required to select between
648      model variants (e.g., the list of cases) or is None if there are no
649      model variants
650    * *par_type* categorizes the model parameters. See
651      :func:`categorize_parameters` for details.
652    * *demo* contains the *{parameter: value}* map used in compare (and maybe
653      for the demo plot, if plots aren't set up to use the default values).
654      If *demo* is not given in the file, then the default values will be used.
655    * *tests* is a set of tests that must pass
656    * *source* is the list of library files to include in the C model build
657    * *Iq*, *Iqxy*, *form_volume*, *ER*, *VR* and *sesans* are python functions
658      implementing the kernel for the module, or None if they are not
659      defined in python
660    * *composition* is None if the model is independent, otherwise it is a
661      tuple with composition type ('product' or 'mixture') and a list of
662      *model_info* blocks for the composition objects.  This allows us to
663      build complete product and mixture models from just the info.
664    """
665    id = None               # type: str
666    filename = None         # type: str
667    name = None             # type: str
668    title = None            # type: str
669    description = None      # type: str
670    parameters = None       # type: ParameterTable
671    demo = None             # type: Dict[str, float]
672    composition = None      # type: Optional[Tuple[str, List[ModelInfo]]]
673    docs = None             # type: str
674    category = None         # type: Optional[str]
675    single = None           # type: bool
676    structure_factor = None # type: bool
677    profile_axes = None     # type: Tuple[str, str]
678    variant_info = None     # type: Optional[List[str]]
679    source = None           # type: List[str]
680    tests = None            # type: List[TestCondition]
681    ER = None               # type: Optional[Callable[[np.ndarray, ...], float]]
682    VR = None               # type: Optional[Callable[[np.ndarray, ...], float]]
683    form_volume = None      # type: Optional[Callable[[np.ndarray, ...], float]]
684    Iq = None               # type: Optional[Callable[[np.ndarray, ...], np.ndarray]]
685    Iqxy = None             # type: Optional[Callable[[np.ndarray, ...], np.ndarray]]
686    profile = None
687    sesans = None
688    mono_details = None     # type: CallDetails
689
690    def __init__(self):
691        # type: () -> None
692        pass
693
694
Note: See TracBrowser for help on using the repository browser.