Beispiel #1
0
def init(integrands, source, *args, **kwargs):
    coefficients = kwargs.pop('coefficients', dict())
    coefficientNames = integrands._coefficientNames
    if len(args) == 1 and isinstance(args[0], dict):
        coefficients.update(args[0])
        args = []
    else:
        args = list(a for a in args if not isinstance(a,DirichletBC) and not a is None)
        dirichletBCs = list(a for a in args if isinstance(a,DirichletBC))
        if len(args) > len(coefficientNames) + len(dirichletBCs):
            raise ValueError('Too many coefficients passed.')

    args += [None] * (len(coefficientNames) - len(args))

    for name, value in kwargs.items():
        try:
            i = coefficientNames[name]
        except KeyError:
            raise ValueError('No such coefficent: ' + name + '.')

        if args[i] is not None:
            raise ValueError('Coefficient already given as positional argument: ' + name + '.')
        args[i] = value

    for key, value in coefficients.items():
        if isinstance(key, Coefficient):
            try:
                i = integrands._renumbering[key]
            except AttributeError:
                raise ValueError('Cannot map UFL coefficients, because model was not generated from UFL form.')
            except KeyError:
                raise ValueError('No such coefficient: ' + str(key) + '.')
        elif isString(key):
            try:
                i = coefficientNames[key]
            except KeyError:
                raise ValueError('No such coefficent: ' + key + '.')
        else:
            raise ValueError('Expecting keys of coefficient map to be strings or  intances of ufl.Coefficient.')
        if args[i] is not None:
            raise ValueError('Coefficient already given as positional or keyword argument: ' + str(key) + '.')
        args[i] = value

    if hasattr(integrands, '_renumbering'):
        for c, i in integrands._renumbering.items():
            if isinstance(c, GridFunction):
                if args[i] is None:
                    args[i] = c.gf

    if any(arg is None for arg in args):
        missing = [name for name, i in coefficientNames.items() if args[i] is None]
        raise ValueError('Missing coefficients: ' + ', '.join(missing) + '.')

    integrands.base.__init__(integrands, *args, **kwargs)

    for c in source.constantList:
        if hasattr(c,"name") and hasattr(c,"value"):
            assert hasattr(integrands,c.name)
            getattr(type(integrands), c.name).fset(integrands, c.value)
Beispiel #2
0
 def append(self, *lines):
     for line in lines:
         if isinstance(line, (list, tuple)):
             self.append(*list(line))
         elif isString(line):
             self.lines += line.split('\n')
         else:
             raise Exception(
                 'Only strings (or lists of them) can be appended to an UnformattedBlock'
             )
Beispiel #3
0
def addStorage(obj, storage):
    if not storage:
        storage = str("numpy")
    if isString(storage):
        import dune.create as create
        assert storage, "wrong storage (" + storage + ") passed to space"
        storage = create.discretefunction(storage)(obj)
    else:
        storage = storage(obj)
    setattr(obj, "storage", storage)
    return storage
Beispiel #4
0
def initModel(model, *args, **kwargs):
    coefficients = kwargs.pop('coefficients', dict())
    if len(args) == 1 and isinstance(args[0], dict):
        coefficients.update(args[0])
        args = []
    else:
        args = list(args)

    try:
        coefficientNames = model._coefficientNames
    except:
        coefficientNames = []
    if len(args) > len(coefficientNames):
        raise ValueError('Too many coefficients passed.')
    args += [None] * (len(coefficientNames) - len(args))

    for name, value in kwargs:
        i = coefficientNames.get(name)
        if i is None:
            raise ValueError('No such coefficent: ' + name + '.')
        if args[i] is not None:
            raise ValueError('Coefficient already given as positional argument: ' + name + '.')
        args[i] = value

    for key, value in coefficients.items():
        if isinstance(key, Coefficient):
            try:
                i = model._renumbering[key]
            except AttributeError:
                raise ValueError('Cannot map UFL coefficients, because model was not generated from UFL form.')
            except KeyError:
                raise ValueError('No such coefficient: ' + str(key) + '.')
        elif isString(key):
            i = coefficientNames.get(name)
            if i is None:
                raise ValueError('No such coefficent: ' + name + '.')
        else:
            raise ValueError('Expecting keys of coefficient map to be ufl.Coefficient instances.')
        if args[i] is not None:
            raise ValueError('Coefficient already given as positional or keyword argument: ' + str(key) + '.')
        args[i] = value

    if hasattr(model, '_renumbering'):
        for c in (k for k in model._renumbering if isinstance(k, GridFunction)):
            i = model._renumbering[c]
            if args[i] is None:
                args[i] = c.gf

    if any(arg is None for arg in args):
        missing = [name for name, i in coefficientNames.items() if args[i] is None]
        raise ValueError('Missing coefficients: ' + ', '.join(missing) + '.')

    model._init(*args)
