예제 #1
0
파일: equation.py 프로젝트: whshangl/finmag
def get_equation_module(for_distribution=False):
    """
    Returns extension module that deals with the equation of motion.
    Will try to return from cache before recompiling.

    By default, dolfin will chose a cache directory using a digest of our code
    and some version numbers. This procedure enables dolfin to detect changes
    to our code and recompile on the fly. However, when we distribute FinMag we
    don't need or want on the fly recompilation and we'd rather have the
    resulting files placed in a directory known ahead of time. For this, call
    this function once with `for_distribution` set to True and ship FinMag
    including the directory build/equation.

    During normal use, our known cache directory is always checked before
    dolfin's temporary ones. Its existence bypasses on the fly recompilation.

    """
    # __file__ will not be available during module init if this module is
    # compiled with cython. So the following line shouldn't be moved to the
    # module level. It is perfectly safe inside this function though.
    MODULE_DIR = path.dirname(path.abspath(__file__))
    SOURCE_DIR = path.join(MODULE_DIR, "native")
    # Define our own cache base directory instead of the default one. This
    # helps in distributing only the compiled code without sources.
    CACHE_DIR = path.join(MODULE_DIR, "build")

    signature = "equation" if for_distribution else ""  # dolfin will chose

    # Try to get the module from the known distribution location before
    # asking instant about its cache. This way a distributed copy of FinMag
    # should never attempt recompilation (which would fail without sources).
    equation_module = instant.import_module("equation", CACHE_DIR)
    if equation_module is not None:
        log.debug("Got equation extension module from distribution location.")
    else:
        with open(path.join(SOURCE_DIR, "equation.h"), "r") as header:
            code = header.read()

        equation_module = df.compile_extension_module(
            code=code,
            sources=["equation.cpp", "terms.cpp", "derivatives.cpp"],
            source_directory=SOURCE_DIR,  # where the sources given above are
            include_dirs=[SOURCE_DIR, find_petsc(),
                          find_slepc()],  # where to look for header files
            # dolfin's compile_extension_module will pass on `module_name` to
            # instant's build_module as `signature`. That's the name of the
            # directory it will be cached in. So don't worry if instant's doc
            # says that passing a module name will disable caching.
            module_name=signature,
            cache_dir=CACHE_DIR,
        )

    return equation_module
예제 #2
0
def test_sum():

    sig = "((instant unittest test16.py))"

    # Trying to import module
    module = import_module(sig, cache_dir="test_cache")
    if module is None:
        print("Defining code")
        c_code = """
        class Sum {
        public:
          virtual double sum(double a, double b){
          return a+b;
        }
      };

      double use_Sum(Sum& sum, double a, double b) {
        return sum.sum(a,b);
      }
        """
        print("Compiling code")
        module = build_module(code=c_code, signature=sig, cache_dir="test_cache")

    # Testing module
    Sum = module.Sum
    use_Sum = module.use_Sum

    sum = Sum()
    a = 3.7
    b = 4.8
    c = use_Sum(sum, a, b)
    print("The sum of %g and %g is %g"% (a, b, c))

    class Sub(Sum):
        def __init__(self):
            Sum.__init__(self)

        def sum(self, a, b):
            print("sub")
            return a-b;

    sub = Sub()
    a = 3.7
    b = 4.8
    c = use_Sum(sub, a, b)
    print("The sub of %g and %g is %g"% (a, b, c))
