source: sasmodels/sasmodels/core.py @ b8e5e21

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

had structure_factor, has_2d and variants to info

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