From 073d8d3cde6e3d02adcd8878afdbbd9a1de16949 Mon Sep 17 00:00:00 2001 From: Martin Bauer <martin.bauer@fau.de> Date: Fri, 10 Nov 2017 16:58:00 +0100 Subject: [PATCH] Advection - diffusion abstraction layer --- finitedifferences.py | 169 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/finitedifferences.py b/finitedifferences.py index e439dcce0..aa3e9d344 100644 --- a/finitedifferences.py +++ b/finitedifferences.py @@ -132,3 +132,172 @@ def __upDownOffsets(d, dim): coord[d] = -1 down = np.array(coord, dtype=np.int) return up, down + + +# --------------------------------------- Advection Diffusion ---------------------------------------------------------- + + +class Advection(sp.Function): + """Advection term, create with advection(scalarField, vectorField)""" + + @property + def scalar(self): + return self.args[0].field + + @property + def vector(self): + if isinstance(self.args[1], Field.Access): + return self.args[1].field + else: + return self.args[1] + + @property + def scalarIndex(self): + return None if len(self.args) <= 2 else int(self.args[2]) + + @property + def dim(self): + return self.scalar.spatialDimensions + + def _latex(self, printer): + nameSuffix = "_%s" % self.scalarIndex if self.scalarIndex is not None else "" + if isinstance(self.vector, Field): + return r"\nabla \cdot(%s %s)" % (printer.doprint(sp.Symbol(self.vector.name)), + printer.doprint(sp.Symbol(self.scalar.name+nameSuffix))) + else: + args = [r"\partial_%d(%s %s)" % (i, printer.doprint(sp.Symbol(self.scalar.name+nameSuffix)), + printer.doprint(self.vector[i])) + for i in range(self.dim)] + return " + ".join(args) + + +def advection(scalar, vector, idx=None): + assert isinstance(scalar, Field), "Advected scalar has to be a pystencils.Field" + + args = [scalar.center(), vector if not isinstance(vector, Field) else vector.center()] + if idx is not None: + args.append(idx) + return Advection(*args) + + +class Diffusion(sp.Function): + + @property + def scalar(self): + return self.args[0].field + + @property + def diffusionCoeff(self): + if isinstance(self.args[1], Field.Access): + return self.args[1].field + else: + return self.args[1] + + @property + def scalarIndex(self): + return None if len(self.args) <= 2 else int(self.args[2]) + + @property + def dim(self): + return self.scalar.spatialDimensions + + def _latex(self, printer): + nameSuffix = "_%s" % self.scalarIndex if self.scalarIndex is not None else "" + diffCoeff = sp.Symbol(self.diffusionCoeff.name) if isinstance(self.diffusionCoeff, Field) else self.diffusionCoeff + return r"div(%s \nabla %s)" % (printer.doprint(diffCoeff), + printer.doprint(sp.Symbol(self.scalar.name+nameSuffix))) + + +def diffusion(scalar, diffusionCoeff, idx=None): + assert isinstance(scalar, Field), "Advected scalar has to be a pystencils.Field" + args = [scalar.center(), diffusionCoeff if not isinstance(diffusionCoeff, Field) else diffusionCoeff.center()] + if idx is not None: + args.append(idx) + return Diffusion(*args) + + +class Transient(sp.Function): + @property + def scalar(self): + return self.args[0].field + + @property + def scalarIndex(self): + return None if len(self.args) <= 1 else int(self.args[1]) + + def _latex(self, printer): + nameSuffix = "_%s" % self.scalarIndex if self.scalarIndex is not None else "" + return r"\partial_t %s" % (printer.doprint(sp.Symbol(self.scalar.name+nameSuffix)),) + + +def transient(scalar, idx=None): + args = [scalar.center()] + if idx is not None: + args.append(idx) + return Transient(*args) + + +class Discretization2ndOrder: + def __init__(self, dx=sp.Symbol("dx"), dt=sp.Symbol("dt")): + self.dx = dx + self.dt = dt + + def _discretize_diffusion(self, expr): + result = 0 + scalar = expr.scalar + for c in range(expr.dim): + if isinstance(expr.diffusionCoeff, Field): + firstDiffs = [offset * + (scalar.neighbor(c, offset) * expr.diffusionCoeff.neighbor(c, offset) - + scalar.center() * expr.diffusionCoeff.center()) + for offset in [-1, 1]] + else: + firstDiffs = [offset * + (scalar.neighbor(c, offset) * expr.diffusionCoeff - + scalar.center() * expr.diffusionCoeff) + for offset in [-1, 1]] + result += firstDiffs[1] - firstDiffs[0] + return result / (self.dx**2) + + def _discretize_advection(self, expr): + idx = 0 if expr.scalarIndex is None else int(expr.scalarIndex) + result = 0 + for c in range(expr.dim): + if isinstance(expr.vector, Field): + assert expr.vector.indexDimensions == 1 + interpolated = [(expr.scalar.neighbor(c, offset)(idx) * expr.vector.neighbor(c, offset)(c) + + expr.scalar.neighbor(c, 0)(idx) * expr.vector.neighbor(c, 0)(c)) / 2 + for offset in [-1, 1]] + else: + interpolated = [(expr.scalar.neighbor(c, offset)(idx) * expr.vector(c) - + expr.scalar.neighbor(c, 0)(idx) * expr.vector(c)) / 2 + for offset in [-1, 1]] + result += interpolated[1] - interpolated[0] + return result / self.dx + + def _discretizeSpatial(self, e): + if isinstance(e, Diffusion): + return self._discretize_diffusion(e) + elif isinstance(e, Advection): + return self._discretize_advection(e) + else: + newArgs = [self._discretizeSpatial(a) for a in e.args] + return e.func(*newArgs) if newArgs else e + + def __call__(self, expr): + transientTerms = expr.atoms(Transient) + if len(transientTerms) == 0: + return self._discretizeSpatial(expr) + elif len(transientTerms) == 1: + transientTerm = transientTerms.pop() + solveResult = sp.solve(expr, transientTerm) + if len(solveResult) != 1: + raise ValueError("Could not solve for transient term" + str(solveResult)) + rhs = solveResult.pop() + idx = 0 if transientTerm.scalarIndex is None else transientTerm.scalarIndex + # explicit euler + return transientTerm.scalar(idx) + self.dt * self._discretizeSpatial(rhs) + else: + raise NotImplementedError("Cannot discretize expression with more than one transient term") + + -- GitLab