예제 #3
0
def compile_extension_module(code, module_name="",
                             additional_declarations="",
                             additional_system_headers=None,
                             **instant_kwargs):
    """
    Just In Time compile DOLFIN C++ code into a Python module.

    *Arguments*
        code
            C++ code which implements any function or C++ class. Any function
            or class available in the C++ DOLFIN namespace can be used and/or
            subclassed. All typemaps from the original Python interface are
            available, making it possible to interface with for example NumPy
            for Array<double/int> arguments. Source which is not wrapped in
            a dolfin namespace will be automatically wrapped.

        module_name
            Force a name of the module. If not set a name based on the hex
            representation of the code will be used.

        additional_declarations
            Additional SWIG declarations can be passed using this argument.

        additional_system_headers :
            System headers needed to compile the generated can be included
            using this argument. The headers are passed using a list of 'str'

    *Returns*

        The JIT compiled extension module

    *Examples of usage*

        The following toy example shows how one can use compiled extension
        modules to access low level PETSc routines:

        .. code-block:: python

            from numpy import arange
            code = '''
            namespace dolfin {

              void PETSc_exp(std::shared_ptr<dolfin::PETScVector> vec)
              {
                Vec x = vec->vec();
                assert(x);
                VecExp(x);
              }
            }
            '''
            ext_module = compile_extension_module(code,
                         additional_system_headers=["petscvec.h"])
            comm = mpi_comm_world()
            vec = PETScVector(comm, 10)
            vec[:] = arange(10)
            print vec[-1]
            ext_module.PETSc_exp(vec)
            print vec[-1]

    """
    # Check the provided arguments
    expect_arg(str, code, "first")
    expect_arg(str, module_name, "module_name")
    expect_arg(str, additional_declarations, "additional_declarations")
    additional_system_headers = \
                expect_list_of(str, additional_system_headers, "additional_system_headers")

    # Check that the code does not use 'using namespace dolfin'
    if re.search("using\s+namespace\s+dolfin",code):
        cpp.dolfin_error("compilemodule.py",
                         "ensure correct argument to compile_extension_module",
                         "Do not use 'using namespace dolfin'. "\
                         "Include the code in namespace dolfin {...} instead")

    # Check if the code does not use namespace dolfin {...}
    if not re.search("namespace\s+dolfin\s*\{[\s\S]+\}", code):

        # Wrap and indet code in namespace dolfin
        codelines = ["namespace dolfin","{"]
        codelines += ["  " + line for line in code.split("\n")]
        codelines += ["}"]
        code = "\n".join(codelines)

    # Create unique module name for this application run
    if module_name is "":
        module_name = "dolfin_compile_code_%s" % \
                      hashlib.md5(repr(code) + dolfin.__version__ + \
                                  str(_interface_version)+\
                                  additional_declarations +\
                                  str(additional_system_headers)).hexdigest()

    # Extract dolfin dependencies and class names
    used_types, declared_types = parse_and_extract_type_info(code)

    # Add any bases of the declared types to used_types
    for declared_type, bases in declared_types.items():
        used_types.update(bases)

    # Filter out dolfin types and add derived and bases for each type
    used_dolfin_types = []
    for dolfin_type in dolfin_type_def:
        for used_type in used_types:
            if dolfin_type in used_type:

                # Add bases and derived types
                used_dolfin_types.extend(\
                    dolfin_type_def[dolfin_type]["bases"])

                # Add dolfin type
                used_dolfin_types.append(dolfin_type)

                break

    # Generate dependency info
    dependencies = {}
    for dolfin_type in used_dolfin_types:
        if dolfin_type_def[dolfin_type]["submodule"] not in dependencies:
            dependencies[dolfin_type_def[dolfin_type]["submodule"]] = []

        dependencies[dolfin_type_def[dolfin_type]["submodule"]].append(\
            dolfin_type_def[dolfin_type]["header"])

    # Need special treatment for template definitions in function/pre.i
    if "function" in dependencies:
        for dolfin_type in ["FunctionSpace", "Function"]:
            dependencies["function"].append(dolfin_type_def[dolfin_type]["header"])

    # Add uint type
    if "common" in dependencies:
        dependencies["common"].append("dolfin/common/types.h")
    else:
        dependencies["common"] = ["dolfin/common/types.h"]

    # Sort the dependencies
    dependencies = sort_submodule_dependencies(dependencies, submodule_info)

    import_lines, headers_includes, file_dependencies = \
                  build_swig_import_info(dependencies, submodule_info, "dolfin.cpp.")

    # Extract header info
    dolfin_system_headers = [header for header in file_dependencies \
                             if not "pre.i" in header]

    # Check the handed import files
    interface_import_files = []

    # Check cache
    compiled_module = instant.import_module(module_name)

    if compiled_module:
        # Check that the swig version of the compiled module is the same as
        # dolfin was compiled with
        check_swig_version(compiled_module)
        return compiled_module

    sys.stdout.flush()
    dolfin.info("Calling DOLFIN just-in-time (JIT) compiler, this may take some time.")

    # Configure instant and add additional system headers

    # Add dolfin system headers
    instant_kwargs["system_headers"] = ["cmath", "iostream","complex",
                                        "stdexcept","numpy/arrayobject.h",
                                        "memory",
                                        "dolfin/common/types.h",
                                        "dolfin/math/basic.h"] + \
                                        instant_kwargs.get("system_headers", [])
    instant_kwargs["system_headers"] += dolfin_system_headers

    # Add user specified system headers
    instant_kwargs["system_headers"] += additional_system_headers

    # Add cmake packages
    instant_kwargs["cmake_packages"]  = ["DOLFIN"] + \
                                        instant_kwargs.get("cmake_packages", [])
    instant_kwargs["signature"] = module_name

    declaration_strs = {"additional_declarations":""}
    declaration_strs["dolfin_import_statement"] = \
                    "\n".join(import_lines)

    # Add any provided additional declarations
    if additional_declarations is not None:
        declaration_strs["additional_declarations"] += additional_declarations

    # Add any shared_ptr declarations
    declaration_strs["shared_ptr_declarations"] = \
        extract_shared_ptr_declaration(declared_types, used_dolfin_types, \
                                       shared_ptr_classes)

    # Compile extension module with instant
    compiled_module = instant.build_module(\
        code              = code,
        additional_declarations = _additional_declarations % declaration_strs,
        **instant_kwargs)

    sys.stdout.flush()

    # Check that the swig version of the compiled module is the same as
    # dolfin was compiled with
    check_swig_version(compiled_module)

    return compiled_module
