source: sasmodels/sasmodels/modelinfo.py @ 7ae2b7f

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

still more linting; ignore numpy types

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