source: sasmodels/sasmodels/core.py @ 190fc2b

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

delint

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