예제 #4
0
def jit_form(form, parameters=None):
    "Just-in-time compile the given form."
    from ffc.backends.ufc import build_ufc_module

    # Check that we get a Form
    if not isinstance(form, Form):
        error("Unable to convert object to a UFL form: %s" % repr(form))

    # Check parameters
    parameters = _check_parameters(form, parameters)

    # Set log level
    set_level(parameters["log_level"])
    set_prefix(parameters["log_prefix"])

    # Wrap input
    jit_object = JITObject(form, parameters)

    # Set prefix for generated code
    module_name = "ffc_form_" + jit_object.signature()

    # Use Instant cache if possible
    cache_dir = parameters["cache_dir"] or None
    module = instant.import_module(module_name, cache_dir=cache_dir)
    if module:
        debug("Reusing form from cache.")
    else:
        # Take lock to serialise file removal.
        # Need to add "_0" to lock as instant.import_module acquire
        # lock with name: module_name
        with instant.file_lock(instant.get_default_cache_dir(),
                               module_name + "_0") as lock:

            # Retry Instant cache. The module may have been created while we waited
            # for the lock, even if it didn't exist before.
            module = instant.import_module(module_name, cache_dir=cache_dir)
            if module:
                debug("Reusing form from cache.")
            else:
                # Write a message
                log(INFO + 5,
                    "Calling FFC just-in-time (JIT) compiler, this may take some time.")

                # Generate code
                compile_form(form,
                             prefix=module_name,
                             parameters=parameters)

                # Build module using Instant (through UFC)
                debug("Compiling and linking Python extension module, this may take some time.")
                hfile   = module_name + ".h"
                cppfile = module_name + ".cpp"

                if parameters["cpp_optimize"]:
                    cppargs = parameters["cpp_optimize_flags"].split()
                else:
                    cppargs = ["-O0"]

                module = build_ufc_module(
                    hfile,
                    source_directory = os.curdir,
                    signature = module_name,
                    sources = [cppfile] if parameters["split"] else [],
                    cppargs = cppargs,
                    cache_dir = cache_dir)

                # Remove code
                if os.path.isfile(hfile):
                    os.unlink(hfile)
                if parameters["split"] :
                    if os.path.isfile(cppfile):
                        os.unlink(cppfile)

    # Construct instance of compiled form
    check_swig_version(module)
    prefix = module_name
    compiled_form = _instantiate_form(module, prefix)
    return compiled_form, module, prefix
