source: sasmodels/sasmodels/core.py @ eafc9fa

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

refactor kernel wrappers to simplify q input handling

  • 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    return model(q_vectors)
121
122def get_weights(info, pars, name):
123    """
124    Generate the distribution for parameter *name* given the parameter values
125    in *pars*.
126
127    Uses "name", "name_pd", "name_pd_type", "name_pd_n", "name_pd_sigma"
128    from the *pars* dictionary for parameter value and parameter dispersion.
129    """
130    relative = name in info['partype']['pd-rel']
131    limits = info['limits'][name]
132    disperser = pars.get(name+'_pd_type', 'gaussian')
133    value = pars.get(name, info['defaults'][name])
134    npts = pars.get(name+'_pd_n', 0)
135    width = pars.get(name+'_pd', 0.0)
136    nsigma = pars.get(name+'_pd_nsigma', 3.0)
137    value, weight = weights.get_weights(
138        disperser, npts, width, nsigma, value, limits, relative)
139    return value, weight / np.sum(weight)
140
141def dispersion_mesh(pars):
142    """
143    Create a mesh grid of dispersion parameters and weights.
144
145    Returns [p1,p2,...],w where pj is a vector of values for parameter j
146    and w is a vector containing the products for weights for each
147    parameter set in the vector.
148    """
149    value, weight = zip(*pars)
150    if len(value) > 1:
151        value = [v.flatten() for v in np.meshgrid(*value)]
152        weight = np.vstack([v.flatten() for v in np.meshgrid(*weight)])
153        weight = np.prod(weight, axis=0)
154    return value, weight
155
156def call_kernel(kernel, pars, cutoff=0):
157    """
158    Call *kernel* returned from :func:`make_kernel` with parameters *pars*.
159
160    *cutoff* is the limiting value for the product of dispersion weights used
161    to perform the multidimensional dispersion calculation more quickly at a
162    slight cost to accuracy. The default value of *cutoff=0* integrates over
163    the entire dispersion cube.  Using *cutoff=1e-5* can be 50% faster, but
164    with an error of about 1%, which is usually less than the measurement
165    uncertainty.
166    """
167    fixed_pars = [pars.get(name, kernel.info['defaults'][name])
168                  for name in kernel.fixed_pars]
169    pd_pars = [get_weights(kernel.info, pars, name) for name in kernel.pd_pars]
170    return kernel(fixed_pars, pd_pars, cutoff=cutoff)
171
172def call_ER(info, pars):
173    """
174    Call the model ER function using *pars*.
175
176    *info* is either *model.info* if you have a loaded model, or *kernel.info*
177    if you have a model kernel prepared for evaluation.
178    """
179    ER = info.get('ER', None)
180    if ER is None:
181        return 1.0
182    else:
183        vol_pars = [get_weights(info, pars, name)
184                    for name in info['partype']['volume']]
185        value, weight = dispersion_mesh(vol_pars)
186        individual_radii = ER(*value)
187        #print(values[0].shape, weights.shape, fv.shape)
188        return np.sum(weight*individual_radii) / np.sum(weight)
189
190def call_VR(info, pars):
191    """
192    Call the model VR function using *pars*.
193
194    *info* is either *model.info* if you have a loaded model, or *kernel.info*
195    if you have a model kernel prepared for evaluation.
196    """
197    VR = info.get('VR', None)
198    if VR is None:
199        return 1.0
200    else:
201        vol_pars = [get_weights(info, pars, name)
202                    for name in info['partype']['volume']]
203        value, weight = dispersion_mesh(vol_pars)
204        whole, part = VR(*value)
205        return np.sum(weight*part)/np.sum(weight*whole)
206
Note: See TracBrowser for help on using the repository browser.