source: sasmodels/sasmodels/core.py @ f49675c

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since f49675c was f49675c, checked in by wojciech, 8 years ago

No GPU forcing through opencl=True

  • Property mode set to 100644
File size: 9.2 KB
RevLine 
[aa4946b]1"""
2Core model handling routines.
3"""
[6d6508e]4from __future__ import print_function
5
[98f60fc]6__all__ = [
[6d6508e]7    "list_models", "load_model", "load_model_info",
[2547694]8    "build_model", "precompile_dlls",
[98f60fc]9    ]
[f734e7d]10
[7bf4757]11import os
[2547694]12from os.path import basename, dirname, join as joinpath
[f734e7d]13from glob import glob
14
[7ae2b7f]15import numpy as np # type: ignore
[f734e7d]16
[aa4946b]17from . import generate
[6d6508e]18from . import modelinfo
19from . import product
[72a081d]20from . import mixture
[aa4946b]21from . import kernelpy
22from . import kerneldll
[f734e7d]23try:
[aa4946b]24    from . import kernelcl
25    HAVE_OPENCL = True
[ee8f734]26except Exception:
[aa4946b]27    HAVE_OPENCL = False
28
[f619de7]29try:
30    from typing import List, Union, Optional, Any
31    from .kernel import KernelModel
[dd7fc12]32    from .modelinfo import ModelInfo
[f619de7]33except ImportError:
34    pass
35
[4d76711]36# TODO: refactor composite model support
37# The current load_model_info/build_model does not reuse existing model
38# definitions when loading a composite model, instead reloading and
39# rebuilding the kernel for each component model in the expression.  This
40# is fine in a scripting environment where the model is built when the script
41# starts and is thrown away when the script ends, but may not be the best
42# solution in a long-lived application.  This affects the following functions:
43#
44#    load_model
45#    load_model_info
46#    build_model
[f734e7d]47
[8407d8c]48KINDS = ("all", "py", "c", "double", "single", "opencl", "1d", "2d",
[2547694]49         "nonmagnetic", "magnetic")
[0b03001]50def list_models(kind=None):
[f619de7]51    # type: () -> List[str]
[aa4946b]52    """
53    Return the list of available models on the model path.
[40a87fa]54
55    *kind* can be one of the following:
56
57        * all: all models
58        * py: python models only
59        * c: compiled models only
60        * single: models which support single precision
61        * double: models which require double precision
[8407d8c]62        * opencl: controls if OpenCL is supperessed
[40a87fa]63        * 1d: models which are 1D only, or 2D using abs(q)
64        * 2d: models which can be 2D
65        * magnetic: models with an sld
66        * nommagnetic: models without an sld
[aa4946b]67    """
[0b03001]68    if kind and kind not in KINDS:
[2547694]69        raise ValueError("kind not in " + ", ".join(KINDS))
[3d9001f]70    files = sorted(glob(joinpath(generate.MODEL_PATH, "[a-zA-Z]*.py")))
[f734e7d]71    available_models = [basename(f)[:-3] for f in files]
[0b03001]72    selected = [name for name in available_models if _matches(name, kind)]
73
74    return selected
75
76def _matches(name, kind):
[2547694]77    if kind is None or kind == "all":
[0b03001]78        return True
79    info = load_model_info(name)
80    pars = info.parameters.kernel_parameters
81    if kind == "py" and callable(info.Iq):
82        return True
83    elif kind == "c" and not callable(info.Iq):
84        return True
85    elif kind == "double" and not info.single:
86        return True
[d2d6100]87    elif kind == "single" and info.single:
88        return True
[8407d8c]89    elif kind == "opencl" and info.opencl:
[407bf48]90        return True
[2547694]91    elif kind == "2d" and any(p.type == 'orientation' for p in pars):
[d2d6100]92        return True
[40a87fa]93    elif kind == "1d" and all(p.type != 'orientation' for p in pars):
[0b03001]94        return True
[2547694]95    elif kind == "magnetic" and any(p.type == 'sld' for p in pars):
[0b03001]96        return True
[2547694]97    elif kind == "nonmagnetic" and any(p.type != 'sld' for p in pars):
[d2d6100]98        return True
[0b03001]99    return False
[f734e7d]100
[f619de7]101def load_model(model_name, dtype=None, platform='ocl'):
[dd7fc12]102    # type: (str, str, str) -> KernelModel
[b8e5e21]103    """
104    Load model info and build model.
[f619de7]105
106    *model_name* is the name of the model as used by :func:`load_model_info`.
107    Additional keyword arguments are passed directly to :func:`build_model`.
[b8e5e21]108    """
[f619de7]109    return build_model(load_model_info(model_name),
110                       dtype=dtype, platform=platform)
[aa4946b]111
112
[17bbadd]113def load_model_info(model_name):
[f619de7]114    # type: (str) -> modelinfo.ModelInfo
[aa4946b]115    """
116    Load a model definition given the model name.
[1d4017a]117
118    This returns a handle to the module defining the model.  This can be
119    used with functions in generate to build the docs or extract model info.
[aa4946b]120    """
[72a081d]121    parts = model_name.split('+')
122    if len(parts) > 1:
123        model_info_list = [load_model_info(p) for p in parts]
124        return mixture.make_mixture_info(model_info_list)
125
126    parts = model_name.split('*')
127    if len(parts) > 1:
128        if len(parts) > 2:
129            raise ValueError("use P*S to apply structure factor S to model P")
130        P_info, Q_info = [load_model_info(p) for p in parts]
131        return product.make_product_info(P_info, Q_info)
132
[68e7f9d]133    kernel_module = generate.load_kernel_module(model_name)
[6d6508e]134    return modelinfo.make_model_info(kernel_module)
[d19962c]135
136
[17bbadd]137def build_model(model_info, dtype=None, platform="ocl"):
[dd7fc12]138    # type: (modelinfo.ModelInfo, str, str) -> KernelModel
[aa4946b]139    """
140    Prepare the model for the default execution platform.
141
142    This will return an OpenCL model, a DLL model or a python model depending
143    on the model and the computing platform.
144
[17bbadd]145    *model_info* is the model definition structure returned from
146    :func:`load_model_info`.
[bcd3aa3]147
[aa4946b]148    *dtype* indicates whether the model should use single or double precision
[dd7fc12]149    for the calculation.  Choices are 'single', 'double', 'quad', 'half',
150    or 'fast'.  If *dtype* ends with '!', then force the use of the DLL rather
151    than OpenCL for the calculation.
[aa4946b]152
153    *platform* should be "dll" to force the dll to be used for C models,
154    otherwise it uses the default "ocl".
155    """
[6d6508e]156    composition = model_info.composition
[72a081d]157    if composition is not None:
158        composition_type, parts = composition
159        models = [build_model(p, dtype=dtype, platform=platform) for p in parts]
160        if composition_type == 'mixture':
161            return mixture.MixtureModel(model_info, models)
162        elif composition_type == 'product':
[e79f0a5]163            P, S = models
[72a081d]164            return product.ProductModel(model_info, P, S)
165        else:
166            raise ValueError('unknown mixture type %s'%composition_type)
[aa4946b]167
[fa5fd8d]168    # If it is a python model, return it immediately
169    if callable(model_info.Iq):
170        return kernelpy.PyModel(model_info)
171
[def2c1b]172    numpy_dtype, fast, platform = parse_dtype(model_info, dtype, platform)
[7891a2a]173
[72a081d]174    source = generate.make_source(model_info)
[7891a2a]175    if platform == "dll":
[f2f67a6]176        #print("building dll", numpy_dtype)
[a4280bd]177        return kerneldll.load_dll(source['dll'], model_info, numpy_dtype)
[aa4946b]178    else:
[f2f67a6]179        #print("building ocl", numpy_dtype)
[dd7fc12]180        return kernelcl.GpuModel(source, model_info, numpy_dtype, fast=fast)
[aa4946b]181
[7bf4757]182def precompile_dlls(path, dtype="double"):
[7891a2a]183    # type: (str, str) -> List[str]
[b8e5e21]184    """
[7bf4757]185    Precompile the dlls for all builtin models, returning a list of dll paths.
[b8e5e21]186
[7bf4757]187    *path* is the directory in which to save the dlls.  It will be created if
188    it does not already exist.
[b8e5e21]189
190    This can be used when build the windows distribution of sasmodels
[7bf4757]191    which may be missing the OpenCL driver and the dll compiler.
[b8e5e21]192    """
[7891a2a]193    numpy_dtype = np.dtype(dtype)
[7bf4757]194    if not os.path.exists(path):
195        os.makedirs(path)
196    compiled_dlls = []
197    for model_name in list_models():
198        model_info = load_model_info(model_name)
[a4280bd]199        if not callable(model_info.Iq):
200            source = generate.make_source(model_info)['dll']
[7bf4757]201            old_path = kerneldll.DLL_PATH
202            try:
203                kerneldll.DLL_PATH = path
[def2c1b]204                dll = kerneldll.make_dll(source, model_info, dtype=numpy_dtype)
[7bf4757]205            finally:
206                kerneldll.DLL_PATH = old_path
207            compiled_dlls.append(dll)
208    return compiled_dlls
[b8e5e21]209
[7891a2a]210def parse_dtype(model_info, dtype=None, platform=None):
211    # type: (ModelInfo, str, str) -> (np.dtype, bool, str)
[dd7fc12]212    """
213    Interpret dtype string, returning np.dtype and fast flag.
214
215    Possible types include 'half', 'single', 'double' and 'quad'.  If the
216    type is 'fast', then this is equivalent to dtype 'single' with the
217    fast flag set to True.
218    """
[7891a2a]219    # Assign default platform, overriding ocl with dll if OpenCL is unavailable
[8407d8c]220    # If opencl=False OpenCL is switched off
[f49675c]221    # Finally one can force OpenCL calculation even if HAVE_OPENCL=False
222    if platform is None:
[7891a2a]223        platform = "ocl"
[8407d8c]224    if platform == "ocl" and not HAVE_OPENCL or not model_info.opencl:
[7891a2a]225        platform = "dll"
[f49675c]226    #if  model_info.opencl and not HAVE_OPENCL:
227    #    platform = "ocl"
[7891a2a]228
229    # Check if type indicates dll regardless of which platform is given
230    if dtype is not None and dtype.endswith('!'):
231        platform = "dll"
[dd7fc12]232        dtype = dtype[:-1]
233
[7891a2a]234    # Convert special type names "half", "fast", and "quad"
[2547694]235    fast = (dtype == "fast")
[7891a2a]236    if fast:
237        dtype = "single"
[2547694]238    elif dtype == "quad":
[7891a2a]239        dtype = "longdouble"
[2547694]240    elif dtype == "half":
[7891a2a]241        dtype = "f16"
242
243    # Convert dtype string to numpy dtype.
244    if dtype is None:
[2547694]245        numpy_dtype = (generate.F32 if platform == "ocl" and model_info.single
246                       else generate.F64)
[dd7fc12]247    else:
[7891a2a]248        numpy_dtype = np.dtype(dtype)
[9890053]249
[7891a2a]250    # Make sure that the type is supported by opencl, otherwise use dll
[2547694]251    if platform == "ocl":
[7891a2a]252        env = kernelcl.environment()
253        if not env.has_type(numpy_dtype):
254            platform = "dll"
255            if dtype is None:
256                numpy_dtype = generate.F64
[dd7fc12]257
[7891a2a]258    return numpy_dtype, fast, platform
[0c24a82]259
[2547694]260def list_models_main():
[40a87fa]261    # type: () -> None
262    """
263    Run list_models as a main program.  See :func:`list_models` for the
264    kinds of models that can be requested on the command line.
265    """
[0b03001]266    import sys
267    kind = sys.argv[1] if len(sys.argv) > 1 else "all"
268    print("\n".join(list_models(kind)))
[2547694]269
270if __name__ == "__main__":
271    list_models_main()
Note: See TracBrowser for help on using the repository browser.