def pointSample(self, x0): from dune.generator import algorithm, path from dune.common import FieldVector import numpy x0 = FieldVector(x0) if self.pointSampler is None: import io _pointSampleCode = \ """ #ifndef FEMPY_UTILITY_HH #define FEMPY_UTILITY_HH #include <dune/fem/misc/linesegmentsampler.hh> #include <dune/fem/gridpart/common/entitysearch.hh> #include <dune/fem/function/localfunction/const.hh> template <class GF, class DT> typename GF::RangeType sample(const GF &gf, DT &point) { typedef typename GF::DiscreteFunctionSpaceType::GridPartType GridPartType; Dune::Fem::EntitySearch<GridPartType> search(gf.space().gridPart()); const auto &entity = search(point); const auto localPoint = entity.geometry().local(point); return constLocalFunction(gf,entity).evaluate(localPoint); } #endif // FEMPY_UTILITY_HH """ self.pointSampler = algorithm.load('sample', io.StringIO(_pointSampleCode), self.gridFunction, x0) v = self.pointSampler(self.gridFunction, x0) return v
def lineSample(self, x0, x1, N): from dune.generator import algorithm, path from dune.common import FieldVector import numpy x0, x1 = FieldVector(x0), FieldVector(x1) if self.lineSampler is None: import io _lineSampleCode = \ """ #ifndef FEMPY_UTILITY_HH #define FEMPY_UTILITY_HH #include <vector> #include <utility> #include <dune/fem/misc/linesegmentsampler.hh> #include <dune/fem/gridpart/common/entitysearch.hh> #include <dune/fem/function/localfunction/const.hh> template <class GF, class DT> std::pair<std::vector<DT>, std::vector<typename GF::RangeType>> sample(const GF &gf, DT &start, DT &end, int n) { Dune::Fem::LineSegmentSampler<typename GF::GridPartType> sampler(gf.gridPart(),start,end); std::vector<DT> coords(n); std::vector<typename GF::RangeType> values(n); sampler(gf,values); sampler.samplePoints(coords); return std::make_pair(coords,values); } #endif """ self.lineSampler = algorithm.load('sample', io.StringIO(_lineSampleCode), self.gridFunction, x0, x1, N) p, v = self.lineSampler(self.gridFunction, x0, x1, N) x, y = numpy.zeros(len(p)), numpy.zeros(len(p)) length = (x1 - x0).two_norm for i in range(len(x)): x[i] = (p[i] - x0).two_norm / length y[i] = v[i][0] return x, y
if t >= saveStep: print(t, grid.size(0), sum(estimate.dofVector), hTol, "# timestep", flush=True) plot(solution[1], figsize=(15, 4)) saveStep += 100 # <markdowncell> # Postprocessing # Show solution along a given line # <codecell> x0 = FieldVector([0.25, 0.65]) x1 = FieldVector([0.775, 0.39]) p, v = algorithm.run('sample', 'utility.hh', solution, x0, x1, 1000) from matplotlib import pyplot import numpy x = numpy.zeros(len(p)) y = numpy.zeros(len(p)) l = (x1 - x0).two_norm for i in range(len(x)): x[i] = (p[i] - x0).two_norm / l y[i] = v[i][1] pyplot.plot(x, y) pyplot.show()
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
# # .. _algorithms: topics.rst # # Dense Vectors and the Geometry Classes # A quick survey of Dune-Common and Dune-Geometry # The core module Dune-Common provides some classes for dense linear algebra. # The `FieldVector` and `FieldMatrix` classes are heavily used in the grid # geometry realizations. The conceptional basis for these geometries is # provided by Dune-Geometry, providing, for example, reference elements and # quadrature rules. # <codecell> import time, numpy, math, sys import matplotlib.pyplot as pyplot from dune.common import FieldVector, FieldMatrix x = FieldVector([0.25, 0.25, 0.25]) import dune.geometry geometryType = dune.geometry.simplex(2) referenceElement = dune.geometry.referenceElement(geometryType) print("\t".join(str(c) for c in referenceElement.corners)) for p in dune.geometry.quadratureRule(geometryType, 3): print(p.position, p.weight) # <markdowncell> # # Grid Construction and Basic Interface # We now move on to the Dune-Grid module. First let us discuss different # possibilities of constructing a grid. # <codecell>
if scvf.ipGlobal.two_norm < 0.25: return 1.0 else: return 0.0 def initial(self, entity): return 0.0 problem = Problem() ###################### # Transport equation # ###################### velocity = FieldVector([1] * dimension) upwindWeight = 1.0 def advectiveFlux(insideConcentration, outsideConcentration, normal): normalVelocity = velocity * normal upwindConcentration = insideConcentration downwindConcentration = outsideConcentration if normalVelocity < 0.0: upwindConcentration, downwindConcentration = downwindConcentration, upwindConcentration return normalVelocity * (upwindWeight * upwindConcentration + (1.0 - upwindWeight) * downwindConcentration) ########################## # Define solution vector #
# <markdowncell> # Instead of plotting this using paraview we want to only study the # solution along a single line. This requires findings points # $x_i = x_0+\frac{i}{N}(x1-x0)$ for $i=0,\dots,N$ within the unstructured # grid. This would be expensive to compute on the Python so we implement # this algorithm in C++ using the `LineSegmentSampler` class available in # `Dune-Fem`. The resulting `algorithm` returns a pair of two lists with # coordinates $x_i$ and the values of the grid function at these points: # # .. literalinclude:: utility.hh # # <codecell> import dune.generator.algorithm as algorithm from dune.common import FieldVector x0, x1 = FieldVector([0, 0, 0]), FieldVector([0, 0, 1]) p, v = algorithm.run('sample', 'utility.hh', uh3d, x0, x1, 100) x, y = numpy.zeros(len(p)), numpy.zeros(len(p)) length = (x1 - x0).two_norm for i in range(len(x)): x[i] = (p[i] - x0).two_norm / length y[i] = v[i][0] pyplot.plot(x, y) pyplot.show() # <markdowncell> # **Note**: the coordinates returned are always in the interval $[0,1]$ so # if physical coordinates are required, they need to be rescaled. # Also, function values returned by the `sample` function # are always of a `FieldVector` type, so that even for a scalar example # a `v[i]` is a vector of dimension one, so that `y[i]=v[i][0]` has to be
op = create.operator("galerkin", inner(jump(u),jump(v))*dS, spc) w = spc.interpolate([0],name="tmp") op(uh, w) dgError = [ math.sqrt( integrate(grid,(uh[0]-exact[0])**2,order=7) ), math.sqrt( integrate(grid,inner(grad(uh[0]-exact[0]),grad(uh[0]-exact[0])),order=7)\ + w.scalarProductDofs(uh)) ] l2Errors = [dgError[0]] h1Errors = [dgError[1]] zh = spc.interpolate([0],name="dual_h") dualOp = create.scheme("galerkin", [adjoint(a)==0], spc, solver="cg", parameters={"newton." + k: v for k, v in newtonParameter.items()}) pointFunctional = spc.interpolate([0],name="pointFunctional") point = FieldVector([0.6,0.4]) errors = [ expression2GF(grid, exact-s, reconOrder) for s in solutions ] dualErrors = algorithm.run('pointFunctional', 'pointfunctional.hh', point, pointFunctional, *errors) dualOp.solve(target=zh, rhs=pointFunctional) dualWeight.project(zh-zh) for i in range(levels): if error[2][useEstimate] < tolerance: print("COMPLETED:",error[2][useEstimate],"<",tolerance) break marked = mark(estimate, error[2][useEstimate]/grid.size(0)) print("elements marked:", marked,"/",grid.size(0),flush=True) if sum(marked)==0: break adapt(uh) loadBalance(uh) level += 1
def testGF_second(gridView): gf1 = gridView.function(lambda e,x:\ math.sin(math.pi*(e.geometry.toGlobal(x)[0]+e.geometry.toGlobal(x)[1]))) if True: a = 2. gf1 = gridView.function(lambda e,x:\ math.sin(a*math.pi*(e.geometry.toGlobal(x)[0]+e.geometry.toGlobal(x)[1])), name="gf1") lgf1 = gf1.localFunction() average1 = 0 for e in gridView.elements: lgf1.bind(e) average1 += lgf1([0.5, 0.5]) * e.geometry.volume # print(average1) # gf1.plot() gf2 = gridView.function("myFunction", StringIO(codeFunc), a, name="gf2") lgf2 = gf2.localFunction() average2 = 0 for e in gridView.elements: lgf2.bind(e) average2 += lgf2([0.5, 0.5]) * e.geometry.volume # print(average2) # gf2.plot() # assert abs(average1-average2)<1e-12 diff = 0 for e in gridView.elements: lgf1.bind(e) lgf2.bind(e) diff += abs(lgf1([0.5, 0.5]) - lgf2([0.5, 0.5])) assert diff < 1e-12 if True: gf1 = gridView.function(lambda e,x:\ [math.sin(2*math.pi*(e.geometry.toGlobal(x)[0]+e.geometry.toGlobal(x)[1])),\ (e.geometry.toGlobal(x)[0]-0.5)*2], name="gf1") lgf1 = gf1.localFunction() average1 = 0 for e in gridView.elements: lgf1.bind(e) average1 += sum(lgf1([0.5, 0.5])) * e.geometry.volume # print(average1) # gf1.plot() a = FieldVector([2]) gf2 = gridView.function("myVecFunction", StringIO(codeVecFunc), a, gf1, name="gf2") lgf2 = gf2.localFunction() average2 = 0 for e in gridView.elements: lgf2.bind(e) average2 += sum(lgf2([0.5, 0.5])) * e.geometry.volume # print(average2) # gf2.plot() assert abs(average1 - average2) < 1e-12 diff = 0 for e in gridView.elements: lgf1.bind(e) lgf2.bind(e) diff += abs(lgf1([0.5, 0.5]).two_norm - lgf2([0.5, 0.5]).two_norm) assert diff < 1e-12 a[0] = 3 diff = 0 for e in gridView.elements: lgf1.bind(e) lgf2.bind(e) v = lgf1([0.5, 0.5]) v[1] *= 3. / 2. diff += abs(v.two_norm - lgf2([0.5, 0.5]).two_norm) assert diff < 1e-12 gridView.writeVTK("test_gf", pointdata=[gf1, gf2]) if False: a = 2. @gridFunction(gridView) def gf1(e, x): return math.sin( a * math.pi * (e.geometry.toGlobal(x)[0] + e.geometry.toGlobal(x)[1])) lgf1 = gf1.localFunction() average1 = 0 for e in gridView.elements: lgf1.bind(e) average1 += lgf1([0.5, 0.5]) * e.geometry.volume # print(average1) # gf1.plot() @gridFunction(gridView) def gf2(x): return math.sin(a * math.pi * (x[0] + x[1])) gf2 = gridView.function("myFunction", StringIO(codeFunc), a, name="gf2") lgf2 = gf2.localFunction() average2 = 0 for e in gridView.elements: lgf2.bind(e) average2 += lgf2([0.5, 0.5]) * e.geometry.volume # print(average2) # gf2.plot() assert abs(average1 - average2) < 1e-12 diff = 0 for e in gridView.elements: lgf1.bind(e) lgf2.bind(e) diff += abs(lgf1([0.5, 0.5]) - lgf2([0.5, 0.5])) assert diff < 1e-12