source: sasmodels/sasmodels/core.py @ 7bb290c

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

refactor compare.py so that bumps/sasview not required for simple tests

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