def compile_cpp_code(cpp_code): """Compile a user C(++) string to a Python object with pybind11. Note this is still experimental. """ if not pkgconfig.exists('dolfin'): raise RuntimeError( "Could not find DOLFIN pkg-config file. Please make sure appropriate paths are set." ) # Get pkg-config data for DOLFIN d = pkgconfig.parse('dolfin') # Set compiler/build options # FIXME: need to locate Python libs and pybind11 from distutils import sysconfig params = dijitso.params.default_params() pyversion = "python" + sysconfig.get_config_var("LDVERSION") params['cache']['lib_prefix'] = "" params['cache']['lib_basename'] = "" params['cache']['lib_loader'] = "import" params['build']['include_dirs'] = d["include_dirs"] + get_pybind_include( ) + [sysconfig.get_config_var("INCLUDEDIR") + "/" + pyversion] params['build']['libs'] = d["libraries"] + [pyversion] params['build']['lib_dirs'] = d["library_dirs"] + [ sysconfig.get_config_var("LIBDIR") ] params['build']['cxxflags'] += ('-fno-lto', ) # enable all define macros from DOLFIN dmacros = () for dm in d['define_macros']: if len(dm[1]) == 0: dmacros += ('-D' + dm[0], ) else: dmacros += ('-D' + dm[0] + '=' + dm[1], ) params['build']['cxxflags'] += dmacros # This seems to be needed by OSX but not in Linux # FIXME: probably needed for other libraries too if cpp.common.has_petsc(): import os params['build']['libs'] += ['petsc'] params['build']['lib_dirs'] += [os.environ["PETSC_DIR"] + "/lib"] module_hash = hashlib.md5(cpp_code.encode('utf-8')).hexdigest() module_name = "dolfin_cpp_module_" + module_hash module, signature = dijitso.jit(cpp_code, module_name, params, generate=jit_generate) return module
def jit_compile_form(a, parameters=None): """JIT-compile form and return ctypes function pointer""" # Prevent modification of user parameters parameters = parameters.copy() if parameters is not None else {} # Use tsfc as default form compiler compiler = parameters.pop("compiler", "tsfc") compile_form = { "ffc": ffc_compile_wrapper, "tsfc": tsfc_compile_wrapper }[compiler] # Define generation function executed on cache miss def generate(form, name, signature, jit_params): code = compile_form(form, parameters=parameters) return None, code, () # Compute unique name hash_data = ("minidolfin", compiler, canonicalize_metadata(parameters), a.signature()) hash = hashlib.sha512(str(hash_data).encode("utf-8")).hexdigest() name = "minidolfin_{}_{}".format(compiler, hash) # Set dijitso into C mode jit_params = { 'build': { 'cxx': 'cc', 'cxxflags': ('-Wall', '-shared', '-fPIC', '-std=c11'), }, 'cache': { 'src_postfix': '.c' }, } # Do JIT compilation module, name = dijitso.jit(a, name, jit_params, generate=generate) # Grab assembly kernel from ctypes module and set its arguments func = getattr(module, 'form_cell_integral_otherwise') func.argtypes = (ctypes.c_void_p, ctypes.c_void_p) return func
def cjit(code, func_name, func_argtypes): """JIT code and return ctypes function with given name and arguments""" obj = None name = "dijit" + hashlib.sha1(code.encode()).hexdigest() params = { 'build': { 'cxx': 'cc', 'cxxflags': ('-Wall', '-shared', '-fPIC', '-std=c11'), }, 'cache': {'src_postfix': '.c'}, } def generate(obj, name, signature, params): return None, code, () module, name = dijitso.jit(obj, name, params, generate=generate) func = getattr(module, func_name) func.argtypes = func_argtypes return func
def jit_build(ufl_object, module_name, parameters): "Wraps dijitso jit with some parameter conversion etc." import dijitso # FIXME: Expose more dijitso parameters? # FIXME: dijitso build params are not part of module_name here. # Currently dijitso doesn't add to the module signature. # Translating the C++ flags from ffc parameters to dijitso # to get equivalent behaviour to instant code build_params = {} build_params["debug"] = not parameters["cpp_optimize"] build_params["cxxflags_opt"] = tuple(parameters["cpp_optimize_flags"].split()) build_params["cxxflags_debug"] = ("-O0",) build_params["include_dirs"] = (get_ufc_include_path(),) + _string_tuple(parameters.get("external_include_dirs")) build_params["lib_dirs"] = _string_tuple(parameters.get("external_library_dirs")) build_params["libs"] = _string_tuple(parameters.get("external_libraries")) # Interpreting FFC default "" as None, use "." if you want to point to curdir cache_dir = parameters.get("cache_dir") or None if cache_dir: cache_params = {"cache_dir": cache_dir} else: cache_params = {} # This will do some rudimenrary checking of the params and fill in dijitso defaults params = dijitso.validate_params({ "cache": cache_params, "build": build_params, "generator": parameters, # ffc parameters, just passed on to jit_generate }) # Carry out jit compilation, calling jit_generate only if needed module, signature = dijitso.jit(jitable=ufl_object, name=module_name, params=params, generate=jit_generate) return module
def dijitso_jit(*args, **kwargs): return dijitso.jit(*args, **kwargs)
def compile_class(cpp_data): """Compile a user C(++) string or set of statements to a Python object cpp_data is a dict containing: "name": must be "expression" or "subdomain" "statements": must be a string, or list/tuple of strings "properties": a dict of float properties "jit_generate": callable (generates cpp code with this dict as input) """ import pkgconfig if not pkgconfig.exists('dolfin'): raise RuntimeError( "Could not find DOLFIN pkg-config file. Please make sure appropriate paths are set." ) # Get DOLFIN pkg-config data d = pkgconfig.parse('dolfin') # Set compiler/build options params = dijitso.params.default_params() params['build']['include_dirs'] = d["include_dirs"] params['build']['libs'] = d["libraries"] params['build']['lib_dirs'] = d["library_dirs"] name = cpp_data['name'] if name not in ('subdomain', 'expression'): raise ValueError("DOLFIN JIT only for SubDomain and Expression") statements = cpp_data['statements'] properties = cpp_data['properties'] if not isinstance(statements, (str, tuple, list)): raise RuntimeError( "Expression must be a string, or a list or tuple of strings") # Flatten tuple of tuples (2D array) and get value_shape statement_array = numpy.array(statements) cpp_data['statements'] = tuple(statement_array.flatten()) cpp_data['value_shape'] = statement_array.shape # Make a string representing the properties (and distinguish float/GenericFunction) # by adding '*' for GenericFunction property_str = '' for k, v in properties.items(): property_str += str(k) if hasattr(v, '_cpp_object') and isinstance( v._cpp_object, cpp.function.GenericFunction): property_str += '*' hash_str = str(statements) + str(property_str) module_hash = hashlib.md5(hash_str.encode('utf-8')).hexdigest() module_name = "dolfin_" + name + "_" + module_hash try: module, signature = dijitso.jit(cpp_data, module_name, params, generate=cpp_data['jit_generate']) submodule = dijitso.extract_factory_function(module, "create_" + module_name)() except: raise RuntimeError("Unable to compile C++ code with dijitso") if name == 'expression': python_object = cpp.function.make_dolfin_expression(submodule) else: python_object = cpp.mesh.make_dolfin_subdomain(submodule) # Set properties to initial values # FIXME: maybe remove from here (do it in Expression and SubDomain instead) for k, v in properties.items(): python_object.set_property(k, v) return python_object
def compile_extension_module( ode, monitored, params, ): """ Compile an extension module, based on the C code from the ode """ dijitso_params = dijitso.validate_params(dijitso.params.default_params()) args, args_doc = parse_arguments(params) # Create unique module name for this application run modulename = module_signature(ode, monitored, params, languange="C") compiled_module = load_module(modulename) if compiled_module is not None: return compiled_module # Do not generate any Python functions python_params = params.copy() for name in python_params.functions: if name == "monitored": continue python_params.functions[name].generate = False jacobian_code = parse_jacobian_declarations(ode, args, args_doc, params) monitor_code = parse_monitor_declaration(ode, args, args_doc, params, monitored) pgen = PythonCodeGenerator(python_params) cgen = CCodeGenerator(params) code_dict = cgen.code_dict( ode, monitored=monitored, include_init=False, include_index_map=False, ) pcode = "\n\n".join(list( pgen.code_dict(ode, monitored=monitored).values())) argtypes = args_to_argtypes(args) cpp_data = { "code_dict": add_dll_export(code_dict), "includes": ["#include <math.h>"], } rhs_code = rhs_template.format( rhs_function_name=params.functions.rhs.function_name, args=args, rhs_name=params.functions.rhs.result_name, args_doc=args_doc, states_name=params.code.states.array_name, ) rhs_binding = binding_template.format( funcname="rhs", argtypes=argtypes, restype="None", ) monitor_binding = "" if monitor_code != "": monitor_binding = binding_template.format( funcname="monitor", argtypes=argtypes, restype="None", ) jacobian_binding = "" if jacobian_code != "": jacobian_binding = binding_template.format( funcname="compute_jacobian", argtypes=argtypes, restype="None", ) compiled_module_code = module_template.format( signature=modulename, cache=repr(dijitso_params["cache"]), code="\n\n\n".join([rhs_code, pcode, monitor_code, jacobian_code]), bindings="\n".join([rhs_binding, monitor_binding, jacobian_binding]), ) module, signature = dijitso.jit( cpp_data, modulename, dijitso_params, generate=_jit_generate, ) save_module(compiled_module_code, modulename) info("Calling GOTRAN just-in-time (JIT) compiler, this may take some " "time...") sys.stdout.flush() info(" done") sys.stdout.flush() return load_module(signature)