diff --git a/phasefield.py b/phasefield.py new file mode 100644 index 0000000000000000000000000000000000000000..b30a2866b2d2b920832a43e45dc2cb577e070b69 --- /dev/null +++ b/phasefield.py @@ -0,0 +1,91 @@ +import sympy as sp +from pystencils.equationcollection.equationcollection import EquationCollection +from pystencils.sympyextensions import kroneckerDelta, multidimensionalSummation +from lbmpy.maxwellian_equilibrium import getWeights +from lbmpy.chapman_enskog.derivative import expandUsingLinearity, Diff +from lbmpy.methods.creationfunctions import createFromEquilibrium +from lbmpy.moments import getDefaultMomentSetForStencil +from lbmpy.simplificationfactory import createSimplificationStrategy +from lbmpy.updatekernels import createStreamPullCollideKernel + + +def functionalDerivative(functional, v, constants=None): + """ + - assumes that gradients are represented by Diff() node (from Chapman Enskog module) + - Diff(Diff(r)) represents the divergence of r + """ + functional = expandUsingLinearity(functional, constants=constants) + diffs = functional.atoms(Diff) + + diffV = Diff(v) + assert diffV in diffs # not necessary in general, but for this use case this should be true + + nonDiffPart = functional.subs({d: 0 for d in diffs}) + + partialF_partialV = sp.diff(nonDiffPart, v) + + dummy = sp.Dummy() + partialF_partialGradV = functional.subs(diffV, dummy).diff(dummy).subs(dummy, diffV) + + result = partialF_partialV - Diff(partialF_partialGradV) + return expandUsingLinearity(result, constants=constants) + + +def discreteLaplace(field, index, dx): + dim = field.spatialDimensions + count = 0 + result = 0 + for d in range(dim): + for offset in (-1, 1): + count += 1 + result += field.neighbor(d, offset)(index) + + result -= count * field.center()(index) + result /= dx ** 2 + return result + + +def createCahnHilliardEquilibrium(stencil, mu, gamma=1): + weights = getWeights(stencil, c_s_sq=sp.Rational(1, 3)) + + kd = kroneckerDelta + + def s(*args): + for r in multidimensionalSummation(*args, dim=len(stencil[0])): + yield r + + op = sp.Symbol("rho") + v = sp.symbols("v_:%d" % (len(stencil[0]),)) + + equilibrium = [] + for d, w in zip(stencil, weights): + c_s = sp.sqrt(sp.Rational(1, 3)) + result = gamma * mu / (c_s ** 2) + result += op * sum(d[i] * v[i] for i, in s(1)) / (c_s ** 2) + result += op * sum(v[i] * v[j] * (d[i] * d[j] - c_s ** 2 * kd(i, j)) for i, j in s(2)) / (2 * c_s ** 4) + equilibrium.append(w * result) + + rho = sp.Symbol("rho") + equilibrium[0] = rho - sp.expand(sum(equilibrium[1:])) + return tuple(equilibrium) + + +def createCahnHilliardUpdateRule(stencil, relaxationRate, pdfArr, velocityField, mu, orderParameterOut, gamma=1): + equilibrium = createCahnHilliardEquilibrium(stencil, mu, gamma) + v = sp.symbols("v_:%d" % (len(stencil[0]),)) + op = sp.Symbol("rho") + + rrRates = {m: relaxationRate for m in getDefaultMomentSetForStencil(stencil)} + method = createFromEquilibrium(stencil, tuple(equilibrium), rrRates, compressible=True) + + inputEqs = [sp.Eq(op, sum(method.preCollisionPdfSymbols))] + inputEqs += [sp.Eq(v_i, velocityField(i)) for i, v_i in enumerate(v)] + + collisionRule = method.getCollisionRule(EquationCollection(inputEqs, [])) + simplification = createSimplificationStrategy(method) + collisionRule = simplification(collisionRule) + collisionRule.mainEquations.append(sp.Eq(orderParameterOut, op)) + updateRule = createStreamPullCollideKernel(collisionRule, numpyField=pdfArr) + updateRule.collisionRule = collisionRule + updateRule.method = method + return updateRule