From 198600bfa4537bcb5c9f7931b9ba0401e3ff0bf8 Mon Sep 17 00:00:00 2001
From: Martin Bauer <martin.bauer@fau.de>
Date: Tue, 13 Feb 2018 21:52:01 +0100
Subject: [PATCH] 2D scenarios - drops on interface - falling drop

---
 phasefield/analytical.py     |   2 +-
 phasefield/experiments2D.py  | 160 +++++++++++++++++++++++++++++++++++
 phasefield/kerneleqs.py      |   4 +-
 phasefield/phasefieldstep.py |  11 ++-
 4 files changed, 173 insertions(+), 4 deletions(-)
 create mode 100644 phasefield/experiments2D.py

diff --git a/phasefield/analytical.py b/phasefield/analytical.py
index d98ac542..819d6f81 100644
--- a/phasefield/analytical.py
+++ b/phasefield/analytical.py
@@ -64,7 +64,7 @@ def freeEnergyFunctionalNPhasesPenaltyTerm(orderParameters, interfaceWidth=inter
     numPhases = len(orderParameters)
     if kappa is None:
         kappa = sp.symbols("kappa_:%d" % (numPhases,))
-    if not hasattr(kappa, "__length__"):
+    if not hasattr(kappa, "__len__"):
         kappa = [kappa] * numPhases
 
     def f(x):
diff --git a/phasefield/experiments2D.py b/phasefield/experiments2D.py
new file mode 100644
index 00000000..dc94cd42
--- /dev/null
+++ b/phasefield/experiments2D.py
@@ -0,0 +1,160 @@
+import numpy as np
+from itertools import cycle
+
+from lbmpy.boundaries import NoSlip
+from pystencils import makeSlice
+
+
+def setPhase(pfStep, phaseIndex, sliceObj, value=1.0, others=0.0):
+    for b in pfStep.dataHandling.iterate(sliceObj):
+        b[pfStep.phiFieldName][..., phaseIndex].fill(value)
+        if others is not None:
+            for i in range(pfStep.numOrderParameters):
+                if i == phaseIndex:
+                    continue
+                else:
+                    b[pfStep.phiFieldName][..., i].fill(others)
+
+
+def boxBetweenPhases(pfStep, phaseIndices=(0, 1, 2), values=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
+                     boxX=(0.25, 0.75), boxY=(0.25, 0.75)):
+    assert all(idx < pfStep.numOrderParameters for idx in phaseIndices)
+    assert pfStep.dataHandling.dim == 2
+
+    domainSize = pfStep.dataHandling.shape
+    x1, x2 = (int(c * domainSize[0]) for c in boxX)
+    y1, y2 = (int(c * domainSize[1]) for c in boxY)
+    
+    pfStep.reset()
+    
+    for b in pfStep.dataHandling.iterate():
+        x, y = b.cellIndexArrays[0:2]
+        phi = b[pfStep.phiFieldName]
+
+        innerBox = np.logical_and(np.logical_and(x < x2, x > x1),
+                                  np.logical_and(y < y2, y > y1))
+
+        upperValues, lowerValues, midValues = values
+        for phaseIdx, val in zip(phaseIndices, lowerValues):
+            phi[y <= domainSize[1] //2, phaseIdx] = val
+        for phaseIdx, val in zip(phaseIndices, upperValues):
+            phi[y > domainSize[1] // 2, phaseIdx] = val
+        for phaseIdx, val in zip(phaseIndices, midValues):
+            phi[innerBox, phaseIdx] = val
+
+    pfStep.setPdfFieldsFromMacroscopicValues()
+
+
+def initThreeBoxes(pfStep, phaseIndices=(0, 1, 2), values=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
+    assert all(idx < pfStep.numOrderParameters for idx in phaseIndices)
+    assert pfStep.dataHandling.dim == 2
+
+    domainSize = pfStep.dataHandling.shape
+    x1, x2 = (domainSize[0] // 3), (domainSize[0] // 3) * 2
+    y1, y2 = (domainSize[1] // 3), (domainSize[1] // 3) * 2
+
+    pfStep.reset()
+
+    for b in pfStep.dataHandling.iterate():
+        x, y = b.cellIndexArrays[0:2]
+        phi = b[pfStep.phiFieldName]
+        xMid = np.logical_and(x1 < x, x < x2)
+        yMid = np.logical_and(y1 < y, y < y2)
+
+        center = np.logical_and(xMid, yMid)
+        side = np.logical_and(np.logical_not(xMid),yMid)
+        topBottom = np.logical_not(yMid)
+
+        midValues, sideValues, topBottomValues = values
+        for phaseIdx, val in zip(phaseIndices, midValues):
+            phi[center, phaseIdx] = val
+        for phaseIdx, val in zip(phaseIndices, sideValues):
+            phi[side, phaseIdx] = val
+        for phaseIdx, val in zip(phaseIndices, topBottomValues):
+            phi[topBottom, phaseIdx] = val
+
+    pfStep.setPdfFieldsFromMacroscopicValues()
+
+
+def phasePlot(pfStep, sliceObj=makeSlice[:, :], linewidth=1.0):
+    import lbmpy.plot2d as plt
+    colors = ['#fe0002', '#00fe00', '#0000ff', '#ffa800', '#f600ff']
+    colorCycle = cycle(colors)
+
+    for i in range(pfStep.numOrderParameters):
+        s = sliceObj + (i,)
+        plt.scalarFieldAlphaValue(pfStep.phi[s], next(colorCycle), clip=True, interpolation='bilinear')
+    for i in range(pfStep.numOrderParameters):
+        s = sliceObj + (i,)
+        plt.scalarFieldContour(pfStep.phi[s], levels=[0.5], colors='k', linewidth=linewidth)
+
+
+def dropsBetweenTwoPhase():
+    import lbmpy.plot2d as plt
+    import sympy as sp
+    from lbmpy.phasefield.phasefieldstep import PhaseFieldStep
+    from lbmpy.phasefield.analytical import freeEnergyFunctionalNPhasesPenaltyTerm
+    c = sp.symbols("c_:4")
+    F = freeEnergyFunctionalNPhasesPenaltyTerm(c, 1, [0.01, 0.02, 0.001, 0.02], 0.0001)
+    sc = PhaseFieldStep(F, c, domainSize=(2*200, 2*70), openMP=4, hydroDynamicRelaxationRate=1.9)
+    setPhase(sc, 0, makeSlice[:, 0.5:])
+    setPhase(sc, 1, makeSlice[:, :0.5])
+    setPhase(sc, 2, makeSlice[0.2:0.4, 0.3:0.7])
+    setPhase(sc, 3, makeSlice[0.7:0.8, 0.3:0.7])
+    sc.setPdfFieldsFromMacroscopicValues()
+    #sc.run(1)
+    #for i in range(3):
+    #    plt.subplot(3, 1, i+1)
+    #    plt.scalarField(sc.phi[:, :, i])
+    for i in range(500000):
+        print("Step", i)
+        plt.figure(figsize=(14, 7))
+        phasePlot(sc, makeSlice[:, 5:-5], linewidth=0.1)
+        plt.axis('off')
+        plt.tight_layout()
+        plt.savefig('step%03d.png' % (i,), bbox_inches='tight')
+        plt.clf()
+        sc.run(25)
+    plt.show()
+
+
+def fallingDrop():
+    import lbmpy.plot2d as plt
+    import sympy as sp
+    from lbmpy.phasefield.phasefieldstep import PhaseFieldStep
+    from lbmpy.phasefield.analytical import freeEnergyFunctionalNPhasesPenaltyTerm
+    c = sp.symbols("c_:3")
+    F = freeEnergyFunctionalNPhasesPenaltyTerm(c, 1, [0.0008, 0.0008, 0.00002])
+    gravity = -1e-5
+    sc = PhaseFieldStep(F, c, domainSize=(160, 200), openMP=4,
+                        hydroDynamicRelaxationRate=1.9,
+                        orderParameterForce={2: (0, gravity), 1: (0, gravity)})
+    setPhase(sc, 0, makeSlice[:, 0.4:])
+    setPhase(sc, 1, makeSlice[:, :0.4])
+    setPhase(sc, 2, makeSlice[0.45:0.55, 0.8:0.9])
+    sc.hydroLbmStep.boundaryHandling.setBoundary(NoSlip(), makeSlice[:, 0])
+    sc.hydroLbmStep.boundaryHandling.setBoundary(NoSlip(), makeSlice[:, -1])
+
+    sc.setPdfFieldsFromMacroscopicValues()
+    #sc.run(1)
+    #for i in range(3):
+    #    plt.subplot(3, 1, i+1)
+    #    plt.scalarField(sc.phi[:, :, i])
+    for i in range(650):
+        print("Step", i)
+        plt.figure(figsize=(14, 10))
+        plt.subplot(1, 2, 1)
+        phasePlot(sc, makeSlice[:, 5:-15], linewidth=0.1)
+        plt.axis('off')
+        plt.subplot(1, 2, 2)
+        plt.vectorField(sc.velocity[:, 5:-15, :])
+        plt.axis('off')
+        plt.tight_layout()
+        plt.savefig('fallingDrop_boundary2_%05d.png' % (i,), bbox_inches='tight')
+        plt.clf()
+        sc.run(200)
+    plt.show()
+
+
+if __name__ == '__main__':
+    fallingDrop()
\ No newline at end of file
diff --git a/phasefield/kerneleqs.py b/phasefield/kerneleqs.py
index b3d778e4..10400fde 100644
--- a/phasefield/kerneleqs.py
+++ b/phasefield/kerneleqs.py
@@ -39,12 +39,14 @@ def pressureTensorKernel(freeEnergy, orderParameters, phiField, pressureTensorFi
     return eqs
 
 
-def forceKernelUsingPressureTensor(forceField, pressureTensorField, dx=1):
+def forceKernelUsingPressureTensor(forceField, pressureTensorField, extraForce=None, dx=1):
     dim = forceField.spatialDimensions
     indexMap = symmetricTensorLinearization(dim)
 
     p = sp.Matrix(dim, dim, lambda i, j: pressureTensorField(indexMap[i,j] if i < j else indexMap[j, i]))
     f = forceFromPressureTensor(p)
+    if extraForce:
+        f += extraForce
     return [sp.Eq(forceField(i), finiteDifferences2ndOrder(f_i, dx).expand())
             for i, f_i in enumerate(f)]
 
diff --git a/phasefield/phasefieldstep.py b/phasefield/phasefieldstep.py
index 98245be4..fdee2790 100644
--- a/phasefield/phasefieldstep.py
+++ b/phasefield/phasefieldstep.py
@@ -1,3 +1,4 @@
+import sympy as sp
 from lbmpy.lbstep import LatticeBoltzmannStep
 from lbmpy.phasefield.cahn_hilliard_lbm import cahnHilliardLbmMethod
 from lbmpy.phasefield.kerneleqs import muKernel, forceKernelUsingMu, CahnHilliardFDStep, pressureTensorKernel, \
@@ -14,7 +15,8 @@ class PhaseFieldStep:
     def __init__(self, freeEnergy, orderParameters, domainSize=None, dataHandling=None,
                  name='pf', hydroLbmParameters={},
                  hydroDynamicRelaxationRate=1.0, cahnHilliardRelaxationRates=1.0, densityOrderParameter=None,
-                 target='cpu', openMP=False, kernelParams={}, dx=1, dt=1, solveCahnHilliardWithFiniteDifferences=False):
+                 target='cpu', openMP=False, kernelParams={}, dx=1, dt=1, solveCahnHilliardWithFiniteDifferences=False,
+                 orderParameterForce=None):
 
         if dataHandling is None:
             dataHandling = SerialDataHandling(domainSize, periodicity=True)
@@ -54,7 +56,12 @@ class PhaseFieldStep:
                                                       target=target, cpuOpenMP=openMP).compile()
 
         # F Kernel
-        self.forceEqs = forceKernelUsingPressureTensor(self.forceField, self.pressureTensorField, dx)
+        extraForce = sp.Matrix([0] * self.dataHandling.dim)
+        if orderParameterForce is not None:
+            for orderParameterIdx, force in orderParameterForce.items():
+                extraForce += self.phiField(orderParameterIdx) * sp.Matrix(force)
+        self.forceEqs = forceKernelUsingPressureTensor(self.forceField, self.pressureTensorField, dx=dx,
+                                                       extraForce=extraForce)
         self.forceFromPressureTensorKernel = createKernel(self.forceEqs, target=target, cpuOpenMP=openMP).compile()
         self.pressureTensorSync = dataHandling.synchronizationFunction([self.pressureTensorFieldName], target=target)
 
-- 
GitLab