Beispiel #5
0
def function(gv,callback,includeFiles=None,*args,name=None,order=None,dimRange=None):
    if name is None:
        name = "tmp"+str(gv._gfCounter)
        gv.__class__._gfCounter += 1
    if isString(callback):
        if includeFiles is None:
            raise ValueError("""if `callback` is the name of a C++ function
            then at least one include file containing that function must be
            provided""")

        # unique header guard is added further down
        source  = '#include <config.h>\n\n'
        source += '#define USING_DUNE_PYTHON 1\n\n'
        includes = []
        if isString(includeFiles):
            if not os.path.dirname(includeFiles):
                with open(includeFiles, "r") as include:
                    source += include.read()
                source += "\n"
            else:
                source += "#include <"+includeFiles+">\n"
                includes += [includeFiles]
        elif hasattr(includeFiles,"readable"): # for IOString
            with includeFiles as include:
                source += include.read()
            source += "\n"
        elif isinstance(includeFiles, list):
            for includefile in includeFiles:
                if not os.path.dirname(includefile):
                    with open(includefile, "r") as include:
                        source += include.read()
                    source += "\n"
            else:
                source += "#include <"+includefile+">\n"
                includes += [includefile]
        includes += gv.cppIncludes
        argTypes = []
        for arg in args:
            t,i = cppType(arg)
            argTypes.append(t)
            includes += i

        signature = callback + "( " + ", ".join(argTypes) + " )"
        moduleName = "gf_" + hashIt(signature) + "_" + hashIt(source)

        # add unique header guard with moduleName
        source = '#ifndef Guard_'+moduleName+'\n' + \
                 '#define Guard_'+moduleName+'\n\n' + \
                 source

        includes = sorted(set(includes))
        source += "".join(["#include <" + i + ">\n" for i in includes])
        source += "\n"
        source += '#include <dune/python/grid/function.hh>\n'
        source += '#include <dune/python/pybind11/pybind11.h>\n'
        source += '\n'

        source += "PYBIND11_MODULE( " + moduleName + ", module )\n"
        source += "{\n"
        source += "  module.def( \"gf\", [module] ( "+gv.cppTypeName + " &gv"+"".join([", "+argTypes[i] + " arg" + str(i) for i in range(len(argTypes))]) + " ) {\n"
        source += "      auto callback="+callback+"<"+gv.cppTypeName+">( "+",".join(["arg"+str(i) for i in range(len(argTypes))]) +"); \n"
        source += "      return Dune::Python::registerGridFunction<"+gv.cppTypeName+",decltype(callback)>(module,pybind11::cast(gv),\"tmp\",callback);\n"
        source += "    },"
        source += "    "+",".join(["pybind11::keep_alive<0,"+str(i+1)+">()" for i in range(len(argTypes)+1)])
        source += ");\n"
        source += "}\n"
        source += "#endif\n"
        gf = builder.load(moduleName, source, signature).gf(gv,*args)
    else:
        if len(inspect.signature(callback).parameters) == 1: # global function, turn into a local function
            callback_ = callback
            callback = lambda e,x: callback_(e.geometry.toGlobal(x))
        else:
            callback_ = None
        if dimRange is None:
            # if no `dimRange` attribute is set on the callback,
            # try to evaluate the function to determin the dimension of
            # the return value. This can fail if the function is singular in
            # the computational domain in which case an exception is raised
            e = gv.elements.__iter__().__next__()
            try:
                y = callback(e,e.referenceElement.position(0,0))
            except ArithmeticError:
                try:
                    y = callback(e,e.referenceElement.position(0,2))
                except ArithmeticError:
                    raise TypeError("can not determin dimension of range of "+
                      "given grid function due to arithmetic exceptions being "+
                      "raised. Add a `dimRange` parameter to the grid function to "+
                      "solve this issue - set `dimRange`=0 for a scalar function.")
            try:
                dimRange = len(y)
            except TypeError:
                dimRange = 0
        if dimRange > 0:
            scalar = "false"
        else:
            scalar = "true"
        FieldVector(dimRange*[0]) # register FieldVector for the return value
        if not dimRange in gv.__class__._functions.keys():
            # unique header key is added further down
            source  = '#include <config.h>\n\n'
            source += '#define USING_DUNE_PYTHON 1\n\n'
            includes = gv.cppIncludes

            signature = gv.cppTypeName+"::gf<"+str(dimRange)+">"
            moduleName = "gf_" + hashIt(signature) + "_" + hashIt(source)

            # add unique header guard with moduleName
            source = '#ifndef Guard_'+moduleName+'\n' + \
                     '#define Guard_'+moduleName+'\n\n' + \
                     source

            includes = sorted(set(includes))
            source += "".join(["#include <" + i + ">\n" for i in includes])
            source += "\n"
            source += '#include <dune/python/grid/function.hh>\n'
            source += '#include <dune/python/pybind11/pybind11.h>\n'
            source += '\n'

            source += "PYBIND11_MODULE( " + moduleName + ", module )\n"
            source += "{\n"
            source += "  typedef pybind11::function Evaluate;\n";
            source += "  Dune::Python::registerGridFunction< "+gv.cppTypeName+", Evaluate, "+str(dimRange)+" >( module, \"gf\", "+scalar+" );\n"
            source += "}\n"
            source += "#endif\n"
            gfModule = builder.load(moduleName, source, signature)
            gfFunc = getattr(gfModule,"gf"+str(dimRange))
            if callback_ is not None:
                gfFunc.localCall = gfFunc.__call__
                feval = lambda self,e,x=None: callback_(e) if x is None else self.localCall(e,x)
                subclass = type(gfFunc.__name__, (gfFunc,), {"__call__": feval})
                gv.__class__._functions[dimRange] = subclass
            else:
                gv.__class__._functions[dimRange] = gfFunc
        gf = gv.__class__._functions[dimRange](gv,callback)
    def gfPlot(gf, *args, **kwargs):
        gf.grid.plot(gf,*args,**kwargs)
    gf.plot = gfPlot.__get__(gf)
    gf.name = name
    gf.order = order
    return gf
