diff --git a/boundaries/boundaryconditions.py b/boundaries/boundaryconditions.py index 19d2d690e640ccd00a6a66d7cef03f49741d0863..e7146e3fb953159ac586fec7dd4f8ec088416077 100644 --- a/boundaries/boundaryconditions.py +++ b/boundaries/boundaryconditions.py @@ -51,6 +51,11 @@ class Boundary(object): class NoSlip(Boundary): + + def __init__(self, name=None): + """Set an optional name here, to mark boundaries, for example for force evaluations""" + self._name = name + """No-Slip, (half-way) simple bounce back boundary condition, enforcing zero velocity at obstacle""" def __call__(self, pdfField, directionSymbol, lbMethod, **kwargs): neighbor = offsetFromDir(directionSymbol, lbMethod.dim) @@ -58,11 +63,20 @@ class NoSlip(Boundary): return [sp.Eq(pdfField[neighbor](inverseDir), pdfField(directionSymbol))] def __hash__(self): - # All boundaries of these class behave equal -> should also be equal - return hash("NoSlip") + # All boundaries of these class behave equal -> should also be equal (as long as name is equal) + return hash(self.name) def __eq__(self, other): - return type(other) == NoSlip + if not isinstance(other, NoSlip): + return False + return self.name == other.name + + @property + def name(self): + if self._name: + return self._name + else: + return type(self).__name__ class NoSlipFullWay(Boundary): diff --git a/boundaries/forceevaluation.py b/boundaries/forceevaluation.py new file mode 100644 index 0000000000000000000000000000000000000000..3e741d733379b48fcfdefa3dd9199b6f77077707 --- /dev/null +++ b/boundaries/forceevaluation.py @@ -0,0 +1,52 @@ +import numpy as np +from lbmpy.stencils import inverseDirection + + +def calculateForceOnNoSlipBoundary(boundaryObject, boundaryHandling, pdfArray): + indArr = boundaryHandling.getBoundaryIndexArray(boundaryObject) + method = boundaryHandling.lbMethod + + stencil = np.array(method.stencil) + + if method.dim == 2: + x, y = indArr['x'], indArr['y'] + values = 2 * pdfArray[x, y, indArr['dir']] + else: + assert method.dim == 3 + x, y, z = indArr['x'], indArr['y'], indArr['z'] + values = 2 * pdfArray[x, y, z, indArr['dir']] + + forces = stencil[indArr['dir']] * values[:, np.newaxis] + return forces.sum(axis=0) + + +def calculateForceOnBoundary(boundaryObject, boundaryHandling, pdfArray): + indArr = boundaryHandling.getBoundaryIndexArray(boundaryObject) + method = boundaryHandling.lbMethod + + stencil = np.array(method.stencil) + invDirection = np.array([method.stencil.index(inverseDirection(d)) + for d in method.stencil]) + + if method.dim == 2: + x, y = indArr['x'], indArr['y'] + offsets = stencil[indArr['dir']] + + fluidValues = pdfArray[x, y, indArr['dir']] + boundaryValues = pdfArray[x + offsets[:, 0], + y + offsets[:, 1], + invDirection[indArr['dir']]] + else: + assert method.dim == 3 + x, y, z = indArr['x'], indArr['y'], indArr['z'] + offsets = stencil[indArr['dir']] + + fluidValues = pdfArray[x, y, z, indArr['dir']] + boundaryValues = pdfArray[x + offsets[:, 0], + y + offsets[:, 1], + z + offsets[:, 2], + invDirection[indArr['dir']]] + + values = fluidValues + boundaryValues + forces = stencil[indArr['dir']] * values[:, np.newaxis] + return forces.sum(axis=0) diff --git a/boundaries/handlinginterface.py b/boundaries/handlinginterface.py index de3f28fbc5bae24fae2fe1edba9231344a6a620a..ba768e4c448b225f4a471b9b172aff42362e995b 100644 --- a/boundaries/handlinginterface.py +++ b/boundaries/handlinginterface.py @@ -113,6 +113,10 @@ class GenericBoundaryHandling(object): if target not in ('cpu', 'gpu'): raise ValueError("Invalid target '%s' . Allowed values: 'cpu' or 'gpu'" % (target,)) + @property + def lbMethod(self): + return self._lbMethod + def getBoundaryNameToFlagDict(self): result = {self._flagFieldInterface.getName(o): self._flagFieldInterface.getFlag(o) for o in self._boundaryInfos} result['fluid'] = self._flagFieldInterface.getFlag('fluid') diff --git a/scenarios.py b/scenarios.py index d1ab830fc5d7b1d2fcc707822777ff2024a005e4..0687bb277f7ab345cbc361edf202efb60dfc5726 100644 --- a/scenarios.py +++ b/scenarios.py @@ -26,6 +26,7 @@ that defines the viscosity of the fluid (valid values being between 0 and 2). import numpy as np import sympy as sp +from lbmpy.boundaries.forceevaluation import calculateForceOnBoundary, calculateForceOnNoSlipBoundary from lbmpy.geometry import setupChannelWalls, addParabolicVelocityInflow from pystencils.field import getLayoutOfArray, createNumpyArrayWithLayout from pystencils.slicing import sliceFromDirection, addGhostLayers, removeGhostLayers, normalizeSlice, makeSlice @@ -346,6 +347,14 @@ class Scenario(object): """Equation collection defining the LBM update rule (already in simplified form)""" return self._lbmKernel.updateRule + def calculateForceOnBoundary(self, boundaryObject): + """Computes force on boundary using simple momentum exchange method""" + if isinstance(boundaryObject, NoSlip): + return calculateForceOnNoSlipBoundary(boundaryObject, self.boundaryHandling, self._pdfArrays[0]) + else: + self.runBoundaryHandlingOnly() + return calculateForceOnBoundary(boundaryObject, self.boundaryHandling, self._pdfArrays[0]) + def animateVelocity(self, steps=10, **kwargs): import lbmpy.plot2d as plt @@ -415,3 +424,11 @@ class Scenario(object): self._getMacroscopic(pdfs=self._pdfArrays[0], density=self._density, velocity=self._velocity, **self.kernelParams) + def runBoundaryHandlingOnly(self): + isGpuSimulation = len(self._pdfGpuArrays) > 0 + if isGpuSimulation: + self._pdfGpuArrays[0].set(self._pdfArrays[0]) + self._boundaryHandling(pdfs=self._pdfGpuArrays[0], **self.kernelParams) + self._pdfGpuArrays[0].get(self._pdfArrays[0]) + else: + self._boundaryHandling(pdfs=self._pdfArrays[0], **self.kernelParams)