예제 #5
0
def test_timing():
    #_t = None
    def tic():
        global _t
        _t = -time.time()

    def toc(msg=""):
        t = time.time() + _t
        print("t = %f  (%s)" % (t, msg))
        return t

    c_code = """
double sum(double a, double b)
{
  return a+b;
}
    """

    class Sig:
        def __init__(self, sig):
            self.sig = sig

        def signature(self):
            time.sleep(1.0)
            return self.sig

        def __hash__(self):
            time.sleep(0.5)
            return hash(self.sig)

        def __cmp__(self, other):
            if isinstance(other, Sig):
                return cmp(self.sig, other.sig)
            return -1

    sig = Sig("((test18.py signature))")
    cache_dir = "test_cache"

    # Time a few builds
    tic()
    module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    assert module is not None
    t1 = toc("first build")

    tic()
    module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    assert module is not None
    t2 = toc("second build")

    tic()
    module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    assert module is not None
    t3 = toc("third build")

    # Time importing
    tic()
    module = import_module(sig, cache_dir)
    assert module is not None
    t4 = toc("first import")

    tic()
    module = import_module(sig, cache_dir)
    assert module is not None
    t5 = toc("second import")

    assert t1 > 1
    assert t2 < 1 and t2 > 0.4
    assert t3 < 1 and t3 > 0.4
    assert t4 < 1 and t4 > 0.4
    assert t5 < 1 and t5 > 0.4
예제 #6
0
def jit(ode,
        field_states=None,
        field_parameters=None,
        monitored=None,
        code_params=None,
        cppargs=None):
    """
    Generate a goss::ODEParameterized from a gotran ode and JIT compile it

    Arguments:
    ----------
    ode : gotran.ODE
        The gotran ode, either as an ODE or as an ODERepresentation
    field_states : list
        A list of state names, which should be treated as field states
    field_parameters : list
        A list of parameter names, which should be treated as field parameters
    monitored : list
        A list of names of intermediates of the ODE. Code for monitoring
        the intermediates will be generated.
    code_params : dict
        Parameters controling the code generation
    cppargs : str
        Default C++ argument passed to the C++ compiler
    """

    # Code generators
    cgen = GossCodeGenerator(ode, field_states, field_parameters, monitored,
                             code_params)
    cgen.params.class_code = True
    pgen = PythonCodeGenerator(cgen.params)

    # Create unique module name for this application run
    module_name = "goss_compiled_module_{0}_{1}".format(\
        ode.name, hashlib.sha1((ode.signature() + \
                               repr(code_params) + \
                               repr(field_states) + \
                               repr(field_parameters) + \
                               repr(monitored) + \
                               #instant.get_swig_version() + \
                               instant.__version__ + \
                               gotran.__version__ + \
                               str(cppargs)).encode()).hexdigest())

    # Check cache
    compiled_module = instant.import_module(module_name)

    if compiled_module:
        return getattr(compiled_module, cgen.name)()

    push_log_level(INFO)

    # Init state code
    python_code = pgen.init_states_code(ode)
    cpp_code = cgen.class_code()

    info("Calling GOSS just-in-time (JIT) compiler, this may take some "\
         "time...")
    sys.stdout.flush()

    # Configure instant and add additional system headers
    instant_kwargs = configure_instant()

    instant_kwargs["cppargs"] = cppargs or instant_kwargs["cppargs"]
    instant_kwargs["cmake_packages"] = ["GOSS"]

    declaration_form = dict(\
        ModelName = cgen.name,
        python_code = python_code,
        )

    # Compile extension module with instant
    compiled_module = instant.build_module(\
        code = cpp_code,
        additional_declarations = _additional_declarations.format(\
            **declaration_form),
        signature = module_name,
        **instant_kwargs)

    info(" done")
    pop_log_level()
    sys.stdout.flush()

    # Return an instantiated class
    return getattr(compiled_module, cgen.name)()
