r""" C types wrapper for sasview models. The global attribute *ALLOW_SINGLE_PRECISION_DLLS* should be set to *True* if you wish to allow single precision floating point evaluation for the compiled models, otherwise it defaults to *False*. The compiler command line is stored in the attribute *COMPILE*, with string substitutions for %(source)s and %(output)s indicating what to compile and where to store it. The actual command is system dependent. On windows systems, you have a choice of compilers. *MinGW* is the GNU compiler toolchain, available in packages such as anaconda and PythonXY, or available stand alone. This toolchain has had difficulties on some systems, and may or may not work for you. In order to build DLLs, *gcc* must be on your path. If the environment variable *SAS_OPENMP* is given then -fopenmp is added to the compiler flags. This requires a version of MinGW compiled with OpenMP support. An alternative toolchain uses the Microsoft Visual C++ compiler, available free from microsoft: ``_ Again, this requires that the compiler is available on your path. This is done by running vcvarsall.bat in a windows terminal. Install locations are system dependent, such as: C:\Program Files (x86)\Common Files\Microsoft\Visual C++ for Python\9.0\vcvarsall.bat or maybe C:\Users\yourname\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\vcvarsall.bat And again, the environment variable *SAS_OPENMP* controls whether OpenMP is used to compile the C code. This requires the Microsoft vcomp90.dll library, which doesn't seem to be included with the compiler, nor does there appear to be a public download location. There may be one on your machine already in a location such as: C:\Windows\winsxs\x86_microsoft.vc90.openmp*\vcomp90.dll If you copy this onto your path, such as the python directory or the install directory for this application, then OpenMP should be supported. """ import sys import os import tempfile import ctypes as ct from ctypes import c_void_p, c_int, c_longdouble, c_double, c_float import numpy as np from . import generate from .kernelpy import PyInput, PyModel from .exception import annotate_exception # Compiler platform details if sys.platform == 'darwin': #COMPILE = "gcc-mp-4.7 -shared -fPIC -std=c99 -fopenmp -O2 -Wall %s -o %s -lm -lgomp" COMPILE = "gcc -shared -fPIC -std=c99 -O2 -Wall %(source)s -o %(output)s -lm" elif os.name == 'nt': # call vcvarsall.bat before compiling to set path, headers, libs, etc. if "VCINSTALLDIR" in os.environ: # MSVC compiler is available, so use it. OpenMP requires a copy of # vcomp90.dll on the path. One may be found here: # C:/Windows/winsxs/x86_microsoft.vc90.openmp*/vcomp90.dll # Copy this to the python directory and uncomment the OpenMP COMPILE # TODO: remove intermediate OBJ file created in the directory # TODO: maybe don't use randomized name for the c file CC = "cl /nologo /Ox /MD /W3 /GS- /DNDEBUG /Tp%(source)s " LN = "/link /DLL /INCREMENTAL:NO /MANIFEST /OUT:%(output)s" if "SAS_OPENMP" in os.environ: COMPILE = " ".join((CC, "/openmp", LN)) else: COMPILE = " ".join((CC, LN)) else: COMPILE = "gcc -shared -fPIC -std=c99 -O2 -Wall %(source)s -o %(output)s -lm" if "SAS_OPENMP" in os.environ: COMPILE = COMPILE + " -fopenmp" else: COMPILE = "cc -shared -fPIC -fopenmp -std=c99 -O2 -Wall %(source)s -o %(output)s -lm" DLL_PATH = tempfile.gettempdir() ALLOW_SINGLE_PRECISION_DLLS = True def dll_path(info, dtype="double"): """ Path to the compiled model defined by *info*. """ from os.path import join as joinpath, split as splitpath, splitext basename = splitext(splitpath(info['filename'])[1])[0] if np.dtype(dtype) == generate.F32: basename += "32" elif np.dtype(dtype) == generate.F64: basename += "64" else: basename += "128" return joinpath(DLL_PATH, basename+'.so') def make_dll(source, info, dtype="double"): """ Load the compiled model defined by *kernel_module*. Recompile if any files are newer than the model file. *dtype* is a numpy floating point precision specifier indicating whether the model should be single or double precision. The default is double precision. The DLL is not loaded until the kernel is called so models can be defined without using too many resources. Set *sasmodels.kerneldll.DLL_PATH* to the compiled dll output path. The default is the system temporary directory. Set *sasmodels.ALLOW_SINGLE_PRECISION_DLLS* to True if single precision models are allowed as DLLs. """ if callable(info.get('Iq',None)): return PyModel(info) dtype = np.dtype(dtype) if dtype == generate.F16: raise ValueError("16 bit floats not supported") if dtype == generate.F32 and not ALLOW_SINGLE_PRECISION_DLLS: dtype = generate.F64 # Force 64-bit dll if dtype == generate.F32: # 32-bit dll tempfile_prefix = 'sas_'+info['name']+'32_' elif dtype == generate.F64: tempfile_prefix = 'sas_'+info['name']+'64_' else: tempfile_prefix = 'sas_'+info['name']+'128_' source = generate.convert_type(source, dtype) source_files = generate.sources(info) + [info['filename']] dll= dll_path(info, dtype) newest = max(os.path.getmtime(f) for f in source_files) if not os.path.exists(dll) or os.path.getmtime(dll)