source: sasmodels/sasmodels/core.py @ bcd3aa3

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

simplify core model loader interface so model can be loaded directly from model name

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