예제 #7
0
def test_build():

    #_t = None
    def tic():
        global _t
        _t = -time.time()

    def toc(msg=""):
        t = time.time() + _t
        print("t = %f  (%s)" % (t, msg))
        return t

    c_code = """
double sum(double a, double b)
{
  return a+b;
}
    """

    class Sig:
        def __init__(self, sig):
            self.sig = sig

        def signature(self):
            time.sleep(1.0)
            return self.sig

        def __hash__(self):
            time.sleep(0.5)
            return hash(self.sig)

        def __cmp__(self, other):
            if isinstance(other, Sig):
                return cmp(self.sig, other.sig)
            return -1

    modulename = "test19_ext"
    cache_dir = "test19_cache"
    shutil.rmtree(cache_dir, ignore_errors=True)
    shutil.rmtree(modulename, ignore_errors=True)

    # Build and rebuild with explicit modulename
    tic()
    module = build_module(code=c_code,
                          modulename=modulename,
                          cache_dir=cache_dir)
    assert module is not None
    t1 = toc("(1) With modulename")

    tic()
    module = build_module(code=c_code,
                          modulename=modulename,
                          cache_dir=cache_dir)
    assert module is not None
    t2 = toc("(2) With modulename")
    assert t1 > t2

    # Try importing module in a separate python process
    python_interp = sys.executable
    cmd = python_interp + ' -c "import %s"' % modulename
    print(cmd)
    stat = os.system(cmd)
    assert stat == 0  # a

    # Build and rebuild with a valid filename as signature
    sig = "test19_signature_module"
    tic()
    module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    assert module is not None
    t1 = toc("(1) With signature")

    tic()
    module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    assert module is not None
    t2 = toc("(2) With signature")
    assert t1 > t2

    tic()
    module = import_module(sig, cache_dir)
    assert module is not None
    t3 = toc("(3) import_module")
    assert t1 > t3

    # Try importing module in a separate python process
    python_interp = sys.executable
    cmd = python_interp + ' -c "import instant; assert instant.import_module(\'%s\', \'%s\') is not None"' % (
        sig, cache_dir)
    print(cmd)
    stat = os.system(cmd)
    assert stat == 0  # b

    # Build and rebuild with generic signature string
    sig = "((test19_signature_module))"
    tic()
    module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    assert module is not None
    t1 = toc("(1) With signature")

    tic()
    module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    assert module is not None
    t2 = toc("(2) With signature")
    assert t1 > t2

    tic()
    module = import_module(sig, cache_dir)
    assert module is not None
    t3 = toc("(3) import_module")
    assert t1 > t3

    # Try importing module in a separate python process
    python_interp = sys.executable
    cmd = python_interp + ' -c "import instant; assert instant.import_module(\'%s\', \'%s\') is not None"' % (
        sig, cache_dir)
    print(cmd)
    stat = os.system(cmd)
    assert stat == 0  # c

    print(
        "Skipping unit test, see https://bugs.launchpad.net/instant/+bug/518389"
    )
    # Build and rebuild with generic signature object
    #sig = Sig("((test19_signature_module))")
    #tic()
    #module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    #assert module is not None
    #t1 = toc("(1) With signature")
    #tic()
    #module = build_module(code=c_code, signature=sig, cache_dir=cache_dir)
    #assert module is not None
    #t2 = toc("(2) With signature")
    #assert t1 > t2
    #tic()
    #module = import_module(sig, cache_dir)
    #assert module is not None
    #t3 = toc("(3) import_module")
    #assert t1 > t3

    # Build and rebuild without modulename or signature
    tic()
    module = build_module(code=c_code, cache_dir=cache_dir)
    assert module is not None
    t1 = toc("(1) Without modulename or signature")

    tic()
    module = build_module(code=c_code, cache_dir=cache_dir)
    assert module is not None
    t2 = toc("(2) Without modulename or signature")
    assert t1 > t2