source: sasmodels/sasmodels/core.py @ d07c883

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

Merge branch 'master' of github.com:sasview/sasmodels

  • Property mode set to 100644
File size: 7.0 KB
Line 
1"""
2Core model handling routines.
3"""
4
5from os.path import basename, dirname, join as joinpath
6from glob import glob
7
8import numpy as np
9
10from . import models
11from . import weights
12from . import generate
13
14from . import kernelpy
15from . import kerneldll
16try:
17    from . import kernelcl
18    HAVE_OPENCL = True
19except:
20    HAVE_OPENCL = False
21
22__all__ = [
23    "list_models", "load_model_definition", "precompile_dll",
24    "load_model", "make_kernel", "call_kernel", "call_ER", "call_VR",
25]
26
27def list_models():
28    """
29    Return the list of available models on the model path.
30    """
31    root = dirname(__file__)
32    files = sorted(glob(joinpath(root, 'models', "[a-zA-Z]*.py")))
33    available_models = [basename(f)[:-3] for f in files]
34    return available_models
35
36
37def load_model_definition(model_name):
38    """
39    Load a model definition given the model name.
40
41    This returns a handle to the module defining the model.  This can be
42    used with functions in generate to build the docs or extract model info.
43    """
44    __import__('sasmodels.models.'+model_name)
45    model_definition = getattr(models, model_name, None)
46    return model_definition
47
48
49def precompile_dll(model_name, dtype="double"):
50    """
51    Precompile the dll for a model.
52
53    Returns the path to the compiled model.
54
55    This can be used when build the windows distribution of sasmodels
56    (which may be missing the OpenCL driver and the dll compiler), or
57    otherwise sharing models with windows users who do not have a compiler.
58
59    See :func:`sasmodels.kerneldll.make_dll` for details on controlling the
60    dll path and the allowed floating point precision.
61    """
62    model_definition = load_model_definition(model_name)
63    source, info = generate.make(model_definition)
64    return kerneldll.make_dll(source, info, dtype=dtype)
65
66
67def isstr(s):
68    """
69    Return True if *s* is a string-like object.
70    """
71    try: s + ''
72    except: return False
73    return True
74
75def load_model(model_definition, dtype="single", platform="ocl"):
76    """
77    Prepare the model for the default execution platform.
78
79    This will return an OpenCL model, a DLL model or a python model depending
80    on the model and the computing platform.
81
82    *model_definition* is the python module which defines the model.  If the
83    model name is given instead, then :func:`load_model_definition` will be
84    called with the model name.
85
86    *dtype* indicates whether the model should use single or double precision
87    for the calculation. Any valid numpy single or double precision identifier
88    is valid, such as 'single', 'f', 'f32', or np.float32 for single, or
89    'double', 'd', 'f64'  and np.float64 for double.
90
91    *platform* should be "dll" to force the dll to be used for C models,
92    otherwise it uses the default "ocl".
93    """
94    if isstr(model_definition):
95        model_definition = load_model_definition(model_definition)
96    source, info = generate.make(model_definition)
97    if callable(info.get('Iq', None)):
98        return kernelpy.PyModel(info)
99
100    ## for debugging:
101    ##  1. uncomment open().write so that the source will be saved next time
102    ##  2. run "python -m sasmodels.direct_model $MODELNAME" to save the source
103    ##  3. recomment the open.write() and uncomment open().read()
104    ##  4. rerun "python -m sasmodels.direct_model $MODELNAME"
105    ##  5. uncomment open().read() so that source will be regenerated from model
106    # open(info['name']+'.c','w').write(source)
107    # source = open(info['name']+'.cl','r').read()
108
109    if (platform == "dll"
110            or not HAVE_OPENCL
111            or not kernelcl.environment().has_type(dtype)):
112        return kerneldll.load_dll(source, info, dtype)
113    else:
114        return kernelcl.GpuModel(source, info, dtype)
115
116def make_kernel(model, q_vectors):
117    """
118    Return a computation kernel from the model definition and the q input.
119    """
120    model_input = model.make_input(q_vectors)
121    return model(model_input)
122
123def get_weights(info, pars, name):
124    """
125    Generate the distribution for parameter *name* given the parameter values
126    in *pars*.
127
128    Uses "name", "name_pd", "name_pd_type", "name_pd_n", "name_pd_sigma"
129    from the *pars* dictionary for parameter value and parameter dispersion.
130    """
131    relative = name in info['partype']['pd-rel']
132    limits = info['limits'][name]
133    disperser = pars.get(name+'_pd_type', 'gaussian')
134    value = pars.get(name, info['defaults'][name])
135    npts = pars.get(name+'_pd_n', 0)
136    width = pars.get(name+'_pd', 0.0)
137    nsigma = pars.get(name+'_pd_nsigma', 3.0)
138    value, weight = weights.get_weights(
139        disperser, npts, width, nsigma, value, limits, relative)
140    return value, weight / np.sum(weight)
141
142def dispersion_mesh(pars):
143    """
144    Create a mesh grid of dispersion parameters and weights.
145
146    Returns [p1,p2,...],w where pj is a vector of values for parameter j
147    and w is a vector containing the products for weights for each
148    parameter set in the vector.
149    """
150    value, weight = zip(*pars)
151    if len(value) > 1:
152        value = [v.flatten() for v in np.meshgrid(*value)]
153        weight = np.vstack([v.flatten() for v in np.meshgrid(*weight)])
154        weight = np.prod(weight, axis=0)
155    return value, weight
156
157def call_kernel(kernel, pars, cutoff=0):
158    """
159    Call *kernel* returned from :func:`make_kernel` with parameters *pars*.
160
161    *cutoff* is the limiting value for the product of dispersion weights used
162    to perform the multidimensional dispersion calculation more quickly at a
163    slight cost to accuracy. The default value of *cutoff=0* integrates over
164    the entire dispersion cube.  Using *cutoff=1e-5* can be 50% faster, but
165    with an error of about 1%, which is usually less than the measurement
166    uncertainty.
167    """
168    fixed_pars = [pars.get(name, kernel.info['defaults'][name])
169                  for name in kernel.fixed_pars]
170    pd_pars = [get_weights(kernel.info, pars, name) for name in kernel.pd_pars]
171    return kernel(fixed_pars, pd_pars, cutoff=cutoff)
172
173def call_ER(info, pars):
174    """
175    Call the model ER function using *pars*.
176
177    *info* is either *model.info* if you have a loaded model, or *kernel.info*
178    if you have a model kernel prepared for evaluation.
179    """
180    ER = info.get('ER', None)
181    if ER is None:
182        return 1.0
183    else:
184        vol_pars = [get_weights(info, pars, name)
185                    for name in info['partype']['volume']]
186        value, weight = dispersion_mesh(vol_pars)
187        individual_radii = ER(*value)
188        #print(values[0].shape, weights.shape, fv.shape)
189        return np.sum(weight*individual_radii) / np.sum(weight)
190
191def call_VR(info, pars):
192    """
193    Call the model VR function using *pars*.
194
195    *info* is either *model.info* if you have a loaded model, or *kernel.info*
196    if you have a model kernel prepared for evaluation.
197    """
198    VR = info.get('VR', None)
199    if VR is None:
200        return 1.0
201    else:
202        vol_pars = [get_weights(info, pars, name)
203                    for name in info['partype']['volume']]
204        value, weight = dispersion_mesh(vol_pars)
205        whole, part = VR(*value)
206        return np.sum(weight*part)/np.sum(weight*whole)
207
Note: See TracBrowser for help on using the repository browser.