// DO NOT EDIT ! // This file is generated using the MantaFlow preprocessor (prep generate). /****************************************************************************** * * MantaFlow fluid solver framework * Copyright 2011 Tobias Pfaff, Nils Thuerey * * This program is free software, distributed under the terms of the * Apache License, Version 2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Plugins for using vortex sheet meshes * ******************************************************************************/ #include #include "vortexsheet.h" #include "vortexpart.h" #include "shapes.h" #include "commonkernels.h" #include "conjugategrad.h" #include "randomstream.h" #include "levelset.h" using namespace std; namespace Manta { //! Mark area of mesh inside shape as fixed nodes. //! Remove all other fixed nodes if 'exclusive' is set void markAsFixed(Mesh &mesh, const Shape *shape, bool exclusive = true) { for (int i = 0; i < mesh.numNodes(); i++) { if (shape->isInside(mesh.nodes(i).pos)) mesh.nodes(i).flags |= Mesh::NfFixed; else if (exclusive) mesh.nodes(i).flags &= ~Mesh::NfFixed; } } static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "markAsFixed", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; Mesh &mesh = *_args.getPtr("mesh", 0, &_lock); const Shape *shape = _args.getPtr("shape", 1, &_lock); bool exclusive = _args.getOpt("exclusive", 2, true, &_lock); _retval = getPyNone(); markAsFixed(mesh, shape, exclusive); _args.check(); } pbFinalizePlugin(parent, "markAsFixed", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("markAsFixed", e.what()); return 0; } } static const Pb::Register _RP_markAsFixed("", "markAsFixed", _W_0); extern "C" { void PbRegister_markAsFixed() { KEEP_UNUSED(_RP_markAsFixed); } } //! Adapt texture coordinates of mesh inside shape //! to obtain an effective inflow effect void texcoordInflow(VortexSheetMesh &mesh, const Shape *shape, const MACGrid &vel) { static Vec3 t0 = Vec3::Zero; // get mean velocity int cnt = 0; Vec3 meanV(0.0); FOR_IJK(vel) { if (shape->isInsideGrid(i, j, k)) { cnt++; meanV += vel.getCentered(i, j, k); } } meanV /= (Real)cnt; t0 -= mesh.getParent()->getDt() * meanV; mesh.setReferenceTexOffset(t0); // apply mean velocity for (int i = 0; i < mesh.numNodes(); i++) { if (shape->isInside(mesh.nodes(i).pos)) { Vec3 tc = mesh.nodes(i).pos + t0; mesh.tex1(i) = tc; mesh.tex2(i) = tc; } } } static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "texcoordInflow", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh &mesh = *_args.getPtr("mesh", 0, &_lock); const Shape *shape = _args.getPtr("shape", 1, &_lock); const MACGrid &vel = *_args.getPtr("vel", 2, &_lock); _retval = getPyNone(); texcoordInflow(mesh, shape, vel); _args.check(); } pbFinalizePlugin(parent, "texcoordInflow", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("texcoordInflow", e.what()); return 0; } } static const Pb::Register _RP_texcoordInflow("", "texcoordInflow", _W_1); extern "C" { void PbRegister_texcoordInflow() { KEEP_UNUSED(_RP_texcoordInflow); } } ; //! Init smoke density values of the mesh surface inside source shape void meshSmokeInflow(VortexSheetMesh &mesh, const Shape *shape, Real amount) { for (int t = 0; t < mesh.numTris(); t++) { if (shape->isInside(mesh.getFaceCenter(t))) mesh.sheet(t).smokeAmount = amount; } } static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "meshSmokeInflow", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh &mesh = *_args.getPtr("mesh", 0, &_lock); const Shape *shape = _args.getPtr("shape", 1, &_lock); Real amount = _args.get("amount", 2, &_lock); _retval = getPyNone(); meshSmokeInflow(mesh, shape, amount); _args.check(); } pbFinalizePlugin(parent, "meshSmokeInflow", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("meshSmokeInflow", e.what()); return 0; } } static const Pb::Register _RP_meshSmokeInflow("", "meshSmokeInflow", _W_2); extern "C" { void PbRegister_meshSmokeInflow() { KEEP_UNUSED(_RP_meshSmokeInflow); } } struct KnAcceleration : public KernelBase { KnAcceleration(MACGrid &a, const MACGrid &v1, const MACGrid &v0, const Real idt) : KernelBase(&a, 0), a(a), v1(v1), v0(v0), idt(idt) { runMessage(); run(); } inline void op( IndexInt idx, MACGrid &a, const MACGrid &v1, const MACGrid &v0, const Real idt) const { a[idx] = (v1[idx] - v0[idx]) * idt; } inline MACGrid &getArg0() { return a; } typedef MACGrid type0; inline const MACGrid &getArg1() { return v1; } typedef MACGrid type1; inline const MACGrid &getArg2() { return v0; } typedef MACGrid type2; inline const Real &getArg3() { return idt; } typedef Real type3; void runMessage() { debMsg("Executing kernel KnAcceleration ", 3); debMsg("Kernel range" << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", 4); }; void operator()(const tbb::blocked_range &__r) const { for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) op(idx, a, v1, v0, idt); } void run() { tbb::parallel_for(tbb::blocked_range(0, size), *this); } MACGrid &a; const MACGrid &v1; const MACGrid &v0; const Real idt; }; //! Add vorticity to vortex sheets based on buoyancy void vorticitySource(VortexSheetMesh &mesh, Vec3 gravity, const MACGrid *vel = NULL, const MACGrid *velOld = NULL, Real scale = 0.1, Real maxAmount = 0, Real mult = 1.0) { Real dt = mesh.getParent()->getDt(); Real dx = mesh.getParent()->getDx(); MACGrid acceleration(mesh.getParent()); if (vel) KnAcceleration(acceleration, *vel, *velOld, 1.0 / dt); const Real A = -1.0; Real maxV = 0, meanV = 0; for (int t = 0; t < mesh.numTris(); t++) { Vec3 fn = mesh.getFaceNormal(t); Vec3 source; if (vel) { Vec3 a = acceleration.getInterpolated(mesh.getFaceCenter(t)); source = A * cross(fn, a - gravity) * scale; } else { source = A * cross(fn, -gravity) * scale; } if (mesh.isTriangleFixed(t)) source = 0; mesh.sheet(t).vorticity *= mult; mesh.sheet(t).vorticity += dt * source / dx; // upper limit Real v = norm(mesh.sheet(t).vorticity); if (maxAmount > 0 && v > maxAmount) mesh.sheet(t).vorticity *= maxAmount / v; // stats if (v > maxV) maxV = v; meanV += v; } cout << "vorticity: max " << maxV << " / mean " << meanV / mesh.numTris() << endl; } static PyObject *_W_3(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "vorticitySource", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh &mesh = *_args.getPtr("mesh", 0, &_lock); Vec3 gravity = _args.get("gravity", 1, &_lock); const MACGrid *vel = _args.getPtrOpt("vel", 2, NULL, &_lock); const MACGrid *velOld = _args.getPtrOpt("velOld", 3, NULL, &_lock); Real scale = _args.getOpt("scale", 4, 0.1, &_lock); Real maxAmount = _args.getOpt("maxAmount", 5, 0, &_lock); Real mult = _args.getOpt("mult", 6, 1.0, &_lock); _retval = getPyNone(); vorticitySource(mesh, gravity, vel, velOld, scale, maxAmount, mult); _args.check(); } pbFinalizePlugin(parent, "vorticitySource", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("vorticitySource", e.what()); return 0; } } static const Pb::Register _RP_vorticitySource("", "vorticitySource", _W_3); extern "C" { void PbRegister_vorticitySource() { KEEP_UNUSED(_RP_vorticitySource); } } void smoothVorticity(VortexSheetMesh &mesh, int iter = 1, Real sigma = 0.2, Real alpha = 0.8) { const Real mult = -0.5 / sigma / sigma; // pre-calculate positions and weights vector vort(mesh.numTris()), pos(mesh.numTris()); vector weights(3 * mesh.numTris()); vector index(3 * mesh.numTris()); for (int i = 0; i < mesh.numTris(); i++) { pos[i] = mesh.getFaceCenter(i); mesh.sheet(i).vorticitySmoothed = mesh.sheet(i).vorticity; } for (int i = 0; i < mesh.numTris(); i++) { for (int c = 0; c < 3; c++) { int oc = mesh.corners(i, c).opposite; if (oc >= 0) { int t = mesh.corners(oc).tri; weights[3 * i + c] = exp(normSquare(pos[t] - pos[i]) * mult); index[3 * i + c] = t; } else { weights[3 * i + c] = 0; index[3 * i + c] = 0; } } } for (int it = 0; it < iter; ++it) { // first, preload for (int i = 0; i < mesh.numTris(); i++) vort[i] = mesh.sheet(i).vorticitySmoothed; for (int i = 0, idx = 0; i < mesh.numTris(); i++) { // loop over adjacent tris Real sum = 1.0f; Vec3 v = vort[i]; for (int c = 0; c < 3; c++, idx++) { Real w = weights[index[idx]]; v += w * vort[index[idx]]; sum += w; } mesh.sheet(i).vorticitySmoothed = v / sum; } } for (int i = 0; i < mesh.numTris(); i++) mesh.sheet(i).vorticitySmoothed *= alpha; } static PyObject *_W_4(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "smoothVorticity", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh &mesh = *_args.getPtr("mesh", 0, &_lock); int iter = _args.getOpt("iter", 1, 1, &_lock); Real sigma = _args.getOpt("sigma", 2, 0.2, &_lock); Real alpha = _args.getOpt("alpha", 3, 0.8, &_lock); _retval = getPyNone(); smoothVorticity(mesh, iter, sigma, alpha); _args.check(); } pbFinalizePlugin(parent, "smoothVorticity", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("smoothVorticity", e.what()); return 0; } } static const Pb::Register _RP_smoothVorticity("", "smoothVorticity", _W_4); extern "C" { void PbRegister_smoothVorticity() { KEEP_UNUSED(_RP_smoothVorticity); } } //! Seed Vortex Particles inside shape with K41 characteristics void VPseedK41(VortexParticleSystem &system, const Shape *shape, Real strength = 0, Real sigma0 = 0.2, Real sigma1 = 1.0, Real probability = 1.0, Real N = 3.0) { Grid temp(system.getParent()); const Real dt = system.getParent()->getDt(); static RandomStream rand(3489572); Real s0 = pow((Real)sigma0, (Real)(-N + 1.0)); Real s1 = pow((Real)sigma1, (Real)(-N + 1.0)); FOR_IJK(temp) { if (shape->isInsideGrid(i, j, k)) { if (rand.getReal() < probability * dt) { Real p = rand.getReal(); Real sigma = pow((1.0 - p) * s0 + p * s1, 1. / (-N + 1.0)); Vec3 randDir(rand.getReal(), rand.getReal(), rand.getReal()); Vec3 posUpd(i + rand.getReal(), j + rand.getReal(), k + rand.getReal()); normalize(randDir); Vec3 vorticity = randDir * strength * pow((Real)sigma, (Real)(-10. / 6. + N / 2.0)); system.add(VortexParticleData(posUpd, vorticity, sigma)); } } } } static PyObject *_W_5(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "VPseedK41", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; VortexParticleSystem &system = *_args.getPtr("system", 0, &_lock); const Shape *shape = _args.getPtr("shape", 1, &_lock); Real strength = _args.getOpt("strength", 2, 0, &_lock); Real sigma0 = _args.getOpt("sigma0", 3, 0.2, &_lock); Real sigma1 = _args.getOpt("sigma1", 4, 1.0, &_lock); Real probability = _args.getOpt("probability", 5, 1.0, &_lock); Real N = _args.getOpt("N", 6, 3.0, &_lock); _retval = getPyNone(); VPseedK41(system, shape, strength, sigma0, sigma1, probability, N); _args.check(); } pbFinalizePlugin(parent, "VPseedK41", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("VPseedK41", e.what()); return 0; } } static const Pb::Register _RP_VPseedK41("", "VPseedK41", _W_5); extern "C" { void PbRegister_VPseedK41() { KEEP_UNUSED(_RP_VPseedK41); } } //! Vortex-in-cell integration void VICintegration(VortexSheetMesh &mesh, Real sigma, Grid &vel, const FlagGrid &flags, Grid *vorticity = NULL, Real cgMaxIterFac = 1.5, Real cgAccuracy = 1e-3, Real scale = 0.01, int precondition = 0) { MuTime t0; const Real fac = 16.0; // experimental factor to balance out regularization // if no vort grid is given, use a temporary one Grid vortTemp(mesh.getParent()); Grid &vort = (vorticity) ? (*vorticity) : (vortTemp); vort.clear(); // map vorticity to grid using Peskin kernel int sgi = ceil(sigma); Real pkfac = M_PI / sigma; const int numTris = mesh.numTris(); for (int t = 0; t < numTris; t++) { Vec3 pos = mesh.getFaceCenter(t); Vec3 v = mesh.sheet(t).vorticity * mesh.getFaceArea(t) * fac; // inner kernel // first, summate Real sum = 0; for (int i = -sgi; i < sgi; i++) { if (pos.x + i < 0 || (int)pos.x + i >= vort.getSizeX()) continue; for (int j = -sgi; j < sgi; j++) { if (pos.y + j < 0 || (int)pos.y + j >= vort.getSizeY()) continue; for (int k = -sgi; k < sgi; k++) { if (pos.z + k < 0 || (int)pos.z + k >= vort.getSizeZ()) continue; Vec3i cell(pos.x + i, pos.y + j, pos.z + k); if (!flags.isFluid(cell)) continue; Vec3 d = pos - Vec3(i + 0.5 + floor(pos.x), j + 0.5 + floor(pos.y), k + 0.5 + floor(pos.z)); Real dl = norm(d); if (dl > sigma) continue; // precalc Peskin kernel sum += 1.0 + cos(dl * pkfac); } } } // then, apply normalized kernel Real wnorm = 1.0 / sum; for (int i = -sgi; i < sgi; i++) { if (pos.x + i < 0 || (int)pos.x + i >= vort.getSizeX()) continue; for (int j = -sgi; j < sgi; j++) { if (pos.y + j < 0 || (int)pos.y + j >= vort.getSizeY()) continue; for (int k = -sgi; k < sgi; k++) { if (pos.z + k < 0 || (int)pos.z + k >= vort.getSizeZ()) continue; Vec3i cell(pos.x + i, pos.y + j, pos.z + k); if (!flags.isFluid(cell)) continue; Vec3 d = pos - Vec3(i + 0.5 + floor(pos.x), j + 0.5 + floor(pos.y), k + 0.5 + floor(pos.z)); Real dl = norm(d); if (dl > sigma) continue; Real w = (1.0 + cos(dl * pkfac)) * wnorm; vort(cell) += v * w; } } } } // Prepare grids for poisson solve Grid vortexCurl(mesh.getParent()); Grid rhs(mesh.getParent()); Grid solution(mesh.getParent()); Grid residual(mesh.getParent()); Grid search(mesh.getParent()); Grid temp1(mesh.getParent()); Grid A0(mesh.getParent()); Grid Ai(mesh.getParent()); Grid Aj(mesh.getParent()); Grid Ak(mesh.getParent()); Grid pca0(mesh.getParent()); Grid pca1(mesh.getParent()); Grid pca2(mesh.getParent()); Grid pca3(mesh.getParent()); MakeLaplaceMatrix(flags, A0, Ai, Aj, Ak); CurlOp(vort, vortexCurl); // Solve vector poisson equation for (int c = 0; c < 3; c++) { // construct rhs if (vel.getType() & GridBase::TypeMAC) GetShiftedComponent(vortexCurl, rhs, c); else GetComponent(vortexCurl, rhs, c); // prepare CG solver const int maxIter = (int)(cgMaxIterFac * vel.getSize().max()); GridCgInterface *gcg = new GridCg( solution, rhs, residual, search, flags, temp1, &A0, &Ai, &Aj, &Ak); gcg->setAccuracy(cgAccuracy); gcg->setUseL2Norm(true); gcg->setICPreconditioner( (GridCgInterface::PreconditionType)precondition, &pca0, &pca1, &pca2, &pca3); // iterations for (int iter = 0; iter < maxIter; iter++) { if (!gcg->iterate()) iter = maxIter; } debMsg("VICintegration CG iterations:" << gcg->getIterations() << ", res:" << gcg->getSigma(), 1); delete gcg; // copy back solution *= scale; SetComponent(vel, solution, c); } } static PyObject *_W_6(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "VICintegration", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; VortexSheetMesh &mesh = *_args.getPtr("mesh", 0, &_lock); Real sigma = _args.get("sigma", 1, &_lock); Grid &vel = *_args.getPtr>("vel", 2, &_lock); const FlagGrid &flags = *_args.getPtr("flags", 3, &_lock); Grid *vorticity = _args.getPtrOpt>("vorticity", 4, NULL, &_lock); Real cgMaxIterFac = _args.getOpt("cgMaxIterFac", 5, 1.5, &_lock); Real cgAccuracy = _args.getOpt("cgAccuracy", 6, 1e-3, &_lock); Real scale = _args.getOpt("scale", 7, 0.01, &_lock); int precondition = _args.getOpt("precondition", 8, 0, &_lock); _retval = getPyNone(); VICintegration( mesh, sigma, vel, flags, vorticity, cgMaxIterFac, cgAccuracy, scale, precondition); _args.check(); } pbFinalizePlugin(parent, "VICintegration", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("VICintegration", e.what()); return 0; } } static const Pb::Register _RP_VICintegration("", "VICintegration", _W_6); extern "C" { void PbRegister_VICintegration() { KEEP_UNUSED(_RP_VICintegration); } } //! Obtain density field from levelset with linear gradient of size sigma over the interface void densityFromLevelset(const LevelsetGrid &phi, Grid &density, Real value = 1.0, Real sigma = 1.0) { FOR_IJK(phi) { // remove boundary if (i < 2 || j < 2 || k < 2 || i >= phi.getSizeX() - 2 || j >= phi.getSizeY() - 2 || k >= phi.getSizeZ() - 2) density(i, j, k) = 0; else if (phi(i, j, k) < -sigma) density(i, j, k) = value; else if (phi(i, j, k) > sigma) density(i, j, k) = 0; else density(i, j, k) = clamp( (Real)(0.5 * value / sigma * (1.0 - phi(i, j, k))), (Real)0.0, value); } } static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { try { PbArgs _args(_linargs, _kwds); FluidSolver *parent = _args.obtainParent(); bool noTiming = _args.getOpt("notiming", -1, 0); pbPreparePlugin(parent, "densityFromLevelset", !noTiming); PyObject *_retval = 0; { ArgLocker _lock; const LevelsetGrid &phi = *_args.getPtr("phi", 0, &_lock); Grid &density = *_args.getPtr>("density", 1, &_lock); Real value = _args.getOpt("value", 2, 1.0, &_lock); Real sigma = _args.getOpt("sigma", 3, 1.0, &_lock); _retval = getPyNone(); densityFromLevelset(phi, density, value, sigma); _args.check(); } pbFinalizePlugin(parent, "densityFromLevelset", !noTiming); return _retval; } catch (std::exception &e) { pbSetError("densityFromLevelset", e.what()); return 0; } } static const Pb::Register _RP_densityFromLevelset("", "densityFromLevelset", _W_7); extern "C" { void PbRegister_densityFromLevelset() { KEEP_UNUSED(_RP_densityFromLevelset); } } } // namespace Manta