source: sasmodels/sasmodels/modelinfo.py @ f6029fd

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

unfortunately, no syntax checking in comment type hints

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