Beispiel #6
0
    def load(self, moduleName, source, pythonName, extraCMake=None):
        # use with-statement to log info if compiling takes some time
        class PrintCompiling:
            def handle_signal(self, signum, frame):
                logger.info("Compiling "+pythonName)
            def __enter__(self):
                signal.signal(signal.SIGALRM, self.handle_signal)
                signal.alarm(1)
            def __exit__(self, type, value, traceback):
                signal.alarm(0)
        class EmptyPrintCompiling:
            def __enter__(self): pass
            def __exit__(self, type, value, traceback): pass

        ## TODO replace if rank with something better
        ## and remove barrier further down

        # check if we need to initialize dune-py either because
        # this is the first call to load or because an external module with metadata has been registered
        if not self.initialized or not self.externalPythonModules == getExternalPythonModules():
            self.initialize()
        if comm.rank == 0:
            module = sys.modules.get("dune.generated." + moduleName)
            if module is None:
                logger.debug("Module {} not loaded".format(moduleName))
                # make sure nothing (compilation, generating and building) is taking place
                with Lock(os.path.join(self.dune_py_dir, '..', 'lock-module.lock'), flags=LOCK_EX):
                    # module must be generated so lock the source file
                    with Lock(os.path.join(self.dune_py_dir, 'lock-'+moduleName+'.lock'), flags=LOCK_EX):
                        sourceFileName = os.path.join(self.generated_dir, moduleName + ".cc")
                        line = "dune_add_pybind11_module(NAME " + moduleName + " EXCLUDE_FROM_ALL)"
                        # first check if this line is already present in the CMakeLists file
                        # (possible if a previous script was stopped by user before module was compiled)
                        with open(os.path.join(self.generated_dir, "CMakeLists.txt"), 'r') as out:
                            found = line in out.read()
                        if not os.path.isfile(sourceFileName) or not found:
                            logger.info("Generating " + pythonName)
                            # don't print 'Compiling' twice
                            PrintCompiling = EmptyPrintCompiling

                            code = str(source)
                            with open(os.path.join(sourceFileName), 'w') as out:
                                out.write(code)
                            assert os.path.isfile(sourceFileName), "Error in writing module .cc file"
                            if not found:
                                origPos = -1
                                with open(os.path.join(self.generated_dir, "CMakeLists.txt"), 'a') as out:
                                    # store original file size
                                    origPos = out.tell()
                                    out.write(line+"\n")
                                    if extraCMake is not None:
                                        for x in extraCMake:
                                            out.write(x.replace("TARGET",moduleName)+"\n")
                                # update build system
                                logger.debug(
                                    "Configuring module {} ({}) with CMake".format(
                                        pythonName, moduleName
                                    )
                                )
                                try:
                                    # self.compile()
                                    cmake = subprocess.Popen( "cmake .".split(),
                                                       cwd=self.dune_py_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                                    stdout, stderr = cmake.communicate()
                                    #logger.debug("CMake configuration output: "+buffer_to_str(stdout))
                                    if cmake.returncode > 0:
                                        raise CompileError(buffer_to_str(stderr))
                                except: # all exceptions will cause a problem here
                                    os.remove(os.path.join(sourceFileName))
                                    # remove line from CMakeLists to avoid problems
                                    with open(os.path.join(self.generated_dir, "CMakeLists.txt"), 'a') as out:
                                        out.truncate(origPos)
                                    raise
                        elif isString(source) and not source == open(os.path.join(sourceFileName), 'r').read():
                            logger.info("Compiling " + pythonName + " (updated)")
                            # don't print 'Compiling' twice
                            PrintCompiling = EmptyPrintCompiling

                            code = str(source)
                            with open(os.path.join(sourceFileName), 'w') as out:
                                out.write(code)
                        else:
                            # we only can directly load the module
                            # sanity check
                            line = "dune_add_pybind11_module(NAME " + moduleName + " EXCLUDE_FROM_ALL)"
                            # the CMakeLists file should already include this line
                            with open(os.path.join(self.generated_dir, "CMakeLists.txt"), 'r') as out:
                                found = line in out.read()
                            assert found, "CMakeLists.txt file does not contain an entry to build"+moduleName

                # end of exclusive dune-py lock

                # we always compile even if the module is always compiled since it can happen
                # that dune-py was updated in the mean time.
                # This step is quite fast but there is room for optimization.

                # for compilation a shared lock is enough
                with Lock(os.path.join(self.dune_py_dir, '..', 'lock-module.lock'), flags=LOCK_SH):
                    # lock generated module
                    with Lock(os.path.join(self.dune_py_dir, 'lock-'+moduleName+'.lock'), flags=LOCK_EX):
                        with PrintCompiling():
                            self.compile(target=moduleName)

        ## TODO remove barrier here
        comm.barrier()

        logger.debug("Loading " + moduleName)
        module = importlib.import_module("dune.generated." + moduleName)

        if self.force:
            logger.info("Reloading " + pythonName)
            module = reload_module(module)

        return module
Beispiel #7
0
def load(className, includeFiles, *args,
         options=None, bufferProtocol=False, dynamicAttr=False,
         holder="default",
         baseClasses=None ):
    if options is None: options=[]
    if baseClasses is None: baseClasses=[]
    if not bufferProtocol: # kwargs.get("bufferProtocol", False):
        clsParams = []
    else:
        clsParams = ['pybind11::buffer_protocol()']
    if dynamicAttr:
        clsParams += ['pybind11::dynamic_attr()']

    source = '#include <config.h>\n\n'
    source += '#define USING_DUNE_PYTHON 1\n\n'
    includes = []
    if isString(includeFiles) or hasattr(includeFiles,"readable"):
        includeFiles = [includeFiles]
    for includefile in includeFiles:
        if isString(includefile):
            if not os.path.dirname(includefile):
                with open(includefile, "r") as include:
                    source += include.read()
                source += "\n"
            else:
                source += "#include <"+includefile+">\n"
                includes += [includefile]
        elif hasattr(includefile,"readable"): # for IOString
            with includefile as include:
                source += include.read()
            source += "\n"

    argTypes = []
    for arg in args:
        t,i = cppType(arg)
        argTypes.append(t)
        includes += i

    signature = className + "( " + ", ".join(argTypes) + " )"
    moduleName = "class_" + hashIt(signature) + "_" + hashIt(source)

    includes = sorted(set(includes))

    includes += ["python/dune/generated/"+moduleName+".cc"]

    source += "".join(["#include <" + i + ">\n" for i in includes])
    source += "\n"
    source += '#include <dune/python/common/typeregistry.hh>\n'
    source += '#include <dune/python/pybind11/pybind11.h>\n'
    source += '\n'

    source += "PYBIND11_MODULE( " + moduleName + ", module )\n"
    source += "{\n"


    for i, bc in enumerate(baseClasses):
        if not holder == "default":
            baseHolder = ", " + holder + "<" + bc + ">"
        else:
            baseHolder = ''
        source += 'Dune::Python::insertClass' +\
                       '< ' + bc + baseHolder + '>' +\
                       '( module, "cls' + str(i) + '"' +\
                       ', Dune::Python::GenerateTypeName("' + bc + '")' +\
                       ', Dune::Python::IncludeFiles{}' +\
                       ");\n"
        options.append(bc)

    if not holder == "default":
        options += [holder + "<" + className + ">"]

    source += "auto cls = Dune::Python::insertClass< "+className+\
                   ', '.join(('',)+tuple(options)) + ' >('+\
                 "module, \"cls\","+','.join(('',)+tuple(clsParams))+\
                 "Dune::Python::GenerateTypeName(\""+className+"\"),"+\
                 "Dune::Python::IncludeFiles{"+",".join(["\""+f+"\"" for f in includes])+"}).first;\n"

    ctorArgs = ", ".join([argTypes[i] + " arg" + str(i) for i in range(len(argTypes))])
    source += "cls.def( pybind11::init( [] ( "+ctorArgs+" ) {\n"
    source += "return new "+className+"( "+",".join(["arg"+str(i) for i in range(len(argTypes))]) +"); \n"
    source += "})"+" ".join([", pybind11::keep_alive< 1, {} >()".format(i+2) for i in range(len(argTypes))]) + "\n"
    source += ");\n"
    source += "}"

    source = "#ifndef def_"+moduleName+\
             "\n#define def_"+moduleName+"\n"+\
             source+\
             "\n#endif\n"

    return builder.load(moduleName, source, signature).cls(*args)
Beispiel #8
0
def load(functionName, includes, *args):
    '''Just in time compile an algorithm.

    Generates binding for a single (template) function. The name of the
    function and the C++ types of the arguments passed to this function are
    used to generate a static type used in the bindings. The file(s)
    containing the code for the function are passed in either as single
    string or as a list of strings. Note that these files will be copied
    into the generated module. The file name can include a path name. So in
    the simples case `includes="header.hh" will include the file from the
    current working directory. To include a file from the directory
    containing the calling script use
    `includes=dune.generator.path(__file__)+"header.hh"`.

    Args:
        functionName:    name of the C++ function to provide bindings for
        includes:        single or list of files to add to the generated module
        *args:           list of arguments that will be passed to the generated module

    Returns:
        Callalble object
    '''

    # header guard is added further down
    source  = '#include <config.h>\n\n'
    source += '#define USING_DUNE_PYTHON 1\n\n'
    if isString(includes):
        with open(includes, "r") as include:
            source += include.read()
        source += "\n"
        includes = []
    elif hasattr(includes,"readable"): # for IOString
        with includes as include:
            source += include.read()
        source += "\n"
        includes = []
    elif isinstance(includes, list):
        for includefile in includes:
            with open(includefile, "r") as include:
                source += include.read()
        source += "\n"
        includes = []

    argTypes = []
    for arg in args:
        t,i = cppType(arg)
        argTypes.append(t)
        includes += i

    signature = functionName + "( " + ", ".join(argTypes) + " )"
    moduleName = "algorithm_" + hashIt(signature) + "_" + hashIt(source)

    # add unique header guard with moduleName
    source = '#ifndef Guard_'+moduleName+'\n' + \
             '#define Guard_'+moduleName+'\n\n' + \
             source

    includes = sorted(set(includes))
    source += "".join(["#include <" + i + ">\n" for i in includes])
    source += "\n"
    source += '#include <dune/python/common/typeregistry.hh>\n'
    source += '#include <dune/python/pybind11/pybind11.h>\n'
    source += '\n'

    source += "PYBIND11_MODULE( " + moduleName + ", module )\n"
    source += "{\n"

    source += "  module.def( \"run\", [] ( " + ", ".join([argTypes[i] + " arg" + str(i) for i in range(len(argTypes))]) + " ) {\n"
    source += "      return " + functionName + "( " + ", ".join(["arg" + str(i) for i in range(len(argTypes))]) + " );\n"
    source += "    } );\n"

    source += "}\n"
    source += "#endif\n"

    return builder.load(moduleName, source, signature).run
Beispiel #9
0
    def load(self, moduleName, source, pythonName):

        ## TODO replace if rank if something better
        ## and remove barrier further down
        if comm.rank == 0:
            module = sys.modules.get("dune.generated." + moduleName)
            if module is None:
                # make sure nothing (compilation, generating and building) is # taking place
                with Lock(os.path.join(self.dune_py_dir, 'lock-module.lock'),
                          flags=LOCK_EX):
                    # module must be generated so lock the source file
                    with Lock(os.path.join(self.dune_py_dir,
                                           'lock-' + moduleName + '.lock'),
                              flags=LOCK_EX):
                        sourceFileName = os.path.join(self.generated_dir,
                                                      moduleName + ".cc")
                        line = "dune_add_pybind11_module(NAME " + moduleName + " EXCLUDE_FROM_ALL)"
                        # first check if this line is already present in the CMakeLists file
                        # (possible if a previous script was stopped by user before module was compiled)
                        with open(
                                os.path.join(self.generated_dir,
                                             "CMakeLists.txt"), 'r') as out:
                            found = line in out.read()
                        if not os.path.isfile(sourceFileName) or not found:
                            logger.info("Compiling " + pythonName)
                            code = str(source)
                            with open(os.path.join(sourceFileName),
                                      'w') as out:
                                out.write(code)
                            if not found:
                                with open(
                                        os.path.join(self.generated_dir,
                                                     "CMakeLists.txt"),
                                        'a') as out:
                                    out.write(line + "\n")
                                # update build system
                                logger.debug("Rebuilding module")
                                try:
                                    self.compile()
                                except KeyboardInterrupt:
                                    os.remove(os.path.join(sourceFileName))
                                    raise
                        elif isString(source) and not source == open(
                                os.path.join(sourceFileName), 'r').read():
                            logger.info("Compiling " + pythonName +
                                        " (updated)")
                            code = str(source)
                            with open(os.path.join(sourceFileName),
                                      'w') as out:
                                out.write(code)
                        else:
                            logger.debug("Loading " + pythonName)
                            line = "dune_add_pybind11_module(NAME " + moduleName + " EXCLUDE_FROM_ALL)"
                            # the CMakeLists file should already include this line
                            with open(
                                    os.path.join(self.generated_dir,
                                                 "CMakeLists.txt"),
                                    'r') as out:
                                found = line in out.read()
                            assert found, "CMakeLists file does not contain an entry to build" + moduleName
                # end of exclusive dune-py lock

                # for compilation a shared lock is enough
                with Lock(os.path.join(self.dune_py_dir, 'lock-module.lock'),
                          flags=LOCK_SH):
                    # lock generated module
                    with Lock(os.path.join(self.dune_py_dir,
                                           'lock-' + moduleName + '.lock'),
                              flags=LOCK_EX):
                        logger.debug("Now compiling " + moduleName)
                        self.compile(moduleName)
            ## end if module is not None

        ## TODO remove barrier here
        comm.barrier()
        module = importlib.import_module("dune.generated." + moduleName)

        if self.force:
            logger.info("Reloading " + pythonName)
            module = reload_module(module)

        return module