Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
def dijitso_jit(*args, **kwargs):
    return dijitso.jit(*args, **kwargs)
Beispiel #6
0
def dijitso_jit(*args, **kwargs):
    return dijitso.jit(*args, **kwargs)
Beispiel #7
0
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
Beispiel #8
0
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)