source: sasmodels/sasmodels/core.py @ aa4946b

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

refactor so kernels are loaded via core.load_model

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