source: sasmodels/sasmodels/core.py @ d18582e

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

default to double precision if single=False is set in model file

  • Property mode set to 100644
File size: 7.1 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=None, 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.  If *None*, then use
90    'single' unless the model defines single=False.
91
92    *platform* should be "dll" to force the dll to be used for C models,
93    otherwise it uses the default "ocl".
94    """
95    if isstr(model_definition):
96        model_definition = load_model_definition(model_definition)
97    if dtype is None:
98        dtype = 'single' if getattr(model_definition, 'single', True) else 'double'
99    source, info = generate.make(model_definition)
100    if callable(info.get('Iq', None)):
101        return kernelpy.PyModel(info)
102
103    ## for debugging:
104    ##  1. uncomment open().write so that the source will be saved next time
105    ##  2. run "python -m sasmodels.direct_model $MODELNAME" to save the source
106    ##  3. recomment the open.write() and uncomment open().read()
107    ##  4. rerun "python -m sasmodels.direct_model $MODELNAME"
108    ##  5. uncomment open().read() so that source will be regenerated from model
109    # open(info['name']+'.c','w').write(source)
110    # source = open(info['name']+'.cl','r').read()
111
112    if (platform == "dll"
113            or not HAVE_OPENCL
114            or not kernelcl.environment().has_type(dtype)):
115        return kerneldll.load_dll(source, info, dtype)
116    else:
117        return kernelcl.GpuModel(source, info, dtype)
118
119def make_kernel(model, q_vectors):
120    """
121    Return a computation kernel from the model definition and the q input.
122    """
123    return model(q_vectors)
124
125def get_weights(info, pars, name):
126    """
127    Generate the distribution for parameter *name* given the parameter values
128    in *pars*.
129
130    Uses "name", "name_pd", "name_pd_type", "name_pd_n", "name_pd_sigma"
131    from the *pars* dictionary for parameter value and parameter dispersion.
132    """
133    relative = name in info['partype']['pd-rel']
134    limits = info['limits'][name]
135    disperser = pars.get(name+'_pd_type', 'gaussian')
136    value = pars.get(name, info['defaults'][name])
137    npts = pars.get(name+'_pd_n', 0)
138    width = pars.get(name+'_pd', 0.0)
139    nsigma = pars.get(name+'_pd_nsigma', 3.0)
140    value, weight = weights.get_weights(
141        disperser, npts, width, nsigma, value, limits, relative)
142    return value, weight / np.sum(weight)
143
144def dispersion_mesh(pars):
145    """
146    Create a mesh grid of dispersion parameters and weights.
147
148    Returns [p1,p2,...],w where pj is a vector of values for parameter j
149    and w is a vector containing the products for weights for each
150    parameter set in the vector.
151    """
152    value, weight = zip(*pars)
153    if len(value) > 1:
154        value = [v.flatten() for v in np.meshgrid(*value)]
155        weight = np.vstack([v.flatten() for v in np.meshgrid(*weight)])
156        weight = np.prod(weight, axis=0)
157    return value, weight
158
159def call_kernel(kernel, pars, cutoff=0):
160    """
161    Call *kernel* returned from :func:`make_kernel` with parameters *pars*.
162
163    *cutoff* is the limiting value for the product of dispersion weights used
164    to perform the multidimensional dispersion calculation more quickly at a
165    slight cost to accuracy. The default value of *cutoff=0* integrates over
166    the entire dispersion cube.  Using *cutoff=1e-5* can be 50% faster, but
167    with an error of about 1%, which is usually less than the measurement
168    uncertainty.
169    """
170    fixed_pars = [pars.get(name, kernel.info['defaults'][name])
171                  for name in kernel.fixed_pars]
172    pd_pars = [get_weights(kernel.info, pars, name) for name in kernel.pd_pars]
173    return kernel(fixed_pars, pd_pars, cutoff=cutoff)
174
175def call_ER(info, pars):
176    """
177    Call the model ER function using *pars*.
178
179    *info* is either *model.info* if you have a loaded model, or *kernel.info*
180    if you have a model kernel prepared for evaluation.
181    """
182    ER = info.get('ER', None)
183    if ER is None:
184        return 1.0
185    else:
186        vol_pars = [get_weights(info, pars, name)
187                    for name in info['partype']['volume']]
188        value, weight = dispersion_mesh(vol_pars)
189        individual_radii = ER(*value)
190        #print(values[0].shape, weights.shape, fv.shape)
191        return np.sum(weight*individual_radii) / np.sum(weight)
192
193def call_VR(info, pars):
194    """
195    Call the model VR function using *pars*.
196
197    *info* is either *model.info* if you have a loaded model, or *kernel.info*
198    if you have a model kernel prepared for evaluation.
199    """
200    VR = info.get('VR', None)
201    if VR is None:
202        return 1.0
203    else:
204        vol_pars = [get_weights(info, pars, name)
205                    for name in info['partype']['volume']]
206        value, weight = dispersion_mesh(vol_pars)
207        whole, part = VR(*value)
208        return np.sum(weight*part)/np.sum(weight*whole)
209
Note: See TracBrowser for help on using the repository browser.