source: sasmodels/sasmodels/core.py @ cde11f0f

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

make 'fast' a numeric type option as f32 with loose accuracy

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